ビットマップを扱う際に、Not enough storage"エラーが発生する。

BMPファイルを扱う際、BMP.HeightやBMP.Widthを設定しようとすると、"EOutofresources - Not enough storage"が表示されます。これらの指示の直後、スタックトレースが(この順番で)表示されます。 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'の呼び出しがあると思うのですが。

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;
ソリューション

私の実験によると、最大ビットマップサイズは、以下のものに依存します。

  • OSのバージョン(例えば、XPはSevenよりも小さなビットマップリソースを許可するようです)。
  • OSのエディション(64ビットOSは32ビットOSより大きなリソース割り当てが可能)。
  • 現在インストールされているRAM(および空き容量)。
  • すでに割り当てられているビットマップの数(これらは共有リソースであるため)。

そのため、巨大なデータ(画面上のビットマップの解像度以上)を扱う場合、ビットマップの割り当てが成功するかどうかはわかりません。

ここにいくつかの解決策があります (私はそのうちのいくつかを使用しました)。

  • しかし、専用のプロセス関数を書く必要がありますし、32ビットOSでは、IMHO VirtualAlloc API (FastMM4 が大きなメモリブロックのために呼び出す API) は、1GB以上の連続したメモリを割り当てることができないでしょう。
  • 前バージョンの強化:巨大なRAMブロックを処理するために64ビットプロセスを使用するか(XE2コンパイラを歓迎します)、一時保存用にファイルを使用し、処理のためにその内容をメモリマッピングします(PhotoShopや他の巨大メモリを扱う方法です) - RAMが十分であれば、一時ファイルを使用しても遅くなりません(データはディスク上に書き出されません)。
  • 大きな画像を小さな画像にタイル化する - JPEGライブラリは、画像の一部のみをレンダリングすることができ、それは簡単にビットマップリソースに収まるでしょう。
  • 例えば、ビットマップから読み込む場合、その内容を一時的なメモリブロックまたはファイルにコピーし、そのリソースを解放してから、読み込む先のビットマップを割り当てます。
  • パフォーマンスについては、正しく作ってから、速くすることです。

完璧な解決策はありません。(私の好みは小さい画像を使うことで、簡単にマルチスレッド処理できる利点があるので、新しいCPUではかなり速くなるかもしれません)また、リソース配分は、あなたの真新しい64ビットWindows 7 PCではうまくいっても、あなたの顧客の32ビットXPコンピュータでは失敗するかもしれないことを認識しておいてください。

解説 (0)