How do I draw an array made bitmap on screen using bitblt? - c++

Instead of drawing using Moveto and LineTo, I want to use a hand made bitmap, that I will fill with an array that I will create myself, and use that to fill the screen.
Right now, the array is just filled with red,
But when I draw to the screen I get all black.
This is the code:
void CCGWorkView::OnDraw(CDC* pDC)
{
CCGWorkDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
pDCToUse->FillSolidRect(&r, world.bg_color);
BITMAPINFO bminfo;
CPaintDC hdc(this);
CRect rect;
GetWindowRect(&rect);
int h = rect.bottom - rect.top,
w = rect.right - rect.left;
int *bits = new int[w * h];
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP bm = CreateCompatibleBitmap(hdc, w, h);
SelectObject(hdcMem, bm);
bminfo.bmiHeader.biSize = sizeof(bminfo.bmiHeader);
bminfo.bmiHeader.biWidth = w;
bminfo.bmiHeader.biHeight = h;
bminfo.bmiHeader.biPlanes = 1;
bminfo.bmiHeader.biBitCount = 32;
bminfo.bmiHeader.biCompression = BI_RGB;
bminfo.bmiHeader.biSizeImage = 0;
bminfo.bmiHeader.biXPelsPerMeter = 1;
bminfo.bmiHeader.biYPelsPerMeter = 1;
bminfo.bmiHeader.biClrUsed = 0;
bminfo.bmiHeader.biClrImportant = 0;
for (int i = 0; i < w * h; i++) {
bits[i] = RGB(255, 0, 0);
}
SetDIBits(hdcMem, bm, 0, h, bits, &bminfo, 0);
BitBlt(hdc, rect.left, rect.top, w, h, hdcMem, rect.left, rect.top, SRCCOPY);
DeleteDC(hdcMem);
DeleteObject(bm);
delete bits;
}

There are a couple of issues in your code.
First, you don’t need CPaintDC, as the pDC is passed into your OnDraw() function.
This may have actually caused your black display, as the newly created DC has a one pixel black-and-white bitmap selected into it, and when you call CreateCompatibleBitmap(), that bitmap is also monochrome.
Second, SetDIBits() expect RGBQUAD colors (https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-rgbquad), NOT RGB.
And, as Constantine Georgiou suggested, you should de-select your bitmap out of DC before deleting it to avoid resource leaks, even if MFC handles it for you.
Here is modified code:
void CMFCApplication1View::OnDraw(CDC* pDC)
{
CMFCApplication1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
//pDCToUse->FillSolidRect(&r, world.bg_color);
BITMAPINFO bminfo;
CRect rect;
GetClientRect(&rect);
int h = rect.bottom - rect.top,
w = rect.right - rect.left;
int* bits = new int[w * h];
HDC hdcMem = CreateCompatibleDC(pDC->m_hDC);
HBITMAP bm = CreateCompatibleBitmap(pDC->m_hDC, w, h);
HGDIOBJ hOld = SelectObject(hdcMem, bm);
bminfo.bmiHeader.biSize = sizeof(bminfo.bmiHeader);
bminfo.bmiHeader.biWidth = w;
bminfo.bmiHeader.biHeight = h;
bminfo.bmiHeader.biPlanes = 1;
bminfo.bmiHeader.biBitCount = 32;
bminfo.bmiHeader.biCompression = BI_RGB;
bminfo.bmiHeader.biSizeImage = 0;
bminfo.bmiHeader.biXPelsPerMeter = 1;
bminfo.bmiHeader.biYPelsPerMeter = 1;
bminfo.bmiHeader.biClrUsed = 0;
bminfo.bmiHeader.biClrImportant = 0;
RGBQUAD red = { 0, 0, 255, 0 };
for (int i = 0; i < w * h; i++) {
bits[i] = *((int*)&red);
}
SetDIBits(hdcMem, bm, 0, h, bits, &bminfo, DIB_RGB_COLORS);
BitBlt(pDC->m_hDC, rect.left, rect.top, w, h, hdcMem, rect.left, rect.top, SRCCOPY);
SelectObject(hdcMem, hOld);
DeleteDC(hdcMem);
DeleteObject(bm);
delete[] bits;
}

Related

MFC, Drawing text in memory context (printing)

I got stuck with a problem - I need to create a bitmap in memory, draw some text i it and save it as a BMP file and then print out the bitmap with physical printer. I can do this drawing in the dialog window context - it works fine. But when I try to do the same drawing in printer context the text doesn't appear. I really can't figure out why it is so. Please, help me guys. Thanks in advance. Here is the code:
void CMy2Dlg::OnButton1()
{
// TODO: Add your control notification handler code here
CPrintDialog pd(false);
if (pd.DoModal()==IDOK)
{
CDC PrintDC;
HDC hdc = pd.CreatePrinterDC();
PrintDC.Attach(hdc);
DOCINFO infStru;
::ZeroMemory (&infStru, sizeof (DOCINFO));
CString title="Print test";
infStru.cbSize = sizeof (DOCINFO);
infStru.lpszDocName=title;
infStru.lpszOutput=NULL;
PrintDC.StartDoc(&infStru);
PrintDC.StartPage();
{
CRect r, r2;
CBitmap memBMP, * pOldBitmap;
CPaintDC dc(this);
CDC memDC, *pDC = &memDC;
CFont font, * pOldFont;
int width = 2000;
int height = 1500;
int textwidth = 300;
int textheight = 150;
int oldMapMode = 0;
int oldbkmode = 0;
int i, j;
LOGFONT logFont, lf;
COLORREF oldTextColor;
memset(&logFont, 0, sizeof(logFont));
logFont.lfHeight = 16;
logFont.lfWidth = 0;
logFont.lfEscapement = 0;
logFont.lfOrientation = 0;
logFont.lfWeight = FW_NORMAL;
logFont.lfItalic = FALSE;
logFont.lfUnderline = FALSE;
logFont.lfStrikeOut = 0;
logFont.lfCharSet = ANSI_CHARSET;
logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
logFont.lfQuality = DEFAULT_QUALITY;
logFont.lfPitchAndFamily = DEFAULT_PITCH | FF_SWISS;
strcpy(logFont.lfFaceName, "Arial");
if(memDC.CreateCompatibleDC(&PrintDC)) {
if (memBMP.CreateCompatibleBitmap(&PrintDC, width, height)) {
pOldBitmap = pDC->SelectObject(&memBMP);
pDC->FillSolidRect(0, 0, width, height, RGB(200, 200, 200));
oldTextColor = pDC->SetTextColor(RGB(255,0,0));
oldMapMode = pDC->SetMapMode(MM_LOMETRIC);
oldbkmode = pDC->SetBkMode(TRANSPARENT);
lf = logFont;
lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(pDC->GetSafeHdc(), LOGPIXELSY), 72);
//lf.lfHeight = 100;
font.CreateFontIndirect(&lf);
pOldFont = pDC->SelectObject(&font);
r.left = 10;
r.top = 10;
r.right = r.left + textwidth;
r.bottom = r.top + textheight;
r.top *= -1;
r.bottom *= -1;
pDC->MoveTo(r.left, r.top);
pDC->LineTo(r.right, r.top);
pDC->LineTo(r.right, r.bottom);
pDC->LineTo(r.left, r.bottom);
pDC->LineTo(r.left, r.top);
pDC->DrawText("qwerty", &r, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
pDC->SetMapMode(oldMapMode);
pDC->SetTextColor(oldTextColor);
pDC->SetBkMode(oldbkmode);
PrintDC.BitBlt(10, 10, width, height, pDC, 0, 0, SRCCOPY);
pDC->SelectObject(pOldBitmap);
pDC->SelectObject(pOldFont);
font.DeleteObject();
memBMP.DeleteObject();
pDC->DeleteDC();
}
}
}
PrintDC.EndPage();
PrintDC.EndDoc();
PrintDC.Detach();
DeleteDC(hdc);
}
}
If SetMapMode(MM_LOMETRIC) is used on memory DC, then memory DC has to be drawn upside down when using BitBlt to copy to printer/display DC. The width/height will have to be adjusted as well. Just use the default map mode (MM_TEXT). Use SetMapMode(MM_LOMETRIC) when you are drawing on printer DC and you want that specific measurement units.
void CMy2Dlg::OnButton1()
{
//create the bitmap
int w = 600, h = 400;
CClientDC dc(this);
CBitmap bmp;
CDC memdc;
memdc.CreateCompatibleDC(&dc);
bmp.CreateCompatibleBitmap(&dc, w, h);
auto oldbmp = memdc.SelectObject(bmp);
//draw on bitmap
memdc.FillSolidRect(0, 0, w, h, RGB(200, 200, 200));
memdc.SetTextColor(RGB(255, 0, 0));
CRect rc(0, 0, w, h);
memdc.DrawText(L"qwerty", &rc, 0);
dc.BitBlt(0, 0, w, h, &memdc, 0, 0, SRCCOPY);//optional: draw the bitmap on dialog
CPrintDialog pd(false);
if(pd.DoModal() == IDOK)
{
CDC PrintDC;
HDC hdc = pd.GetPrinterDC();
PrintDC.Attach(hdc);
DOCINFO docinfo = { sizeof(docinfo) };
docinfo.lpszDocName = L"Print test";
PrintDC.StartDoc(&docinfo);
PrintDC.StartPage();
PrintDC.BitBlt(0, 0, w, h, &memdc, 0, 0, SRCCOPY);
PrintDC.EndPage();
PrintDC.EndDoc();
}
dc.SelectObject(oldbmp);
}

How can I keep reusing HBITMAP and HDC continually?

I am trying to get HBIPMAP working by reusing the HBITMAP and the HDC for performance reasons.
This is a small test project I wanted to do to learn more about CPU based Rasterization. For the window, im using SDL2.
The below code works if we comment out:
DeleteDC(hdcMem);
hdcMem = CreateCompatibleDC(device);
I can not find any example in the year 2018+.
mBackBuffer is just a Vector(DWORD)
void Device::createDeviceFromHWND(const HWND& hwnd, const int& width, const int& height)
{
// This is hacked code for an example.
auto device = GetDC(hwnd);
DWORD colorSize = 4; // ARGB;
// Create page section
// https://learn.microsoft.com/en-us/windows/desktop/memory/creating-named-shared-memory
HANDLE hMapFile;
LPCTSTR pBuf;
// https://learn.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createfilemappinga
hMapFile = CreateFileMappingA
(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
width * height * colorSize,
NULL
);
if (hMapFile == NULL)
{
return;
}
DWORD* buffer = (DWORD*)MapViewOfFile(
hMapFile,
FILE_MAP_ALL_ACCESS,
0,
0,
width * height * colorSize
);
BITMAPINFOHEADER header;
memset(&header, 0, sizeof(BITMAPINFOHEADER));
// https://msdn.microsoft.com/en-us/02f8ed65-8fed-4dda-9b94-7343a0cfa8c1
header.biSize = sizeof(BITMAPINFOHEADER);
header.biWidth = width;
header.biHeight = height;
header.biPlanes = 1;
header.biBitCount = 32;
header.biCompression = BI_RGB;
header.biSizeImage = width * height * sizeof(BYTE);
header.biXPelsPerMeter = 0;
header.biYPelsPerMeter = 0;
header.biClrUsed = 0;
header.biClrImportant = 0;
tagBITMAPINFO bitmap;
memset(&bitmap, 0, sizeof(tagBITMAPINFO));
// https://learn.microsoft.com/en-us/windows/desktop/api/wingdi/ns-wingdi-tagbitmapinfo
tagRGBQUAD RGBQUAD;
memset(&RGBQUAD, 0, sizeof(tagRGBQUAD));
bitmap.bmiHeader = header;
bitmap.bmiColors[0] = RGBQUAD;
LPVOID p;
// https://learn.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-createdibsection
auto hBitMap = CreateDIBSection
(
device,
&bitmap,
DIB_RGB_COLORS,
&p,
hMapFile,
0
);
for (DWORD i = 0; i < width * height; ++i)
{
buffer[i] = 0xFF0000;
}
HDC hdcMem = CreateCompatibleDC(device);
auto oldHBITMAP = (HBITMAP)SelectObject(hdcMem, hBitMap);
BitBlt(
device,
0,
0,
width,
height,
hdcMem,
0,
0,
SRCCOPY
);
DeleteDC(hdcMem);
for (DWORD i = 0; i < width * height; ++i)
{
buffer[i] = 0;
}
hdcMem = CreateCompatibleDC(device);
BitBlt(
device,
400,
300,
width,
height,
hdcMem,
0,
0,
SRCCOPY
);
}
The output is a red screen, but you should see the black section in corner right.
There are several issues here, some not related to bitmap.
The handle from GetDC should be cleaned up by ReleaseDC when the handle is no longer needed.
The handle from CreateFileMapping should be cleaned up by CloseHandle, and MapViewOfFile should be cleaned up by UnmapViewOfFile.
HBITMAP handle must be cleaned up by DeleteObject
It is recommend to cleanup after SelectObject by calling SelectOject(hMemDC, oldHBitmap)
If you don't restore the old bitmap, and try to delete hMemDC, Windows cannot fulfill the request because there is another bitmap selected in device context. Windows will try to fix this error but it may fail if the code is too convoluted.
Note that Windows gives you a limit of 10,000 GDI handles. The application will crash very quickly if you don't manage these handles properly. Refer to WinAPI documentation for these functions. If in doubt, use the task manager to monitor "GDI handles" for your program.
The code should work as expected once you fix these problems, see the example below.
This of course is for demonstration only. In real world application you probably want to save the HBITMAP in the heap, instead of stack, as well as some other values. You want to minimize repeated creating these handles.
As noted in other answers and comments, painting should be done in response to WM_PAINT, where you get HDC from BeginPaint (and cleanup with EndPaint). Therefore you should avoid GetDC/ReleaseDC
void Device::createDeviceFromHWND(const HWND& hwnd, const int& width, const int& height)
{
auto hdc = GetDC(hwnd);
auto hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
width * height * sizeof(DWORD), NULL);
auto buffer = (DWORD*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0,
width * height * sizeof(DWORD));
BITMAPINFOHEADER biheader = { sizeof(biheader), width, height, 1, 32, BI_RGB };
LPVOID bits;
auto hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&biheader, DIB_RGB_COLORS,
&bits, hMapFile, 0);
for(int i = 0; i < width * height; ++i)
buffer[i] = 0xFF0000;
auto memdc = CreateCompatibleDC(hdc);
auto oldhbitmap = SelectObject(memdc, hbitmap);
BitBlt(hdc, 0, 0, width, height, memdc, 0, 0, SRCCOPY);
for(int i = 0; i < width * height; ++i)
buffer[i] = 0;
BitBlt(hdc, 0, 0, 100, 100, memdc, 0, 0, SRCCOPY);
SelectObject(memdc, oldhbitmap); //<- ***EDIT***
//oldhbitmap is selected in to memdc, now we can destroy hbitmap and memdc
DeleteObject(hbitmap);
DeleteDC(memdc);
ReleaseDC(hwnd, hdc);
UnmapViewOfFile(buffer);
CloseHandle(hMapFile);
}
Side note, you don't gain anything by using reference operator & for constant values. Just change the function prototype as follows:
void createDeviceFromHWND(const HWND hwnd, const int width, const int height);
Also, this can be done without CreateFileMapping, and use buffer shown below. buffer will be valid as long as hbitmap is valid.
void test(const HWND hwnd, const int w, const int h)
{
auto hdc = GetDC(hwnd);
//use the negative value of height, so bitmap bits are not upside-down
BITMAPINFOHEADER bi = { sizeof(bi), w, -h, 1, 32, BI_RGB };
DWORD* buffer;
auto hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS,
(void**)&buffer, NULL, 0);
auto memdc = CreateCompatibleDC(hdc);
auto oldbmp = SelectObject(memdc, hbitmap);
for(int i = 0; i < w * h; ++i) buffer[i] = 0xFF0000;
BitBlt(hdc, 0, 0, w, h, memdc, 0, 0, SRCCOPY);
//draw black square on top-left
for(int y = 0; y < 100; y++)
for(int x = 0; x < 100; x++)
buffer[y * w + x] = 0;
BitBlt(hdc, 0, 0, 100, 100, memdc, 0, 0, SRCCOPY);
//cleanup:
SelectObject(memdc, oldbmp);
DeleteObject(hbitmap); //<- buffer is not valid after hbitmap is destroyed
DeleteDC(memdc);
ReleaseDC(hwnd, hdc);
}
This approach is wrong. When the target window gets WM_PAINT all your work will be undone.
Always paint your windows the way you want them with WM_PAINT and BeginPaint.
You are not editing the bitmap here:
for (unsigned int i = 0; i < width * height; ++i)
{
mBackBuffer[i] = 0;
}
just filling the array, the bitmap was created from, with nulls.

Converting Color Bitmap to Grayscale in C++

Need to convert a color bitmap to a grey scale. Here is the approach:
HDC hdcWindow = GetDC(hWnd);
HBITMAP hDIBBitmap;
{
// Create a compatible DC which is used in a BitBlt from the window DC
HDC hdcMemDC = CreateCompatibleDC(hdcWindow);
SelectObject(hdcMemDC, bmHatch);
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = bm.bmWidth;
bmi.bmiHeader.biHeight = bm.bmHeight; // top-down
bmi.bmiHeader.biPlanes = bm.bmPlanes;
bmi.bmiHeader.biBitCount = bm.bmBitsPixel;
UINT* pBits;
HBITMAP hDIBBitmap = CreateDIBSection(hdcMemDC, &bmi, DIB_RGB_COLORS, (void**)&pBits, NULL, NULL);
for (int i = 0; i < bm.bmWidth; i++) {
for (int j = 0; j < bm.bmHeight; j++) {
UINT val = pBits[i + j * bm.bmWidth];
if (val != 0)
{
COLORREF clr = val;
UINT newVal = (GetRValue(clr) + GetBValue(clr) + GetGValue(clr)) / 3;
pBits[i + j * bm.bmWidth] = newVal;
}
}
}
SelectObject(hdcMemDC, hDIBBitmap);
// draw content of memory bitmap in the window
BitBlt(hdcWindow, 0, 0, bm.bmWidth, bm.bmHeight, hdcMemDC, 0, 0, SRCCOPY);
DeleteObject(hDIBBitmap);
DeleteDC(hdcMemDC);
}
ReleaseDC(hWnd, hdcWindow);
In the above code sample, the input bitmap is given by bm which is a Bitmap instance.
Created a compatible DC. Loaded the bitmap into using the selectObject statement.
Then wanted to change the bits by creating the DIB section for traversing the bitmap values. After changing the values, the hDIBBitmap is selected and then finally drawing using the BitBlt function.
When I comment out the following line, I can see the original bitmap rendered correctly:
SelectObject(hdcMemDC, hDIBBitmap);
What I observed was the pBits is always 0 and hence the transformed gray scale image is not being rendered and instead getting a black picture.
Please advise on what is wrong with this approach.
CreateDIBSection is creating a blank bitmap with that usage. None of the bits from the original bitmap are used. If you paint it you get a black bitmap.
If you comment out SelectObject(hdcMemDC, hDIBBitmap) then this new hDIBBitmap is ignored as well. You print the original bitmap which was selected in device context earlier: SelectObject(hdcMemDC, bmHatch)
To convert HBITMAP to gray scale use the following code. Note this will not work for palette bitmaps.
Converting 24-bit or 32-bit HBITMAP to grayscale:
void grayscale(HBITMAP hbitmap)
{
BITMAP bm;
GetObject(hbitmap, sizeof(bm), &bm);
if(bm.bmBitsPixel < 24)
{
DebugBreak();
return;
}
HDC hdc = GetDC(HWND_DESKTOP);
DWORD size = ((bm.bmWidth * bm.bmBitsPixel + 31) / 32) * 4 * bm.bmHeight;
BITMAPINFO bmi
{sizeof(BITMAPINFOHEADER),bm.bmWidth,bm.bmHeight,1,bm.bmBitsPixel,BI_RGB,size};
int stride = bm.bmWidth + (bm.bmWidth * bm.bmBitsPixel / 8) % 4;
BYTE *bits = new BYTE[size];
GetDIBits(hdc, hbitmap, 0, bm.bmHeight, bits, &bmi, DIB_RGB_COLORS);
for(int y = 0; y < bm.bmHeight; y++) {
for(int x = 0; x < stride; x++) {
int i = (x + y * stride) * bm.bmBitsPixel / 8;
BYTE gray = BYTE(0.1 * bits[i+0] + 0.6 * bits[i+1] + 0.3 * bits[i+2]);
bits[i+0] = bits[i+1] = bits[i+2] = gray;
}
}
SetDIBits(hdc, hbitmap, 0, bm.bmHeight, bits, &bmi, DIB_RGB_COLORS);
ReleaseDC(HWND_DESKTOP, hdc);
delete[]bits;
}
Usage
//convert to grayscale
//this should be called once
grayscale(hbitmap);
//paint image
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP oldbmp = SelectObject(memdc, hbitmap);
BITMAP bm;
GetObject(hbitmap, sizeof(bm), &bm);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, memdc, 0, 0, SRCCOPY);
...

Win32/GDI: How to use RealizePalette() to modify color table?

According to MSDN documentation on GDI function RealizePalette():
The RealizePalette function modifies the palette for the device
associated with the specified device context. If the device context is
a memory DC, the color table for the bitmap selected into the DC is
modified.
Behavior described in italics doesn't seem to work in the following code:
void test_color_tbl_modify(HWND hWnd) {
// initialize BITMAPINFO struct for a mono bitmap(red&white)
const int bmp_w = 16, bmp_h = 32;
std::vector<BYTE> vBytes(sizeof(BITMAPINFO) + sizeof(RGBQUAD) * 2); // for mono bitmaps, bmiColors member of BITMAPINFO contains two RGBQUAD elems
BITMAPINFO* pbmi = reinterpret_cast<BITMAPINFO*>(vBytes.data());
{
BITMAPINFOHEADER bih{};
{
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = bmp_w;
bih.biHeight = bmp_h;
bih.biPlanes = 1;
bih.biBitCount = 1;
bih.biCompression = BI_RGB;
bih.biSizeImage = ((bih.biWidth * bih.biBitCount + 31) & ~31) / 8 // each scanline aligned on DWORD boundary
* bih.biHeight;
bih.biClrUsed = 2;
bih.biClrImportant = 0;
}
pbmi->bmiHeader = bih;
pbmi->bmiColors[0] = RGBQUAD{0,0,255,0}; // red
pbmi->bmiColors[1] = RGBQUAD{255,255,255,0}; // white
}
// create a mono DDB
HDC hdc = GetDC(hWnd);
HDC hdcmem = CreateCompatibleDC(hdc); // has 1x1 mono bitmap selected into it by default
HBITMAP hbitmap = CreateCompatibleBitmap(hdcmem, bmp_w, bmp_h); // creates a mono-bitmap(see above)
// draw something
HGDIOBJ hOldBmp = SelectObject(hdcmem, hbitmap);
for(int y = 0; y < bmp_h; ++y) {
for(int x = 0; x < bmp_w; ++x) {
COLORREF col = x & 1 && y & 1 ? RGB(255, 255, 255) : RGB(0, 0, 0);
SetPixel(hdcmem, x, y, col);
}
}
// blit image to client area by creating a DIB section and
// copying DIBits to it from hbitmap(works as expected: red&white image)
{
// get DIBits
std::vector<BYTE> vDIBits_buf(pbmi->bmiHeader.biSizeImage);
void* pvBits = vDIBits_buf.data();
SelectObject(hdcmem, hOldBmp); // hbitmap must NOT be selected into a DC before GetDIBits() call as per documentation
if(!GetDIBits(hdc, hbitmap,
0, (UINT)bmp_h,
pvBits, pbmi, DIB_RGB_COLORS)) { // resets biClrUsed & biClrImportant to 0, and pbmi->bmiColors to black&white -- why??
MessageBox(hWnd, L"GetDIBits has failed", L"Failed", MB_OK);
}
// reset members modified(for some reason) by GetDIBits()
// back to their original values
pbmi->bmiHeader.biClrUsed = 2;
pbmi->bmiColors[0] = RGBQUAD{0,0,255,0};
pbmi->bmiColors[1] = RGBQUAD{255,255,255,0};
// copy DIBits from hbitmap to hDIBSecion
void* pvBits_dest = nullptr;
HBITMAP hDIBSecion = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, &pvBits_dest, NULL, 0);
memcpy(pvBits_dest, pvBits, pbmi->bmiHeader.biSizeImage);
// blit hDIBSecion to client area
HGDIOBJ hOldBmp = SelectObject(hdcmem, hDIBSecion);
BitBlt(hdc, 0, 0, bmp_w, bmp_h, hdcmem, 0, 0, SRCCOPY); // blits a red&white image as expected
// clean up
SelectObject(hdcmem, hOldBmp);
DeleteObject(hDIBSecion);
}
// blit image to client area by modifying color table of hbitmap
// (does not work as expected: black&white image instead of green&black)
{
// initialize palette(green&black)
std::vector<BYTE> log_palette_buf(sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * 2);
LOGPALETTE* plplt = reinterpret_cast<LOGPALETTE*>(log_palette_buf.data());
plplt->palNumEntries = 2;
plplt->palVersion = 0x0300;
PALETTEENTRY pe_black{0,0,0,0};
PALETTEENTRY pe_green{0,255,0,0};
plplt->palPalEntry[0] = pe_green;
plplt->palPalEntry[1] = pe_black;
// modify color table of hbitmap to green&black
HGDIOBJ hOldBmp = SelectObject(hdcmem, hbitmap); // select hbitmap into memory DC first
HPALETTE hp = CreatePalette(plplt);
HPALETTE hp_old = SelectPalette(hdcmem, hp, FALSE);
RealizePalette(hdcmem); // supposed to modify color table of selected bitmap(hbitmap) according to documentation
// blit hbitmap to client area
BitBlt(hdc, bmp_w, 0, bmp_w, bmp_h, hdcmem, 0, 0, SRCCOPY); // ???blits a black&white image???
// clean-up
SelectObject(hdcmem, hOldBmp);
SelectPalette(hdcmem, hp_old, FALSE);
DeleteObject(hp);
}
// clean up
DeleteObject(hbitmap);
DeleteDC(hdcmem);
ReleaseDC(hWnd, hdc);
}
What am I missing?

Getting RGB data of a minimised window

I want to get RGB data of a particular part of a running window which is minimised and I'm not quite sure how to do it. Here is my piece of code for getting bitmap info of a window for a specified rectangle.
BYTE* LoadBMPFromHandle(HWND hwnd, int leftTopX, int leftTopY, int width, int height)
{
BITMAPINFO bmi;
BITMAPINFOHEADER bmpiHeader;
HDC dc, memdc;
HBITMAP hbWnd;
bmpiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpiHeader.biPlanes = 1;
bmpiHeader.biBitCount = 24;
bmpiHeader.biCompression = BI_RGB;
bmpiHeader.biSizeImage = 0;
bmpiHeader.biXPelsPerMeter = 3780;
bmpiHeader.biYPelsPerMeter = 3780;
bmpiHeader.biClrUsed = 0;
bmpiHeader.biClrImportant = 0;
bmpiHeader.biWidth = width;
bmpiHeader.biHeight = height;
bmi.bmiHeader = bmpiHeader;
dc = GetDC(hwnd);
memdc = CreateCompatibleDC(dc);
hbWnd = CreateCompatibleBitmap(dc, width, height);
SelectObject(memdc, hbWnd);
BitBlt(memdc, 0, 0, width, height, dc, leftTopX, leftTopY, SRCCOPY);
int paddedWidth = width + (4-(width%4))%4;
BYTE* BGRwnd = new BYTE [paddedWidth*height*3];
if( 0 == GetDIBits(dc, hbWnd, 0, height, BGRwnd, &bmi, DIB_RGB_COLORS) )
{
cout <<"GetDIBits function fail..." << endl;
DeleteDC(memdc);
DeleteObject(hbWnd);
ReleaseDC(hwnd, dc);
exit(0);
}
DeleteDC(memdc);
DeleteObject(hbWnd);
ReleaseDC(hwnd, dc);
return BGRwnd;
}
However, this function would only work if there is no another window overlapping with the one I have a handle to.
hwnd - handle to my window
LoadBMPFromHandle has to retrieve BGR (blue green red) data of a rectangle with upper left coordinates (leftTopX, leftTopY) and dimesions width x height.
Thank You!