Recent

Author Topic: [SOLVED] TImagelist load at run time  (Read 1030 times)

torbente

  • Sr. Member
  • ****
  • Posts: 298
    • Noso Main Page
[SOLVED] TImagelist load at run time
« on: January 16, 2021, 01:14:58 am »
I created a TImageList and loaded there all the glyphs for my buttons, using the inspector panel.
Now i want remove the imagelist from the form and do it programatically.

Code: Pascal  [Select][+][-]
  1. GLyphFolder := 'gliphs/'; // the folder where i have the png files
  2. MyGlyphs := TImageLits;
  3. // and to create and fill it
  4. MyGlyphs := Timagelist.Create(form1);
  5. MyGlyphs.Height:=16;
  6. MyGlyphs.Width:=16;

and now? My glyphs files names are numerated, from 1.png to 26.png, so i could load all them with a simple for bucle... if i knew how...
« Last Edit: January 18, 2021, 09:28:33 pm by torbente »
Noso Cryptocurrency Main Developer
https://github.com/DevTeamNoso/NosoWallet

jamie

  • Hero Member
  • *****
  • Posts: 4199
Re: TImagelist load at run time
« Reply #1 on: January 16, 2021, 02:44:39 am »
Create a Tbitmap and use that as the intermedia load from file..


And in a loop...

ImageLIst.AddMasked(ThebitMapFromFile, theTransparentColor);


if you don't have a transparent color, in other words it's a solid image... then ImageList.Add(TheBitMapFromFile, NIl);


As for the file name you need to control the name using the IntTostr(ANumber)+'.bmp';

Etc
The only true wisdom is knowing you know nothing

Handoko

  • Hero Member
  • *****
  • Posts: 4081
  • My goal: build my own game engine using Lazarus
Re: TImagelist load at run time
« Reply #2 on: January 16, 2021, 04:16:20 am »
I wrote a simple demo, try it:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, StdCtrls, Buttons, ExtCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     BitBtn1: TBitBtn;
  16.     BitBtn2: TBitBtn;
  17.     BitBtn3: TBitBtn;
  18.     BitBtn4: TBitBtn;
  19.     Button1: TButton;
  20.     procedure Button1Click(Sender: TObject);
  21.     procedure FormCreate(Sender: TObject);
  22.   end;
  23.  
  24. var
  25.   Form1: TForm1;
  26.  
  27. implementation
  28.  
  29. {$R *.lfm}
  30.  
  31. { TForm1 }
  32.  
  33. procedure TForm1.Button1Click(Sender: TObject);
  34. var
  35.   anImageList: TImageList;
  36.   anImage:     TImage;
  37. begin
  38.   Button1.Enabled    := False;
  39.  
  40.   anImageList        := TImageList.Create(Self); // Let the form free it
  41.   anImage            := TImage.Create(nil);
  42.   anImageList.Width  := 60;
  43.   anImageList.Height := 67;
  44.  
  45.   // Load pictures into image list
  46.   anImage.Picture.LoadFromFile('gloves.png') ;
  47.   anImageList.Add(anImage.Picture.Bitmap, nil);
  48.   anImage.Picture.LoadFromFile('hood.png') ;
  49.   anImageList.Add(anImage.Picture.Bitmap, nil);
  50.   anImage.Picture.LoadFromFile('robe.png') ;
  51.   anImageList.Add(anImage.Picture.Bitmap, nil);
  52.   anImage.Picture.LoadFromFile('sandals.png') ;
  53.   anImageList.Add(anImage.Picture.Bitmap, nil);
  54.  
  55.   // Button1
  56.   BitBtn1.Images     := anImageList;
  57.   BitBtn1.ImageIndex := 0;
  58.   BitBtn1.Caption    := '';
  59.  
  60.   // Button2
  61.   BitBtn2.Images     := anImageList;
  62.   BitBtn2.ImageIndex := 1;
  63.   BitBtn2.Caption    := '';
  64.  
  65.   // Button3
  66.   BitBtn3.Images     := anImageList;
  67.   BitBtn3.ImageIndex := 2;
  68.   BitBtn3.Caption    := '';
  69.  
  70.   // Button4
  71.   BitBtn4.Images     := anImageList;
  72.   BitBtn4.ImageIndex := 3;
  73.   BitBtn4.Caption    := '';
  74.  
  75.   // Cleaning up
  76.   anImage.Free;
  77.   {anImageList.Free;}   // <--- Do not free the imagelist
  78. end;
  79.  
  80. procedure TForm1.FormCreate(Sender: TObject);
  81. begin
  82.   BitBtn1.Caption := 'nothing';
  83.   BitBtn2.Caption := 'nothing';
  84.   BitBtn3.Caption := 'nothing';
  85.   BitBtn4.Caption := 'nothing';
  86. end;
  87.  
  88. end.

Now i want remove the imagelist from the form and do it programatically.

No, don't do it. Removing the image list will cause the images disappeared from the buttons. You can try it on the demo, it is on the line #77.

I used TImage instead of TBitmap (as mentioned by jamie) because TBitmap cannot load png files directly. If the picture format is supported, you should use TBitmap, which is more lightweight.

If you want to support transparent bitmap, which is not shown on the demo. You need to use Mask, which is already explained by jamie.

Note:

The demo is for showing how to load images to a image list and how to set the images to buttons. If it is my project, I will do it differently.

Source of the pictures are from OpenGameArt, all are licensed in CC0.

torbente

  • Sr. Member
  • ****
  • Posts: 298
    • Noso Main Page
Re: TImagelist load at run time
« Reply #3 on: January 16, 2021, 05:34:12 am »
@handoko
I mean i removed it from the form1, i want all my components to be created programatically at run time.

@jamie
Yes, i have transparent pngs for my buttons.

Im trying to figure out a function that adds the image of the filename in the imagelist

Code: Pascal  [Select][+][-]
  1. MyGlyphs := TImageLits;
  2. Procedure AddImage(filename:string);
  3. var
  4.   srcBmp: TBitmap;
  5.   picture: TPicture;
  6. begin
  7.   Picture := TPicture.Create;
  8.   try
  9.     Picture.LoadFromFile(Filename);
  10.     SrcBmp := TBitmap.Create;
  11.     SrcBmp.Assign(Picture.Graphic);
  12.     MyGlyphs.Add(SrcBmp, nil);
  13.   finally
  14.     Picture.Free;
  15.   end;
  16. end;
Function take from here: https://forum.lazarus.freepascal.org/index.php?topic=31915.0

But this not keeps the transparent color. :'(
Noso Cryptocurrency Main Developer
https://github.com/DevTeamNoso/NosoWallet

lucamar

  • Hero Member
  • *****
  • Posts: 3607
Re: TImagelist load at run time
« Reply #4 on: January 16, 2021, 05:55:54 am »
But this not keeps the transparent color. :'(

As Jamie explained, instead of:
Code: Pascal  [Select][+][-]
  1.     MyGlyphs.Add(SrcBmp, nil);
you should use:
Code: Pascal  [Select][+][-]
  1.     MyGlyphs.AddMasked(SrcBmp, MaskColor);
where MaskColor is the color that should be regarded as "transparent".

Only use TImageList.Add() if either:
  • your images are fully opaque (using Nil as mask), or
  • you have two images: the normal one and a "mask" representing the transparent parts, like old style Windows icons.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.10/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

trev

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1282
  • Former Delphi 1-7, 10.2 User
Re: TImagelist load at run time
« Reply #5 on: January 16, 2021, 06:48:19 am »
Handoko's code adapted with ifdefs to also work under macOS:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. {$IFDEF Darwin}
  6. {$modeswitch objectivec1}
  7. {$ENDIF}
  8.  
  9. interface
  10.  
  11. uses
  12.   Classes, SysUtils, Forms, Controls, StdCtrls, Buttons, ExtCtrls,
  13.   {$IFDEF Darwin}
  14.   CocoaAll,   // Needed for NSBundle
  15.   CocoaUtils  // Needed for NSStringToString
  16.   {$ENDIF}
  17.   ;
  18.  
  19. type
  20.  
  21.   { TForm1 }
  22.  
  23.   TForm1 = class(TForm)
  24.     BitBtn1: TBitBtn;
  25.     BitBtn2: TBitBtn;
  26.     BitBtn3: TBitBtn;
  27.     BitBtn4: TBitBtn;
  28.     Button1: TButton;
  29.     procedure Button1Click(Sender: TObject);
  30.     procedure FormCreate(Sender: TObject);
  31.   end;
  32.  
  33. var
  34.   Form1: TForm1;
  35.  
  36. implementation
  37.  
  38. {$R *.lfm}
  39.  
  40. { TForm1 }
  41.  
  42. {$IFDEF Darwin}
  43. // Images are located in the application bundle's Resources directory
  44. Function GetPathForImageResource(Image: NSString) : string;
  45. begin
  46.   Result := NSStringToString(NSBundle.mainBundle.pathForImageResource(Image));
  47. end;
  48. {$ENDIF}
  49.  
  50. procedure TForm1.Button1Click(Sender: TObject);
  51. var
  52.   anImageList: TImageList;
  53.   anImage:     TImage;
  54. begin
  55.   Button1.Enabled    := False;
  56.  
  57.   anImageList        := TImageList.Create(Self); // Let the form free it
  58.   anImage            := TImage.Create(nil);
  59.   anImageList.Width  := 60;
  60.   anImageList.Height := 67;
  61.  
  62.   // Load pictures into image list
  63.   {$IFDEF Darwin}
  64.   anImage.Picture.LoadFromFile(GetPathForImageResource(NSStr('gloves.png')));
  65.   {$ELSE}
  66.   anImage.Picture.LoadFromFile('gloves.png');
  67.   {$ENDIF}
  68.   anImageList.Add(anImage.Picture.Bitmap, nil);
  69.   {$IFDEF Darwin}
  70.   anImage.Picture.LoadFromFile(GetPathForImageResource(NSStr('hood.png')));
  71.   {$ELSE}
  72.   anImage.Picture.LoadFromFile('hood.png') ;
  73.   {$ENDIF}
  74.   anImageList.Add(anImage.Picture.Bitmap, nil);
  75.   {$IFDEF Darwin}
  76.   anImage.Picture.LoadFromFile(GetPathForImageResource(NSStr('robe.png')));
  77.   {$ELSE}
  78.   anImage.Picture.LoadFromFile('robe.png') ;
  79.   {$ENDIF}
  80.   anImageList.Add(anImage.Picture.Bitmap, nil);
  81.   {$IFDEF Darwin}
  82.   anImage.Picture.LoadFromFile(GetPathForImageResource(NSStr('sandals.png')));
  83.   {$ELSE}
  84.   anImage.Picture.LoadFromFile('sandals.png') ;
  85.   {$ENDIF}
  86.   anImageList.Add(anImage.Picture.Bitmap, nil);
  87.  
  88.   // Button1
  89.   BitBtn1.Images     := anImageList;
  90.   BitBtn1.ImageIndex := 0;
  91.   BitBtn1.Caption    := '';
  92.  
  93.   // Button2
  94.   BitBtn2.Images     := anImageList;
  95.   BitBtn2.ImageIndex := 1;
  96.   BitBtn2.Caption    := '';
  97.  
  98.   // Button3
  99.   BitBtn3.Images     := anImageList;
  100.   BitBtn3.ImageIndex := 2;
  101.   BitBtn3.Caption    := '';
  102.  
  103.   // Button4
  104.   BitBtn4.Images     := anImageList;
  105.   BitBtn4.ImageIndex := 3;
  106.   BitBtn4.Caption    := '';
  107.  
  108.   // Cleaning up
  109.   anImage.Free;
  110.   {anImageList.Free;}   // <--- Do not free the imagelist
  111. end;
  112.  
  113. procedure TForm1.FormCreate(Sender: TObject);
  114. begin
  115.   BitBtn1.Caption := 'nothing';
  116.   BitBtn2.Caption := 'nothing';
  117.   BitBtn3.Caption := 'nothing';
  118.   BitBtn4.Caption := 'nothing';
  119. end;
  120.  
  121. end.
Lazarus 2.1 r64368 FPC 3.3.1 r48100 macOS 10.14.6 Xcode 11.3.1
Lazarus 2.1 r64455 3.3.1 r48839   macOS 11.2.2 aarch64 Xcode 12.4
Lazarus 2.1 r61574 3.3.1 r42318 FreeBSD 12.1 amd64 VMware VM
Lazarus 2.1 r61574 3.0.4 Ubuntu 20.04 Parallels VM
Lazarus 2.0.10 3.2.0 Win10 Parallels VM

GAN

  • Sr. Member
  • ****
  • Posts: 316
Re: TImagelist load at run time
« Reply #6 on: January 16, 2021, 06:51:25 am »


Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, Buttons, ExtCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     BitBtn1: TBitBtn;
  16.     BitBtn2: TBitBtn;
  17.     BitBtn3: TBitBtn;
  18.     ImageList1: TImageList;
  19.     procedure FormCreate(Sender: TObject);
  20.   private
  21.     procedure LoadImages;
  22.   public
  23.  
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.  
  29. implementation
  30.  
  31. {$R *.lfm}
  32.  
  33. { TForm1 }
  34.  
  35. procedure TForm1.FormCreate(Sender: TObject);
  36. begin
  37.   LoadImages;
  38. end;
  39.  
  40. procedure TForm1.LoadImages;
  41. var
  42.   imagen:TImage;
  43.   i:Integer;
  44. begin
  45.   imagen:=TImage.Create(nil);
  46.   imagen.Picture.Clear;
  47.   ImageList1.Clear;
  48.  
  49.   imagen.Picture.LoadFromFile(Application.Location+'1.png');
  50.   ImageList1.Insert(0,imagen.Picture.Bitmap,nil);
  51.   ImageList1.GetBitmap(0,BitBtn1.Glyph);
  52.   imagen.Picture.Clear;
  53.  
  54.   imagen.Picture.LoadFromFile(Application.Location+'2.png');
  55.   ImageList1.Insert(1,imagen.Picture.Bitmap,nil);
  56.   ImageList1.GetBitmap(1,BitBtn2.Glyph);
  57.   imagen.Picture.Clear;
  58.  
  59.   imagen.Picture.LoadFromFile(Application.Location+'3.png');
  60.   ImageList1.Insert(2,imagen.Picture.Bitmap,nil);
  61.   ImageList1.GetBitmap(2,BitBtn3.Glyph);
  62.   imagen.Picture.Clear;
  63.  
  64.   FreeAndNil(imagen);
  65. end;
  66.  
  67.  
  68. end.
  69.      

Attached project.
Lazarus 2.0.8 FPC 3.0.4 Linux Mint Mate 19.3
Zeos 7̶.̶2̶.̶6̶ 7.1.3a-stable - Sqlite 3.32.3

https://searchlazarus.blogspot.com/

torbente

  • Sr. Member
  • ****
  • Posts: 298
    • Noso Main Page
Re: TImagelist load at run time
« Reply #7 on: January 16, 2021, 11:12:09 am »
Quote
where MaskColor is the color that should be regarded as "transparent".

And how i could get that color? Somehow, imagelist detect it automatically when i add the files manually using the object inspector.

Could be REALLY usefull:

imagelist.AddFromFile.pngmasked(filename) // using top left pixel as transparent
imagelist.AddFromFile.png(filename)
imagelist.AddFromFile.bmp(filename)
an so on...
Noso Cryptocurrency Main Developer
https://github.com/DevTeamNoso/NosoWallet

wp

  • Hero Member
  • *****
  • Posts: 8110
Re: TImagelist load at run time
« Reply #8 on: January 16, 2021, 11:45:06 am »
Since your images are png there is no need to use TBitmap, better to use TPortableNetworkGraphic because it automatically handles the alpha channel transparency correctly. The TImageList.Add requires the images as a TCustomBitmap - this is the ancestor of TBitmap and TPortableNetworkGraphic. Therefore you can directly add pngs to the image list this way without any lossy conversions.
Code: Pascal  [Select][+][-]
  1. var
  2.   png: TCustomBitmap;
  3. ...
  4.   png := TPortableNetworkGraphic.Create;
  5.   try
  6.     png.LoadFromFile('images/7.png');
  7.     FImageList.Add(png, nil);
  8.   finally
  9.     png.Free;
  10.   end;

The next point to be mentioned is that the idea of loading all images at runtimes requires that all images must be available when the program runs which makes distribution of the program much more complicated. It's better to add the images to the program resources which are linked into the binary, and to add them to the imagelist from there. Usually I create a resource file by using LazRes (which you maybe still have to compile from its sources in folder tools of your Lazarus installation). Here is a typical batchfile to create a resource "images.res" (you must adapt the path to LazRes.exe to your folder structure...):

Code: [Select]
set LAZRES_CMD=C:\Lazarus\lazarus-trunk_fpc-3.0.4\tools\lazres.exe
dir /b *.png > filelist.txt
%LAZRES_CMD% ../images.res @filelist.txt

This creates a file images.res with all png files found in the current folder. In the resource file, the images are named like their files, but without the extension. When this batch file is stored in and executed from the images folder which I assume to be a subfolder of the Pascal source the images.res is written to the source folder. Then you must add a line {$R images.res} to the project file (don't add it to a second file!). Finally you call ImageList.AddResourceName with the name of the resource to add the image to the image list:

Code: Pascal  [Select][+][-]
  1.   for i := 1 to 6 do
  2.     FImageList.AddResourceName(HINSTANCE, IntToStr(i));
This assumes that the images are named '1', '2', ..., '6'.

Please see the attached demo.

« Last Edit: January 16, 2021, 12:49:55 pm by wp »
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

jamie

  • Hero Member
  • *****
  • Posts: 4199
Re: TImagelist load at run time
« Reply #9 on: January 16, 2021, 06:10:15 pm »
Somewhere around here I need to find it, it's old Delphi documentation, that the lower left corner defines the transparent color if you use a default method of assignment, that is if the image is going to be transparent..

 So when drawing transparent specifically Delphi uses the lower left corner for button icons etc..

 So why does the TimageList follow that same rule here?

 Maybe it does I don't know it but using the Imagelist.AddMasked(Image ,clDefault) to me would indicate to use the lower left corner  of the image.

 using clNone means no mask and anything else should be what ever the color was specified.

 I don't get the use of png's desire of using its alpha channel?

 This most likely is the leading cause of why I have so much trouble populating a TimageList with proper transparency functionality...
I always end up reworking the image to some different resolution to get it working , like 16 colors.
The only true wisdom is knowing you know nothing

wp

  • Hero Member
  • *****
  • Posts: 8110
Re: TImagelist load at run time
« Reply #10 on: January 16, 2021, 06:26:07 pm »
Don't ask me about the old Delphi method of pseudo-transparency, it never worked when I expected that it should. Why were there so many DrawTransparent painting routines out there to work around some issues?

When I need transparency I use png images, they even can fade an image into the background which the old method cannot. Unlike old Delphi Lazarus can handle png in the same way as bmp.
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

jamie

  • Hero Member
  • *****
  • Posts: 4199
Re: TImagelist load at run time
« Reply #11 on: January 16, 2021, 08:39:53 pm »
I never had any issues with the Delphi's implementation of the TImagelist as for Drawing the image transparently selectively.
 The issues stem mostly from problems of loading random images and some pngs which I like to store due to size just do not work!

I've had to use a paint program to convert them over to something simple like a BMP..


 Don't worry on my account, I know how to work around …



The only true wisdom is knowing you know nothing

torbente

  • Sr. Member
  • ****
  • Posts: 298
    • Noso Main Page
Re: TImagelist load at run time
« Reply #12 on: January 17, 2021, 12:29:01 pm »
Quote
The next point to be mentioned is that the idea of loading all images at runtimes requires that all images must be available when the program runs which makes distribution of the program much more complicated.
So when a TImageList is placed in the form and filled using the GUI,it does something similar with those pictures (including the files data in the exe file as resources) ? I already tested with a simple project and i was able o run it separately without the images.
Noso Cryptocurrency Main Developer
https://github.com/DevTeamNoso/NosoWallet

wp

  • Hero Member
  • *****
  • Posts: 8110
Re: TImagelist load at run time
« Reply #13 on: January 17, 2021, 12:37:42 pm »
When you add an image to the ImageList in Lazarus design mode the image data are stored in the lfm file so that the original image is no longer needed. Although different in detail it is the same effect as working with resource files (in fact, the lfm is some kind of resource): the image data are stored inside the binary of the application.
« Last Edit: January 17, 2021, 12:56:34 pm by wp »
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

lucamar

  • Hero Member
  • *****
  • Posts: 3607
Re: TImagelist load at run time
« Reply #14 on: January 17, 2021, 12:56:38 pm »
When you add an image to the ImageList in Lazarus design mode the image data are stored in the lfm file [...]

which itself is included as a resource in the executable, so yes, it does "something similar" with the images ;)
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.10/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

 

TinyPortal © 2005-2018