### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: Solved: Png Grayscale (Got vertical line artifacts)  (Read 401 times)

#### Boleeman

• Hero Member
• Posts: 532
##### Solved: Png Grayscale (Got vertical line artifacts)
« on: May 17, 2024, 03:41:04 pm »
I was playing around with the grayscale code from https://forum.lazarus.freepascal.org/index.php?topic=50124.0

It stated: "Fastest way, no external libraries needed, no temporary image needed, good final quality:"

Solved by adding TBitmapPixel = record B, G, R, A: UInt8 end;     Thanks to WP.

Code: Pascal  [Select][+][-]
1.     procedure GrayBuffer(ABuffer: TBitmap);
2.     type
3.       //TBitmapPixel = record B, G, R: UInt8 end;  // For RGB
4.         TBitmapPixel = record B, G, R, A: UInt8 end;   // For ARGB
5.     type
6.       PBitmapLine = ^TBitmapLine;
7.       TBitmapLine = array [UInt16] of TBitmapPixel;
8.     var
9.       Line: PBitmapLine;
10.       LineIndex, PixelIndex: Integer;
12.     begin
13.       ABuffer.BeginUpdate();
14.
15.       for LineIndex := 0 to ABuffer.Height - 1 do
16.       begin
17.         Line := ABuffer.ScanLine[LineIndex];
18.
19.         for PixelIndex := 0 to ABuffer.Width - 1 do
20.           with Line^[PixelIndex] do
21.           begin
22.             GrayShade := Round(0.299 * R + 0.587 * G + 0.114 * B);
23.
27.           end;
28.       end;
29.
30.       ABuffer.EndUpdate();
31.     end;

Now used this working code:

Code: Pascal  [Select][+][-]
1. unit Unit1;
2.
3. {\$mode objfpc}{\$H+}
4.
5. interface
6.
7. uses
8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, LCLType, FPImage,
9.   IntfGraphics, StdCtrls;
10.
11. type
12.
13.   { TForm1 }
14.
15.   TForm1 = class(TForm)
16.     btnTograyscale: TButton;
17.     OpenDialog1: TOpenDialog;
18.     procedure btnTograyscaleClick(Sender: TObject);
19.   private
20.
21.   public
22.
23.   end;
24.
25. var
26.   Form1: TForm1;
27.
28. implementation
29.
30. {\$R *.lfm}
31.
32. procedure GrayBuffer(ABuffer: TBitmap);
33. type
34.   //TBitmapPixel = record B, G, R: UInt8 end;
35.    TBitmapPixel = record B, G, R, A: UInt8 end;
36. type
37.   PBitmapLine = ^TBitmapLine;
38.   TBitmapLine = array [UInt16] of TBitmapPixel;
39. var
40.   Line: PBitmapLine;
41.   LineIndex, PixelIndex: Integer;
43. begin
44.   ABuffer.BeginUpdate();
45.
46.   for LineIndex := 0 to ABuffer.Height - 1 do
47.   begin
48.     Line := ABuffer.ScanLine[LineIndex];
49.
50.     for PixelIndex := 0 to ABuffer.Width - 1 do
51.       with Line^[PixelIndex] do
52.       begin
53.         GrayShade := Round(0.299 * R + 0.587 * G + 0.114 * B);
54.
58.       end;
59.   end;
60.
61.   ABuffer.EndUpdate();
62. end;
63.
64. { TForm1 }
65.
66. procedure TForm1.btnTograyscaleClick(Sender: TObject);
67. var
68.   PNGImage: TPortableNetworkGraphic;
69.   GrayscaleBitmap: TBitmap;
70.   OpenDialog: TOpenDialog;
71. begin
72.   PNGImage := TPortableNetworkGraphic.Create;
73.   GrayscaleBitmap := TBitmap.Create;
74.   OpenDialog := TOpenDialog.Create(nil);
75.
76.   try
77.     // Configure the OpenDialog
78.     OpenDialog.Filter := 'PNG Files|*.png';
79.     OpenDialog.Title := 'Select a PNG File';
80.
81.     // If the user selects a file and clicks OK
82.     if OpenDialog.Execute then
83.     begin
84.       // Load the selected PNG file into the PNGImage object
86.
87.       // Load the PNG image into a TBitmap object
88.       GrayscaleBitmap.Assign(PNGImage);
89.
90.       // Convert the TBitmap to grayscale
91.       GrayBuffer(GrayscaleBitmap);
92.
93.       // Display the grayscale image on the form
94.       Canvas.Draw(0, 0, GrayscaleBitmap);
95.
96.       // Save the grayscale image as gray_myimage.png using PNG format
97.       PNGImage.Assign(GrayscaleBitmap);
98.       PNGImage.SaveToFile('gray_myimage.png');
99.     end;
100.   finally
101.     OpenDialog.Free;
102.     GrayscaleBitmap.Free;
103.     PNGImage.Free;
104.   end;
105. end;
106.
107. end.
108.

All solved now !

« Last Edit: May 17, 2024, 05:02:08 pm by Boleeman »

#### Ally

• Jr. Member
• Posts: 54
##### Re: Png Grayscale: Got vertical line artifacts
« Reply #1 on: May 17, 2024, 04:09:43 pm »
Hello Boleeman,

here is an example program.
The conversion to grayscale is done by the unit rhsBitmapGrayscale.pas

Greetings
Roland

#### wp

• Hero Member
• Posts: 12067
##### Re: Png Grayscale: Got vertical line artifacts
« Reply #2 on: May 17, 2024, 04:35:24 pm »
When you work with Canvas.Scanline you must know what you are doing. A png image very probably has 4 color channels (red, green, blue, and alpha - 32 bits per pixel); the alpha (transparency) channel is why the png format has become so popular. The TBitmapPixel record in your code, however, has only three elements (B, G, R)! Probably, adding a 4th element (A) to the record should fix the issue:
Code: Pascal  [Select][+][-]
1.     type
2.       TBitmapPixel = record B, G, R, A: UInt8 end;
3.
BUT: Without further investigation, I would not know whether the A should be added as first or as last record element, and I also would not be able to sware that this is correct in all widgetsets. So - I don't know what I am doing, and I'd use a well-tested method written by others instead.

Ally's method, BitmapGrayscale, BTW has been included in the GraphUtil unit of Lazarus 3.0x.
« Last Edit: May 17, 2024, 05:00:54 pm by wp »

#### Boleeman

• Hero Member
• Posts: 532
##### Re: Png Grayscale: Got vertical line artifacts
« Reply #3 on: May 17, 2024, 04:51:20 pm »
Thanks Roland and WP.

WP, The png I was using had alpha.  You were correct TBitmapPixel = record B, G, R, A: UInt8 end;  solved it.
Also Thanks for the conversion of GeoPattern. Was so happy playing with it.

Roland, got and error at if ImageSource.HasGraphic then , but found a slightly different version of your gray scaler so I replaced:

procedure TForm1.Scale;
begin
if ImageSource.HasGraphic then
ImageScaled.Picture.Assign(ImageSource.Picture)
else
Exit;

if (ImageScaled.Picture.Width / ImageScaled.Picture.Height) > (Panel1.Width / Panel1.Height) then
BitmapScale(ImageScaled.Picture.Bitmap, Panel1.Width, 0)
else
BitmapScale(ImageScaled.Picture.Bitmap, 0, Panel1.Height);
end;

with

procedure TForm1.Scale;
begin
ImageScaled.Picture.Assign(ImageSource.Picture);
if (ImageSource.Picture.Width > Panel1.Width) or (ImageSource.Picture.Height > Panel1.Height) then
if (ImageScaled.Picture.Width / ImageScaled.Picture.Height) > (Panel1.Width / Panel1.Height) then
BitmapScale(ImageScaled.Picture.Bitmap, Panel1.Width, 0)
else
BitmapScale(ImageScaled.Picture.Bitmap, 0, Panel1.Height);
end;

Many thanks Roland for that.
Also thanks for the Vector version of Spiral of Theodorus. Much appreciated.

« Last Edit: May 17, 2024, 04:55:06 pm by Boleeman »