C++ WIN32: Rescaling Bitmaps/Giving Bitmaps HDC's - c++

So i've been trying to rescale a bitmap without it printing the original and reprinting the rescaled image. I'm trying to use StretchBlt(), based on the MSDN Microsoft rescaling images function:
https://msdn.microsoft.com/en-us/library/windows/desktop/dd162950(v=vs.85).aspx
but that requires a secondary hdc tied to the Source, which stretching can't be done without printing the HBITMAP first. Is there a way to convert HBITMAP's into HDC's? I have been able to get HANDLE's out of HBITMAP's, which might provide a more direct route. The other thing i could do is create a resized bitmap in allocated memory (not saved) out of the standard bitmap and print that.
The standard way i print bitmaps is:
HBITMAP hBitmap;
static HANDLE hDIB = NULL;
CHAR szFileName[MAX_PATH] = "fileName.bmp";
hDIB = OpenDIB((LPSTR)szFileName);
hBitmap = BitmapFromDIB(hDIB, NULL);
DrawBitmap(hdc, x, y, hBitmap, SRCCOPY);
Another option i could try is to look into another means of displaying the bmp. I'm pretty new to win32, so I don't know any other means of accomplishing this task. Any insight into how i can rescale the BITMAP without printing it in the first place.

The link you posted (Scaling an Image) already contains code, that renders a bitmap. All you need to do is replace the call to BitBlt with StretchBlt:
BOOL DrawBitmap (HDC hDC, INT x, INT y, INT width, INT height, HBITMAP hBitmap, DWORD dwROP)
{
HDC hDCBits;
BITMAP Bitmap;
BOOL bResult;
if (!hDC || !hBitmap)
return FALSE;
hDCBits = CreateCompatibleDC(hDC);
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
SelectObject(hDCBits, hBitmap);
// Replace with StretchBlt call
//bResult = BitBlt(hDC, x, y, Bitmap.bmWidth, Bitmap.bmHeight, hDCBits, 0, 0, dwROP);
bResult = StretchBlt(hDC, x, y, width, height,
hDCBits, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, dwROP);
DeleteDC(hDCBits);
return bResult;
}
You can call this from your WM_PAINT message handler, for example:
case WM_PAINT:
{
PAINTSTRUCT ps = { 0 };
HDC hDC = ::BeginPaint( hWnd, &ps );
RECT rc = { 0 };
::GetClientRect( hWnd, &rc );
DrawBitmap( hDC, 0, 0, rc.right, rc.bottom, hBitmap, SRCCOPY );
::EndPaint( hWnd, &ps );
}
break;

Related

Creating a new bitmap with given dimensions filled with given color

I'm creating a program using the Win32 API, and I need to create a new bitmap filled with one color and with given dimensions.
Here is my code:
m_hBitmap =( HBITMAP ) CreateCompatibleBitmap(hDC, iWidth, iHeight);
HDC hDCn = CreateCompatibleDC( hDC );
SelectObject( hDCn, m_hBitmap );
ExtFloodFill(hDCn, 0, 0, crColor, FLOODFILLSURFACE);
DeleteDC( hDCn );
The bitmap dimensions match, but the bitmap is always black regardless of the crColor parameter.
Use FillRect() instead of ExtFloodFill().
Also, you need to de-select the bitmap before you delete the DC, otherwise the original bitmap created and selected into the DC by CreateCompatibleDC() will be leaked.
Try this:
m_hBitmap = CreateCompatibleBitmap(hDC, iWidth, iHeight);
HDC hDCn = CreateCompatibleDC(hDC);
HBITMAP hOld = (HBITMAP) SelectObject(hDCn, m_hBitmap); // <-- SAVE OLD BITMAP!
//ExtFloodFill(hDCn, 0, 0, crColor, FLOODFILLSURFACE);
RECT r;
r.left = r.top = 0;
r.right = iWidth;
r.bottom = iHeight;
HBRUSH hBrush = CreateSolidBrush(crColor);
FillRect(hDCn, &r, hBrush);
DeleteObject(hBrush);
SelectObject(hDCn, hOld); // <-- RESTORE OLD BITMAP
DeleteDC(hDCn);

Double-buffering AlphaBlend rect with text overlay

I'm a little confused on how to double buffer this. I'm
not sure if I need to create another CreateCompatibleBitmap or CreateCompatibleDC and how to link it all.
This works as is but I don't think its double buffered right.
void __OnPaint(HWND hWnd, HDC _hdc = nullptr)
{
HDC hdc = _hdc;
PAINTSTRUCT paint;
RECT& rcClient = paint.rcPaint;
if (!_hdc)
hdc = BeginPaint(hWnd, &paint);
else
GetClientRect(hWnd, &rcClient);
if (hdc)
{
int width = rcClient.right - rcClient.left;
int height = rcClient.bottom - rcClient.top;
HDC hDCMem = CreateCompatibleDC(_hdc);
HBITMAP hBitmapMem = CreateCompatibleBitmap(hDCMem, width, height);
SelectObject(hDCMem, hBitmapMem);
Rectangle(hDCMem, 0, 0, width, height);
BLENDFUNCTION bfn;
bfn.BlendOp = AC_SRC_OVER;
bfn.BlendFlags = 0;
bfn.AlphaFormat = 0;
bfn.SourceConstantAlpha = 0x50;
AlphaBlend(hdc, 0, 0, width, height, hDCMem, 0, 0, width, height, bfn);
SetTextColor(hdc, RGB(255, 0, 0));
SetBkMode(hdc, TRANSPARENT);
DrawText(hdc, "Your text here", -1, &rcClient, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
//BitBlt(hdc, 0, 0, width, height, hDCMem, 0, 0, SRCCOPY);
DeleteDC(hDCMem);
DeleteObject(hBitmapMem);
}
if (!_hdc)
EndPaint(hWnd, &paint);
}
Also i found i have another problem with this.
i move my window in WM_TIMER, i call my __onpaint, problem im having is that it does not redraw it has something todo with the alphaBlend, it keeps what ever was under the window at the time of 1st draw, since it worked before i was using that
double buffering is to do all your printing and drawing on a temporary bitmap, that should be stored somewhere. the drawings on that bitmap can happen outside of WM_PAINT event (eg: on Adding items or selection change).
then on WM_PAINT event, the only thing you have to do is to project that bitmap to the window via BitBlt function or similar functions.
the way you are using AlphaBlend is wrong. AlphaBlend is used to draw images that have an AlphaChanel over an existing image as an overlay.

Only screenshot a specific area of a window

I'm currently screenshotting windows using the following lines of code:
UpdateWindow(hwnd);
HDC window_dc = GetDC(hwnd);
HDC res = CreateCompatibleDC(window_dc);
RECT r;
GetClientRect(hwnd, &r);
HBITMAP bmp = CreateCompatibleBitmap(window_dc, r.right - r.left, r.bottom - r.top);
SelectObject(res, bmp);
PrintWindow(hwnd, res, PW_CLIENTONLY);
DeleteObject(bmp);
ReleaseDC(hwnd, window_dc);
Now is there a way, to only screenshot a specific area of my HWND? I realized, that on some applications, the screenshotting takes siginificantly longer when the window is only a little bit larger. So I figured if I was able to only screenshot the important frame, I could really improve my speed.
EDIT: It needs to work for windows which are in the background, eg. overlapped by other windows.
You could try it like this:
bool ScreenShot(HWND hwnd, int x, int y, int w, int h, LPCSTR file){
HDC source = GetDC(hwnd);
HDC memory = CreateCompatibleDC(source);
HBITMAP bitmap = CreateCompatibleBitmap(source, w, h);
HBITMAP bitmapOld = (HBITMAP)SelectObject(memory, hBitmap);
BitBlt(memory, 0, 0, w, h, source, x, y, SRCCOPY);
hBitmap = (HBITMAP)SelectObject(memory, bitmapOld);
DeleteDC(source);
DeleteDC(memory);
HPALETTE pal = NULL;
if(saveBitmap(file, bitmap, pal)) return true;
return false;
}

SetDIBits fails on WM_PAINT when trying to draw

I am trying to draw plot using matrix, where each element is DWORD value, that represent pixel ARGB value. Idea behind the application is that one separate thread do the calculation (mat. formula), whose result will be the matrix with ARGB values. When WM_PAINT is raised I will use function to draw, using the filled matrix:
void DrawImage( HDC hDC, WORD wWidth, WORD wHeight )
{
HBITMAP hBitmap;
HDC hMemDC;
BITMAPINFO bi;
int iSize = sizeof( BITMAPINFO );
memcpy( &bi, dwBytes + 3, iSize);
hBitmap = CreateCompatibleBitmap(hDC, wWidth, wHeight);
hMemDC = CreateCompatibleDC( hDC );
if ( 0 == SetDIBits( hDC, hBitmap, 0,
wHeight, dwBytes, &bi, DIB_RGB_COLORS ) )
{
// error MSDN http://msdn.microsoft.com/en-us/library/
//windows/desktop/dd162973%28v=vs.85%29.aspx
}
hBitmap = (HBITMAP) SelectObject(hMemDC, hBitmap);
BitBlt(hDC, 0, 0, wWidth, wHeight, hMemDC, 0, 0, SRCCOPY);
DeleteObject(SelectObject(hMemDC, hBitmap));
DeleteDC(hMemDC);
}
so, when WM_PAINT is raised:
case WM_PAINT:
{
PAINTSTRUCT ps = { 0 };
HDC hDC = BeginPaint( hWnd, &ps );
DrawImage( hDC, iWidth, iHeight );
EndPaint( hWnd, &ps );
return 0L;
}
but nothing happens. The area is black. When SetDIBits returns 0 it means that "One or more of the input parameters is invalid" according to MSDN. I am out of ideas..
sizeof( BITMAPINFO ) includes only 1 pixel. BITMAPINFO is a variable-length structure so you need to compute its size dynamically and allocate sufficient memory dynamically.
You pointed me to investigate handling BITMAPINFO structure, and there it was. After I have changed one plane, 32 bits per pixel and width 800 and height 600 - it worked!
void DrawImage( HDC hDC, WORD wWidth, WORD wHeight )
{
HDC hMemDC;
BITMAPINFO bmi;
ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biHeight = 600;
bmi.bmiHeader.biWidth = 800;
bmi.bmiHeader.biPlanes = 1;
hMemDC = CreateCompatibleDC( hDC );
HBITMAP hBitmap = CreateDIBSection( hMemDC, &bmi, DIB_RGB_COLORS,
(void**) &dwBytes, NULL, 0);
SetDIBits( hDC, hBitmap, 0, wHeight, dwBytes, &bmi, DIB_RGB_COLORS );
HBITMAP hOldBitmap = (HBITMAP) SelectObject( hMemDC, hBitmap );
BitBlt(hDC, 0, 0, wWidth, wHeight, hMemDC, 0, 0, SRCCOPY);
SelectObject( hMemDC, hBitmap );
DeleteObject( hBitmap );
DeleteDC(hMemDC);
}
I got proper result:

How to get the array of RGB values for each pixel of the client area of a window

Is there a way to receive the colour values for each pixel in the client area of a window, with gdi?
As noted in comment by #JerryCoffin. Here's a simple example
hDC = GetDC(hwnd);
hBitmap = CreateCompatibleBitmap(hDC, width, height);
hMemDC = CreateCompatibleDC(hDC);
hOld = SelectObject(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, width, height, hDC, x, y, SRCCOPY);
// Clean up
DeleteDC(hMemDC);
ReleaseDC(hwnd, hDC);
You should have a bitmap object selected into memory DC for which you can use GetPixel GDI function and then you can also extract the color values using GetRValue() , GetGValue() , and GetBValue() macros.