Page 1 of 1

Can wxDC::DrawBitmap(...true) even draw bitmaps with alpha channels?

Posted: Wed Sep 04, 2019 7:34 pm
by Gabriele Giuseppini
Dear all,

I'm building a wxBitmap based off my RGBA data, carefully writing the alpha channel as follows:

Code: Select all

    wxBitmap bitmap;
    bitmap.Create(imageData.Size.Width, imageData.Size.Height, 32);

    wxPixelData<wxBitmap, wxAlphaPixelFormat> pixelData(bitmap);
    ...loop here...
    writeIt.Alpha() = readIt->a;
    ...
When I use this bitmap in a wxGenericStaticBitmap, transparency is honored. When I try to draw it with a wxDC, instead, there's no transparency, even when the _useMask_ flag is set to true:

Code: Select all

                dc.DrawBitmap(
                    infoTile.Bitmap,
                    infoTile.RectVirtual.GetLeft() + infoTileContentLeftMargin
                        + PreviewImageWidth / 2 - infoTile.Bitmap.GetWidth() / 2
                        - originVirtual.x,
                    infoTile.RectVirtual.GetTop() + InfoTileInset
                        + PreviewImageHeight - infoTile.Bitmap.GetHeight()
                        - originVirtual.y,
                    true);
This is on Windows 7, in case it matters.

Is wxDC::DrawBitmap's _useMask_ flag supposed to work?

Thank you in advance for any pointers!

Re: Can wxDC::DrawBitmap(...true) even draw bitmaps with alpha channels?

Posted: Wed Sep 04, 2019 7:39 pm
by doublemax
Yes, that should work. Did you destroy pixelData before drawing the bitmap?

Re: Can wxDC::DrawBitmap(...true) even draw bitmaps with alpha channels?

Posted: Wed Sep 04, 2019 7:54 pm
by Gabriele Giuseppini
Yes, PixelData went out of scope as the wxBitmap was created in another function.

Does it matter that the DC is created in the following way?

Code: Select all

void ShipPreviewPanel2::OnPaint(wxPaintEvent & event)
{
    if (!mBufferedDCBitmap || mBufferedDCBitmap->GetSize() != this->GetSize())
    {
        mBufferedDCBitmap = std::make_unique<wxBitmap>(this->GetSize());
    }

    wxBufferedPaintDC bufDc(this, *mBufferedDCBitmap);

    Render(bufDc);
}

Re: Can wxDC::DrawBitmap(...true) even draw bitmaps with alpha channels?

Posted: Wed Sep 04, 2019 8:17 pm
by doublemax
In order to narrow down where the problem is:
1) Try with a loaded bitmap, e.g. the toucan.png that's used in some of the wx samples
2) Try with a custom (non buffered) wxPaintDC

Check if one of them makes any difference.

BTW: What OS and wxWidgets version are you using?

Re: Can wxDC::DrawBitmap(...true) even draw bitmaps with alpha channels?

Posted: Wed Sep 04, 2019 9:09 pm
by Gabriele Giuseppini
Using wxPaintDC from the paint event didn't help, but interestingly enough wxBitmap::LoadFile(...the toucan...) worked fine, I can see transparency.

Yet my bitmap is definitely transparent when displayed with wxGenericStaticBitmap. Here's how I create the bitmap:

Code: Select all

wxBitmap WxHelpers::MakeBitmap(RgbaImageData const & imageData)
{
    if (imageData.Size.Width == 0 || imageData.Size.Height == 0)
    {
        throw std::runtime_error("Cannot create bitmap with one zero dimension");
    }

    wxBitmap bitmap;
    bitmap.Create(imageData.Size.Width, imageData.Size.Height, 32);

    wxPixelData<wxBitmap, wxAlphaPixelFormat> pixelData(bitmap);
    if (!pixelData)
    {
        throw std::runtime_error("Cannot get bitmap pixel data");
    }

    assert(pixelData.GetWidth() == imageData.Size.Width);
    assert(pixelData.GetHeight() == imageData.Size.Height);

    rgbaColor const * readIt = imageData.Data.get();
    auto writeIt = pixelData.GetPixels();
    writeIt.OffsetY(pixelData, imageData.Size.Height - 1);
    for (int y = 0; y < imageData.Size.Height; ++y)
    {
        // Save current iterator
        auto rowStart = writeIt;

        for (int x = 0; x < imageData.Size.Width; ++x, ++readIt, ++writeIt)
        {
            writeIt.Red() = readIt->r;
            writeIt.Green() = readIt->g;
            writeIt.Blue() = readIt->b;
            writeIt.Alpha() = readIt->a;
        }

        // Move write iterator to next row
        writeIt = rowStart;
        writeIt.OffsetY(pixelData, -1);
    }

    return bitmap;
}
What else should I do here to make it work with wxDC::DrawBitmap?

This is Windows 7 64-bit, wxWidgets built from tag 3.1.2.

Thanks for you help by the way.

Re: Can wxDC::DrawBitmap(...true) even draw bitmaps with alpha channels?

Posted: Wed Sep 04, 2019 10:15 pm
by doublemax

Code: Select all

writeIt.Red() = readIt->r;
The RGB values must be pre-multiplied with alpha.

Code: Select all

writeIt.Red() = readIt->r * readIt->a / 256;

Re: Can wxDC::DrawBitmap(...true) even draw bitmaps with alpha channels?

Posted: Wed Sep 04, 2019 10:56 pm
by New Pagodi
doublemax wrote:
Wed Sep 04, 2019 10:15 pm
[/code]The RGB values must be pre-multiplied with alpha.

Code: Select all

writeIt.Red() = readIt->r * readIt->a / 256;
I don't know if this matters, but the way building a wxBitmap from RGBA data is done in the wxSTC source is

Code: Select all

            p.Red()   = wxPy_premultiply(red,   alpha);
            p.Green() = wxPy_premultiply(green, alpha);
            p.Blue()  = wxPy_premultiply(blue,  alpha);
            p.Alpha() = alpha;
where wxPy_premultiply is a macro defined as

Code: Select all

#if defined(__WXMSW__) || defined(__WXMAC__)
#define wxPy_premultiply(p, a)   ((p) * (a) / 0xff)
#else
#define wxPy_premultiply(p, a)   (p)
#endif
I would think that is slightly more cross platform, but I'm not sure.

Re: Can wxDC::DrawBitmap(...true) even draw bitmaps with alpha channels?

Posted: Thu Sep 05, 2019 5:03 am
by doublemax
It would be interesting to know if pre-multiplication only should happen under Win and OSX. The "image" sample, which is the only one that uses raw bitmap access, does it unconditionally.

Re: Can wxDC::DrawBitmap(...true) even draw bitmaps with alpha channels?

Posted: Thu Sep 05, 2019 8:13 pm
by Gabriele Giuseppini
Ugh...after multiplying r, g, and b with alpha, it works!

So, what's the rationale for the pre-multiplication? It basically makes transparent pixels darker...so in the wxDC implementation the effect of alpha depends on the darkness of a pixel?

Re: Can wxDC::DrawBitmap(...true) even draw bitmaps with alpha channels?

Posted: Thu Sep 05, 2019 8:28 pm
by doublemax
So, what's the rationale for the pre-multiplication? It basically makes transparent pixels darker...so in the wxDC implementation the effect of alpha depends on the darkness of a pixel?
No, the result will always be correct. I think the underlying graphics apis need this and it's propably for performance reasons.

Re: Can wxDC::DrawBitmap(...true) even draw bitmaps with alpha channels?

Posted: Thu Sep 05, 2019 9:33 pm
by Gabriele Giuseppini
Sure, I get now what's going on here. With alpha blending, the resulting color is cBitmap*alpha + cBackground*(1-alpha); Windows requires the first term to be pre-computed, and it only takes care of the second.

Thank you so much for your help, as always!!!