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..5a71c25 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 @@ -422,6 +438,13 @@ class bitmap_image return; } + write_image(stream); + + stream.close(); + } + + void write_image(std::ostream& stream) const + { bitmap_information_header bih; bih.width = width_; @@ -457,8 +480,6 @@ class bitmap_image stream.write(reinterpret_cast(data_ptr), sizeof(unsigned char) * bytes_per_pixel_ * width_); stream.write(padding_data,padding); } - - stream.close(); } inline void set_all_ith_bits_low(const unsigned int bitr_index) @@ -1364,18 +1385,18 @@ class bitmap_image } template - inline void read_from_stream(std::ifstream& stream,T& t) + inline void read_from_stream(std::istream& stream,T& t) { stream.read(reinterpret_cast(&t),sizeof(T)); } template - inline void write_to_stream(std::ofstream& stream,const T& t) const + inline void write_to_stream(std::ostream& stream,const T& t) const { stream.write(reinterpret_cast(&t),sizeof(T)); } - inline void read_bfh(std::ifstream& stream, bitmap_file_header& bfh) + inline void read_bfh(std::istream& stream, bitmap_file_header& bfh) { read_from_stream(stream,bfh.type ); read_from_stream(stream,bfh.size ); @@ -1393,7 +1414,7 @@ class bitmap_image } } - inline void write_bfh(std::ofstream& stream, const bitmap_file_header& bfh) const + inline void write_bfh(std::ostream& stream, const bitmap_file_header& bfh) const { if (big_endian()) { @@ -1413,7 +1434,7 @@ class bitmap_image } } - inline void read_bih(std::ifstream& stream,bitmap_information_header& bih) + inline void read_bih(std::istream& stream,bitmap_information_header& bih) { read_from_stream(stream,bih.size ); read_from_stream(stream,bih.width ); @@ -1443,7 +1464,7 @@ class bitmap_image } } - inline void write_bih(std::ofstream& stream, const bitmap_information_header& bih) const + inline void write_bih(std::ostream& stream, const bitmap_information_header& bih) const { if (big_endian()) { @@ -1499,6 +1520,13 @@ class bitmap_image return; } + read_bitmap(stream); + + stream.close(); + } + + bool read_bitmap(std::istream& stream) + { width_ = 0; height_ = 0; @@ -1516,10 +1544,8 @@ class bitmap_image 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; + return false; } if (bih.bit_count != 24) @@ -1527,11 +1553,9 @@ class bitmap_image 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; + return false; } if (bih.size != bih.struct_size()) @@ -1539,11 +1563,9 @@ class bitmap_image 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; + return false; } width_ = bih.width; @@ -1566,13 +1588,11 @@ class bitmap_image 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; + return false; } create_bitmap(); @@ -1584,6 +1604,8 @@ class bitmap_image stream.read(reinterpret_cast(data_ptr), sizeof(char) * bytes_per_pixel_ * width_); stream.read(padding_data,padding); } + + return true; } template @@ -1606,12 +1628,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,