raw bitmap data to jpeg or png C++ - c++

I have bytearray where every three bytes describes 1 pixel (RGB). The task is to convert it to jpeg or png.
Actually, I am using Zint (open source lib for generating barcodes) that uses libpng to generate image file and save it to file system, but in Zintthe function png_plot() except generating image also save it on disk which is undesirable.
As result I think there two ways:
1. from bitmap bytearray to bmp -> jpeg / png (using some other lib)
2. writing hook or some similar to png_plot()
Can you give me some advices?
Thank you.
Upd: for #peacemaker
FILE *f;
zint_symbol *my_symbol;
my_symbol = ZBarcode_Create();
ZBarcode_Encode_and_Buffer(my_symbol, (unsigned char *)argv[1], 0, 0);
f = fopen("bitmap.bmp", "w");
fwrite(my_symbol->bitmap, sizeof(*(my_symbol->bitmap)), my_symbol->bitmap_height * my_symbol->bitmap_width, f);
ZBarcode_Delete(my_symbol);
fclose(f);

In order to convert between image formats, the easiest way would be using the class CImage shared by MFC and ATL and defined in the header file atlimage.h.
CImage image;
HRESULT res = image.Load("in.bmp");
image.Save("out.jpg");
image.Save("out.gif");
image.Save("out.png");
image.Save("out.tif");
If you have a RGB buffer and want to create a bitmap: just create and save a bitmap header into a file and add the RGB buffer to it.
To create the header you can use the BITMAPFILEHEADER, BITMAPINFOHEADER and RGBQUAD structures from GDI defined in the header WinGDI.h
Here is an example on how to fill the header data:
BITMAPINFOHEADER bmpInfoHdr;
bmpInfoHdr.biSize = sizeof(BITMAPINFOHEADER);
bmpInfoHdr.biHeight = nHeight;
bmpInfoHdr.biWidth = nWidthPadded;
bmpInfoHdr.biPlanes = 1;
bmpInfoHdr.biBitCount = bitsPerPixel;
bmpInfoHdr.biSizeImage = nHeight * nWidthPadded * nSPP;
bmpInfoHdr.biCompression = BI_RGB;
bmpInfoHdr.biClrImportant = 0;
bmpInfoHdr.biClrUsed = 0;
bmpInfoHdr.biXPelsPerMeter = 0;
bmpInfoHdr.biYPelsPerMeter = 0;
bmpFileHdr.bfType = BITMAP_FORMAT_BMP;
bmpFileHdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + bmpInfoHdr.biSize +
sizeof(RGBQUAD)*numColors + bmpInfoHdr.biSizeImage);
bmpFileHdr.bfReserved1 = 0;
bmpFileHdr.bfReserved2 = 0;
bmpFileHdr.bfOffBits = (DWORD) (sizeof(BITMAPFILEHEADER) + bmpInfoHdr.biSize +
sizeof(RGBQUAD)*numColors);
Keep into account that the bitmaps are stored upside-down and that the width of the image must be aligned on a DWORD except for RLE-compressed bitmaps.(they must be multiple of 4 bytes, add a padding if necessary).
if ((nWidth%4) != 0)
nPadding = ((nWidth/4) + 1) * 4;
When saving your buffer, add the needed padding to each row...
Summarizing, these are the needed steps to create a bitmap file from a rgb buffer:
//1. create bmp header
//2. save header to file:
write(file, &bmpFileHdr, sizeof(BITMAPFILEHEADER));
write(file, &bmpInfoHdr, sizeof(BITMAPINFOHEADER));
write(file, &colorTable, numColors * sizeof(RGBQUAD));
//3. add rgb buffer to file:
for(int h=0; h<nHeight; h++) {
for(int w=0; w<nWidth; w++) {
//3.a) add row to file
//3.b) add padding for this row to file
}
}

I used the CImage Class from ATL.
int width=0, height=0;
char * val = "9788994774480";
zint_symbol *my_symbol;
my_symbol = ZBarcode_Create();
//ZBarcode_Encode_and_Buffer(my_symbol,(unsigned char *) val, 0, 0);
ZBarcode_Encode(my_symbol, (unsigned char *) val, 0);
ZBarcode_Buffer(my_symbol, 0);
height = my_symbol->bitmap_height;
width = my_symbol->bitmap_width;
char * imgBits = my_symbol->bitmap;
CImage img;
img.Create(width, height, 24 /* bpp */, 0 /* No alpha channel */);
int nPixel = 0;
for(int row = 0; row < height; row++)
{
for(int col = 0; col < width; col++)
{
BYTE r = (BYTE)imgBits[nPixel];
BYTE g = (BYTE)imgBits[nPixel+1];
BYTE b = (BYTE)imgBits[nPixel+2];
img.SetPixel(col, row , RGB(r, g, b));
nPixel += 3;
}
}
img.Save("CImage.bmp", Gdiplus::ImageFormatBMP);
ZBarcode_Delete(my_symbol);

is there anyway to do this other than using SetPixel? I am experiencing major performance issues with SetPixel and need an alternative method... I have tried using CreateDIBSection to no avail. The barcode displays slanted and is unusable. here is my code for that:
void *bits = (unsigned char*)(my_symbol->bitmap);
HBITMAP hBitmap = CreateDIBSection(pDC->GetSafeHdc(), &info, DIB_RGB_COLORS, (void **)&pDestData, NULL, 0);
memcpy(pDestData, my_symbol->bitmap, info.bmiHeader.biSizeImage);
img.Attach(hBitmap);
Another option that produces the same result is this:
BITMAPINFO info;
BITMAPINFOHEADER BitmapInfoHeader;
BitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfoHeader.biWidth = my_symbol->bitmap_width;
BitmapInfoHeader.biHeight = -(my_symbol->bitmap_height);
BitmapInfoHeader.biPlanes = 1;
BitmapInfoHeader.biBitCount = 24;
BitmapInfoHeader.biCompression = BI_RGB;
BitmapInfoHeader.biSizeImage = 0;
BitmapInfoHeader.biXPelsPerMeter = 0;
BitmapInfoHeader.biYPelsPerMeter = 0;
BitmapInfoHeader.biClrUsed = 0;
BitmapInfoHeader.biClrImportant = 0;
info.bmiHeader = BitmapInfoHeader;
HBITMAP hbmp = CreateDIBitmap(dc, &BitmapInfoHeader, CBM_INIT, (LPVOID *)my_symbol->bitmap, (LPBITMAPINFO)&info, DIB_RGB_COLORS);
img.Attach(hbmp);

Related

What should be a BITMAPINFOHEADER construction If biCompression is BI_PNG?

I need suggestions about the BITMAPINFOHEADER construction for PNG images. This bitmap info header will be used to save a buffer in png file format using GdiPlus::Bitmap. My image buffer is a 32 bit PNG image.
I have done the following but GdiPlus::Bitmap::GetLastStatus() says "Invalid Parameter" and I can't save the buffer to a png file format.
void CreateBitmapInfo(long lWidth, long lHeight, bool bColor, CComHeapPtr<BITMAPINFO>& apBitmapInfo)
{
const auto bitCount = 32;
const auto colorUsed = 2;
long lBitmapInfoSize = sizeof(BITMAPINFOHEADER);
if (!bColor)
lBitmapInfoSize += colorUsed * sizeof(RGBQUAD);
TRY_CONDITION(apBitmapInfo.AllocateBytes(lBitmapInfoSize));
::memset(apBitmapInfo, 0, lBitmapInfoSize);
apBitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
apBitmapInfo->bmiHeader.biWidth = lWidth;
apBitmapInfo->bmiHeader.biHeight = lHeight;
apBitmapInfo->bmiHeader.biPlanes = 1;
apBitmapInfo->bmiHeader.biBitCount = bitCount;
apBitmapInfo->bmiHeader.biCompression = BI_PNG;
apBitmapInfo->bmiHeader.biSizeImage = gsl::narrow_cast<uint32_t>(((lWidth + 7) & 0xFFFFFFF8) * lHeight / 8); // zero for BI_RGB
apBitmapInfo->bmiHeader.biXPelsPerMeter = 0;
apBitmapInfo->bmiHeader.biYPelsPerMeter = 0;
apBitmapInfo->bmiHeader.biClrUsed = bColor ? 0 : colorUsed;
apBitmapInfo->bmiHeader.biClrImportant = 0;
if (!bColor)
{
// create a gray scale color table
for (int i = 0; i < colorUsed; i++)
{
RGBQUAD& rgb = *(apBitmapInfo->bmiColors + i);
rgb.rgbRed = rgb.rgbGreen = rgb.rgbBlue = gsl::narrow_cast<BYTE>(i);
}
}
}

Can I output a one channel image acquired from camera into a winAppi window?

I have a pointer to an image acquired from acamera using a third party SDK. The image is one band (Mono 8). I want to output it as a bittmap into a winAppi window. What i do is
HBITMAP hBitmap = NULL;
BITMAPINFOHEADER bmih;
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = 1280;
bmih.biHeight = 960;
bmih.biPlanes = 1;
bmih.biBitCount = 8;
bmih.biCompression = BI_RGB;
bmih.biSizeImage = 0;
bmih.biXPelsPerMeter = 0;
bmih.biYPelsPerMeter = 0;
bmih.biClrUsed = 0;
bmih.biClrImportant = 0;
BITMAPINFO dbmi;
ZeroMemory(&dbmi, sizeof(dbmi));
dbmi.bmiHeader = bmih;
dbmi.bmiColors->rgbBlue = 0;
dbmi.bmiColors->rgbGreen = 0;
dbmi.bmiColors->rgbRed = 0;
dbmi.bmiColors->rgbReserved = 0;
void* bits = &aquiredImageCPU.m_sMemory.ptr()[0];
hBitmap = CreateDIBitmap(dc, &bmih, CBM_INIT, bits, &dbmi, DIB_RGB_COLORS);
src = CreateCompatibleDC(dc);
SelectObject(src, hBitmap);
BitBlt(dc, 10,10,512, 512, src, 0, 0, SRCCOPY);
If i output the image as matrix and previou it using my library (a library where i use imsave similiar to matlabs imsave) i can see that the image is ok(grayscale image). But when i output it to winAppi window it tranforms it to RGB. I think it has to do with
HDC dc = GetDC(hwnd);
src = CreateCompatibleDC(dc);
Any suggestions?
Thank you
What i did in a similiar case is to take every 8 bits and to copy them 2 more times after the chunk of 8 bits. That works but it is costly and i need to have a real tile application.
auto outputImageHight = 1280;
auto outputImageWidth = 960;
unsigned char *myArray = new unsigned char[3 * outputImageHight * outputImageWidth];
for (int i = 0; i < outputImageHight; i++)
for (int j = 0; j < outputImageWidth; j++)
{
unsigned char ucTmp = (unsigned char)(image(i, j));
myArray[3 * (i + outputImageHight * j) + 0] = ucTmp;
myArray[3 * (i + outputImageHight * j) + 1] = ucTmp;
myArray[3 * (i + outputImageHight * j) + 2] = ucTmp;
}
An 8-bit bitmap requires a color table. Since you want grayscale, you have to set up the color table to have 256 levels of gray. You've set the first one to black, which is correct, but you haven't set the rest.
BITMAPINFO is actually a variably sized structure. The bmiColors field is just a placeholder for the first color in the color table. You have to allocate extra space for the entire color table and fill it out.
std::size_t size = sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD);
std::vector<char> buffer(size);
BITMAPINFO *dbmi = reinterpret_cast<BITMAPINFO *>(buffer.data());
ZeroMemory(dbmi, size); // probably unnecessary
dbmi->bmiHeader = bmih;
for (int i = 0; i < 256; ++i) {
dbmi->bmiColors[i].rgbBlue = i;
dbmi->bmiColors[i].rgbGreen = i;
dbmi->bmiColors[i].rgbRed = i;
dbmi->bmiColors[i].rgbReserved = 0;
}

How to display jpg image by ImageMagicK in MFC?

I have got a hard time to display jpg file at VIEW class by ImageMagicK in MFC.
The following code are my code. the problem is looking as following
image
void CTestview::DoDisplayImage(void)
{
CDC *pDC = GetDC();
if (fileposition != NULL)
AfxMessageBox(fileposition);
char m_szAppPath[255];
//m_szAppPath = (char) * fileposition;
(void)MagickCore::SetClientPath(fileposition);
InitializeMagick(fileposition);
MagickCore::RegisterStaticModules();
// Parse command line for standard shell commands, DDE, file open
Image m_Image;
m_Image.read(fileposition);
/*
char m_szAppPath[255];
(void) MagickCore::SetClientPath(m_szAppPath);
InitializeMagick(m_szAppPath);
MagickCore::RegisterStaticModules();
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
Image master;
master.read("D:\\work\\mfc_test5\\q1.jpg");
*/
if (pDC != NULL && m_Image.isValid())
{
CRect rectClient;
GetClientRect(rectClient);
// Clear the background
pDC->FillSolidRect(rectClient, pDC->GetBkColor());
// Set up the Windows bitmap header
BITMAPINFOHEADER bmi;
bmi.biSize = sizeof(BITMAPINFOHEADER); // Size of structure
bmi.biWidth = m_Image.columns(); // Bitmaps width in pixels
bmi.biHeight = (-1)*m_Image.rows(); // Bitmaps height n pixels
bmi.biPlanes = 1; // Number of planes in the image
bmi.biBitCount = 32; // The number of bits per pixel
bmi.biCompression = BI_RGB; // The type of compression used
bmi.biSizeImage = 0; // The size of the image in bytes
bmi.biXPelsPerMeter = 0; // Horizontal resolution
bmi.biYPelsPerMeter = 0; // Veritical resolution
bmi.biClrUsed = 0; // Number of colors actually used
bmi.biClrImportant = 0; // Colors most important
// Extract the pixels from Magick++ image object and convert to a DIB section
PixelPacket *pPixels = m_Image.getPixels(0, 0, m_Image.columns(), m_Image.rows());
RGBQUAD *prgbaDIB = 0;
HBITMAP hBitmap = CreateDIBSection
(
pDC->m_hDC, // handle to device context
(BITMAPINFO *)&bmi, // pointer to structure containing bitmap size, format, and color data
DIB_RGB_COLORS, // color data type indicator: RGB values or palette indices
(void**)&prgbaDIB, // pointer to variable to receive a pointer to the bitmap's bit values
NULL, // optional handle to a file mapping object
0 // offset to the bitmap bit values within the file mapping object
);
if (!hBitmap)
return;
unsigned long nPixels = m_Image.columns() * m_Image.rows();
RGBQUAD *pDestPixel = prgbaDIB;
// Transfer pixels, scaling to Quantum
/*
for( unsigned long nPixelCount = nPixels; nPixelCount ; nPixelCount-- )
{
pDestPixel->rgbRed = MagickCore::GetPixelRed(m_Image.constImage(),pPixels)/257;
pDestPixel->rgbGreen = MagickCore::GetPixelGreen(m_Image.constImage(),pPixels)/257;
pDestPixel->rgbBlue = MagickCore::GetPixelBlue(m_Image.constImage(),pPixels)/257;
pDestPixel->rgbReserved = 0;
++pDestPixel;
pPixels+=MagickCore::GetPixelChannels(m_Image.constImage());
}
*/
for (int i = 0; i < 256; i++)
{
pPixels->blue = (BYTE)i;
pPixels->green = (BYTE)i;
pPixels->red = (BYTE)i;
pPixels->opacity = 0;
pPixels++;
}
// Now copy the bitmap to device.
HDC hMemDC = CreateCompatibleDC(pDC->m_hDC);
SelectObject(hMemDC, hBitmap);
BitBlt(pDC->m_hDC, 0, 0, m_Image.columns(), m_Image.rows(), hMemDC, 0, 0, SRCCOPY);
DeleteObject(hMemDC);
}
}
Especially, at the following code does not sure.
What should I do for solving this problem?
for( int i = 0 ; i < 256 ; i++ )
{
pPixels->blue = (BYTE)i;
pPixels->green = (BYTE)i;
pPixels->red = (BYTE)i;
pPixels->opacity = 0;
pPixels++;
}
Would you please help me how do I do for solving this problem?
As you can see, the output image has black.

C++ Saving a Bitmap File

so what I am trying to do is to have my program take a screenshot and save it on the computer. The part of actually taking the screenshot I will program later, and I am first trying to solve the problem of how to actually save a bmp file on the computer. I found the following code that would help me out with that:
// szPathName : Specifies the pathname
// lpBits : Specifies the bitmap bits
// w : Specifies the image width
// h : Specifies the image height
bool SaveImage(char* szPathName, void* lpBits, int w, int h)
{
//Create a new file for writing
FILE *pFile = fopen(szPathName, "wb");
if(pFile == NULL)
{
return false;
}
BITMAPINFOHEADER BMIH;
BMIH.biSize = sizeof(BITMAPINFOHEADER);
BMIH.biSizeImage = w * h * 3;
// Create the bitmap for this OpenGL context
BMIH.biSize = sizeof(BITMAPINFOHEADER);
BMIH.biWidth = w;
BMIH.biHeight = h;
BMIH.biPlanes = 1;
BMIH.biBitCount = 24;
BMIH.biCompression = BI_RGB;
BMIH.biSizeImage = w * h* 3;
BITMAPFILEHEADER bmfh;
int nBitsOffset = sizeof(BITMAPFILEHEADER) + BMIH.biSize;
LONG lImageSize = BMIH.biSizeImage;
LONG lFileSize = nBitsOffset + lImageSize;
bmfh.bfType = 'B'+('M'<<8);
bmfh.bfOffBits = nBitsOffset;
bmfh.bfSize = lFileSize;
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
//Write the bitmap file header
UINT nWrittenFileHeaderSize = fwrite(&bmfh, 1,
sizeof(BITMAPFILEHEADER), pFile);
//And then the bitmap info header
UINT nWrittenInfoHeaderSize = fwrite(&BMIH,
1, sizeof(BITMAPINFOHEADER), pFile);
//Finally, write the image data itself
//-- the data represents our drawing
UINT nWrittenDIBDataSize =
fwrite(lpBits, 1, lImageSize, pFile);
fclose(pFile);
return true;
}
So what is the issue.... Well I do not understand the varialbe IpBits. there is a brief explanation of the lpBits in the comments of the code (lpBits: Specifies the bitmap bits)... but I don't know what that actually means. I tried going into msdn and looking into the fopen and fclose functions since fclose is the function that will eventually use the lpbits that I pass to the SaveImage function.... and well it seemed that the lpBits variable in the fclose function varies depending on what variable was passed in the fopen function. I tried to find out what the "wb" of the fopen function means but was unsuccessful (even searching on msdn).
QUESTION: in the case that I use "wb" as the second variable in the fopen function in my previous code, what exactly would the lpBits in the fclose function be? When I ask what exactly would it be, I mean... what type of variable is it (in the code it is placed as void* which basically allows it to be any variable) and I would appriciate any feedback you could give.
Thanks guys!
lpBits refers to an array of bytes with size lImageSize.
Each byte of the array will contain a single color component, in this order: B, G and R: each pixel takes three bytes, one for each color component.
Please note that the code you posted doesn't take into consideration the 4 bytes alignment of each image's row. Each image's row must be aligned on a 4 bytes boundary, so the correct formula for lImageSize is:
lImageSize = h * ((w * 3 + 3) & 0xfffffffc);
You can create the lpbits by yourself:
lpbits = new BYTE[lImageSize];
or by using CreateDIBSection() as stated in the answer from Logicrat
Commenting the code:
// lpBits stand for long pointer bits
// szPathName : Specifies the pathname -> the file path to save the image
// lpBits : Specifies the bitmap bits -> the buffer (content of the) image
// w : Specifies the image width
// h : Specifies the image height
bool SaveImage(char* szPathName, void* lpBits, int w, int h) {
// Create a new file for writing
FILE* pFile = fopen(szPathName, "wb"); // wb -> w: writable b: binary, open as writable and binary
if (pFile == NULL) {
return false;
}
BITMAPINFOHEADER BMIH; // BMP header
BMIH.biSize = sizeof(BITMAPINFOHEADER);
BMIH.biSizeImage = w * h * 3;
// Create the bitmap for this OpenGL context
BMIH.biSize = sizeof(BITMAPINFOHEADER);
BMIH.biWidth = w;
BMIH.biHeight = h;
BMIH.biPlanes = 1;
BMIH.biBitCount = 24;
BMIH.biCompression = BI_RGB;
BMIH.biSizeImage = w * h * 3;
BITMAPFILEHEADER bmfh; // Other BMP header
int nBitsOffset = sizeof(BITMAPFILEHEADER) + BMIH.biSize;
LONG lImageSize = BMIH.biSizeImage;
LONG lFileSize = nBitsOffset + lImageSize;
bmfh.bfType = 'B' + ('M' << 8);
bmfh.bfOffBits = nBitsOffset;
bmfh.bfSize = lFileSize;
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
// Write the bitmap file header // Saving the first header to file
UINT nWrittenFileHeaderSize = fwrite(&bmfh, 1, sizeof(BITMAPFILEHEADER), pFile);
// And then the bitmap info header // Saving the second header to file
UINT nWrittenInfoHeaderSize = fwrite(&BMIH, 1, sizeof(BITMAPINFOHEADER), pFile);
// Finally, write the image data itself
//-- the data represents our drawing // Saving the file content in lpBits to file
UINT nWrittenDIBDataSize = fwrite(lpBits, 1, lImageSize, pFile);
fclose(pFile); // closing the file.
return true;
}
Some improvement to substitute the C code with C++:
The improvement were:
Using std::string instead of char* that originally need to be const char*
Using vector instead of void* (could be a problem in the original code, if the width and height provided was wrong or miscalculated the program will read invalid memory because there is no notion of size of lpBits. The content of the file not need to change when saving, adding const-correctness
Using std::ofstream instead of FILE.
Code:
// lpBits stand for long point bits
// szPathName : Specifies the pathname -> the file path to save the image
// lpBits : Specifies the bitmap bits -> the buffer (content of the) image
// w : Specifies the image width
// h : Specifies the image height
bool SaveImage(const std::string& szPathName, const std::vector<char>& lpBits, int w, int h) {
// Create a new file for writing
std::ofstream pFile(szPathName, std::ios_base::binary);
if (!pFile.is_open()) {
return false;
}
BITMAPINFOHEADER bmih;
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = w;
bmih.biHeight = h;
bmih.biPlanes = 1;
bmih.biBitCount = 24;
bmih.biCompression = BI_RGB;
bmih.biSizeImage = w * h * 3;
BITMAPFILEHEADER bmfh;
int nBitsOffset = sizeof(BITMAPFILEHEADER) + bmih.biSize;
LONG lImageSize = bmih.biSizeImage;
LONG lFileSize = nBitsOffset + lImageSize;
bmfh.bfType = 'B' + ('M' << 8);
bmfh.bfOffBits = nBitsOffset;
bmfh.bfSize = lFileSize;
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
// Write the bitmap file header
pFile.write((const char*)&bmfh, sizeof(BITMAPFILEHEADER));
UINT nWrittenFileHeaderSize = pFile.tellp();
// And then the bitmap info header
pFile.write((const char*)&bmih, sizeof(BITMAPINFOHEADER));
UINT nWrittenInfoHeaderSize = pFile.tellp();
// Finally, write the image data itself
//-- the data represents our drawing
pFile.write(&lpBits[0], lpBits.size());
UINT nWrittenDIBDataSize = pFile.tellp();
pFile.close();
return true;
}
Study the Windows API CreateDIBSection(). With this API, Windows will allocate the memory for the pixels you need. When it allocates the memory, it will give you the memory address in a long pointer. That is what "lpbits" refers to - a long pointer to the bits that were allocated.
The "wb" in fopen() means "Write Binary". Without the "b" (i.e., if you use only a "w" in the second parameter), fopen will open the file in text mode, which will cause the file to be written as though it were text, which may change the '\n' character in a system-dependent way.
Here is a constructor I use for class PixMapAny, which is primarily used for off-screen drawing but can also be used to read pixmaps.
PixMapAny::PixMapAny(int width, int height, int depth)
{
m_dc.CreateCompatibleDC(NULL);
m_width = width;
m_height = height;
m_depth = depth;
// The declaration of 'fake' creates a storage area big enough to
// contain a BITMAPINFO structure composed of a BITMAPINFOHEADER
// and a 256-element array of RGBQUAD values.
long fake[266];
LPBITMAPINFO pbmi = (LPBITMAPINFO) fake;
// Initialize the area to all zeros
for(int x = 0; x < 266; x++) fake[x] = 0;
// Fill in the header with the characteristics of the bitmap we want
// to write.
pbmi->bmiHeader.biSize = sizeof(pbmi->bmiHeader);
pbmi->bmiHeader.biWidth = m_width;
pbmi->bmiHeader.biHeight = -m_height;
pbmi->bmiHeader.biPlanes = 1;
pbmi->bmiHeader.biBitCount = 24;
pbmi->bmiHeader.biCompression = BI_RGB;
// Tell the system to allocate room for the pixmap.
// 'ppvbits' receives a pointer to the pixmap memory.
m_dib = CreateDIBSection(m_dc.m_hDC, pbmi, DIB_RGB_COLORS, &m_ppvbits, NULL, 0);
// ____________________________________________________________________________
// Select the bitmap into the device context
m_prev = (CBitmap *) m_dc.SelectObject(m_dib);
// ____________________________________________________________________________
}
In this example, the height is negative because the rows in the pixmap will be ordered in a top-down fashion, i.e., the address of the top row is less than the address of the bottom row.
Once a pixmap has been constructed in this manner, copying into an area of an open windows is easy. Here, pDC is a pointer to the target window's device context, and x and y are coordinates within that window:
void PixMapAny::Blit(int x, int y, CDC * pDC)
{
pDC->BitBlt(x,y,m_width,m_height,&m_dc,0,0,SRCCOPY);
}

How to make 8-bit bitmap appear as monochrome in C++?

When I set up and create a 24-bit bitmap like this:
//fileheader
BITMAPFILEHEADER* bf = new BITMAPFILEHEADER;
bf->bfType = 0x4d42;
bf->bfSize = 6054400 + 54;
bf->bfOffBits = 54;
//infoheader
BITMAPINFOHEADER* bi = new BITMAPINFOHEADER;
bi->biSize = 40;
bi->biWidth = 2752;
bi->biHeight = -733;
bi->biPlanes = 1;
bi->biBitCount = 24;
bi->biCompression = 0;
//bi->biSizeImage = 6054400;
bi->biXPelsPerMeter = 2835;
bi->biYPelsPerMeter = 2835;
bi->biClrUsed = 0;
bi->biClrImportant = 0;
pFrame->GetImage(m_imageData);
//
//create bitmap...
//(hbit is a global variable)
BITMAPINFO* bmi;
bmi = (BITMAPINFO*)bi;
HDC hdc = ::GetDC(NULL);
hbit = CreateDIBitmap(hdc, bi, CBM_INIT, m_imageData, bmi, DIB_RGB_COLORS);
I get an output image like this:
But when I change bitcount from 24 to 8 (which also allows for 3x image size, allowing me to go from 733 width to the image's natural width of 2200), I get an image like this (along with a lot of instability):
My output looks like this:
BITMAP* bi = new BITMAP;
CBitmap bmp;
bmp.Attach(hbit);
CClientDC dc(pWnd);
CDC bmDC;
bmDC.CreateCompatibleDC(&dc);
CBitmap *pOldbmp = bmDC.SelectObject(&bmp);
bmp.GetBitmap(bi);
dc.BitBlt(384,26,bi->bmWidth/3,bi->bmHeight,&bmDC,0,0,SRCCOPY);
//note: if bitcount is 8, height and width need to be /3,
//if 24, only width gets /3
bmDC.SelectObject(pOldbmp);
//explicitly delete everything just to be safe
delete bi;
DeleteObject(bmp);
DeleteObject(dc);
DeleteObject(pOldbmp);
DeleteObject(bmDC);
So my questions are:
Why is this happening when I switch from 24 to 8?
Is there an easy way to output the image as monochrome rather than color?
One last thing:
My coworker wrote this function a long time ago for a similar issue, but he said I may be able to use it. I can't get it to work, unfortunately:
void CopyMono8ToBgrx(byte* pDestBlue, byte* pDestGreen, byte *pDestRed, byte* pDestAlpha)
{
byte* pSrc;
byte* pSrcEnd;
pSrc = ( byte* ) m_imageData;
pSrcEnd = pSrc + ( 2752*2200 );
while ( pSrc < pSrcEnd )
{
byte data = *pSrc;
*pDestBlue = data;
*pDestGreen = data;
*pDestRed = data;
*pDestAlpha = 255; // alpha is always 255 (fully opaque)
pSrc++;
pDestBlue += 4;
pDestGreen += 4;
pDestRed += 4;
pDestAlpha += 4;
}
}
You should create a color pallete. Try this:
struct BITMAPINFO256 {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[256];
} bmi;
memset(&bmi, 0, sizeof(BITMAPINFO256));
bmi.bmiHeader.biSize = 40;
bmi.bmiHeader.biWidth = 2752;
bmi.bmiHeader.biHeight = -733;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 8;
bmi.bmiHeader.biCompression = 0;
bmi.bmiHeader.biXPelsPerMeter = 2835;
bmi.bmiHeader.biYPelsPerMeter = 2835;
bmi.bmiHeader.biClrUsed = 256;
bmi.bmiHeader.biClrImportant = 0;
for (unsigned int i = 0; i < 256; i++) {
bmi.bmiColors[i].rgbRed = i;
bmi.bmiColors[i].rgbGreen = i;
bmi.bmiColors[i].rgbBlue = i;
}
And then when you call CreateDIBitmap it will become:
hbit = CreateDIBitmap(hdc, &bmi.bmiHeader, CBM_INIT, m_imageData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);
Also note that you should be careful to also increase the offset in your BITMAPFILEHEADER so that it it expresses that there is color pallete defined before the actual pixels data (yesterday I was having hard time because of this, see Creating 8bpp bitmap with GDI and saving it as a file):
bf->bfOffBits = 54 + sizeof(RGBQUAD)*256;
And to that function that your coworker wrote: It's better to use Luminance to convert colors to gray-scale equivalents:
Hope this helps :)
8 bit per pixel images are assuming a color palette following BITMAPINFOHEADER structure (see BITMAPINFO::bmiColors). If you make the palette to be 256 gray shades, the image is going to me 8 bpp grayscale. Now it's color with random colors on it.
The function CopyMono8ToBgrx you quoted creates full color bitmap, with gray individual pixels (R=G=B).