Problem with resizing images lazarus / 39-Graphics ---------------------------------------------------------------------- Tomi · 2023-07-21 15:28 Hello! I would like draw images in different sizes on a TImage component and later save these in their new size. First, I just try to show a selected image from a ListBox in the size of the TImage, but it always shows me those images in their original size. Here is my piece of code: 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 TForm1.ListBox1Click(Sender: TObject);begin  if ListBox1.ItemIndex<>-1 then  begin    if FileExists(ListBox1.Items.Strings[ListBox1.ItemIndex]) then    begin     Image1.Picture.LoadFromFile(ListBox1.Items.Strings[ListBox1.ItemIn dex]);       Image1.Picture.Bitmap.SetSize(Image1.Width,Image1.Height);       Image1.Canvas.StretchDraw(Image1.ClientRect,Image1.Picture.Graphic);    end;  end;end; ---------------------------------------------------------------------- Handoko · 2023-07-21 17:14 Try this: 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 TForm1.Button1Click(Sender: TObject);var  Tmp: TJPEGImage;begin  Tmp := TJPEGImage.Create;  Tmp.LoadFromFile('test.jpg');  Image1.Canvas.StretchDraw(Image1.ClientRect, Tmp);  Tmp.Free;end; ---------------------------------------------------------------------- wp · 2023-07-21 17:57 There has been a thread on image resizing here recently. Please search the forum. ---------------------------------------------------------------------- KodeZwerg · 2023-07-21 18:00 Quote from: wp on July 21, 2023, 07:57:38 pmThere has been a thread on image resizing here recently. Please search the forum.Maybe he does not know how, so just click here. ---------------------------------------------------------------------- Tomi · 2023-07-22 07:42 Quote from: KodeZwerg on July 21, 2023, 08:00:26 pmMaybe he does not know how, so just click here. Hello KodeZwerg! I tried on the basis of that thread, but my program doesn't resize the images: 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 TForm1.ListBox1Click(Sender: TObject);var bitmap: TBitmap;begin  if ListBox1.ItemIndex<>-1 then  begin    if FileExists(ListBox1.Items.Strings[ListBox1.ItemIndex]) then    begin    bitmap:=TBitmap.Create;      Image1.Picture.LoadFromFile(ListBox1.I tems.Strings[ListBox1.ItemIndex]);      bitmap.SetSize(Image1.Width,Image1.Height);      bitmap.Canvas.StretchDraw(Image1.ClientRect,Image1.Picture.Bitmap);    bitmap.Free;    end;  end;end; ---------------------------------------------------------------------- Tomi · 2023-07-22 07:53 Quote from: Handoko on July 21, 2023, 07:14:24 pmTry this: (...) Thanks, Handoko, I changed your code a bit and now it works correctly. 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 TForm1.ListBox1Click(Sender: TObject);var tmpimage: TImage;begin  if ListBox1.ItemIndex<>-1 then  begin    if FileExists(ListBox1.Items.Strings[ListBox1.ItemIndex]) then    begin    Image1.Picture:=nil; //To prevent mixing images on canvas I clear it      tmpimage:=TImage.Create(self);      tmpimage.Picture.LoadFromF ile(ListBox1.Items.Strings[ListBox1.ItemIndex]);      Image1.Canvas.St retchDraw(Image1.ClientRect,tmpimage.picture.graphic);      tmpimage.Free;    end;  end;end;Otherwise I use this program to manipulate primarily .PNG files. ---------------------------------------------------------------------- KodeZwerg · 2023-07-22 08:15 Quote from: Tomi on July 22, 2023, 09:42:31 amHello KodeZwerg! I tried on the basis of that thread, but my program doesn't resize the images:Hello, of course it does not do what you try. I try to comment your code so you might know whats wrong. Quote from: Tomi on July 22, 2023, 09:42:31 amCode: 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 TForm1.ListBox1Click(Sender: TObject);var bitmap: TBitmap;begin// you check if itemindex in unequal to -1, ok, go on  if ListBox1.ItemIndex<>-1 then  begin// you check if file exists, ok, go on    if FileExists(ListBox1.Items.Strings[ListBox1.ItemIndex]) then  begin// you create a bitmap instance, ok, go on      bitmap:=TBitmap.Create;// you load the checked filename into image1 of your formular      Image1.Picture.LoadFromFile(ListBox1.Items.Strings[ ListBox1.ItemIndex]);// you fail to use safety blocks, try/finally// you tell the bitmap to be same big as image1 is      bitmap.SetSize(Image1.Width,Image1.Height);// you draw on the bitmap a 1:1 copy of image1 for whatever reason      bitmap.Canvas.StretchDraw(Image1.ClientRect,Image1.Picture.Bitmap);// you free the bitmap image without doing anything, so why created at all?      bitmap.Free;    end;  end;end; so i suggest you to use one of the tested methods that you find in that thread to have a correct way for resizing an image, examples about how to use it correctly you also find there. ---------------------------------------------------------------------- Tomi · 2023-07-22 09:01 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 // you draw on the bitmap a 1:1 copy of image1 for whatever reason      bitmap.Canvas.StretchDraw(Image1.ClientRect,Image1.Picture.Bitmap);// you free the bitmap image without doing anything, so why created at all?      bitmap.Free;Yes, I draw the content of Image1 on the bitmap, but I use the StretchDraw function on it, even so the bitmap is not draws the resized image but in its original size. Image1 is a TImage component on the Form and I use it to show the selected images for the user in its width and height sizes. So it is a preview image. ---------------------------------------------------------------------- Handoko · 2023-07-22 09:11 I am not sure what you meant '... except that it shows the previous image." Here I have a fully working demo: 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, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls; type   { TForm1 }   TForm1 = class(TForm)    Button1: TButton;    Image1: TImage;    ListBox1: TListBox;    procedure Button1Click(Sender: TObject);    procedure FormCreate(Sender: TObject);    procedure FormResize(Sender: TObject);    procedure ListBox1Click(Sender: TObject);  end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject);begin  Image1.Anchors := [akTop, akLeft, akBottom, akRight];  ListBox1.AddItem('img1.bmp', nil);  ListBox1.AddItem('img2.jpeg', nil);  ListBox1.AddItem('img3.png', nil);end; procedure TForm1.Button1Click(Sender: TObject);const  Filename = 'export.jpeg';begin  if (Image1.Picture.Width <= 0) then Exit; // Empty  Image1.Picture.SaveToFile(Filename);  ShowMessage('File saved as ' + Filename +'.');end; procedure TForm1.FormResize(Sender: TObject);begin  Image1.Picture.Clear;end; procedure TForm1.ListBox1Click(Sender: TObject);var  TmpBitmap: TCustomBitmap;  Filename: string;begin  Filename := ListBox1.Items.Strings[ListBox1.ItemIndex];  if not(FileExists(Filename)) then Exit;  case Filename.Substring(Filename.Length-4, 4).ToLower of    '.bmp': TmpBitmap := TBitmap.Create;    'jpeg': TmpBitmap := TJPEGImage.Create;    '.png': TmpBitmap := TPortableNetworkGraphic.Create;    else      Exit;  end;  TmpBitmap.LoadFromFile(Filename);  Image1.Canvas.StretchDraw(Image1.ClientRect, TmpBitmap);  TmpBitmap.Free;end; end. Tested on Linux only but it should work on Windows too. The code is good for simple usage only. If performance is important or you need wider file format support, you should consider to use other graphics library: https://wiki.freepascal.org/Graphics_libraries ---------------------------------------------------------------------- KodeZwerg · 2023-07-22 09:57 Quote from: Tomi on July 22, 2023, 11:01:05 amYes, I draw the content of Image1 on the bitmap, but I use the StretchDraw function on it, even so the bitmap is not draws the resized image but in its original size. Image1 is a TImage component on the Form and I use it to show the selected images for the user in its width and height sizes. So it is a preview image. I show you your mistake, I hope that opens your eyes. Quote from: KodeZwerg on July 22, 2023, 10:15:30 amCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i       bitmap.SetSize(Image1.Width,Image1.Height);      bitmap.Canvas.Stretch Draw(Image1.ClientRect,Image1.Picture.Bitmap);Quote from: Tomi on July 22, 2023, 09:53:13 amCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i       tmpimage.Picture.LoadFromFile(ListBox1.Items.Strings[List Box1.ItemIndex]);      Image1.Canvas.StretchDraw(Image1.ClientRect,tmpimage.picture.graphic); In your first code you manipulate the bitmap but do nothing with it (plus that it is not correct) In your second code you manipulate the image and do it also not that correct. Bascicly you just need to play with the properties. 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 TForm1.ListBox1Click(Sender: TObject);begin  if ListBox1.ItemIndex<>-1 then    if FileExists(ListBox1.Items.Strings[ListBox1.ItemIndex]) then      begin        Image1.Stretch := False;        Image1.Picture. LoadFromFile(ListBox1.Items.Strings[ListBox1.ItemIndex]);        Image1.Proportional := True;        Image1.Stretch := True;      end;end;(Just written in here, not tested) ---------------------------------------------------------------------- Tomi · 2023-07-22 09:57 But why lose all .PNGs their transparency? All of these will be black background color: 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 TForm1.ListBox1Click(Sender: TObject);var tmpimage: TImage;begin  if ListBox1.ItemIndex<>-1 then  begin    if FileExists(ListBox1.Items.Strings[ListBox1.ItemIndex]) then    begin    Image1.Picture:=nil;      tmpimage:=TImage.Create(self);      tmpim age.Picture.LoadFromFile(ListBox1.Items.Strings[ListBox1.ItemIndex]);      tmpimage.picture.bitmap.transparent:=true;      Image1.Canvas.StretchDraw(Image1.ClientRect,tmpimage.picture.bitmap);      tmpimage.Free;    end;  end;end; ---------------------------------------------------------------------- Handoko · 2023-07-22 10:07 Quote from: Tomi on July 22, 2023, 11:57:32 amBut why lose all .PNGs their transparency? Not all png files support transparency. You have to set/change the pixel format, only grayscale and alpha, and truecolor and alpha can support transparency. https://en.wikipedia.org/wiki/PNG#Pixel_format As far as I know, changing pixel format isn't as easy as it sound. It will be easier to use libraries specialized for graphics. ---------------------------------------------------------------------- Tomi · 2023-07-22 12:57 Should I do this pixel format changing with an external image editor? Interesting, other programs (i.e. picture viewer in the Windows 10) show correctly transparency of these images, only Lazarus would not support it? ---------------------------------------------------------------------- Tomi · 2023-07-22 13:09 Wow, big thanks, KodeZwerg! The program works correctly with your code, too, and it also shows the transparency correctly. Here is: 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 TForm1.ListBox1Click(Sender: TObject);begin  if ListBox1.ItemIndex<>-1 then  begin    if FileExists(ListBox1.Items.Strings[ListBox1.ItemIndex]) then    begin    Image1.Picture:=nil;      Image1.Stretch:=false;      Image1.Pictur e.LoadFromFile(ListBox1.Items.Strings[ListBox1.ItemIndex]);      Image1.Proportional:=true;      Image1.Stretch:=true;    end;  end;end; Stretch and Proportional are True by default, therefore I didn't care about them. So does the temporary switching also affect the result? Tricky! ---------------------------------------------------------------------- Handoko · 2023-07-22 13:20 Quote from: Tomi on July 22, 2023, 02:57:05 pm... only Lazarus would not support it? It is not Lazarus issue but the graphics library limitation. I haven't gone deep into it, but the Graphics unit has poor support for various image types. You really should use other graphics library if you want to work with graphics. At least in my old project shows, changing it from using Graphics to FPimage, it has better support for loading png and tiff file: https://forum.lazarus.freepascal.org/index.php/topic,35313.msg256719.h tml#msg256719 The whole discussion spanned very long, but in short the previous versions of the program did not support certain types of png and tiff images, reported by J-G. After I used FPimage for loading files, it then had better (or full) support for png and tiff files. Again I have to say, you should consider to use other graphics library if you want to work with graphics. ---------------------------------------------------------------------- lainz · 2023-07-22 13:28 Here a tutorial on how to do resample with BGRABitmap https://wiki.freepascal.org/BGRABitmap_tutorial_2 ---------------------------------------------------------------------- Tomi · 2023-07-22 13:40 Quote from: lainz on July 22, 2023, 03:28:48 pmHere a tutorial on how to do resample with BGRABitmap https://wiki.freepascal.org/BGRABitmap_tutorial_2 Oh, thanks, Lainz! I didn't find these pages so far, but it seems useful. If you only knew how much I browsed on the internet about these in the last days... ---------------------------------------------------------------------- Handoko · 2023-07-22 13:42 The start of the tutorial is here: https://wiki.freepascal.org/BGRABitmap_tutorial BGRABitmap offers many features that can't be found it Graphics unit, like rotation and better file type support. Unfortunately, it is not fully hardware accelerated. I used it in my CATWW, I had to add image caching feature to make it runs smoothly: https://forum.lazarus.freepascal.org/index.php/topic,50989.0.html ---------------------------------------------------------------------- Tomi · 2023-07-22 15:02 And finally, why this code shows only left upper corner of the image instead of resized copy of 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 procedure TForm1.Button2Click(Sender: TObject);var tempimg: TImage;begin  tempimg:=TImage.Create(Self);  tempimg.Stretch:=false;  tempimg.Picture.LoadFromFile(ListBox1.Items.Strings[0]);  tempimg.Proportional:=true;  tempimg.Stretch:=true;  tempimg.picture.B itmap.SetSize(strtoint(Edit1.Text),strtoint(Edit2.Text));  tempimg.picture.SaveToFile('TEST.png');  tempimg.free;end; ---------------------------------------------------------------------- Tomi · 2023-07-23 09:26 I changed on my code a bit and it shows the resized image too, but it doesn't save in this new size, only in their original size, and I don't know, why?  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 TForm1.Button2Click(Sender: TObject);var newimage: TImage;begin  if ListBox1.ItemIndex<>-1 then  begin     newimage:=TImage.Create(Self);     newimage.parent:=Form1;     newimage.autosize:=false;     newimage.Stretch:=false;     newimage.Picture.LoadFromFile(ListBox1.Items.Strings[0]);     newimage.Proportional:=true;     newimage.Stretch:=true;     newimage.Width:=strtoint(Edit1.Text);     newimage.Height:=strtoint(Edit2.Text);     //newimage.Picture.LoadFromFile(ListBox1.Items.Strings[0]);     newimage.Picture.SaveToFile('TEST.png');     //newimage.free; //So it shows the small image on the left top corner.  end;end; ---------------------------------------------------------------------- Handoko · 2023-07-23 09:28 Have you tried my code? It saved in the new sizes, at least in my test in Linux. Quote from: Tomi on July 23, 2023, 11:26:48 am... save in this new size, only in their original size ... I didn't inspect into your issue. But my guess is, what you did wasn't really resize the image data. What it did only showing the stretched data in screen memory. Why? Because for example, you load an image into TImage and do several times resizing for scaling up and down, the image quality will be still maintained. But if you understand the math behind image resizing, scaling up and down is a destructive action to the image data, quality loss will happen. In the TImage case, the quality is kept, that can be done only if the 'real' image data hasn't changed. So if you save the image data, you only save the original data. ---------------------------------------------------------------------- Tomi · 2023-07-23 15:29 Quote from: Handoko on July 23, 2023, 11:28:42 amHave you tried my code? It saved in the new sizes, at least in my test in Linux. Yes, I tried your code a few hours ago and it really save the images in new sizes, but the transparency loses - although the preview shows it in new size and with transparency. But I figured out a possible solution: I copy the content of the preview image onto another image with two for loops and the colors[x,y] function. Where the pixels are transparent, I will not copy the color data, otherwise I will put it onto the other image. But I don't know, what is the color of a transparent background? I think, it is not clWhite. ---------------------------------------------------------------------- Handoko · 2023-07-23 16:07 Quote from: Tomi on July 23, 2023, 05:29:16 pmBut I figured out a possible solution ... Good luck with your experiment. Even if that really works, using for loop to copy data between images' memory is a big no in graphics programming. Sooner or later you will have performance issue. I haven't tried but I know TImage has Transparent property, maybe you need to set it to true: https://lazarus-ccr.sourceforge.io/docs/lcl/extctrls/timage.html Use the right tools to do the right job. TImage, TPicture and TBitmap are for simply storing and showing its image data. They are not for serious image editing purpose. Quote from: Tomi on July 23, 2023, 05:29:16 pm... the colors[x,y] function ... If you really want to access image data pixel by pixel, don't use colors function. Still slow but using ScanLine command will be several times faster. In the link below, find the "Get pixel color" in the Graphics section: https://wiki.freepascal.org/Portal:HowTo_Demos#Graphics That demo will show you what PixelFormat is, how image data being stored in memory,  how to know which pixel is transparent. That demo should work on most png files but it fails on some rare png formats. ---------------------------------------------------------------------- Tomi · 2023-07-25 13:17 Finally I found the solution! The bit depth of the source image was 32 bits, but Lazarus default saves it in 24 bits. Therefore I must set the correct pixelformat in 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 newimage.pixelformat:=pf32bit;The most important thing to place this command before the line contains StretchDraw() function, because if you place it after that function, the result will be an empty image with SaveToFile. Pfff... the life of programmers is not easy, you have to pay attention to many things. ---------------------------------------------------------------------- KodeZwerg · 2023-07-25 13:26 Quote from: Tomi on July 25, 2023, 03:17:12 pmPfff... the life of programmers is not easy, you have to pay attention to many things. Thats why you should learn from given examples, use specific classes for specific formats, that ease your doing. ---------------------------------------------------------------------- Tomi · 2023-07-27 07:59 One more question: I would like get the color depth of the image to set it on another, saved image. I would like use the GetPixelFormat command for this, but it throws an "identifier idents no member "GetPixelFormat"" error message. This is my piece of code: 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 newimage.pixelformat:=oldimage.picture.bitmap.GetPixelFormat;Oldimage is a TImage component. What is wrong with this? ---------------------------------------------------------------------- TRon · 2023-07-27 08:12 Quote from: Tomi on July 27, 2023, 09:59:28 amOne more question: I would like get the color depth of the image to set it on another, saved image. I would like use the GetPixelFormat command for this, but it throws an "identifier idents no member "GetPixelFormat"" error message. This is my piece of code: 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 newimage.pixelformat:=oldimage.picture.bitmap.GetPixelFormat;Oldimage is a TImage component. What is wrong with this? GetPixelFormat function is declared as private somewhere along the way (at TCustomBitmap). Try PixelFormat property instead. ---------------------------------------------------------------------- Tomi · 2023-07-27 08:21 Quote from: TRon on July 27, 2023, 10:12:34 amGetPixelFormat function is declared as private somewhere along the way (at TCustomBitmap). Try PixelFormat property instead. Thanks for your advice, TRon! I tried it and now it works. ---------------------------------------------------------------------- Tomi · 2023-07-27 14:21 Then, ladies and gentlemen, here is my little program to manipulate images. It is my first real user program, so this is not a game. But maybe useful for other people, too. If you want resize and rename big amount of images at the same time, it will came in handy. Link to download: https://itsmprog.000webhostapp.com/lazjatekok/kepmeretezo.zip