hossi
hossi

Reputation: 85

Load TPicture from raw pixel data array

I am completely new to delphi and I have a project where I need to load a picture from a pointer to a C-array holding raw pixel data.

I can't simply use TPicture.LoadFromFile because the actual loading is performed within a C++ DLL. All I get back from the DLL is a pointer to the image width, height, format and the pointer to the array holding the pixel data.

DLL_Image = packed record
        Width           : UInt32;        { Width of image. [px] }
        Height          : UInt32;    { Height of image. [px] }
        Format          : ImageFormat;   { (Color) Format of image. }
        Data            : ^UInt8;    { Points to image data. }
end;
DLL_PImage = ^DLL_Image;

What I tried is loading the image data into a TMemoryStream followed by TPicture.LoadFromStream() which does not work. IIRC I had to write a proper instance of TPicture to the stream to make this work.

Is there a way to load the image from the data array?

Upvotes: 3

Views: 908

Answers (1)

Andreas Rejbrand
Andreas Rejbrand

Reputation: 108963

You can easily create a VCL TBitmap object with the given pixel data by coping the pixel data to the scanlines of the TBitmap.

Here is a simple example.

First we create some sample raw pixel data:

function MakePixelData(const AWidth, AHeight: Integer): pointer;
var
  p: PRGBQuad;
  y, x: Integer;
begin
  GetMem(Result, AWidth * AHeight * sizeof(TRGBQuad));
  p := PRGBQuad(Result);
  y := 0;
  while y < AHeight do
  begin
    x := 0;
    while x < AWidth do
    begin
      p.rgbRed := 255 * x div AWidth;
      p.rgbGreen := 255 * y div AHeight;
      p.rgbBlue := 255;
      Inc(x);
      Inc(p);
    end;
    Inc(y);
  end;
end;

procedure FreePixelData(var AData: pointer);
begin
  FreeMem(AData);
end;

The MakePixelData function allocates some memory on the heap for a bitmap of a given size and fills it with a simple gradient:

Sample bitmap with a simple gradient

The FreePixelData procedure is then used to free this memory.

Now to the interesting part, using this raw pixel data to obtain a VCL TBitmap object:

procedure TForm1.FormCreate(Sender: TObject);
const
  W = 400;
  H = 200;
var
  RawPixelData: pointer;
  bm: TBitmap;
  y: Integer;

  function PixelData(ARowIndex: Integer): pointer;
  begin
    Result := PByte(RawPixelData) + ARowIndex * W * sizeof(TRGBQuad);
  end;

begin

  RawPixelData := MakePixelData(W, H);
  try

    bm := TBitmap.Create;
    try
      bm.SetSize(W, H);
      bm.PixelFormat := pf32bit;
      for y := 0 to bm.Height - 1 do
        CopyMemory(bm.ScanLine[y], PixelData(y), bm.Width * sizeof(TRGBQuad));
      bm.SaveToFile('D:\Desktop\Bitmap from raw pixel data.bmp');
    finally
      bm.Free;
    end;

  finally
    FreePixelData(RawPixelData);
  end;

end;

This will create a VCL TBitmap from the raw pixel data. Just to demonstrate that it works, I save it as a file. But you could just as well display it in a TImage control or use it for some custom VCL drawing.

Just be careful to use the same pixel format everywhere. In my example, I use 32-bit RGBX data.

Upvotes: 6

Related Questions