Using StretchDIBits to copy PNG (with alpha) - c++

Could someone help me?
How I can copy png to virtual HDC.
Transparent pixels are always black.
I tried but alpha is always black.
Seems like BITMAPINFOHEADER does not support alpha.
Thank you very much.
and sorry for my English. Peace.
(Note I need to use GDI only)
// fill background
HBRUSH hbrush = CreateSolidBrush(RGB(color.red(), color.green(), color.blue()));
::FillRect(m_virtualHDC, &wRect, hbrush);
DeleteObject(hbrush);
// load image
QImage pix;
pix.load("D:\\1.png");
// draw image with alpha
BITMAPINFO bmInfo;
ZeroMemory(&bmInfo, sizeof(BITMAPINFO));
BITMAPINFOHEADER& BMPInfoHeader = bmInfo.bmiHeader;
BMPInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
BMPInfoHeader.biWidth = pix.width();
BMPInfoHeader.biHeight = pix.height();
BMPInfoHeader.biPlanes = 1;
BMPInfoHeader.biBitCount = 32;
BMPInfoHeader.biCompression = BI_RGB;
BMPInfoHeader.biSizeImage = pix.byteCount();
BMPInfoHeader.biXPelsPerMeter = 0;
BMPInfoHeader.biYPelsPerMeter = 0;
BMPInfoHeader.biClrUsed = 0;
SetStretchBltMode(m_virtualHDC, HALFTONE);
StretchDIBits(m_virtualHDC,
targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(),
sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(),
pix.bits(), &bmInfo, DIB_RGB_COLORS, SRCCOPY);

GDI in general does not support an alpha channel.
You need to use an API that has dedicated alpha support, e. g. AlphaBlend() or GDI+.
Most of these APIs have very poor resampling quality. I suggest to use a third-party library like stb_image_resize to resample in memory and use APIs like AlphaBlend() only to display the final result to the screen, without doing further scaling.

Related

StretchBlt only works when nHeightDest is negative

I'm trying to use StretchBlt in order to copy pixels from a memory hdc to the window hdc.
The memory hdc gets the image from an invisible window which renders a stream using openGL.
Here's my code:
BITMAPINFOHEADER createBitmapHeader(int width, int height) {
BITMAPINFOHEADER header;
header.biSize = sizeof(BITMAPINFOHEADER);
header.biWidth = width;
header.biHeight = height;
header.biPlanes = 1;
header.biBitCount = 32;
header.biCompression = BI_RGB;
header.biSizeImage = 0;
header.biXPelsPerMeter = 0;
header.biYPelsPerMeter = 0;
header.biClrUsed = 0;
header.biClrImportant = 0;
return header;
}
...
HDC memoryHdc = CreateCompatibleDC(windowHdc);
BITMAPINFO bitmapInfo;
bitmapInfo.bmiHeader = createBitmapHeader(targetDimensions.width, targetDimensions.height);
HBITMAP bitmap = CreateDIBitmap(windowHdc, &bitmapInfo.bmiHeader, CBM_INIT, offscreenBuffer, &bitmapInfo, DIB_RGB_COLORS);
SelectObject(memoryHdc, bitmap);
DeleteObject(bitmap);
SetStretchBltMode(windowHdc, COLORONCOLOR);
StretchBlt(windowHdc,
targetDimensions.x, targetDimensions.y,
targetDimensions.width, -targetDimensions.height,
memoryHdc,
sourceDimensions.x, sourceDimensions.y,
sourceDimensions.width, sourceDimensions.height,
SRCCOPY);
DeleteDC(memoryHdc);
Where windowHdc is the hdc of the window to which I want the StretchBlt to copy the pixels to, and offscreenBuffer is a void* to the pixels copied from the offscreen window in which the openGL is rendering.
This code works great, except that the image is upside down and I want it vertically flipped.
I know that this happens because:
If nHeightSrc and nHeightDest have different signs, the function
creates a mirror image of the bitmap along the y-axis
But when I remove the minus sign and both target and source heights are the same then I see no image in the window.
Just to check, I tried to put the minus on the sourceDimensions.height but that also results in no image, and the same if I try to negate the widths (both target and source).
Any idea why?
Thanks.

Completely transparent image

Does anyone know how I can create a Completely transparent image or given am image (HBITMAP) how can I wipe it Completely so that all pixels in it are 100% transparent?
Thank you.
The bitmap needs to be 32-bit so it has an alpha channel that you can set opacity values with. If your image is not 32-bit, you will have to create a new 32-bit bitmap and copy the original pixels into it.
I also found the solution without the use of GDI+.
BITMAPV5HEADER bi = {sizeof(BITMAPV5HEADER), 320, 240, 1, 32, BI_BITFIELDS, 0, 0, 0, 0, 0, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000};
HDC hDc = ::CreateCompatibleDC(0);
if(NULL != hDc)
{
RGBQUAD* pArgb;
HBITMAP tBmp = ::CreateDIBSection(hDc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&pArgb, NULL, 0);
if(NULL != tBmp)
{
int x, y;
DWORD pBits;
pBits = (DWORD*)pArgb;
for(y = 0; y < bi.bV5Height; y++)
{
for(x = 0; x < bi.bV5Width; x++)
{
*pBits = 0;
pBits++;
}
}
}
::DeleteObject(hDc);
}
tBmp will be a 320 x 240 bitmap that is completely transparent
You can use a monochrome bitmap as a mask to create transparent images from colour ones. This is quite complex. See this example (in VB but uses Win32 APIs directly)
Alternatively, the TransparentBlt function might make what you're trying to do unnecessary.
I found the answer in GDI+. The best way to do this is to attach the device context of the image that you are working with with GDI+ graphic object and then call its clear method.

Animated Image in Win32

How can i put an animated GIF to my dialog in my native Win32 application?
I have a loading indicator, and a loading process.
Thanks :-)
Not sure if GDI+ could be considered as native win32. In case you can use it check the following example: CodeProject
You could use the Animation Control. You would have to convert your .gif to an .avi though.
Its very easy to use GdiPlus to load a variety of image formats including jpeg, gif (animated), png and so on.
This code demonstrates how to quickly load a single frame of an image into an HBITMAP :-
#include <gdiplus.h>
#pragma comment(lib,"gdiplus.lib")
using namespace Gdiplus;
HBITMAP LoadImageWithGdiPlus(LPCTSTR pszPngPath)
{
Image image(pszPngPath);
int width = image.GetWidth();
int height = image.GetHeight();
BITMAPINFO bmi;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biClrImportant = 0;
bmi.bmiHeader.biClrUsed = 0;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biHeight = height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biSize = sizeof (bmi.bmiHeader);
bmi.bmiHeader.biSizeImage = 0; //calc later
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biXPelsPerMeter = 0;
bmi.bmiHeader.biYPelsPerMeter = 0;
BYTE* pBmp = NULL;
HBITMAP hbm = CreateDIBSection(NULL,&bmi,DIB_RGB_COLORS,(void**)&pBmp,NULL,0);
HDC hdc = CreateCompatibleDC(NULL);
HGDIOBJ hobj = SelectObject(hdc,hbm);
Graphics graphics(hdc);
graphics.DrawImage(&image,0,0);
SelectObject(hdc,hobj);
DeleteDC(hdc);
return hbm;
}
Since you have a tight timeframe on this one I searched for a working example to animate gifs on win32 and I found a nice implementation on cplusplus.com.
It's called GIF View [direct link] by Juan Soulie.
It's fairly simple to implement a timer to change what's displayed. You might set up a text block, with no text in it, with a background color and just change the size. It will look like an expanding colored bar with very little overhead.

Display 32bit bitmap - Palette

I have an image data in a buffer(type - long) from a scanner which is 32 bit.
For example, buffer[0]'s corresponding pixel value is 952 which is [184, 3, 0, 0] <-[R,G,B,A];
I want to display/Paint/draw on to the screen; I am confused when i tried to read about displying bitmaps. I looked at win32 functions, CBitmap class, windows forms (picture box) etc I am hard to understand the general idea/appraoch for displaying this buffer data on to a application window.
I have constructed the BITMAPFILEHEADER AND BITMAPINFOHEADER; Has the pixel data in a buffer, (unsigned char *)vInBuff whose size is vImageSz;
//construct the BMP file Header
vBmfh.bfType = 19778;
vBmfh.bfSize = 54+vImageSz;//size of the whole image
vBmfh.bfReserved2 = 0;
vBmfh.bfReserved1 = 0;
vBmfh.bfOffBits = 54;//offset from where the pixel data can be found
//Construct the BMP info header
vBmih.biSize = 40;//size of header from this point
vBmih.biWidth = 1004;
vBmih.biHeight = 1002;
vBmih.biPlanes = 1;
vBmih.biCompression = BI_RGB;
vBmih.biSizeImage = vBmih.biWidth*vBmih.biHeight*4;
vBmih.biBitCount = 32;
vBmih.biClrUsed = 0;
vBmih.biClrUsed = 0;
1.What is that i should be doing next to display this?
2 What should i be using to display the 32bit bitmap? I see people using createwindows functions, windows forms, MFC etc;
3.I also understand that BitBlt,createDIBSection, OnPaint etc are being used? I am confused by these various functions and coding platforms? Please suggest me a simple approach.
4.How can i create a palette to display a 32 bit image?
Thanks
Raj
EDITED TRYING TO IMPLEMENT DAVE'S APPROACH, CAN SOMEBODY COMMENT ON MY IMPLEMTATION. I couldn't continue to the bitblt as i donot have two HDC, i donot know how to get this one? Any help please
DisplayDataToImageOnScreen(unsigned char* vInBuff, int vImageSz) // buffer with pixel data, Size of pixel data
{
unsigned char* vImageBuff = NULL;
HDC hdcMem=CreateCompatibleDC(NULL);
HBITMAP hBitmap = CreateDIBSection(hdcMem,
(BITMAPINFO*)&vBmih,
DIB_RGB_COLORS,
(void **)&vImageBuff,
NULL, 0);
GetDIBits(hdcMem,
hBitmap,
0,
1,
(void**)&vImageBuff,
(BITMAPINFO*)&vBmih,
DIB_RGB_COLORS);
memcpy(vImageBuff,vInBuff,vImageSz);
}
An alternative if you just want to plot it on screen is to use TinyPTC ( http://sourceforge.net/projects/tinyptc/files/ ). It's just 3 functions and very simple if you just want to plot some pixels.
EDIT: Seems like http://www.pixeltoaster.com is a continuation of TinyPTC, probably preffered.
If you have image's bytes already in a buffer you can use:
a CBitmap object (MFC) and the method CBitmap::CreateBitmapIndirect
or
win32 routine CreateBitmapIndirect.
Now you can use BitBlt to draw it on a DC. To get a window DC use GetDC.
There is no need to create a pallete for 32 bit images.
Here's a simplified approach you can try, broken down into steps:
BITMAPINFO bitmapinfo = { 0 };
bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapinfo.bmiHeader.biWidth = 1004;
bitmapinfo.bmiHeader.biHeight = -1002;
bitmapinfo.bmiHeader.biPlanes = 1;
bitmapinfo.bmiHeader.biCompression = BI_RGB;
HBITMAP hBitmap = CreateDIBSection(NULL,
&bitmapinfo,
DIB_RGB_COLORS,
(void **)&vImageBuff,
NULL,
0);
Now party on vImageBuff and then cache hBitmap somewhere so that within your wndproc you can then in the WM_PAINT handler:
select hBitmap into temp compatible HDC
call BitBlt(..., SRCCOPY) from the compatible HDC to the window's HDC. the other parameters should be obvious. don't try to stretch or do anything fancy at first.
remember to select the original dummy bitmap into the temp HDC before destroying it.
If you aren't seeing results try looping through vImageBuff and just set every pixel to RGB(255, 0, 0), or something like that, just to sanity check the rest of the logic.
If nothing is drawing make sure that the alpha component for each pixel is 255.
If you're getting a garbled image then you need to double-check pixelformat, stride, etc.
Here's a strategy you might like:
Create a bitmap with the same size as your scanned data, and the same format (use CreateDIBSection).
Use GetDIBits to get the base address of the pixel data.
Copy your data (from the scanner) to the address GetDIBits returns.
Now render your bitmap! (use BitBlt, or somesuch).
Regarding palettes- a 32bit image does not, generally, have an explicit palette - you'd need 16.7million (assuming 8bits of alpha) values in the palette. Generally the palette is assumed to be 8bits red, 8bits green, 8bits blue, as you've described above.

How to draw 32-bit alpha channel bitmaps?

I need to create a custom control to display bmp images with alpha channel. The background can be painted in different colors and the images have shadows so I need to truly "paint" the alpha channel.
Does anybody know how to do it?
I also want if possible to create a mask using the alpha channel information to know whether the mouse has been click on the image or on the transparent area.
Any kind of help will be appreciated!
Thanks.
Edited(JDePedro): As some of you have suggested I've been trying to use alpha blend to paint the bitmap with alpha channel. This just a test I've implemented where I load a 32-bit bitmap from resources and I try to paint it using AlphaBlend function:
void CAlphaDlg::OnPaint()
{
CClientDC dc(this);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP);
BITMAP BitMap;
bitmap.GetBitmap(&BitMap);
int nWidth = BitMap.bmWidth;
int nHeight = BitMap.bmHeight;
CBitmap *pOldBitmap = dcMem.SelectObject(&bitmap);
BLENDFUNCTION m_bf;
m_bf.BlendOp = AC_SRC_OVER;
m_bf.BlendFlags = 0;
m_bf.SourceConstantAlpha = 255;
m_bf.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(dc.GetSafeHdc(), 100, 100, nWidth, nHeight, dcMem.GetSafeHdc(), 0, 0,nWidth, nHeight,m_bf);
dcMem.SelectObject(pOldBitmap);
CDialog::OnPaint();
}
This is just a test so I put the code in the OnPaint of the dialog (I also tried the AlphaBlend function of the CDC object).
The non-transparent areas are being painted correctly but I get white where the bitmap should be transparent.
Any help???
This is a screenshot..it's not easy to see but there is a white rectangle around the blue circle:
alt text http://img385.imageshack.us/img385/7965/alphamh8.png
Ok. I got it! I have to pre-multiply every pixel for the alpha value. Someone can suggest the optimized way to do that?
For future google users, here is a working pre-multiply function. Note that this was taken from http://www.viksoe.dk/code/alphatut1.htm .
inline void PremultiplyBitmapAlpha(HDC hDC, HBITMAP hBmp)
{
BITMAP bm = { 0 };
GetObject(hBmp, sizeof(bm), &bm);
BITMAPINFO* bmi = (BITMAPINFO*) _alloca(sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
::ZeroMemory(bmi, sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BOOL bRes = ::GetDIBits(hDC, hBmp, 0, bm.bmHeight, NULL, bmi, DIB_RGB_COLORS);
if( !bRes || bmi->bmiHeader.biBitCount != 32 ) return;
LPBYTE pBitData = (LPBYTE) ::LocalAlloc(LPTR, bm.bmWidth * bm.bmHeight * sizeof(DWORD));
if( pBitData == NULL ) return;
LPBYTE pData = pBitData;
::GetDIBits(hDC, hBmp, 0, bm.bmHeight, pData, bmi, DIB_RGB_COLORS);
for( int y = 0; y < bm.bmHeight; y++ ) {
for( int x = 0; x < bm.bmWidth; x++ ) {
pData[0] = (BYTE)((DWORD)pData[0] * pData[3] / 255);
pData[1] = (BYTE)((DWORD)pData[1] * pData[3] / 255);
pData[2] = (BYTE)((DWORD)pData[2] * pData[3] / 255);
pData += 4;
}
}
::SetDIBits(hDC, hBmp, 0, bm.bmHeight, pBitData, bmi, DIB_RGB_COLORS);
::LocalFree(pBitData);
}
So then your OnPaint becomes:
void MyButton::OnPaint()
{
CPaintDC dc(this);
CRect rect(0, 0, 16, 16);
static bool pmdone = false;
if (!pmdone) {
PremultiplyBitmapAlpha(dc, m_Image);
pmdone = true;
}
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 255;
bf.AlphaFormat = AC_SRC_ALPHA;
HDC src_dc = m_Image.GetDC();
::AlphaBlend(dc, rect.left, rect.top, 16, 16, src_dc, 0, 0, 16, 16, bf);
m_Image.ReleaseDC();
}
And the loading of the image (in the constructor of your control):
if ((HBITMAP)m_Image == NULL) {
m_Image.LoadFromResource(::AfxGetResourceHandle(), IDB_RESOURCE_OF_32_BPP_BITMAP);
}
The way I usually do this is via a DIBSection - a device independent bitmap that you can modify the pixels of directly. Unfortunately there isn't any MFC support for DIBSections: you have to use the Win32 function CreateDIBSection() to use it.
Start by loading the bitmap as 32-bit RGBA (that is, four bytes per pixel: one red, one green, one blue and one for the alpha channel). In the control, create a suitably sized DIBSection. Then, in the paint routine
Copy the bitmap data into the DIBSection's bitmap data, using the alpha channel byte to blend the bitmap image with the background colour.
Create a device context and select the DIBSection into it.
Use BitBlt() to copy from the new device context to the paint device context.
You can create a mask given the raw bitmap data simply by looking at the alpha channel values - I'm not sure what you're asking here.
You need to do an alpha blend with your background color, then take out the alpha channel to paint it to the control.
The alpha channel should just be every 4th byte of your image. You can use that directly for your mask, or you can just copy every 4th byte to a new mask image.
Painting it is very easy with the AlphaBlend function.
As for you mask, you'll need to get the bits of the bitmap and examine the alpha channel byte for each pixel you're interested in.
An optimised way to pre-multiply the RGB channels with the alpha channel is to set up a [256][256] array containing the calculated multiplication results. The first dimension is the alpha value, the second is the R/G/B value, the values in the array are the pre-multiplied values you need.
With this array set up correctly, you can calculate the value you need like this:
R = multiplicationLookup[alpha][R];
G = multiplicationLookup[alpha][G];
B = multiplicationLookup[alpha][B];
You are on the right track, but need to fix two things.
First use ::LoadImage( .. LR_CREATEDIBSECTION ..) instead of CBitmap::LoadBitmap. Two, you have to "pre-multiply" RGB values of every pixel in a bitmap to their respective A value. This is a requirement of AlphaBlend function, see AlphaFormat description on this MSDN page. T
The lpng has a working code that does the premultiplication of the DIB data.