The following example will open a bitmap image called 'input.bmp' by
instantiating an instance of the bitmap_image class and then proceed
to count the number of pixels that have a red channel value greater than or equal
to 111, then proceed to print the count to stdout.
[Code: bitmap_simple_example01.cpp]
#include <cstdio>
#include "bitmap_image.hpp"
int main()
{
bitmap_image image("input.bmp");
if (!image)
{
printf("Error - Failed to open: input.bmp\n");
return 1;
}
unsigned int total_number_of_pixels = 0;
const unsigned int height = image.height();
const unsigned int width = image.width();
for (std::size_t y = 0; y < height; ++y)
{
for (std::size_t x = 0; x < width; ++x)
{
rgb_t colour;
image.get_pixel(x, y, colour);
if (colour.red >= 111)
total_number_of_pixels++;
}
}
printf("Number of pixels with red >= 111: %d\n",total_number_of_pixels);
return 0;
}
The following example will create a bitmap of dimensions 200x200
pixels, set the background colour to orange, then proceed to draw
a circle centered in the middle of the bitmap of radius 50 pixels
and of colour red then a rectangle centered in the middle of the
bitmap with a width and height of 100 pixels and of colour blue.
The newly constructed image will then be saved to disk with the
name: 'output.bmp'.
[Code: bitmap_simple_example02.cpp]
The following example will render a baseline image using a combination of plasma and
checkered pattern effects. Then proceed to apply a lens distortion upon the base image.
Finally both the base and the lens distorted versions of the images will be saved to
file as 'base.bmp' and 'lens_effect.bmp'
respectively.
[Code: bitmap_simple_example05.cpp]
The following example will render a baseline image using a combination of plasma and
checkered pattern effects. Then proceed to apply a swirl distortion upon the base image.
Finally both the base and the swirl distorted versions of the images will be saved to
file as 'base.bmp' and 'swirl_effect.bmp'
respectively. [Code: bitmap_simple_example06.cpp]
#include <cmath>
#include "bitmap_image.hpp"
int main()
{
bitmap_image base(600,600);
base.clear();
{
const double c1 = 0.8;
const double c2 = 0.4;
const double c3 = 0.2;
const double c4 = 0.6;
::srand(0x5A5A5A5A);
plasma(base, 0, 0, base.width(), base.height(), c1, c2, c3, c4, 7.0, jet_colormap);
checkered_pattern(20, 20, 250, bitmap_image::red_plane , base);
checkered_pattern(20, 20, 10, bitmap_image::green_plane, base);
checkered_pattern(20, 20, 10, bitmap_image::blue_plane , base);
}
bitmap_image swirl_image(base.width(),base.height());
swirl_image.clear();
const double swirl_center_x = base.width () / 2.0;
const double swirl_center_y = base.height() / 2.0;
const double swirl_radius = std::min(base.width(), base.height()) / 3.0;
const double pi_ = 3.1415926535897932384626433832795028841971;
const double swirl_angle = pi_ / 3.0;
for (unsigned int y = 0; y < base.height(); ++y)
{
for (unsigned int x = 0; x < base.width(); ++x)
{
const double dx = x - swirl_center_x;
const double dy = y - swirl_center_y;
const double distance = std::sqrt((dx * dx) + (dy * dy));
const double angle = swirl_angle * (distance / swirl_radius);
const double cosa = std::cos(angle);
const double sina = std::sin(angle);
int sx = static_cast<int>(dx * cosa - dy * sina + swirl_center_x);
int sy = static_cast<int>(dx * sina + dy * cosa + swirl_center_y);
if (
(sx >= 0) &&
(sy >= 0) &&
(sx < (int)base.width ()) &&
(sy < (int)base.height())
)
{
unsigned char red;
unsigned char green;
unsigned char blue;
base .get_pixel(sx, sy, red, green, blue);
swirl_image.set_pixel( x, y, red, green, blue);
}
}
}
base .save_image("base.bmp" );
swirl_image.save_image("swirl_effect.bmp");
return 0;
}
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'.
[Code: bitmap_simple_example07.cpp]
#include <algorithm>
#include <cstdlib>
#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;
}
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 proceed to move within the plane using the curve as their
path. The example demonstrates the construction of a piecewise colour palette
and the response_image functionality. After having 'simulated' N-frames, the
final frame will be converted to a bitmap and then saved to file as
'fireballs.bmp'.
[Fireballs Animation][Code: bitmap_simple_example09.cpp]
#include <cmath>
#include <iterator>
#include <vector>
#include "bitmap_image.hpp"
struct lissajous_curve
{
lissajous_curve(const double xs, const double ys)
: scale_x(xs),
scale_y(ys)
{}
inline double x(const double t) const { return scale_x * std::sin(4 * t); }
inline double y(const double t) const { return scale_y * std::cos(3 * t); }
double scale_x;
double scale_y;
};
int main()
{
bitmap_image image(700,500);
image.clear();
lissajous_curve curve(image.width () / 2.0 - 25, image.height() / 2.0 - 25);
const double pi_ = 3.1415926535897932384626433832795028841971;
const double delta = (2.0 * pi_) / 100000.0;
const std::size_t max_fire_balls = 30;
const std::size_t number_of_frames = 3000;
const double cooling_factor = 0.940; // [0,1]
// Arc-length of curve: x(t) = a0 * sin(4t), y(t) = a1 * cos(3t)
const double curve_length = 6151.0;
double segment_length = curve_length / max_fire_balls;
double curr_seg_length = 0;
double prev_x = curve.x(0);
double prev_y = curve.y(0);
std::vector<double> fire_ball;
// Set the initial location for each fireball
for (double t = delta; fire_ball.size() < max_fire_balls; t += delta)
{
const double center_x = curve.x(t);
const double center_y = curve.y(t);
const double dx = (prev_x - center_x);
const double dy = (prev_y - center_y);
curr_seg_length += std::sqrt((dx * dx) + (dy * dy));
prev_x = center_x;
prev_y = center_y;
if (curr_seg_length >= segment_length)
{
curr_seg_length = 0.0;
fire_ball.push_back(t);
}
}
response_image<double> resp_image(image.width(),image.height(), -1.0);
response_image<double> fb_misses (image.width(),image.height(), -1.0);
resp_image.set_all(-999.0);
fb_misses .set_all( 0.0);
std::vector<rgb_t> fire_palette;
// Baseline colours used in fire palette
rgb_t black = make_colour( 0, 0, 0);
rgb_t red = make_colour(255, 0, 0);
rgb_t yellow = make_colour(255, 255, 0);
rgb_t white = make_colour(255, 255, 255);
// Setup the fire palette:
// Black (Coolest - 0) --> Red --> Yellow --> White (Hottest - 999)
generate_colours(334, black, red, std::back_inserter(fire_palette));
generate_colours(333, red, yellow, std::back_inserter(fire_palette));
generate_colours(333, yellow, white, std::back_inserter(fire_palette));
for (std::size_t k = 0; k < number_of_frames; ++k)
{
fb_misses.inc_all(1);
// Render fireballs on response image
for (std::size_t i = 0; i < fire_ball.size(); ++i)
{
const double fb_x = curve.x(fire_ball[i]) + image.width () / 2.0;
const double fb_y = curve.y(fire_ball[i]) + image.height() / 2.0;
// Draw circles with radii in the range [1,10]
for (double t = 0; t < (2 * pi_); t += (((2.0 * pi_) / 360)))
{
for (double r = 1; r <= 10; ++r)
{
std::size_t rx = static_cast<std::size_t>(r * std::sin(t) + fb_x);
std::size_t ry = static_cast<std::size_t>(r * std::cos(t) + fb_y);
// Per-frame in the range [0,100]
double heat_distortion = 50.0 * std::cos(delta * i) + 50;
resp_image(rx,ry) = fire_palette.size() * 0.8 +
heat_distortion +
(::rand() % 100);
fb_misses (rx,ry) = 0;
}
}
// Move fireball to its next location
fire_ball[i] += delta;
}
// Apply cooling process to the entire plane
for (std::size_t y = 1; y < resp_image.height() - 1; ++y)
{
for (std::size_t x = 1; x < resp_image.width() - 1; ++x)
{
double avg = (
resp_image(x - 1, y - 1) + resp_image(x , y - 1) +
resp_image(x + 1, y - 1) + resp_image(x - 1, y ) +
resp_image(x + 1, y ) + resp_image(x , y + 1) +
resp_image(x - 1, y + 1) + resp_image(x + 1, y + 1)
) / (7.0 + cooling_factor);
// Only allow cooler averages to be applied
if (avg > resp_image(x, y))
continue;
// More rapidly cool points that haven't seen fireballs in the last N-frames
if (fb_misses(x,y) > 2000)
avg *= 0.90 + ((::rand() % 10) / 100.0);
// Clamp average in the range [0,999]
resp_image(x,y) = ((avg < 0.0) ? 0.0 : ((avg > 999.0) ? 999.0 : avg));
}
}
}
convert_rsp_to_image(resp_image, fire_palette, image);
image.save_image("fireballs.bmp");
return 0;
}
The following example will 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 shape fill routines and colour maps.
Once complete the rendering will be saved to disk with the name:
'circles_and_triangles.bmp'.
[Code: bitmap_simple_example11.cpp]
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 will be saved to disk with the name:
'spirals.bmp'.
[Code: bitmap_simple_example12.cpp]
#include <cmath>
#include <cstdlib>
#include "bitmap_image.hpp"
struct point_t
{
point_t(double _x = 0.0, double _y = 0.0) : x(_x), y(_y) {}
double x,y;
};
int main()
{
const double pi_ = 3.1415926535897932384626433832795028841971;
const double a = 20.0;
const double b = 20.0;
const double dr = (2.0 * pi_) / 1000.0;
const std::size_t N = 5;
const double delta_angle = (2.0 * pi_) / N;
std::vector<point_t> spiral;
for (std::size_t i = 0; i < N; ++i)
{
spiral.push_back(
point_t(a * std::cos((delta_angle * i)),
a * std::sin((delta_angle * i))));
}
const int canvas_width = 600;
const int canvas_height = 600;
cartesian_canvas canvas(canvas_width,canvas_height);
canvas.image().clear(0);
{
// 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, gray_colormap);
}
for (double angle = dr; (a + b * angle) < canvas.image().width() / 2.0; angle += dr)
{
for (std::size_t i = 0; i < spiral.size(); ++i)
{
const double theta = angle + delta_angle * i;
const double d = (a + b * angle);
const point_t curr(d * std::cos(theta), d * std::sin(theta));
const double centre_ratio =
(sqrt(curr.x * curr.x + curr.y * curr.y) / (canvas.image().width() / 2.0));
if (centre_ratio <= 0.25) canvas.pen_width(1);
else if (centre_ratio <= 0.50) canvas.pen_width(2);
else if (centre_ratio <= 0.75) canvas.pen_width(3);
else if (centre_ratio <= 1.00) canvas.pen_width(4);
unsigned int index = (unsigned int)(1000.0 * centre_ratio);
canvas.pen_color(hsv_colormap[index]);
canvas.line_segment(spiral[i].x, spiral[i].y, curr.x, curr.y);
spiral[i] = curr;
}
}
canvas.image().save_image("spirals.bmp");
return 0;
}
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
the cells. The example demonstrates the copying to-and-from 'Regions Of Interest' ROI
within and between images. Once the shuffling as been complete the shuffled image will be
saved to disk with the name: 'shuffled.bmp'.
[Code: bitmap_simple_example13.cpp]
#include <cmath>
#include <cstdlib>
#include <vector>
#include "bitmap_image.hpp"
void shuffle(unsigned int n, std::vector<unsigned int>& v)
{
::srand(0x13A1515A);
for (unsigned int i = 0; i < n; ++i) v.push_back(i);
for (unsigned int i = v.size() - 1; i > 0; --i)
{ std::swap(v[i], v[rand() % (i + 1)]); }
}
int main()
{
bitmap_image image("tiger.bmp");
const unsigned int divisions = 3;
const unsigned int block_width = image.width () / divisions;
const unsigned int block_height = image.height() / divisions;
std::vector<unsigned int> cell;
shuffle(divisions * divisions, cell);
bitmap_image shuffled(image.width(), image.height());
bitmap_image region;
shuffled.clear();
for (std::size_t i = 0; i < cell.size(); ++i)
{
unsigned int x_offset = block_width * (i % divisions);
unsigned int y_offset = block_height * (i / divisions);
// Copy region from original image
image.region(x_offset, y_offset,
block_width, block_height, region);
x_offset = block_width * (cell[i] % divisions);
y_offset = block_height * (cell[i] / divisions);
// Paste region to new location in shuffled image
shuffled.copy_from(region, x_offset, y_offset);
}
shuffled.save_image("shuffled.bmp");
return 0;
}
#include <cstdlib>
#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<int>(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<int>(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;
}
Final Note:
The above examples are for exposition purposes, primarily intended to demonstrate
the functionality of the bitmap_image library using short, concise and simple to
understand pieces of code. As such certain 'coding' simplifications were
made which would not necessarily be acceptable in production implementations. As
an example the use of the quick-n-dirty libc 'rand' function should be
replaced with the C++ standard library's more modern and robust
std::random
facilities. Hence when evaluating the above examples such 'issues' should perhaps
be taken into consideration.