Recent

Author Topic: Direct pixel access (TBitmap.Rawdata)  (Read 7567 times)

Tibb

  • New Member
  • *
  • Posts: 26
Direct pixel access (TBitmap.Rawdata)
« on: February 12, 2017, 12:40:47 am »
Hello,

Getting the message "Invalid vertical pixel index 1" for the code bellow (I got the idea from here: http://wiki.lazarus.freepascal.org/Fast_direct_pixel_access).

Also, what is the purpose of '* $010101'? >>
Code: Pascal  [Select][+][-]
  1. TempIntfImage.Colors[X, Y] := TColorToFPColor(FastBitmap.FPixelmap[X, Y]); // * $010101);?
  2.  
The code does not compile if im using the multiplier.

Thank you.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs;
  9.  
  10. type
  11.  
  12.   TFastBitmap = class
  13.   private
  14.     FPixelmap: array of array of TColor;
  15.     function GetSize: TPoint;
  16.     procedure SetSize(const AValue: TPoint);
  17.   public
  18.     function SetPixel( aX, aY: integer; aPixelInfo: TColor ): integer;
  19.     property Size: TPoint read GetSize write SetSize;
  20.   end;
  21.  
  22.   { TForm1 }
  23.  
  24.   TForm1 = class(TForm)
  25.     procedure FormCreate(Sender: TObject);
  26.   private
  27.     { private declarations }
  28.   public
  29.     { public declarations }
  30.   end;
  31.  
  32. function FastBitmapToBitmap(FastBitmap: TFastBitmap; Bitmap: TBitmap): integer;
  33.  
  34. var
  35.   Form1: TForm1;
  36.   FastBitmap: TFastBitmap;
  37.   bmp : TBitmap;
  38.  
  39. implementation
  40.  
  41. uses IntfGraphics;
  42.  
  43. {$R *.lfm}
  44.  
  45. { TForm1 }
  46.  
  47. procedure TForm1.FormCreate(Sender: TObject);
  48. var
  49.    pixelColor: TColor;
  50.    _point: Tpoint;
  51. begin
  52.    pixelColor := TColor($FF0000);
  53.    _point.x := 640;
  54.    _point.y := 480;
  55.    //
  56.    bmp := TBitmap.create;
  57.         FastBitmap := TFastBitmap.Create;
  58.    FastBitmap.Size := _point;
  59.    FastBitmap.SetPixel(320,240,pixelColor);
  60.    //
  61.    FastBitmapToBitmap( FastBitmap, bmp );
  62.    //
  63.    self.Canvas.Pixels[FastBitmap.Size.x, FastBitmap.Size.y];
  64.    self.Canvas.Draw(0,0,bmp);
  65. end;
  66.  
  67. { TFastBitmap }
  68.  
  69. function TFastBitmap.GetSize: TPoint;
  70. begin
  71.   Result.X := Length(FPixelmap);
  72.   if Result.X > 0 then Result.Y := Length(FPixelmap[0])
  73.     else Result.Y := 0;
  74. end;
  75.  
  76. procedure TFastBitmap.SetSize(const AValue: TPoint);
  77. begin
  78.   SetLength(FPixelmap, AValue.X, AValue.Y);
  79. end;
  80.  
  81. function TFastBitmap.SetPixel(aX, aY: integer; aPixelInfo: TColor): integer;
  82. begin
  83.   FPixelmap[aX, aY] := aPixelInfo;
  84. end;
  85.  
  86. function FastBitmapToBitmap(FastBitmap: TFastBitmap; Bitmap: TBitmap): integer;
  87. var
  88.   X, Y: Integer;
  89.   TempIntfImage: TLazIntfImage;
  90. begin
  91.   try
  92.     TempIntfImage := Bitmap.CreateIntfImage;
  93.     //
  94.     for X := 0 to FastBitmap.Size.X - 1 do
  95.       for Y := 0 to FastBitmap.Size.Y - 1 do
  96.         TempIntfImage.Colors[X, Y] := TColorToFPColor(FastBitmap.FPixelmap[X, Y]); // * $010101);  //Invalid vertical pixel index 1
  97.     //
  98.     Bitmap.LoadFromIntfImage(TempIntfImage);
  99.   finally
  100.     TempIntfImage.Free;
  101.   end;
  102.   result := 1;
  103. end;
  104.  
  105. end.
  106.  
« Last Edit: February 12, 2017, 10:41:29 am by tibb »

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Invalid vertical pixel index 1
« Reply #1 on: February 12, 2017, 07:50:50 am »
TempIntfImage looks of size 0,0.
Set or create it  the same size.

Anyway, are you serious about copying an image pixel by pixel? FastBitmap is SLOW code...
Use the memory lay-out of the array to do it at once and also Introduce scanline to do it line by line.
At that point it is just as fast as a normal bitmap, because that works like that... :D

Also note your loop is extra slow, because you process x and y in the wrong order.
Natural order for your array is for every y, process x (i.e. line by line), which corresponds to sequential pixel access in memory.
That gives much, much faster code than jumping all over the place.
« Last Edit: February 12, 2017, 08:04:01 am by Thaddy »
Specialize a type, not a var.

Tibb

  • New Member
  • *
  • Posts: 26
Re: Invalid vertical pixel index 1
« Reply #2 on: February 12, 2017, 10:35:39 am »
Ok, im doing this way:

Code: Pascal  [Select][+][-]
  1. function FastBitmapToBitmap(FastBitmap: TFastBitmap; Bitmap: TBitmap): integer;
  2. var
  3.   X, Y: Integer;
  4.   PixelPtr: PInteger;
  5.   PixelRowPtr: PInteger;
  6.   RawImage: TRawImage;
  7.   BytePerPixel: Integer;
  8. begin
  9.   try
  10.     Bitmap.BeginUpdate(False);
  11.     RawImage := Bitmap.RawImage;
  12.     PixelRowPtr := PInteger(RawImage.Data);
  13.     BytePerPixel := RawImage.Description.BitsPerPixel div 8;
  14.     for Y := 0 to FastBitmap.Size.Y - 1 do begin
  15.       PixelPtr := PixelRowPtr;
  16.       for X := 0 to FastBitmap.Size.X - 1 do begin
  17.         PixelPtr^ := FastBitmap.PixelMap[X, Y];
  18.         Inc(PByte(PixelPtr), BytePerPixel);
  19.       end;
  20.       Inc(PByte(PixelRowPtr), RawImage.Description.BytesPerLine);
  21.     end;
  22.   finally
  23.     Bitmap.EndUpdate(False);
  24.   end;
  25. end;
  26.  
     

Getting SIGSEGV at line PixelPtr^ := FastBitmap.PixelMap[_X, _Y]; because RawImage.Data (PixelPtr) is nil. Why is it nil? There is RawImage methods .Init and .CreateData(), using them does not help. Data gets value only in case i load something into the bitmap? But the purpose here is to fill rawdata directly.

UPDATE: Calling Bitmap.SetSize() helped. :/
« Last Edit: February 12, 2017, 11:03:26 am by tibb »

Tibb

  • New Member
  • *
  • Posts: 26
Re: Direct pixel access (TBitmap.Rawdata)
« Reply #3 on: February 12, 2017, 11:18:12 am »
Code is running without error, but i cannot see the generated pic.

After i copy the pixel data, im using:
Code: Pascal  [Select][+][-]
  1. form.Canvas.Draw(0,0,bmp);

But the canvas does not change. Anything else that should be done?
Thanks!

Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: Direct pixel access (TBitmap.Rawdata)
« Reply #4 on: February 12, 2017, 11:21:21 am »
You need to use TForm.OnPaint event and do painting inside it.

Tibb

  • New Member
  • *
  • Posts: 26
Re: Invalid vertical pixel index 1
« Reply #5 on: February 12, 2017, 11:22:11 am »
Also note your loop is extra slow, because you process x and y in the wrong order.
Natural order for your array is for every y, process x (i.e. line by line), which corresponds to sequential pixel access in memory.
That gives much, much faster code than jumping all over the place.

Thats an example code from lazarus wiki. I noticed that the first time, but did not want to modify the example (yet). (:

Tibb

  • New Member
  • *
  • Posts: 26
Re: Direct pixel access (TBitmap.Rawdata)
« Reply #6 on: February 12, 2017, 11:26:04 am »
You need to use TForm.OnPaint event and do painting inside it.

Thaddy and Cyrax, thank you.

Chronos

  • Full Member
  • ***
  • Posts: 240
    • PascalClassLibrary
Re: Direct pixel access (TBitmap.Rawdata)
« Reply #7 on: February 24, 2017, 11:52:11 pm »
Hello,

Getting the message "Invalid vertical pixel index 1" for the code bellow (I got the idea from here: http://wiki.lazarus.freepascal.org/Fast_direct_pixel_access).

Also, what is the purpose of '* $010101'? >>
Code: Pascal  [Select][+][-]
  1. TempIntfImage.Colors[X, Y] := TColorToFPColor(FastBitmap.FPixelmap[X, Y]); // * $010101);?
  2.  
The code does not compile if im using the multiplier.

Thank you.
Purpose of * $010101 was to convert grayscale color format which was originally defined as TFastBitmapPixel = Byte to TColor. I updated wiki page to use abstract function FastPixelToTColor to be more clear that fast bitmap color format needs to be converted to TColor which is used by TCanvas.
In fact this was mentioned in wiki page "In this test let assume that we have simple bitmap structure designed as two dimensional byte array where each pixel have 256 possible colors. This could be gray image or some palette mapped image." just before list of possible drawing methods.

Order of processing X and Y was update in the article so Y is used in outer loop and X is used in inner loop.

Also added some hints to methods http://wiki.lazarus.freepascal.org/Fast_direct_pixel_access#Draw_methods

And the article assume drawing to TImage component instance where Bitmap should already initialized to given size and the drawing doesn't to be done during OnPaint event like in case of drawing directly to Form or PaintBox Canvas. But yes for drawing directly to form canvas it is better to draw to temporary bitmap and then draw to form canvas.

Which reminds me that if you want draw to entire form area then you probably want to redefine main form method:
procedure EraseBackground(DC: HDC); override;
to do nothing:
procedure TFormMain.EraseBackground(DC: HDC);
begin
  // Do nothing, all background space covered by controls
end;
And also for Linux you may want set form as opaque using ControlStyle := ControlStyle + [csOpaque];

 

TinyPortal © 2005-2018