Win32 C++ BitBlt Raster Methods and Transparency - c++

I recently asked a question about this, and understood the answer, but couldn't translate it into code. After another day of messing around with stuff, and fixing the leaks. I literally cannot for the life of me figure this out, though.
This is slightly different, all I need to do is get the background bitmap, under the map bitmap.
HDC hdc = GetDC(hWnd);
HDC hdcMem = CreateCompatibleDC(hdc);
HDC hdcMem2 = CreateCompatibleDC(hdc);
ReleaseDC(hWnd, hdc);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, bitmap.hbmBackground);
BitBlt(buffer.getBufferDC(), 1, 1, WINDOW_WIDTH, WINDOW_HEIGHT, hdcMem, 0, 0, SRCCOPY);
HBITMAP hbmOld2 = (HBITMAP)SelectObject(hdcMem2, bitmap.hbmMap);
BitBlt(buffer.getBufferDC(), 1, 1, WINDOW_WIDTH, WINDOW_HEIGHT, hdcMem2, 0, 0, SRCPAINT);
SelectObject(hdcMem2, hbmOld2);
My problem is combining... creating memory dcs to keep results of raster operations. I can not at all wrap my head around this, any help would be great.
Thanks.

Probably the most difficult part of this is the rectangle in the middle. The other white areas in the mask are the places the background should show through, but inside that rectangle you have white where the background should not show through.
For the moment, I'm going to assume we can start with those sections being different colors. It doesn't really matter what color we use for the transparent part (the part where the background should show) as long as it's unique. For now I'll assume it's an RGB value for 254, 254, 254--code won't mistake it for pure white (255, 255, 255), but usually not visually distinguishable.
With that, the job becomes quite simple: we can use TransparentBlt to handle the transparency, so you blit your background to a destination with BitBlt, then blit your mask to that destination using TransparentBlt. Simplifying a bit (leaving out parameters we don't care much about for the moment) the code looks something like this:
HDC mask, background, combined;
mask = CreateCompatibleDC(screen);
background = CreateCompatibleDC(screen);
combined = CreateCompatibleDC(screen);
HBITMAP mask_bmp = LoadBitmap(MAKEINTRESOURCE(IDB_MASK));
HBITMAP back_bmp = LoadBitmap(MAKEINTRESOURCE(IDB_BKGND));
HBITMAP result = CreateCompatibleBitmap(screen);
mask_original = Selectobject(mask, mask_bmp);
back_original = SelectObject(background, back_bmp);
combined_original = SelectObject(combined, result);
BitBlt(background, result, SRCCOPY);
TransparentBlt(mask, result, RGB(254, 254, 254));
BitBlt(result, screen, SRCCOPY);
SelectObject(mask, mask_original);
SelectObject(background, back_original);
SelectObject(combined, combined_original);
DeleteDC(mask);
DeleteDC(background);
DeleteDC(combined):
DeleteObject(result);

Related

How to draw text with transparency using GDI?

My goal is to dynamically put some arbitrary text into an HICON image (at runtime.) I'm using the following code:
//Error checks are omitted for brevity
//First create font
LOGFONT lf = {0};
lf.lfHeight = -58;
lf.lfWeight = FW_NORMAL;
lf.lfOutPrecision = OUT_TT_PRECIS; //Use TrueType fonts for anti-alliasing
lf.lfQuality = CLEARTYPE_QUALITY;
lstrcpy(lf.lfFaceName, L"Segoe UI");
HFONT hFont = ::CreateFontIndirect(&lf);
//HICON hIcon = original icon to use as a source
//I'm using a large 256x256 pixel icon
hIcon = (HICON)::LoadImage(theApp.m_hInstance, MAKEINTRESOURCE(IDI_ICON_GREEN_DIAMOND), IMAGE_ICON, 256, 256, LR_DEFAULTCOLOR);
ICONINFO ii = {0};
::GetIconInfo(hIcon, &ii);
BITMAP bm = {0};
::GetObject(ii.hbmColor, sizeof(bm), &bm);
SIZE szBmp = {bm.bmWidth, bm.bmHeight};
HDC hDc = ::GetDC(hWnd);
HDC hMemDC = ::CreateCompatibleDC(hDc);
HGDIOBJ hOldBmp = ::SelectObject(hMemDC, ii.hbmColor);
HGDIOBJ hOldFont = ::SelectObject(hMemDC, hFont);
::SetBkMode(hMemDC, TRANSPARENT);
::SetTextColor(hMemDC, RGB(255, 0, 0)); //Red text
//Draw text
//NOTE that DrawText API behaves in a similar way
::TextOut(hMemDC, 0, 0, L"Hello", 5);
::SelectObject(hMemDC, hOldFont);
::SelectObject(hMemDC, hOldBmp);
//We need a simple mask bitmap for the icon
HBITMAP hBmpMsk = ::CreateBitmap(szBmp.cx, szBmp.cy, 1, 1, NULL);
ICONINFO ii2 = {0};
ii2.fIcon = TRUE;
ii2.hbmColor = ii.hbmColor;
ii2.hbmMask = hBmpMsk;
//Create updated icon
HICON hIcon2 = ::CreateIconIndirect(&ii2);
//Cleanup
::DeleteObject(hBmpMsk);
::DeleteDC(hMemDC);
::ReleaseDC(hWnd, hDc);
::DeleteObject(ii.hbmColor);
::DeleteObject(ii.hbmMask);
::DeleteObject(hFont);
and then I can display the icon in my window from OnPaint() handler (so that I can see how it turns out) as such:
::DrawIconEx(dc.GetSafeHdc(), 0, 0,
hIcon2,
256, 256, NULL,
::GetSysColorBrush(COLOR_BTNFACE),
DI_NORMAL);
So here's what I get:
To see what's going on pixel-wise in my hIcon2 I called GetDIBits on its ii.hbmColor from the code above. The resulting pixel array where my word "Hello" was supposed to be shown looked like this:
The pixels are encoded as BGRA in that memory dump, so the 4th byte in each DWORD stands for transparency: 0=transparent, FF=opaque. But in this case TextOut doesn't fill out transparency, or leaves it as 0, which is interpreted as "fully transparent." Instead it seems to pre-multiply it into the RGB colors themselves.
Note that if I keep looking further down the same bitmap, where the green diamond begins, the image pixels seem to have transparency bytes set correctly:
Any idea how to draw text so that the API could set those transparency bytes?
EDIT: As was suggested below I tried the following GDI+ method:
HGDIOBJ hOldBmp = ::SelectObject(hMemDC, ii.hbmColor);
Graphics grpx(hMemDC);
RectF rcfTxt(0.0f, 0.0f, (REAL)szBmp.cx, (REAL)szBmp.cy);
Font gdiFont(L"Segoe UI", 58.0f, FontStyleRegular, UnitPixel);
SolidBrush gdiBrush(Color(255, 0, 0));
StringFormat gdiSF;
gdiSF.SetAlignment(StringAlignmentNear);
gdiSF.SetFormatFlags(StringFormatFlagsNoWrap);
gdiSF.SetHotkeyPrefix(HotkeyPrefixNone);
//The reason I was using GDI was because I was setting
//spacing between letters using SetTextCharacterExtra()
//Unfortunately with GDI+ this does not work!
HDC hTmpDC = grpx.GetHDC();
::SetTextCharacterExtra(hTmpDC, -4); //This doesn't do anything!
grpx.ReleaseHDC(hTmpDC);
grpx.DrawString(L"Hello", 5, &gdiFont, rcfTxt, &gdiSF, &gdiBrush);
::SelectObject(hMemDC, hOldBmp);
and besides not being able to set character spacing (which I could with GDI using SetTextCharacterExtra) here's what I got (slightly enlarged for visibility):
So clearly still an issue with transparency.
Taken from an old post by Microsoft MVP Mike D Sutton here.
When you create a DC it initially has default 'stock' objects selected
into it, including the stock 1*1*1 Bitmap. Since there is a Bitmap
already selected into the DC when you call DrawText() it will still
try and render to it even though pretty much everything (apart from
one pixel) will be clipped.
What you need to do is to create a Bitmap,
either DDB or DIBSection, and select that into your DC before drawing
to it.
First though you need to find the size of your Bitmap since you
want it large enough to display your text in, so for that you use the
DrawText() call again on the initial DC but include the DT_CALCRECT
flag. What this does is rather than drawing anything it simply
measures how large the text is and dumps that into the RECT you pass
the call. From here you can go ahead and create your DIBSection using
those dimensions and select it into your DC. Finally perform your
existing DrawText ()call (you may also want to use SetBkMode/Color())
which will render the text to the DIBSection from which you can get at
the data.
This seems to work pretty well here:
HBITMAP CreateAlphaTextBitmap(LPCSTR inText, HFONT inFont, COLORREF inColour) {
int TextLength = (int)strlen(inText);
if (TextLength <= 0) return NULL;
// Create DC and select font into it
HDC hTextDC = CreateCompatibleDC(NULL);
HFONT hOldFont = (HFONT)SelectObject(hTextDC, inFont);
HBITMAP hMyDIB = NULL;
// Get text area
RECT TextArea = {0, 0, 0, 0};
DrawText(hTextDC, inText, TextLength, &TextArea, DT_CALCRECT);
if ((TextArea.right > TextArea.left) && (TextArea.bottom > TextArea.top)) {
BITMAPINFOHEADER BMIH;
memset(&BMIH, 0x0, sizeof(BITMAPINFOHEADER));
void *pvBits = NULL;
// Specify DIB setup
BMIH.biSize = sizeof(BMIH);
BMIH.biWidth = TextArea.right - TextArea.left;
BMIH.biHeight = TextArea.bottom - TextArea.top;
BMIH.biPlanes = 1;
BMIH.biBitCount = 32;
BMIH.biCompression = BI_RGB;
// Create and select DIB into DC
hMyDIB = CreateDIBSection(hTextDC, (LPBITMAPINFO)&BMIH, 0, (LPVOID*)&pvBits, NULL, 0);
HBITMAP hOldBMP = (HBITMAP)SelectObject(hTextDC, hMyDIB);
if (hOldBMP != NULL) {
// Set up DC properties
SetTextColor(hTextDC, 0x00FFFFFF);
SetBkColor(hTextDC, 0x00000000);
SetBkMode(hTextDC, OPAQUE);
// Draw text to buffer
DrawText(hTextDC, inText, TextLength, &TextArea, DT_NOCLIP);
BYTE* DataPtr = (BYTE*)pvBits;
BYTE FillR = GetRValue(inColour);
BYTE FillG = GetGValue(inColour);
BYTE FillB = GetBValue(inColour);
BYTE ThisA;
for (int LoopY = 0; LoopY < BMIH.biHeight; LoopY++) {
for (int LoopX = 0; LoopX < BMIH.biWidth; LoopX++) {
ThisA = *DataPtr; // Move alpha and pre-multiply with RGB
*DataPtr++ = (FillB * ThisA) >> 8;
*DataPtr++ = (FillG * ThisA) >> 8;
*DataPtr++ = (FillR * ThisA) >> 8;
*DataPtr++ = ThisA; // Set Alpha
}
}
// De-select bitmap
SelectObject(hTextDC, hOldBMP);
}
}
// De-select font and destroy temp DC
SelectObject(hTextDC, hOldFont);
DeleteDC(hTextDC);
// Return DIBSection
return hMyDIB;
}
If you need an example of how to call it then try something like this
(inDC is the DC to render to):
void TestAlphaText(HDC inDC, int inX, int inY) {
const char *DemoText = "Hello World!\0";
RECT TextArea = {0, 0, 0, 0};
HFONT TempFont = CreateFont(50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Arial\0");
HBITMAP MyBMP = CreateAlphaTextBitmap(DemoText, TempFont, 0xFF);
DeleteObject(TempFont);
if (MyBMP) { // Create temporary DC and select new Bitmap into it
HDC hTempDC = CreateCompatibleDC(inDC);
HBITMAP hOldBMP = (HBITMAP)SelectObject(hTempDC, MyBMP);
if (hOldBMP) {
BITMAP BMInf; // Get Bitmap image size
GetObject(MyBMP, sizeof(BITMAP), &BMInf);
// Fill blend function and blend new text to window
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0x80;
bf.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(inDC, inX, inY, BMInf.bmWidth, BMInf.bmHeight,
hTempDC, 0, 0, BMInf.bmWidth, BMInf.bmHeight, bf);
// Clean up
SelectObject(hTempDC, hOldBMP);
DeleteObject(MyBMP);
DeleteDC(hTempDC);
}
}
}
All credit to answer and code go to original posters on that forum, I've simply reposted it so that this answer will be valid if the links die.
This reply is coming almost 3 years after the question was posted, but people still consult these things long into the future. So I'll explain what's happening.
DrawText (and other GDI text functions) will work on a transparent bitmap. The text is not coming out black even though it displays that way. The alpha channel is set to 0 on all pixels the text draws to, overriding whatever alpha you had set previously. If you set an alpha value in SetTextColor the text will render all black. If you're feeling ambitious you can run through pixel by pixel and target anything not your fill color (which requires a single fill color) but the problem then becomes one of the nature of ClearType being overridden and all alphas are set to whatever you set them to. The text ends up looking very funky. If you use a constant alpha for your background fill you can simply do a blanket run across the entire bitmap's bits after the text is drawn and reset all the alpha values. Since you have to read a byte to determine if it's background or not, you might as well just set every pixel's alpha to whatever the standard alpha is for that image and bypass the slow compares. This works reasonably well and I've found it to be very acceptable. In this day and age, MS should have taken care of this long ago but it's not to be.
https://learn.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-antialiasing-with-text-use
Gdiplus::Bitmap bmp( your_Width, your_Height, PixelFormat64bppARGB);
//PixelFormat64bppARGB ARGB needed
FontFamily fontFamily(L"Arial");
Font font(&fontFamily, 29, FontStyleRegular, UnitPoint);
Gdiplus::RectF rectF(00.0f, 10.0f, your_Width, your_Height);
StringFormat stringFormat;
SolidBrush solidBrush(Color(63, 0, 0, 255));
stringFormat.SetAlignment(StringAlignmentCenter);
//solidBrush Color(63, 0, 0, 255) ARGB neede
graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
graphics.DrawString("your_text", -1, &font, rectF, &stringFormat, &solidBrush);
//TextRenderingHintAntiAlias this needed

winapi: from HDC to an HBITMAP

I would like to do something which I believe is fairly simple but since I am new to the winapi I am finding a lot of problems. Basically I have an HDC (which I am BitBlitting from a loaded Bitmap) and I am drawing a rectangle on it. Then I would like to BitBlt that HDC onto a new HBITMAP Object, but alas for now to no avail.
Here is my code which I have been trying to get to work for a couple of hours now
BITMAPINFO info;
Bitmap *tempbmp = Bitmap::FromFile(L"C:\\Users\\abelajc\\Pictures\\BackgroundImage.png", false);
HBITMAP loadedbackground;
tempbmp->GetHBITMAP(NULL, &loadedbackground);
HBRUSH hRed = CreateSolidBrush(RGB(255, 0, 0));
HDC pDC = GetDC(0);
HDC TmpDC = CreateCompatibleDC(pDC); //main DC on which we will paint on
HDC dcBmp = CreateCompatibleDC(TmpDC); //DC for the loadedbackground HBitmap
HGDIOBJ TmpObj2 = SelectObject(dcBmp , tempbmp); //Selecting Bitmap in DC
BitBlt(TmpDC, 0, 0, 512, 512, dcBmp, 0, 0, SRCCOPY);
SelectObject(dcBmp, TmpObj2); //Deselecting Bitmap from DC
DeleteDC(dcBmp);
RECT rectangle;
SetRect(&rectangle, 5, 5, 20, 20);
FillRect(TmpDC, &rectangle, hRed);
HDC hCompDC = CreateCompatibleDC(TmpDC);
HBITMAP hBmp = CreateCompatibleBitmap(TmpDC, 512, 512);
HBITMAP hOld = (HBITMAP)SelectObject(hCompDC, hBmp);
BitBlt(hCompDC, 0, 0, 512, 512, TmpDC, 0, 0, SRCCOPY);
SelectObject(hCompDC, hOld);
DeleteDC(hCompDC);
Bitmap *image = new Bitmap(hBmp, NULL);
I think you just need some clarification about GDI.
A DC is exactly what its name imply : a device context. It's just a context, nothing concrete. Some DCs are context to a real graphic device, some others (memory DCs) are context to a virtual graphic surface in memory. The DCs you create with CreateCompatibleDC are memory DC, but creating the DC only create the context, not the memory surface. As the MSDN documentation says :
Before an application can use a memory DC for drawing operations, it must select a bitmap of the correct width and height into the DC.
You need to associate a HBITMAP with the DC. After doing that, you can consider that drawing to the DC is essentially drawing to the bitmap. The memory DC is the 'window' to the bitmap.
Once you understand that, you will see that your program can be greatly shortened. Feel free to comment if you still have problems.

Create a GDI Rectangle image

I must be doing something wrong or have missed something because all I actually want to is render a rectangle into a bitmap, so that I can CreateWindowEx() on it. Does anyone know what I'm missing?
HDC hdc = GetDC(hWnd);
// Create Pen and brush for the rectangle
HPEN pn = CreatePen(style, stroke, pen);
HBRUSH br = CreateSolidBrush(brush);
// Create a compatible bitmap and DC from the window DC with the correct dimensions
HDC bm_hdc = CreateCompatibleDC(hdc);
HBITMAP hImage = CreateCompatibleBitmap(bm_hdc, sz.x, sz.y);
// Select the bitmap, pen, brush into the DC
HGDIOBJ bm_obj = SelectObject(bm_hdc, hImage);
HGDIOBJ pn_obj = SelectObject(bm_hdc, pn);
HGDIOBJ br_obj = SelectObject(bm_hdc, br);
// Draw the rectangle into the compatible DC with the bitmap selected
::Rectangle(bm_hdc, xPos, yPos, xPos + xSize, yPos + ySize);
// Restore the old selections
SelectObject(bm_hdc, br_obj);
SelectObject(bm_hdc, pn_obj);
SelectObject(bm_hdc, bm_obj);
// Delete the not needed DC, pen and brush
DeleteDC(bm_hdc);
DeleteObject(br);
DeleteObject(pn);
ReleaseDC(hWnd, hdc);
// Create the window and send a message to set the static image
HWND win = CreateWindow(TEXT("STATIC"), NULL, WS_CHILD | SS_BITMAP | WS_VISIBLE, pos.x, pos.y, sz.x, sz.y, hWnd, NULL, hInst, NULL)));
HGDIOBJ obj = (HGDIOBJ)SendMessage(win, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImage);
// Delete the old image
if (obj)
DeleteObject(hImage);
Hummmm... but this doesn't work... All I get is a completely black area and not the rectangle that I have drawn. Any ideas why? Do I need to create another DC and BitBlt() between device contexts?
Thanks for all the help everyone, but I've actually solved it myself and it was SUCH a silly mistake too... Consider this line...:-
::Rectangle(bm_hdc, xPos, yPos, xPos + xSize, yPos + ySize);
Nothing wrong with that at first glance, right? WRONG! If you look at my code, I create a compatible bitmap of the required size to contain my rectangle and try to render the rectangle into this bitmap (which is selected into the DC).
But... WHERE in the bitmap am I rendering? xPos and yPos are window positions of the rectangle, but I'm not rendering to the Window DC am I?!? d'oh! That's right, xPos and yPos should both be 0 because I'm rendering into a bitmap of the correct size and it's when the Window is displayed that xPos and yPos should contain screen coordinates!
Wow... what a dumb mistake and thanks for the nice spot on the HDC from the Window rather than from the compatible DC. I did know that a memory DC has a 1bit depth, but still made that classic blunder. Thanks everyone.
Try changing this line HBITMAP hImage = CreateCompatibleBitmap(bm_hdc, sz.x, sz.y); into this :
HBITMAP hImage = CreateCompatibleBitmap(hdc, sz.x, sz.y);
Paul Watt wrote excellent articles for GDI and image composition with MsImage32.dll.
I am reffering you to this article because it addresses your problem, and here are the relevant quotes and code snippets:
The memory DC is initialized with a mono-chromatic 1x1 pixel bitmap by default.
Avoid a Common Mistake
Before we get too far away from code where I showed you what you need to start running, I want to make sure you are holding this new pair of scissors safely. Do not use a Memory DC with a call to CreateCompatibleBitmap.
...
// You may be tempted to do this; DON'T:
HDC hMemDC = ::CreateCompatibleDC(hDC);
// DON'T DO THIS
// |
// V
HBITMAP hBmp = ::CreateCompatibleBitmap(hMemDC, width, height);
...
// TIP: Try to use the same DC to create
// the Bitmap which you used to create the memory DC.
Remember the part about how The memory DC is initialized with a mono-chromatic 1x1 pixel bitmap by default?!
As for the remarks of member Raymond Chen I believe he is also right, but since you said that your actual code is different this is the only thing I can see as a mistake.
Hopefully this helps.
Best regards.

Combine StretchBlt and TransparentBlt properly, so transparent bitmap can be created properly

INTRODUCTION AND RELEVANT INFORMATION:
Recently, I have asked, here in SO, a question about scaling a bitmap properly, so it can keep the quality of the picture:
Bitmap loses quality when stretched/shrinked on buttons background.
I have tried to employ a suggestion made in a comment, to use `StretchBlt, so I have made a small demo program.
It did improve the bitmaps sharpness, after I have set stretch mode to BLACKONWHITE.
I would like to try to make the portion of the bitmap, with the certain color-say black for example, transparent.
I have used TransparentBlt before, but I don't know how to do it now.
PROBLEM:
In order to preserve the sharpness of the picture, I need to StretchBlt it in the memory DC, with stretch mode being BLACKONWHITE.
The problem is that I do not know how to Blt it transparently into main window's DC.
Here is a code snippet from the demo app:
case WM_PAINT:
{
// main window's DC
hdc = BeginPaint(hWnd, &ps);
// main window's client rectangle
RECT r;
GetClientRect( hWnd, &r );
// memory DC for double buffering
HDC MemDC = CreateCompatibleDC( hdc );
// fill it with test brush
FillRect( MemDC, &r, (HBRUSH)GetStockObject( GRAY_BRUSH ) );
// select loaded bitmap into memory DC
HBITMAP old = (HBITMAP)SelectObject( MemDC, bmp );
// get bitmaps dimensions
BITMAP b;
GetObject( bmp, sizeof(BITMAP), &b );
// needed to preserve bitmap's sharpness
SetStretchBltMode( hdc, BLACKONWHITE );
StretchBlt( hdc, 0, 0, r.right - r.left, r.bottom - r.top,
MemDC, 0, 0, b.bmWidth, b.bmHeight, SRCCOPY );
/* TransparentBlt( ... ); call should go here,
so I can make portion of the bitmap transparent,
in order for the gray brush can be seen */
// cleanup
SelectObject( MemDC, old );
DeleteDC(MemDC);
EndPaint(hWnd, &ps);
}
return 0L;
break;
QUESTION:
How to modify the above code, so a bitmap can be transparent, in order for test brush to be seen ?
The original image is bellow.
I just need to use TransparentBlt( ..., RGB( 0, 0, 0 ) ); to make it transparent in black areas.
The example picture that shows result:
MY EFFORTS:
Browsing through Internet, I have found only simple tutorials, regarding double buffering.
I haven't found anything like this, but to be honest, I am inexperienced in WIN32 API, so I don't know how to phrase the question properly, in order to get better search results.
If further information is required, ask for it and I will supply it.
It is omitted to keep the question short.
You Need to create a mask use specific raster operations to copy only the Pixels were the mask is defined.
http://www.winprog.org/tutorial/transparency.html
The next code is MFC, but you can easily extract and convert the MFC objects into the Standard GDI operations.
http://www.codeproject.com/Articles/703/Drawing-Transparent-Bitmap-with-ease-with-on-the-f

C++ GDI+ Drawing Image on layered window not working

So I've found a lot of code samples, guides, and answers on SO about drawing an image to a layered window. I've tried using pure HBITMAPS and the WIC libs to draw, and now I'm on to GDI+ to draw (which is much simpler and is seemingly easier to use, and thus far it has solved a lot of errors that were caused by faulty WIC code).
I'm currently stuck on UpdateLayeredWindow. No matter what I try I can't get it to work. Right now, it's returning 87, or ERROR_INVALID_PARAMETER. The question is, which one is incorrect? I'm stumped! The below code seems to be the solution other than the fact that UpdateLayeredWindow is refusing to work.
What am I doing wrong?
Here is the code that sets up the HDC/bitmap information/graphics object.
// Create DC
_oGrphInf.canvasHDC = GetDC(_hwndWindow);
// Create drawing 'canvas'
_oGrphInf.lpBits = NULL;
_oGrphInf.bmpCanvas = CreateDIBSection(_oGrphInf.canvasHDC,
&_oGrphInf.bmpWinInformation, DIB_RGB_COLORS,
&_oGrphInf.lpBits, NULL, 0);
// Create graphics object
_oGrphInf.graphics = new Gdiplus::Graphics(_oGrphInf.canvasHDC);
The above works fine - I step through it and all of the pointers work.
And here is the method that draws the PNG.
void Splash::DrawPNG(PNG* lpPNG, int x, int y)
{
LOGD("Drawing bitmap!");
HDC hdcMem = CreateCompatibleDC(_oGrphInf.canvasHDC);
// Select
HBITMAP bmpOld = (HBITMAP)SelectObject(hdcMem, _oGrphInf.bmpCanvas);
Gdiplus::Color trans(0, 0, 0, 0);
_oGrphInf.graphics->Clear(trans);
_oGrphInf.graphics->DrawImage(lpPNG->GetImage(), x, y);
_oGrphInf.graphics->Flush();
SIZE szSize = {_oGrphInf.bmpWinInformation.bmiHeader.biWidth,
_oGrphInf.bmpWinInformation.bmiHeader.biHeight};
// Setup drawing location
POINT ptLoc = {0, 0};
POINT ptSrc = {0, 0};
// Set up alpha blending
BLENDFUNCTION blend = {0};
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
blend.BlendFlags = 0;
// Update
if(UpdateLayeredWindow(_hwndWindow, _oGrphInf.canvasHDC, &ptLoc,
&szSize, hdcMem, &ptSrc,
(COLORREF)RGB(0, 0, 0),
&blend, ULW_ALPHA) == FALSE)
LOGE("Could not update layered window: %u", GetLastError());
// Delete temp objects
SelectObject(hdcMem, bmpOld);
DeleteObject(hdcMem);
DeleteDC(hdcMem);
}
Pulling my hair out! Help?
EDIT: I just decided to re-write the call to UpdateLayeredWindow function, which solved the incorrect parameter issue. Here is what I came up with. However, it still does not work. What am I doing wrong?
UpdateLayeredWindow(_hwndWindow, _oGrphInf.canvasHDC,
NULL, NULL, hdcMem, &ptLoc,
RGB(0, 0, 0), &blend, ULW_ALPHA)
For alpha information to be preserved in drawing operations, you have to make your Graphics object based on a memory-backed Bitmap object, not an HDC, and of course your Bitmap needs to be in a format with an alpha channel.
You'll need to use this Bitmap constructor: http://msdn.microsoft.com/en-us/library/ms536315%28v=vs.85%29.aspx
Just give that a stride of 0, a pointer to your DIB's bits, and PixelFormat32bppPARGB.
Then use Graphics::FromImage to create your Graphics object.
I've never used UpdateLayeredWindow, so I can't verify that that side of it is correct.