Finding a small bmp file in a bigger bmp file - c++

I want to find a small bmp file from another bigger bmp file (the bigger one is captured from screen and called Sample.bmp , the small bmp file is called Button.bmp . Thing is the when comparing the images the file can't be found anywhere.
the compare code :
for (int i=0;i<SCREEN_WIDTH-width;++i)
{
for (int j=0;j<SCREEN_HEIGHT-height;++j)
{
boolean isequal = true;
for(int qqq=i;qqq<i+width;++qqq)
{
for (int kkk=j;kkk<j+height;++kkk)
{
if (PI[qqq][kkk]!=NPI[qqq-i][kkk-j]) isequal = false;
if (isequal == false)
{
qqq = i + width + 1;
kkk = j + height + 1;
}
}
}
if (isequal==true)
{
MidX = i;
MidY = j;
return;
}
}
}
note : Screen_width and Screen_height are for the bigger image and width and height are for the smaller one
Full Code:
void readBMP()
{
int i;
FILE* f = fopen("Sample.bmp", "rb");
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header
// extract image height and width from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
int size = 3 * width * height;
unsigned char* data = new unsigned char[size]; // allocate 3 bytes per pixel
fread(data, sizeof(unsigned char), size, f); // read the rest of the data at once
fclose(f);
for(int qq=0;qq<SCREEN_WIDTH;++qq)
for (int kk=0;kk<SCREEN_HEIGHT;++kk)
{
PI[qq][kk][0] = data[kk * width + qq];
PI[qq][kk][1] = data[kk * width + qq + 1];
PI[qq][kk][2] = data[kk * width + qq + 2];
}
}
void FindImageInScreen(char* FileName)
{
FILE* f = fopen(FileName, "rb");
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header
// extract image height and width from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
int size = 3 * width * height;
unsigned char* data = new unsigned char[size]; // allocate 3 bytes per pixel
fread(data, sizeof(unsigned char), size, f); // read the rest of the data at once
fclose(f);
for(int qq=0;qq<width;++qq)
for (int kk=0;kk<height;++kk)
{
NPI[qq][kk][0] = data[kk * width + qq];
NPI[qq][kk][1] = data[kk * width + qq + 1];
NPI[qq][kk][2] = data[kk * width + qq + 2];
}
for (int i=0;i<SCREEN_WIDTH-width;++i)
{
for (int j=0;j<SCREEN_HEIGHT-height;++j)
{
boolean isequal = true;
for(int qqq=i;qqq<i+width;++qqq)
{
for (int kkk=j;kkk<j+height;++kkk)
{
if (PI[qqq][kkk][0]!=NPI[qqq-i][kkk-j][0]) isequal = false;
if (isequal == false)
{
qqq = i + width + 1;
kkk = j + height + 1;
}
}
}
if (isequal==true)
{
MidX = i;
MidY = j;
return;
}
}
}
MidX = -1;
MidY = -1;
return;
}
definition of arrays (added because of request) , This is before functions execute :
PI = new unsigned int**[SCREEN_WIDTH];
for (int i=0;i<SCREEN_WIDTH;++i)
PI[i] = new unsigned int*[SCREEN_HEIGHT];
for (int i=0;i<SCREEN_WIDTH;++i)
for (int j=0;j<SCREEN_HEIGHT;++j)
PI[i][j] = new unsigned int[3];
NPI = new unsigned int**[SCREEN_WIDTH];
for (int i=0;i<SCREEN_WIDTH;++i)
NPI[i] = new unsigned int*[SCREEN_HEIGHT];
for (int i=0;i<SCREEN_WIDTH;++i)
for (int j=0;j<SCREEN_HEIGHT;++j)
NPI[i][j] = new unsigned int[3];
The First function executes then the second. and sorry for some bad programming because I did thousands of changes to make it work!

PI[qq][kk][0] = data[kk * width + qq];
From how PI and NPI are filled in, it appears that they are 3-dimensional arrays (it would help if you included their definition in the code sample). But
if (PI[qqq][kkk]!=NPI[qqq-i][kkk-j]) isequal = false;
which is only indexing 2 dimensions of each. PI[a][b] is the address of the array containing PI[a][b][0..2] and will certainly never match the address of NPI[x][y], so this statement is always returning false I expect.

Lets get you started. Here is a better LoadBMP.
Yours, among other thing, read the size, and uses SCREEN_HEIGHT anyway.
Using this for loading both images is probably easier.
#include <vector>
#include <cstdio>
#include <string>
using namespace std;
typedef unsigned char UC;
struct RGB { UC r,g,b; };
bool operator == ( const RGB& p1, const RGB& p2 ) { return p1.r==p2.r && p1.g==p2.g && p1.b==p2.b; }
struct BMP
{
int width;
int height;
vector<RGB> pixels;
RGB& Pix(int x,int y) { return pixels[ y*width + x ]; }
};
void LoadBMP( BMP& bmp, const char* filename )
{
FILE* f = fopen(filename, "rb");
UC info[54];
fread(info, 1, 54, f); // read the 54-byte header
// extract image height and width from header
bmp.width = *(int*) (info+18);
bmp.height = *(int*) (info+22);
// scanlines are always multiple of 4, padded with 0-3 bytes
int scanlinesize = 3*bmp.width;
while( scanlinesize % 4 ) ++scanlinesize;
int size = scanlinesize * bmp.height;
UC* data = new UC[size];
fread(data, 1, size, f);
fclose(f);
bmp.pixels.clear();
bmp.pixels.reserve(bmp.height*bmp.width);
for(int yy=0;yy<bmp.height;++yy)
{
UC* p = data+scanlinesize*yy;
for (int xx=0;xx<bmp.width;++xx)
{
RGB rgb;
rgb.b = *p++;
rgb.g = *p++;
rgb.r = *p++;
bmp.pixels.push_back(rgb);
}
}
delete[] data;
}

Related

How to correct horizontal flip function to prevent it from altering images' colors

stb_image.h provides a method to flip an image vertically and it works fine. I tried to implement an horizontal flip aka mirror but it alters the image colors.
On pictures that only have 3 colors you could get bluish or reddish or even magenta colored pictures instead of their actual colors. It's the same if we're talking about JPEG or PNG images, you get the same strange results. Curiously if you flip that very same image vertically, its colors look normal.
I've tried testing pretty much any function you could find here and the code I'm providing you with has been the only one that got me close to my actual goal.
// Function I've been trying to implement to enable Horizontal Flip
static void stbi_horizontal_flip(void *image, int w, int h, int bytes_per_pixel)
{
size_t line_bytes = (size_t)w * bytes_per_pixel;
stbi_uc temp[line_bytes];
stbi_uc *bytes = (stbi_uc *)image;
Debug() << line_bytes;
for (int col = 0; col < h; col++) {
stbi_uc *line = bytes + col * line_bytes;
memcpy(&temp, line, line_bytes);
for (int row = 0; row < line_bytes; row++) {
line[row] = temp[line_bytes - row - bytes_per_pixel];
}
}
stbi_horizontally_flip_on_load = false;
}
// stb_image's function for Vertical Flip
static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel)
{
int row;
size_t bytes_per_row = (size_t)w * bytes_per_pixel;
stbi_uc temp[2048];
stbi_uc *bytes = (stbi_uc *)image;
for (row = 0; row < (h>>1); row++) {
stbi_uc *row0 = bytes + row * bytes_per_row;
stbi_uc *row1 = bytes + (h - row - 1) * bytes_per_row;
size_t bytes_left = bytes_per_row;
while (bytes_left) {
size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp);
memcpy(temp, row0, bytes_copy);
memcpy(row0, row1, bytes_copy);
memcpy(row1, temp, bytes_copy);
row0 += bytes_copy;
row1 += bytes_copy;
bytes_left -= bytes_copy;
}
}
}
static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)
{
stbi__result_info ri;
void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8);
if (result == NULL) return NULL;
if (ri.bits_per_channel != 8) {
STBI_ASSERT(ri.bits_per_channel == 16);
result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
ri.bits_per_channel = 8;
}
// #TODO: move stbi__convert_format to here
if (stbi_horizontally_flip_on_load) {
int channels = req_comp ? req_comp : *comp;
stbi_horizontal_flip(result, *x, *y, channels * sizeof(stbi_uc));
}
if (stbi__vertically_flip_on_load) {
int channels = req_comp ? req_comp : *comp;
stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc));
}
return (unsigned char *) result;
}
STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
{
unsigned char *result;
stbi__context s;
stbi__start_file(&s,f);
result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
if (result) {
// need to 'unget' all the characters in the IO buffer
fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
}
return result;
}
STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)
{
FILE *f = stbi__fopen(filename, "rb");
unsigned char *result;
if (!f) return stbi__errpuc("can't fopen", "Unable to open file");
result = stbi_load_from_file(f,x,y,comp,req_comp);
fclose(f);
return result;
}
STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
{
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
}
void Gosu::load_image_file(Gosu::Bitmap& bitmap, const string& filename)
{
Buffer buffer;
load_file(buffer, filename);
load_image_file(bitmap, buffer.front_reader());
}
void Gosu::load_image_file(Gosu::Bitmap& bitmap, Reader input)
{
bool needs_color_key = is_bmp(input);
stbi_io_callbacks callbacks;
callbacks.read = read_callback;
callbacks.skip = skip_callback;
callbacks.eof = eof_callback;
int x, y, n;
stbi_uc* bytes = stbi_load_from_callbacks(&callbacks, &input, &x, &y, &n, STBI_rgb_alpha);
if (bytes == nullptr) {
throw runtime_error("Cannot load image: " + string(stbi_failure_reason()));
}
bitmap.resize(x, y);
printf("Channels %d, Gosu Color size %d, unsigned char size %d, bytes array size %d",
n, sizeof(Gosu::Color), sizeof(stbi_uc), sizeof(bytes));
// Output: Channels 3 or 4, Gosu Color size 4, unsigned char size 1, bytes array 8
memcpy(bitmap.data(), bytes, x * y * sizeof(Gosu::Color));
stbi_image_free(bytes);
if (needs_color_key) apply_color_key(bitmap, Gosu::Color::FUCHSIA);
}```
// Output: Channels 3 or 4, Gosu Color size 4, unsigned char size 1, bytes array 8
That is what I got back from stb_image, but I'd prefer to get an 8bit array instead. Even so what actually matters is to get rid of that unexpected color change.
Thanks to Igor's comment I could focus on my immediate problem and not long after I came up with the code I've posted below.
What I've been wondering since I finally could flip the images horizontally was why the other methods I found either on the web or as part of image processors' code didn't work as expected. O_o? Sometimes I copied and pasted them only changing some variables' names or types to match stb_image's and they still failed either to compile or display a decent result.
By the way, I tried before to subtract positions to the right value to no avail but it made me think some of them could be used as nice color blend effects. XD
// Horizontal Flip by Kyonides Arkanthes shared under GPLv2 or v3
static void stbi_kyon_horizontal_flip(void *image, int w, int h, int bytes_per_pixel)
{
size_t line_bytes = (size_t)w * bytes_per_pixel;
stbi_uc temp[line_bytes];
stbi_uc *bytes = (stbi_uc *)image;
int lpos, rpos;
for (int col = 0; col < h; col++) {
stbi_uc *line = bytes + col * line_bytes;
memcpy(&temp, line, line_bytes);
for (int row = 0; row < w; row++) {
lpos = row * bytes_per_pixel;
rpos = line_bytes - row * bytes_per_pixel - 1;
line[lpos] = temp[rpos - 3];
line[lpos + 1] = temp[rpos - 2];
line[lpos + 2] = temp[rpos - 1];
line[lpos + 3] = temp[rpos];
}
}
stbi_kyon_horizontally_flip_on_load = false;
}```
You just reversed the order of RGBA, you try to use this, I tested, the effect is normal.
for (int row = 0; row < Qimg2.width(); row++) {
lpos = row * bytes_per_pixel;
rpos = line_bytes - row * bytes_per_pixel - 1;
line[lpos] = temp[rpos - 2];
line[lpos + 1] = temp[rpos - 1];
line[lpos + 2] = temp[rpos - 3];
line[lpos + 3] = temp[rpos];
}

C++: BMP rotate image

Ok guys, it's the third time I'm posting the same question (previous are here and here).
Now at this time I will try to explain what's my problem:
So first them all, I need to rotate a .bmp image and it's not rotate correctly. But I don't need to rotate a random image with extension .bmp, I need to rotate this one. I've tried with many other images and all of them was rotated correctly, except mine.
In this moment my code it works just for 180-degree, how could make it to works on any degree which is multiple of 90-degree (I need to rotate my image just with 90, 180 or 270 degrees, not more).
I don't need any kind of external library for this code like CImage, OpenCV, ImageMagik and so on... I need to make this code to work.
So yeh, that's it. And here you can find my actual result.
CODE:
#include <array>
using namespace std;
struct BMP {
int width;
int height;
unsigned char header[54];
unsigned char *pixels;
int row_padded;
int size_padded;
};
void writeBMP(string filename, BMP image) {
string fileName = "Output Files\\" + filename;
FILE *out = fopen(fileName.c_str(), "wb");
fwrite(image.header, sizeof(unsigned char), 54, out);
unsigned char tmp;
for (int i = 0; i < image.height; i++) {
for (int j = 0; j < image.width * 3; j += 3) {
//Convert(B, G, R) to(R, G, B)
tmp = image.pixels[j];
image.pixels[j] = image.pixels[j + 2];
image.pixels[j + 2] = tmp;
}
}
fwrite(image.pixels, sizeof(unsigned char), image.size_padded, out);
fclose(out);
}
BMP readBMP(string filename) {
BMP image;
string fileName = "Input Files\\" + filename;
FILE *in = fopen(fileName.c_str(), "rb");
fread(image.header, sizeof(unsigned char), 54, in); // read the 54-byte header
// extract image height and width from header
image.width = *(int *) &image.header[18];
image.height = *(int *) &image.header[22];
image.row_padded = (image.width * 3 + 3) & (~3); // ok size of a single row rounded up to multiple of 4
image.size_padded = image.row_padded * image.height; // padded full size
image.pixels = new unsigned char[image.size_padded]; // yeah !
if (fread(image.pixels, sizeof(unsigned char), image.size_padded, in) == image.size_padded) {
unsigned char tmp;
for (int i = 0; i < image.height; i++) {
for (int j = 0; j < image.width * 3; j += 3) {
//Convert (B, G, R) to (R, G, B)
tmp = image.pixels[j];
image.pixels[j] = image.pixels[j + 2];
image.pixels[j + 2] = tmp;
}
}
}
fclose(in);
return image;
}
BMP rotate(BMP image, double degree) {
BMP newImage = image;
unsigned char *pixels = new unsigned char[image.size_padded];
int height = image.height;
int width = image.width;
for (int x = 0; x < height; x++) {
for (int y = 0; y < width; y++) {
pixels[(x * width + y) * 3 + 0] = image.pixels[((height - 1 - x) * width + (width - 1 - y)) * 3 + 0];
pixels[(x * width + y) * 3 + 1] = image.pixels[((height - 1 - x) * width + (width - 1 - y)) * 3 + 1];
pixels[(x * width + y) * 3 + 2] = image.pixels[((height - 1 - x) * width + (width - 1 - y)) * 3 + 2];
}
}
newImage.pixels = pixels;
return newImage;
}
int main() {
BMP image = readBMP("Input-1.bmp");
image = rotate(image, 180);
writeBMP("Output.bmp", image);
return 0;
}
You have major memory leak. pixels = new unsigned char[size]; must be freed otherwise there is potentially several megabytes leak with every rotation. You have to rewrite the function to keep track of memory allocations.
When you rotate the image by 90 or 270 of the image, the widht/height of image changes. The size may change too because of padding. The new dimension has to be recorded in header file.
In C++ you can use fopen, but std::fstream is preferred.
Here is an example which works in Windows for 24bit images only. In Big-endian systems you can't use memcpy the way I used it below.
Note, this is for practice only. As #datenwolf explained you should use a library for real applications. Most standard libraries such Windows GDI library (basic drawing functions) offer solution for these common tasks.
#include <iostream>
#include <fstream>
#include <string>
#include <Windows.h>
bool rotate(char *src, char *dst, BITMAPINFOHEADER &bi, int angle)
{
//In 24bit image, the length of each row must be multiple of 4
int padw = 4 - ((bi.biWidth * 3) % 4);
if(padw == 4) padw = 0;
int padh = 4 - ((bi.biHeight * 3) % 4);
if(padh == 4) padh = 0;
int pad2 = 0;
if(padh == 1 || padh == 3) pad2 = 2;
bi.biHeight += padh;
int w = bi.biWidth;
int h = bi.biHeight;
if(angle == 90 || angle == 270)
{
std::swap(bi.biWidth, bi.biHeight);
}
else
{
bi.biHeight -= padh;
}
for(int row = 0; row < h; row++)
{
for(int col = 0; col < w; col++)
{
int n1 = 3 * (col + w * row) + padw * row;
int n2 = 0;
switch(angle)
{
case 0: n2 = 3 * (col + w * row) + padw * row; break;
case 90: n2 = 3 * ((h - row - 1) + h * col) + pad2 * col; break;
case 180: n2 = 3 * (col + w * (h - row - 1)) + padw * (h - row - 1); break;
case 270: n2 = 3 * (row + h * col) + pad2 * col; break;
}
dst[n2 + 0] = src[n1 + 0];
dst[n2 + 1] = src[n1 + 1];
dst[n2 + 2] = src[n1 + 2];
}
}
for(int row = 0; row < bi.biHeight; row++)
for(int col = 0; col < padw; col++)
dst[bi.biWidth * 3 + col] = 0;
bi.biSizeImage = (bi.biWidth + padw) * bi.biHeight * 3;
return true;
}
int main()
{
std::string input = "input.bmp";
std::string output = "output.bmp";
BITMAPFILEHEADER bf = { 0 };
BITMAPINFOHEADER bi = { sizeof(BITMAPINFOHEADER) };
std::ifstream fin(input, std::ios::binary);
if(!fin) return 0;
fin.read((char*)&bf, sizeof(bf));
fin.read((char*)&bi, sizeof(bi));
int size = 3 * (bi.biWidth + 3) * (bi.biHeight + 3);
char *src = new char[size];
char *dst = new char[size];
fin.read(src, bi.biSizeImage);
//use 0, 90, 180, or 270 for the angle
if(rotate(src, dst, bi, 270))
{
bf.bfSize = 54 + bi.biSizeImage;
std::ofstream fout(output, std::ios::binary);
fout.write((char*)&bf, 14);
fout.write((char*)&bi, 40);
fout.write((char*)dst, bi.biSizeImage);
}
delete[]src;
delete[]dst;
return 0;
}
The BMP file format is a complicated, convoluted beast and there's no such thing as a "simple" BMP file reader. The code you have there makes certain hard coded assumptions on the files you're trying to read (24bpp true color, tightly packed, no compression) that it will flat (on its face) when it encounters anything that isn't that specific format. Unfortunately, for you, the majority of BMP files out there is not of that kind. To give you an idea of what a fully conforming BMP reader must support have a look at this page:
http://entropymine.com/jason/bmpsuite/bmpsuite/html/bmpsuite.html
And the code you have up there does not even check if there's a valid file magic bytes signature and if the header is valid. So that's your problem right there: You don't have a BMP file reader. You have something that actually spits out pixels if you're lucky enough the feed it something that by chance happens to be in the right format.

Resize volume file length and width in C++

I have a volume file that has a variable slice length, image height of 640, and image width of 512. I want to convert it to a file with a height of 620 and a width of 420 and write it to a new file. I want to take the 20 pixels off the top of the height and take 46 off of each side of the width to do this resizing. Each pixel is unsigned 16 bits. I'm not sure how to do the actual manipulation.
This is what I have in my main:
FILE* pfile = NULL;
FILE* pfile2 = NULL;
pFile = fopen("test.vol", "rb");
fseek (pFile , 0 , SEEK_END);
int fileData = ftell(pFile);
rewind(pFile);
char* FileBuffer = new char[fileData];
fread(FileBuffer, fileData, 1, pFile);
int height = 640;
int width = 512;
int slizeSize = height * width * 2;
int sliceCount = fileData / sliceSize;
uint16_t pixels;
int newHeight = height - 20;
int newWidth = width - 92;
int newSliceSize = newHeight * newWidth * 2;
int newImageSize = newSliceSize * sliceCount;
char* NewFileBuffer = new char[newImageSize];
for (int i = 0; i < newHeight; i++) {
for (int i = 0; i < newWidth; i++) {
}
// need help in these for loops and after
}
fclose (pFile);
free (FileBuffer);
pFile2 = fopen("test2.vol", "wb");
fwrite(NewFileBuffer, NewImageSize, 1, pFile2);
If you're programming in C++, use C++: get rid of FILE*and other C stuff.
You're allocating an array of char with new, but call free to release memory: you should call delete[], or even better you should use std::vector and let it manage memory for you.
You're dealing with 16 bits values, so why not reading/writing/processing 16 bits values?
First, read the file:
#include <fstream>
#include <iterator>
#include <vector>
int main()
{
using data_type = uint16_t;
std::basic_ifstream<data_type> file_in{
"test.vol",
std::ifstream::binary
};
std::vector<data_type> FileBuffer{
std::istreambuf_iterator<data_type>(file_in),
std::istreambuf_iterator<data_type>() // end of stream iterator
};
Now FileBuffer is an array of uint16_t filled with the content of "test.vol", with managed memory allocation and deallocation.
Next part, resize the volume. I assume your data are packed in the following order: columns, lines, slices. It is unclear if the top are the first, or the last lines of a slice. Here is my proposition using iterators and std::copy:
size_t height = 640, width = 512;
size_t sliceSize = height * width;
size_t sliceCount = FileBuffer.size() / sliceSize;
size_t newHeight = height - 20, newWidth = width - 92;
size_t newSliceSize = newHeight * newWidth;
size_t newImageSize = newSliceSize * sliceCount;
std::vector<data_type> NewFileBuffer(newImageSize);
auto it_buffer = FileBuffer.begin();
auto it_new_buffer = NewFileBuffer.begin();
for (size_t i = 0; i < sliceCount; ++i)
{
// skip 20 first lines, remove and uncomment the next line
// if you want to skip the last ones
auto it_line = it_buffer + 20*width;
//auto it_line = it_buffer;
for (size_t j = 0; j < newHeight; ++j)
{
auto it_column = it_line + 46;
it_new_buffer = std::copy(
it_column,
it_column + newWidth,
it_new_buffer
);
it_line += width; // go to next line
}
it_buffer += sliceSize; // go to next slice
}
Finally, write the file:
std::basic_ofstream<data_type> file_out{
"test2.vol",
std::ofstream::binary
};
std::copy(
NewFileBuffer.begin(),
NewFileBuffer.end(),
std::ostreambuf_iterator<data_type>(file_out)
);
}

LibQREncode qrcode to BMP

I'm creating a qrcode with the library qrencode.h
This creation is working nice but how would one output the qrcode to a BMP file within c++?
At this very moment i have this code:
const char* szSourceSring = QRCODE_TEXT;
unsigned int unWidth, x, y, l, n, unWidthAdjusted, unDataBytes;
unsigned char* pRGBData, *pSourceData, *pDestData;
QRcode* pQRC;
FILE* f;
if (pQRC = QRcode_encodeString(szSourceSring, 4, QR_ECLEVEL_H, QR_MODE_8, 1))
{
unWidth = pQRC->width;
unWidthAdjusted = unWidth * OUT_FILE_PIXEL_PRESCALER * 3;
if (unWidthAdjusted % 4)
unWidthAdjusted = (unWidthAdjusted / 4 + 1) * 4;
unDataBytes = unWidthAdjusted * unWidth * OUT_FILE_PIXEL_PRESCALER;
// Allocate pixels buffer
if (!(pRGBData = (unsigned char*)malloc(unDataBytes)))
{
printf("Out of memory");
}
// Preset to white
memset(pRGBData, 0xff, unDataBytes);
// Prepare bmp headers
BITMAPFILEHEADER kFileHeader;
kFileHeader.bfType = 0x4D42; // "BM"
kFileHeader.bfSize = sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER) +
unDataBytes;
kFileHeader.bfReserved1 = 0;
kFileHeader.bfReserved2 = 0;
kFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER);
BITMAPINFOHEADER kInfoHeader;
kInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
kInfoHeader.biWidth = unWidth * OUT_FILE_PIXEL_PRESCALER;
kInfoHeader.biHeight = -((int)unWidth * OUT_FILE_PIXEL_PRESCALER);
kInfoHeader.biPlanes = 1;
kInfoHeader.biBitCount = 24;
kInfoHeader.biCompression = BI_RGB;
kInfoHeader.biSizeImage = 0;
kInfoHeader.biXPelsPerMeter = 0;
kInfoHeader.biYPelsPerMeter = 0;
kInfoHeader.biClrUsed = 0;
kInfoHeader.biClrImportant = 0;
// Convert QrCode bits to bmp pixels
pSourceData = pQRC->data;
for(y = 0; y < unWidth; y++)
{
pDestData = pRGBData + unWidthAdjusted * y * OUT_FILE_PIXEL_PRESCALER;
for(x = 0; x < unWidth; x++)
{
if (*pSourceData & 1)
{
for(l = 0; l < OUT_FILE_PIXEL_PRESCALER; l++)
{
for(n = 0; n < OUT_FILE_PIXEL_PRESCALER; n++)
{
*(pDestData + n * 3 + unWidthAdjusted * l) = PIXEL_COLOR_B;
*(pDestData + 1 + n * 3 + unWidthAdjusted * l) = PIXEL_COLOR_G;
*(pDestData + 2 + n * 3 + unWidthAdjusted * l) = PIXEL_COLOR_R;
}
}
}
pDestData += 3 * OUT_FILE_PIXEL_PRESCALER;
pSourceData++;
}
}
// Output the bmp file
/*if (((f = fopen(OUT_FILE, "r")) != NULL))
{*/
f = fopen(OUT_FILE, "wb");
fwrite(&kFileHeader, sizeof(BITMAPFILEHEADER), 14, f);
fwrite(&kInfoHeader, sizeof(BITMAPINFOHEADER), 40, f);
fwrite(pRGBData, sizeof(unsigned char), unDataBytes, f);
fclose(f);
/* }
else
{
printf("Unable to open file");
}
*/
// Free data
free(pRGBData);
QRcode_free(pQRC);
}
else
{
printf("NULL returned");
}
But somehow this creates a BMP with corrupt headers. Whenever i'm opening the bmp file it says:
"BMP Image has unsupported header size"
What am i doing wrong?
And is it possible to save to png instead of BMP?
I have access to the libPNG library
Here is a code example which dumps a 24 bpp bmp file created from a QR-Code. The error you see is probably not caused by the QR-Code library, but rather something in the bmp file code.
The bmp file created by this example works fine with the image viewer packaged with my Windows 8.1. If you also do not see the error, you could check for differences in each binary output to pinpoint the problem. If you want.
This question is tagged "C++" and "C++11", so this example uses the C++ std library for file output, and doesn't use malloc. (But almost equally bad -- I use new and delete in some container code, where a std::vector member is preferred...don't tell anyone). Also, this example writes each piece of data directly to the file, instead of using a file-sized intermediate buffer, like pDestData.
#include <iostream>
#include <fstream>
// A fake (or "somewhat limited") QR Code data container
struct Qrc {
int dimsize; // the width and height
unsigned char* data; // buffer which contains the elements
Qrc() {
static const unsigned int bin[] = { // encodes an important secret message
0xfc8b7d7f,0xa801a83,0xd6e54d76,0xaa9eb2ed,0x43ed05db,0xb8786837,0x55555fe0,
0x5a4c807f,0xcf315c00,0x6e8019ce,0xc7819e0d,0xd4857ba8,0x4ac5e347,0xf6f349ba,
0xd433ccdd,0x2998361e,0x4453fab3,0x526d9085,0x81f38924,0xb4da0811,0x84b3131a,
0x9639915e,0x3b74a4ff,0x42aa0c11,0x4127be16,0x1f4350,0xff620296,0xad54de1,
0xd38c2272,0xa3f76155,0x5366a7ab,0x9bdd2257,0x300d5520,0x85842e7f,0 };
dimsize = 33;
data = new unsigned char[dimsize * dimsize];
auto p = data;
auto endp = p + dimsize * dimsize;
for(unsigned int b : bin) {
for(int i=0; i<32; ++i) {
if(p == endp) break;
*(p++) = b & (1 << i) ? 255 : 0;
} } }
Qrc(const Qrc&) = delete;
Qrc& operator = (const Qrc&) = delete;
~Qrc() { delete [] data; }
};
struct BIH { // a private definition of BITMAPINFOHEADER
unsigned int sz;
int width, height;
unsigned short planes;
short bits;
unsigned int compress, szimage;
int xppm, yppm;
unsigned int clrused, clrimp;
};
void SaveBmp(const char* filename, const Qrc& qrc) {
// Asker's Qrc struct delivered as a pointer, from a C API, but this example doesn't mimic that.
std::ofstream ofs(filename, std::ios_base::out | std::ios_base::binary);
if(!ofs) {
std::cout << "Writing " << filename << " failed\n";
return;
}
const int side_len = qrc.dimsize; // width and height of the (square) QR Code
const int pixel_side_len = 4; // QRC element's size in the bmp image (in pixels)
const int bmp_line_bytes = side_len * pixel_side_len * 3;
const int bmp_line_pad_bytes = (4 - bmp_line_bytes % 4) % 4; // bmp line data padding size
const int bmp_data_size = side_len * (bmp_line_bytes + bmp_line_pad_bytes);
BIH bih = { sizeof(bih) };
bih.width = side_len * pixel_side_len; // element count * element size
bih.height = -side_len * pixel_side_len; // negative height => data begins at top of image
bih.planes = 1;
bih.bits = 24;
const int header_size = sizeof(bih) + 14; // size of the bmp file header
const int filesize = header_size + bmp_data_size; // size of the whole file
ofs.write("BM", 2);
ofs.write(reinterpret_cast<const char*>(&filesize), 4);
ofs.write("\0\0\0\0", 4); // 2x 16-bit reserved fields
ofs.write(reinterpret_cast<const char*>(&header_size), 4);
ofs.write(reinterpret_cast<const char*>(&bih), sizeof(bih));
// pixel colors, as Blue, Green, Red char-valued triples
// the terminating null also makes these usable as 32bpp BGRA values, with Alpha always 0.
static const char fg_color[] = "\0\0\0";
static const char bg_color[] = "\xff\xff\xff";
auto pd = qrc.data;
// send pixel data directly to the bmp file
// QRC elements are expanded into squares
// whose sides are "pixel_side_len" in length.
for(int y=0; y<side_len; ++y) {
for(int j=0; j<pixel_side_len; ++j) {
auto pdj = pd;
for(int x=0; x<side_len; ++x) {
for(int i=0; i<pixel_side_len; ++i) {
// *pdj will be 0 or 255 (from "fake" Qrc)
// Using "*pdj & 1" here, just to match asker's code
// without knowing why this was done.
ofs.write(*pdj & 1 ? fg_color : bg_color, 3);
}
++pdj;
}
if(bmp_line_pad_bytes) {
ofs.write("\0\0\0", bmp_line_pad_bytes);
}
}
pd += side_len;
}
}
int main() {
SaveBmp("MyQrCode.bmp", Qrc());
}

Writing BMP image in pure c/c++ without other libraries

In my algorithm, I need to create an information output. I need to write a boolean matrix into a bmp file.
It must be a monocromic image, where pixels are white if the matrix on such element is true.
Main problem is the bmp header and how to write this.
See if this works for you...
In this code, I had 3 2-dimensional arrays, called red,green and blue. Each one was of size [width][height], and each element corresponded to a pixel - I hope this makes sense!
FILE *f;
unsigned char *img = NULL;
int filesize = 54 + 3*w*h; //w is your image width, h is image height, both int
img = (unsigned char *)malloc(3*w*h);
memset(img,0,3*w*h);
for(int i=0; i<w; i++)
{
for(int j=0; j<h; j++)
{
x=i; y=(h-1)-j;
r = red[i][j]*255;
g = green[i][j]*255;
b = blue[i][j]*255;
if (r > 255) r=255;
if (g > 255) g=255;
if (b > 255) b=255;
img[(x+y*w)*3+2] = (unsigned char)(r);
img[(x+y*w)*3+1] = (unsigned char)(g);
img[(x+y*w)*3+0] = (unsigned char)(b);
}
}
unsigned char bmpfileheader[14] = {'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0};
unsigned char bmpinfoheader[40] = {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0};
unsigned char bmppad[3] = {0,0,0};
bmpfileheader[ 2] = (unsigned char)(filesize );
bmpfileheader[ 3] = (unsigned char)(filesize>> 8);
bmpfileheader[ 4] = (unsigned char)(filesize>>16);
bmpfileheader[ 5] = (unsigned char)(filesize>>24);
bmpinfoheader[ 4] = (unsigned char)( w );
bmpinfoheader[ 5] = (unsigned char)( w>> 8);
bmpinfoheader[ 6] = (unsigned char)( w>>16);
bmpinfoheader[ 7] = (unsigned char)( w>>24);
bmpinfoheader[ 8] = (unsigned char)( h );
bmpinfoheader[ 9] = (unsigned char)( h>> 8);
bmpinfoheader[10] = (unsigned char)( h>>16);
bmpinfoheader[11] = (unsigned char)( h>>24);
f = fopen("img.bmp","wb");
fwrite(bmpfileheader,1,14,f);
fwrite(bmpinfoheader,1,40,f);
for(int i=0; i<h; i++)
{
fwrite(img+(w*(h-i-1)*3),3,w,f);
fwrite(bmppad,1,(4-(w*3)%4)%4,f);
}
free(img);
fclose(f);
Clean C Code for Bitmap (BMP) Image Generation
This code does not use any library other than stdio.h. So, it can be easily incorporated in other languages of C-Family, like- C++, C#, Java.
#include <stdio.h>
const int BYTES_PER_PIXEL = 3; /// red, green, & blue
const int FILE_HEADER_SIZE = 14;
const int INFO_HEADER_SIZE = 40;
void generateBitmapImage(unsigned char* image, int height, int width, char* imageFileName);
unsigned char* createBitmapFileHeader(int height, int stride);
unsigned char* createBitmapInfoHeader(int height, int width);
int main ()
{
int height = 361;
int width = 867;
unsigned char image[height][width][BYTES_PER_PIXEL];
char* imageFileName = (char*) "bitmapImage.bmp";
int i, j;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
image[i][j][2] = (unsigned char) ( i * 255 / height ); ///red
image[i][j][1] = (unsigned char) ( j * 255 / width ); ///green
image[i][j][0] = (unsigned char) ( (i+j) * 255 / (height+width) ); ///blue
}
}
generateBitmapImage((unsigned char*) image, height, width, imageFileName);
printf("Image generated!!");
}
void generateBitmapImage (unsigned char* image, int height, int width, char* imageFileName)
{
int widthInBytes = width * BYTES_PER_PIXEL;
unsigned char padding[3] = {0, 0, 0};
int paddingSize = (4 - (widthInBytes) % 4) % 4;
int stride = (widthInBytes) + paddingSize;
FILE* imageFile = fopen(imageFileName, "wb");
unsigned char* fileHeader = createBitmapFileHeader(height, stride);
fwrite(fileHeader, 1, FILE_HEADER_SIZE, imageFile);
unsigned char* infoHeader = createBitmapInfoHeader(height, width);
fwrite(infoHeader, 1, INFO_HEADER_SIZE, imageFile);
int i;
for (i = 0; i < height; i++) {
fwrite(image + (i*widthInBytes), BYTES_PER_PIXEL, width, imageFile);
fwrite(padding, 1, paddingSize, imageFile);
}
fclose(imageFile);
}
unsigned char* createBitmapFileHeader (int height, int stride)
{
int fileSize = FILE_HEADER_SIZE + INFO_HEADER_SIZE + (stride * height);
static unsigned char fileHeader[] = {
0,0, /// signature
0,0,0,0, /// image file size in bytes
0,0,0,0, /// reserved
0,0,0,0, /// start of pixel array
};
fileHeader[ 0] = (unsigned char)('B');
fileHeader[ 1] = (unsigned char)('M');
fileHeader[ 2] = (unsigned char)(fileSize );
fileHeader[ 3] = (unsigned char)(fileSize >> 8);
fileHeader[ 4] = (unsigned char)(fileSize >> 16);
fileHeader[ 5] = (unsigned char)(fileSize >> 24);
fileHeader[10] = (unsigned char)(FILE_HEADER_SIZE + INFO_HEADER_SIZE);
return fileHeader;
}
unsigned char* createBitmapInfoHeader (int height, int width)
{
static unsigned char infoHeader[] = {
0,0,0,0, /// header size
0,0,0,0, /// image width
0,0,0,0, /// image height
0,0, /// number of color planes
0,0, /// bits per pixel
0,0,0,0, /// compression
0,0,0,0, /// image size
0,0,0,0, /// horizontal resolution
0,0,0,0, /// vertical resolution
0,0,0,0, /// colors in color table
0,0,0,0, /// important color count
};
infoHeader[ 0] = (unsigned char)(INFO_HEADER_SIZE);
infoHeader[ 4] = (unsigned char)(width );
infoHeader[ 5] = (unsigned char)(width >> 8);
infoHeader[ 6] = (unsigned char)(width >> 16);
infoHeader[ 7] = (unsigned char)(width >> 24);
infoHeader[ 8] = (unsigned char)(height );
infoHeader[ 9] = (unsigned char)(height >> 8);
infoHeader[10] = (unsigned char)(height >> 16);
infoHeader[11] = (unsigned char)(height >> 24);
infoHeader[12] = (unsigned char)(1);
infoHeader[14] = (unsigned char)(BYTES_PER_PIXEL*8);
return infoHeader;
}
Without the use of any other library you can look at the BMP file format. I've implemented it in the past and it can be done without too much work.
Bitmap-File Structures
Each bitmap file contains a
bitmap-file header, a
bitmap-information header, a color
table, and an array of bytes that
defines the bitmap bits. The file has
the following form:
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
RGBQUAD aColors[];
BYTE aBitmapBits[];
... see the file format for more details
this is a example code copied from
https://en.wikipedia.org/wiki/User:Evercat/Buddhabrot.c
void drawbmp (char * filename) {
unsigned int headers[13];
FILE * outfile;
int extrabytes;
int paddedsize;
int x; int y; int n;
int red, green, blue;
extrabytes = 4 - ((WIDTH * 3) % 4); // How many bytes of padding to add to each
// horizontal line - the size of which must
// be a multiple of 4 bytes.
if (extrabytes == 4)
extrabytes = 0;
paddedsize = ((WIDTH * 3) + extrabytes) * HEIGHT;
// Headers...
// Note that the "BM" identifier in bytes 0 and 1 is NOT included in these "headers".
headers[0] = paddedsize + 54; // bfSize (whole file size)
headers[1] = 0; // bfReserved (both)
headers[2] = 54; // bfOffbits
headers[3] = 40; // biSize
headers[4] = WIDTH; // biWidth
headers[5] = HEIGHT; // biHeight
// Would have biPlanes and biBitCount in position 6, but they're shorts.
// It's easier to write them out separately (see below) than pretend
// they're a single int, especially with endian issues...
headers[7] = 0; // biCompression
headers[8] = paddedsize; // biSizeImage
headers[9] = 0; // biXPelsPerMeter
headers[10] = 0; // biYPelsPerMeter
headers[11] = 0; // biClrUsed
headers[12] = 0; // biClrImportant
outfile = fopen(filename, "wb");
//
// Headers begin...
// When printing ints and shorts, we write out 1 character at a time to avoid endian issues.
//
fprintf(outfile, "BM");
for (n = 0; n <= 5; n++)
{
fprintf(outfile, "%c", headers[n] & 0x000000FF);
fprintf(outfile, "%c", (headers[n] & 0x0000FF00) >> 8);
fprintf(outfile, "%c", (headers[n] & 0x00FF0000) >> 16);
fprintf(outfile, "%c", (headers[n] & (unsigned int) 0xFF000000) >> 24);
}
// These next 4 characters are for the biPlanes and biBitCount fields.
fprintf(outfile, "%c", 1);
fprintf(outfile, "%c", 0);
fprintf(outfile, "%c", 24);
fprintf(outfile, "%c", 0);
for (n = 7; n <= 12; n++)
{
fprintf(outfile, "%c", headers[n] & 0x000000FF);
fprintf(outfile, "%c", (headers[n] & 0x0000FF00) >> 8);
fprintf(outfile, "%c", (headers[n] & 0x00FF0000) >> 16);
fprintf(outfile, "%c", (headers[n] & (unsigned int) 0xFF000000) >> 24);
}
//
// Headers done, now write the data...
//
for (y = HEIGHT - 1; y >= 0; y--) // BMP image format is written from bottom to top...
{
for (x = 0; x <= WIDTH - 1; x++)
{
red = reduce(redcount[x][y] + COLOUR_OFFSET) * red_multiplier;
green = reduce(greencount[x][y] + COLOUR_OFFSET) * green_multiplier;
blue = reduce(bluecount[x][y] + COLOUR_OFFSET) * blue_multiplier;
if (red > 255) red = 255; if (red < 0) red = 0;
if (green > 255) green = 255; if (green < 0) green = 0;
if (blue > 255) blue = 255; if (blue < 0) blue = 0;
// Also, it's written in (b,g,r) format...
fprintf(outfile, "%c", blue);
fprintf(outfile, "%c", green);
fprintf(outfile, "%c", red);
}
if (extrabytes) // See above - BMP lines must be of lengths divisible by 4.
{
for (n = 1; n <= extrabytes; n++)
{
fprintf(outfile, "%c", 0);
}
}
}
fclose(outfile);
return;
}
drawbmp(filename);
Here is a C++ variant of the code that works for me. Note I had to change the size computation to account for the line padding.
// mimeType = "image/bmp";
unsigned char file[14] = {
'B','M', // magic
0,0,0,0, // size in bytes
0,0, // app data
0,0, // app data
40+14,0,0,0 // start of data offset
};
unsigned char info[40] = {
40,0,0,0, // info hd size
0,0,0,0, // width
0,0,0,0, // heigth
1,0, // number color planes
24,0, // bits per pixel
0,0,0,0, // compression is none
0,0,0,0, // image bits size
0x13,0x0B,0,0, // horz resoluition in pixel / m
0x13,0x0B,0,0, // vert resolutions (0x03C3 = 96 dpi, 0x0B13 = 72 dpi)
0,0,0,0, // #colors in pallete
0,0,0,0, // #important colors
};
int w=waterfallWidth;
int h=waterfallHeight;
int padSize = (4-(w*3)%4)%4;
int sizeData = w*h*3 + h*padSize;
int sizeAll = sizeData + sizeof(file) + sizeof(info);
file[ 2] = (unsigned char)( sizeAll );
file[ 3] = (unsigned char)( sizeAll>> 8);
file[ 4] = (unsigned char)( sizeAll>>16);
file[ 5] = (unsigned char)( sizeAll>>24);
info[ 4] = (unsigned char)( w );
info[ 5] = (unsigned char)( w>> 8);
info[ 6] = (unsigned char)( w>>16);
info[ 7] = (unsigned char)( w>>24);
info[ 8] = (unsigned char)( h );
info[ 9] = (unsigned char)( h>> 8);
info[10] = (unsigned char)( h>>16);
info[11] = (unsigned char)( h>>24);
info[20] = (unsigned char)( sizeData );
info[21] = (unsigned char)( sizeData>> 8);
info[22] = (unsigned char)( sizeData>>16);
info[23] = (unsigned char)( sizeData>>24);
stream.write( (char*)file, sizeof(file) );
stream.write( (char*)info, sizeof(info) );
unsigned char pad[3] = {0,0,0};
for ( int y=0; y<h; y++ )
{
for ( int x=0; x<w; x++ )
{
long red = lround( 255.0 * waterfall[x][y] );
if ( red < 0 ) red=0;
if ( red > 255 ) red=255;
long green = red;
long blue = red;
unsigned char pixel[3];
pixel[0] = blue;
pixel[1] = green;
pixel[2] = red;
stream.write( (char*)pixel, 3 );
}
stream.write( (char*)pad, padSize );
}
Note that the lines are saved from down to up and not the other way around.
Additionally, the scanlines must have a byte-length of multiples of four, you should insert fill bytes at the end of the lines to ensure this.
I just wanted to share an improved version of Minhas Kamal's code because although it worked well enough for most applications, I had a few issues with it still. Two highly important things to remember:
The code (at the time of writing) calls free() on two static arrays. This will cause your program to crash. So I commented out those lines.
NEVER assume that your pixel data's pitch is always (Width*BytesPerPixel). It's best to let the user specify the pitch value. Example: when manipulating resources in Direct3D, the RowPitch is never guaranteed to be an even multiple of the byte depth being used. This can cause errors in your generated bitmaps (especially at odd resolutions such as 1366x768).
Below, you can see my revisions to his code:
const int bytesPerPixel = 4; /// red, green, blue
const int fileHeaderSize = 14;
const int infoHeaderSize = 40;
void generateBitmapImage(unsigned char *image, int height, int width, int pitch, const char* imageFileName);
unsigned char* createBitmapFileHeader(int height, int width, int pitch, int paddingSize);
unsigned char* createBitmapInfoHeader(int height, int width);
void generateBitmapImage(unsigned char *image, int height, int width, int pitch, const char* imageFileName) {
unsigned char padding[3] = { 0, 0, 0 };
int paddingSize = (4 - (/*width*bytesPerPixel*/ pitch) % 4) % 4;
unsigned char* fileHeader = createBitmapFileHeader(height, width, pitch, paddingSize);
unsigned char* infoHeader = createBitmapInfoHeader(height, width);
FILE* imageFile = fopen(imageFileName, "wb");
fwrite(fileHeader, 1, fileHeaderSize, imageFile);
fwrite(infoHeader, 1, infoHeaderSize, imageFile);
int i;
for (i = 0; i < height; i++) {
fwrite(image + (i*pitch /*width*bytesPerPixel*/), bytesPerPixel, width, imageFile);
fwrite(padding, 1, paddingSize, imageFile);
}
fclose(imageFile);
//free(fileHeader);
//free(infoHeader);
}
unsigned char* createBitmapFileHeader(int height, int width, int pitch, int paddingSize) {
int fileSize = fileHeaderSize + infoHeaderSize + (/*bytesPerPixel*width*/pitch + paddingSize) * height;
static unsigned char fileHeader[] = {
0,0, /// signature
0,0,0,0, /// image file size in bytes
0,0,0,0, /// reserved
0,0,0,0, /// start of pixel array
};
fileHeader[0] = (unsigned char)('B');
fileHeader[1] = (unsigned char)('M');
fileHeader[2] = (unsigned char)(fileSize);
fileHeader[3] = (unsigned char)(fileSize >> 8);
fileHeader[4] = (unsigned char)(fileSize >> 16);
fileHeader[5] = (unsigned char)(fileSize >> 24);
fileHeader[10] = (unsigned char)(fileHeaderSize + infoHeaderSize);
return fileHeader;
}
unsigned char* createBitmapInfoHeader(int height, int width) {
static unsigned char infoHeader[] = {
0,0,0,0, /// header size
0,0,0,0, /// image width
0,0,0,0, /// image height
0,0, /// number of color planes
0,0, /// bits per pixel
0,0,0,0, /// compression
0,0,0,0, /// image size
0,0,0,0, /// horizontal resolution
0,0,0,0, /// vertical resolution
0,0,0,0, /// colors in color table
0,0,0,0, /// important color count
};
infoHeader[0] = (unsigned char)(infoHeaderSize);
infoHeader[4] = (unsigned char)(width);
infoHeader[5] = (unsigned char)(width >> 8);
infoHeader[6] = (unsigned char)(width >> 16);
infoHeader[7] = (unsigned char)(width >> 24);
infoHeader[8] = (unsigned char)(height);
infoHeader[9] = (unsigned char)(height >> 8);
infoHeader[10] = (unsigned char)(height >> 16);
infoHeader[11] = (unsigned char)(height >> 24);
infoHeader[12] = (unsigned char)(1);
infoHeader[14] = (unsigned char)(bytesPerPixel * 8);
return infoHeader;
}
I edited ralf's htp code so that it would compile (on gcc, running ubuntu 16.04 lts). It was just a matter of initializing the variables.
int w = 100; /* Put here what ever width you want */
int h = 100; /* Put here what ever height you want */
int red[w][h];
int green[w][h];
int blue[w][h];
FILE *f;
unsigned char *img = NULL;
int filesize = 54 + 3*w*h; //w is your image width, h is image height, both int
if( img )
free( img );
img = (unsigned char *)malloc(3*w*h);
memset(img,0,sizeof(img));
int x;
int y;
int r;
int g;
int b;
for(int i=0; i<w; i++)
{
for(int j=0; j<h; j++)
{
x=i; y=(h-1)-j;
r = red[i][j]*255;
g = green[i][j]*255;
b = blue[i][j]*255;
if (r > 255) r=255;
if (g > 255) g=255;
if (b > 255) b=255;
img[(x+y*w)*3+2] = (unsigned char)(r);
img[(x+y*w)*3+1] = (unsigned char)(g);
img[(x+y*w)*3+0] = (unsigned char)(b);
}
}
unsigned char bmpfileheader[14] = {'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0};
unsigned char bmpinfoheader[40] = {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0};
unsigned char bmppad[3] = {0,0,0};
bmpfileheader[ 2] = (unsigned char)(filesize );
bmpfileheader[ 3] = (unsigned char)(filesize>> 8);
bmpfileheader[ 4] = (unsigned char)(filesize>>16);
bmpfileheader[ 5] = (unsigned char)(filesize>>24);
bmpinfoheader[ 4] = (unsigned char)( w );
bmpinfoheader[ 5] = (unsigned char)( w>> 8);
bmpinfoheader[ 6] = (unsigned char)( w>>16);
bmpinfoheader[ 7] = (unsigned char)( w>>24);
bmpinfoheader[ 8] = (unsigned char)( h );
bmpinfoheader[ 9] = (unsigned char)( h>> 8);
bmpinfoheader[10] = (unsigned char)( h>>16);
bmpinfoheader[11] = (unsigned char)( h>>24);
f = fopen("img.bmp","wb");
fwrite(bmpfileheader,1,14,f);
fwrite(bmpinfoheader,1,40,f);
for(int i=0; i<h; i++)
{
fwrite(img+(w*(h-i-1)*3),3,w,f);
fwrite(bmppad,1,(4-(w*3)%4)%4,f);
}
fclose(f);
The best bitmap encoder is the one you do not write yourself. The file format is a lot more involved, than one might expect. This is evidenced by the fact, that all proposed answers do not create a monochrome (1bpp) bitmap, but rather write out 24bpp files, that happen to only use 2 colors.
The following is a Windows-only solution, using the Windows Imaging Component. It doesn't rely on any external/3rd party libraries, other than what ships with Windows.
Like every C++ program, we need to include several header files. And link to Windowscodecs.lib while we're at it:
#include <Windows.h>
#include <comdef.h>
#include <comip.h>
#include <comutil.h>
#include <wincodec.h>
#include <vector>
#pragma comment(lib, "Windowscodecs.lib")
Next up, we declare our container (a vector, of vectors! Of bool!), and a few smart pointers for convenience:
using _com_util::CheckError;
using container = std::vector<std::vector<bool>>;
_COM_SMARTPTR_TYPEDEF(IWICImagingFactory, __uuidof(IWICImagingFactory));
_COM_SMARTPTR_TYPEDEF(IWICBitmapEncoder, __uuidof(IWICBitmapEncoder));
_COM_SMARTPTR_TYPEDEF(IWICBitmapFrameEncode, __uuidof(IWICBitmapFrameEncode));
_COM_SMARTPTR_TYPEDEF(IWICStream, __uuidof(IWICStream));
_COM_SMARTPTR_TYPEDEF(IWICPalette, __uuidof(IWICPalette));
With that all settled, we can jump right into the implementation. There's a bit of setup required to get a factory, an encoder, a frame, and get everything prepared:
void write_bitmap(wchar_t const* pathname, container const& data)
{
// Create factory
IWICImagingFactoryPtr sp_factory { nullptr };
CheckError(sp_factory.CreateInstance(CLSID_WICImagingFactory, nullptr,
CLSCTX_INPROC_SERVER));
// Create encoder
IWICBitmapEncoderPtr sp_encoder { nullptr };
CheckError(sp_factory->CreateEncoder(GUID_ContainerFormatBmp, nullptr, &sp_encoder));
// Create stream
IWICStreamPtr sp_stream { nullptr };
CheckError(sp_factory->CreateStream(&sp_stream));
CheckError(sp_stream->InitializeFromFilename(pathname, GENERIC_WRITE));
// Initialize encoder with stream
CheckError(sp_encoder->Initialize(sp_stream, WICBitmapEncoderNoCache));
// Create new frame
IWICBitmapFrameEncodePtr sp_frame { nullptr };
IPropertyBag2Ptr sp_properties { nullptr };
CheckError(sp_encoder->CreateNewFrame(&sp_frame, &sp_properties));
// Initialize frame with default properties
CheckError(sp_frame->Initialize(sp_properties));
// Set pixel format
// SetPixelFormat() requires a pointer to non-const
auto pf { GUID_WICPixelFormat1bppIndexed };
CheckError(sp_frame->SetPixelFormat(&pf));
if (!::IsEqualGUID(pf, GUID_WICPixelFormat1bppIndexed))
{
// Report unsupported pixel format
CheckError(WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT);
}
// Set size derived from data argument
auto const width { static_cast<UINT>(data.size()) };
auto const height { static_cast<UINT>(data[0].size()) };
CheckError(sp_frame->SetSize(width, height));
// Set palette on frame. This is required since we use an indexed pixel format.
// Only GIF files support global palettes, so make sure to set it on the frame
// rather than the encoder.
IWICPalettePtr sp_palette { nullptr };
CheckError(sp_factory->CreatePalette(&sp_palette));
CheckError(sp_palette->InitializePredefined(WICBitmapPaletteTypeFixedBW, FALSE));
CheckError(sp_frame->SetPalette(sp_palette));
At that point everything is set up, and we have a frame to dump our data into. For 1bpp files, every byte stores the information of 8 pixels. The left-most pixel is stored in the MSB, with pixels following all the way down to the right-most pixel stored in the LSB.
The code isn't entirely important; you'll be replacing that with whatever suits your needs, when you replace the data layout of your input anyway:
// Write data to frame
auto const stride { (width * 1 + 7) / 8 };
auto const size { height * stride };
std::vector<unsigned char> buffer(size, 127u);
// Convert data to match required layout. Each byte stores 8 pixels, with the
// MSB being the leftmost, the LSB the right-most.
for (size_t x { 0 }; x < data.size(); ++x)
{
for (size_t y { 0 }; y < data[x].size(); ++y)
{
auto shift { x % 8 };
auto mask { 0x80 >> shift };
auto bit { mask * data[x][y] };
auto& value { buffer[y * stride + x / 8] };
value &= ~mask;
value |= bit;
}
}
CheckError(sp_frame->WritePixels(height, stride,
static_cast<UINT>(buffer.size()), buffer.data()));
What's left is to commit the changes to the frame and the encoder, which will ultimately write the image file to disk:
// Commit frame
CheckError(sp_frame->Commit());
// Commit image
CheckError(sp_encoder->Commit());
}
This is a test program, writing out an image to a file passed as the first command-line argument:
#include <iostream>
int wmain(int argc, wchar_t* argv[])
try
{
if (argc != 2)
{
return -1;
}
CheckError(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED));
// Create 64x64 matrix
container data(64, std::vector<bool>(64, false));
// Fill with arrow pointing towards the upper left
for (size_t i { 0 }; i < data.size(); ++i)
{
data[0][i] = true;
data[i][0] = true;
data[i][i] = true;
}
::write_bitmap(argv[1], data);
::CoUninitialize();
}
catch (_com_error const& e)
{
std::wcout << L"Error!\n" << L" Message: " << e.ErrorMessage() << std::endl;
}
It produces the following 64x64 image (true 1bpp, 4096 pixels, 574 bytes in size):
If you get strange colors switches in the middle of your image using the above C++ function. Be sure to open the outstream in binary mode:
imgFile.open(filename, std::ios_base::out | std::ios_base::binary);
Otherwise windows inserts unwanted characters in the middle of your file! (been banging my head on this issue for hours)
See related question here: Why does ofstream insert a 0x0D byte before 0x0A?
Here's a simple c++ bmp image file class.
class bmp_img {
public:
constexpr static int header_size = 14;
constexpr static int info_header_size = 40;
constexpr static size_t bytes_per_pixel = 3;
bmp_img(size_t width, size_t height) :
image_px_width{ width }, image_px_height{ height }, row_width{ image_px_width * bytes_per_pixel },
row_padding{ (4 - row_width % 4) % 4 }, row_stride{ row_width + row_padding }, file_size{ header_size + info_header_size + (image_px_height * row_stride) },
image(image_px_height, std::vector<unsigned char>(row_width))
{
//header file type
file_header[0] = 'B';
file_header[1] = 'M';
//header file size info
file_header[2] = static_cast<unsigned char>(file_size);
file_header[3] = static_cast<unsigned char>(file_size >> 8);
file_header[4] = static_cast<unsigned char>(file_size >> 16);
file_header[5] = static_cast<unsigned char>(file_size >> 24);
//header offset to pixel data
file_header[10] = header_size + info_header_size;
//info header size
info_header[0] = info_header_size;
//info header image width
info_header[4] = static_cast<unsigned char>(image_px_width);
info_header[5] = static_cast<unsigned char>(image_px_width >> 8);
info_header[6] = static_cast<unsigned char>(image_px_width >> 16);
info_header[7] = static_cast<unsigned char>(image_px_width >> 24);
//info header image height
info_header[8] = static_cast<unsigned char>(image_px_height);
info_header[9] = static_cast<unsigned char>(image_px_height >> 8);
info_header[10] = static_cast<unsigned char>(image_px_height >> 16);
info_header[11] = static_cast<unsigned char>(image_px_height >> 24);
//info header planes
info_header[12] = 1;
//info header bits per pixel
info_header[14] = 8 * bytes_per_pixel;
}
size_t width() const {
return image_px_width;
}
size_t height() const {
return image_px_height;
}
void set_pixel(size_t x, size_t y, int r, int g, int b) {
image[y][x * bytes_per_pixel + 2] = r;
image[y][x * bytes_per_pixel + 1] = g;
image[y][x * bytes_per_pixel + 0] = b;
}
void fill(int r, int g, int b) {
for (int y = 0; y < image_px_height; ++y) {
for (int x = 0; x < image_px_width; ++x) {
set_pixel(x, y, r, g, b);
}
}
}
void write_to_file(const char* file_name) const {
std::ofstream img_file(file_name, std::ios_base::binary | std::ios_base::out);
img_file.write((char*)file_header, header_size);
img_file.write((char*)info_header, info_header_size);
std::vector<char> allignment(row_padding);
for (int y = image_px_height - 1; y >= 0; --y) {
img_file.write((char*)image[y].data(), row_width);
img_file.write(allignment.data(), row_padding);
}
img_file.close();
}
private:
size_t image_px_width;
size_t image_px_height;
size_t row_width;
size_t row_padding;
size_t row_stride;
size_t file_size;
unsigned char file_header[header_size] = { 0 };
unsigned char info_header[info_header_size] = { 0 };
std::vector<std::vector<unsigned char>> image;
};
C++ answer, flexible API, assumes little-endian system to code-golf it a bit. Note this uses the bmp native y-axis (0 at the bottom).
#include <vector>
#include <fstream>
struct image
{
image(int width, int height)
: w(width), h(height), rgb(w * h * 3)
{}
uint8_t & r(int x, int y) { return rgb[(x + y*w)*3 + 2]; }
uint8_t & g(int x, int y) { return rgb[(x + y*w)*3 + 1]; }
uint8_t & b(int x, int y) { return rgb[(x + y*w)*3 + 0]; }
int w, h;
std::vector<uint8_t> rgb;
};
template<class Stream>
Stream & operator<<(Stream & out, image const& img)
{
uint32_t w = img.w, h = img.h;
uint32_t pad = w * -3 & 3;
uint32_t total = 54 + 3*w*h + pad*h;
uint32_t head[13] = {total, 0, 54, 40, w, h, (24<<16)|1};
char const* rgb = (char const*)img.rgb.data();
out.write("BM", 2);
out.write((char*)head, 52);
for(uint32_t i=0 ; i<h ; i++)
{ out.write(rgb + (3 * w * i), 3 * w);
out.write((char*)&pad, pad);
}
return out;
}
int main()
{
image img(100, 100);
for(int x=0 ; x<100 ; x++)
{ for(int y=0 ; y<100 ; y++)
{ img.r(x,y) = x;
img.g(x,y) = y;
img.b(x,y) = 100-x;
}
}
std::ofstream("/tmp/out.bmp") << img;
}
This code uses some newer C++ features. I've used it to create 8bit and 24bit bmp files. It only writes bmp files, one day we may read them too!
I didn't like all the shifting and error proneess for endian safety.
It could use lots more comments but the code is pretty straight forward. The supposedly run-time detection of endianness results in code being optimized away on all the compilers I tested (a while ago).
endian_type.h >> Endian safe POD type.
#ifndef ENDIAN_TYPE_H
#define ENDIAN_TYPE_H
#include <algorithm>
#include <type_traits>
namespace endian_type {
template <typename T, bool store_as_big_endian>
struct EndianType {
using value_type = T;
static_assert(std::is_fundamental_v<value_type>,
"EndianType works for fundamental data types");
EndianType() = default;
EndianType(const value_type& value)
: value{ convert_to(value) } {}
struct TypeAsBytes {
unsigned char value[sizeof(value_type)];
};
static constexpr bool is_big_endian() {
union { int ival; char cval; } uval;
uval.ival = 1;
return 0 == uval.cval;
}
static TypeAsBytes convert_to(const value_type& ivalue) {
TypeAsBytes ovalue;
const unsigned char* p_ivalue = (const unsigned char*)&ivalue;
if (store_as_big_endian != is_big_endian()) {
std::reverse_copy(p_ivalue, p_ivalue + sizeof(value_type), ovalue.value);
} else {
std::copy(p_ivalue, p_ivalue + sizeof(value_type), ovalue.value);
}
return ovalue;
}
static value_type convert_from(const TypeAsBytes& ivalue) {
value_type ovalue;
unsigned char* p_ovalue = (unsigned char*) &ovalue;
const unsigned char* p_ivalue = (const unsigned char*)&ivalue;
if (store_as_big_endian != is_big_endian()) {
std::reverse_copy(p_ivalue, p_ivalue + sizeof(value_type), p_ovalue);
}
else {
std::copy(p_ivalue, p_ivalue + sizeof(value_type), p_ovalue);
}
return ovalue;
}
value_type get() const {
return convert_from(value);
}
EndianType& set(const value_type& ivalue) {
value = convert_to(ivalue);
return *this;
}
operator value_type() const {
return get();
}
EndianType& operator=(const value_type& ivalue) {
set(ivalue);
return *this;
}
private:
TypeAsBytes value;
};
template <typename T>
using BigEndian = EndianType<T, true>;
template <typename T>
using LittleEndian = EndianType<T, false>;
} // namespace endian_type
#endif // ENDIAN_TYPE_H
The following contains the write_bmp functions.
bmp_writer.h >> the BMP writer header
#ifndef BMP_WRITER
#define BMP_WRITER
#include "endian_type.h"
#include <cctype>
#include <vector>
#include <fstream>
namespace bmp_writer {
template <typename T>
using LittleEndian = endian_type::LittleEndian<T>;
struct Header {
char magic[2]{ 'B', 'M' };
LittleEndian<std::uint32_t> size;
LittleEndian<std::uint16_t> app_data1;
LittleEndian<std::uint16_t> app_data2;
LittleEndian<std::uint32_t> offset;
};
struct Info {
LittleEndian<std::uint32_t> info_size{ 40 };
LittleEndian<std::uint32_t> width;
LittleEndian<std::uint32_t> height;
LittleEndian<std::uint16_t> count_colour_planes{ 1 };
LittleEndian<std::uint16_t> bits_per_pixel;
LittleEndian<std::uint32_t> compression{};
LittleEndian<std::uint32_t> image_bytes_size;
LittleEndian<std::uint32_t> resolution_horizontal{ 2835 };
LittleEndian<std::uint32_t> resolution_vertical{ 2835 };
LittleEndian<std::uint32_t> count_pallete_entries{ 0 };
LittleEndian<std::uint32_t> important_colours{ 0 };
};
template <std::size_t count>
class Palette {
public:
static constexpr std::uint32_t NUM_CHANNELS = 4;
using Entry = std::uint8_t[NUM_CHANNELS];
private:
Palette() {
for (auto i = 0; i < count; ++i) {
auto& entry = table[i];
for (auto j = 0; j < NUM_CHANNELS - 1; ++j) {
entry[j] = i;
}
}
}
Palette(const Palette&) = delete;
Palette(const Palette&&) = delete;
Palette& operator=(const Palette&) = delete;
Palette& operator=(const Palette&&) = delete;
public:
static const Palette& get() {
static const Palette palette;
return palette;
}
Entry table[count];
};
static_assert(sizeof(Info) == 40, "");
template <typename T>
void write_bmp(
std::ofstream& out,
std::uint32_t width,
std::uint32_t height,
std::uint16_t count_colour_planes,
const T* data,
std::uint32_t data_size
) {
auto& palette = Palette<256>::get();
Header header;
Info info;
info.width = width;
info.height = height;
//info.count_colour_planes = count_colour_planes;
const std::uint32_t t_per_pixel = data_size / (width * height);
info.bits_per_pixel = std::uint16_t(sizeof(T) * 8 * t_per_pixel);
const std::uint32_t row_len = width * sizeof(T) * t_per_pixel;
// Round row up to next multiple of 4.
const std::uint32_t padded_row_len = (row_len + 3) & ~3u;
const std::uint32_t data_size_bytes = padded_row_len * height;
info.image_bytes_size = data_size_bytes;
if (count_colour_planes == 1) {
header.offset = sizeof(Info) + sizeof(Header) + sizeof(palette);
} else {
header.offset = sizeof(Info) + sizeof(Header);
}
header.size = header.offset + height * padded_row_len;
out.write(reinterpret_cast<const char*>(&header), sizeof(header));
out.write(reinterpret_cast<const char*>(&info), sizeof(info));
if (count_colour_planes == 1) {
out.write(reinterpret_cast<const char*>(&palette), sizeof(palette));
}
const char padding[3] = {};
for (int i = height; i > 0;) {
--i;
const char* p_row =
reinterpret_cast<const char*>(data + i * width);
out.write(p_row, row_len);
if (padded_row_len != row_len) {
out.write(padding, padded_row_len - row_len);
}
}
};
template <typename T>
void write_bmp(
std::ofstream& out,
std::uint32_t width,
std::uint32_t height,
std::uint16_t count_colour_planes,
const std::vector<T>& data
) {
write_bmp(out, width, height, count_colour_planes,
&*data.cbegin(), data.size());
}
template <typename T>
void write_bmp(
const std::string& outfilename,
std::uint32_t width,
std::uint32_t height,
std::uint16_t count_colour_planes,
const std::vector<T>& data
) {
std::ofstream out{ outfilename, std::ios_base::binary };
if (!out) {
throw std::runtime_error("Failed to open: " + outfilename);
}
write_bmp(out, width, height, count_colour_planes,
&*data.begin(), static_cast<std::uint32_t>(data.size()));
out.close();
}
} // namespace
#endif // BMP_WRITER
And an example of use:
#include "bmp_writer.h"
struct PixelType {
PixelType(std::uint8_t r, std::uint8_t g, std::uint8_t b)
: c{ b, g, r } {}
PixelType(std::uint32_t c)
: c{ (c >> 16) & 0xffu, (c >> 8) & 0xffu, c & 0xffu } {}
PixelType() = default;
std::uint8_t c[3] = {};
};
void bmp_writer_test1() {
const int size_x = 20;
const int size_y = 10;
std::vector<PixelType> data(size_x * size_y);
// Write some pixels.
data[2] = PixelType(0xff0000); // red
data[10] = PixelType(0x00ff00); // green
bmp_writer::write_bmp(
"test_bmp_writer1.bmp",
std::uint32_t(size_x),
std::uint32_t(size_y),
std::uint16_t(sizeof(PixelType)),
data
);
}
void bmp_writer_test2() {
const int size_x = 20;
const int size_y = 10;
PixelType data[size_x * size_y];
// Write some pixels.
data[15] = PixelType(0xff, 0, 0); // red
data[17] = PixelType(0, 0xff, 0); // green
std::ofstream out{ "test_bmp_writer2.bmp", std::ios_base::binary };
if (!out) {
throw std::runtime_error("Failed to open: " "test_bmp_writer2.bmp");
}
bmp_writer::write_bmp(
out,
std::uint32_t(size_x),
std::uint32_t(size_y),
std::uint16_t(sizeof(PixelType)),
data,
sizeof(data) / sizeof PixelType
);
}