2016-09-17 52 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