From 624fb94c7e7986d1dda731fa15ee8fce4d7a13c4 Mon Sep 17 00:00:00 2001 From: ArashPartow Date: Sat, 24 Mar 2018 14:38:43 +1100 Subject: [PATCH] C++ Bitmap Image Reader Writer Library http://partow.net/programming/bitmap/index.html --- .travis.yml | 2 +- bitmap_image.hpp | 67 ++++++++++------ readme.md | 193 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 228 insertions(+), 34 deletions(-) diff --git a/.travis.yml b/.travis.yml index d88c773..c4e7b2a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: cpp sudo: required -dist: trusty +dist: xenial compiler: - gcc diff --git a/bitmap_image.hpp b/bitmap_image.hpp index 33e114d..ee73501 100644 --- a/bitmap_image.hpp +++ b/bitmap_image.hpp @@ -49,6 +49,13 @@ class bitmap_image red_plane = 2 }; + struct rgb_t + { + unsigned char red; + unsigned char green; + unsigned char blue; + }; + bitmap_image() : file_name_(""), width_ (0), @@ -164,10 +171,11 @@ class bitmap_image { const unsigned int y_offset = y * row_increment_; const unsigned int x_offset = x * bytes_per_pixel_; + const unsigned int offset = y_offset + x_offset; - blue = data_[y_offset + x_offset + 0]; - green = data_[y_offset + x_offset + 1]; - red = data_[y_offset + x_offset + 2]; + blue = data_[offset + 0]; + green = data_[offset + 1]; + red = data_[offset + 2]; } template @@ -176,6 +184,13 @@ class bitmap_image get_pixel(x, y, colour.red, colour.green, colour.blue); } + inline rgb_t get_pixel(const unsigned int x, const unsigned int y) const + { + rgb_t colour; + get_pixel(x, y, colour.red, colour.green, colour.blue); + return colour; + } + inline void set_pixel(const unsigned int x, const unsigned int y, const unsigned char red, const unsigned char green, @@ -183,10 +198,11 @@ class bitmap_image { const unsigned int y_offset = y * row_increment_; const unsigned int x_offset = x * bytes_per_pixel_; + const unsigned int offset = y_offset + x_offset; - data_[y_offset + x_offset + 0] = blue; - data_[y_offset + x_offset + 1] = green; - data_[y_offset + x_offset + 2] = red; + data_[offset + 0] = blue; + data_[offset + 1] = green; + data_[offset + 2] = red; } template @@ -418,7 +434,8 @@ class bitmap_image if (!stream) { - std::cerr << "bitmap_image::save_image(): Error - Could not open file " << file_name << " for writing!" << std::endl; + std::cerr << "bitmap_image::save_image(): Error - Could not open file " + << file_name << " for writing!" << std::endl; return; } @@ -1495,7 +1512,8 @@ class bitmap_image if (!stream) { - std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - file " << file_name_ << " not found!" << std::endl; + std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - " + << "file " << file_name_ << " not found!" << std::endl; return; } @@ -1513,36 +1531,41 @@ class bitmap_image if (bfh.type != 19778) { + std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - " + << "Invalid type value " << bfh.type << " expected 19778." << std::endl; + bfh.clear(); bih.clear(); stream.close(); - std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - Invalid type value " << bfh.type << " expected 19778." << std::endl; return; } if (bih.bit_count != 24) { + std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - " + << "Invalid bit depth " << bih.bit_count << " expected 24." << std::endl; + bfh.clear(); bih.clear(); stream.close(); - std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - Invalid bit depth " << bih.bit_count << " expected 24." << std::endl; - return; } if (bih.size != bih.struct_size()) { + std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - " + << "Invalid BIH size " << bih.size + << " expected " << bih.struct_size() << std::endl; + bfh.clear(); bih.clear(); stream.close(); - std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - Invalid BIH size " << bih.size << " expected " << bih.struct_size() << std::endl; - return; } @@ -1552,7 +1575,7 @@ class bitmap_image bytes_per_pixel_ = bih.bit_count >> 3; unsigned int padding = (4 - ((3 * width_) % 4)) % 4; - char padding_data[4] = {0,0,0,0}; + char padding_data[4] = { 0x00, 0x00, 0x00, 0x00 }; std::size_t bitmap_file_size = file_size(file_name_); @@ -1563,15 +1586,16 @@ class bitmap_image if (bitmap_file_size != bitmap_logical_size) { + std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - " + << "Mismatch between logical and physical sizes of bitmap. " + << "Logical: " << bitmap_logical_size << " " + << "Physical: " << bitmap_file_size << std::endl; + bfh.clear(); bih.clear(); stream.close(); - std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - Mismatch between logical and physical sizes of bitmap. " << - "Logical: " << bitmap_logical_size << " " << - "Physical: " << bitmap_file_size << std::endl; - return; } @@ -1606,12 +1630,7 @@ class bitmap_image std::vector data_; }; -struct rgb_t -{ - unsigned char red; - unsigned char green; - unsigned char blue; -}; +typedef bitmap_image::rgb_t rgb_t; inline bool operator==(const rgb_t& c0, const rgb_t& c1) { diff --git a/readme.md b/readme.md index 76de6fa..c10dcb7 100644 --- a/readme.md +++ b/readme.md @@ -415,7 +415,76 @@ int main() ---- -#### Simple Example 7 - Maze Generation +#### Simple Example 7 - Frosted Glass Effect +The following example will render a baseline image using a +combination of plasma and checkered pattern effects. Then +proceed to apply a frosted glass diffusion effect upon the +base image. Finally the frosted glass version of the image +will be saved to 'glass_effect.bmp'. + +```c++ +#include +#include +#include "bitmap_image.hpp" + +int main() +{ + const int width = 600; + const int height = 600; + const int kernel_size = 10; + + bitmap_image base(width,height); + + base.clear(); + + { + const double c1 = 0.8; + const double c2 = 0.4; + const double c3 = 0.2; + const double c4 = 0.6; + + ::srand(0xA5AA57A5); + + plasma(base, 0, 0, base.width(), base.height(), c1, c2, c3, c4, 3.0, jet_colormap); + + checkered_pattern(30, 30, 230, bitmap_image:: red_plane, base); + checkered_pattern(30, 30, 0, bitmap_image::green_plane, base); + checkered_pattern(30, 30, 100, bitmap_image:: blue_plane, base); + } + + bitmap_image glass_image(base.width(),base.height()); + + glass_image = base; + + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + const unsigned int min_x = std::max(0, x - kernel_size); + const unsigned int min_y = std::max(0, y - kernel_size); + const unsigned int max_x = std::min(x + kernel_size, width - 1); + const unsigned int max_y = std::min(y + kernel_size, height - 1); + const unsigned int dx = (max_x - min_x); + const unsigned int dy = (max_y - min_y); + const unsigned int N = rand() % (dx * dy); + const unsigned int cx = (N % dx) + min_x; + const unsigned int cy = (N / dx) + min_y; + + glass_image.set_pixel(x, y, base.get_pixel(cx, cy)); + } + } + + glass_image.save_image("glass_effect.bmp"); + + return 0; +} +``` + +![ScreenShot](http://www.partow.net/programming/bitmap/images/glass_effect.png?raw=true "C++ Bitmap Library Frosted Glass Effect Example - By Arash Partow") + +---- + +#### Simple Example 8 - Maze Generation The following example will render a maze generated using a simple recursive backtracking algorithm. The example demonstrates the use of the drawing and colouring functionalities. Once the maze has been @@ -435,7 +504,7 @@ const move_t move[5] = { { 0, 0, 0 }, // North East South West - { 0,-1, S }, { 1, 0, W }, { 0, 1, N }, {-1, 0, E } + { 0, -1, S }, { 1, 0, W }, { 0, 1, N }, { -1, 0, E } }; const int movemap[] = {0, 1, 2, 0, 3, 0, 0, 0, 4}; @@ -463,9 +532,9 @@ void generate_maze(int cx, int cy, response_image& maze) if ( (x < 0) || (y < 0) || - (untouched != maze(x,y)) || (x >= (int)maze.width ()) || - (y >= (int)maze.height()) + (y >= (int)maze.height()) || + (untouched != maze(x,y)) ) continue; @@ -559,7 +628,7 @@ int main() ---- -#### Simple Example 8 - Fireballs Along A Lissajous Curve +#### Simple Example 9 - Fireballs Along A Lissajous Curve The following example is an old-school graphical effect of rendering fireballs that have been placed equidistant to their immediate neighbours following a Lissajous curve. The fireballs will then @@ -728,7 +797,7 @@ int main() ---- -#### Simple Example 9 - Sierpinski Triangle Via Monte-Carlo Method +#### Simple Example 10 - Sierpinski Triangle Via Monte-Carlo Method The following example will render the Sierpinski triangle fractal using a linear difference equation based monte-carlo process, and then proceed to save the generated bitmap as *'sierpinski_triangle.bmp'*. @@ -792,7 +861,7 @@ int main() ---- -#### Simple Example 10 - Circles And Equilateral Triangles +#### Simple Example 11 - Circles And Equilateral Triangles The following example randomly generate circles and proceed to inscribe multiple levels of inner equilateral triangles. The example demonstrates the use of the cartesian canvas, pen functions, various @@ -883,7 +952,7 @@ int main() ---- -#### Simple Example 11 - Archimedean Spirals +#### Simple Example 12 - Archimedean Spirals The following example renders Archimedean spirals upon a gray-scale plasma background. The example demonstrates the use of the cartesian canvas, pen functions, and colour maps. Once complete the rendering @@ -977,7 +1046,7 @@ int main() ---- -#### Simple Example 12 - Image Shuffle +#### Simple Example 13 - Image Shuffle The following example will take as input *'tiger.bmp'*. Then proceed to dissect the image into 9 cells of 3x3, then proceed to randomly shuffle cells. The example demonstrates the copying to-and-from @@ -1042,6 +1111,112 @@ int main() ---- +#### Simple Example 14 - Phyllotaxis Spiral +The following example renders a Phyllotaxis spiral upon a copper +plasma background. The example demonstrates the use of the cartesian +canvas, circle fill function, and colour maps. Once complete the +rendering will be saved to disk with the name: *'phyllotaxis.bmp'*. + +```c++ +#include +#include +#include "bitmap_image.hpp" + +int main() +{ + const int canvas_width = 600; + const int canvas_height = 600; + + const double pi = 3.1415926535897932384626433832795028841971; + const double phi = pi * (3.0 - std::sqrt(5.0)); + const double radius = (std::min(canvas_width, canvas_height) / 2.0) - 5.0; + const double N = 1200.0; + const double spread = radius / std::sqrt(N); + const double p_radius = std::floor(spread / 2.0); + + cartesian_canvas canvas(canvas_width,canvas_height); + + { + // Render background using Plasma effect + const double c1 = 0.9; + const double c2 = 0.5; + const double c3 = 0.3; + const double c4 = 0.7; + + bitmap_image& image = canvas.image(); + + ::srand(0xA5AA5AA5); + + plasma(image, 0, 0, image.width(), image.height(), c1, c2, c3, c4, 3.0, copper_colormap); + } + + for (double i = 0.0; i < N; ++i) + { + const double theta = phi * i; + const double d = spread * std::sqrt(i); + const double x = d * std::cos(theta); + const double y = d * std::sin(theta); + + canvas.pen_color(hsv_colormap[static_cast(1000.0 * (i / N))]); + canvas.fill_circle(x, y, p_radius); + } + + canvas.image().save_image("phyllotaxis.bmp"); + + return 0; +} +``` + +![ScreenShot](http://www.partow.net/programming/bitmap/images/phyllotaxis.png?raw=true "C++ Bitmap Library Phyllotaxis Spiral - By Arash Partow") + +---- + +#### Simple Example 15 - Pointillism Effect +The following example will render an input image of a *Sunflower* +using an approximation of the Pointillism painting technique. Once +the rendering is complete the image will be saved to disk with the +name: *'pointillist.bmp'*. + +```c++ +#include +#include "bitmap_image.hpp" + +int main() +{ + bitmap_image base("sunflower.bmp"); + + cartesian_canvas canvas(base.width(),base.height()); + canvas.image() = base; + + const int pixel_count = base.width() * base.height(); + const int N = static_cast(pixel_count * 0.03); // 3% of pixels + const double rnd_ratio = pixel_count / (1.0 + RAND_MAX); + + ::srand(0xA57A57A5); + + for (int i = 0; i < N; ++i) + { + const int r = static_cast(rand() * rnd_ratio); + const int x = (r % base.width()); + const int y = (r / base.width()); + const double cx = x - (base.width() / 2.0); + const double cy = (base.height() / 2.0) - y; + const double radius = 1.0 + (r % 7); + + canvas.pen_color(base.get_pixel(x, y)); + canvas.fill_circle(cx, cy, radius); + } + + canvas.image().save_image("pointillist.bmp"); + + return 0; +} +``` + +![ScreenShot](http://www.partow.net/programming/bitmap/images/pointillist.png?raw=true "C++ Bitmap Library Pointillism Effect - By Arash Partow") + +---- + #### Final Note The above examples are for exposition purposes, primarily intended to demonstrate the functionality of the bitmap_image library using short,