Dealing with cursor masks - winapi

I'm trying to get the data (byte[]) of the current mouse cursor, but one type of cursor (known as MaskedColor) needs special treatment.
http://elektronotdienst-nuernberg.de/bugs/cursor.html
It's the grab and grabbing cursors from this page.
I wonder if there's any way to combine somehow the mask with the color data.
The color data comes with alpha = 0, so there's no transparency (which is available in the mask).
The mask looks like this (32x32, 128 bytes):
And the color info is this (32x32, 4096 bytes, but with alpha=0):
Getting the cursor references:
var cursorInfo = new CursorInfo(false); //Create struct.
if (!User32.GetCursorInfo(out cursorInfo))
return;
if (cursorInfo.Flags != ScreenToGif.Native.Constants.CursorShowing)
{
Gdi32.DeleteObject(cursorInfo.CursorHandle);
return;
}
var iconHandle = User32.CopyIcon(cursorInfo.CursorHandle);
if (iconHandle == IntPtr.Zero)
{
Gdi32.DeleteObject(cursorInfo.CursorHandle);
return;
}
if (!User32.GetIconInfo(iconHandle, out var iconInfo))
{
User32.DestroyIcon(iconHandle);
Gdi32.DeleteObject(cursorInfo.CursorHandle);
return;
}
Getting the cursor color and mask buffers:
try
{
//Color.
var colorHeader = new BitmapInfoHeader(false);
Gdi32.GetDIBits(_windowDeviceContext, iconInfo.Color, 0, 0, null, ref colorHeader, DibColorModes.RgbColors);
//Mask.
var maskHeader = new BitmapInfoHeader(false);
Gdi32.GetDIBits(_windowDeviceContext, iconInfo.Mask, 0, 0, null, ref maskHeader, DibColorModes.RgbColors);
Whenever there's a color buffer, draw to a bitmap to get the cursor animation steps.
if (colorHeader.Height != 0)
{
//Create bitmap.
var compatibleBitmap = Gdi32.CreateCompatibleBitmap(_windowDeviceContext, colorHeader.Width, colorHeader.Height);
var oldBitmap = Gdi32.SelectObject(_compatibleDeviceContext, compatibleBitmap);
//Draw image.
var ok = User32.DrawIconEx(_compatibleDeviceContext, 0, 0, cursorInfo.CursorHandle, 0, 0, _cursorStep, IntPtr.Zero, DrawIconFlags.Image);
if (!ok)
{
_cursorStep = 0;
User32.DrawIconEx(_compatibleDeviceContext, 0, 0, cursorInfo.CursorHandle, 0, 0, _cursorStep, IntPtr.Zero, DrawIconFlags.Image);
}
else
_cursorStep++;
Here comes the tricky part, if there's a mask, I need to apply to that cursor color buffer to know which pixels are transparent or not.
I tried to call DrawIconEx() passing Normal which Mask + Color in the last parameter, but without any difference.
if (maskHeader.SizeImage > 0)
{
//User32.DrawIconEx(_compatibleDeviceContext, 0, 0, cursorInfo.CursorHandle, 0, 0, _cursorStep, IntPtr.Zero, DrawIconFlags.Mask);
var ok2 = Gdi32.MaskBlt(_compatibleDeviceContext, 0, 0, colorHeader.Width, colorHeader.Height, compatibleBitmap, 0, 0, iconInfo.Mask, 0, 0, (int)CopyPixelOperations.SourceErase);
}
The last part is to actually get the final cursor as a byte[].
colorHeader.Height *= -1;
var colorBuffer = new byte[colorHeader.SizeImage];
Gdi32.GetDIBits(_windowDeviceContext, compatibleBitmap, 0, (uint)(colorHeader.Height * -1), colorBuffer, ref colorHeader, DibColorModes.RgbColors);
//Send colorBuffer
//Erase bitmaps.
Gdi32.SelectObject(_compatibleDeviceContext, oldBitmap);
Gdi32.DeleteObject(compatibleBitmap);
return;
}
For the mask monochrome cursor, just get the mask itself as byte[].
var maskBuffer = new byte[maskHeader.SizeImage];
maskHeader.Height *= -1;
Gdi32.GetDIBits(_windowDeviceContext, iconInfo.Mask, 0, (uint)(maskHeader.Height * -1), maskBuffer, ref maskHeader, DibColorModes.RgbColors);
//Send maskBuffer
}
finally
{
Gdi32.DeleteObject(iconInfo.Color);
Gdi32.DeleteObject(iconInfo.Mask);
User32.DestroyIcon(iconHandle);
Gdi32.DeleteObject(cursorInfo.CursorHandle);
}
Edit
Well, I can merge the mask buffer with the color buffer manually, but the issue is that I don't have a straightforward way to detect if the cursor needs the mask or not.
It works for the masked-color cursors:
But it butchers normal cursors (semi-transparent pixels turn opaque):
Both have a mask and color data, but the only difference is that the masked cursor will have its color buffer with all alpha = 0.
var colorHeight = colorHeader.Height * -1;
var colorWidth = colorHeader.Width; //Bug: For some reason, after calling GetDIBits() for the mask, the width of the color struct shifts.
var maskBuffer2 = new byte[maskHeader.SizeImage];
maskHeader.Height *= -1;
Gdi32.GetDIBits(_windowDeviceContext, iconInfo.Mask, 0, (uint)(maskHeader.Height * -1), maskBuffer2, ref maskHeader, DibColorModes.RgbColors);
var targetPitch = colorBuffer.Length / colorHeight;
var cursorPitch = maskBuffer2.Length / maskHeader.Height * -1;
//Merge mask with color.
for (var row = 0; row < colorWidth; row++)
{
//128 in binary.
byte mask = 0x80;
for (var col = 0; col < colorHeight; col++)
{
//Reads current pixel and merge with mask.
//Each mask byte holds information for 8 pixels.
var targetIndex = row * targetPitch + col * 4;
var xor = (maskBuffer2[row * cursorPitch + col / 8] & mask) == mask;
colorBuffer[targetIndex + 3] = (byte) (xor ? 255 : 0);
//Shifts the mask around until it reaches 1, then resets it back to 128.
if (mask == 0x01)
mask = 0x80;
else
mask = (byte)(mask >> 1);
}
}
The non-elegant way to know for sure that I need the mask is to check all alphas in the cursor buffer, if all zero, then I need the mask.
var needsMask = true;
for (var index = 0; index < colorBuffer.Length; index += 4)
{
if (colorBuffer[index] == 0) continue;
//If there's any non-zero alpha value, it means that the mask is not necessary.
needsMask = false;
break;
}
if (!needsMask)
{
//Send colorBuffer.
return;
}
//Send colorBuffer + maskBuffer.
At least it gives me the correct result:

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;
}

Compose an image with floating point layers in webgl

I have trying to render an image in the browser which is built like this:
A bunch of rectangles are each filled with a radial gradient (ideally Gaussian, but can be approximated with a few stopping points
Each rectangle is rotated and translated before being deposited on a drawing area
The image is flattened by summing all the intensities of the rectangles (and cropping to the drawing area's dimensions )
The intensity is rescaled so that the highest intensity is 255 and the lowest 0 (ideally I can apply some sort of gamma correction too)
Finally an image is drawn where the color of each pixel is taken from a palette of 256 colors.
The reason I cannot do this easily with a canvas object is that I need to be working in floating points or I'll lose precision. I do not know in advance what the maximum intensity and minimum intensity will be, so I cannot merely draw transparent rectangles and hope for the best.
Is there a way to do this in webgl? If so, how would I go about it?
You can use the regular canvas to perform this task :
1) check min/max of your rects, so you can build a mapping function double -> [0-255] out of that range.
2) draw the rects in 'lighter' mode == add the component values.
3) you might have a saturation when several rects overlaps : if so, double the mapping range and go to 2).
Now if you don't have saturation just adjust the range to use the full [0-255] range of the canvas, and you're done.
Since this algorithm makes use of getImageData, it might not reach 60 fps on all browsers/devices. But more than 10fps on desktop/Chrome seems perfectly possible.
Hopefully the code below will clarify my description :
//noprotect
// boilerplate
var cv = document.getElementById('cv');
var ctx = cv.getContext('2d');
// rectangle collection
var rectCount = 30;
var rects = buildRandRects(rectCount);
iterateToMax();
// --------------------------------------------
function iterateToMax() {
var limit = 10; // loop protection
// initialize min/max mapping based on rects min/max
updateMapping(rects);
//
while (true) {
// draw the scene using current mapping
drawScene();
// get the max int value from the canvas
var max = getMax();
if (max == 255) {
// saturation ?? double the min-max interval
globalMax = globalMin + 2 * (globalMax - globalMin);
} else {
// no sauration ? Just adjust the min-max interval
globalMax = globalMin + (max / 255) * (globalMax - globalMin);
drawScene();
return;
}
limit--;
if (limit <= 0) return;
}
}
// --------------------------------------------
// --------------------------------------------
// Oriented rectangle Class.
function Rect(x, y, w, h, rotation, min, max) {
this.min = min;
this.max = max;
this.draw = function () {
ctx.save();
ctx.fillStyle = createRadialGradient(min, max);
ctx.translate(x, y);
ctx.rotate(rotation);
ctx.scale(w, h);
ctx.fillRect(-1, -1, 2, 2);
ctx.restore();
};
var that = this;
function createRadialGradient(min, max) {
var gd = ctx.createRadialGradient(0, 0, 0, 0, 0, 1);
var start = map(that.min);
var end = map(that.max);
gd.addColorStop(0, 'rgb(' + start + ',' + start + ',' + start + ')');
gd.addColorStop(1, 'rgb(' + end + ',' + end + ',' + end + ')');
return gd;
}
}
// Mapping : float value -> 0-255 value
var globalMin = 0;
var globalMax = 0;
function map(value) {
return 0 | (255 * (value - globalMin) / (globalMax - globalMin));
}
// create initial mapping
function updateMapping(rects) {
globalMin = rects[0].min;
globalMax = rects[0].max;
for (var i = 1; i < rects.length; i++) {
var thisRect = rects[i];
if (thisRect.min < globalMin) globalMin = thisRect.min;
if (thisRect.max > globalMax) globalMax = thisRect.max;
}
}
// Random rect collection
function buildRandRects(rectCount) {
var rects = [];
for (var i = 0; i < rectCount; i++) {
var thisMin = Math.random() * 1000;
var newRect = new Rect(Math.random() * 400, Math.random() * 400, 10 + Math.random() * 50, 10 + Math.random() * 50, Math.random() * 2 * Math.PI, thisMin, thisMin + Math.random() * 1000);
rects.push(newRect);
}
return rects;
}
// draw all rects in 'lighter' mode (=sum values)
function drawScene() {
ctx.save();
ctx.globalCompositeOperation = 'source-over';
ctx.clearRect(0, 0, cv.width, cv.height);
ctx.globalCompositeOperation = 'lighter';
for (var i = 0; i < rectCount; i++) {
var thisRect = rects[i];
thisRect.draw();
}
ctx.restore();
}
// get maximum value for r for this canvas
// ( == max r, g, b value for a gray-only drawing. )
function getMax() {
var data = ctx.getImageData(0, 0, cv.width, cv.height).data;
var max = 0;
for (var i = 0; i < data.length; i += 4) {
if (data[i] > max) max = data[i];
if (max == 255) return 255;
}
return max;
}
<canvas id='cv' width = 400 height = 400></canvas>

DX11 convert pixel format BGRA to RGBA

I have currently the problem that a library creates a DX11 texture with BGRA pixel format.
But the displaying library can only display RGBA correctly. (This means the colors are swapped in the rendered image)
After looking around I found a simple for-loop to solve the problem, but the performance is not very good and scales bad with higher resolutions. I'm new to DirectX and maybe I just missed a simple function to do the converting.
// Get the image data
unsigned char* pDest = view->image->getPixels();
// Prepare source texture
ID3D11Texture2D* pTexture = static_cast<ID3D11Texture2D*>( tex );
// Get context
ID3D11DeviceContext* pContext = NULL;
dxDevice11->GetImmediateContext(&pContext);
// Copy data, fast operation
pContext->CopySubresourceRegion(texStaging, 0, 0, 0, 0, tex, 0, nullptr);
// Create mapping
D3D11_MAPPED_SUBRESOURCE mapped;
HRESULT hr = pContext->Map( texStaging, 0, D3D11_MAP_READ, 0, &mapped );
if ( FAILED( hr ) )
{
return;
}
// Calculate size
const size_t size = _width * _height * 4;
// Access pixel data
unsigned char* pSrc = static_cast<unsigned char*>( mapped.pData );
// Offsets
int offsetSrc = 0;
int offsetDst = 0;
int rowOffset = mapped.RowPitch % _width;
// Loop through it, BRGA to RGBA conversation
for (int row = 0; row < _height; ++row)
{
for (int col = 0; col < _width; ++col)
{
pDest[offsetDst] = pSrc[offsetSrc+2];
pDest[offsetDst+1] = pSrc[offsetSrc+1];
pDest[offsetDst+2] = pSrc[offsetSrc];
pDest[offsetDst+3] = pSrc[offsetSrc+3];
offsetSrc += 4;
offsetDst += 4;
}
// Adjuste offset
offsetSrc += rowOffset;
}
// Unmap texture
pContext->Unmap( texStaging, 0 );
Solution:
Texture2D txDiffuse : register(t0);
SamplerState texSampler : register(s0);
struct VSScreenQuadOutput
{
float4 Position : SV_POSITION;
float2 TexCoords0 : TEXCOORD0;
};
float4 PSMain(VSScreenQuadOutput input) : SV_Target
{
return txDiffuse.Sample(texSampler, input.TexCoords0).rgba;
}
Obviously iterating over a texture on you CPU is not the most effective way. If you know that colors in a texture are always swapped like that and you don't want to modify the texture itself in your C++ code, the most straightforward way would be to do it in the pixel shader. When you sample the texture, simply swap colors there. You won't even notice any performance drop.

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?

UpdateLayeredWindow and DrawText

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);
}
}
}

Resources