I could need some help to figure out how to feed the proc below. I need to write a monochrome BMP file. The code below (its from: How to Save monochrome Image as bmp in windows C++ ?) looks like to be able to do this. I'm now stuck on how to convert a std::bitset or preferably boost::dynamic_bitset into this byte* format. All of my attempts so far failed, I wasn't able to write something like an 8x8 checker pattern into the BMP. The proc creates the BMP and it is readable by Photoshop, but the content is a mess. So any suggestions how to solve this are appreciated!
Save1BppImage(byte* ImageData, const char* filename, long w, long h){
int bitmap_dx = w; // Width of image
int bitmap_dy = h; // Height of Image
// create file
std::ofstream file(filename, std::ios::binary | std::ios::trunc);
if(!file) return;
// save bitmap file headers
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER * infoHeader;
infoHeader = (BITMAPINFOHEADER*) malloc(sizeof(BITMAPINFOHEADER) );
RGBQUAD bl = {0,0,0,0}; //black color
RGBQUAD wh = {0xff,0xff,0xff,0xff}; // white color
fileHeader.bfType = 0x4d42;
fileHeader.bfSize = 0;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + (sizeof(BITMAPINFOHEADER));
infoHeader->biSize = (sizeof(BITMAPINFOHEADER) );
infoHeader->biWidth = bitmap_dx;
infoHeader->biHeight = bitmap_dy;
infoHeader->biPlanes = 1;
infoHeader->biBitCount = 1;
infoHeader->biCompression = BI_RGB; //no compression needed
infoHeader->biSizeImage = 0;
infoHeader->biXPelsPerMeter = 0;
infoHeader->biYPelsPerMeter = 0;
infoHeader->biClrUsed = 2;
infoHeader->biClrImportant = 2;
file.write((char*)&fileHeader, sizeof(fileHeader)); //write bitmapfileheader
file.write((char*)infoHeader, (sizeof(BITMAPINFOHEADER) )); //write bitmapinfoheader
file.write((char*)&bl,sizeof(bl)); //write RGBQUAD for black
file.write((char*)&wh,sizeof(wh)); //write RGBQUAD for white
int bytes = (w/8) * h ; //for example for 32X64 image = (32/8)bytes X 64 = 256;
file.write((const char*)ImageData, bytes);
file.close();
}
-edit-
an naive approach of mine was something like this
byte test[64];
for(unsigned int i=0; i<64; ++i)
if(i % 2)
test[i] = 0;
else
test[i] = 1;
Save1BppImage(test, "C:/bitmap.bmp", 8, 8);
The code you have is very close. Here are a few thoughts about where it might be off.
The bfOffBits value must include the size of the palette.
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + (sizeof(BITMAPINFOHEADER)) + 2*sizeof(RGBQUAD);
Some software may interpret 0 as white and 1 as black, regardless of what the palette says. Even though the file format allows you to go either way, you're better off specifying the palette in that order and inverting your bits if necessary.
Each row of a bitmap will start on a 4-byte boundary. If your bitmap width isn't a multiple of 32, you're going to need some padding between each row.
BMP files are ordered from the bottom row to the top row, which is backwards from the way most people organize their arrays.
The last two recommendations are combined to look something like this:
int bytes_in = (w + 7) / 8;
int bytes_out = ((w + 31) / 32) * 4;
const char * zeros[4] = {0, 0, 0, 0};
for (int y = h - 1; y >= 0; --y)
{
file.write(((const char *)ImageData) + (y * bytes_in), bytes_in);
if (bytes_out != bytes_in)
file.write(zeros, bytes_out - bytes_in);
}
Just for the archive, below the working version. It takes a boost bitset as input pixel storage.
void bitsetToBmp(boost::dynamic_bitset<unsigned char> bitset, const char* filename, int width, int height){
//write the bitset to file as 1-bit deep bmp
//bit order 0...n equals image pixels top left...bottom right, row by row
//the bitset must be at least the size of width*height, this is not checked
std::ofstream file(filename, std::ios::binary | std::ios::trunc);
if(!file) return;
// save bitmap file headers
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER * infoHeader;
infoHeader = (BITMAPINFOHEADER*) malloc(sizeof(BITMAPINFOHEADER) );
RGBQUAD bl = {0,0,0,0}; //black color
RGBQUAD wh = {0xff,0xff,0xff,0xff}; // white color
fileHeader.bfType = 0x4d42;
fileHeader.bfSize = 0;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + (sizeof(BITMAPINFOHEADER)) + 2*sizeof(RGBQUAD);
infoHeader->biSize = (sizeof(BITMAPINFOHEADER) );
infoHeader->biWidth = width;
infoHeader->biHeight = height;
infoHeader->biPlanes = 1;
infoHeader->biBitCount = 1;
infoHeader->biCompression = BI_RGB; //no compression needed
infoHeader->biSizeImage = 0;
infoHeader->biXPelsPerMeter = 0;
infoHeader->biYPelsPerMeter = 0;
infoHeader->biClrUsed = 2;
infoHeader->biClrImportant = 2;
file.write((char*)&fileHeader, sizeof(fileHeader)); //write bitmapfileheader
file.write((char*)infoHeader, (sizeof(BITMAPINFOHEADER) )); //write bitmapinfoheader
file.write((char*)&bl,sizeof(bl)); //write RGBQUAD for black
file.write((char*)&wh,sizeof(wh)); //write RGBQUAD for white
// convert the bits into bytes and write the file
int offset, numBytes = ((width + 31) / 32) * 4;
byte* bytes = (byte*) malloc(numBytes * sizeof(byte));
for(int y=height - 1; y>=0; --y){
offset = y * width;
memset(bytes, 0, (numBytes * sizeof(byte)));
for(int x=0; x<width; ++x)
if(bitset[offset++]){
bytes[x / 8] |= 1 << (7 - x % 8);
};
file.write((const char *)bytes, numBytes);
};
free(bytes);
file.close();
}
I wonder if theres a simpler/faster way to put the bits into a file? The whole bitset could instead be overhanded as array of rows to skip the subset extraction.
I have something very similiar...
This approach DOES NOT treat the padding of the BMP format. So You can only make bitmaps with width multiple of 4.
This is NOT a monochromatic bitmap. It's a RGB format, but you can tune it easily.
This is NOT an exactly answer to you, but for sure may be useful for you.
Enjoy it.
void createBitmap( byte * imageData, const char * filename, int width, int height )
{
BITMAPFILEHEADER bitmapFileHeader;
memset( &bitmapFileHeader, 0, sizeof( bitmapFileHeader ) );
bitmapFileHeader.bfType = ( 'B' | 'M' << 8 );
bitmapFileHeader.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER );
bitmapFileHeader.bfSize = bitmapFileHeader.bfOffBits + width * height * 3;
BITMAPINFOHEADER bitmapInfoHeader;
memset( &bitmapInfoHeader, 0, sizeof( bitmapInfoHeader ) );
bitmapInfoHeader.biSize = sizeof( BITMAPINFOHEADER );
bitmapInfoHeader.biWidth = width;
bitmapInfoHeader.biHeight = height;
bitmapInfoHeader.biPlanes = 1;
bitmapInfoHeader.biBitCount = 24;
std::ofstream file( filename, std::fstream::binary );
file.write( reinterpret_cast< char * >( &bitmapFileHeader ), sizeof( bitmapFileHeader ) );
file.write( reinterpret_cast< char * >( &bitmapInfoHeader ), sizeof( bitmapInfoHeader ) );
// the pixels!
file.write( imageData, width * height * 3 );
file.close();
}
int main( int argc, const char * argv[] )
{
int width = 12; // multiple of 4
int height = 12;
byte imageData[ width * height * 3 ];
// fill imageData the way you want, this is just a sample
// on how to set the pixel at any specific (X,Y) position
for ( int y = 0; y < height; ++y )
{
for ( int x = 0; x < width; ++x )
{
int pos = 3 * ( y * width + x );
byte pixelColor = ( x == 2 && y == 2 ) ? 0x00 : 0xff;
imageData[ pos ] = pixelColor;
imageData[ pos + 1 ] = pixelColor;
imageData[ pos + 2 ] = pixelColor;
}
}
createBitmap( imageData, "bitmap.bmp", width, height );
return 0;
}
In this sample we want a white bitmap with a single black pixel at position X = 2, Y = 2.
The BMP format constiders that Y grows up from bottom to top.
If you have a bitmap width a pixel per bit (the real monochromatic bitmap) you can test the bits and fill the imageData. To test a bit in a byte do like myByte >> position & 1 where position is the bit you wanna test from 0 to 7.
Related
Im trying to capture mouse cursor using windows API GetCursorInfo and taking in to CURSORINFO structure after I reading ICONINFO using GetIconInfo so I will get hbmMask and hbmColor bitmap
The hbmMask bitmap is first applied with an AND raster operation, then the hbmColor bitmap is applied with an XOR raster operation. This results in an opaque cursor and a transparent background but this is not happening in my POC complete code in below example.
After doing AND raster on mask data and XOR on color data the final result will be cursor and covered with white colored rectangle 32*32.
void save_as_bitmap(unsigned char *bitmap_data, int rowPitch, int width, int height, char *filename)
{
// A file is created, this is where we will save the screen capture.
FILE *f;
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
//Make the size negative if the image is upside down.
bi.biHeight = -height;
//There is only one plane in RGB color space where as 3 planes in YUV.
bi.biPlanes = 1;
//In windows RGB, 8 bit - depth for each of R, G, B and alpha.
bi.biBitCount = 32;
//We are not compressing the image.
bi.biCompression = BI_RGB;
// The size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps.
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
// rowPitch = the size of the row in bytes.
DWORD dwSizeofImage = rowPitch * height;
// Add the size of the headers to the size of the bitmap to get the total file size
DWORD dwSizeofDIB = dwSizeofImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
//Size of the file
bmfHeader.bfSize = dwSizeofDIB;
//bfType must always be BM for Bitmaps
bmfHeader.bfType = 0x4D42; //BM
// TODO: Handle getting current directory
fopen_s(&f, filename, "wb");
DWORD dwBytesWritten = 0;
dwBytesWritten += fwrite(&bmfHeader, sizeof(BITMAPFILEHEADER), 1, f);
dwBytesWritten += fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, f);
dwBytesWritten += fwrite(bitmap_data, 1, dwSizeofImage, f);
fclose(f);
}
//ST484 : HBIMAPtoBYTE : Convert BITMAP to BYTE array.
std::vector<BYTE> HBIMAPtoBYTE( HBITMAP hBitmap,
int &hBitmapSize,
bool &bResult,
int &nWidth,
int &nHeight )
{
bResult = true;
BITMAP bmp;
if (!GetObject(hBitmap, sizeof(BITMAP), (LPVOID)&bmp))
{
DeleteObject(hBitmap);
bResult = false;
}
int rpcbiPlanes = 32;
BITMAPINFO info;
memset(&info, 0, sizeof(BITMAPINFO));
info.bmiHeader.biSize = sizeof(info.bmiHeader);
info.bmiHeader.biWidth = bmp.bmWidth;
info.bmiHeader.biHeight = -bmp.bmHeight;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = rpcbiPlanes;
info.bmiHeader.biCompression= BI_RGB;
size_t pixelSize = info.bmiHeader.biBitCount / 8;
size_t scanlineSize = (pixelSize * info.bmiHeader.biWidth + 3) & ~3;
size_t bitmapSize = bmp.bmHeight * scanlineSize;
hBitmapSize = bitmapSize;
nWidth = bmp.bmWidth;
nHeight = bmp.bmHeight;
std::vector<BYTE> pixels(bitmapSize);
HDC hdc = ::GetDC(NULL);
if(!GetDIBits(hdc, hBitmap, 0, bmp.bmHeight, &pixels[0], &info, DIB_RGB_COLORS))
{
hBitmapSize = 0;
bResult = false;
}
return pixels;
}
// getHCursor : Capture cursor.
CURSORINFO getHCursor()
{
CURSORINFO cursorInfo;
cursorInfo.cbSize = sizeof(CURSORINFO);
if (GetCursorInfo(&cursorInfo) == 0)
{
MessageBox(NULL, _T("Exception : GetCursorInfo creation failed"),_T("message"),MB_OK|MB_SYSTEMMODAL);
cursorInfo.hCursor = NULL;
return cursorInfo;
}
return cursorInfo;
}
//Main Call
int _tmain(int argc, _TCHAR* argv[])
{
int CountP = 0;
while (true)
{
CURSORINFO CursorInfo = getHCursor();
if (CursorInfo.hCursor == NULL)
{
::Sleep(MinSleep);
continue;
}
ICONINFO iconInfo;
if (!GetIconInfo(CursorInfo.hCursor, &iconInfo))
{
MessageBox(NULL, _T("Exception : GetIconInfo creation failed"),_T("message"),MB_OK|MB_SYSTEMMODAL);
::Sleep(MinSleep);
}
std::vector<BYTE> bColorBitmap;
std::vector<BYTE> bMaskBitmap;
std::vector<BYTE> bDestBitmap;
int sz_hbmColor = 0;
int sz_hbmMask = 0;
int sz_hbDest = 0;
int nWidth = 0;
int nHeight = 0;
bool hbmColor_result = false;
bool hbmMask_result = false;
bool hbmDest_result = false;
int rpcbiPlanes = 32;
bool isColorShape = (iconInfo.hbmColor != NULL);
// read mask and color in to byte vector.
bColorBitmap = HBIMAPtoBYTE(iconInfo.hbmColor,sz_hbmColor,hbmColor_result,nWidth,nHeight);
bMaskBitmap = HBIMAPtoBYTE(iconInfo.hbmMask,sz_hbmMask,hbmMask_result,nWidth,nHeight);
//Create Dummy bitmap using width and height filled with black color.
HBITMAP desBitmap = CreateBitmap(nWidth,nHeight,1,rpcbiPlanes,NULL);
if(desBitmap != NULL)
{
// read dummy bitmap in to byte vector.
bDestBitmap = HBIMAPtoBYTE(desBitmap,sz_hbDest,hbmDest_result,nWidth,nHeight);
}
//the mask bitmap is first applied with an AND raster operation.
for(int i = 0; i < nHeight ; i++)
{
for(int j = 0; j < nWidth; j++)
{
bDestBitmap[i*4*nWidth + j*4 ] &= bMaskBitmap[i*4*nWidth + j*4 ];
bDestBitmap[i*4*nWidth + j*4 + 1] &= bMaskBitmap[i*4*nWidth + j*4 + 1];
bDestBitmap[i*4*nWidth + j*4 + 2] &= bMaskBitmap[i*4*nWidth + j*4 + 2];
bDestBitmap[i*4*nWidth + j*4 + 3] &= bMaskBitmap[i*4*nWidth + j*4 + 3];
}
}
//then the color bitmap is applied with an XOR raster operation.
for(int i = 0; i < nHeight ; i++)
{
for(int j = 0; j < nWidth; j++)
{
bDestBitmap[i*4*nWidth + j*4 ] ^= bColorBitmap[i*4*nWidth + j*4 ];
bDestBitmap[i*4*nWidth + j*4 + 1] ^= bColorBitmap[i*4*nWidth + j*4 + 1];
bDestBitmap[i*4*nWidth + j*4 + 2] ^= bColorBitmap[i*4*nWidth + j*4 + 2];
bDestBitmap[i*4*nWidth + j*4 + 3] ^= bColorBitmap[i*4*nWidth + j*4 + 3];
}
}
//Save Color bitmap.
sprintf_s(file_name,"C:\\Test\\captured\\Cursor_%d.bmp", CountP);
save_as_bitmap(&(bDestBitmap[0]), nWidth*4, nWidth, nHeight, file_name);
CountP++;
Sleep(MaxSleep);
}
return 0;
}
After saving cursor getting image like this
Finally I found excellent result using windows API.
Include comctl32.lib in to your project
HIMAGELIST iCursorList=ImageList_Create(nWidth,nHeight,ILC_COLOR32|ILC_MASK,8,8);
ImageList_AddMasked(iCursorList,iconInfo.hbmColor,000000);
DeleteObject(iconInfo.hbmColor);
TRANSPARENT_HICON = ImageList_GetIcon(iCursorList,0,ILD_TRANSPARENT);
1 ) Create ImageList_Create using width, height, ILC_COLOR32|ILC_MASK
2 ) Add bitmap in to ImageList_AddMasked and apply which color you want to make transparent, 000000 are black color in my code.
3 ) Get transparent icon from ImageList_GetIcon.
I'm creating small BMP files for my application and some of them, depending on number of pixels, crashes my app and Windows sees them corrupted. An example of working size is 60 x 60px, but i.e. 61 x 61 is not (variables m_width and m_height).
The structs used (#pragma pack is applied to BMP-related ones):
struct Rgb /// vector's content
{
uint8_t r;
uint8_t g;
uint8_t b;
};
#pragma pack(push, 1)
struct FileHeader
{
int16_t bfType;
int32_t bfSize;
int16_t bfReserved1;
int16_t bfReserved2;
int32_t bfOffBits;
};
struct BitMapInfoHeader
{
int32_t biSize;
int32_t biWidth;
int32_t biHeight;
int16_t biPlanes;
int16_t biBitCount;
int32_t biCompression;
int32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
int32_t biClrUsed;
int8_t biClrImportant;
int8_t biClrRotation;
int16_t biReserved;
};
struct RGBQuad
{
int8_t rgbBlue;
int8_t rgbGreen;
int8_t rgbRed;
int8_t rgbReserved;
};
#pragma pack(pop)
I allocate memory for whole BMP file, assign pointers to each file region, fill in the structs, copy pixel data from other array and save the file. Code:
int m_width = 60, m_height = 60;
uint8_t* data = new uint8_t[ m_width * m_height ];
memset( data, 0, m_width * m_height );
data[ 2 ] = 1; /// one pixel differs
std::vector< Rgb > RGBVec = { { 223, 223, 123 }, { 230, 0, 12 } };
int numberOfSymbols = RGBVec.size();
FileHeader* fileHeader;
BitMapInfoHeader* infoHeader;
RGBQuad* colorTable;
uint8_t* m_pBMPFile; /// pointer to bitmap in memory
uint8_t* m_BMPData; /// begin of pixel data
int m_BMPFileLength = sizeof( FileHeader ) + sizeof( BitMapInfoHeader )
+ numberOfSymbols * sizeof( RGBQuad ) + m_width * m_height;
/// assign pointers to specific parts of bitmap:
m_pBMPFile = new uint8_t[ m_BMPFileLength ];
memset( m_pBMPFile, 0, m_BMPFileLength );
fileHeader = reinterpret_cast< FileHeader* >( m_pBMPFile );
infoHeader = reinterpret_cast< BitMapInfoHeader* >( m_pBMPFile + sizeof( FileHeader ) );
colorTable =
reinterpret_cast< RGBQuad* >( m_pBMPFile + sizeof( FileHeader ) + sizeof( BitMapInfoHeader ) );
m_BMPData = reinterpret_cast< uint8_t* >( m_pBMPFile + sizeof( FileHeader ) + sizeof( BitMapInfoHeader )
+ numberOfSymbols * sizeof( RGBQuad ) );
///////////
/// FileHeader:
fileHeader->bfType = 0x4d42; /// magic number
fileHeader->bfSize = m_BMPFileLength;
fileHeader->bfOffBits = int( m_BMPData - m_pBMPFile );
/// BitMapInfoHeader:
infoHeader->biSize = 40;
infoHeader->biWidth = m_width;
infoHeader->biHeight = -m_height; /// multiplied by -1 so pixels are displayed top-down
infoHeader->biPlanes = 1;
infoHeader->biBitCount = 8;
infoHeader->biCompression = 0;
infoHeader->biSizeImage = 0;
infoHeader->biXPelsPerMeter = 2835;
infoHeader->biYPelsPerMeter = 2835;
infoHeader->biClrUsed = numberOfSymbols;
infoHeader->biClrImportant = 0;
infoHeader->biClrRotation = 0;
/// palette:
int i = 0;
for( auto& s : RGBVec )
{
( &colorTable[ i ] )->rgbRed = s.r;
( &colorTable[ i ] )->rgbGreen = s.g;
( &colorTable[ i ] )->rgbBlue = s.b;
++i;
}
/// apply pixel data:
memcpy( m_BMPData, data, m_width * m_height );
/// save:
std::ofstream file2( "out.bmp", std::ios::binary | std::ios::trunc );
file2.write( ( char* )m_pBMPFile, m_BMPFileLength );
file2.close();
delete[] m_pBMPFile;
Compiled on VS2015, 64 bit.
The main issue is that m_width should be padded so that the width in bytes is divisible by 4. You can use the formula below for width_in_bytes which guarantees that. In this case it changes width from 61 to 64. The extra bytes can be ignored.
Furthermore you can simplify your code by avoiding overuse of pointers. Declaring headers as local variables is enough. Use std::vector to allocate data instead of new/delete. And use a single RGBQ header that can manage the data. Example:
struct RGBQ { uint8_t rgbBlue, rgbGreen, rgbRed, rgbReserved; };
int m_width = 61, m_height = 61;
int bitcount = 8;
int width_in_bytes = ((m_width * bitcount + 31) / 32) * 4;
int imagesize = width_in_bytes * m_height;
std::vector<RGBQ> color_table{ { 223, 223, 123 }, { 230, 0, 12 } };
std::vector<uint8_t> data(imagesize);
data[2] = 1;
FileHeader fileHeader = { 0 };
BitMapInfoHeader infoHeader = { 0 };
fileHeader.bfType = 0x4d42;
fileHeader.bfSize = sizeof(fileHeader) + sizeof(infoHeader) + color_table.size()
* sizeof(RGBQ) + data.size();
fileHeader.bfOffBits = sizeof(fileHeader) + sizeof(infoHeader);
infoHeader.biSize = 40;
infoHeader.biWidth = m_width;
infoHeader.biHeight = -m_height;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = bitcount;
infoHeader.biClrUsed = color_table.size();
std::ofstream file2("out.bmp", std::ios::binary | std::ios::trunc);
file2.write((char*)&fileHeader, sizeof(fileHeader));
file2.write((char*)&infoHeader, sizeof(infoHeader));
file2.write((char*)color_table.data(), color_table.size() * sizeof(RGBQ));
file2.write((char*)data.data(), data.size());
file2.close();
My problem is this: I wanna try to make a white color 24bit bitmap, but everytime I write to the bmp file, it gives me white black/white stripes. i dont understand why? Maybe i skip some bytes?
If you want more information on the code just ask.
setup settings:
void setup_settings( ) {
// information om billedet
pic.infoHeader.biSize = sizeof(BMP_InfoHeader);
pic.infoHeader.biBitCount = 24;
pic.infoHeader.biWidth = WIDTH; // Hoejte i pixels
pic.infoHeader.biHeight = HEIGH; // bredte i pixels
pic.infoHeader.biPlanes = 1;
pic.infoHeader.biCompression = 0;
pic.infoHeader.biSizeImage = WIDTH * HEIGH * (pic.infoHeader.biBitCount/8);
pic.infoHeader.biXPelsPerMeter = 0;
pic.infoHeader.biYPelsPerMeter = 0;
pic.infoHeader.biClrUsed = 0;
pic.infoHeader.biClrInportant = 0;
pic.fileHeader.bfType[0] = 'B';
pic.fileHeader.bfType[1] = 'M';
pic.fileHeader.bfReservered1 = pic.fileHeader.bfReservered2 = 0;
pic.fileHeader.bfOffBits = sizeof(BMP_FileHeader) + pic.infoHeader.biSize;
}
this funktion definition for my SaveBitmapFile is:
int SaveBitmapFile(const std::string filename, bit24* image){
// gem filen
std::ofstream writer(FileName.c_str(), std::ofstream::binary);
if(!writer.is_open()){
printf("Error: While Writing\n");
return -1;
}
writer.write(reinterpret_cast<char *>(&pic.fileHeader), sizeof(BMP_FileHeader) );
writer.write(reinterpret_cast<char *>(&pic.infoHeader), sizeof(BMP_InfoHeader) );
writer.write(reinterpret_cast<char*>(&image[0]), pic.infoHeader.biSizeImage);
writer.close();
return 0;
}
My structures:
#pragma pack(1)
typedef struct{
uint32_t value : 24;
}bit24;
#pragma pack(0)
// Billedet
#pragma pack(1)
typedef struct{
unsigned int Width;
unsigned int Heigh;
bit24* RGB;
}Image;
#pragma pack(0)
typedef struct {
BMP_FileHeader fileHeader;
BMP_InfoHeader infoHeader;
Image data;
}BMP_Data;
My source main source code:
// the pic is a type of BMP_Data. sorry if i this is really messy.
int main( int argc, char *argv[] ){
setup_settings();
pic.data.Heigh = pic.infoHeader.biHeight;
pic.data.Width = pic.infoHeader.biWidth;
int bytesPerRGB = (pic.infoHeader.biBitCount/8);
//padded bytes?
int paddedBytes = ( pic.data.Width * bytesPerRGB) % 4;
printf("PaddedBytes: %d\n", paddedBytes);
pic.data.RGB = new bit24[ pic.data.Heigh * pic.data.Width * bytesPerRGB];
uint8_t r,g,b;
r = 0xFF;
g = 0xFF;
b = 0xFF;
/*
for( unsigned int y = 0; y < pic.data.Heigh; y++)
for( unsigned int x = 0; x < pic.data.Width; x++)
{
pic.data.RGB[x + (y*pic.data.Width )].value = ( (b << 0) | (g << 8) | (r << 16) );
}
*/
for( unsigned int i = 0; i < pic.data.Heigh * pic.data.Width * bytesPerRGB; i+=3){
pic.data.RGB[i ].value = ( (b << 0) | (g << 8) | (r << 16) );
}
SaveBitmapFile(FileName, pic.data.RGB);
delete [] pic.data.RGB;
return 0;
}
You MUST fill all BitmapInfoHeader fields.
- Assuming you want 24bits bitmap:
BITMAPINFOHEADER bi =
{
sizeof(BITMAPINFOHEADER), // DWORD, = 40
WIDTH, // DWORD, image width in pix
HEIGHT, // DWORD, image width in pix
1, // WORD, planes MUST be 1
24,// WORD, num of bits per pixel
BI_RGB, // DWORD, (no compression = BI_RGB = 0)
0, // DWORD, sizeof image data, can be 0 if BI_RGB = 0
0, // DWORD, x-resolution
0, // DWORD, y-resolution
0, // DWORD, colors used (palette images only)
0, // DWORD, importand colors (palette images only)
};
Also make sure every row (horizontal line) of pixels is padded to multiple of 4 bytes !!!
(So begin with images of N x 4 width - they have no padding issues)
Okay, I have found the problem.
After I changed the bit24 to:
typedef struct{
uint8_t blue;
uint8_t green;
uint8_t red;
}RGB_Data;
from:
#pragma pack(1)
typedef struct{
uint32_t value : 24;
}bit24;
#pragma pack(0)
and a little change in the main:
for( unsigned int i = 0; i < pic.data.Heigh * pic.data.Width * bytesPerRGB; i++){
pic.data.RGB[i].blue = b;
pic.data.RGB[i].green = g;
pic.data.RGB[i].red = r;
}
it worked like a charm. thanx you for your help :)
I read this tutorial http://tipsandtricks.runicsoft.com/Cpp/BitmapTutorial.html about bitmap and it really helped..I need to read color integer values from elements of pixel array. How to do that?
Ok heres the code for putting data into rgb array
BYTE* ConvertBMPToRGBBuffer ( BYTE* Buffer, int width, int height )
{
if ( ( NULL == Buffer ) || ( width == 0 ) || ( height == 0 ) )
return NULL;
// find the number of padding bytes
int padding = 0;
int scanlinebytes = width * 3;
while ( ( scanlinebytes + padding ) % 4 != 0 ) // DWORD = 4 bytes
padding++;
// get the padded scanline width
int psw = scanlinebytes + padding;
// create new buffer
BYTE* newbuf = new BYTE[width*height*3];
// now we loop trough all bytes of the original buffer,
// swap the R and B bytes and the scanlines
long bufpos = 0;
long newpos = 0;
for ( int y = 0; y < height; y++ )
for ( int x = 0; x < 3 * width; x+=3 )
{
newpos = y * 3 * width + x;
bufpos = ( height - y - 1 ) * psw + x;
newbuf[newpos] = Buffer[bufpos + 2];
newbuf[newpos + 1] = Buffer[bufpos+1];
newbuf[newpos + 2] = Buffer[bufpos];
}
return newbuf;
}
It looks like your image is in RGB interleaved format. To get a pixel at (x,y), simply index the array at that location. It would be easiest if your buffer pointed to a structure type. Something like:
typedef struct RGBPixel {
BYTE red;
BYTE green;
BYTE blue;
} RGBPixel;
Then you could do something like this:
RGBPixel* pixels = (RGBPixel*)newbuf;
To get a pixel at (x,y), you'd do this:
RGBPixel aPixel = pixels [ y * width + x ];
I am trying to capture an image of the screen for use in screencasting. Thus I need a fast solution, and cannot rely on shell programs such as import or xwd.
This is the code I have written so far, but it fails and gives me a junk image, which just seems to show fragments of several images with odd colors tossed together.
Any ideas on what I am doing wrong?
#include <X11/Xlib.h>
#include <X11/X.h>
#include <cstdio>
#include <CImg.h>
using namespace cimg_library;
int main()
{
Display *display = XOpenDisplay(NULL);
Window root = DefaultRootWindow(display);
XWindowAttributes gwa;
XGetWindowAttributes(display, root, &gwa);
int width = gwa.width;
int height = gwa.height;
XImage *image = XGetImage(display,root, 0,0 , width,height,AllPlanes, ZPixmap);
unsigned char *array = new unsigned char[width * height * 3];
unsigned long red_mask = image->red_mask;
unsigned long green_mask = image->green_mask;
unsigned long blue_mask = image->blue_mask;
for (int x = 0; x < width; x++)
for (int y = 0; y < height ; y++)
{
unsigned long pixel = XGetPixel(image,x,y);
unsigned char blue = pixel & blue_mask;
unsigned char green = (pixel & green_mask) >> 8;
unsigned char red = (pixel & red_mask) >> 16;
array[(x + width * y) * 3] = red;
array[(x + width * y) * 3+1] = green;
array[(x + width * y) * 3+2] = blue;
}
CImg<unsigned char> pic(array,width,height,1,3);
pic.save_png("blah.png");
printf("%ld %ld %ld\n",red_mask>> 16, green_mask>>8, blue_mask);
return 0;
}
You are mistaken about the way array is laid out in memory, as you can find out by declaring img before the loop and adding this printf to your inner loop:
printf("%ld %ld %u %u %u\n",x,y,pic.offset(x,y,0),pic.offset(x,y,1),pic.offset(x,y,2));
This yields (on my 1920x1200 screen):
0 0 0 2304000 4608000
0 1 1920 2305920 4609920
0 2 3840 2307840 4611840
and so on, indicating that the red/green/blue subimages are kept "together" instead of the three color components of a single pixel being adjacent to each other.
The builtin CImg accessors will make your code work:
pic(x,y,0) = red;
pic(x,y,1) = green;
pic(x,y,2) = blue;
You can use libpng
int code = 0;
FILE *fp;
png_structp png_ptr;
png_infop png_info_ptr;
png_bytep png_row;
// Open file
fp = fopen ("test.png", "wb");
if (fp == NULL){
fprintf (stderr, "Could not open file for writing\n");
code = 1;
}
// Initialize write structure
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL){
fprintf (stderr, "Could not allocate write struct\n");
code = 1;
}
// Initialize info structure
png_info_ptr = png_create_info_struct (png_ptr);
if (png_info_ptr == NULL){
fprintf (stderr, "Could not allocate info struct\n");
code = 1;
}
// Setup Exception handling
if (setjmp (png_jmpbuf (png_ptr))){
fprintf(stderr, "Error during png creation\n");
code = 1;
}
png_init_io (png_ptr, fp);
// Write header (8 bit colour depth)
png_set_IHDR (png_ptr, png_info_ptr, width, height,
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
// Set title
char *title = "Screenshot";
if (title != NULL){
png_text title_text;
title_text.compression = PNG_TEXT_COMPRESSION_NONE;
title_text.key = "Title";
title_text.text = title;
png_set_text (png_ptr, png_info_ptr, &title_text, 1);
}
png_write_info (png_ptr, png_info_ptr);
// Allocate memory for one row (3 bytes per pixel - RGB)
png_row = (png_bytep) malloc (3 * width * sizeof (png_byte));
// Write image data
int x, y;
for (y = 0; y < height; y++){
for (x = 0; x < width; x++){
unsigned long pixel = XGetPixel (image, x, y);
unsigned char blue = pixel & blue_mask;
unsigned char green = (pixel & green_mask) >> 8;
unsigned char red = (pixel & red_mask) >> 16;
png_byte *ptr = &(png_row[x*3]);
ptr[0] = red;
ptr[1] = green;
ptr[2] = blue;
}
png_write_row (png_ptr, png_row);
}
// End write
png_write_end (png_ptr, NULL);
// Free
fclose (fp);
if (png_info_ptr != NULL) png_free_data (png_ptr, png_info_ptr, PNG_FREE_ALL, -1);
if (png_ptr != NULL) png_destroy_write_struct (&png_ptr, (png_infopp)NULL);
if (png_row != NULL) free (png_row);
image has to stored in memory as R1R2R3R4R5R6......G1G2G3G4G5G6.......B1B2B3B4B5B6.
cimg storage