UpdateLayeredWindow not respecting alpha channel with certain colored pixels

EDN Admin

Well-known member
Joined
Aug 7, 2010
Messages
12,794
Location
In the Machine
I seem to have uncovered another bug when using layered windows. I can load a PNG with a per-pixel alpha channel and display it without any issues. I can then iterate through that bitmap and adjust the red, green, or blue channel without adjusting the alpha
channels intensity and display the image with per-pixel transparency just fine. However, if I set the red, green, and blue channel intensities to 255, the per-pixel alpha channel fails to be acknowledged and the layered window renders as an opaque white rectangle.
The color key, which should be unused, is set to red, so I am lost as to what would be causing this behavior.
Thank you,
Jason Stern

<pre class="prettyprint #include <Objidl.h>
#include <tchar.h>
#include <Wincodec.h>
#include <Windows.h>
#include <WinUser.h>
#include <xutility>

#include "Resource.h"

const TCHAR CLASS_NAME [] = TEXT ("Test");
HWND hwnd = 0;
BLENDFUNCTION blend_function;
HBITMAP transparent_png_handle;
RECT window_size = { 0, 0, 320, 240 };
HGLOBAL resource_data = NULL;

IWICBitmapSource* LoadImageFromDecoder (IWICBitmapDecoder* image_decoder)
{
IWICBitmapSource* bitmap_source (NULL);
IWICBitmapFrameDecode* frame_pointer (NULL);
image_decoder->GetFrame (0, &frame_pointer);
WICConvertBitmapSource (GUID_WICPixelFormat32bppPBGRA, frame_pointer, &bitmap_source);
frame_pointer->Release();
return bitmap_source;
}

IWICBitmapSource* LoadBitmapFromStream (IStream* image_stream)
{
bool release_decoder (false);
IWICImagingFactory* wic_imaging_factory (NULL);
IWICBitmapDecoder* wic_bitmap_decoder (NULL);
IWICBitmapSource* wic_bitmap_source (NULL);
CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory, (LPVOID*) &wic_imaging_factory);
wic_imaging_factory->CreateDecoderFromStream (image_stream, NULL, WICDecodeMetadataCacheOnDemand,
&wic_bitmap_decoder);
wic_bitmap_source = LoadImageFromDecoder (wic_bitmap_decoder);
wic_bitmap_decoder->Release();
wic_imaging_factory->Release();
return wic_bitmap_source;
}

HBITMAP CreateHBitmap (IWICBitmapSource* bitmap_source)
{
UINT width (0);
UINT height (0);
bitmap_source->GetSize (&width, &height);
BITMAPINFO bitmap_info;
ZeroMemory (&bitmap_info, sizeof (bitmap_info));
bitmap_info.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
bitmap_info.bmiHeader.biWidth = width;
bitmap_info.bmiHeader.biHeight = -((LONG)height);
bitmap_info.bmiHeader.biPlanes = 1;
bitmap_info.bmiHeader.biBitCount = 32;
bitmap_info.bmiHeader.biCompression = BI_RGB;
void* image_bits (NULL);
HDC screen_device_context (GetDC (NULL));
HBITMAP bitmap_handle (CreateDIBSection (screen_device_context, &bitmap_info, DIB_RGB_COLORS,
&image_bits, NULL, 0));
ReleaseDC (NULL, screen_device_context);
const UINT stride (width << 2);
const UINT buffer_size (stride * height);
bitmap_source->CopyPixels (NULL, stride, buffer_size, static_cast<BYTE *> (image_bits));
window_size.right = (LONG)width;
window_size.bottom = (LONG)height;
return bitmap_handle;
}

IStream* CreateStreamOnResource (LPCTSTR resource_name, LPCTSTR resource_type)
{
IStream* image_stream (NULL);
HRSRC resource_handle (FindResource (NULL, resource_name, resource_type));
DWORD resource_size (SizeofResource (NULL, resource_handle));
HGLOBAL image_handle (LoadResource (NULL, resource_handle));
LPVOID source_resource_data (LockResource (image_handle));
resource_data = GlobalAlloc (GMEM_MOVEABLE, resource_size);
LPVOID locked_resource_data = GlobalLock (resource_data);
CopyMemory (locked_resource_data, source_resource_data, resource_size);
GlobalUnlock (resource_data);
CreateStreamOnHGlobal (resource_data, TRUE, &image_stream);
return image_stream;
}

IWICBitmapSource* LoadImageFromResource (LPCTSTR resource_name, LPCTSTR resource_type)
{
IStream* image_stream (CreateStreamOnResource (resource_name, resource_type));
IWICBitmapSource* image_source (LoadBitmapFromStream (image_stream));
image_stream->Release();
return image_source;
}

void loadImage (void)
{
HINSTANCE instance_handle (GetModuleHandle (0));
CoInitialize (0);
LPCSTR resource_name (MAKEINTRESOURCE (IDB_TRANSPARENT_PNG));
IWICBitmapSource* source (LoadImageFromResource (resource_name, TEXT ("PNG")));
transparent_png_handle = CreateHBitmap (source);
source->Release ();
CoUninitialize ();
}

LRESULT CALLBACK WindowProc (HWND window_handle, UINT message, WPARAM w_param, LPARAM l_param)
{
LRESULT result (0);

switch (message)
{
case WM_DESTROY:
PostQuitMessage (0);
if (resource_data != NULL)
{
GlobalFree (resource_data);
}
break;

default:
result = DefWindowProc (window_handle, message, w_param, l_param);
break;
}
return result;
}

void adjustRGB (HDC& target_device_context, HBITMAP& autowhite_texture, HBITMAP& texture,
int mask)
{
if (texture != NULL)
{
BITMAP bitmap;
ZeroMemory (&bitmap, sizeof (BITMAP));
LPBITMAPINFO bitmap_info (NULL);
WORD bitmap_width (0), bitmap_height (0);
GetObject (texture, sizeof (bitmap), &bitmap);
bitmap_info = (LPBITMAPINFO)&bitmap;
bitmap_width = (WORD)bitmap_info->bmiHeader.biWidth;
bitmap_width -= (bitmap_width % 4);
bitmap_height = (WORD)bitmap_info->bmiHeader.biHeight;
int* bitmap_data = new int[bitmap_width * bitmap_height];
HDC window_device_context = GetWindowDC (NULL);
BITMAPINFO requested_bitmap_info;
ZeroMemory (&requested_bitmap_info, sizeof (BITMAPINFO));
requested_bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
requested_bitmap_info.bmiHeader.biWidth = bitmap_width;
requested_bitmap_info.bmiHeader.biHeight = -bitmap_height;
requested_bitmap_info.bmiHeader.biPlanes = 1;
requested_bitmap_info.bmiHeader.biBitCount = 32;
requested_bitmap_info.bmiHeader.biCompression = BI_RGB;
requested_bitmap_info.bmiHeader.biSizeImage = bitmap_width * bitmap_height * 4;
requested_bitmap_info.bmiHeader.biXPelsPerMeter = 0;
requested_bitmap_info.bmiHeader.biYPelsPerMeter = 0;
requested_bitmap_info.bmiHeader.biClrUsed = 0;
requested_bitmap_info.bmiHeader.biClrImportant = 0;
GetDIBits (window_device_context, texture, 0, bitmap_height,
(LPVOID)bitmap_data, &requested_bitmap_info, DIB_RGB_COLORS);
for (int index (0);
index < (bitmap.bmWidth * bitmap.bmHeight) - 1;
++index)
{
bitmap_data [index] |= mask;
}
SetDIBits (target_device_context, autowhite_texture, 0, bitmap_height,
(LPVOID)bitmap_data, &requested_bitmap_info, DIB_RGB_COLORS);
delete[] bitmap_data;
ReleaseDC (NULL, window_device_context);
}
}

int CALLBACK WinMain (__in HINSTANCE hInstance, __in HINSTANCE hPrevInstance,
__in LPSTR lpCmdLine, __in int nCmdShow)
{
HINSTANCE h_instance = GetModuleHandle (0);

blend_function.BlendOp = AC_SRC_OVER;
blend_function.BlendFlags = 0;
blend_function.SourceConstantAlpha = 0xFF;
blend_function.AlphaFormat = AC_SRC_ALPHA;

WNDCLASSEX wcex;
wcex.cbSize = sizeof (WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = h_instance;
wcex.hIcon = LoadIcon (h_instance, MAKEINTRESOURCE (IDI_APPLICATION));
wcex.hCursor = LoadCursor (NULL, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush (RGB (255, 0, 0));
wcex.lpszMenuName = 0;
wcex.lpszClassName = CLASS_NAME;
wcex.hIconSm = LoadIcon (wcex.hInstance, MAKEINTRESOURCE (IDI_APPLICATION));
RegisterClassEx (&wcex);

loadImage ();
SIZE destination_size = { window_size.right - window_size.left, window_size.bottom - window_size.top };

hwnd = CreateWindowEx (WS_EX_LAYERED | WS_EX_APPWINDOW, CLASS_NAME, CLASS_NAME,
WS_VISIBLE | WS_CAPTION | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU,
window_size.left, window_size.top, destination_size.cx, destination_size.cy,
NULL, NULL, GetModuleHandle (0), NULL);

if (hwnd)
{
ShowWindow (hwnd, SW_SHOW);

POINT source_coordinates = { 0 };
POINT destination_coordinates = { window_size.left, window_size.top };
HDC screen_device_context (GetDC (NULL));
HDC memory_device_context (CreateCompatibleDC (screen_device_context));

// displaying the original image works:
// HBITMAP texture (transparent_png_handle);
// HBITMAP previously_selected_object = (HBITMAP)SelectObject (memory_device_context, texture);
// however, switching to this:
HBITMAP texture (CreateCompatibleBitmap (screen_device_context, destination_size.cx, destination_size.cy));
HBITMAP previously_selected_object = (HBITMAP)SelectObject (memory_device_context, texture);
adjustRGB (memory_device_context, texture, transparent_png_handle, 0x00FFFFFF);
// the alpha channel is lost. but if we adjust a single channel (in this case green):
// HBITMAP texture (CreateCompatibleBitmap (screen_device_context, destination_size.cx, destination_size.cy));
// HBITMAP previously_selected_object = (HBITMAP)SelectObject (memory_device_context, texture);
// adjustRGB (memory_device_context, texture, transparent_png_handle, 0x0000FF00);
// ...the alpha channel is not lost.

UpdateLayeredWindow (hwnd, screen_device_context, &destination_coordinates, &destination_size,
memory_device_context, &source_coordinates, RGB (255, 0, 0), &blend_function, ULW_ALPHA);
SelectObject (memory_device_context, previously_selected_object);
DeleteDC (memory_device_context);
ReleaseDC (NULL, screen_device_context);

while (true)
{
MSG msg;
BOOL ret (GetMessage (&msg, 0, 0, 0));
if (ret > 0)
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
else
break;
}
}
}
[/code]
<br/>


View the full article
 
Back
Top