Direct pixel access (TBitmap.Rawdata) lazarus / 39-Graphics ---------------------------------------------------------------------- Tibb · 2017-02-11 23:40 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TempIntfImage.Colors[X, Y] := TColorToFPColor(FastBitmap.FPixelmap[X, Y]); // * $010101);?  The code does not compile if im using the multiplier. Thank you. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs; type   TFastBitmap = class  private    FPixelmap: array of array of TColor;    function GetSize: TPoint;    procedure SetSize(const AValue: TPoint);  public  function SetPixel( aX, aY: integer; aPixelInfo: TColor ): integer;  property Size: TPoint read GetSize write SetSize;  end;   { TForm1 }   TForm1 = class(TForm)    procedure FormCreate(Sender: TObject);  private    { private declarations }  public    { public declarations }  end; function FastBitmapToBitmap(FastBitmap: TFastBitmap; Bitmap: TBitmap): integer; var  Form1: TForm1;  FastBitmap: TFastBitmap;  bmp : TBitmap; implementation uses IntfGraphics; {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject);var   pixelColor: TColor;   _point: Tpoint;begin   pixelColor := TColor($FF0000);   _point.x := 640;   _point.y := 480;   //   bmp := TBitmap.create;      FastBitmap := TFastBitmap.Create;   FastBitmap.Size := _point;   FastBitmap.SetPixel(320,240,pixelColor);   //   FastBitmapToBitmap( FastBitmap, bmp );   //   self.Canvas.Pixels[FastBitmap.Size.x, FastBitmap.Size.y];   self.Canvas.Draw(0,0,bmp);end; { TFastBitmap } function TFastBitmap.GetSize: TPoint;begin  Result.X := Length(FPixelmap);  if Result.X > 0 then Result.Y := Length(FPixelmap[0])    else Result.Y := 0;end; procedure TFastBitmap.SetSize(const AValue: TPoint);begin  SetLength(FPixelmap, AValue.X, AValue.Y);end; function TFastBitmap.SetPixel(aX, aY: integer; aPixelInfo: TColor): integer;begin  FPixelmap[aX, aY] := aPixelInfo;end; function FastBitmapToBitmap(FastBitmap: TFastBitmap; Bitmap: TBitmap): integer;var  X, Y: Integer;  TempIntfImage: TLazIntfImage;begin  try    TempIntfImage := Bitmap.CreateIntfImage;  //    for X := 0 to FastBitmap.Size.X - 1 do      for Y := 0 to FastBitmap.Size.Y - 1 do        TempIntfImage.Colors[X, Y] := TColorToFPColor(FastBitmap.FPixelmap[X, Y]); // * $010101);  //Invalid vertical pixel index 1    //    Bitmap.LoadFromIntfImage(TempIntfImage);  finally    TempIntfImage.Free;  end;  result := 1;end; end. ---------------------------------------------------------------------- Thaddy · 2017-02-12 06:50 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... 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. ---------------------------------------------------------------------- Tibb · 2017-02-12 09:35 Ok, im doing this way: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function FastBitmapToBitmap(FastBitmap: TFastBitmap; Bitmap: TBitmap): integer;var  X, Y: Integer;  PixelPtr: PInteger;  PixelRowPtr: PInteger;  RawImage: TRawImage;  BytePerPixel: Integer;begin  try    Bitmap.BeginUpdate(False);    RawImage := Bitmap.RawImage;    PixelRowPtr := PInteger(RawImage.Data);    BytePerPixel := RawImage.Description.BitsPerPixel div 8;    for Y := 0 to FastBitmap.Size.Y - 1 do begin      PixelPtr := PixelRowPtr;      for X := 0 to FastBitmap.Size.X - 1 do begin        PixelPtr^ := FastBitmap.PixelMap[X, Y];        Inc(PByte(PixelPtr), BytePerPixel);      end;      Inc(PByte(PixelRowPtr), RawImage.Description.BytesPerLine);    end;  finally    Bitmap.EndUpdate(False);  end;end;     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. :/ ---------------------------------------------------------------------- Tibb · 2017-02-12 10:18 Code is running without error, but i cannot see the generated pic. After i copy the pixel data, im using: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i form.Canvas.Draw(0,0,bmp); But the canvas does not change. Anything else that should be done? Thanks! ---------------------------------------------------------------------- Cyrax · 2017-02-12 10:21 You need to use TForm.OnPaint event and do painting inside it. ---------------------------------------------------------------------- Tibb · 2017-02-12 10:22 Quote from: Thaddy on February 12, 2017, 07:50:50 amAlso 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 · 2017-02-12 10:26 Quote from: Cyrax on February 12, 2017, 11:21:21 amYou need to use TForm.OnPaint event and do painting inside it. Thaddy and Cyrax, thank you. ---------------------------------------------------------------------- Chronos · 2017-02-24 22:52 Quote from: tibb on February 12, 2017, 12:40:47 amHello, 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TempIntfImage.Colors[X, Y] := TColorToFPColor(FastBitmap.FPixelmap[X, Y]); // * $010101);?  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/Fa st_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];