使用位图时“存储空间不足”错误

时间:2011-10-30 14:50:56

标签: delphi

当我尝试设置BMP.Height或BMP.Width时,当我尝试使用BMP文件时,我得到了“EOutofresources - 没有足够的存储空间”。在这些指令之后,堆栈跟踪是(按此顺序): ntdll.dll.RtlLeaveCriticalSection,kernel32.dll.FileTimeToDosDateTime,GDI32.dll.GdiReleaseDC,GDI32.dll.PatBlt,kernel32.dll.ReadFile或类似这样:

|7E429130|user32.dll          GetParent                  
|7C90FF2D|ntdll.dll           RtlGetNtGlobalFlags    
|77F15A00|GDI32.dll           GdiReleaseDC  
|7C83069E|kernel32.dll        FileTimeToDosDateTime  
|7C9010E0|ntdll.dll           RtlLeaveCriticalSection  
|        |my function (where I set BMP.Height or BMP.Width)

此刻我确信它必须对内存碎片做一些事情 - 系统有足够的免费ram来处理我的图像但内存是碎片的,所以没有足够大的块来保存我的图像。但后来我看到它在Windows启动后11秒发生了一次。我的程序在循环中循环,我只处理一次图像!因此,这与RAM碎片无关。

当我收到此错误时,其他情况(但仍与绘图有关)如下:

|77F16A7E|GDI32.dll           IntersectClipRect     
|77F16FE5|GDI32.dll           BitBlt              
|7E429011|user32.dll          OffsetRect        
|7E42A97D|user32.dll          CallWindowProcA        
|7E42A993|user32.dll          CallWindowProcA        
|7C9010E0|ntdll.dll           RtlLeaveCriticalSection
|7E4196C2|user32.dll          DispatchMessageA     
|7E4196B8|user32.dll          DispatchMessageA      
|0058A2E1|UTest.exe           UTest.dpr 
|7C90DCB8|ntdll.dll           ZwSetInformationThread

我认为在BMP.Height之后,堆栈跟踪中始终存在'RtlLeaveCriticalSection'调用。

this帖子通过编辑Windows注册表项指向可能的解决方案。但是,帖子说它只适用于Win XP。虽然我的错误也出现在Win 7上。


我看到很多类似的帖子(其中一些帖子与将文件保存到磁盘密切相关)但直到没有人回来报告他修复了错误。


更新

根据您的要求,这是出现错误的代码:

procedure TMyBitmap.SetLargeSize(iWidth, iHeight: Integer);
CONST ctBytesPerPixel= 3;
begin
 { Protect agains huge/empty images }
 if iWidth<     1  then iWidth:=     1 else
 if iWidth> 32768  then iWidth:= 32768;

 if iHeight<     1 then iHeight:=     1 else
 if iHeight> 32768 then iHeight:= 32768;

 { Set image type }
 if iWidth * iHeight * ctBytesPerPixel > 9000000 {~9MB}                       
 then HandleType:= bmDIB                                         { Pros and cons: -no hardware acceleration, +supports larger images }
 else HandleType:= bmDDB;                                                      

 { Total size is higher than 1GB? }
 if (iWidth* iHeight* ctBytesPerPixel) > 1*GB then
  begin
     Width  := 8000;                                                            { Set a smaller size }
     Height := 8000;                                                            { And rise an error }
     RAISE Exception.Create('Image is too large.');
  end;

 { Set size }
 Width := iWidth;                           <----------------- HERE
 Height:= iHeight;
end;

1 个答案:

答案 0 :(得分:4)

从我的实验中,最大位图大小取决于:

  • 操作系统版本(例如XP似乎允许比七个更小的位图资源);
  • 操作系统版本(64位操作系统允许比32位操作系统更大的资源分配);
  • 安装当前的RAM(并且免费);
  • 已分配的位图数(因为这些是共享资源)。

因此,当您开始使用大量数据(超过屏幕上的位图分辨率)时,您无法确定位图分配是否成功。

以下是一些可能的解决方案(我已经使用了其中一些):

  • 分配不是位图资源,而是使用普通的内存块,然后使用直接的Win32 BitBlt API绘制它 - 但是你必须编写一些专用的流程函数(或使用一些第三方库),然后32位操作系统,IMHO VirtualAlloc API(由FastMM4调用的大块内存)将无法分配超过1 GB的连续内存;
  • 以前版本的增强:要么使用64位进程来处理巨大的RAM块(欢迎使用XE2编译器),要么使用文件进行临时存储,然后对其内容进行内存映射处理(这是PhotoShop或其他句柄的处理方式)巨大的内存) - 如果你有足够的RAM,使用临时文件将不需要更慢(没有数据将写在磁盘上);
  • 将大图片拼接成较小的图片 - JPEG库只能渲染图片的一部分,并且很容易适合位图资源;
  • 在所有情况下,防止任何重复的位图资源(因为共享所有位图资源):例如,如果您正在读取位图,将其内容复制到临时内存块或文件中,释放其资源,然后分配您的目标位图;
  • 关于性能,做对,然后加快速度 - 不要在实施过早包含“技巧”(您的客户可能会接受等待几秒钟,但不会接受全局故障)。

没有完美的解决方案(我的偏好是使用较小的图片,因为它具有易于多线程处理的优点,因此可以通过新CPU加速很多),并且要注意资源分配可能在您的全新64位Windows 7 PC上工作,但在客户的32位XP计算机上失败。