您的位置:首页 > 其它

关于GDI+产生OutOfMemory问题及GDI+用法概要

2014-05-18 13:10 676 查看
最近学校作业要处理图片,本来是要使用openCV的,但之前一直学的win32编程(mfc不习惯)(一直看的比较老的那本经典的《Windows程序设计》Charles Petzold著),并且对API的要求只是打开图片,给我像素矩阵就行,剩下 的要自己写,windows自带的API应该能搞定吧,之前也学过GDI,但都是画线什么的,顶多是打开BMP图片,而我要处理的有jpg格式的,就在网上搜吧,果然有GDI+,支持这种格式,那就开始吧。

中间的过程废话就不说了,走过不少弯路。

首先需要进行配置,看了网上的教程,有VC6.0和VS的,之前一直在用VC6.0,但VC6出的时候GDI+还没有出生的,GDI+还是在VC7的时候(不知道对不对,没见过这个版本)配置较麻烦,看到还需要下载用到的dll跟文件,作业还催着的,那我就换用VS吧,反正感觉又差不了多少。感觉最简单的一种方法就是直接在程序开始加上这几句。

程序中每句话的作用就不具体解释了,光捡重要的说吧,各位有兴趣可以自己查,这个简单多了。

#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment(lib, "gdiplus.lib")


然后就可以正常使用GDI+了。

但还有一项十分重要的工作,那就是初始化GDI+,代码如下:

GdiplusStartupInput gdiplusStartupInput;
   ULONG_PTR gdiplusToken;
   GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

然后在程序结束之前,需要关闭GDI+,使用下面的语句

GdiplusShutdown(gdiplusToken);


下面讲到重点了:这一切做完之后,按理应该做自己想做的了,但只要一打开图片,获取得到 的状态就是OutOfMemory。

Image* image = new Image(L"2.jpg");
   Status st = image->GetLastStatus();


这里st一直是OutOfMemory,而不是我想要的OK,那就开始找吧。

就开始找毛病,把网上OutOfMemory产生原因相关的资料都翻了遍,网上也有很多问这类问题,但好像最后都不了了之。

这里就先直接给出答案吧,这是Windows的一个bug,这不是我下的结论,是网上许多人都说的,我只不过是验证了它。因为按照MSDN里说的,打不开的话应该是给出InvalidParameter,给出OutOfMemory坑人啊。

这里先交待一下,造成这个问题的另一个直接原因是我的无知了(由于一直用VC6,对VS的工程结构不是很了解,所以对程序读取文件位置弄错了,看到这各位看官让你们失望了,这个让我痛苦一整天的问题我也是活该啊,谁让我不知道这点呢

),但要不是这样,我也不会发现这个问题了。

产生OutOfMemory的原因也许不是内存不足了,而我的程序的原因是我把图片的位置放错了。因为我一直用的相对 路径,且按照VC6那样直接把图片跟工程文件放在一直,而VS生成的工程则是工程文件是一个,旁边还有一个文件夹,相对路径的话就应该把文件放在那个文件夹内。千万不能与工程文件放在一起,那样的话需要用绝对路径(我若一开始就用绝对路径就好了,免去了这许多麻烦)。



这里给出一个小的程序,各位可以参考,是控制台程序,从http://msdn.microsoft.com/en-us/library/windows/desktop/ms535380(v=vs.85).aspx修改了一下,添加了输出打开图片后的状态。

#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>

using namespace Gdiplus;
using namespace std;
#pragma comment(lib, "gdiplus.lib")
int main()
{
   Gdiplus::GdiplusStartupInput gdiplusStartupInput;
   ULONG_PTR gdiplusToken;
   Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

   Image* image = new Image(L"2.jpg");//这里是要打开的图片名
   Status st = image->GetLastStatus();
   printf("%d\n",st);/*将状态作为整数变量输出,对应状态如下,共有0-21,列出用到的三项。
                                             Ok                          = 0,
                                             GenericError                = 1,
                                             InvalidParameter            = 2,
                                             OutOfMemory                 = 3,*/
   printf("The width of the image is %u.\n", image->GetWidth());
   printf("The height of the image is %u.\n", image->GetHeight()); 

   delete image;
   Gdiplus::GdiplusShutdown(gdiplusToken);
   
   return 0;
}


对于要使用窗口的同学,这里也给出一个用win32 API实现的,用鼠标在窗口单击产生文件的读取和显示,也是参考网上的改的(貌似我经常这样啊),在http://www.cnblogs.com/memset/archive/2012/05/12/2497109.html看到的,比我这个好多了,实现的拖动到窗口打开图片。

#include <windows.h>
#include <stdio.h>
#include <gdiplus.h>
using namespace Gdiplus;

#pragma comment(lib,"gdiplus")
#define Width 600
#define Height 600
wchar_t filename[]=L"1.jpg";/*E:\\VS\\my_demo*/
void set_window_size(HWND hWnd,int width,int height)
{
RECT rcWindow,rcClient;
int border_width,border_height;

GetWindowRect(hWnd,&rcWindow);
GetClientRect(hWnd,&rcClient);

border_width = (rcWindow.right-rcWindow.left) - (rcClient.right-rcClient.left);
border_height = (rcWindow.bottom-rcWindow.top) - (rcClient.bottom-rcClient.top);

SetWindowPos(hWnd,0,0,0,border_width+width,border_height+height,SWP_NOMOVE|SWP_NOZORDER);
}

void draw_image(HWND hWnd,wchar_t* file)
{
HDC hdc;
int width,height;

//加载图像
Image image(file);
if(image.GetLastStatus() != Status::Ok){
MessageBoxA(hWnd,"图片无效!",NULL,MB_OK);
return;
}

//取得宽度和高度
width = image.GetWidth();
height = image.GetHeight();

//更新窗口大小
set_window_size(hWnd,width,height);

hdc = GetDC(hWnd);

//绘图
Graphics graphics(hdc);
graphics.DrawImage(&image,0,0,width,height);

ReleaseDC(hWnd,hdc);

return;
}
LRESULT CALLBACK WinLiuProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);

int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
)
{
WNDCLASS wnd;
wnd.cbClsExtra=0;
wnd.cbWndExtra=0;
wnd.hbrBackground=(HBRUSH)GetStockObject(DEFAULT_PALETTE);
wnd.hCursor=LoadCursor(NULL,IDC_CROSS);
wnd.hIcon=LoadIcon(NULL,IDI_WINLOGO);
wnd.hInstance=hInstance;
wnd.lpfnWndProc=WinLiuProc;
wnd.lpszClassName=L"liu";
wnd.lpszMenuName=NULL;
wnd.style=CS_HREDRAW|CS_VREDRAW;

RegisterClass(&wnd);

HWND hwnd;
hwnd=CreateWindowA("liu","画线程序",WS_OVERLAPPEDWINDOW,0,0,Width,Height,NULL,NULL,hInstance,NULL);
//GdiPlus初始化
ULONG_PTR gdiplusToken;
GdiplusStartupInput gdiplusInput;
GdiplusStartup(&gdiplusToken,&gdiplusInput,NULL);

ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);
MSG Msg;
while(GetMessage(&Msg,NULL,0,0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}

//GdiPlus 取消初始化
GdiplusShutdown(gdiplusToken);

return 0;
}

LRESULT CALLBACK WinLiuProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
int i=0,k=0,l=0;
switch(uMsg)
{

case WM_CREATE:

GetClientRect(hwnd,&rect);
return 0;
case WM_PAINT:

return 0;
case WM_CLOSE:
if(IDOK==MessageBoxA(hwnd,"确定要关闭此窗口吗","提醒",MB_OKCANCEL))
{
DestroyWindow(hwnd);
}
return 0;
case WM_LBUTTONDOWN:
draw_image(hwnd,filename);
return 0;
case WM_DESTROY:
PostQuitMessage(1);
break;
default:
return DefWindowProc(hwnd, uMsg,wParam, lParam);

}
return 0;
}

最后,总结一下,程序使用GDI+产生了OutOfMemory也不一定就是真正的内存不足,要先查一查是不是文件读取程序是不是有问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: