【windows gdi+】GDI+ Image類加載圖片時異常問題處理與分析

 問題描述:

項目裏一個控件,須要加載本地圖片,單張第一次加載的時候能夠的,可是從新選擇其餘圖片,會出現圖片顯示異常的現象。markdown

直接上圖,圖片部分顯示不全了。編碼

問題分析:

一開始懷疑現象是有個圖片重複選擇後顯示是好的,其餘某幾個圖很容易復現,就懷疑是圖片問題,查了半天jpg圖片完整性,發現圖片也沒什麼區別,後面又查分辨率,在DrawImage()裏修改分辨率爲偶數了也不行。。。最後只能看代碼了,發現是代碼裏本身清理GLOBAL內存了。其實以前有項目我遇到清理GLOBAL內存致使圖片顯示不全或者異常的問題。時間過久忘記了。仍是記錄一下吧。spa

知識附錄:


1, 是Bitmap(RT_BITMAP)類型的圖片沒法加載, RT_BITMAP是預約義類型, 資源裏面沒有bmp文件的頭, SizeofResource 的返回值要比圖片文件少幾個字節,由於少了這幾個字節, 因此GDI+會返回invalid parameter錯誤。

2, 從IStream裏面建立出來的Image對象會引用到堆裏面的GLOBAL內存, 若是GLOBAL內存hBuffer被釋放了, 建立的Image的內容就會被破壞,有時只能畫出一小部分圖片, 有時整個圖片有大片的亂碼,好像是編碼失敗的樣子, 視當時的內存情況而定。code

另外調用Image的Clone也沒用,深層次想Clone不是真正的深拷貝,仍是依賴那個GLOBAL內存的,若是那個內存被銷燬了,Clone的圖片也會異常。orm

只有在銷燬或者析構的時候才能清理那個GLOBAL內存,不然會致使顯示異常(不是100%出現,而是間歇性出現,可是確定會出現)。對象

這是看到的demo,拷貝過來的,能夠看看,注意看註釋。圖片

​

CYourClass::~CYourClass()
{
    for(IMG_VECTOR::iterator it = m_arImage.begin(); it != m_arImage.end(); it++)
        delete *it;
    for(HGLB_VECTOR::iterator it = m_arGlobal.begin(); it != m_arGlobal.end(); it++)
    {
        ::GlobalUnlock(*it);
        ::GlobalFree(*it);
    }
}


void CYourClass::AddImage(HMODULE hInst, UINT nResourceID, LPCTSTR lpType)
{
    if(lpType == RT_BITMAP)
    {
        //GDI+ can not load RT_BITMAP resouce, 
        //because they are predefined resource, 
        //they don't contains the image file header.
        assert(FALSE);
        return;
    }

    HRSRC hResource = ::FindResource(hInst, MAKEINTRESOURCE(nResourceID), lpType);
    if (!hResource)
        return;

    DWORD imageSize = ::SizeofResource(hInst, hResource);
    if (!imageSize)
        return;

    const void* pResourceData = ::LockResource(::LoadResource(hInst, hResource));
    if (!pResourceData)
        return;

    HGLOBAL hBuffer = ::GlobalAlloc(GMEM_FIXED, imageSize);
    if (NULL == hBuffer)
        return;

    void* pBuffer = ::GlobalLock(hBuffer);
    if (pBuffer)
    {
        CopyMemory(pBuffer, pResourceData, imageSize);
        IStream* pStream = NULL;
        if (::CreateStreamOnHGlobal(hBuffer, FALSE, &pStream) == S_OK)
        {
            Gdiplus::Image * pImage = Gdiplus::Image::FromStream(pStream);
            pStream->Release();
            if (pImage)
            { 
                if (pImage->GetLastStatus() == Gdiplus::Ok &&
                    pImage->GetWidth() > 0)
                {
                    m_arImage.push_back(pImage);

                    //it seems the image will take usage of the global memory.
                    //so the global memory should be kept until the image destroy.
                    //GDI++此種打開本地文件的方式佔用了這片內存,只有在你銷燬圖片時才能銷燬這片    
                    //內存,不然會破壞你的圖片,致使顯示異常
                    m_arGlobal.push_back(hBuffer);
                    return;
                }

                delete pImage;
            }
        }
        ::GlobalUnlock(hBuffer);
    }
    ::GlobalFree(hBuffer);
}

​
複製代碼

\ip

相關文章
相關標籤/搜索