[SOLVED] Possible file formats for TPicture.LoadFromFile + TPicture.SaveToFile? lazarus / 39-Graphics ---------------------------------------------------------------------- Hartmut · 2025-01-01 15:36 I want to find a list, which graphic formats are possible with TPicture.LoadFromFile and with TPicture.SaveToFile. I found nothing in the documentation https://lazarus- ccr.sourceforge.io/docs/lcl/graphics/tpicture.html and in https://wiki .freepascal.org/Developing_with_Graphics#Loading/Saving_an_image_from/ to_the_disk I only found a very old info: QuoteNote that TPicture does not support all formats supported by Lazarus, as of Lazarus 0.9.31 it supports BMP, PNG, JPEG, Pixmap and PNM From some tests I know, that TPicture.LoadFromFile can read: .PNG .JPG .JPEG .BMP .GIF .ICO .TIF .TIFF but I assume that there are more. From some tests I know, that TPicture.SaveToFile can write: .PNG .JPG .JPEG .BMP .TIF .TIFF but I assume that there are more. Where do I find or who knows a complete list? If the answer differs for Windows and Linux please mention this. Thanks in advance. ---------------------------------------------------------------------- Fibonacci · 2025-01-01 16:11 https://gitlab.com/freepascal.org/lazarus/lazarus/- /blob/main/lcl/include/picture.inc?ref_type=heads#L44 ---------------------------------------------------------------------- Hartmut · 2025-01-01 16:27 Thank you Fibonacci for your quick reply: 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 function GetLocalizedFormatDescription(const GraphicClass: TGraphicClass; const OriginalDescription: string): string;begin  case GraphicClass.ClassName of    'TPortableNetworkGraphic': Result:=rsPortableNetworkGraphic;    'TPixmap': Result:=rsPixmap;    'TBitmap': Result:=rsBitmaps;    'TCursorImage': Result:=rsCursor;    'TIcon': Result:=rsIcon;    'TIcnsIcon': Result:=rsIcns;    'TJpegImage': Result:=rsJpeg;    'TTiffImage': Result:=rsTiff;    'TGIFImage': Result:=rsGIF;    'TPortableAnyMapGraphic': Result:=rsPortablePixmap;    'TTGAImage': Result:=rsTGA;  otherwise  Result:=OriginalDescription;  end;end; This function lists a couple of GraphicClasses, but e.g. from 'TCursorImage' I don't know which file extensions this includes. What I want, is a list of all possible file extensions, as I showed in my 1st post. And how do I know, which of them are writeable and which not? ---------------------------------------------------------------------- domasz · 2025-01-01 18:41 PNG XPM BMP CUR ICO ICNS JPEG JPG JPE TIFF TIF GIF PGM, PBM, PPM, PNM TGA ---------------------------------------------------------------------- wp · 2025-01-01 19:14 There is a function GraphicFilter(TGraphicClass): String in unit Graphics which returns a list of all the registered graphic file extensions supported by the specified TGraphicClass. (It is applied in the TOpenPictureDialog for the general TGraphic classtype to create the graphics file filter list in the dialog.) The returned string contains, in an alternating sequence, file format names and extensions. You could parse this string and extract the extensions. For determining whether an extension is supported for writing you can call the class function TFPCustomImage.FindWriterFromExtension(const extension:String): TFPCustomImageWriterClass of TFPCustomImage (unit FPImage); when it returns nil you know that there is no writer for this extension. Likewise, use TFPCustomImage.FindReaderFromExtension to determine whether the extension can be read. ---------------------------------------------------------------------- Hartmut · 2025-01-03 09:30 Quote from: domasz on January 01, 2025, 07:41:48 pmPNG XPM BMP CUR ICO ICNS JPEG JPG JPE TIFF TIF GIF PGM, PBM, PPM, PNM TGA Thanks domasz for your reply. Unfortunately this list is not correct: JFIF is missing and PNM and TGA are wrong (I tested with Lazarus 3.6.0). Thanks a lot wp for your answer. To use Graphics.GraphicFilter() requires that you have a complete list of all possible graphic classes and you have to do some parsing of the results. To use FPImage.TFPCustomImage.FindWriterFromExtension() requires that you have a list of all possible extensions to check them one by one. With this infos I created the following 2 functions: 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 uses sysutils,Graphics; function getExtensions_TPicture_LoadFromFile(show: boolean): string;   {returns a list of all file extensions, which procedure TPicture.LoadFromFile    can read.    In: show: show all extensions together with their class names to the console?    The list of all existing graphic classes was taken from Lazarus 3.6.0,    constructor TPicFileFormatsList.Create in file    /lazarus/lcl/include/picture.inc}   var s: string;    procedure add(X: TGraphicClass);      {adds the possible file extensions of class 'X' to variable 's'}      var z: string;      begin      z:=X.GetFileExtensions; {get list of extensions, separated by ';'}      z:=StringReplace(z,';',' ',[rfReplaceAll]);      s:=s + ' ' + z;      if show then writeln(X.ClassName, ': ', z);      end;    begin   s:='';   add(TPortableNetworkGraphic);   add(TPixmap);   add(TBitmap);   add(TCursorImage);   add(TIcon);   add(TIcnsIcon);   add(TJpegImage);   add(TTiffImage);   add(TGIFImage);   add(TPortableAnyMapGraphic);    delete(s,1,1); {delete first Blank}   if show then writeln('All Extensions: ', s);   exit(s);   end; The result of this function with Lazarus 3.6.0 is: Code: Text  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TPortableNetworkGraphic: pngTPixmap: xpmTBitmap: bmpTCursorImage: curTIcon: icoTIcnsIcon: icnsTJpegImage: jpeg jpg jpe jfifTTiffImage: tif tiffTGIFImage: gifTPortableAnyMapGraphic: pbm pgm ppmAll Extensions: png xpm bmp cur ico icns jpeg jpg jpe jfif tif tiff gif pbm pgm ppm 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 uses strutils,FPImage; function getExtensions_TPicture_SaveToFile(s: string): string;   {checks the list of file extensions in 's', which of them can be written by    TPicture.SaveToFile() and returns all positive results as a list.    In: s: you can use the result from function getExtensions_TPicture_LoadFromFile}   var X: TFPCustomImageWriterClass;       z,ext: string;       p,i: integer;   begin   z:='';   p:=WordCount(s,[' ']);   for i:=1 to p do      begin      ext:=ExtractWord(i,s,[' ']);      X:=TFPCustomImage.FindWriterFromExtension(ext);      if Assigned(X) then z:=z + ' ' + ext;      end;    delete(z,1,1); {delete first Blank}// writeln('All Extensions: ', z);   exit(z);   end; The result of this function with Lazarus 3.6.0 is: Code: Text  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i All Extensions: png bmp jpeg jpg tif tiff pbm pgm ppm In the next days I will add this code and it's results to the wiki htt ps://wiki.freepascal.org/Developing_with_Graphics#Possible_file_format s_for_TPicture ---------------------------------------------------------------------- wp · 2025-01-03 10:30 Quote from: Hartmut on January 03, 2025, 10:30:12 amTo use Graphics.GraphicFilter() requires that you have a complete list of all possible graphic classes [...] No, you can simply call it for TGraphic because TGraphic the ancestor of all the graphics formats that we are talking about. Please see the attached project for a worked-out sample. ---------------------------------------------------------------------- Hartmut · 2025-01-04 13:02 Quote from: wp on January 03, 2025, 11:30:40 amQuote from: Hartmut on January 03, 2025, 10:30:12 amTo use Graphics.GraphicFilter() requires that you have a complete list of all possible graphic classes [...] No, you can simply call it for TGraphic because TGraphic the ancestor of all the graphics formats that we are talking about. Thanks a lot wp for this new info, I did not know this. Function Graphics.GraphicFilter(TGraphic) returns the complete list of all file extensions (but requires some "wild" parsing) and function Graphics.GraphicFileMask(TGraphic) returns the same extensions list (but parsing is easier). I had a look at the sources and saw, that both functions use local class Graphics.TPicFileFormatsList, which in it's constructor generates a list of all graphic classes which e.g. TPicture.LoadFromFile() can read. But e.g. function Graphics.GraphicExtension(TGraphic) does not return this list. QuotePlease see the attached project for a worked-out sample. I had a look into your 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 type  TAccessKind = (akUnknown, akRead, akWrite); function CreateExtensionsList(AccessKind: TAccessKind): TStringList;var  formats, exts: TStringArray;  ext: String;  i, j: Integer;  readerClass: TFPCustomImageReaderClass;  writerClass: TFPCustomImageWriterClass;begin  Result := TStringList.create;  Result.Sorted := true;  Result.Duplicates := dupIgnore;  formats := GraphicFilter(TGraphic).Split('|');  i := 1;  while i < Length(formats) do  begin    exts := formats[i].Split(';');    for j := 0 to High(exts) do    begin      ext := exts[j];      if ext[1] = '*' then Delete(ext, 1, 1);      case AccessKind of        akUnknown:          Result.Add(ext);        akRead:          begin            readerClass := TFPCustomImage.FindReaderFromExtension(ext);          if readerClass <> nil then Result.Add('%s ---> %s', [ext, readerClass.ClassName]);          end;        akWrite:          begin            writerClass := TFPCustomImage.FindWriterFromExtension(ext);            if writerClass <> nil then Result.Add('%s ---> %s', [ext, writerClass.ClassName]);          end;      end;    end;    inc(i, 2);  end;end; Your parsing of function Graphics.GraphicFilter(TGraphic) is quite tricky: you start array formats[] at index 1 instead of 0, you use only each second item of this array and you skip duplicate extensions to create the list ;-) Your results for AccessKind 'akUnknown' and 'akWrite' are the same which I produced, but for 'akRead' you show only 7 extensions (see screenshot) instead of 16. E.g. '.ico' is missing, but it can definitely be read by TPicture.LoadFromFile(). Have no idea where this difference comes from. ---------------------------------------------------------------------- wp · 2025-01-04 17:04 Quote from: Hartmut on January 04, 2025, 02:02:08 pmFunction Graphics.GraphicFilter(TGraphic) returns the complete list of all file extensions (but requires some "wild" parsing) and function Graphics.GraphicFileMask(TGraphic) returns the same extensions list (but parsing is easier). Ah, good point. I did not see this, it would have saved parsing the file format list Quote from: Hartmut on January 04, 2025, 02:02:08 pmYour parsing of function Graphics.GraphicFilter(TGraphic) is quite tricky: you start array formats[] at index 1 instead of 0, you use only each second item of this array and you skip duplicate extensions to create the list ;-) No, it is quite natural when you know that this list is used by the file dialogs. When you drop a TOpenDialog on a form and open the editor of the Filter property you see a grid with two columns, "Filter name" and "Filter", one row for each format. "Filter Name" is the description which appears in the Dialogs filter box, e.g. 'Photos'. The corresponding extension(s) are listed in column "Filter", separated by semicolon, e.g. '*.jpg;*.jpeg'. When you close the dialog and look at the Filter property directly you see the string 'Photos|*.jpg;*.jpeg'. Or when you add another format, eg. 'Bitmaps' with ext '*.bmp' you'll have a filter text like this: 'Photos|*.jpg;*.jpeg|Bitmaps|*.bmp'. So, descriptions and extensions are alway alternating. All items are separates by '|'. When you split the filter string at the '|' you'll have a string array with Description1, Extensions1, Description2, Extensions2, etc. The first extension is at index 1 - that's why I am start seeking at this index. And I advance with step 2 because I don't need the descriptions. And in order to get the individual extensions a second splitting operation is needed, now at the semicolon which separates the various extension of a format. Quote from: Hartmut on January 04, 2025, 02:02:08 pmI had a look at the sources and saw, that both functions use local class Graphics.TPicFileFormatsList, which in it's constructor generates a list of all graphic classes I know. This would give the easiest access to the list of registered file formats. I don't know why the authors of the Graphics unit decided to lock the list totally from the outside. Quote from: Hartmut on January 04, 2025, 02:02:08 pmYour results for AccessKind 'akUnknown' and 'akWrite' are the same which I produced, but for 'akRead' you show only 7 extensions (see screenshot) instead of 16. E.g. '.ico' is missing, but it can definitely be read by TPicture.LoadFromFile(). Strange... For *.ico, probably the explanation is that it can contain multiple images of different types (PNG and DIB). Therefore, this format is read in a different way from the others. It reads the general header, and only during reading the needed reader classes are created. ---------------------------------------------------------------------- Hartmut · 2025-01-05 10:21 Thanks wp for your long post. Quote from: wp on January 04, 2025, 06:04:02 pmQuote from: Hartmut on January 04, 2025, 02:02:08 pmYour parsing of function Graphics.GraphicFilter(TGraphic) is quite tricky: you start array formats[] at index 1 instead of 0, you use only each second item of this array and you skip duplicate extensions to create the list ;-) No, it is quite natural when you know that this list is used by the file dialogs. I rarely have used the 'Filter' property of TOpenDialog. But I was not aware that function Graphics.GraphicFilter(TGraphic) returns the needed Filter for opening graphic files. Now I know. QuoteQuote from: Hartmut on January 04, 2025, 02:02:08 pmI had a look at the sources and saw, that both functions use local class Graphics.TPicFileFormatsList, which in it's constructor generates a list of all graphic classes I know. This would give the easiest access to the list of registered file formats. I don't know why the authors of the Graphics unit decided to lock the list totally from the outside. I have more often found in the sources useful things, which unfortunately were local or declared private. I don't see the benefit to do so. QuoteQuote from: Hartmut on January 04, 2025, 02:02:08 pmYour results for AccessKind 'akUnknown' and 'akWrite' are the same which I produced, but for 'akRead' you show only 7 extensions (see screenshot) instead of 16. E.g. '.ico' is missing, but it can definitely be read by TPicture.LoadFromFile(). Strange... For *.ico, probably the explanation is that it can contain multiple images of different types (PNG and DIB). Therefore, this format is read in a different way from the others. It reads the general header, and only during reading the needed reader classes are created. Perhaps this is the reason. I assume that you saw, that in total 9 extensions are missing (xpm cur ico icns jpe jfif pbm pgm ppm), not only *.ico.