Ein Bild sagt mehr als tausend Worte. Aber viele Bilder verbrauchen viel Traffic, wäre es nicht besser wenn nur die Bilder übertragen werden müssen, die der Benutzer auch wirklich sehen will? Und würden kleine Vorschaubilder in Bildergalerien oder Foren nicht auch die Übersichtlichkeit erhöhen?
Ja. Und genau deshalb erzeugen die meisten Bildergalerien und Bilderhoster diese kleinen Vorschaubilder, auch Thumbnails genannt. In dieser Anleitung möchte ich erklären wie man solche Vorschaubilder in PHP erstellen kann.
Wichtig: Diese Anleitung setzt grundlegende Kenntnisse über PHP voraus. (Grundlagen, Bedingungen, Variablen, Funktionen)
Funktionsweise
Im Grunde ist das Ganze recht einfach. Das PHP-Skript öffnet das Originalbild und erstellt ein kleines Bild, welches z.B. nur 100×100 Pixel groß ist. Dann wird das Originalbild auf die Größe des kleinen Vorschaubilds geschrumpft und in dieses eingefügt. Jetzt könnte man noch einen Rahmen um das Vorschaubild zeichnen. Oder einen kleinen schwarzen Balken mit Informationen über die Dateigröße oder die Auflösung des Originalbilds einfügen. In dieser Anleitung werde ich der Einfachheit halber nicht darauf eingehen wie man mittels HTML/PHP Bilder hochladen kann, sondern nur darauf wie man die Vorschaubilder erstellt.
Informationen über das Bild ermitteln
Gehen wir mal davon aus das das Originalbild bereits auf dem Server gespeichert ist. Zuerst einmal muss herausgefunden werden welche Auflösung das Originalbild hat, und um welches Bildformat (PNG, JPEG, GIF, …) es sich handelt. Hier bietet sich die Funktion getimagesize(string $filename [, array &$imageinfo]) an. Die Funktion ermittelt Breite, Höhe und mime-Typ des Bildes. Die Funktion kann noch einige weitere Informationen liefern, diese sind aber erstmal unwichtig.
$originalbild = "bilder/bild.png"; $informationen = getimagesize($original_dateiname); if ($informationen == FALSE || ($informationen[0] + $informationen[1]) == 0) throw new Exception("Fehler beim Ermitteln der Bildauflösung!"); $breite = $informationen[0]; $hoehe = $informationen[1]; $format = $informationen['mime'];
Wenn beim Ermitteln der Informationen ein Fehler auftritt gibt getimagesize() FALSE zurück. Wenn die Funktion nicht in der Lage ist die Auflösung des Bildes herauszufinden liefert sie als Breite und Höhe jeweils den Wert 0. Sollte einer dieser Fehler auftreten wird eine Fehlermeldung ausgegeben und die Ausführung des Skripts abgebrochen (throw new Exception(…)). Wenn kein Fehler aufgetreten ist werden Breite, Höhe und Bildformat in $breite, $hoehe und $format gespeichert.
Was ist ein mime-Typ?
Multipurpose Internet Mail Extensions, kurz MIME ermöglicht es bei Übertragungen im Internet festzulegen welches Format die übertragenen Daten haben, z.B. Bilder, Filme, Text, etc.
Mehr Informationen zu MIME finden sich bei Wikipedia.
Was hat es mit diesem throw new Exception(…) auf sich?
Exceptions, zu deutsch Ausnahmen, sind eine Möglichkeit der Fehlerbehandlung. Man umschließt den Quelltext einfach mit einem try { <Quelltext } catch (Exception $e) { <Fehlerbehandlung> }. Wenn ein Fehler auftritt wirft man einfach eine Ausnahme mit throw new Exception(”Fehlermeldung”);, der Quelltext danach wird dann nicht mehr ausgeführt, PHP springt direkt zum catch-Block und führt die entsprechende Fehlerbehandlung aus.
Mehr Informationen zur Ausnahmebehandlung mit Exceptions finden sich im PHP Handbuch.
Originalbild öffnen
Jetzt wo das Bildformat bekannt ist, kann das Bild auch geöffnet und damit gearbeitet werden, jedenfalls wenn das Dateiformat unterstützt wird. In einer switch-Bedingung werden nun die 4 häufigsten Bildformate abgefragt und die entsprechende Funktion sie zu öffnen aufgerufen. PHP unterstützt noch ein paar weitere Formate, diese 3 sind aber die am häufigsten verwendeten. Wenn das Bild ein anderes Format hat wird wieder die Ausführung des Skripts abgebrochen und eine Fehlermeldung ausgegeben.
switch($format) { case "image/gif": $originalbild = imagecreatefromgif($original_dateiname); break; case "image/jpeg": $originalbild = imagecreatefromjpeg($original_dateiname); break; case "image/png": $originalbild = imagecreatefrompng($original_dateiname); break; default: throw new Exception("Bildformat nicht unterstützt!"); }
Verhältnis Höhe/Breite berechnen
Damit das Bild beim verkleinern nicht gestreckt wird, muss nun das Verhältnis zwischen Höhe und Breite ausgerechnet werden, um die die Größe des Vorschaubilds entsprechend anzupassen. Erstmal werden Höhe und Breite auf je 100 Pixel festgelegt, diese Werte können natürlich höher/niedriger gesetzt werden, je nachdem wie groß das Vorschaubild sein soll. Dann wird das Verhältnis zwischen Höhe/Breite des Originalbilds berechnet in dem die Höhe durch die Breite respektive die Breite durch die Höhe geteilt wird. Wenn das Bild breiter als hoch ist ($breite_verhaeltnis > $hoehe_verhaeltnis), wird die Höhe, ansonsten die Breite angepasst. Um die Höhe anzupassen wird die gewünschte Höhe des Vorschaubilds durch das Verhältnis der Breite zur Höhe geteilt. Anschließend wird das Ergebnis mithilfe der Funktion ceil(float $value) gerundet, da es keine halben Pixel gibt.
Die Breite wird genau so berechnet, nur mit anderen Werten.
$vorschau_breite = 100; $vorschau_hoehe = 100; $breite_verhaeltnis = $breite / $hoehe; $hoehe_verhaeltnis = $hoehe / $breite; if ($breite_verhaeltnis > $hoehe_verhaeltnis) $vorschau_hoehe = ceil(($vorschau_hoehe / $breite_verhaeltnis)); else $vorschau_breite = ceil(($vorschau_breite / $hoehe_verhaeltnis));
Vorschaubild erstellen und Rahmen zeichnen
Jetzt kann das Vorschaubild erstellt werden, und zwar mit der Funktion imagecreatetruecolor(int $width, int $height). Warum imagecreatetruecolor und nicht imagecreate? Weil bei Bildern die mit imagecreate erzeugt wurden nur 256 Farben zur Verfügung stehen. Bei GIF-Bildern ist das egal, bei PNG, JPEG o.Ä. aber nicht. Wenn das Bild nicht erstellt werden konnte gibt imagecreatetruecolor FALSE zurück, in dem Fall wird wieder einfach eine Exception geworfen.
Um den Rahmen um das Vorschaubild zu zeichnen muss man erst die Farbe die man verwenden will bestimmen, dafür benutzt man die Funktion imagecolorallocate(resource $im, int $red, int $green, int $blue). Diese Funktion gibt einen Bezeichner zurück unter dem die Farbe aufrufbar ist. Zu den Parametern der Funktion ist zu sagen: Der erste Parameter ist das Bild für welches die Farbe bestimmt ist, in diesem Fall $vorschaubild, die anderen 3 Parameter bestimmen den Rot/Grün/Blau-Anteil der Farbe (0-255).
Der Rahmen wird letztendlich mit der Funktion zum Zeichnen von Rechtecken imagerectangle(resource $im, int $x1, int $y1, int $x2, $int y2, int $col) gezeichnet. $im ist wieder das Bild in das gezeichnet werden soll, $x1 bzw. $y1 stellen die linke obere Koordinate, $x2 und $y2 die rechte untere Koordinate dar. Mit $col bestimmt man die Farbe des Rechtecks. Als Startkoordinate wird 0|0 verwendet, als Endkoordinate $vorschau_breite – 1|$vorschau_hoehe – 1. Minus 1 weil man in der Programmierung meistens bei 0 anfängt zu zählen und unser Bild sozusagen 0-99 Pixel breit und 0-99 Pixel hoch ist.
$vorschaubild = imagecreatetruecolor($vorschau_breite, $vorschau_hoehe); if (!$vorschaubild) throw new Exception("Fehler beim Erstellen des Vorschaubilds!"); $rahmenfarbe = imagecolorallocate($vorschaubild, 0, 0, 0); imagerectangle($vorschaubild, 0, 0, $vorschau_breite - 1, $vorschau_hoehe - 1, $rahmenfarbe);
Originalbild schrumpfen und ins Vorschaubild einfügen
Nun muss das Originalbild so weit verkleinert werden damit es in das Vorschaubild reinpasst. Dazu gibt es die Funktionen imagecopyresampled(resource $dst_image, resource $src_image, int $dst_x, int $dst_y, int $src_x, int $src_y, int $dst_w, int $dst_h, int $src_w, int $src_h) und imagecopyresized(resource $dst_im, resource $src_im, int $dstX, int $dstY, int $srcX, int $srcY, int $dstW, int $dst_H, int $src_W, int $src_H). Der erste Parameter legt fest in welches Bild das verkleinerte Bild geschrieben werden soll, der zweite bestimmt das Originalbild, die beiden nächsten parameter bestimmen die Startkoordinaten im Zielbild, die darauffolgenden beiden die Koordinaten im Originalbild. Darauf folgen noch jeweils Höhe und Breite von Original und Zielbild.
Hier ist zu beachten das die Startkoordinaten im Zielbild jeweils 1 sind, würde bei 0 angefangen werden zu zeichnen würde der Rahmen wieder “übermalt” werden. Im Originalbild wird natürlich alles von 0|0 bis $breite|$hoehe erfasst. Bei der Breite/Höhe des Vorschaubilds müssen 2 Pixel wegen dem Rahmen abgezogen werden (jeweils 1 Pixel oben/unten, und einer links/rechts).
Wenn beim verkleinern ein Fehler auftritt gibt die Funktion FALSE zurück.
imagecopyresampled($vorschaubild, $originalbild, 1, 1, 0, 0, $vorschau_breite - 2, $vorschau_hoehe - 2, $breite, $hoehe);
Die Funktion imagecopyresized arbeitet zwar schneller (ungefähr 400%) als imagecopyresampled, die Ergebnisse sehen aber auch bei weitem nicht so gut aus.
Vorschaubild abspeichern
Nun muss das Vorschaubild noch auf dem Server abgespeichert werden, denn bis jetzt befindet es sich ja nur im Speicher. Dafür können die Funktionen imagepng, imagegif oder imagejpeg verwendet werden. Es gibt natürlich auch noch andere Funktionen für andere Bildformate, die hier aufgezählten sind aber die gängigsten. Der erste Parameter der Funktion gibt immer das Bild an, das gespeichert werden soll, der zweite den Dateinamen/Pfad unter dem es gespeichert werden soll. Den Funktionen zum Abspeichern von JPEGs und PNGs kann man zusätzlich noch sagen welche Qualität das Zielbild haben soll, bzw. wie stark es komprimiert werden soll.
imagepng($vorschaubild, "vorschaubild.png");
Optional: Kleiner Balken mit Informationen
Das Vorschaubild ist jetzt fertig, aber wäre es nicht praktisch wenn im Vorschaubild angezeigt werden würde welche Dateigröße und welche Auflösung das Originalbild hat?
Zuerst muss die Dateigröße des Originalbildes ermittelt werden, die Funktion filesize(string $filename) macht das. Die zurückgegebene Dateigröße muss nun nur noch von Bytes in Kibibytes bzw. Mebibytes umgerechnet werden. Dazu teilen man die Dateigröße durch 1024. Wenn ein Bild aber größer als 1024 KiB ist, sollte man es in MiB umrechnen, im Vorschaubild ist ja nicht viel Platz zur Anzeige von Informationen. Also fragt man ab ob das Bild größer oder gleich 1 MiB ist, und wenn das der Fall ist, wird die Dateigröße nochmal durch 1024 geteilt.
$bildgroesse = filesize($original_dateiname); $bildgroesse /= 1024; if ($bildgroesse > 1024) { $bildgroesse /= 1024; $bildgroesse_text = ceil($bildgroesse) . " MiB"; } else $bildgroesse_text = ceil($bildgroesse) . " KiB";
Die Auflösung des Originalbildes wurde ja schon am Anfang mittels getimagesize() ermittelt und in $hoehe und $breite abgespeichert.
$text = "{$breite}x{$hoehe} $bildgroesse_text";
So lassen sich die Informationen recht kompakt darstellen.
Da die Informationen aber in den meisten Fällen immer noch zu lang für das Vorschaubild sind, muss die Größe des Vorschaubildes etwas angehoben werden, anstatt 100×100 also besser 120×120.
$vorschau_breite = 120; $vorschau_hoehe = 120;
Da die Informationen das Bild ja nicht überdecken sollen muss das Vorschaubild etwas vergrößert werden:
$vorschaubild = imagecreatetruecolor($vorschau_breite, $vorschau_hoehe + 16);
Um die Informationen mittig anzuzeigen muss erstmal die Länge des Textes berechnet werden, indem man die Anzahl der Buchstaben (+ Leerzeichen) mal die Anzahl vertikaler Pixel pro Buchstabe rechnet. Dann zieht man das Ergebnis von der Breite des Vorschaubildes ab und teilt das ganze durch 2.
$startposition = ($vorschau_breite - (strlen($text) * 7)) / 2;
Der Text braucht natürlich auch wieder eine Farbe, Rot/Grün/Blau auf 255 ergibt die Farbe Weiß:
$text_farbe = imagecolorallocate($vorschaubild, 255, 255, 255);
Nun muss ein kleiner Balken unterhalb des Vorschaubildes gezeichnet werden, dazu wird die Funktion imagefilledrectangle verwendet. Diese funktioniert wie imagerectangle, nur das diese Funktion das Rechteck auch ausmalt. Als Farbe wird die selbe Farbe wie für den Rahmen verwendet.
imagefilledrectangle($vorschaubild, 0, $vorschau_hoehe, $vorschau_hoehe - 1, $vorschau_breite - 1, $rahmenfarbe);
Jetzt können die Informationen endlich in den eben gezeichneten Balken geschrieben werden. Um Text in Bilder zu schreiben gibt es in PHP die Funktionen imagestring und imagettftext, erstere kann zwar nur Bitmap-Schriftarten darstellen (imagettftext kann auch TrueType-Schriftarten verwenden), das reicht aber für diese Anleitung vollkommen aus. Der erste Parameter bestimmt wieder das Bild in das geschrieben werden soll, der zweite die Schriftart, wenn hier Zahlen zwischen 1 und 5 angegeben werden, wird eine Standardschriftart verwendet. Parameter 3 und 4 geben an an welcher Stelle begonnen werden soll zu schreiben, die anderen Beiden bestimmen den Text und die Schriftfarbe.
imagestring($vorschaubild, 3, $startposition, $vorschau_hoehe + 1, $text, $text_farbe);
Fertig!
“{$breite}x{$hoehe}” Warum die geschweiften Klammern?
Ohne die geschleiften Klammern würde PHP denken es sind die Variablen $breite[b]x[/b] und $höhe gemeint, durch die geschweiften Klammern weiß PHP das die Variable nur $breite heißt. So lassen sich übrigens auch Arrays direkt in Strings einbinden: “{$array['index']}”
Abschließendes
Das wars? Ja, natürlich lässt sich das ganze noch verbessern und erweitern, z.B. kann man das ganze in eine Klasse packen, den Balken mit den Informationen transparent über das Bild legen und vieles mehr. Man kann noch Überprüfungen einbauen, z.B. kann man noch abfragen ob das Originalbild bereits kleiner ist als das Vorschaubild, in dem Fall macht es natürlich wenig Sinn es zu verkleinern. Aber das überlasse ich euch, lasst eurer Fantasie freien Lauf.
Hier noch einmal der gesamte Quelltext:
$original_dateiname = "original.png"; /* Aufloesung und Bildformat bestimmen */ $informationen = getimagesize($original_dateiname); /* Wenn das fehlschlägt -> Fehler */ if ($informationen == FALSE || ($informationen[0] + $informationen[1]) == 0) throw new Exception("Fehler beim Ermitteln der Bildauflösung!"); $breite = $informationen[0]; $hoehe = $informationen[1]; $format = $informationen['mime']; /* Originalbild oeffnen */ switch($format) { case "image/gif": $originalbild = imagecreatefromgif($original_dateiname); break; case "image/jpeg": $originalbild = imagecreatefromjpeg($original_dateiname); break; case "image/png": $originalbild = imagecreatefrompng($original_dateiname); break; default: throw new Exception("Bildformat nicht unterstützt!"); } $vorschau_breite = 100; $vorschau_hoehe = 100; /* Breite/Hoehe Verhältnis berechnen und Groesse des Vorschaubildes entsprechend anpassen */ $breite_verhaeltnis = $breite / $hoehe; $hoehe_verhaeltnis = $hoehe / $breite; if ($breite_verhaeltnis > $hoehe_verhaeltnis) $vorschau_hoehe = ceil(($vorschau_hoehe / $breite_verhaeltnis)); else $vorschau_breite = ceil(($vorschau_breite / $hoehe_verhaeltnis)); /* Vorschaubild erstellen */ $vorschaubild = imagecreatetruecolor($vorschau_breite, $vorschau_hoehe); if (!$vorschaubild) throw new Exception("Fehler beim Erstellen des Vorschaubilds!"); /* Rahmen zeichnen */ $rahmenfarbe = imagecolorallocate($vorschaubild, 0, 0, 0); imagerectangle($vorschaubild, 0, 0, $vorschau_breite - 1, $vorschau_hoehe - 1, $rahmenfarbe); /* Originalbild verkleinern und in das Vorschaubild einfuegen */ imagecopyresampled($vorschaubild, $originalbild, 1, 1, 0, 0, $vorschau_breite - 2, $vorschau_hoehe - 2, $breite, $hoehe); /* Vorschaubild abspeichern */ imagepng($vorschaubild, "vorschaubild.png");






Sehr schoene Anleitung, leider blicke ich bei PHP noch nicht ganz durch, da ich mich momentan hauptsaechlich mit Java befasse. Vielleicht versuche ich das Tutorial mal in Java umzusetzen, ist ja eigentlich eine Interessante Funktion, welche sich sicherlich auch gut als Bildergalerie, natuerlich mit anderen Werten machen wuerde.
Gruss
Henrik
Schöne Anleitung
du bist der Beste!
ich liebe deinen Blog!