3
votes

I get an "EOutofresources - Not enough storage" when I try to work with BMP files when I try to set BMP.Height or BMP.Width. Imediatelly after these instructions, the stack trace is (in this order): ntdll.dll.RtlLeaveCriticalSection, kernel32.dll.FileTimeToDosDateTime, GDI32.dll.GdiReleaseDC, GDI32.dll.PatBlt, kernel32.dll.ReadFile or like this:

|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)

At a moment I was sure that it has to do something with memory fragmentation - the system had enough free ram to process my image BUT the memory was fragmented so there was no block large enough to hold my image. But then I have seen it happening once 11 seconds after Windows start up. My program cycled through the loop where I process the images ONLY once! So, this could not be related to RAM fragmentation.

A different situation (but still related to drawing) when I got this error is below:

|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

I think there is always a 'RtlLeaveCriticalSection' call in the stack trace after BMP.Height.

There is this post pointing to a possible solution by editing a Windows registry key. However, the post says that it applies only to Win XP. While my error appears also on Win 7.


I see many similar posts (some of them are close connected to saving a file to disk) but until nobody came back to report that he fixed the error.


Update:

As you requested, this is the code where the error appears:

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
please show source code... it sounds more like a resource leak (i.e. Handle leak) than a memory problem...Yahia
How big are the bitmaps?Andreas Rejbrand
The bitmaps can have any size. Usually they should be normal digital camera pictures (4-16 Mpixels).Z80
Update: This guy says he has a solution by seting the format to pf24bit: stackoverflow.com/questions/1473165/…Z80
Pool your images and don't constantly create/destroy.Marco van de Voort

1 Answers

4
votes

From my experiment, the maximum bitmap size depends on:

  • The OS version (e.g. XP seems to allow smaller bitmap resources than Seven);
  • The OS edition (64 bit OS allows bigger resource allocation than 32 bit OS);
  • The current RAM installed (and free);
  • The number of bitmaps already allocated (since those are shared resources).

So you can not be sure that a bitmap allocation will be successful, when you start working with huge data (more than an on-screen bitmap resolution).

Here are some potential solutions (I've used some of them):

  • Allocate not a bitmap resource, but a plain memory block to work with, then use direct Win32 BitBlt API to draw it - but you'll have to write some dedicated process functions (or use some third party libraries), and on 32 bit OS, IMHO VirtualAlloc API (the one called by FastMM4 for big blocks of memory) won't be able to allocate more than 1 GB of contiguous memory;
  • Enhancement of the previous version: either use a 64 bit process to handle huge RAM block (welcome XE2 compiler), or use a file for temporary storage, then memory-map its content for processing (it is how PhotoShop or other handle huge memory) - if you have enough RAM, using a temporary file won't be necessary slower (no data will be written on disk);
  • Tile you big pictures into smaller pictures - the JPEG library is able to render only a portion of the picture, and it will fit easily into a bitmap resource;
  • In all cases, prevent any duplicated bitmap resource (since all bitmap resource are shared): for instance, if you are reading from a bitmap, copy its content into a temporary memory block or file, release its resource, then allocate your destination bitmap;
  • About performance, make it right, then make it fast - do not include "tricks" too early in your implementation (your customer may accept waiting some seconds, but won't accept a global failure).

There is no perfect solution (my preference is to use smaller pictures, because it has the advantage of being easily multi-threaded for the process, so may speed up a lot with new CPU), and be aware that resource allocation may work on your brand new 64 bit Windows Seven PC, but fail on the 32 bit XP computer of your customer.