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

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
Gabriele Giuseppini
Experienced Solver
Experienced Solver
Posts: 56
Joined: Fri Oct 12, 2018 6:12 pm
Location: Amsterdam, the Netherlands

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

Post 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!
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

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

Post by doublemax »

Yes, that should work. Did you destroy pixelData before drawing the bitmap?
Use the source, Luke!
Gabriele Giuseppini
Experienced Solver
Experienced Solver
Posts: 56
Joined: Fri Oct 12, 2018 6:12 pm
Location: Amsterdam, the Netherlands

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

Post 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);
}
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

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

Post 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?
Use the source, Luke!
Gabriele Giuseppini
Experienced Solver
Experienced Solver
Posts: 56
Joined: Fri Oct 12, 2018 6:12 pm
Location: Amsterdam, the Netherlands

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

Post 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.
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

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

Post 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;
Use the source, Luke!
New Pagodi
Super wx Problem Solver
Super wx Problem Solver
Posts: 466
Joined: Tue Jun 20, 2006 6:47 pm
Contact:

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

Post 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.
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

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

Post 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.
Use the source, Luke!
Gabriele Giuseppini
Experienced Solver
Experienced Solver
Posts: 56
Joined: Fri Oct 12, 2018 6:12 pm
Location: Amsterdam, the Netherlands

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

Post 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?
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

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

Post 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.
Use the source, Luke!
Gabriele Giuseppini
Experienced Solver
Experienced Solver
Posts: 56
Joined: Fri Oct 12, 2018 6:12 pm
Location: Amsterdam, the Netherlands

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

Post 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!!!
Post Reply