【问题标题】:How to create an imagelist from a PNG?如何从 PNG 创建图像列表?
【发布时间】:2014-12-25 12:31:10
【问题描述】:

我已经看到here,您可以创建具有透明度的图像列表。它工作......有点。

我用它为列表控件创建了一个图像列表。结果有点令人失望:

左边是它的外观。右侧是列表控件的显示方式。看起来它只是试图将 alpha 用作蒙版,并且试图通过抖动来近似任何混合区域。有没有办法让它变得更好,以便我得到一个实际的 alpha 混合图像?

如果有什么不同的话,这里是来源:

class CDlg : public CDialog
{
    DECLARE_DYNCREATE(CDlg)

public:
    CDlg(CWnd* pParent = NULL);   // standard constructor
    virtual ~CDlg();

    // Dialog Data
    enum { IDD = IDD_BS_PRINT };
    CGdiPlusBitmapResource m_pBitmap;

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    virtual BOOL OnInitDialog();

    DECLARE_MESSAGE_MAP()
public:
    CListCtrl m_printOptions;
};

BOOL CDlg::OnInitDialog()
{
    __super::OnInitDialog();

    m_pBitmap.Load(IDB_RIBBON_HOMELARGE, _T("PNG"), AfxGetResourceHandle());
    HBITMAP hBitmap;
    m_pBitmap.m_pBitmap->GetHBITMAP(RGB(0, 0, 0), &hBitmap);

    CImageList *pList = new CImageList;
    CBitmap bm;
    bm.Attach(hBitmap);
    pList->Create(32, 32, ILC_COLOR32, 0, 4);
    pList->Add(&bm, RGB(255, 0, 255));
    m_printOptions.SetImageList(pList, LVSIL_NORMAL);

//...
    return TRUE;
}

IMPLEMENT_DYNCREATE(CDlg, CDialog)

CBSPrintDlg::CBSPrintDlg(CWnd* pParent /*=NULL*/)
: CBCGPDialog(CBSPrintDlg::IDD, pParent)
{
}

CBSPrintDlg::~CBSPrintDlg()
{
}

void CBSPrintDlg::DoDataExchange(CDataExchange* pDX)
{
    CBCGPDialog::DoDataExchange(pDX);

    DDX_Control(pDX, IDC_PRINT_OPTIONS, m_printOptions);
}

有关 CGdiPlusBitmapResource 实现的源代码,请查看 here

透明的原图是这样的:

@Barmak 尝试使用不同的图像,它看起来不错。我认为这是因为透明度靠近边缘而不位于图像内。见这里:

【问题讨论】:

  • 您绝对可以在工具栏上使用透明按钮。我对CGdiPlusBitmapResource 不熟悉,但对GetHBITMAP 的调用看起来很可疑。提供颜色键意味着正在移除透明度。
  • 好的,那么我如何读取PNG 并将其提供给CListCtrl,同时保持它的alpha 通道?
  • 对不起,我刚刚尝试过 GetHBITMAP,但这不是问题:它似乎可以根据需要保持透明度。您可能需要预乘 alpha,但我现在没有时间尝试。
  • 您是否考虑过使用 www.smalleranimals.com 的 ImgSource 库?它支持 32 位透明图像,并且可以将它们从一种格式复制到另一种格式。它可能会帮助您解决这个问题。
  • 我无法复制该显示错误。你确定那是使用的 *.png 图像吗?

标签: winapi mfc


【解决方案1】:

编辑 ----------

Gdiplus::GetHBITMAP 中的第一个参数应该是背景颜色。使用RGB(0, 0, 0)作为背景色会导致半透明像素与黑色匹配。

使用Gdiplus::Color(255,255,255,255)(白色)会改善外观(因为ListView背景也是白色的)。但最好将背景改为Gdiplus::Color(0,255,255,255)(透明)以匹配任何背景。

{
    CGdiPlusBitmapResource gdibmp;
    if (gdibmp.Load(IDB_RIBBON_HOMELARGE, _T("PNG"), AfxGetResourceHandle()))
    {
        HBITMAP hBitmap;
        gdibmp.m_pBitmap->GetHBITMAP(Gdiplus::Color::Transparent, &hBitmap);
        ImageList_AddMasked(*pList, hBitmap, 0);
    }
}

这假设图像都是 32x32 像素。如果图像大小不同,则必须在添加到图像列表之前调整它们的大小。

{
    CGdiPlusBitmapResource gdibmp;
    if (gdibmp.Load(id, _T("PNG"), AfxGetResourceHandle()))
    {
        //resize image to 32x32 pixels
        Gdiplus::Bitmap newBmp(32, 32, PixelFormat32bppPARGB);
        double oldh = (double)gdibmp.m_pBitmap->GetHeight();
        double oldw = (double)gdibmp.m_pBitmap->GetWidth();
        double neww = 32;
        double newh = 32;

        double ratio = oldw / oldh;
        if (oldw > oldh)
            newh = neww / ratio;
        else
            neww = newh * ratio;

        Gdiplus::Graphics graphics(&newBmp);
        graphics.SetInterpolationMode(Gdiplus::InterpolationMode::InterpolationModeHighQualityBicubic);
        graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
        graphics.DrawImage(gdibmp.m_pBitmap, 0, 0, (int)neww, (int)newh);

        //add `newBmp` to image list
        HBITMAP hBitmap;
        newBmp.GetHBITMAP(Gdiplus::Color::Transparent, &hBitmap);
        ImageList_AddMasked(m_ImageList, hBitmap, 0);
    }
}


使用GdiPlus::GetHICON 获取图标句柄...使用CGdiPlusBitmapResource 类,应该可以使用以下内容:
HICON hicon;
m_pBitmap.Load(IDB_RIBBON_HOMELARGE, _T("PNG"), AfxGetResourceHandle());
m_pBitmap.m_pBitmap->GetHICON(&hicon);
pList->Add(hicon);

或使用GetHBITMAP

还要确保启用 Visual Styles 以改进 ListView 图标的外观。

用透明背景测试图像:

【讨论】:

  • it should be possible to use the following。不,它不起作用。 :( 另外,我不明白您的第二个选项。资源中的 png 还是外部文件?我在 .RC 文件中的哪个位置放置了 "image_name" RCDATA "path.png" 行?这有关系吗?
  • 您可能在 *.rc 文件旁边有一个名为 *.rc2 的文件。右键单击 *.rc2 并使用文本编辑器打开它。您可以在 *.rc2 的末尾添加 "image_name" RCDATA "file_path.png" --- 或在 *.rc 文件的末尾添加 --- 这与您使用 CGdiPlusBitmapResource 添加其他 png 资源的方式类似
  • 我从链接下载了CGdiPlusBitmapResource,它适用于我添加到问题中的测试图像。我正在使用 VS2015/Windows 10。您应该提供有关您遇到的错误的更多信息,添加窗口/对话框的全屏截图。我想你还没有设置视觉样式。
  • 我正在使用带有默认 MFC 应用程序的 VS2013、Win8.1 作为 Window Dialog 应用程序项目。我将研究视觉样式,看看我是否没有启用它们。哦,当我运行 LoadResource_GdiBitmap() 函数时,FindResource() 返回一个 nullptr。编译指示在我的对话 .cpp 文件中。
  • 所以,使用我的原始代码,您的图像看起来不错(请参阅我的问题,我添加了一个示例),但我无法让它与您描述的 HICON 函数一起使用(或者您的第一个或第二个版本)。我只是在图标应该有的地方得到空白。
【解决方案2】:

PNG 图像包含部分透明的像素(alpha

如上所述,图像只有在浅灰色或白色背景上显示时才能看起来不错。但这并没有发生,背景是黑色的。现在,望远镜周围的抗锯齿像素非常明显,它们根据 alpha 值变成各种深灰色,不再与文档图像的白色背景混合。使用 GDI 函数时非常典型的错误,它不喜欢 alpha。

您可以使用 GDI+ 对其进行修改,确保背景颜色正确。但这是一项相当大的工作量,仍然让您难以正确猜测原始背景颜色。

最好还是回到您使用的任何绘画工具并在那里解决问题。最快的解决方法应该是将其重新保存为 24bpp BMP 图像文件,ymmv。

【讨论】:

  • 虽然这在一般情况下很有趣,但它并没有接近回答这个问题。最初的问题指向一个使用 GDI+ 尝试将半透明图像放入图像列表的类,但它不起作用,而这正是问题试图确定的原因。
  • 它解释了哪里出了问题以及解决问题的最佳方法。您当然可以寻求不同的解决方案,但这既不是最好的也不是最简单的方法。如果那是你喜欢做的事,就按照你的方式去做,再过一年,没有人会阻止你。最好与@Barmak 交谈。
  • 背景?什么背景?我从来没有说过任何背景是任何特定的颜色。如果您在谈论列表控件背景,它实际上是白色的。望远镜周围的区域实际上有半透明像素,但它们显示不正确。我将寻求不同的解决方案,只是因为您的回答没有回答问题,并且没有其他原因。
  • 图像列表完全支持带 alpha 的 32-bpp 颜色,尽管事实上它们使用 GDI。不要使用任何谈论蒙版的界面,并确保源图像具有预乘 alpha。 (您尝试使用紫红色作为“透明”颜色的事实表明源图像未预乘。)
猜你喜欢
  • 1970-01-01
  • 2015-09-05
  • 2014-04-17
  • 2017-01-20
  • 1970-01-01
  • 2012-06-16
  • 2020-09-18
  • 2019-10-20
  • 2011-06-10
相关资源
最近更新 更多