3

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 );
ColmanJ
  • 457
  • 2
  • 12
  • 28
  • I think you have to use GDI+ for alpha channels. The older GDI stuff won't work for 32bit images. See: http://stackoverflow.com/questions/3942781/how-to-genrate-a-monochrome-bit-mask-for-a-32bit-bitmap – Mordachai Nov 20 '11 at 02:44
  • 1
    @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 Nov 20 '11 at 04:22
  • @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 Nov 20 '11 at 04:34
  • @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 completly – ColmanJ Nov 20 '11 at 10:28
  • @joeco - mystery solved - I think; check possible answer below! – BrendanMcK Nov 20 '11 at 10:59
  • @BrendanMcK On my last comment, the `SourceConstantAlpha` only works when I don't use the `AC_SRC_ALPHA`, probably something important I forgot to say – ColmanJ Nov 20 '11 at 11:30

1 Answers1

3

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.

BrendanMcK
  • 14,252
  • 45
  • 54
  • I have to admit i didn't know that thing about the floats, but it doesn't solve my problem :( When i put the value to an exact in of 255 it still doesn't paint anything. I also checked again on msdn, and there it says `Set the SourceConstantAlpha value to 255 (opaque) when you only want to use per-pixel alpha values.`, so if I'm correctly, it says it has to be 255 when you ONLY want to use per-pixel alpha, which means that you should be able to use both, right ? – ColmanJ Nov 20 '11 at 11:23
  • It's likely part of the issue. Chances are you may have a couple of bugs here. Time to step through the code line by line if you need to and confirm which bits you are absolutely sure work. Are you getting success return values from all the APIs here? Does the code work if you use plain BitBlt instead of AlphaBlend? Using BitBlt, and modifying say the blue channel, are you seeing the expected results? – BrendanMcK Nov 20 '11 at 11:30
  • well pretty sure its in my code where i try to add/change the alpha channel. The BitBlt works like it should, or it just shows my image, and when i try to change the values of the channels like blue or just all, nothing changes visual. And i already checked the return values and none of the methods fail – ColmanJ Nov 20 '11 at 11:55
  • Hrm, you have GetHeight() in SetDIBits, but m_ResY in GetDIBits - are they the same value? Possible that you're setting fewer bits than you think you are? The fact that changing any of the channels doesn't work suggests that either get, modify, or set isn't working. You could just stomp on all the pixels with a known RGB value to test the set part in isolation; or examine some of the array by hand to check the Get part. – BrendanMcK Nov 20 '11 at 12:06
  • m_ResY and GetHeight() are the exact same, I also checked the return value of the SetDIBits and GetDiBits and it says that i get as much scanlines as i change. But when i look at the pointer its all zero oO – ColmanJ Nov 20 '11 at 12:45