Are you having problems getting and setting individual pixels in your Delphi XE3 FireMonkey program ?  I’ll discuss how to do it and provide code samples.

The API for accessing individual pixels has changed in XE3 !  The older XE2 TBitmap.Scanline has been removed and replaced with TBitmapData in XE3.   This is not well documented in XE3 and most code examples that I googlized were based on scanline and failed to compile in XE3 !

Another change in XE3 is you now have to lock and unlock bitmaps in order to read or write pixels via TBitmapData.  This prevents other code or threads from modifying the bitmap.

To lock the Bitmap, use Map and unlock with Unmap.  If the bitmap is already locked, Map will return FALSE (it does not raise an error or wait for the lock to be released).

If you only need to write pixels (and not read) there are alternatives to TBitMapData such as Bitmap.Canvas.DrawLine and DrawEllipse.

The examples below are based on a bitmap in a TImage, but the code will be largely applicable to any bitmap

In summary, TBitmapData allows you to read and write individual pixels and it requires you to write some simple bitmap locking code.

Acknowledgements – thanks to Nicholas Ring for pointing me in the direction of TBitmapData.

Example 1)  Read all image pixels and dump pixel info to a TMemo

procedure TForm1.DumpImageButtonClick(Sender: TObject);
  // Example of getting every pixel in a bitmap and dumping to screen.
  // Requires Image1, Memo1
  // try this with a small image first such as 50 x 50 pixels
var
  vBitMapData : TBitmapData;
  vPixelColor : TAlphaColor;  // Note: FireMonkey colors are different from VCL TColor
  x,y         : Integer;
begin
  memo1.Lines.Clear;
                    // dump bitmap pixels
  if image1.Bitmap.Map(TMapAccess.maRead, vBitMapData) then // lock bitmap and get pixels
     begin
     for y := 0 to trunc(image1.Bitmap.height)-1 do     // loop through image lines
       begin
       Memo1.Lines.Add('======================');
       memo1.Lines.Add('Line # ' + IntToStr(Y));
       Memo1.Lines.Add('======================');
       for x := 0 to trunc(image1.Bitmap.width)-1 do  // loop through pixels on the line
           begin
           vPixelColor := vBitmapData.GetPixel(x,y);  // get the pixel colour

           memo1.Lines.Add(                           // dump pixel info to screen
                    'line='    + IntToStr(Y)
                  + ' row='    + IntToStr(X)
                  + ' Colour=' + IntToStr(vBitmapData.GetPixel(x,y))
                  + ' Red='    + IntToStr (TAlphaColorRec(vPixelColor).R) // red
                  + ' Green='  + IntToStr (TAlphaColorRec(vPixelColor).G) // blue
                  + ' Blue='   + IntToStr (TAlphaColorRec(vPixelColor).B) // green
                  )
           end;
       end;
     image1.Bitmap.Unmap(vBitMapData);      // unlock the bitmap
     end;
end;

Example 2) Write a pixel within a TImage bitmap

procedure TForm1.WritePixelButtonClick(Sender: TObject);
 // example of setting a single pixel in a TImage bitmap.
 // the image must already be loaded in the TImage
var
  vBitMapData  : TBitmapData;
  vPixelColor  : TAlphaColor; // note: FireMonkey colors are different from VCL TColor
begin
                              // lock and get the bitmap pixels
  if  image1.Bitmap.Map(TMapAccess.maWrite, vBitMapData) then
      begin
      vPixelColor := TAlphaColorRec.Blue;       // determine colour to use
      vBitmapData.SetPixel(10,20, vPixelColor); // set the pixel colour at x:10, y:20

      image1.Bitmap.Unmap(vBitMapData);         // unlock the bitmap
      end;
end;

7 Responses

  1. HI, Thanks for the info.
    But you might consider to show an example for the DATA pointer, too?
    For highspeed routines(i.e. Softwarerendering) Get/SetPixel might kill speed. And as far as i can see, using the DATA pointer i can access the pixels like i did with scanlines.

  2. Interesting point, thanks for the feedback.

    I probably wont look into DATA but if anyone else does please post a link to the solution here.

    It performed well enough for my needs where I was doing some image processing similar to red eye removal on s single image. If performance is a problem for you, keep in mind that this is probably not the only way to do this, so keep looking for other solutions.

    On a separate note. One of the nice things about this technique is I did not have to write code to deal with the image format such as how many bytes are used for each pixel and how many bytes are used for each pixel colour (R G B bytes). Delphi took care of that behind the scenes. It tested ok for the image formats that I tested, BMP, JPG and GIF.

    Cheers and happy pixel pushing

  3. Hi Scott,
    Did you try the DumpImage routine to a Firemonkey TMemo XE3 ?
    I’m just trying it here and the output to the memo is taking ages to process a 480px tall bitmap!
    I’d heard the TMemo was slow but this is crazy! Have tried a beginUpdate and even adding the output to a TStringList then assigning the .text properties

  4. Andy

    Yes, I tried but no luck. I get the same performance problems with FireMonkey TMemo that you are experiencing. I tried all of my usual VCL tricks to improve performance, but they either do not work on FireMonkey or do not improve performance.

    As an alternative, you could write the output to a text log file instead of to TMemo

  5. Hi, I tested the new TBitmap class in Fmx2 and I noticed a considerable increase in memory consumption, it is only recently that I use FireMonkey and I was wondering if anyone had any suggestions or tips to improve memory. thanks and sorry for the bad english.