Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create an imagelist from a PNG?

Tags:

winapi

mfc

I've seen here that you can create an image list with transparency. It works... sort of.

I used this to create an image list for a list control. The results were a little disappointing:

view of actual list imageview of list image

The one on the left is how it should look. The one on the right is how the list control is displaying it. It looks like it just tried to use the alpha as a mask and any blended area is attempted to be approximated by dithering. Is there a way of getting this better so that I get an actual alpha blended image?

Here is the source if that makes any difference:

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);
}

For source of CGdiPlusBitmapResource implementation look here.

The original image with transparency is this: enter image description here

@Barmak tried with a different image and it looks fine. I think that is because the transparency is near the edge and not located within the image. See here:

enter image description here

like image 583
Adrian Avatar asked Oct 29 '14 17:10

Adrian


1 Answers

Edit ----------

First parameter in Gdiplus::GetHBITMAP should be the background color. Using RGB(0, 0, 0) as background color causes the semi-transparent pixels to match with black.

Using Gdiplus::Color(255,255,255,255) (white) it will improve the appearance (because ListView background is also white). But it's better to change the background to Gdiplus::Color(0,255,255,255) (transparent) to match any background.

{
    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);
    }
}

This assume images are all 32x32 pixels. If images are different sizes, they have to be resized before being added to image list.

{
    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);
    }
}


Using GdiPlus::GetHICON to get the icon handle... With CGdiPlusBitmapResource class, it should be possible to use the following:
HICON hicon;
m_pBitmap.Load(IDB_RIBBON_HOMELARGE, _T("PNG"), AfxGetResourceHandle());
m_pBitmap.m_pBitmap->GetHICON(&hicon);
pList->Add(hicon);

or using GetHBITMAP

Also make sure Visual Styles is enabled for improved appearance of ListView icons.

Test image with transparent background:

enter image description here

like image 100
Barmak Shemirani Avatar answered Sep 20 '22 09:09

Barmak Shemirani