【问题标题】:"Not enough storage" error while working with bitmaps使用位图时出现“存储空间不足”错误
【发布时间】:2011-10-30 14:50:56
【问题描述】:

我在尝试设置 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)

有一刻我确信它必须与内存碎片有关 - 系统有足够的可用内存来处理我的图像但是内存碎片所以没有足够大的块来容纳我的图像。但后来我看到它在 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;

【问题讨论】:

  • 请显示源代码...听起来更像是资源泄漏(即句柄泄漏)而不是内存问题...
  • 位图有多大?
  • 位图可以是任意大小。通常它们应该是普通的数码相机图片(4-16 Mpixels)。
  • 更新:这家伙说他有一个解决方案,将格式设置为 pf24bit:stackoverflow.com/questions/1473165/…
  • 汇集您的图像,不要不断创建/销毁。

标签: delphi


【解决方案1】:

根据我的实验,最大位图大小取决于:

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

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

这里有一些潜在的解决方案(我已经使用了其中的一些):

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-12
    • 2012-12-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多