Dynamisch erzeugte Bilder Dynamische Erzeugung von Bildern im geräteabhängigen angepassten Formaten Da es mir widerstrebt Bilder in unzähligen Formaten auf der Festplatte zu speichern und diese Sammlung dann jedes mal wenn ein Gerät mit anderen Abmessungen erscheint zu erweitern, ist eine Lösung von Nöten die Bilder in minimaler Zeit skaliert. Da das Laden eines kompletten Bildes in ein Image Objekt und es dann zu skalieren sehr zeitaufwändig ist, muss je nach Zielgröße entschieden werden. Wird z.B. nur eine kleine Voransicht benötigt, kann sie aus einer internen Voransicht (EXIF Thumbnail) generiert werden. Gerade kleine Voransichten treten gehäuft auf (z.B. in Bildergalerien), größere eher in geringerer Anzahl pro Seitenaufruf, gerade darum ist ein spezielles Augenmerk auch darauf zu Richten, schon beim Entwurf gewisse Schwellwerte zu beachten. Im Regelfall hat so ein Thumbnail Abmessungen von 160x120 Pixel! Wird eine Bild mit größeren Abmessungen benötigt aber dennoch viel kleiner als das Original, wird nicht die komplette Auflösung benötigt und so kann es auch reichen nur ein Teil der Auflösung in den Speicher zu laden und dann die Skalierung vorzunehmen. Zur Zeit (Stand 01.11.2023) existiert meines Erachtens ein Fehler in der Datei fpreadjpeg.pas des Free Pascal Package fcl-image, der das fehlerfreie Scalieren verhindert, hier folgt nun ein Patch für das Problem. Index: packages/fcl-image/src/fpreadjpeg.pas =================================================================== --- packages/fcl-image/src/fpreadjpeg.pas (Revision 30e7bfb) +++ packages/fcl-image/src/fpreadjpeg.pas (Arbeitskopie) @@ -258,6 +258,19 @@ c: word; Status,Scan: integer; ReturnValue,RestartLoop: Boolean; + S: TSize; + + function TranslateSize(const Sz: TSize): TSize; + begin + case FOrientation of + eoUnknown, eoNormal, eoMirrorHor, eoMirrorVert, eoRotate180: Result := Sz; + eoMirrorHorRot270, eoRotate90, eoMirrorHorRot90, eoRotate270: + begin + Result.Width := Sz.Height; + Result.Height := Sz.Width; + end; + end; + end; procedure InitReadingPixels; var d1,d2:integer; @@ -465,7 +478,9 @@ jpeg_start_decompress(@FInfo); - Img.SetSize(FWidth,FHeight); +// Img.SetSize(FWidth,FHeight); + S := TranslateSize(TSize.Create(FInfo.output_width,FInfo.output_height)); + Img.SetSize(S.Width, S.Height); GetMem(SampArray,SizeOf(JSAMPROW)); GetMem(SampRow,FInfo.output_width*FInfo.output_components); In der folgenden Unit ImgUtils habe ich nun die benötigten Funktionen zusammengefasst, bei der Entwicklung habe ich besonders darauf geachtet das die von den Browser unterstützten Formate (JPEG, PNG, GIF) berücksichtigt werden, aber auch am Beispiel von BMP eine Konvertierung eingebaut ist. Das Ermitteln der Abmessungen wurde bewusst in separate Funktionen auf der Basis einer Stream-Analyse ausgelagert, da das der Weg über die speziellen Bild-Klassen zu zeitaufwändig ist. Um ein Thumbnail aus einer JPEG-Datei zu lesen hätte sicherlich auch der Weg über eine EXIF Komponente gewählt werden können, jedoch muss ich auch an dieser Stelle wieder auf die Geschwindigkeit zurück kommen, das gezielte auslesen auf Stream-Basis ist natürlich um einiges schneller. Auch in der eigentlichen Funktion zum Skalieren der Bilder habe ich nur native Möglichkeiten genutzt nicht auf System eigene, ich nutze Free Pascal Standard Units aus dem Package fcl-image und eine eigene Unit FPWriteGIF("https://www.gocher.me/FPWriteGIF") die fehlt nämlich noch in diesem Package, die von mir geschriebene Unit veröffentliche ich natürlich auch noch, animierte GIFs werden jedoch z.Z. noch nicht unterstützt. Ein Geschwindigkeitsvergleich mit der GDI-Plus Variante unter Windows ergab sogar kleinere Zeitersparnisse, aber da die Bildskalierung in sehr kurzer Zeit geschieht ist es sehr schlecht messbar, eine Skalierung mit ImageMagick dauerte hingegen erheblich länger und macht eine "On-the-fly" Generierung, also ohne Caching quasi unmöglich. imgutils.pas("https://www.gocher.me/code/imgutils.pas")