Recent

Author Topic: scanline example  (Read 673 times)

speter

  • New Member
  • *
  • Posts: 12
scanline example
« on: May 17, 2020, 11:54:20 am »
G'Day Folks,

Over the past couple of days I've been looking at bitmap.scanline.
I didn't find many lazarus specific examples of drawing with scanline, so I've included below a simple example.
The attachment is a ZIP file with the project files and the 7 small images.
If you couldn't be bothered grabbing the zip, the source below is probably all you need.

The images are each 100x100px in various image formats and colour depths.
6 of the 7 images are drawn correctly (a 24bit PNG image doesn't draw correctly).
If anyone has any suggestions for improving the example, please post. :)

There is an image of the program window at:
https://i.imgur.com/K2LJzKn.jpg (1126x365px).

 
Code: Pascal  [Select][+][-]
  1. {
  2.   This program loads 7 images and then draws them on the form's canvas;
  3.   first using canvas.draw and then secondly using bitmap.scanline.
  4.  
  5.   Stephen Peter.
  6.   2020-05-17
  7.  
  8.   updated:
  9.   2020-05-18
  10.     replaced img.Picture.bitmap.PixelFormat
  11.     with     img.picture.bitmap.RawImage.Description.BitsPerPixel
  12. }
  13. unit main;
  14.  
  15. {$mode objfpc}{$H+}
  16.  
  17. interface
  18.  
  19. uses
  20.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls;
  21.  
  22. const
  23.   num_pics = 7;
  24. type
  25.  
  26.   { TForm1 }
  27.  
  28.   TForm1 = class(TForm)              // form: width=1105; height=320
  29.     Memo1: TMemo;                    // memo: align=left; width=325
  30.     procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  31.     procedure FormCreate(Sender: TObject);
  32.     procedure FormPaint(Sender: TObject);
  33.   private
  34.     count : integer;
  35.     procedure drawit(a : integer);
  36.   public
  37.     images  : array [1..num_pics] of timage;
  38.     isok : array [1..num_pics] of boolean;
  39.   end;
  40.  
  41. const
  42.   fn : array [1..num_pics] of string =
  43.     ('pic_4.gif', 'pic_8.gif',
  44.      'pic_24.bmp','pic_24.jpg','pic_24.png',
  45.      'pic_32.bmp','pic_32.png');
  46. var
  47.   Form1: TForm1;
  48.  
  49. implementation
  50.  
  51. {$R *.lfm}
  52.  
  53. { TForm1 }
  54.  
  55. //-------------------------------------------------------------------------
  56. procedure TForm1.FormCreate(Sender: TObject);
  57. //-------------------------------------------------------------------------
  58. var
  59.   a : integer;
  60. begin
  61.   for a := 1 to num_pics do
  62.     if fileexists(fn[a]) then
  63.       begin
  64.         isok[a] := false;
  65.         images[a] := timage.create(form1);
  66.         images[a].visible := false;
  67.         try
  68.           images[a].picture.LoadFromFile(fn[a]);
  69.           isok[a] := true;
  70.         except
  71.           memo1.lines.add('There was a problem with image #'+a.tostring+' ('+fn[a]+').');
  72.         end
  73.       end
  74.     else
  75.       memo1.lines.add('Image #'+a.tostring+' was NOT found ('+fn[a]+').');
  76. end;
  77.  
  78. //-------------------------------------------------------------------------
  79. procedure TForm1.drawit(a : integer);
  80. //-------------------------------------------------------------------------
  81. type
  82.   // 24 bit image types
  83.   Trgb24 = packed record
  84.     b,g,r : byte;
  85.   end;
  86.   Trgb24scanline = array [word] of Trgb24;
  87.   Prgb24scanline = ^Trgb24scanline;
  88.  
  89.   // 32 bit image types
  90.   Trgb32 = packed record
  91.     b,g,r,a: byte;
  92.   end;
  93.   Trgb32scanline = packed array[word] of Trgb32;
  94.   Prgb32scanline = ^Trgb32scanline;
  95. var
  96.   xpos, x,y, bitsperpx : integer;
  97.   img : timage;
  98.   bitmap : tbitmap;
  99.   source24bit : Prgb24scanline;
  100.   source32bit : Prgb32scanline;
  101.   dest24bit   : Prgb24scanline;
  102. begin
  103.   xpos := memo1.width + 10 + count*110;
  104.   img := images[a];
  105.  
  106.   // draw the piccy
  107.   canvas.draw(xpos,10,img.picture.bitmap);
  108.  
  109.   // draw the file-name
  110.   canvas.Brush.color := cldefault;
  111.   canvas.TextOut(xpos,112,'('+a.tostring+') '+fn[a]);
  112.  
  113.   // draw the pixel format
  114.   bitsperpx := img.picture.bitmap.RawImage.Description.BitsPerPixel;
  115.   canvas.TextOut(xpos,133,bitsperpx.tostring);
  116.  
  117.   // draw the piccy using scanline
  118.   bitmap := tbitmap.create;
  119.   try
  120.     bitmap.width  := 100;
  121.     bitmap.height := 100;
  122.     bitmap.PixelFormat := pf24bit;
  123.  
  124.     for y := 0 to bitmap.height-1 do
  125.       begin
  126.         dest24bit := bitmap.scanline[y];
  127.  
  128.         if bitsperpx = 24 then
  129.           begin
  130.             source24bit := img.picture.bitmap.scanline[y];
  131.             for x := 0 to bitmap.width-1 do
  132.               begin
  133.                 // copy the pixel
  134.                 dest24bit^[x] := source24bit^[x];
  135.                 { could alternatively assign each colour byte
  136.                 dest24bit^[x].r := source24bit^[x].r;
  137.                 dest24bit^[x].g := source24bit^[x].g;
  138.                 dest24bit^[x].b := source24bit^[x].b;}
  139.               end;
  140.           end
  141.         else if bitsperpx = 32 then
  142.           begin
  143.             source32bit := img.picture.bitmap.scanline[y];
  144.             for x := 0 to bitmap.width-1 do
  145.               begin
  146.                 // copy the pixel (colours)
  147.                 dest24bit^[x].r := source32bit^[x].r;
  148.                 dest24bit^[x].g := source32bit^[x].g;
  149.                 dest24bit^[x].b := source32bit^[x].b;
  150.               end;
  151.           end;
  152.       end;
  153.     canvas.draw(xpos,170,bitmap);
  154.     canvas.TextOut(xpos,272,'^made using');
  155.     canvas.TextOut(xpos,293,'  scanline');
  156.   finally
  157.     bitmap.free;
  158.   end;
  159.  
  160.   inc(count);
  161. end;
  162.  
  163. //-------------------------------------------------------------------------
  164. procedure TForm1.FormPaint(Sender: TObject);
  165. //-------------------------------------------------------------------------
  166. var
  167.   a : integer;
  168. begin
  169.   count := 0;
  170.   for a := 1 to num_pics do
  171.     if isok[a] then
  172.       drawit(a);
  173. end;
  174.  
  175. //-------------------------------------------------------------------------
  176. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  177. //-------------------------------------------------------------------------
  178. var
  179.   a : integer;
  180. begin
  181.   for a := 1 to num_pics do
  182.     images[a].free;
  183. end;
  184.  
  185. end.

Edit:
2020-05-18, new version of code; replaced ZIP file with new version.
« Last Edit: May 18, 2020, 02:07:53 am by speter »

wp

  • Hero Member
  • *****
  • Posts: 7529
Re: scanline example
« Reply #1 on: May 17, 2020, 12:50:30 pm »
Maybe this is a bug, maybe this is specification of png, I don't know. But when you look at Images[a].Picture.Bitmap.RawImage.Description.BitsPerPixel you'll see that the png image #5 has 32 bits per pixel although its PixelFormat is reported as pf24bit. So, when you replace the checks for PixelFormat in the y loop by checks for img.Picture.Bitmap.RawImage.Description.BitsPerPixel the program will work correctly.

It always has been said that ScanLine is not portable, I don't know if this is still true since it is working on Linux in addition to Windows (did not check Mac).
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

Handoko

  • Hero Member
  • *****
  • Posts: 3755
  • My goal: build my own game engine using Lazarus
Re: scanline example
« Reply #2 on: May 17, 2020, 12:58:04 pm »

circular

  • Hero Member
  • *****
  • Posts: 3440
    • Personal webpage
Re: scanline example
« Reply #3 on: May 17, 2020, 03:10:39 pm »
As explained there, PixelFormat is not reliable. You can use the information about the raw image though:
https://forum.lazarus.freepascal.org/index.php/topic,43001.msg300308.html#msg300308
Conscience is the debugger of the mind

Handoko

  • Hero Member
  • *****
  • Posts: 3755
  • My goal: build my own game engine using Lazarus
Re: scanline example
« Reply #4 on: May 17, 2020, 03:55:39 pm »
Oops, I missed that post.
Thank you.

speter

  • New Member
  • *
  • Posts: 12
Re: scanline example
« Reply #5 on: May 18, 2020, 01:21:16 am »
Here is a second scanline example, this one showing "transparency" effects. :)

The attached ZIP file includes the project files (including the 100x100px image).

A image of the app is at:
https://i.imgur.com/dQhO4I6.jpg (322x153px)

Code: Pascal  [Select][+][-]
  1. {
  2. This program gives a simple example of using bitmap.scanline in Lazarus to draw
  3. graphics. It uses a 24bit image (and will not work with other image depths).
  4.  
  5. Stephen Peter, 2020-05-17.
  6. }
  7. unit example2;
  8.  
  9. {$mode objfpc}{$H+}
  10.  
  11. interface
  12.  
  13. uses
  14.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  15.   ComCtrls, ExtCtrls;
  16.  
  17. type
  18.  
  19.   { TForm1 }
  20.  
  21.   TForm1 = class(TForm)             // W=314; H=120; onpaint=formpaint
  22.     Label1: TLabel;                 // L=0; T=0; cap='Opacity'
  23.     opacityTrackBar: TTrackBar;     // min=0; max=10; onchange=formpaint
  24.     opacityPanel: TPanel;           // L=0; T=26; W=84; H=31
  25.     procedure FormPaint(Sender: TObject);
  26.   private
  27.  
  28.   public
  29.  
  30.   end;
  31.  
  32. var
  33.   Form1: TForm1;
  34.  
  35. implementation
  36.  
  37. {$R *.lfm}
  38.  
  39. { TForm1 }
  40.  
  41. //-------------------------------------------------------------------------
  42. procedure TForm1.FormPaint(Sender: TObject);
  43. //-------------------------------------------------------------------------
  44. type
  45.   Trgb24 = packed record
  46.     b,g,r : byte;
  47.   end;
  48.   Trgb24scanline = array [word] of Trgb24;
  49.   Prgb24scanline = ^Trgb24scanline;
  50. const
  51.   q_colours : array [0..3] of Trgb24 = (
  52.     (b:150; g:150; r:255),
  53.     (b:150; g:255; r:150),
  54.     (b:255; g:150; r:150),
  55.     (b:250; g:250; r:250) );
  56. var
  57.   x,y : Integer;
  58.   source_bitmap, output_bitmap : Tbitmap;
  59.   source_scanline, output_scanline : Prgb24scanline;
  60.   q : byte;
  61.   opacity : single;
  62. begin
  63.   source_bitmap := Tbitmap.create;
  64.   output_bitmap := Tbitmap.create;
  65.   try
  66.     source_bitmap.LoadFromFile('pic.bmp'); // 100x100px 24bit image
  67.  
  68.     //--- draw source_bitmap
  69.     Canvas.Draw(94,10, source_bitmap);
  70.  
  71.     //--- create output_bitmap
  72.     output_bitmap.PixelFormat := pf24bit;
  73.     output_bitmap.Width  := source_bitmap.Width;
  74.     output_bitmap.Height := source_bitmap.Height;
  75.     opacity := (opacityTrackBar.position/opacityTrackBar.max); // 0..1 higher is more opaque
  76.     for y := 0 to source_bitmap.Height - 1 do
  77.       begin
  78.         source_scanline := source_bitmap.ScanLine[y];
  79.         output_scanline := output_bitmap.ScanLine[y];
  80.         for x := 0 to source_bitmap.Width - 1 do
  81.           if (y >= 10) and (y < source_bitmap.height-10) and
  82.              (x >= 10) and (x < source_bitmap.width-10) then
  83.             begin
  84.               q := 2*ord(y > source_bitmap.height div 2) + ord(x > source_bitmap.width div 2);
  85.               output_scanline^[x].b := round(opacity*q_colours[q].b + (1-opacity)*source_scanline^[x].b);
  86.               output_scanline^[x].g := round(opacity*q_colours[q].g + (1-opacity)*source_scanline^[x].g);
  87.               output_scanline^[x].r := round(opacity*q_colours[q].r + (1-opacity)*source_scanline^[x].r);
  88.             end
  89.           else
  90.             begin
  91.               output_scanline^[x].b := source_scanline^[x].b;
  92.               output_scanline^[x].g := source_scanline^[x].g;
  93.               output_scanline^[x].r := source_scanline^[x].r;
  94.             end;
  95.       end;
  96.  
  97.     //--- draw picture (with transparency)
  98.     Canvas.Draw(204,10, output_bitmap);
  99.  
  100.   finally
  101.     source_bitmap.Free;
  102.     output_bitmap.free;
  103.   end;
  104. end;
  105.  
  106. end.
  107.  

 

TinyPortal © 2005-2018