Lazarus

Programming => Graphics and Multimedia => Graphics => Topic started by: lainz on October 17, 2020, 11:05:20 pm

Title: ImageList SVG
Post by: lainz on October 17, 2020, 11:05:20 pm
Hi! With @circular we want to make a SVG / LZP image list.

We need a bit of help.

 Retina display: we have already a solution to this. But we need to pass another parameter to determine the real size of the bitmap.
 As well we drawn the image on the fly, so there is in memory only the SVG file. All the resolutions are drawn when needed.

The idea is to make if possible backward compatible. And we need our own IDE editor since the current one will have no sense since we need to load only svg and lzp images.

If I remember correctly the user wp did most of the new cool changes like multi resolution? So if you can please help us to understand how we can integrate or code.

Basically I think we'll be doing our own draw method targeting any desired size. The we need to integrate with current methods.

Help  :)
Title: Re: ImageList SVG
Post by: winni on October 17, 2020, 11:15:19 pm
Hi!

Bravo lainz & circular!

This is realy needed.

For a lot a users the multi resolution imagelist is very confusing.
We had the discussion again some days ago in the german forum.

A SVG ImageList - that rocks!

Good luck!

Winni


Title: Re: ImageList SVG
Post by: wp on October 17, 2020, 11:39:33 pm
If I remember correctly the user wp did most of the new cool changes like multi resolution? So if you can please help us to understand how we can integrate or code.
No, the imagelist as well as the high-dpi support were written by Ondrej Pokornej, I've seen him in the forum occasionally, but you should probably contact him directly by PM.

Ideally the svg image list should be compatible to the current imagelist and be independent of BGRA. I don't know if this is possible...

There are a lot of virtual methods and properties depending on the ppi of the system, e.g.

Code: Pascal  [Select][+][-]
  1.     procedure DrawForPPI(ACanvas: TCanvas; AX, AY, AIndex: Integer;
  2.       AImageWidthAt96PPI, ATargetPPI: Integer; ACanvasFactor: Double; AEnabled: Boolean = True); overload;
  3.     procedure DrawForPPI(ACanvas: TCanvas; AX, AY, AIndex: Integer;
  4.       AImageWidthAt96PPI, ATargetPPI: Integer; ACanvasFactor: Double; ADrawEffect: TGraphicsDrawEffect); overload;
  5.  
  6.   property
  7.     property Height: Integer read FHeight write SetHeight default 16;
  8.     property HeightForPPI[AImageWidth, APPI: Integer]: Integer read GetHeightForPPI;
  9.     property HeightForWidth[AWidth: Integer]: Integer read GetHeightForWidth;
  10.     property Width: Integer read FWidth write SetWidth default 16;
  11.     property WidthForPPI[AImageWidth, APPI: Integer]: Integer read GetWidthForPPI;
  12.     property SizeForPPI[AImageWidth, APPI: Integer]: TSize read GetSizeForPPI;  
  13.  

In Ondrej's implementation, every control using an ImageList has a property ImagesWidth which defines the image width needed by the control at 96ppt. (by default, this value is 0, which means that the image width is taken from the Width property of the ImageList). This way one control can extract "small" images, another control "large" images from the same image list.
Title: Re: ImageList SVG
Post by: circular on October 17, 2020, 11:53:41 pm
Our image list won't be independent from BGRABitmap for sure. We want to draw SVG as well as LZP files (which can be vectorial as well). Though we would like it to be compatible with TCustomImageList so that it can be used in existing components.
Title: Re: ImageList SVG
Post by: lainz on October 18, 2020, 12:36:03 am
Yes it will be part of bgracontrols as well. Or lazpaint controls if it requires some lazpaint files.

I don't see the point of not using bgrabitmap since its mature. Well tested and works in all major operating systems.

If we need it installed with lazarus (if that is the reason of don't use BGRA) why not include bgrabitmap into it finally  %)

Edit: I contacted Ondrej
Title: Re: ImageList SVG
Post by: lainz on October 18, 2020, 03:12:17 am
I tried with the actual release of the TCustomImageList and seems that I can't override almost nothing. The virtual methods are useless. I don't blame no one for that, is hard sometimes to define what can be overriden and what not..
Title: Re: ImageList SVG
Post by: circular on October 18, 2020, 04:22:59 am
The other option is to fill the image list with images at some definition. Problem is we don't know the scaling factor before the controls are actually created. When the OnShow event of the form is triggered, then the scaling factor is known. So maybe fill the list at that moment.

But it would be much simpler to create the image when they are drawn, because at this point we know the scale. At least I suppose we would need GetResolution method to be virtual, so we can override it. If a resolution is queried that does not exist already, then we could create it at this point, computing all images for this resolution.

At the moment, the only working solution I can see is to populate the image list with images with the wanted size without canvas scale and double the size as well. With the Scale property to True if I understand correctly. Or am I missing something?
Title: Re: ImageList SVG
Post by: lainz on October 18, 2020, 04:37:06 am
Good idea. I like the idea to add the resolutions from the SVG at runtime. The code is more simple, and it can work as well when adding images by code. The image is computed only once not every time it needs to be drawn as well.

I think it will be enough since anyways the wanted image size is fixed in some point, the Width property of the ImageList.

Edit: I changed my mind, Is compatible if just you can plug it and work with current image lists, no need of fancy IDE plugin, just adding images by code is good enough. And if we need to trigger some event in order to work, because is not automatic, no problem at all as well, just write some wiki explaining it and that' all.

Adding a single line of code OnShow is better than rethinking it again for the IDE plugin or because we need to load the images with code.

I prefer anyways storing in project options > resources the stuff, and load with LoadFromResource... methods.

Just my opinion, the code will be more easy to do and it will work with a bit of effort of putting a single line of code in the OnShow event.
Title: Re: ImageList SVG
Post by: Ondrej Pokorny on October 18, 2020, 09:49:24 am
The needed resolutions are generated at run-time in:
Code: Pascal  [Select][+][-]
  1. TCustomImageListResolutions.GetImageLists
  2. -->
  3. procedure TCustomImageListResolution.AddImages(
  4.   AValue: TCustomImageListResolution);
  5.  

You need your own TCustomImageListResolution class (probably TSVGImageListResolution) that will override the AddImages method (make it virtual protected) and that will generate the bitmap images from the SVG list.

You need to register the new class in TSVGImageList.GetResolutionClass (overridden TCustomImageList.GetResolutionClass).

---
Just work on it, modify the LCL image list code as you need (shouldn't be too much). I will review and apply the patches.

---

As for the SVG imagelist itself. It's a good idea and if it will be based on RGBA then it should be added into the RGBA project/package, or its own. Everybody can install it easily with the OPM.
Title: Re: ImageList SVG
Post by: PascalDragon on October 18, 2020, 11:52:45 am
A SVG ImageList - that rocks!

Especially for small icon sizes (e.g. 16x16 or 24x24) using handmade icons results in much better and cleaner icons than relying on some automatic scaling cause this way one can easily discard details that aren't necessary in these small resolutions. Or one can make sure that indeed one pixel wide lines are used instead of lines that are essentially subpixel thanks to the scaling.
Title: Re: ImageList SVG
Post by: Ondrej Pokorny on October 18, 2020, 12:14:07 pm
A SVG ImageList - that rocks!

Especially for small icon sizes (e.g. 16x16 or 24x24) using handmade icons results in much better and cleaner icons than relying on some automatic scaling cause this way one can easily discard details that aren't necessary in these small resolutions. Or one can make sure that indeed one pixel wide lines are used instead of lines that are essentially subpixel thanks to the scaling.

SVG supports responsive drawing - i.e. you can define different layers/images for different resolutions within one SVG file. The question is if it is supported by RGBA.

Anyway:
Actually it would be a good idea to add a function(event handler) to TImageList to generate icon resultions externally. In that case you could add bitmap resolutions for 16px and 24px normally (PNGs) and higher resolutions could be generated from SVG.

That would be quite easy to do and with this a normal TImageList would support SVG icons itself - just by calling an event handler to generate the needed image resolutions.

In that case a TSVGImageList would just need to store a list for the SVG icons and override the rendering of the bigger resolutions. Simple as that.
Title: Re: ImageList SVG
Post by: circular on October 18, 2020, 02:56:13 pm
Nope responsive drawing isn't handled by BGRA. Though I think it is ok if we design the icons to look ok at low resolutions.

For now, I have found this working solution, where GetUnscaledIcons returns an ImageList with images of the requested size (or compute them by stretching them if necessary). On MacOS basically I create both resolutions anyway. Is it how this is supposed to be done with current TImageList?
Code: Pascal  [Select][+][-]
  1.     unscaled := GetUnscaledIcons(ASize);
  2.     retina := GetUnscaledIcons(ASize*2);
  3.     bmpUnscaled := TBitmap.Create;
  4.     bmpRetina := TBitmap.Create;
  5.  
  6.     result := TImageList.Create(nil);
  7.     result.Width := ASize;
  8.     result.Height := ASize;
  9.     result.Scaled := true;
  10.     result.RegisterResolutions([ASize, ASize*2]);
  11.     for i := 0 to unscaled.Count-1 do
  12.     begin
  13.       unscaled.GetBitmap(i, bmpUnscaled);
  14.       retina.GetBitmap(i, bmpRetina);
  15.       result.AddMultipleResolutions([bmpUnscaled, bmpRetina]);
  16.     end;
  17.  
  18.     bmpUnscaled.Free;
  19.     bmpRetina.Free;
  20.     unscaled.Free;
  21.     retina.Free;  

That doesn't work for menus though.
Title: Re: ImageList SVG
Post by: lainz on October 18, 2020, 04:59:02 pm
Looking good, so no need of the SVG image list for now, it's too complex to integrate.  :-\
Title: Re: ImageList SVG
Post by: circular on October 18, 2020, 06:21:12 pm
Going forward, what I really think would be great would be to be able to render the bitmaps on the fly. Basically when the button draws the glyph from the image list, at that point it could be computed with the adequate resolution.
Title: Re: ImageList SVG
Post by: BeanzMaster on October 18, 2020, 06:53:30 pm
Hi on Delphi a really cool open source project exist https://github.com/EtheaDev/SVGIconImageList perhaps He can inspire you

A good article on svg icon https://www.pushing-pixels.org/2011/11/04/about-those-vector-icons.html

Regards
Title: Re: ImageList SVG
Post by: lainz on October 18, 2020, 09:03:49 pm
Going forward, what I really think would be great would be to be able to render the bitmaps on the fly. Basically when the button draws the glyph from the image list, at that point it could be computed with the adequate resolution.

Not sure what is needed to override. In bgracontrols we use getbitmap but as well can be used the draw method...

We definitely need to edit the sources of imagelist for that you need Lazarus trunk installed...
Title: Re: ImageList SVG
Post by: lainz on November 14, 2020, 03:31:01 pm
I've implemented the BGRA SVG Image List, is not compatible with TImageList, but it will be useful for new SVG controls.
Title: Re: ImageList SVG
Post by: circular on November 14, 2020, 04:48:43 pm
I am thinking that we can add a function to populate a TImageList. Like a function with an array of sizes.
Title: Re: ImageList SVG
Post by: lainz on November 14, 2020, 05:41:19 pm
I am thinking that we can add a function to populate a TImageList. Like a function with an array of sizes.

Good idea.
Title: Re: ImageList SVG
Post by: winni on November 14, 2020, 06:01:47 pm
Hi!

I have not yet looked into the code, but more than  an imageList I would like to get an icon of any size on the fly:

Code: Pascal  [Select][+][-]
  1. MyBGRAbitmap := SVGimageList.exportIcon (1,48,48);
  2.  

Winni
Title: Re: ImageList SVG
Post by: lainz on November 15, 2020, 02:21:56 am
Hi!

I have not yet looked into the code, but more than  an imageList I would like to get an icon of any size on the fly:

Code: Pascal  [Select][+][-]
  1. MyBGRAbitmap := SVGimageList.exportIcon (1,48,48);
  2.  

Winni

Good idea, thanks, I will implement it.

Edit: Done, is used like this

Code: Pascal  [Select][+][-]
  1. procedure TForm1.BGRAVirtualScreen1Redraw(Sender: TObject; Bitmap: TBGRABitmap);
  2. var
  3.   bmp: TBGRABitmap;
  4. begin
  5.   bmp := BGRASVGImageList1.Get(0, Bitmap.Width, Bitmap.Height);
  6.   Bitmap.PutImage(0, 0, bmp, dmDrawWithTransparency);
  7.   bmp.Free;
  8. end;
Title: Re: ImageList SVG
Post by: lainz on November 15, 2020, 03:46:57 am
I am thinking that we can add a function to populate a TImageList. Like a function with an array of sizes.

Done

Code: Pascal  [Select][+][-]
  1. BGRASVGImageList1.PopulateImageList(ImageList1, [16, 20, 24, 32]);
Title: Re: ImageList SVG
Post by: circular on November 15, 2020, 11:20:42 pm
Great stuff  :)

In order to have the size fully dynamic depending on the DPI, I suggest that the Width / Height of the image list be interpreted relative to a ReferenceDPI property with default value 96. Then the size in pixels for a certain surface will be Round(Width / ReferenceDPI * TargetDPI) and similarly vertically.

Following this principle, the Draw function with only x and y parameters would be replaced by a function with x and y parameters and a target DPI. Alternatively, we can have a Draw function with x and y and a target control or provide a function that computes the DPI. Indeed, the target DPI can be computed from the PixelsPerInch and GetCanvasScaleFactor.

To let the controls know what is the resulting size, it would be handy to have a function GetSizeOnSurface(ATargetDPI: integer): TSize. So the controls that use the image list can determine the layout to place the image.

What do you think?
Title: Re: ImageList SVG
Post by: lainz on November 15, 2020, 11:28:39 pm
For me it's ok. By default the image list is designed to have at 96, but I'm not sure on macOS. On Windows say I have 16x16, I must provide in the array the values for each zoom factor, or at least the actual, say only 16 or if its 200%, 16 and 32.
TinyPortal © 2005-2018