Artifacts when displaying transparent png in timage lazarus / 21-General ---------------------------------------------------------------------- Jonny · 2025-02-04 14:44 Lazarus 4.99 (rev main_4_99-920-g0d73603378) FPC 3.3.1 x86_64-linux- gtk2 When loading a transparent png file and displaying it in a timage on my form, it produces artifacts all over the image. 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 program test; uses  Interfaces, Classes, Forms, ExtCtrls, FPImage, FPReadPNG; var  frmMain: TForm;  imgForm: TImage;  imgLogo: TFPMemoryImage;  imgReader: TFPReaderPNG; begin  Application.Initialize;  Application.CreateForm(TForm,frmMain);  frmMain.Width := 600;  frmMain.Height := 600;  imgForm := TImage.Create(frmMain);  imgForm.Parent := frmMain;  imgForm.Proportional := True;  imgForm.Stretch := True;  imgForm.BoundsRect := frmMain.BoundsRect;  try    imgReader := TFPReaderPNG.Create;    imgLogo := TFPMemoryImage.Create(0,0);    imgLogo.LoadFromFile('test.png',imgReader);    imgForm.Transparent := True; // clean without but no transparency    imgForm.Picture.Assign(imgLogo);  finally    imgReader.Free;    imgLogo.Free;  end;  Application.Run;end.  Should I be doing something to handle the transparency? ---------------------------------------------------------------------- wp · 2025-02-04 17:00 An unusual way to create an LCL application... Normally, I do this: In Lazarus, I create a new LCL project ("File" > "New" > "Project" > "Application")Drop a TImage component on the newly created formTo reproduce the settings in your posted project, use the Object Inspector and set Form1.Width = 600, Form1.Height = 600,  Image1.Align = alClient, Image1.Stretch = true (I'd also set Image1.Proportional = true to keep the aspect ratio when the image size changes)Click on the '...' next to Image1.Picture and load the image to be displayedVoila - ready: You see the image in the form, correctly, no artefactsWhen you do it your way, maybe some initialization is missing, I don't know and don't want to investigate, because I do not want to motivate you to create LCL applications this way. The Transparent property is meant for old color-transparency of bitmaps: all pixels having the "TransparentColor" (color of the top/left - or bottom/left? - pixel of the image are not painted when Transparent is true. But you have true alpha-channel transparency in the png image, and the Transparent property is not needed, maybe even harmful. To convince yourself that the image really is transparent, change the Color property of the form - this color will be visibile through the transparent parts of the loaded image. Or draw a gradient onto the form: Click on the '...' next to the OnPaint property of the form and add the following code to the newly created procedure Form1Paint: 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 procedure procedure TForm1.FormPaint(Sender: TObject);begin  Canvas.GradientFill(Rect(0, 0, Width, Height), clRed, clYellow, gdVertical);end; I adding this modified project as a attachment. Maybe I should also mention that a transparent picture loaded into a TImage is always transparent to the form, but not to the desktop! ---------------------------------------------------------------------- Jonny · 2025-02-04 17:34 Quote from: wpAn unusual way to create an LCL application... Apologies, I created a compact and minimal project to quickly identify the issue. Quote from: wpClick on the '...' next to Image1.Picture and load the image to be displayed My source image will either be a TFPMemoryImage that I generate myself or an external png file supplied after the program is already compiled and running. I am afraid that I cannot preload the image using the method that you suggested, Quote from: wpWhen you do it your way, maybe some initialization is missing, I don't know and don't want to investigate, because I do not want to motivate you to create LCL applications this way. Don't worry, I am not creating LCL methods that way. It was just to demonstrate the issue, taken from a much, much larger program, in the minimal amount of lines. Your lfm contains: 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   object Image1: TImage    Left = 0    Height = 600    Top = 0    Width = 600  Align = alClient    Picture.Data = {      175450...    }    Stretch = True  end  I see no additional initialisations there (but I am interested in how the Picture.Data is created).. Quote from: wpTo convince yourself that the image really is transparent I am convinced, thanks, your explanations are always clear, especially with your examples. Quote from: wpMaybe I should also mention that a transparent picture loaded into a TImage is always transparent to the form, but not to the desktop! I think that I kinda understand the distinction. Looks like the issue is copying the TFPMemoryImage to the TImage. Is there an alternative method to using TImage.Picture.Assign(TFPMemoryImage) which I can try? ---------------------------------------------------------------------- Thaddy · 2025-02-04 17:41 To me it just looks like an unfortunate choice of the transparancy pixel. ---------------------------------------------------------------------- Jonny · 2025-02-04 17:54 My TFPMemoryImage has an alpha channel rather than using a pixel for transparency - not sure why alpha is not being respected. ---------------------------------------------------------------------- Thaddy · 2025-02-04 17:58 QuoteMy TFPMemoryImage has an alpha channel rather than using a pixel for transparency - not sure why alpha is not being respected.As far as I know the alpha channel still relies on a pixel in the main color palate.... ---------------------------------------------------------------------- wp · 2025-02-04 18:17 As I wrote, set the color of the form to, say, yellow, run your program with the line "imgForm.Transparent := true" commented out, and see the yellow background --> the image IS rendered transparently, and now - since Transparent is false - with transparency taken from the alpha-channel. 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 program test; uses  Interfaces, Classes,   Graphics,    // <--- must be added for having "clYellow"  Forms, ExtCtrls, FPImage, FPReadPNG; var  frmMain: TForm;  imgForm: TImage;  imgLogo: TFPMemoryImage;  imgReader: TFPReaderPNG; begin  Application.Initialize;  Application.CreateForm(TForm,frmMain);  frmMain.Width := 600;  frmMain.Height := 600;  frmMain.Color := clYellow;  imgForm := TImage.Create(frmMain);  imgForm.Parent := frmMain;  imgForm.Proportional := True;  imgForm.Stretch := True;  imgForm.BoundsRect := frmMain.BoundsRect;  try    imgReader := TFPReaderPNG.Create;    imgLogo := TFPMemoryImage.Create(0,0);    imgLogo.LoadFromFile('test.png',imgReader);    //imgForm.Transparent := True;    imgForm.Picture.Assign(imgLogo);  finally    imgReader.Free;    imgLogo.Free;  end;  Application.Run;end. Since your application is an LCL application (you are using TImage!) you can load the png file by the simpler LCL procedures in the Graphics unit: 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 program test; uses  Interfaces, Classes, Graphics,  Forms, ExtCtrls; var  frmMain: TForm;  imgForm: TImage; begin  Application.Initialize;  Application.CreateForm(TForm,frmMain);  frmMain.Width := 600;  frmMain.Height := 600;  frmMain.Color := clYellow;  imgForm := TImage.Create(frmMain);  imgForm.Parent := frmMain;  imgForm.Proportional := True;  imgForm.Stretch := True;  imgForm.BoundsRect := frmMain.BoundsRect;  imgForm.Picture.LoadFromFile('test.png');  Application.Run;end. ---------------------------------------------------------------------- Jonny · 2025-02-04 18:35 Quote from: wpAs I wrote, set the color of the form to, say, yellow, run your program with the line "imgForm.Transparent := true" commented out, and see the yellow background --> the image IS rendered transparently, and now - since Transparent is false - with transparency taken from the alpha-channel. I compiled with your modifications but did not see the yellow. But not to doubt you, I thought, what could be different between us? My current builds are with the GTK2 widgetset. So I tried with QT5, QT6 and cross compiled with Win32. All worked correctly - the yellow background was there. Therefore it looks like a bug in the LCL - or do I need to initialise somehow differently? ---------------------------------------------------------------------- TRon · 2025-02-04 18:50 Quote from: Jonny on February 04, 2025, 07:35:22 pmMy current builds are with the GTK2 widgetset. It is no problem to reproduce the issue (GTK2/laz 3.6). QuoteTherefore it looks like a bug in the LCL - or do I need to initialise somehow differently? You could try workaround it using the memorystream trick discussed earlier the other thread (I haven't checked yet) ---------------------------------------------------------------------- Jonny · 2025-02-04 19:08 Quote from: TRonIt is no problem to reproduce the issue (GTK2/laz 3.6). Sorry, it is unclear, are you saying that you can or cannot reproduce the issue? Quote from: TRonYou could try workaround it using the memorystream trick discussed earlier the other thread (I haven't checked yet) Indeed, I have been toying with that for a while but have had no luck. ---------------------------------------------------------------------- TRon · 2025-02-04 19:12 Quote from: Jonny on February 04, 2025, 08:08:43 pmSorry, it is unclear, are you saying that you can or cannot reproduce the issue? I can reproduce. I attached an image to my previous post for illustration. QuoteIndeed, I have been toying with that for a while but have had no luck. Also if you load the picture using the 2nd method that wp showed ? e.g. imgForm.Picture.LoadFromStream(MyMemoryStream); fwiw: From the results it is clear that whatever is responsible does not know how to properly handle the alpha channel. ---------------------------------------------------------------------- Jonny · 2025-02-04 19:49 Quote from: TRonI can reproduce. I attached an image to my previous post for illustration. Aha, you must have added it afterwards, your image showed the artifacts, @wp had stated that was because Transparent property was harmful with png images. It is the issue with GTK2 not displaying transparency but QT5, QT6 and Win32 working correctly, that I am now trying to resolve. Quote from: TRonAlso if you load the picture using the 2nd method that wp showed ? e.g. imgForm.Picture.LoadFromStream(MyMemoryStream); fwiw: From the results it is clear that whatever is responsible does not know how to properly handle the alpha channel. OK, I shall try to pass it through a TMemoryStream next. Does this mean the memory requirements will spike again? I am trying to be conservative with resources. ---------------------------------------------------------------------- TRon · 2025-02-04 19:59 Quote from: Jonny on February 04, 2025, 08:49:13 pmAha, you must have added it afterwards, your image showed the artifacts, @wp had stated that was because Transparent property was harmful with png images. Yes, but I consider it a bug. Alpha is indeed not the same as transparency but if you know that then the alpha channel values should be converted. And in case asking how, then this is not our problem but that of how things are (currently) implemented. Might be a restriction of/issue with GTK2 though, I honestly don't know. Quote from: TRonDoes this mean the memory requirements will spike again? I am trying to be conservative with resources. Unfortunately, yes. But, for now at least, also a bit besides the point. First try make it work then see if we can figure out what the best option would be (in case possible and depending on projects preferences). ---------------------------------------------------------------------- Jonny · 2025-02-04 21:46 Still the same when passing through a stream - GTK2 TImage is not drawing transparency. 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 program test; uses  Interfaces, Classes, Forms, ExtCtrls, FPImage, FPReadPNG, FPWritePNG; var  frmMain: TForm;  imgForm: TImage;  imgLogo: TFPMemoryImage;  imgStream: TMemoryStream;  imgReader: TFPReaderPNG;  imgWriter: TFPWriterPNG; begin  Application.Initialize;  Application.CreateForm(TForm,frmMain);  frmMain.Width := 600;  frmMain.Height := 600;  frmMain.Color := $00e0e0;  imgForm := TImage.Create(frmMain);  imgForm.Parent := frmMain;  imgForm.Proportional := True;  imgForm.Stretch := True;  imgForm.BoundsRect := frmMain.BoundsRect;  try    imgReader := TFPReaderPNG.Create;    imgWriter := TFPWriterPNG.Create;    imgLogo := TFPMemoryImage.Create(0,0);    imgStream := TMemoryStream.Create;     imgLogo.LoadFromFile('test.png',imgReader);    imgLogo.SaveToStream(imgStream,imgWriter);     imgStream.Position := 0;    imgForm.Picture.LoadFromStream(imgStream);     imgWriter.UseAlpha := True;    imgLogo.SaveToFile('out.png',imgWriter);  finally    imgWriter.Free;  imgReader.Free;    imgLogo.Free;  end;  Application.Run;end. ---------------------------------------------------------------------- Jonny · 2025-02-04 22:02 I readded the TImage.Transparent property, fed the image through a TMemoryStream and now GTK2 works.... 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 program test; uses  Interfaces, Classes, Forms, ExtCtrls, FPImage, FPReadPNG, FPWritePNG; var  frmMain: TForm;  imgForm: TImage;  imgLogo: TFPMemoryImage;  imgStream: TMemoryStream;  imgReader: TFPReaderPNG;  imgWriter: TFPWriterPNG; begin  Application.Initialize;  Application.CreateForm(TForm,frmMain);  frmMain.Width := 600;  frmMain.Height := 600;  frmMain.Color := $00e0e0;  imgForm := TImage.Create(frmMain);  imgForm.Parent := frmMain;  imgForm.Proportional := True;  imgForm.Stretch := True;  imgForm.BoundsRect := frmMain.BoundsRect;  imgForm.Transparent := True; // <---- Make TImage transparent   try    imgReader := TFPReaderPNG.Create;    imgWriter := TFPWriterPNG.Create;    imgLogo := TFPMemoryImage.Create(0,0);    imgStream := TMemoryStream.Create;     imgLogo.LoadFromFile('test.png',imgReader);    imgLogo.SaveToStream(imgStream,imgWriter);     imgStream.Position := 0;    imgForm.Picture.LoadFromStream(imgStream); // <---- Pass image through stream     imgWriter.UseAlpha := True;    imgLogo.SaveToFile('out.png',imgWriter);  finally    imgWriter.Free;  imgReader.Free;    imgLogo.Free;  end;  Application.Run;end.  So confused - is this a bug then? ---------------------------------------------------------------------- wp · 2025-02-05 17:03 Did you try loading the image by means of the graphics classes as I showed above? This seems to work even for gtk2 and gkt3 (Manjaro Linux KDE24.1.2, Mint 21.1 Cinnamon). 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     program test;         uses      Interfaces, Classes, Graphics,      Forms, ExtCtrls;         var      frmMain: TForm;      imgForm: TImage;      begin      Application.Initialize;      Application.CreateForm(TForm,frmMain);      frmMain.Width := 600;    frmMain.Height := 600;      frmMain.Color := clYellow;      imgForm := TImage.Create(frmMain);      imgForm.Parent := frmMain;      imgForm.Proportional := True;      imgForm.Stretch := True;      //imgForm.Transparent := True;      imgForm.BoundsRect := frmMain.BoundsRect;      imgForm.Picture.LoadFromFile('test.png');    Application.Run;    end. ---------------------------------------------------------------------- Jonny · 2025-02-05 17:51 Quote from: wpDid you try loading the image by means of the graphics classes as I showed above? Yes, your method of directly loading the file into the TImage component works correctly. But I pass the image through a TFPMemoryImage because I need to manipulate it. 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 program test; uses  Interfaces, Classes, Graphics,  Forms, ExtCtrls, FPImage, FPReadPNG; var  frmMain: TForm;  imgForm: TImage;  imgLogo: TFPMemoryImage;  imgReader: TFPReaderPNG; begin  Application.Initialize;  Application.CreateForm(TForm,frmMain);  frmMain.Width := 600;  frmMain.Height := 600;  frmMain.Color := clYellow;  imgForm := TImage.Create(frmMain);  imgForm.Parent := frmMain;  imgForm.Proportional := True;  imgForm.Stretch := True;  //imgForm.Transparent := True;  imgForm.BoundsRect := frmMain.BoundsRect; //  imgForm.Picture.LoadFromFile('test.png');  // do not load directly  imgLogo := TFPMemoryImage.Create(0,0);  // instead load to TFPMemoryImage  imgReader := TFPReaderPNG.Create;  imgLogo.LoadFromFile('test.png',imgReader);//  Manipulate the image in my program (not necessary to show the bug here)  imgForm.Picture.Assign(imgLogo);   Application.Run;end.  This causes the break in GTK2 only. ---------------------------------------------------------------------- lainz · 2025-02-05 17:59 Try with BGRABitmap. ---------------------------------------------------------------------- TRon · 2025-02-05 18:04 Quote from: lainz on February 05, 2025, 06:59:14 pmTry with BGRABitmap. Nothing personal but that answer pisses me off so badly you have no idea  Ofc that works but is besides the point. ---------------------------------------------------------------------- Jonny · 2025-02-05 18:08 Quote from: lainzTry with BGRABitmap. I am afraid that due to external factors here, I cannot use BGRABitmap or any other third party components. (BGRABitmap may be very nice but it is a monster - something that I would avoid even if I had a choice) Besides, if there is a bug, why ignore it, can we confirm the issue and maybe address it? ---------------------------------------------------------------------- TRon · 2025-02-05 18:46 Quote from: Jonny on February 04, 2025, 11:02:33 pmSo confused - is this a bug then? It tells us that assign does not seem to handle the alpha channel properly while the standard graphic loaders seem to do it correctly. It would probably require a pixel by pixel check (actually check the alpha channel) comparing both methods in order to try locate the difference between the two. I consider it a bug though wp mentioned issues with transparency as well so his opinion might differ. Know that wp is better versed in that regards. ---------------------------------------------------------------------- Jonny · 2025-02-05 20:08 Thanks. Not sure what you want me to do. Happy to perform tests if you provide simple instructions. ---------------------------------------------------------------------- TRon · 2025-02-05 23:24 Nothing you can currently do that I am aware. I can confirm the results (both the original issue as well as the workaround) as presented by your examples. If nobody disputes it as being a bug then by logic it becomes one (and should be reported as such). I am trying to figure out if I am able to determine what might be the culprit tough I do hope it isn't something going as deep as being related to gtk2 because in that case I'm pretty much lost. PS: the todo note inside the source of assign method of rasterimage when the source is a TFPCustomImage (ancestor of TFPMemoryimage) might be there for a reason. ---------------------------------------------------------------------- lainz · 2025-02-06 00:00 Quote from: TRon on February 05, 2025, 07:04:33 pmQuote from: lainz on February 05, 2025, 06:59:14 pmTry with BGRABitmap. Nothing personal but that answer pisses me off so badly you have no idea  Ofc that works but is besides the point. Quote from: Jonny on February 05, 2025, 07:08:12 pmQuote from: lainzTry with BGRABitmap. I am afraid that due to external factors here, I cannot use BGRABitmap or any other third party components. (BGRABitmap may be very nice but it is a monster - something that I would avoid even if I had a choice) Besides, if there is a bug, why ignore it, can we confirm the issue and maybe address it? You can use the 'lite' version, with less stuff not a big monster.. Quotecore version of BGRABitmap available: by adding the library to the project include path and setting BGRABITMAP_CORE directive (see test/testcore sample) No packages to install, just add path relative or inside your project folder... No need to install bgracontrols, use it with a normal canvas. ---------------------------------------------------------------------- Jonny · 2025-02-06 00:56 Quote from: lainzYou can use the 'lite' version, with less stuff not a big monster.. core version of BGRABitmap available: by adding the library to the project include path and setting BGRABITMAP_CORE directive (see test/testcore sample) No packages to install, just add path relative or inside your project folder... No need to install bgracontrols, use it with a normal canvas. OK, so I took the last snippet of working code that was posted. - Added BGRABitmap to the uses clause. - Add compiler options: -dBGRABITMAP_CORE - Compiled with GTK2 widgetset Code: [Select]$ ls -la --time-style='+' test.gtk2 -rwxr-xr-x 1 user user 2957824  test.gtk2 $ ls -la --time-style='+' bgratest/test.gtk2 -rwxr-xr-x 1 user user 3859232  bgratest/test.gtk2 $ size -G test.gtk2       text       data        bss      total filename    2007720     939693     110312    3057725 test.gtk2 $ size -G bgratest/test.gtk2       text       data        bss      total filename    2717064    1133857     185272    4036193 bgratest/test.gtk2 The executable grows by almost 1Mb, and requires countless source files (I tried working out which ones but gave up). As I shall be deploying to a small embedded system with very limited resources, I am trying to reduce all footprints. BGRABitmap may be great for bigger projects, but for my current use- case it is, unfortunately a monster. ---------------------------------------------------------------------- lainz · 2025-02-06 02:12 I see. Limited hardware is a thing. Hope you can fix the bug.