How to make 8-bit bitmap appear as monochrome in C++? - 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).

Related

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.

Scaling bitmap output distorts image

I have a 2752x2200 bitmap image. I can only display 1/3 of it on my MFC dialog box (for obvious size issues), so if I don't scale the image I only get the top-left 917x733 block (the top-left 1/3 block). I want to zoom the image out by a factor of 3 so that the whole image is dislayed in an area the size of 1/3 of the image. I have set up the grayscale bitmap like so:
//////////////////////////////////////////////////////////////////////////
////////// Setup Bitmap ////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//// FILEHEADER ////
BITMAPFILEHEADER* bf = new BITMAPFILEHEADER;
bf->bfType = 0x4d42;
bf->bfSize = 6054400 + 54 + sizeof(BITMAPINFO);
bf->bfOffBits = 54;
//// INFOHEADER ////
BITMAPINFOHEADER* bi = new BITMAPINFOHEADER;
bi->biSize = 40;
bi->biWidth = 2752;
bi->biHeight = -2200;
bi->biPlanes = 1;
bi->biBitCount = 8;
bi->biCompression = 0;
//bi->biSizeImage = 6054400; //not required
bi->biXPelsPerMeter = 2835;
bi->biYPelsPerMeter = 2835;
bi->biClrUsed = 0;
bi->biClrImportant = 0;
//// INFO ////
BITMAPINFO* pbmi = (BITMAPINFO*)alloca( sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD)*256);
pbmi->bmiHeader.biSize = sizeof (pbmi->bmiHeader);
pbmi->bmiHeader.biWidth = 2752;
pbmi->bmiHeader.biHeight = -2200;
pbmi->bmiHeader.biPlanes = 1;
pbmi->bmiHeader.biBitCount = 8;
pbmi->bmiHeader.biCompression = BI_RGB;
pbmi->bmiHeader.biSizeImage = 0;
pbmi->bmiHeader.biXPelsPerMeter = 14173;
pbmi->bmiHeader.biYPelsPerMeter = 14173;
pbmi->bmiHeader.biClrUsed = 0;
pbmi->bmiHeader.biClrImportant = 0;
//create grayscale color palette
for(int i=0; i<256; i++)
{
pbmi->bmiColors[i].rgbRed = i;
pbmi->bmiColors[i].rgbGreen = i;
pbmi->bmiColors[i].rgbBlue = i;
pbmi->bmiColors[i].rgbReserved = 0;
}
//// IMAGE DATA ////
pFrame->GetImage(m_imageData);
//////////////////////////////////////////////////////////////////////
////// Create image that's printed to dialog box /////////////////////
//////////////////////////////////////////////////////////////////////
HDC hdc = ::GetDC(NULL);
hbit = CreateDIBitmap(hdc, bi, CBM_INIT, m_imageData,
pbmi, DIB_RGB_COLORS);
And then I'm drawing the bitmap onto my dialog box 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.StretchBlt(384,21,bi->bmWidth,bi->bmHeight,&bmDC,0,0,
bi->bmWidth,bi->bmHeight,SRCCOPY);
bmDC.SelectObject(pOldbmp);
The image looks fine with this code, but it's only the top left block of the full image:
In my attempt to scale the image down, I changed the line:
dc.StretchBlt(384,21,bi->bmWidth,bi->bmHeight,&bmDC,0,0,
bi->bmWidth,bi->bmHeight,SRCCOPY);
to:
dc.StretchBlt(384,21,bi->bmWidth/3,bi->bmHeight/3,&bmDC,0,0,
bi->bmWidth,bi->bmHeight,SRCCOPY); // 1/3 original size
Now my output looks zoomed out and it's showing the whole image(good), but everything looks distorted (bad):
(Note the circular rings around the border of the image. Those shouldn't be there and when you actually see the live video of my image stream, they pulsate and basically ruin the image).
My question is: What is causing this distortion and is there something simple I can do to fix it?
EDIT: After trying StretchDIBits():
StretchDIBits(dc.m_hDC, 384, 21, bi->bmWidth/3, bi->bmHeight/3, 0,
0,bi->bmWidth,bi->bmHeight, myObv->GetImageData(),
myObv->GetPBMI(), DIB_RGB_COLORS, SRCCOPY);
my output looked like this:
i.imgur.com/DA49P8x.png
If you are zooming the bitmap, the windows api may not perform well visually because it is designed to optimized for performance. Add the following line before StretchBlt to enhance the bitmap operation:
SetStretchBltMode(dc.m_hDC, HALFTONE);
Instead of StretchBLT try StretchDIBits:
StretchDIBits(dc.m_hDC, 384, 21, bi->bmWidth/3, bi->bmHeight/3, 0, 0,
bi->bmWidth, bi->bmHeight, m_imageData, pbmi, DIB_RGB_COLORS, SRCCOPY);

raw bitmap data to jpeg or png 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);

Drawing on 8bpp grayscale bitmap (unmanaged C++)

I've been attempting to draw on an 8bpp grayscale bitmap without success. Here are some of my attempts. Maybe someone can point out what I'm doing wrong.
===================================================
Attempt 1: Create, select, and draw:
In constructor:
CBitmap bm;
bm.CreateBitmap (200, 200, 1, 8, NULL);
In OnDraw:
CDC *mdc=new CDC ();
HGDIOBJ tmp = mdc->SelectObject(bm);
Result: tmp is NULL, indicating failure.
===================================================
Attempt 2: CreateDIBSection
In constructor:
HBITMAP hbm;
BITMAPINFOHEADER bih;
BITMAPINFO bi;
HANDLE hb;
CDC* myDc = new CDC ();
HDC hdc = myDc->GetSafeHdc ();
void* bits;
RGBQUAD rq [256];
initBi ();
hbm = CreateDIBSection (hdc, &bi, DIB_RGB_COLORS, &bits, NULL, 0);
...
void CEightBitDrawingView::initBi()
{
bih.biSize = sizeof (BITMAPINFOHEADER);
bih.biWidth = 200;
bih.biHeight = -200;
bih.biPlanes = 1;
bih.biBitCount = 8;
bih.biCompression = BI_RGB;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = 14173;
bih.biYPelsPerMeter = 14173;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
memset ((void *) rq, 0, 256 * sizeof (RGBQUAD));
bi.bmiHeader = bih;
bi.bmiColors = rq;
}
Result: This doesn't even compile because the BITMAPINFO bmiColors member is defined as:
RGBQUAD bmiColors[1];
so won't accept more than one RGB color. In fact, nothing I assign to this member compiles! (Could they possibly make it any more complex!?)
Any suggestions would be appreciated! Thanks!
===================================================
Here. Code that demonstrates how to - in a not managed world - allocate a dynamically sized structure on the stack, fill it in and pass it to CreateDIBSection.
#include <malloc.h>
HBITMAP CreateGreyscaleBitmap(int cx, int cy)
{
BITMAPINFO* pbmi = (BITMAPINFO*)alloca( sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256);
pbmi->bmiHeader.biSize = sizeof (pbmi->bmiHeader);
pbmi->bmiHeader.biWidth = cx;
pbmi->bmiHeader.biHeight = cy;
pbmi->bmiHeader.biPlanes = 1;
pbmi->bmiHeader.biBitCount = 8;
pbmi->bmiHeader.biCompression = BI_RGB;
pbmi->bmiHeader.biSizeImage = 0;
pbmi->bmiHeader.biXPelsPerMeter = 14173;
pbmi->bmiHeader.biYPelsPerMeter = 14173;
pbmi->bmiHeader.biClrUsed = 0;
pbmi->bmiHeader.biClrImportant = 0;
for(int i=0; i<256; i++)
{
pbmi->bmiColors[i].rgbRed = i;
pbmi->bmiColors[i].rgbGreen = i;
pbmi->bmiColors[i].rgbBlue = i;
pbmi->bmiColors[i].rgbReserved = 0;
}
PVOID pv;
return CreateDIBSection(NULL,pbmi,DIB_RGB_COLORS,&pv,NULL,0);
}
In both your examples, you created a new CDC with the following line:
CDC* pDC = new CDC();
But there's something missing: This will just create a new CDC object, but without a valid HDC handle attached to it. You need to call CDC::CreateCompatibleDC first, otherwise trying to select any object into this DC will fail.
Regarding the bmiColors: This member is defined as 1 sized array because the data behind it depends on the color depth and type of bitmap. This is documented in the MSDN. For example, if you had a 128x128 pixels 8Bit Bitmap, you would have to allocate the following amount of mem:
128 * 128 * sizeof(WORD) + sizeof(BITMAPINFOHEADER)
I finally resorted to using a .NET graphics tool (Aurigma) to create an 8bpp bitmap, and passed its handle to the unmanaged C++.
Then in C++:
HDC memDc = CreateCompatibleDC (NULL);
HGDIOBJ Obmp = ::SelectObject(memDc, varLayer); // Handle to 8-bit bitmap.
I was able to select the bitmap into a CDC and draw on it. Not 100% unmanaged, but this allowed me to do the drawing in unmanaged code, which gives acceptable speed.
Your bitmap needs to be compatible (same color-depth) as the display context you're going to render it on. Also, 8-bits/pixel bitmaps aren't necessarily grayscale - that's a function of what palette you're using.