4
votes

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.

2

2 Answers

1
votes

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.

1
votes

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