GDI+ & Delphi, PNG resource, DrawImage, ColorConversion -> Out of Memory
I have started to toy around with GDI+ in Delphi 2009. Among the things that I wanted to do was to load a PNG resource and apply a Color Conversion to it when drawing it to the Graphics object. I am using the code provided in http://www.bilsen.com/gdiplus/. To do that I just added a new constructor to TGPBitmap that uses the same code found in <www.codeproject.com>/KB/GDI-plus/cgdiplusbitmap.aspx (C++) or <www.masm32.com>/board/index.php?topic=10191.0 (MASM) converted to Delphi.
For reference, the converted code is as follows:
constructor TGPBitmap.Create(const Instance: HInst; const PngName: String; dummy : PngResource_t);
const
cPngType : string = 'PNG';
var
hResource : HRSRC;
imageSize : DWORD;
pResourceData : Pointer;
hBuffer : HGLOBAL;
pBuffer : Pointer;
pStream : IStream;
begin
inherited Create;
hResource := FindResource(Instance, PWideChar(PngName), PWideChar(cPngType));
if hResource = 0 then Exit;
imageSize := SizeofResource(Instance, hResource);
if imageSize = 0 then Exit;
pResourceData := LockResource(LoadResource(Instance, hResource));
if pResourceData = nil then Exit;
hBuffer := GlobalAlloc(GMEM_MOVEABLE, imageSize);
if hBuffer <> 0 then
begin
try
pBuffer := GlobalLock(hBuffer);
if pBuffer <> nil then
begin
try
CopyMemory(pBuffer, pResourceData, imageSize);
if CreateStreamOnHGlobal(hBuffer, FALSE, pStream) = S_OK then
begin
GdipCheck(GdipCreateBitmapFromStream(pStream, FNativeHandle));
end;
finally
GlobalUnlock(hBuffer);
pStream := nil;
end;
end;
finally
GlobalFree(hBuffer);
end;
end;
end;
The code seems to work fine as I am able to draw the loaded image without any problems. However, if I try to apply a Color Conversion when drawing it, then I get a lovely error: (GDI+ Error) Out of Memory.
If I load the bitmap from a file, or if I create a temporary to which I draw the initial bitmap and then use the temporary, then it works just fine.
What bugs me is that if I take the C++ project from codeproject, add the same PNG as resource and use the same color conversion (in other words, do the exact same thing I am doing in Delphi in the same order and with the same function calls that happen to go to the same DLL), then it works.
The C++ code looks like this:
const Gdiplus::ColorMatrix cTrMatrix = {
{
{1.0, 0.0, 0.0, 0.0, 0.0},
{0.0, 1.0, 0.0, 0.0, 0.0},
{0.0, 0.0, 1.0, 0.0, 0.0},
{0.0, 0.0, 0.0, 0.5, 0.0},
{0.0, 0.0, 0.0, 0.0, 1.0}
}
};
Gdiplus::ImageAttributes imgAttrs;
imgAttrs.SetColorMatrix(&cTrMatrix, Gdiplus::ColorMatrixFlagsDefault, Gdiplus::ColorAdjustTypeBitmap);
graphics.DrawImage(*pBitmap, Gdiplus::Rect(0, 0, pBitmap->m_pBitmap->GetWidth(), pBitmap->m_pBitmap->GetHeight()), 0, 0, pBitmap->m_pBitmap->GetWidth(), pBitmap->m_pBitmap->GetHeight(), Gdiplus::UnitPixel, &imgAttrs);
The Delphi counterpart is:
const
cTrMatrix: TGPColorMatrix = (
M: ((1.0, 0.0, 0.0, 0.0, 0.0),
(0.0, 1.0, 0.0, 0.0, 0.0),
(0.0, 0.0, 1.0, 0.0, 0.0),
(0.0, 0.0, 0.0, 0.5, 0.0),
(0.0, 0.0, 0.0, 0.0, 1.0)));
var
lImgAttrTr : IGPImageAttributes;
lBitmap : IGPBitmap;
begin
// .开发者_开发知识库..
lImgAttrTr := TGPImageAttributes.Create;
lImgAttrTr.SetColorMatrix(cTrMatrix, ColorMatrixFlagsDefault, ColorAdjustTypeBitmap);
aGraphics.DrawImage
(
lBitmap,
TGPRect.Create
(
0, 0, lBitmap.Width, lBitmap.Height
),
0, 0, lBitmap.Width, lBitmap.Height,
UnitPixel,
lImgAttrTr
);
I am completely clueless as to what may be causing the issue, and Google has not been of any help. Any ideas, comments and explanations are highly appreciated.
I can't fully follow your code, so consider this a more general statement: the Out of memory error is often seen with color conversions if not both the source and the target bitmap are 32bbp.
I had the same problem. The pixel format of the image loaded from resource in fact is NOT PixelFormat32bppARGB. Should be, but seems that it's not. My solution that works is:
// Create local bimap with PixelFormat32bppARGB flag
Gdiplus::Bitmap local_bmp(WIDTH, HEIGHT, PixelFormat32bppARGB);
Gdiplus::Graphics gfx(&local_bmp);
// Redraw the original bitmap on the local without transformation.
gfx.DrawImage(&resource_bmp, 0, 0);
// ... set the matrix and attributes
// Do the transformation on the local_bmp
screenGfx.DrawImage(&local_bmp,Rect(0, 0, WIDTH, HEIGHT), 0, 0, WIDTH, HEIGHT, Gdiplus::UnitPixel, &imgAttrs);
精彩评论