Simple and Fast header only Bitmap (BMP) library
- 24 Bits Per Pixel (RGB)
You can either download the header file and use it your project directly, or you can leverage CMake for this.
The library can be easily integrated with FetchContent or through CPM.
An example of CPM integration can be found here.
An example of FetchContent integration can be found here.
Random Pixel Colors
#include "BitmapPlusPlus.hpp"
#include <random>
#include <iostream>
static bmp::Pixel random_color() {
static std::random_device seed{};
static std::default_random_engine engine{seed()};
std::uniform_int_distribution<std::int32_t> dist(0, 255);
bmp::Pixel color{};
color.r = dist(engine);
color.g = dist(engine);
color.b = dist(engine);
return color;
}
int main(void) {
try {
// Create a 512x512 bitmap
bmp::Bitmap image(512, 512);
// Assign a random color to each pixel in the image
for (bmp::Pixel &pixel: image) {
pixel = random_color();
}
// Save bitmap to new file image.bmp
image.save("image.bmp");
// And Voila!
return EXIT_SUCCESS;
}
catch (const bmp::Exception &e) {
std::cerr << "[BMP ERROR]: " << e.what() << std::endl;
return EXIT_FAILURE;
}
}
Draw Primitives
#include <iostream>
#include "BitmapPlusPlus.hpp"
using namespace bmp;
int main() {
// Create a 512x240 blank image
Bitmap image(512, 240);
image.clear(Pixel(0x25292e));
/** Line **/
// Draw a yellow line from position (250, 50) to position (500, 50)
image.draw_line(250, 50, 500, 50, Yellow);
/** Rectangle **/
// Draw a red rectangle in position (10, 10) with size 100x100
image.draw_rect(10, 10, 100, 100, Red);
// Draw a white filled rectangle in position (120, 10) with size 100x100
image.fill_rect(120, 10, 100, 100, White);
/** Triangle **/
image.draw_triangle(60, 120, 10, 220, 120, 220, Cyan);
image.fill_triangle(180, 120, 130, 220, 245, 220, Magenta);
/** Circle **/
// Draw a non-filled Gray circle in position (300, 170) with 50 pixels radius
image.draw_circle(300, 170, 50, Gray);
// Draw a filled Lime circle in position (300, 170) with 50 pixels radius
image.fill_circle(420, 170, 50, Lime);
// Save bitmap
image.save("primitives.bmp");
return EXIT_SUCCESS;
}
Mandelbrot Fractal Set
#include "BitmapPlusPlus.hpp"
#include "color_maps.inl"
#include <cmath>
int main(void) {
bmp::Bitmap image(1280, 960);
double cr, ci;
double nextr, nexti;
double prevr, previ;
constexpr const std::uint16_t max_iterations = 3000;
for (std::int32_t y = 0; y < image.height(); ++y) {
for (std::int32_t x = 0; x < image.width(); ++x) {
cr = 1.5 * (2.0 * x / image.width() - 1.0) - 0.5;
ci = (2.0 * y / image.height() - 1.0);
nextr = nexti = 0;
prevr = previ = 0;
for (std::uint16_t i = 0; i < max_iterations; ++i) {
prevr = nextr;
previ = nexti;
nextr = prevr * prevr - previ * previ + cr;
nexti = 2 * prevr * previ + ci;
if (((nextr * nextr) + (nexti * nexti)) > 4) {
const double z = sqrt(nextr * nextr + nexti * nexti);
// https://en.wikipedia.org/wiki/Mandelbrot_set#Continuous_.28smooth.29_coloring
const std::uint32_t index = static_cast<std::uint32_t>(1000.0 * log2(1.75 + i - log2(log2(z))) /
log2(max_iterations));
image.set(x, y, jet_colormap[index]);
break;
}
}
}
}
image.save("mandelbrot.bmp");
return EXIT_SUCCESS;
}
Julia Fractal Set
#include "BitmapPlusPlus.hpp"
#include "color_maps.inl"
int main(void) {
bmp::Bitmap image(1280, 960);
constexpr const std::uint16_t max_iterations = 300;
constexpr const double cr = -0.70000;
constexpr const double ci = 0.27015;
double prevr, previ;
for (std::int32_t y = 0; y < image.height(); ++y) {
for (std::int32_t x = 0; x < image.width(); ++x) {
double nextr = 1.5 * (2.0 * x / image.width() - 1.0);
double nexti = (2.0 * y / image.height() - 1.0);
for (std::uint16_t i = 0; i < max_iterations; ++i) {
prevr = nextr;
previ = nexti;
nextr = prevr * prevr - previ * previ + cr;
nexti = 2 * prevr * previ + ci;
if (((nextr * nextr) + (nexti * nexti)) > 4) {
const bmp::Pixel color = hsv_colormap[static_cast<std::size_t>((1000.0 * i) / max_iterations)];
image.set(x, y, color);
break;
}
}
}
}
image.save("julia.bmp");
return EXIT_SUCCESS;
}
Modify The Penguin
#include "BitmapPlusPlus.hpp"
int main(void) {
try {
bmp::Bitmap image;
// Load penguin.bmp bitmap
image.load("penguin.bmp");
// Modify loaded image (makes half of the image black)
for (std::int32_t y = 0; y < image.height(); ++y) {
for (std::int32_t x = 0; x < image.width() / 2; ++x) {
image.set(x, y, bmp::Black);
}
}
// Save
image.save("modified-penguin.bmp");
return EXIT_SUCCESS;
}
catch (const bmp::Exception &e) {
return EXIT_FAILURE;
}
}
Rotate, Flip The Penguin
#include "BitmapPlusPlus.hpp"
#include <iostream>
int main(void) {
try {
bmp::Bitmap image;
// Load the original bitmap
image.load(std::filesystem::path(ROOT_DIR) / "images" / "penguin.bmp");
// Test vertical flip
bmp::Bitmap flipped_v = image.flip_v();
flipped_v.save(std::filesystem::path(ROOT_DIR) / "images" / "rotated" / "penguin_flipped_v.bmp");
std::cout << "Vertical flip saved as penguin_flipped_v.bmp" << std::endl;
// Test horizontal flip
bmp::Bitmap flipped_h = image.flip_h();
flipped_h.save(std::filesystem::path(ROOT_DIR) / "images" / "rotated" / "penguin_flipped_h.bmp");
std::cout << "Horizontal flip saved as penguin_flipped_h.bmp" << std::endl;
// Test rotate 90 degrees to the right
bmp::Bitmap rotated_right = image.rotate_90_right();
rotated_right.save(std::filesystem::path(ROOT_DIR) / "images" / "rotated" / "penguin_rotated_right.bmp");
std::cout << "Rotated 90 degrees right saved as penguin_rotated_right.bmp" << std::endl;
// Test rotate 90 degrees to the left
bmp::Bitmap rotated_left = image.rotate_90_left();
rotated_left.save(std::filesystem::path(ROOT_DIR) / "images" / "rotated" / "penguin_rotated_left.bmp");
std::cout << "Rotated 90 degrees left saved as penguin_rotated_left.bmp" << std::endl;
return EXIT_SUCCESS;
}
catch (const bmp::Exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
}
Chess Board
#include <iostream>
#include "BitmapPlusPlus.hpp"
int main() {
try {
// 8x8 chess board
bmp::Bitmap image(640, 640);
const std::size_t board_dims = 8;
const std::size_t rect_w = image.width() / board_dims;
const std::size_t rect_h = image.height() / board_dims;
// Iterate over rects
bool is_white = true;
for (std::size_t x = 0; x < image.width(); x += rect_w) {
for (std::size_t y = 0; y < image.height(); y += rect_h) {
bmp::Pixel color = is_white ? bmp::White : bmp::Black;
// Fill rect
image.fill_rect(x, y, rect_w, rect_h, color);
// Next rect in will be the opposite color
is_white = !is_white;
}
is_white = !is_white;
}
// Save bitmap to file
image.save("chess_board.bmp");
return EXIT_SUCCESS;
}
catch (const bmp::Exception &e) {
std::cerr << "[BMP ERROR]: " << e.what() << '\n';
return EXIT_FAILURE;
}
}
Draw multiple shapes using Polymorphism
#include <iostream>
#include "BitmapPlusPlus.hpp"
struct Shape {
int x, y;
bmp::Pixel color;
Shape(int x, int y, bmp::Pixel color) : x(x), y(y), color(color) {}
virtual void draw(bmp::Bitmap &image) = 0;
};
struct Rectangle : Shape {
int width, height;
Rectangle(int x, int y, int w, int h, bmp::Pixel color) : width(w), height(h), Shape(x, y, color) {}
void draw(bmp::Bitmap &image) override {
image.fill_rect(x, y, width, height, color);
}
};
struct Triangle : Shape {
int x2, y2, x3, y3;
Triangle(int x1, int y1, int x2, int y2, int x3, int y3, bmp::Pixel color) : x2(x2), y2(y2), x3(x3), y3(y3),
Shape(x1, y1, color) {}
void draw(bmp::Bitmap &image) override {
image.fill_triangle(x, y, x2, y2, x3, y3, color);
}
};
struct Circle : Shape {
int radius;
Circle(int x, int y, int radius, bmp::Pixel color) : radius(radius), Shape(x, y, color) {}
void draw(bmp::Bitmap &image) override {
image.fill_circle(x, y, radius, color);
}
};
int main() {
try {
bmp::Bitmap image(640, 256);
bmp::Pixel background_color{bmp::Silver};
image.clear(background_color);
std::vector<Shape *> shapes
{
new Rectangle(20, 20, 180, 180, bmp::Pixel(0xa31d3a)),
new Triangle(310, 20, 230, 200, 400, 200, bmp::Pixel(0x1a5096)),
new Circle(500, 110, 90, bmp::Pixel(0x228035))
};
for (Shape *shape: shapes) {
shape->draw(image);
delete shape;
}
image.save("shapes.bmp");
return EXIT_SUCCESS;
}
catch (const bmp::Exception &e) {
std::cerr << "[BMP ERROR]: " << e.what() << '\n';
return EXIT_FAILURE;
}
}
If you face any problems feel free to open an issue at the issue tracker. If you feel the library is missing a feature, please raise a ticket on Github. Pull request are also welcome.