I want to draw the text on Bitmap and I did it with the summary code below
BITMAPINFO bitmapInfo;
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = _imgWidth;
bitmapInfo.bmiHeader.biHeight = _imgHeight;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 24;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = 0;
HDC hdc = GetDC(NULL);
if (hdc == NULL)
return false;
HFONT hFont = CreateFont( 50, 0, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, 0, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial" );
if(hFont == NULL)
return false;
HBITMAP hBitmap = CreateDIBitmap(hdc, (LPBITMAPINFOHEADER) &bitmapInfo.bmiHeader, CBM_INIT, _BRG24arrayIn, (LPBITMAPINFO) &bitmapInfo, DIB_RGB_COLORS);
if(hBitmap == NULL)
return false;
HDC hMemDC = CreateCompatibleDC(hdc);
if (hMemDC == NULL)
return false;
HBITMAP hBitmapOld = (HBITMAP)SelectObject(hMemDC, hBitmap);
if( hBitmapOld == NULL )
return false;
HFONT hFontOld = (HFONT)SelectObject(hMemDC, hFont);
if ( hFontOld == NULL )
return false;
SetBkMode(hMemDC, TRANSPARENT);
SetTextColor(hMemDC, 0x0000FF00);
RECT rect;
SetRect(&rect, 0, 0, _imgWidth, _imgHeight);
if (DrawText(hMemDC, "11:41:33", -1, &rect, DT_TOP|DT_LEFT) == 0)
return false;
GetDIBits(hdc, hBitmap, 0, _imgHeight, _BRG24arrayOut, (LPBITMAPINFO)&bitmapInfo, DIB_RGB_COLORS);
return true;
The text that I want to draw is "11:41:33" and text alignment is DT_TOP|DT_LEFT
But the result is the text is rotated and occurred on the LEFT-BOTTOM as result image below
The input array _BRG24arrayIn is in BRG24 format, someone can tell me what happen?
Many thanks,
T&TGroup!
You need to negate the height in the BITMAPINFOHEADER structure to get a top-down bitmap (i.e. one where row 0 is at the top rather than the bottom). For example:
bitmapInfo.bmiHeader.biHeight = -_imgHeight;
Related
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?
I'm facing an issue when I want to get a screenshot compressed with RLE using windows api.
I'm using Qt/C++.
Here is my code :
void Screenshot::captureScreenCompressed() {
HWND hDesktopWnd = GetDesktopWindow();
HDC hDesktopDC = GetDC(hDesktopWnd);
HDC hCaptureDC = CreateCompatibleDC(hDesktopDC);
HBITMAP hCaptureBitmap = CreateCompatibleBitmap(hDesktopDC, _screenWidth, _screenHeight);
SelectObject(hCaptureDC, hCaptureBitmap);
BitBlt(hCaptureDC, 0, 0, _screenWidth, _screenHeight, hDesktopDC, 0,0, SRCCOPY | CAPTUREBLT);
BITMAPINFO bmi = {0};
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
bmi.bmiHeader.biWidth = _screenWidth;
bmi.bmiHeader.biHeight = - _screenHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RLE4;
if (GetDIBits(hCaptureDC, hCaptureBitmap, 0, _screenHeight, NULL, &bmi, DIB_RGB_COLORS) == 0) {
qDebug() << "Problem on Compression";
}
ReleaseDC(hDesktopWnd, hDesktopDC);
DeleteDC(hCaptureDC);
DeleteObject(hCaptureBitmap);
}
The final sizeImage is 0 and the getDiBits function returns 0.
I need some help thanks !
I'm trying to draw on memory bitmap and have weird results. This is a code I have:
HDC hdcScreen = GetDC(hWnd);
HDC hdcMemory = CreateCompatibleDC(hdcScreen);
HBITMAP hMemoryBitmap = CreateCompatibleBitmap(hdcScreen, w, h);
HBITMAP hBitmapOld = (HBITMAP)SelectObject(hdcMemory, hMemoryBitmap);
Graphics *memoryGraphics = Graphics::FromHDC(hdcMemory);
SolidBrush brush(Color(255, 255, 0, 0));
memoryGraphics->FillRectangle(&brush, 0, 0, w, h);
POINT dcOffset = { 0, 0 };
SIZE size = { w, h };
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 255;
bf.AlphaFormat = AC_SRC_ALPHA;
UpdateLayeredWindow(hWnd, hdcScreen, &dcOffset, &size, hdcMemory, &dcOffset, 0, &bf, ULW_ALPHA);
delete memoryGraphics;
ReleaseDC(hWnd, hdcScreen);
SelectObject(hdcMemory, hBitmapOld);
DeleteDC(hdcMemory);
DeleteObject(hMemoryBitmap);
And it gives me semitransparent! Why? I clearly specified Color(255, 255, 0, 0) in brush?
I'm making a C++ program which takes screenshot of the entire screen. I am facing quite a problem. When I run the program, it takes screenshot of the console screen only, not the entire desktop.
HDC Screen = CreateDC(L"DISPLAY", NULL, NULL, NULL);
HDC Capture = CreateCompatibleDC(Screen);
int width = GetDeviceCaps(Screen, HORZRES);
int height = GetDeviceCaps(Screen, VERTRES);
LPBYTE lpcapture;
BITMAPINFO bmiCapture =
{ { sizeof(BITMAPINFOHEADER),width,height,1,24,BI_RGB,0,0,0,0,0 } };
HBITMAP hbmCapture = CreateDIBSection(Screen, &bmiCapture, DIB_RGB_COLORS, (LPVOID *)&lpcapture, NULL, 0);
if (hbmCapture)
{
HBITMAP hbmOld = (HBITMAP) SelectObject(Capture, Capture);
BitBlt(Capture, 0, 0, width, height, Screen, 0, 0, SRCCOPY);
SelectObject(Capture, hbmOld);
}
DeleteDC(Capture);
DeleteDC(Screen);
return hbmCapture;
This should work in a console program, but it would show black console screen in the middle
int ScreenCapture(const char* fname)
{
int result = 0;
HWND hWnd = GetDesktopWindow();
HBITMAP hbmScreen = NULL;
HDC hdcScreen = GetDC(NULL);
HDC hdcWindow = GetDC(hWnd);
int w = GetSystemMetrics(SM_CXSCREEN);
int h = GetSystemMetrics(SM_CYSCREEN);
HDC hdcMemDC = CreateCompatibleDC(hdcWindow);
if (!hdcMemDC) goto cleanup;
hbmScreen = CreateCompatibleBitmap(hdcWindow, w, h);
if (!hbmScreen) goto cleanup;
SelectObject(hdcMemDC, hbmScreen);
if (!BitBlt(hdcMemDC, 0, 0, w, h, hdcWindow, 0, 0, SRCCOPY)) goto cleanup;
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = w;
bi.biHeight = h;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
DWORD dwBmpSize = ((w * bi.biBitCount + 31) / 32) * 4 * h;
HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
char* lpbitmap = (char*)GlobalLock(hDIB);
GetDIBits(hdcWindow, hbmScreen, 0, h, lpbitmap, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
bmfHeader.bfSize = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bmfHeader.bfType = 0x4D42; //'BM' for Bitmaps
DWORD temp = 0;
HANDLE hFile = CreateFileA(fname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &temp, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &temp, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &temp, NULL);
CloseHandle(hFile);
GlobalUnlock(hDIB);
GlobalFree(hDIB);
result = 1; //success
cleanup:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
ReleaseDC(hWnd, hdcWindow);
return result;
}
I have a function that essentially takes a screen shot and saves a pointer to it as a structure. I would like to use the same structure for bitmaps that I load from files.
typedef struct _BITMAPCAPTURE {
HBITMAP hbm;
LPDWORD pixels;
INT width;
INT height;
} BITMAPCAPTURE;
BOOL CaptureScreen(BITMAPCAPTURE* bmpCapture)
{
BOOL bResult = FALSE;
if(!bmpCapture)
return bResult;
ZeroMemory(bmpCapture, sizeof(BITMAPCAPTURE));
HDC hdcScreen = GetDC(NULL);
HDC hdcCapture = CreateCompatibleDC(NULL);
int nWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN),
nHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
// Bitmap is stored top down as BGRA,BGRA,BGRA when used as
// DWORDs endianess would change it to ARGB.. windows COLORREF is ABGR
LPBYTE lpCapture;
BITMAPINFO bmiCapture = { {
sizeof(BITMAPINFOHEADER), nWidth, -nHeight, 1, 32, BI_RGB, 0, 0, 0, 0, 0,
} };
bmpCapture->hbm = CreateDIBSection(hdcScreen, &bmiCapture,
DIB_RGB_COLORS, (LPVOID *)&lpCapture, NULL, 0);
if(bmpCapture->hbm){
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcCapture, bmpCapture->hbm);
BitBlt(hdcCapture, 0, 0, nWidth, nHeight, hdcScreen, 0, 0, SRCCOPY);
SelectObject(hdcCapture, hbmOld);
bmpCapture->pixels = (LPDWORD)lpCapture;
bmpCapture->width = nWidth;
bmpCapture->height = nHeight;
bResult = TRUE;
}
DeleteDC(hdcCapture);
DeleteDC(hdcScreen);
return bResult;
}
The handle to the bitmap, as well as the width and height are all easy enough to get but I'm unsure of how to get the pixels.