2016-09-17 50 views
2

我是C++的新手,並且在編寫使用GDI +庫在內存中創建新位圖(不打開/讀取現有位圖)的函數時遇到問題;然後繪製位圖;在保存到PNG之前。特別是,我在位圖創建和保存代碼方面遇到了問題。我受限於使用代碼塊,即使我想要,我也不能使用視覺工作室。代碼如下:是在內存中創建GDI +位圖,然後保存爲PNG

#include "drawImage.h" 
#include <windows.h> 
#include <objidl.h> 
#include <gdiplus.h> 
#include <stdio.h> 
#include <iostream> 
using namespace std; 
using namespace Gdiplus; 

drawImage::drawImage(){} 

void drawImage::DrawBitmap(int width, int height){ 
    GdiplusStartupInput gdiplusStartupInput; 
    ULONG_PTR   gdiplusToken; 
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); 

    { 
    //Create a bitmap 
    Bitmap myBitmap(width, height, PixelFormatCanonical); 
    Graphics g(&myBitmap); 
    Pen blackpen(Color(255,0,0,0), 3); 

    //draw on bitmap 
    int x1 = 1; 
    int x2 = 200; 
    int y1 = 1; 
    int y2 = 200; 
    g.DrawLine(&blackpen, x1,y1,x2,y2); 

    // Save bitmap (as a png) 
    CLSID pngClsid; 
    GetEncoderClsid(L"image/png", &pngClsid); 
    myBitmap.Save(L"C:\\test\\test.png", &pngClsid, NULL); 
    } 

    GdiplusShutdown(gdiplusToken); 
} 

我遇到的問題如下:

  1. 的「節能」的代碼無法編譯,並給出‘GetEncoderClsid’在未聲明的錯誤消息」這個範圍「。但是,我從微軟網站here直接獲得了這個信息。我不認爲這是轉換爲PNG的正確方式,但我不知道另一種方式?當代碼編譯並運行時(通過註釋掉保存代碼),它會在「Bitmap * myBitmap = new Bitmap(width,height,PixelFormatCanonical);」這一行上崩潰。並給出一條錯誤消息,說我的可執行文件已停止工作。

我添加了'gdi32'鏈接庫和'-lgdiplus'作爲鏈接器選項。此外,我已使用this website來幫助與gdi的東西,雖然位圖部分只處理加載現有的位圖(不在內存中創建新的位圖)

我完全失去了做什麼,所以任何幫助或建議這個事情非常感謝。

+0

'drawImage'是如何定義的?你如何調用(使用什麼參數)'DrawBitmap'? – user4407569

+0

題外話:你正在用'new'分配一個新的對象,但是當它不再需要的時候你從不刪除它。這很糟糕,只要離開'DrawBitmap',就會導致內存泄漏。 – user4407569

+0

感謝您的刪除建議。我來自使用垃圾收集器的編程語言。所以我仍然在學習如何做這種類型的內存管理(所以我現在將在函數結束時將其刪除)。至於你的drawImage查詢,這段代碼是非常典型的代碼,以便保持特定的問題,但我在代碼塊中創建了一個類,這就是.cpp代碼。主庫通過以下方式調用它:drawImage di; di.DrawBitmap(250,250); (使用#include「drawImage.h」作爲包含語句) – greenbeast

回答

6

核心問題是將錯誤的像素格式傳遞到Bitmap constructorPixelFormatCanonical不是支持的pixel formats之一。這是一個掩碼,用於確定像素格式是否是規範的(請參閱IsCanonicalPixelFormat)。您必須使用真正的像素格式,例如默認PixelFormat32bppARGB

下面的代碼產生所需的輸出:

首先是,對於GDI +初始化一個小的輔助類。這樣可以確保在所有其他對象被銷燬後才執行判定(即調用GdiplusShutdown)。就銷燬順序而言,它與OP中的附加範圍具有相同的用途。另外,它也允許拋出異常。

#include <windows.h> 
#include <gdiplus.h> 
using namespace Gdiplus; 
#include <stdexcept> 
using std::runtime_error; 

struct GdiplusInit { 
    GdiplusInit() { 
     GdiplusStartupInput inp; 
     GdiplusStartupOutput outp; 
     if (Ok != GdiplusStartup(&token_, &inp, &outp)) 
      throw runtime_error("GdiplusStartup"); 
    } 
    ~GdiplusInit() { 
     GdiplusShutdown(token_); 
    } 
private: 
    ULONG_PTR token_; 
}; 

此代碼取自MSDN示例Retrieving the Class Identifier for an Encoder

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) 
{ 
    UINT num = 0;   // number of image encoders 
    UINT size = 0;   // size of the image encoder array in bytes 

    ImageCodecInfo* pImageCodecInfo = NULL; 

    GetImageEncodersSize(&num, &size); 
    if (size == 0) 
     return -1; // Failure 

    pImageCodecInfo = (ImageCodecInfo*)(malloc(size)); 
    if (pImageCodecInfo == NULL) 
     return -1; // Failure 

    GetImageEncoders(num, size, pImageCodecInfo); 

    for (UINT j = 0; j < num; ++j) 
    { 
     if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) 
     { 
      *pClsid = pImageCodecInfo[j].Clsid; 
      free(pImageCodecInfo); 
      return j; // Success 
     } 
    } 

    free(pImageCodecInfo); 
    return -1; // Failure 
} 

最後,GDI +渲染代碼。它使用自動存儲持續時間的對象,使其更緊湊,更安全。

void drawImage(int width, int height) { 
    GdiplusInit gdiplusinit; 

    //Create a bitmap 
    Bitmap myBitmap(width, height, PixelFormat32bppARGB); 
    Graphics g(&myBitmap); 
    Pen blackpen(Color(255, 0, 0, 0), 3); 

    //draw on bitmap 
    g.DrawLine(&blackpen, 1, 1, 200, 200); 

    // Save bitmap (as a png) 
    CLSID pngClsid; 
    int result = GetEncoderClsid(L"image/png", &pngClsid); 
    if (result == -1) 
     throw runtime_error("GetEncoderClsid"); 
    if (Ok != myBitmap.Save(L"C:\\test\\test.png", &pngClsid, NULL)) 
     throw runtime_error("Bitmap::Save"); 
} 

int main() 
{ 
    drawImage(200, 200); 
    return 0; 
} 

注:它看起來像GetEncoderClsid不應要求,因爲這些都是衆所周知的常量。但是,試圖通過適當的WIC CLSIDCLSID_WICPngEncoder)至Bitmap::Save僅產生了FileNotFound錯誤。

4

對於1 .: GetEncoderClsid不是庫函數,它是一個示例幫助程序定義的here。如果你想使用它從網站複製代碼。 (順便說一句,你的鏈接明確指出這一點。)

2:在創建任何GDI +對象或調用任何GDI +函​​數之前,您需要調用GdiplusStartup。請參閱其文檔here

更準確地說,關於GdiplusShutdown,因爲這似乎使您遇到新的問題:要求所有GDI +對象在調用GdiplusShutdown之前銷燬。這意味着在調用GdiplusShutdown之前,具有靜態存儲的對象(例如pen)必須超出範圍。如果您按照現在的方式在DrawBitmap中調用它,則情況並非如此。無論是打電話GdiplusStartupGdiplusShutdownmain或增加額外的支架{}周圍GdiplusStartupGdiplusShutdown之間你的代碼,因爲這些引入新的範圍,達到}pen將被銷燬。

編輯:Nr。 2通過編輯修復了問題。對於代碼的其餘問題和改進,請參閱@IInspectable的答案。

+0

非常感謝和道歉,因爲我應該更徹底,抓住這一點。我不會將此標記爲答案,因爲它只能部分回答問題。此外,我還不知道(因爲代碼崩潰),如果這將成功轉換位圖爲PNG,然後保存它。但是,再次感謝您,並且您的回覆已得到充分讚賞 – greenbeast

+0

非常感謝你Eichhornchen,也是Ilnspectable。你的幫助非常寶貴。 – greenbeast