首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ 從記憶體緩衝區直接顯示 JPEG/GIF/PNG 圖片:GDI+ IPicture 串流解碼實現

     編輯:VC 教學

網路傳輸、資料庫 Blob、壓縮包解壓等場景下,圖片數據常以 BYTE* 緩衝區形式存在於記憶體中,無實體檔案。本文展示如何利用 GDI+ IStream + OleLoadPicture (支援 JPEG/GIF/BMP/TIFF/PNG) 直接從記憶體解碼並繪製到視窗,無需落地檔案。

核心流程

  1. GlobalAlloc(GMEM_MOVEABLE) 分配可移動全域記憶體。
  2. GlobalLock 鎖定獲取指標,memcpy 複製圖片數據,GlobalUnlock。
  3. CreateStreamOnHGlobal 將 HGLOBAL 包裝為 IStream* (COM 串流介面)。
  4. OleLoadPicture 從串流解碼獲取 IPicture*。
  5. GDI+ Graphics::DrawImage 繪製 (或 IPicture::Render)。

完整封裝函數


// 需包含: #include   using namespace Gdiplus;
// 鏈接: gdiplus.lib
// 專案屬性: C/C++ -> Code Generation -> Runtime Library: Multi-threaded (/MT 或 /MD)

void DrawImageFromMemory(HWND hWnd, const BYTE* pImgData, int nSize, int destW, int destH) {
    // 1. GDI+ 初始化 (每進程一次,建議在 App 啟動時做)
    ULONG_PTR gdiToken;
    GdiplusStartupInput gdiInput;
    GdiplusStartup(&gdiToken, &gdiInput, NULL);

    // 2. 配置全域記憶體並複製數據
    HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, nSize);
    if (!hMem) { GdiplusShutdown(gdiToken); return; }
    BYTE* pMem = (BYTE*)GlobalLock(hMem);
    memcpy(pMem, pImgData, nSize);
    GlobalUnlock(hMem);

    // 3. 建立 IStream
    IStream* pStream = NULL;
    HRESULT hr = CreateStreamOnHGlobal(hMem, TRUE, &pStream); // TRUE: 釋放流時自動釋放 hMem
    if (FAILED(hr)) { GdiplusShutdown(gdiToken); return; }

    // 4. 定位流開頭
    LARGE_INTEGER liZero = {0}; ULARGE_INTEGER uliNewPos;
    pStream->Seek(liZero, STREAM_SEEK_SET, &uliNewPos);

    // 5. OleLoadPicture 解碼 (支援 JPEG/GIF/BMP/TIFF/PNG/ICO/WMF/EMF)
    IPicture* pPic = NULL;
    hr = OleLoadPicture(pStream, nSize, TRUE, IID_IPicture, (void**)&pPic);
    pStream->Release(); // 釋放流 (若 TRUE 也釋放了 hMem)

    if (FAILED(hr) || !pPic) {
        GdiplusShutdown(gdiToken);
        return;
    }

    // 6. GDI+ 繪製 (高質量縮放/抗鋸齒)
    HDC hdc = GetDC(hWnd);
    Graphics g(hdc);
    Image img(pStream); // 注意: 此處需重新建立流或用 IPicture::Render
    // 更簡單: 直接用 IPicture::Render
    RECT rc = {0, 0, destW, destH};
    long hmW, hmH;
    pPic->get_Width(&hmW); pPic->get_Height(&hmH); // HIMETRIC 單位
    pPic->Render(hdc, 0, 0, destW, destH, 0, hmH, hmW, -hmH, &rc);
    ReleaseDC(hWnd, hdc);

    // 7. 清理
    pPic->Release();
    GdiplusShutdown(gdiToken);
}

關鍵點補充

  • GDI+ 初始化/關閉: 應用程式生命週期內僅需一次 GdiplusStartup/GdiplusShutdown,不要在每次繪製時重複調用 (效能差、可能導致 Token 耗盡)。
  • IPicture vs GDI+ Image: OleLoadPicture 返回 IPicture (舊 COM),功能足夠;若需高階特效 (旋轉、矩陣變換、Alpha 混合),可用 Image::FromStream (需流可 Seek)。
  • 錯誤處理: OleLoadPicture 對損壞數據敏感,可能拋出存取違規 (SEH 異常),建議包裹 __try/__except 或驗證數據完整性 (如 JPEG SOI/EOI 標記)。
  • Unicode/ANSI: 純二進位操作,無字元集問題。
  • 大圖片: GlobalAlloc 限制 2GB (32位進程),超大圖片需分塊串流或臨時檔案。

適用場景

  • 網路攝影機 MJPEG 串流幀解碼顯示。
  • 資料庫 IMAGE/BLOB 欄位讀取即時顯示。
  • 加密/壓縮檔案解密後記憶體中直接預覽。
  • 嵌入式系統無檔案系統環境。

此技術是「零落地」圖片顯示的核心基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved