3
votes

I'm trying to add transparency to a hbitmap object but it never draw anything :/

this is the code i use to draw the handle

HDC hdcMem = CreateCompatibleDC(hDC);
    HBITMAP hbmOld = (HBITMAP) SelectObject(hdcMem, m_hBitmap);

    BLENDFUNCTION blender = {AC_SRC_OVER, 0, (int) (2.55 * 100), AC_SRC_ALPHA}; // blend function combines opacity and pixel based transparency
    AlphaBlend(hDC, x, y, rect.right - rect.left, rect.bottom - rect.top, hdcMem, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, blender);

    SelectObject(hdcMem, hbmOld);
    DeleteDC(hdcMem);

and this is the code which should add a alpha channel to the hbitmap

BITMAPINFOHEADER bminfoheader;
    ::ZeroMemory(&bminfoheader, sizeof(BITMAPINFOHEADER));
    bminfoheader.biSize        = sizeof(BITMAPINFOHEADER);
    bminfoheader.biWidth       = m_ResX;
    bminfoheader.biHeight      = m_ResY;
    bminfoheader.biPlanes      = 1;
    bminfoheader.biBitCount    = 32;
    bminfoheader.biCompression = BI_RGB;

    HDC windowDC = CreateCompatibleDC(0);
    unsigned char* pPixels = new unsigned char[m_ResX * m_ResY * 4];

    GetDIBits(windowDC, m_hBitmap, 0, m_ResY, pPixels, (BITMAPINFO*) &bminfoheader, DIB_RGB_COLORS); // load pixel info

    // add alpha channel values of 255 for every pixel if bmp
    for (int count = 0; count < m_ResX * m_ResY; count++)
    {
        pPixels[count * 4 + 3] = 255; <----  here i've tried to change the value to test different transparency, but it doesn't change anything
    }
    SetDIBits(windowDC, m_hBitmap, 0, GetHeight(), pPixels, (BITMAPINFO*) &bminfoheader, DIB_RGB_COLORS); // save the pixel info for later manipulation

    DeleteDC(windowDC);

edit:

this is the code how I create the bitmap I fill the pixeldata in later in some code

m_hBuffer = CreateBitmap( m_ResX, m_ResY, 1, 32, nullptr );
1
I think you have to use GDI+ for alpha channels. The older GDI stuff won't work for 32bit images. See: stackoverflow.com/questions/3942781/…Mordachai
@Mordachai - that's true of most of the GDI functions - like BitBlt - but AlphaBlend (added in Win2K) is documented as specifically supporting per-pixel alpha in the source image.BrendanMcK
@joeco, Have you tried much debugging to try to narrow down where the problem might be? Eg. try using BitBlt first, just to make sure the rest of the code and logic is good. Also try changing the alpha changing code to perhaps set all the G and B channels to the same as the R channel - just to ensure that your bitmap manipulation code is fine. By the way, note that AlphaBlend expects the source bitmap R/G/B to be "premultiplied", so if you're adding an alpha channel, you may need to adjust R/G/B appropriately at that stage too.BrendanMcK
@BrendanMcK, I've tried editing other infomation of the pixels and it does indeed do nothing, anyone an idea why ? :/ note: if i change the SourceConstantAlpha then my image does get transparent, but completlyColmanJ
@joeco - mystery solved - I think; check possible answer below!BrendanMcK

1 Answers

3
votes

This is a fun one!

Guess what this prints out?

#include <stdio.h>

int main()
{
    printf("%d\n", (int) (2.55 * 100));
    return 0;
}

Answer: 254 - not 255. Two things happening here:

  • Floats are often inexact - that 2.55 doesn't get represented by a binary value that represents 2.55 exactly - it's probably something like 2.5499963... (I just made that up, but you get the idea - essentially the number is represented as a sum of fractions of 2 - since binary is base 2 - so something like .5 or .25 can likely be represented exactly, but most other numbers will be represented as an approximation. You typically don't notice this because float print routines will convert back to base 10 for display, which essentially introduces another inexactness that ends up cancelling out the first one: so what you see as the assigned value or the printed out value are not exactly the value of the representation as stored in memory).

  • Casting to int truncates - ie rounds down.

Put these together, and your (2.55 * 100) is getting you 254, not 255 - and you have to have the magic value 255 for per-pixel alpha to work.

So lesson here is stick with pure integers. Or, if you ever do need to convert from float to integers, be aware of what's going on, and work around it (eg. add .5, then truncate - or use a similar technique.)

By the way, this is one of those cases where stepping through code line by line and checking all inputs and outputs at each step (you can never be too paranoid when debugging!) might have shown the issue; right before you step into AlphaBlend, you should see a 254 when you hover over that param (assuming using DevStudio or similar editor) and realize that something's up.