UpdateLayeredWindow and DrawText - winapi

I'm using UpdateLayeredWindow to display an application window. I have created my own custom buttons and i would like to create my own static text. The problem is that when i try to draw the text on the hdc, the DrawText or TextOut functions overwrite the alpha channel of my picture and the text will become transparent. I tried to find a solution to this but i could not find any. My custom controls are designed in such way that they will do all the drawing in a member function called Draw(HDC hDc), so they can only access the hdc. I would like to keep this design. Can anyone help me? I am using MFC and i would want to achieve the desired result without the use of GDI+.

I know this is an old post ... but I just had this very same problem ... and it was driving me CRAZY.
Eventually, I stumbled upon this post by Mike Sutton to the microsoft.public.win32.programmer.gdi newsgroup ... from almost 7 years ago!
Basically, the DrawText (and TextOut) do not play nicely with the alpha channel and UpdateLayeredWindow ... and you need to premultiply the R, G, and B channels with the alpha channel.
In Mike's post, he shows how he creates another DIB (device independent bitmap) upon which he draws the text ... and alpha blends that into the other bitmap.
After doing this, my text looked perfect!
Just in case, the link to the newsgroup post dies ... I am going to include the code here. All credit goes to Mike Sutton (#mikedsutton).
Here is the code that creates the alpha blended bitmap with the text on it:
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;
}
Here is the code that drives the CreateAlphaTextBitmap method:
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)
{
// Get Bitmap image size
BITMAP BMInf;
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);
}
}
}

Related

How to set alpha value for all pixels in a bitmap using MFC or GDI or GDI+

I am in an MFC application. I created a bitmap using a memory DC I want to save it to DIB file.
I found this code to be most elegant so far:
void Save(CBitmap * bitmap) {
CImage image;
image.Attach((HBITMAP)pcBitmap->GetSafeHandle());
image.Save("bla.bmp", Gdiplus::ImageFormatBMP);
}
The resulting file is 32 BPP colorspace with all alpha values set to '0'.
Now I want use the Bitmap as toolbar bitmap:
CMFCToolbar::GetImages()->Load("bla.bmp");
But all the icons are gone.
MFC internally calls PreMultiplyAlpha() when importing the bitmap.
Then RGB components of all pixels are '0'. Effectively the whole bitmap was zeroed.
How can I set the alpha value for each pixel to '0xFF' before saving?
I tried:
void Save(CBitmap * bitmap) {
CImage image;
image.Attach((HBITMAP)pcBitmap->GetSafeHandle());
image.SetHasAlphaChannel(true);
image.AlphaBlend(myBitmapDC, 0, 0);
image.Save("bla.bmp", Gdiplus::ImageFormatBMP);
}
But that affects only RGB values of the pixels.
So far I resisted to iterate over each pixel and modifying the memory of the bitmap. I'm asking for an elegant solution. Maybe a one-liner.
Use GetDIBits to read 32-bit pixel data, and loop through the bits to set alpha to 0xFF.
bool Save(CBitmap *bitmap)
{
if(!bitmap)
return false;
BITMAP bm;
bitmap->GetBitmap(&bm);
if(bm.bmBitsPixel < 16)
return false;
DWORD size = bm.bmWidth * bm.bmHeight * 4;
BITMAPINFOHEADER bih = { sizeof(bih), bm.bmWidth, bm.bmHeight, 1, 32, BI_RGB };
BITMAPFILEHEADER bfh = { 'MB', 54 + size, 0, 0, 54 };
CClientDC dc(0);
std::vector<BYTE> vec(size, 0xFF);
int test = GetDIBits(dc, *bitmap, 0, bm.bmHeight, &vec[0],
(BITMAPINFO*)&bih, DIB_RGB_COLORS);
for(DWORD i = 0; i < size; i += 4)
vec[i + 3] = 0xFF;
CFile fout;
if(fout.Open(filename, CFile::modeCreate | CFile::modeWrite))
{
fout.Write(&bfh, sizeof(bfh));
fout.Write(&bih, sizeof(bih));
fout.Write(&vec[0], size);
return true;
}
return false;
}
As an alternative (but I am not sure if this is reliable) initialize the memory with 0xFF. GetDIBits will set the RGB part but won't overwrite the alpha values:
std::vector<BYTE> vec(size, 0xFF);
GetDIBits...
Or using GDI+
bool Save(CBitmap *bitmap)
{
if(!bitmap)
return false;
BITMAP bm;
bitmap->GetBitmap(&bm);
if(bm.bmBitsPixel < 16)
return false; //needs palette
Gdiplus::GdiplusStartupInput tmp;
ULONG_PTR token;
Gdiplus::GdiplusStartup(&token, &tmp, NULL);
Gdiplus::Bitmap *src = Gdiplus::Bitmap::FromHBITMAP(*bitmap, NULL);
Gdiplus::Bitmap *dst = src->Clone(0, 0, src->GetWidth(), src->GetHeight(),
PixelFormat32bppARGB);
LPCOLESTR clsid_bmp = L"{557cf400-1a04-11d3-9a73-0000f81ef32e}";
CLSID clsid;
CLSIDFromString(clsid_bmp, &clsid);
bool result = dst->Save(L"file.bmp", &clsid) == 0;
delete src;
delete dst;
Gdiplus::GdiplusShutdown(token);
return result;
}

Rendering Windows screenshot capture bitmap as DirectX texture

I'm making progress developing a '3d desktop' directx app that needs to display the current contents of a desktop window (e.g. "Calculator") as a 2D texture on a rectangular surface in directx (11). I'm sooo close but really struggling with the screenshot BMP -> Texture2D step. I do have screenshot->HBITMAP and DDSFile->rendered texture successfully working but can't complete the screenshot->rendered texture.
So far I have working the 'capture the window as a screenshot' bit:
RECT user_window_rectangle;
HWND user_window = FindWindow(NULL, TEXT("Calculator"));
GetClientRect(user_window, &user_window_rectangle);
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
UINT screenshot_width = user_window_rectangle.right - user_window_rectangle.left;
UINT screenshot_height = user_window_rectangle.bottom - user_window_rectangle.top;
hbmp = CreateCompatibleBitmap(hdcScreen, screenshot_width, screenshot_height);
SelectObject(hdc, hbmp);
PrintWindow(user_window, hdc, PW_CLIENTONLY);
At this point I have the window bitmap referenced by HBITMAP hbmp.
Also working is my code to render a DDS file as a texture on a directx/3d rectangle:
ID3D11Device *dev;
ID3D11DeviceContext *dev_context;
...
dev_context->PSSetShaderResources(0, 1, &shader_resource_view);
dev_context->PSSetSamplers(0, 1, &tex_sampler_state);
...
DirectX::TexMetadata tex_metadata;
DirectX::ScratchImage image;
hr = LoadFromDDSFile(L"Earth.dds", DirectX::DDS_FLAGS_NONE, &tex_metadata, image);
hr = CreateShaderResourceView(dev, image.GetImages(), image.GetImageCount(), tex_metadata, &shader_resource_view);
Pixel shader is:
Texture2D ObjTexture
SamplerState ObjSamplerState
float4 PShader(float4 pos : SV_POSITION, float4 color : COLOR, float2 tex : TEXCOORD) : SV_TARGET\
{
return ObjTexture.Sample( ObjSamplerState, tex );
}
The samplerstate (defaulting to linear) is:
D3D11_SAMPLER_DESC sampler_desc;
ZeroMemory(&sampler_desc, sizeof(sampler_desc));
sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampler_desc.MinLOD = 0;
sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
hr = dev->CreateSamplerState(&sampler_desc, &tex_sampler_state);
Question: how do I replace the LoadFromDDSFile bit with some equivalent that takes the HBITMAP from the windows screencapture and ends up with it on the graphics card as ObjTexture ?
Below is my best shot of bridging from the screenshot HBITMAP hbmp to the shader resource screenshot_texture, but it gives a memory access violation from the graphics driver (I think due to my "data.pSysmem = &bmp.bmBits", but no idea really):
GetObject(hbmp, sizeof(BITMAP), (LPSTR)&bmp)
D3D11_TEXTURE2D_DESC screenshot_desc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R8G8B8A8_UNORM, bmp.bmWidth, bmp.bmHeight, 1,
1,
D3D11_BIND_SHADER_RESOURCE
);
int bytes_per_pixel = 4;
D3D11_SUBRESOURCE_DATA data;
ZeroMemory(&data, sizeof(D3D11_SUBRESOURCE_DATA));
data.pSysMem = &bmp.bmBits; //pixel buffer
data.SysMemPitch = bytes_per_pixel * bmp.bmWidth;// line size in byte
data.SysMemSlicePitch = bytes_per_pixel * bmp.bmWidth * bmp.bmHeight;// total buffer size in byte
hr = dev->CreateTexture2D(
&screenshot_desc, //texture format
&data, // pixel buffer use to fill the texture
&screenshot_texture // created texture
);
:::::::::::::::::::::::::SOLUTION::::::::::::::::::::::::::::::::::::::::::
The main issue was trying to use &bmp.bmBits directly as a pixel buffer caused memory conflicts within the graphics driver - this was resolved by using 'malloc' to allocate an appropriately sized block of memory to store the pixel data. Thanks to Chuck Walbourn for helping with my poking around in the dark to work out how the pixel data is actually stored (it was actually 32 bits/pixel by default). It's still possible/likely some of code is relying on luck to read the pixel data correctly, but it's been improved with Chuck's input.
My basic technique was;
FindWindow to get the client window on the desktop
CreateCompatibleBitmap and SelectObject and PrintWindow to get a HBITMAP to the snapshot
malloc to allocate the correct amount of space for a (byte*)pixel buffer
GetDIBits to populate the (byte*)pixel buffer from the HBITMAP
CreateTexture2D to build the texture buffer
CreateShaderResourceView to map the texture to the graphics pixel shader
So working code to screenshot a windows desktop window and pass that as a texture to a direct3d app is:
RECT user_window_rectangle;
HWND user_window = FindWindow(NULL, TEXT("Calculator")); //the window can't be min
if (user_window == NULL)
{
MessageBoxA(NULL, "Can't find Calculator", "Camvas", MB_OK);
return;
}
GetClientRect(user_window, &user_window_rectangle);
//create
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
UINT screenshot_width = user_window_rectangle.right - user_window_rectangle.left;
UINT screenshot_height = user_window_rectangle.bottom - user_window_rectangle.top;
hbmp = CreateCompatibleBitmap(hdcScreen, screenshot_width, screenshot_height);
SelectObject(hdc, hbmp);
//Print to memory hdc
PrintWindow(user_window, hdc, PW_CLIENTONLY);
BITMAPINFOHEADER bmih;
ZeroMemory(&bmih, sizeof(BITMAPINFOHEADER));
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biPlanes = 1;
bmih.biBitCount = 32;
bmih.biWidth = screenshot_width;
bmih.biHeight = 0-screenshot_height;
bmih.biCompression = BI_RGB;
bmih.biSizeImage = 0;
int bytes_per_pixel = bmih.biBitCount / 8;
BYTE *pixels = (BYTE*)malloc(bytes_per_pixel * screenshot_width * screenshot_height);
BITMAPINFO bmi = { 0 };
bmi.bmiHeader = bmih;
int row_count = GetDIBits(hdc, hbmp, 0, screenshot_height, pixels, &bmi, DIB_RGB_COLORS);
D3D11_TEXTURE2D_DESC screenshot_desc = CD3D11_TEXTURE2D_DESC(
DXGI_FORMAT_B8G8R8A8_UNORM, // format
screenshot_width, // width
screenshot_height, // height
1, // arraySize
1, // mipLevels
D3D11_BIND_SHADER_RESOURCE, // bindFlags
D3D11_USAGE_DYNAMIC, // usage
D3D11_CPU_ACCESS_WRITE, // cpuaccessFlags
1, // sampleCount
0, // sampleQuality
0 // miscFlags
);
D3D11_SUBRESOURCE_DATA data;
ZeroMemory(&data, sizeof(D3D11_SUBRESOURCE_DATA));
data.pSysMem = pixels; // texArray; // &bmp.bmBits; //pixel buffer
data.SysMemPitch = bytes_per_pixel * screenshot_width;// line size in byte
data.SysMemSlicePitch = bytes_per_pixel * screenshot_width * screenshot_height;
hr = dev->CreateTexture2D(
&screenshot_desc, //texture format
&data, // pixel buffer use to fill the texture
&screenshot_texture // created texture
);
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc.Format = screenshot_desc.Format;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Texture2D.MostDetailedMip = screenshot_desc.MipLevels;
dev->CreateShaderResourceView(screenshot_texture, NULL, &shader_resource_view);
You are making a lot of assumptions here that the BITMAP returned is actually in 32-bit RGBA form. It is likely not at all in that format, and in any case you need to validate the contents of bmPlanes to be 1 and bmBitsPixel to be 32 if you are assuming it is 4-bytes per pixel. You should read more about the BMP format.
BMPs uses BGRA order, so you can use DXGI_FORMAT_B8G8R8A8_UNORM for the case of bmBitsPixel being 32.
Secondly, you need to derive pitch from bmWidthBytes and not bmWidth.
data.pSysMem = &bmp.bmBits; //pixel buffer
data.SysMemPitch = bmp.bmWidthBytes;// line size in byte
data.SysMemSlicePitch = bmp.bmWidthBytes * bmp.bmHeight;// total buffer size in byte
If bmBitsPixel is 24, there is no DXGI format equivalent to that. You have to copy the data to a 32-bit format such as DXGI_FORMAT_B8G8R8X8_UNORM.
If bmBitsPixel is 15 or 16, you can use DXGI_FORMAT_B5G5R5A1_UNORM on a system with Direct3D 11.1, but remember that 16-bit DXGI formats are not always supported depending on the driver. Otherwise you'll have to convert this data to something else.
For bmBitsPixel values of 1, 2, 4, or 8 you have to convert them as there are no DXGI texture formats that are equivalent.
The main issue was trying to use &bmp.bmBits directly as a pixel buffer caused memory conflicts within the graphics driver - this was resolved by using 'malloc' to allocate an appropriately sized block of memory to store the pixel data. Thanks to Chuck Walbourn for helping with my poking around in the dark to work out how the pixel data is actually stored (it was actually 32 bits/pixel by default). It's still possible/likely some of code is relying on luck to read the pixel data correctly, but it's been improved with Chuck's input.
My basic technique was;
FindWindow to get the client window on the desktop
CreateCompatibleBitmap and SelectObject and PrintWindow to get a HBITMAP to the snapshot
malloc to allocate the correct amount of space for a (byte*)pixel buffer
GetDIBits to populate the (byte*)pixel buffer from the HBITMAP
CreateTexture2D to build the texture buffer
CreateShaderResourceView to map the texture to the graphics pixel shader
So working code to screenshot a windows desktop window and pass that as a texture to a direct3d app is:
RECT user_window_rectangle;
HWND user_window = FindWindow(NULL, TEXT("Calculator")); //the window can't be min
if (user_window == NULL)
{
MessageBoxA(NULL, "Can't find Calculator", "Camvas", MB_OK);
return;
}
GetClientRect(user_window, &user_window_rectangle);
//create
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
UINT screenshot_width = user_window_rectangle.right - user_window_rectangle.left;
UINT screenshot_height = user_window_rectangle.bottom - user_window_rectangle.top;
hbmp = CreateCompatibleBitmap(hdcScreen, screenshot_width, screenshot_height);
SelectObject(hdc, hbmp);
//Print to memory hdc
PrintWindow(user_window, hdc, PW_CLIENTONLY);
BITMAPINFOHEADER bmih;
ZeroMemory(&bmih, sizeof(BITMAPINFOHEADER));
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biPlanes = 1;
bmih.biBitCount = 32;
bmih.biWidth = screenshot_width;
bmih.biHeight = 0-screenshot_height;
bmih.biCompression = BI_RGB;
bmih.biSizeImage = 0;
int bytes_per_pixel = bmih.biBitCount / 8;
BYTE *pixels = (BYTE*)malloc(bytes_per_pixel * screenshot_width * screenshot_height);
BITMAPINFO bmi = { 0 };
bmi.bmiHeader = bmih;
int row_count = GetDIBits(hdc, hbmp, 0, screenshot_height, pixels, &bmi, DIB_RGB_COLORS);
D3D11_TEXTURE2D_DESC screenshot_desc = CD3D11_TEXTURE2D_DESC(
DXGI_FORMAT_B8G8R8A8_UNORM, // format
screenshot_width, // width
screenshot_height, // height
1, // arraySize
1, // mipLevels
D3D11_BIND_SHADER_RESOURCE, // bindFlags
D3D11_USAGE_DYNAMIC, // usage
D3D11_CPU_ACCESS_WRITE, // cpuaccessFlags
1, // sampleCount
0, // sampleQuality
0 // miscFlags
);
D3D11_SUBRESOURCE_DATA data;
ZeroMemory(&data, sizeof(D3D11_SUBRESOURCE_DATA));
data.pSysMem = pixels; // texArray; // &bmp.bmBits; //pixel buffer
data.SysMemPitch = bytes_per_pixel * screenshot_width;// line size in byte
data.SysMemSlicePitch = bytes_per_pixel * screenshot_width * screenshot_height;
hr = dev->CreateTexture2D(
&screenshot_desc, //texture format
&data, // pixel buffer use to fill the texture
&screenshot_texture // created texture
);
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc.Format = screenshot_desc.Format;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Texture2D.MostDetailedMip = screenshot_desc.MipLevels;
dev->CreateShaderResourceView(screenshot_texture, NULL, &shader_resource_view);

Create ARGB DIB

How to create a DIB with ARGB format. I want to blit a image(that has some part transparent in it ) using this DIB.
I tried with the following code but its not working properly
unsigned char * rawdata; ==> Filled by Qimage Raw Data
unsigned char * buffer = NULL;
memset(&bmi, 0, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = width;/* Width of your image buffer */
bmi.bmiHeader.biHeight = -height; /* Height of your image buffer */
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
HBITMAP g_dibbmp = CreateDIBSection(hDesktopDC, &bmi, DIB_RGB_COLORS, (void **)&buffer, 0, 0);
if (!buffer)
{ /* ERROR */
printf("ERROR DIB could not create buffer\n");
}
else
{
printf("DIB created buffer successfully\n");
memcpy(buffer,rawdata,sizeof(rawdata));
}
Please help.
Reagards,
Techtotie.
Here's a snippet I put together from pieces of working code. The main difference I see is setting the mask bits and using memsection.
// assumes height and width passed in
int bpp = 32; // Bits per pixel
int stride = (width * (bpp / 8));
unsigned int byteCount = (unsigned int)(stride * height);
HANDLE hMemSection = ::CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, byteCount, NULL );
if (hMemSection == NULL)
return false;
BITMAPV5HEADER bmh;
memset( &bmh, 0, sizeof( BITMAPV5HEADER ) );
bmh.bV5Size = sizeof( BITMAPV5HEADER );
bmh.bV5Width = width;
bmh.bV5Height = -height;
bmh.bV5Planes = 1;
bmh.bV5BitCount = 32;
bmh.bV5Compression = BI_RGB;
bmh.bV5AlphaMask = 0xFF000000;
bmh.bV5RedMask = 0x00FF0000;
bmh.bV5GreenMask = 0x0000FF00;
bmh.bV5BlueMask = 0x000000FF;
HDC hdc = ::GetDC( NULL );
HBITMAP hDIB = ::CreateDIBSection( hdc, (BITMAPINFO *) &bmh, DIB_RGB_COLORS,
&pBits, hMemSection, (DWORD) 0 );
::ReleaseDC( NULL, hdc );
// Much later when done manipulating the bitmap
::CloseHandle( hMemSection );
Thanks for your answer.
But my problem got solved. It was not actually the problem with the DIB creation.
It was due to the wrong API that I was using for Blitting.
I was using BitBlt for blitting but this API does not take care of the Alpha gradient. Instead of it I tried
TransparentBlt (Refer : http://msdn.microsoft.com/en-us/library/windows/desktop/dd145141(v=vs.85).aspx)
and it worked as this API takes care of copying the Alpha values from Source DC to destination DC.

How to capture screen bitmap correctly in 16/24/32 bit color

I am capturing particular portion of desktop window in a bitmap and trying to print
BGR pixel color value. This captured portion of desktop window is completely filled with 16, 0, 16 color.
When I capture and print the data when my window color depth is 32 everything is right,
but if my window is in 24/16 bit color mode then I am getting different pixel values instead of 16, 0, 16.
I am capturing screen left = 150, top = 150, right = 200, bottom = 200.
*********CAPTURING AN IMAGE FROM DESKTOP*********
iLeft = 150;
iTop = 150;
iRight = iLeft + 50;
iBottom = iTop + 50;
/*
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
// Retrieve the handle to a display device context for the client
// area of the window.
hdcScreen = GetDC(NULL);
hdcWindow = GetDC(hWnd);
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcWindow);
if(!hdcMemDC)
{
MessageBox(hWnd, L"CreateCompatibleDC has failed",L"Failed", MB_OK);
goto done;
}
// Get the client area for size calculation
RECT rcClient;
GetClientRect(hWnd, &rcClient);
//This is the best stretch mode
SetStretchBltMode(hdcWindow,HALFTONE);
//The source DC is the entire screen and the destination DC is the current window (HWND)
if(!StretchBlt(hdcWindow,
0,0,
rcClient.right, rcClient.bottom,
hdcScreen,
0,0,
GetSystemMetrics (SM_CXSCREEN),
GetSystemMetrics (SM_CYSCREEN),
SRCCOPY))
{
MessageBox(hWnd, L"StretchBlt has failed",L"Failed", MB_OK);
goto done;
}
// Create a compatible bitmap from the Window DC
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top);
if(!hbmScreen)
{
MessageBox(hWnd, L"CreateCompatibleBitmap Failed",L"Failed", MB_OK);
goto done;
}
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC,hbmScreen);
// Bit block transfer into our compatible memory DC.
if(!BitBlt(hdcMemDC,
0,0,
rcClient.right-rcClient.left, rcClient.bottom-rcClient.top,
hdcWindow,
0,0,
SRCCOPY))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
goto done;
}
// Get the BITMAP from the HBITMAP
GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
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 = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize);
char *lpbitmap = (char *)GlobalLock(hDIB);
// Gets the "bits" from the bitmap and copies them into a buffer
// which is pointed to by lpbitmap.
GetDIBits(hdcWindow, hbmScreen, 0,
(UINT)bmpScreen.bmHeight,
lpbitmap,
(BITMAPINFO *)&bi, DIB_RGB_COLORS);
// A file is created, this is where we will save the screen capture.
HANDLE hFile = CreateFile(L"captureqwsx.bmp",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
// Add the size of the headers to the size of the bitmap to get the total file size
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
//Size of the file
bmfHeader.bfSize = dwSizeofDIB;
//bfType must always be BM for Bitmaps
bmfHeader.bfType = 0x4D42; //BM
DWORD dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
//Unlock and Free the DIB from the heap
GlobalUnlock(hDIB);
GlobalFree(hDIB);
//Close the handle for the file that was created
CloseHandle(hFile);
//Clean up
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL,hdcScreen);
ReleaseDC(hWnd,hdcWindow);
return 0;
*/
//#if 0
HDC hdcScreen;
// HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
HRGN rgn = NULL;
BITMAP bmpScreen;
// Retrieve the handle to a display device context for the client
// area of the window.
hdcScreen = GetDC(NULL);
//hdcWindow = GetDC(hWnd);
//if(g_hdcMemDC == NULL)
{
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcScreen);
if(!hdcMemDC)
{
//MessageBox(hWnd, L"CreateCompatibleDC has failed",L"Failed", MB_OK);
goto done;
}
// Get the client area for size calculation
//RECT rcClient;
//GetClientRect(hWnd, &rcClient);
//This is the best stretch mode
SetStretchBltMode(hdcMemDC,HALFTONE);
// Create a compatible bitmap from the Window DC
hbmScreen = CreateCompatibleBitmap(hdcScreen, iRight - iLeft, iBottom - iTop);
if(!hbmScreen)
{
//MessageBox(hWnd, L"CreateCompatibleBitmap Failed",L"Failed", MB_OK);
goto done;
}
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC,hbmScreen);
}
//The source DC is the entire screen and the destination DC is the current window (HWND)
/* if(!StretchBlt(hdcWindow,
0,0,
rcClient.right, rcClient.bottom,
hdcScreen,
0,0,
GetSystemMetrics (SM_CXSCREEN),
GetSystemMetrics (SM_CYSCREEN),
SRCCOPY))
{
MessageBox(hWnd, L"StretchBlt has failed",L"Failed", MB_OK);
goto done;
}
*/
// Bit block transfer into our compatible memory DC.
if(!StretchBlt(hdcMemDC,
0,0,
iRight - iLeft,
iBottom - iTop,
hdcScreen,
iLeft,iTop,
iRight - iLeft,
iBottom - iTop,
SRCCOPY))
{
// MessageBox(hWnd, L"StretchBlt has failed",L"Failed", MB_OK);
goto done;
}
}
*******************FUNCTION FOR GETTING THE BITMAP RAW DATA POINTER***********
BYTE* Get24BitPixels(HDC dcDesktop, HBITMAP pBitmap, WORD *pwWidth, WORD *pwHeight, WORD * pReminderWidth)
{
// a bitmap object just to get bitmap width and height
BITMAP bmpBmp;
// pointer to original bitmap info
LPBITMAPINFO pbmiInfo;
// bitmap info will hold the new 24bit bitmap info
BITMAPINFO bmiInfo;
// width and height of the bitmap
WORD wBmpWidth ; WORD wBmpHeight;
// ---------------------------------------------------------
// get some info from the bitmap
// ---------------------------------------------------------
GetObject(pBitmap, sizeof(bmpBmp),&bmpBmp);
pbmiInfo = (LPBITMAPINFO)&bmpBmp;
//get width and height
wBmpWidth = (WORD)pbmiInfo->bmiHeader.biWidth;
int iReminderWidth = (wBmpWidth%4);
//wBmpWidth -= (wBmpWidth%4); // width is 4 byte boundary aligned.
wBmpHeight = (WORD)pbmiInfo->bmiHeader.biHeight;
// copy to caller width and height parms
*pwWidth = wBmpWidth;
*pwHeight = wBmpHeight;
wBmpWidth += (4 - iReminderWidth); // width is 4 byte boundary aligned, thereby increasing the width
//so that it will be fully divible by four , it will cause some extra bytes to be filled in with garbage value
//beyond the actual width of the bitmap, we will be discrading this extra padding pixels data while processign each pixel.
*pReminderWidth = 4 - iReminderWidth;
// ---------------------------------------------------------
// allocate width * height * 24bits pixels
BYTE * pPixels = new BYTE[wBmpWidth*wBmpHeight*3];
if (!pPixels) return NULL;
// get user desktop device context to get pixels from
//HDC hDC = GetWindowDC(NULL);
// fill desired structure
bmiInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiInfo.bmiHeader.biWidth = wBmpWidth;
bmiInfo.bmiHeader.biHeight = -wBmpHeight;
bmiInfo.bmiHeader.biPlanes = 1;
bmiInfo.bmiHeader.biBitCount = 24;
bmiInfo.bmiHeader.biCompression = BI_RGB;
bmiInfo.bmiHeader.biSizeImage = wBmpWidth*wBmpHeight*3;
bmiInfo.bmiHeader.biXPelsPerMeter = 0;
bmiInfo.bmiHeader.biYPelsPerMeter = 0;
bmiInfo.bmiHeader.biClrUsed = 0;
bmiInfo.bmiHeader.biClrImportant = 0;
// get pixels from the original bitmap converted to 24bits
int iRes = GetDIBits(dcDesktop,pBitmap,0,wBmpHeight,(LPVOID)pPixels,&bmiInfo,DIB_RGB_COLORS);
// release the device context
//ReleaseDC(NULL,hDC);
// if failed, cancel the operation.
if (!iRes)
{
delete [] pPixels;
pPixels = NULL;
return NULL;
};
// return the pixel array
return pPixels;
}
Fortunately I got the below mention post on StackOverflow.
Many thanks to Vodemki who posted this answer.
Get Pixel color fastest way?

Drawing text on IMFMediaBuffer in Windows 8 Media Foundation Transform

How would you draw text on a IMFMediaBuffer object, and write it out to another IMFMediaBuffer object?
The context is that I'm building an MFT, and initially I tried using Direct2D and Direct3D11 to achieve this, but to no avail.
I was able to accomplish this with Windows GDI calls. The MFT I created has RGB32 input/output types, which allows me to copy to/from a Bitmap object. I copy the part of the frame I want to overlay with text into a Windows bitmap I create, draw the text, then copy back to the IMFMediaBuffer. Here's my code:
#define BREAK_ON_FAIL(val) { if ( FAILED(hr = (val)) ) { break; } }
HRESULT AddOverlay(IMFSample* pSample)
{
HRESULT hr = S_OK;
IMFMediaBuffer * pBuffer;
HDC hDC, hMemDC;
HBITMAP hBitmap, hOldBitmap;
do
{
BYTE * pBufferData;
UINT32 nWidth, nHeight;
BITMAPV5HEADER bi;
BYTE * pBitmapData;
UINT32 nXOffset, nYOffset;
RECT OverlayRect;
OverlayRect.left = 0;
OverlayRect.top = 0;
OverlayRect.right = 400;
OverlayRect.bottom = 32;
DWORD nOverlayWidth = OverlayRect.right - OverlayRect.left;
DWORD nOverlayHeight = OverlayRect.bottom - OverlayRect.top;
LONG lFrameStride;
// Get the frame dimensions and stride
MFGetAttributeSize(m_pInputType, MF_MT_FRAME_SIZE, &nWidth, &nHeight);
m_pInputType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&lFrameStride))
// Setup offset for the overlay area into the video frame
nXOffset = (nWidth - nOverlayWidth) / 2;
nYOffset = nOverlayHeight-1;
// Set up the bitmap header
ZeroMemory(&bi, sizeof(BITMAPV5HEADER));
bi.bV5Size = sizeof(BITMAPV5HEADER);
bi.bV5Width = nOverlayWidth;
// If the stride is negative, the bitmap is bottom-up, which is designated by a negative height
bi.bV5Height = (lFrameStride > 0) ? nOverlayHeight : -(LONG)nOverlayHeight;
bi.bV5Planes = 1;
bi.bV5BitCount = 32;
bi.bV5Compression = BI_RGB;
// The following mask specification specifies a supported 32 BPP
// alpha format for Windows XP.
bi.bV5RedMask = 0x00FF0000;
bi.bV5GreenMask = 0x0000FF00;
bi.bV5BlueMask = 0x000000FF;
bi.bV5AlphaMask = 0xFF000000;
// Create a DIB section with an alpha channel, along with
// a memory device context
hDC = GetDC(NULL);
hBitmap = CreateDIBSection(hDC, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&pBitmapData, NULL, 0);
hMemDC = CreateCompatibleDC(hDC);
ReleaseDC(NULL, hDC);
// Lock the media buffer for our use
BREAK_ON_FAIL( pSample->GetBufferByIndex(0, &pBuffer) );
BREAK_ON_FAIL( pBuffer->Lock(&pBufferData, NULL, NULL) );
// Copy the video frame to the bitmap (to support transparency)
MFCopyImage(pBitmapData, nOverlayWidth*sizeof(RGBQUAD),
pBufferData + nYOffset*abs(lFrameStride) + nXOffset*sizeof(RGBQUAD), lFrameStride,
nOverlayWidth*sizeof(RGBQUAD), nOverlayHeight);
// Draw on the bitmap
hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
//FillRect(hMemDC, &OverlayRect, WHITE_BRUSH);
SetTextColor(hMemDC, RGB(255,0,0));
SetBkMode(hMemDC, TRANSPARENT);
DrawText(hMemDC, _T("Hello World!"), 12, &OverlayRect, DT_CENTER);
SelectObject(hMemDC, hOldBitmap);
// Copy the bitmap to the buffer
MFCopyImage(pBufferData + nYOffset*abs(lFrameStride) + nXOffset*sizeof(RGBQUAD), lFrameStride,
pBitmapData, nOverlayWidth*sizeof(RGBQUAD),
nOverlayWidth*sizeof(RGBQUAD), nOverlayHeight);
BREAK_ON_FAIL( pBuffer->Unlock() );
} while(false);
DeleteDC(hMemDC);
DeleteObject(hBitmap);
SafeRelease(&pBuffer);
return hr;
}
References:
http://support.microsoft.com/kb/318876

Resources