首页 > 解决方案 > 带有透明图像的 wxSplashScreen

问题描述

我有以下代码用于使用 WxWidgets 显示启动画面:

wxImage::AddHandler(new wxPNGHandler);
wxBitmap splashBm;
if (splashBm.LoadFile(_T("splash.png"), wxBITMAP_TYPE_PNG)) {
    wxSplashScreen scrn{splashBm,
        wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT,
        5000, frame.get(), wxID_ANY, wxDefaultPosition, wxDefaultSize,
        wxSIMPLE_BORDER | wxSTAY_ON_TOP};
    wxYield();
    //Sleep for two seconds before destroying the splash screen and showing main frame
    wxSleep(2);
    //Now destroy the splashscreen
    scrn.Destroy();
}

问题是对于具有透明背景的 PNG 图像,将显示启动窗口的背景。我在带有 XFCE 的 Linux 上。我该如何解决这个问题?

标签: c++wxwidgets

解决方案


经过一番研究,我发现可以使用该方法设置wxNonOwnedWindowwxSplashScreen继承自)的形状bool wxNonOwnedWindow::SetShape(const wxRegion & region)。并且wxFrame(继承自wxNonOwnedWindow和 的父级wxSplashScreen)必须具有样式wxFRAME_SHAPED

SetShape接收wxRegion可以从位图的不透明像素构造的对象。

wxRegion::wxRegion(const wxBitmap & bmp,
                   const wxColour & transColour,
                   int tolerance = 0
                   )

因此,我们可以将图像中的所有透明像素设置为白色,其余的设置为黑色。然后我们构造一个wxRegion使用wxWHITE颜色作为透明色。为此,我做了以下功能:

// Doesn't work well for alpha compositing
wxImage alphaToBlackAndWhiteMask (wxImage img) {

    // Some image types (e.g. gif) have a mask for the transparent pixels
    // instead of an alpha channel.
    if (!img.HasAlpha() && img.HasMask()) {
        img.InitAlpha();
    }

    if (img.HasAlpha()) {
        unsigned char *rgb = img.GetData();
        unsigned char *alpha = img.GetAlpha();

        for (int x = 0, y = 0; x < img.GetWidth()*img.GetHeight()*3; x+=3, y++) {
            // If alpha pixel, make white. Else make black.
            // An alpha value of 0 corresponds to a transparent pixel (null opacity)
            // while a value of 255 means that the pixel is 100% opaque. 
            if (alpha[y] < 20) { // Using some threshold for almost transparent pixels.
                rgb[x] = 0xff;// red
                rgb[x+1] = 0xff;// green
                rgb[x+2] = 0xff;// blue
            } else {
                rgb[x] = 0x00;// red
                rgb[x+1] = 0x00;// green
                rgb[x+2] = 0x00;// blue
            }
        }

        // Remove alpha channel so that the pixels turned white will show
        img.ClearAlpha();
    }

    return img;
}

然后,我按如下方式创建启动画面:

wxImage::AddHandler(new wxGIFHandler);

wxImage splashImg;
if (splashImg.LoadFile(_T("resources/splash.gif"), wxBITMAP_TYPE_GIF)) {

    bool hasAlpha = splashImg.HasAlpha() || splashImg.HasMask();

    wxRegion splashRgn;
    if (hasAlpha) {
        splashRgn = wxRegion(alphaToBlackAndWhiteMask(splashImg), *wxWHITE);
    }
    wxSplashScreen scrn{ splashImg,
        wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT,
        5000, nullptr, -1, wxDefaultPosition, wxDefaultSize,
        wxBORDER_NONE | wxSTAY_ON_TOP | (hasAlpha ? wxFRAME_SHAPED : 0x00) };
    if (hasAlpha) {
        scrn.SetShape(splashRgn);
    }

    // yield main loop so splash screen can show
    wxAppConsole::Yield();
    //Sleep for two seconds before destroying the splash screen and showing main frame
    wxSleep(2);
}

不幸的是,这个解决方案不适用于 alpha 合成。这意味着我们不能拥有具有一定透明度但不完全透明的像素。

我确实偶然发现了这个解决方案,它允许通过将图层堆叠在框架中并在框架上使用wxWindow'virtual bool SetTransparent (wxByte alpha)方法(某些系统可能不支持透明窗口)来实现具有透明度的图层。但正如它所说,alpha 混合并不是真正可行的,因为拥有大量具有不同透明度的像素将需要每个 alpha 级别的帧层和内存中的图像。

如果有人知道允许 alpha 合成的更好解决方案,请告诉我。


推荐阅读