# Dynamische Bildgenerierung – Perl-Variante

US-Wissenschaftler haben jetzt eine Methode gefunden, durch die Bilder von Digitalkameras (!!!) schöner und schärfer werden. Laut »netzeitung« vom Sonntag entdeckten die Forscher einen neuartigen Film, bei dem auf die Silbersalzkörner, die bisher verwendet wurden, verzichtet werden kann ...
– Gefunden auf der Homepage einer bekannten deutschen Wochenzeitschrift

Diese Meldung ist doch interessant. Was wohl als Nächstes kommen wird? Videofilme in Farbe und mit Ton, die anstatt auf einem Magnetband auf einer Plastikscheibe gespeichert werden können?

# Die GD-Library und Perl

Wie schon PHP bietet auch Perl eine Möglichkeit, mit der GD-Library Grafiken zur Laufzeit eines Scripts zu erzeugen. Die Implementierung der GD-Library in Perl ist jedoch anders aufgebaut. Zunächst müssen Sie ein Perl-Modul installieren, das Ihnen den Zugriff auf die GD-Library ermöglicht. In PHP genügt es, die Library einfach zu aktivieren und dann die bereitgestellten Funktionen aufzurufen. Perl ist in dieser Hinsicht ein wenig anspruchsvoller.

Voraussetzung für die nachfolgenden Erklärungen, wie das Modul installiert wird, ist die ActiveState Perl-Distribution in der Version 5.61. Bei älteren oder neueren Versionen kann die Vorgehensweise abweichen. Leider ist es unmöglich, alle Variationen zu berücksichtigen.

Der Ausgangspunkt und das wichtigste Werkzeug ist der Programmer's Package Manager, kurz PPM genannt. Dieses Programm ist kommandozeilen-basiert, so dass alle Schritte durch die Eingabe unterschiedlicher Befehlsfolgen vorgenommen werden. Um das Programm zu starten, reicht es aus, eine Eingabeaufforderung zu öffnen. Unter Windows erreichen Sie dies durch einen Klick auf Start und Ausführen ... Unter Linux müssen Sie lediglich ein Terminalfenster öffnen. Tippen Sie nun ppm3 ein und dann Enter. Der Programmer's Package Manager in der Version 3 sollte daraufhin gestartet werden.

Achten Sie darauf, dass eine Verbindung zum Internet besteht. Sollten Sie keinen Internetzugang besitzen, muss die Installation auf einem anderen Weg erfolgen, der am Ende dieses Kapitels erläutert wird.

Tippen Sie nun die Befehlssequenz search gd ein und dann Enter. Der PPM durchsucht nun die ihm bekannten Internetquellen nach einem Perl-Modul mit dem Namen GD. Dies kann, je nach Geschwindigkeit Ihrer Internetanbindung und Auslastung der Modul-Quellen, einige Sekunden bis maximal eine Minute dauern.

Startbildschirm des Programmer's Package Managers 3 Abbildung 2.1: Startbildschirm des Programmer's Package Managers 3

Anschließend sollte Ihnen der PPM eine nummerierte Liste aller gefundenen Module liefern, die auf die Suchanfrage passen. Unter anderem sollte ein Modul mit dem Namen GD, einer Versionsnummer wie 1.2x.x und der Erklärung »Interface to Gd Graphics Library« gefunden werden. Abbildung 2.2 zeigt eine Beispiel-Ausgabe der Suche. Eintrag Nummer 10 entspricht dem gesuchten Modul.

Suchergebnis des PPM nach dem Modul GD Abbildung 2.2: Suchergebnis des PPM nach dem Modul GD

Wenn Ihre Suchanfrage ein entsprechendes Ergebnis liefert, dann tippen Sie install gd Enter ein. Der PPM sollte nun mit der Installation des Moduls beginnen und in etwa folgende Ausgabe liefern:

====================  
Install 'gd' version 1.27.2 in ActivePerl 5.6.1.632.  
====================  
Downloaded 225001 bytes.  
Extracting 21/21: blib/lib/qd.pl  
Installing D:\\Perl\\site\\lib\\auto\\GD\\GD.bs  
Installing D:\\Perl\\site\\lib\\auto\\GD\\GD.dll  
...  
Writing D:\\Perl\\site\\lib\\auto\\GD\\.packlist  
Successfully installed gd version 1.27.2 in ActivePerl 5.6.1.632.

Wichtig ist dabei die letzte Zeile der Ausgabe. Sollte der PPM Successfully installed ... ausgeben, dann wurde die Installation erfolgreich abgeschlossen.

Nun fehlt noch der letzte Schritt. Die Installation muss daraufhin getestet werden, ob sie auch wirklich einwandfrei funktioniert. Öffnen Sie dafür eine Konsole. Unter Windows erreichen Sie dies durch den Klick auf Start und Ausführen ... und die Eingabe cmd Enter, wenn Sie Windows NT 4.0/2000/XP verwenden. Unter Windows 95/98/Me müssen Sie command Enter eingeben. Tippen Sie nun die folgende Zeile ein, und bestätigen Sie die Eingabe anschließend mit Enter.

perl -e "use gd;"

Diese Zeile startet den Perl-Interpreter und weist ihn an, das Modul GD einzubinden. Wenn das Modul erfolgreich installiert worden ist und Perl das Modul finden und einbinden konnte, darf nun keine Ausgabe erfolgen. Sollte trotzdem etwas ausgegeben werden, z. B. eine Fehlermeldung, überprüfen Sie die vorangegangenen Schritte, und führen Sie diese notfalls noch einmal durch.

# Manuelle Installation

Für den Fall, dass Sie keine Möglichkeit haben, über Ihren Computer ins Internet zu gelangen, finden Sie auf der dem Buch beiliegenden CD‑ROM das Archiv gd.zip im Verzeichnis x:\misc.

Entpacken Sie das Archiv gd.zip in ein beliebiges Verzeichnis auf Ihrer Festplatte, und öffnen Sie eine Konsole. Wechseln Sie in das Verzeichnis, in das Sie gd.zip entpackt haben. Tippen Sie nun das Kommando

ppm3 install gd

ein, und bestätigen Sie die Eingabe mit Enter. Die Installation sollte nun gestartet werden, und am Ende der Ausgabe sollte Successfully installed... erscheinen.

# Erste Versuche

In PHP funktionierte das Erzeugen einer Grafik über die bereitgestellten Funktionen ein Handle, um auf die Grafik zuzugreifen. In Perl stehen die gleichen Funktionen zur Verfügung, der Zugriff auf die Grafik und die einzelnen Funktionen wird jedoch durch ein Objekt gebündelt.

In Perl müssen Sie also zuerst ein Objekt instanziieren. Bei der Instanziierung muss dem Konstruktor der Klasse dann die Breite und die Höhe der neuen Grafik übergeben werden. Konnte die neue leere Grafik erzeugt werden, wird ein Objekt erstellt, das über alle wichtigen Eigenschaften und Methoden verfügt, die Sie benötigen. Wenn das Objekt nicht erstellt werden konnte, sollten Sie das Perl-Script beenden. Wie Sie dies realisieren können, werden Sie in den folgenden Beispielen erfahren.

object GD::image::new(int width, int height)

Zuallererst müssen Sie jedoch daran denken, das GD-Modul in Ihr laufendes Script einzubinden. Dies erfolgt mit der Anweisung use gd;. Ein Beispielaufruf könnte also folgendermaßen aussehen:

use gd;  
$image = new GD::image(175,75);

Diese beiden Zeilen würden eine neue Grafik mit 175 Pixel Breite und 75 Pixel Höhe definieren und das für den Zugriff notwendige Objekt $image erzeugen. Um einen Fehler bei der Instanziierung des Objekts abfangen zu können, wird in der Regel folgende Schreibweise verwendet:

use gd;  
$image = new GD::image(175,75) || die('<Fehlermeldung>');

# Farben

Für jedes Bild benötigen Sie Farbe, und da bei neu mit der GD-Library definierten Grafiken die Farbpalette einer neuen Grafik immer leer ist, müssen Sie diese Farben zunächst definieren. Die Methode, die dafür verwendet werden muss, heißt colorAllocate. Die einzelnen Farbanteile der neuen Farbe, also rote, grüne und blaue, müssen Sie der Methode dabei als Parameter übergeben. Die Funktion liefert dann den Index der Farbe in der Farbpalette als Zahlenwert bzw. Integer zurück.

int image->colorAllocate(int red, int green, int blue)

Da Sie für jeden einzelnen Farbanteil einen Wert zwischen 0 und 255 angeben können, stehen Ihnen 16,6 Millionen Farben zur Verfügung.

Achten Sie darauf, dass Sie den Index der neuen Farbe in einem extra Skalar speichern und dass die erste Farbe, die Sie definieren, automatisch als Hintergrundfarbe für die Grafik verwendet wird.

Nachfolgend finden Sie ein paar Beispiele, wie Sie Farben definieren müssen.

$bg = $image->colorAllocate(255,255,255);  
$red = $image->colorAllocate(255,0,0);  
$green = $image->colorAllocate(0,255,0);  
$blue = $image->colorAllocate(0,0,255);  
$yellow = $image->colorAllocate(255,255,0);

Die Hintergrundfarbe der Grafik wird also weiß. Zusätzlich werden noch vier weitere Farben definiert: Rot, Grün, Blau und Gelb.

# Text einfügen

Auch in Perl gibt es eine Methode, mit der Sie eine beliebige Zeichenkette in eine Grafik einfügen können. Der Name der Methode lautet string und erwartet ebenfalls mehrere Parameter:

void image->string(int font, int x, int y, string str, int color)

Als ersten Parameter müssen Sie der Methode eine Schriftart übergeben. Intern stehen in der GD-Library bereits fünf verschiedene Schriften zur Verfügung, die sich lediglich in der Dicke und der Schriftgröße unterscheiden. Die gewünschte Schrift geben Sie dabei als Konstante an.

Schriftkonstante Erklärung
gdSmallFont 6 × 12 Pixel kleine Schrift
gdLargeFont 8 × 16 Pixel große Schrift
gdMediumBoldFont 7 × 13 Pixel kleine fette Schrift
gdTinyFont Sehr kleine Schrift mit 5 × 8 Pixel Größe
gdGiantFont Große fette Schrift mit 9 × 15 Pixel Größe

Tabelle 2.1: Schriftkonstanten

Die beiden Parameter x und y definieren die Position, an der die Zeichenkette eingefügt werden soll. An der angegebenen Position wird dann die linke obere Ecke der Zeichenkette ausgerichtet. Die Position 0,0 steht für die linke obere Ecke der Grafik. Als vierter Parameter str wird die eigentliche Zeichenkette übergeben. Schlussendlich müssen Sie noch eine Textfarbe angeben. Dabei müssen Sie den Palettenindex der Farbe als fünften Parameter color übergeben.

Ein Beispiel:

$image->string(gdMediumBoldFont,10,10,'Test',$black);

Diese Anweisung würde den Text Test in der Grafik ausgeben. Dabei werden die Farbe $black und die Schrift gdMediumBoldFont verwendet. Die linke obere Ecke des Textes beginnt an der Position 10,10.

Diese Art, einen Text in der Grafik auszugeben, ist sehr bequem und sicherlich der einfachste Weg. Wenn Sie nun z.B. eine Grafik erzeugen möchten, die als Schaltfläche verwendet werden soll, muss der Text in der Regel ausgerichtet werden. Die Position, an der der Text dann ausgegeben werden soll, hängt von der Gesamtbreite des Texts in der gewählten Schrift und der Höhe ab. Damit eine hohe Variabilität erhalten bleibt – also die Schriftart einfach ausgetauscht werden kann –, sollte die Angabe der einzelnen Breiten und Höhen einer Schriftart nicht von Hand vorgenommen werden müssen. Zu jeder Schriftart stehen zwei Eigenschaften zur Verfügung, die schon die entsprechenden Werte enthalten.

int font->width

Diese Eigenschaft gibt die Breite eines Zeichens in der Schriftart zurück.

int font->height

Die Eigenschaft height gibt hingegen die Höhe eines Zeichens in der Schriftart zurück.

Die beiden Eigenschaften könnten folgendermaßen verwendet werden, um die Gesamtbreite und die -höhe eines auszugebenden Textes zu errechnen.

$str = 'Eine Zeichenkette';  
$str_height = gdMediumBoldFont->height;  
$str_width = gdMediumBoldFont->width * length($str);

Diese drei Zeilen definieren zuerst eine Variable, die als Wert Eine Zeichenkette erhält. In der Variablen $str_height wird dann die Höhe des Textes in Pixel gespeichert und in der Variablen $str_width die Gesamtbreite des Textes in Pixel. Dabei wird die Anzahl der Zeichen der Zeichenkette anhand der Funktion length ermittelt und mit der Breite eines einzelnen Zeichens multipliziert.

Mit den errechneten Werten könnten Sie nun einen Text exakt in der Mitte einer Grafik ausrichten oder 10 Pixel vom rechten Rand entfernt usw. Zur Berechnung der exakten Mitte müssen Sie lediglich die Werte der Variablen $str_height und $str_width von der Höhe und Breite der Grafik abziehen und jeweils durch zwei dividieren.

$str_x = (175$str_width) / 2;  
$str_y = (75$str_height) / 2;

In der Variablen $str_x steht nun die x-Position und in der Variablen $str_y die y-Position, an der der Text ausgegeben werden muss, damit dieser exakt in der Mitte der Grafik erscheint.

# Ausgabe des Bildes

Die letzte Hürde ist die Ausgabe der erzeugten Grafik. Generell haben Sie die Wahl zwischen zwei Formaten, in denen die Grafik ausgegeben werden kann: entweder im platzsparenden, aber verlustbehafteten JPEG-Format oder im verlustfreien, jedoch größeren PNG-Format. Je nachdem, für welches Format Sie sich entscheiden, stehen zur Umwandlung die beiden Methoden jpeg und png bereit.

string image->jpeg  
string image->png

Der Aufruf der Methoden unterscheidet sich jedoch von der PHP-Variante gewaltig. Denn sowohl die Methode jpeg als auch die Methode png erzeugen nur den so genannten Datenstrom und geben diesen als String zurück. Die Ausgabe müssen Sie nun selbst vornehmen, indem Sie den erzeugten Datenstrom entweder an den Browser senden oder in eine Datei schreiben.

$jpeg_data = $image->jpeg;  
$png_data = $image->png;

Beide Methodenaufrufe würden den jeweiligen Datenstrom in einem Skalar speichern. Je nachdem, ob Sie den Datenstrom nun im Browser ausgeben möchten oder in eine Datei schreiben wollen, müssen Sie unterschiedliche Wege beschreiten.

Für die Ausgabe des Datenstroms in eine Datei muss ein Unterschied zwischen UNIX/Linux und Windows gemacht werden. An verschiedenen Stellen in diesem Buch habe ich schon darauf hingewiesen, dass unter UNIX/Linux ein Zeilenumbruch nur aus dem Steuerzeichen LF besteht, unter Windows jedoch aus CR/LF. Sie müssen den Kanal, durch den Sie die Daten in die Datei schreiben, also auf den Binärmodus umschalten. Dies übernimmt die Anweisung binmode, gefolgt vom Dateihandle. Also in etwa wie folgt:

binmode JPEGFILE;  
binmode PNGFILE;

Wie Sie nun sicherlich schon ahnen, müssen Sie mit Perl-Funktionen also zuerst eine Datei öffnen, in der der Datenstrom gespeichert werden soll. Dann schalten Sie mit der Anweisung binmode auf den Binärmodus um und schreiben anschließend die Daten in die Datei. Zum Schluss wird das Dateihandle wieder freigegeben.

open(JPEGFILE,">tmp_image.jpg");  
binmode JPEGFILE;  
$jpeg_data = $image->jpeg;  
print JPEGFILE $jpeg_data;  
close(JPEGFILE);

Dieses Beispiel erzeugt zuerst eine neue Datei namens tmp_image.jpg und das Zugriffshandle JPEGFILE. Dann wird der Ausgabekanal auf den Binärmodus umgeschaltet. Im nächsten Schritt wird der JPEG-Datenstrom im Skalar $jpeg_data gespeichert, der anschließend mit print in die Datei geschrieben wird. Diese beiden Schritte ließen sich auch durch folgende Anweisung verkürzen:

print JPEGFILE $image->jpeg;

Abschließend wird die Datei geschlossen. Die kürzere Variante in Bezug auf das PNG-Format würde dann folgendermaßen aussehen:

open(PNGFILE,">tmp_image.png");  
binmode PNGFILE;  
print PNGFILE $image->png;  
close(PNGFILE);

Wie schon in PHP müssen Sie auch in Perl dem Browser zuerst den MIME-Typ der Grafik mitteilen. Anschließend geben Sie den erzeugten Datenstrom einfach im Browser aus.

Normalerweise haben Sie zur Ausgabe des MIME-Typs die CGI-Funktion header verwendet. Diese dürfen Sie an dieser Stelle nicht verwenden, da sie den MIME-Typ text/html ausgibt. Er ist für Grafiken vom Typ JPEG oder PNG jedoch nicht zu gebrauchen. Stattdessen können Sie den passenden MIME-Typ mit einer print-Anweisung an den Browser ausgeben:

print "Content-Type: image/jpeg\n\n";  
  // Ausgabe des MIME-Typs für JPEG-Grafiken  
print "Content-Type: image/png\n\n";  
  // Ausgabe des MIME-Typs für PNG-Grafiken

Denken Sie immer an die beiden Zeilenumbrüche am Ende der Ausgabe. Die sind notwendig, damit der Browser erkennen kann, an welcher Stelle der Datenstrom beginnt. Für die Ausgabe im Browser müssen Sie den Ausgabekanal übrigens nicht explizit auf den Binärmodus umschalten.

Anschließend könnte die Ausgabe des Datenstroms durch eine einfache print-Anweisung erfolgen:

print "Content-Type: image/jpeg\n\n";  
print $image->jpeg;

Im Folgenden finden Sie ein vollständiges Beispiel zur Ausgabe einer Grafik im Browser.

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

$width = 175;
$height = 25;
$font = gdMediumBoldFont;
$str = 'Ein Text';

$image = new GD::Image($width,$height);

$yellow = $image->colorAllocate(255,255,0);
$blue = $image->colorAllocate(0,0,255);

$str_x = ($width - ($font->width * length($str))) / 2;
$str_y = ($height - $font->height) / 2;
$image->string($font,$str_x,$str_y,$str,$blue);

print "Content-Type: image/png\n\n";
print $image->png;

Listing 2.1: Ausgabe einer Grafik im Browser mit Schriftzug

Alle Parameter, die in diesem Script möglichst variabel sein sollen, wurden zu Beginn des Scripts in entsprechenden Variablen gespeichert. Sowohl die Breite als auch die Höhe der zu erzeugenden Grafik wurden in den Variablen $width und $height gespeichert. Die zu verwendende Schriftart wurde in der Variablen $font gespeichert. Diese Möglichkeit habe ich Ihnen bisher noch vorenthalten. Sie funktioniert deshalb, weil alle fünf Schriftarten, die in der GD-Library bereits enthalten sind, im GD-Modul als Objekte vorliegen und einem beliebigen Skalar zugewiesen werden können. Die entsprechenden Eigenschaften werden dann ebenfalls übergeben. Der auszugebende Text wird in der Variablen $str gespeichert.

In den folgenden drei Schritten wird zuerst ein neues Objekt namens $image erzeugt. Die neue Grafik erhält die Größe, die in den Variablen $width und $height gespeichert wurde. Dann werden zwei Farben definiert, eine gelbe und eine blaue. Die gelbe wird automatisch als Hintergrundfarbe verwendet und die blaue später für den Text.

In der Variablen $str_x wird die errechnete x-Position gespeichert, an der der Text ausgegeben werden soll, und in der Variablen $str_y die errechnete y-Position. Nun wird der Text der Variablen $str an der Position $str_x und $str_y in einer blauen Farbe in die Grafik eingefügt.

Zu guter Letzt wird dann zuerst der MIME-Typ der Grafik (in diesem Fall image/png) ausgegeben und anschließend der Datenstrom der Grafik. Die Ausgabe, die das Listing 2.1 erzeugt, ist in Abbildung 2.3 dargestellt.

Mit Perl erzeugte Grafik
Abbildung 2.3: Mit Perl erzeugte Grafik

# Grafiken verändern

Natürlich könnte man vollständige Grafiken durch die zur Verfügung stehenden Methoden zeichnen und im Browser ausgeben. Dies kann jedoch sehr umständlich werden, wenn es lediglich darum geht, eine kleine Schaltfläche zu erstellen, die mit einem Hintergrundbild versehen werden soll. So etwas könnte z. B. als Button zur Navigation verwendet werden. Je komplexer die geplante Grafik jedoch wird, desto länger benötigt Perl, un diese Grafik zu berechnen und zu erzeugen. Schneller und einfacher ginge es, wenn Sie stattdessen eine existierende Grafik als Basis verwenden und einfach nur einen Schriftzug darüber legen könnten. Dies ist kein Problem, denn Sie müssen einfach nur einen anderen Konstruktor verwenden. Anstelle von new GD::Image müssen Sie dann newFromPng GD::Image oder newFromJpeg GD:Image einsetzen.

object GD::Image::newFromPng(FILEHANDLE)  
object GD::Image::newFromJpeg(FILEHANDLE)

Es gibt jedoch ein kleines Detail zu beachten. Sie müssen die Grafik, die Sie als Basis für eine neue Grafik verwenden möchten, zuvor öffnen und das erzeugte Dateihandle als Parameter an einen der beiden Konstruktoren übergeben:

open(PNG,"<basepng.png");  
$image = newFromPng GD::Image(PNG);  
close(PNG);

Zuerst wird also die Basisgrafik ganz normal mit der Funktion open geöffnet. Das so erzeugte Dateihandle wird dann an den Konstruktor übergeben. Da die Basisgrafik vollständig eingelesen wird, können Sie das Dateihandle anschließend beruhigt wieder freigeben. Für den Fall, dass die Basisgrafik nicht geöffnet werden konnte, sollten Sie den Programmablauf sofort mit die abbrechen.

open(PNG,"<basepng.png") || die;

Dadurch können Sie verhindern, dass es zu ellenlangen Fehlermeldungen kommt und unter Umständen Dateien oder Daten beschädigt werden, falls die Grafik nicht erfolgreich geöffnet werden konnte.

Wenn Sie bei der Verwendung einer Grafik als Ausgangspunkt einer neuen Grafik möglichst variabel bleiben wollen, ist es immer hilfreich, die Abmessungen einer Grafik ermitteln zu können. Die neue Grafik erhält nämlich die gleiche Breite und Höhe wie die Basisgrafik. Dafür können Sie die Methode getBounds des image-Objekts verwenden. Diese Methode liefert eine Liste zurück, die die Breite der Grafik und die Höhe in Pixel enthält. Das Element 0 der Liste enthält dabei den Wert für die Breite und das Element 1 den Wert für die Höhe.

list image->getBounds

Der Einfachheit halber sollten Sie die Werte nicht als Liste entgegennehmen, sondern gleich in zwei Skalare aufteilen. Dies erreichen Sie durch die folgende Zuweisung:

($im_width, $im_height) = $image->getBounds();

Das nun folgende Listing demonstriert die Verwendung des Konstruktors newFromPng und der Methode getBounds.

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

$font = gdGiantFont;
$str = 'Schaltfläche';

open(PNG,"<basepng.png") || die;
$image = newFromPng GD::Image(PNG);
close(PNG);

$white = $image->colorAllocate(255,255,255);

($im_width,$im_height) = $image->getBounds();

$str_x = ($im_width - ($font->width * length($str))) / 2;
$str_y = ($im_height - $font->height) / 2;
$image->string($font,$str_x,$str_y,$str,$blue);

print "Content-Type: image/png\n\n";
print $image->png;

Listing 2.2: Eine existierende Grafik wird geladen und mit einem Text versehen

Nach der Einbindung der beiden Module CGI und GD werden die beiden Variablen $font und $str definiert. $font wird die größte verfügbare Schriftart zugewiesen und $str der Text, der später ausgegeben werden soll. Anschließend wird die Grafik basepng.png zum Lesen geöffnet und das Dateihandle PNG erzeugt, das im nächsten Schritt an den Konstruktor newFromPng übergeben wird. Nachdem die Datei wieder geschlossen wurde, wird eine neue Farbe definiert, und die Abmessungen der Grafik werden ermittelt. Die Breite der Grafik wird in $im_width und die Höhe in $im_height gespeichert. Dann werden die Eckpunkte errechnet, an denen der Text ausgegeben werden muss, damit er sowohl horizontal als auch vertikal in der Mitte der Grafik platziert wird. Zum Schluss wird noch der korrekte Header gesendet und die Grafik im Browser ausgegeben.

Ausgabe des Listing 2.2 im Browser
Abbildung 2.4: Ausgabe des Listing 2.2 im Browser

# In einer Grafik zeichnen

Die Ausgabe von Text reicht manchmal vollkommen aus. Dies ist jedoch nicht alles, was Sie mit der GD-Library und Perl alles zaubern können.

# Punkte und Linien

Mit der Methode setPixel können Sie beliebige Pixel in einer Grafik manipulieren, indem Sie deren Farbe verändern. Zuerst einmal erwartet die Methode die x- und y-Position des Pixels, dessen Farbe verändert werden soll. Die Position x wird als erster Parameter und die Position y als zweiter Parameter übergeben. Als dritten und letzten Parameter müssen Sie dann noch eine Farbe angeben.

void image->setPixel(int x, int y, int color)

Ein Beispiel:

$blue = $image->colorAllocate(0,0,255);  
for($i=0; $i<100; $i++)  
{  
  $image->setPixel(15,$i,$blue);  
}

Dieses kurze Beispiel ändert die Farbe der Pixel 0 bis 99 auf der x-Achse 15 in Blau. Es wird also eine Linie gezeichnet, da die manipulierten Pixel alle hintereinander in einer Reihe liegen. Es geht jedoch auch einfacher, und zwar mit den Methoden line bzw. dashedLine. Die Methode line zeichnet eine normale Linie zwischen den angegebenen Eckpunkten in der angegebenen Farbe. Die Methode dashedLine hingegen zeichnet eine gestrichelte Linie zwischen den Eckpunkten in der angegebenen Farbe.

void image->line(int x1, int y1, int x2, int y2, int color)  
void image->dashedLine(int x1, int y1, int x2, int y2, int color)

Sie müssen also zuerst den Startpunkt der Linie als x- und y-Koordinaten getrennt angeben. Anschließend folgt der Endpunkt der Linie, ebenfalls getrennt in x und y. Zum Schluss folgt die Farbe, in der die Linie gezeichnet werden soll.

Da der Start- und der Endpunkt nicht horizontal oder vertikal auf einer Linie liegen müssen, können Sie so natürlich auch eine Diagonale zeichnen. Das folgende Beispiel demonstriert dies.

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

$image = new GD::Image(200,200);

$white = $image->colorAllocate(255,255,255);
$black = $image->colorAllocate(0,0,0);

for($i=10; $i<=190; $i++)
{
  $image->setPixel(195,$i,$black);
  $image->setPixel($i,195,$black);
}

$image->line(10,5,190,5,$black);
$image->dashedLine(5,10,5,190,$black);
$image->line(10,10,190,190,$black);
$image->dashedLine(10,190,190,10,$black);

print "Content-Type: image/png\n\n";
print $image->png;

Listing 2.3: Durchgezogene und solide Linien zeichnen mit Perl

In Listing 2.3 werden insgesamt sechs Linien gezeichnet. Nach der obligatorischen Definition der Grafik und zweier Farben werden zunächst mit einer for-Schleife und der Methode setPixel zwei Linien gezeichnet. Anschließend werden zwei Linien mit der Methode line und zwei Linien mit der Methode dashedLine gezeichnet. Zum Schluss wird die Grafik an den Browser gesendet.

Ausgabe des Listing 2.3 im Browser
Abbildung 2.5: Ausgabe des Listing 2.3 im Browser

# Rechtecke

Zum Zeichnen von Rechtecken stehen Ihnen wiederum zwei Methoden zur Verfügung. Die Methode rectangle zeichnet ein normales Rechteck und die Methode filledRectangle ein mit Farbe gefülltes Rechteck.

void image->rectangle(int x1, int y1, int x2, int y2, int color)  
void image->filledRectangle(int x1, y1, int x2, int y2, int color)

Die beiden ersten Parameter x1 und y1 erwarten jeweils die x- und y-Koordinate des Startpunktes, während x2 und y2 die Koordinaten des Endpunktes erwarten. Als fünften Parameter color können Sie dann die Farbe angeben, in der das Rechteck gezeichnet werden soll.

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

$image = new GD::Image(200,200);

$white = $image->colorAllocate(255,255,255);
$black = $image->colorAllocate(0,0,0);

$image->rectangle(5,5,95,95,$black);
$image->filledRectangle(105,5,195,95,$black);

print "Content-Type: image/png\n\n";
print $image->png;

Listing 2.4: Perl-Script, das zwei Rechtecke zeichnet

Nachdem das Objekt instanziiert wurde und zwei Farben definiert wurden, zeichnet das Script zwei Rechtecke in die Grafik: eines, das nur über eine Außenlinie verfügt, und ein zweites, das flächig gefüllt ist.

Ausgabe des Listing 2.4 im Browser
Abbildung 2.6: Ausgabe des Listing 2.4 im Browser

# Kreise und Kreisbögen

Wenn Sie das Kapitel zur GD-Library in Verbindung mit PHP gelesen haben, kennen Sie zwei Funktionen, mit denen Sie Kreise bzw. Ellipse zeichnen können: imageEllipse und imageFilledEllipse. Diese gibt es in Perl nicht. Daher müssen Sie sich hier ein wenig Abhilfe schaffen. Zunächst einmal werde ich erklären, wie Sie trotz allem einen Kreis oder eine Ellipse zeichnen können.

Die einzige Methode, mit der Sie in Perl etwas Kreisähnliches zeichnen können, ist arc. Sie zeichnet einen Kreisbogen, bei dem Sie den Mittelpunkt des Kreises bzw. der Ellipse festlegen können, aber auch die Höhe und die Breite. Außerdem können Sie den Start- und den Endwinkel angeben, von wo bis wo der Bogen gezeichnet werden soll.

void image->arc(int x, int y, int width, int height, int start_ang, int end_ang, int color)

Für die Parameter x und y können Sie nun einen beliebigen Punkt in der Grafik angeben. Der zu zeichnende Bogen orientiert sich dann an diesem Punkt. Die beiden Parameter width und height definieren die Breite des Kreises bzw. der Ellipse und die Höhe. Mit den Parametern start_ang und end_ang können Sie den Start- und den Endwinkel festlegen. Dabei ist eine Angabe zwischen 0 und 360 erlaubt. Mit dem letzten Parameter definieren Sie dann die Farbe, in der der Bogen gezeichnet werden soll.

Damit Sie den Bogen eines Kreises zeichnen können, müssen den Parametern width und height die gleichen Werte zugewiesen werden. Ein Kreis ist schließlich genauso hoch wie breit. Wenn Sie den Bogen einer Ellipse zeichnen, müssen die Werte unterschiedlich sein.

Das folgende Beispiel zeichnet zwei normale Kreisbögen in einer Grafik.

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

$image = new GD::Image(200,200);

$white = $image->colorAllocate(255,255,255);
$black = $image->colorAllocate(0,0,0);

$image->arc(50,50,90,90,180,270,$black);
$image->arc(150,50,90,90,0,180,$black);

print "Content-Type: image/png\n\n";
print $image->png;

Listing 2.5: Zeichnen von zwei Kreisbögen

Nachdem eine neue leere Grafik erzeugt worden ist und zwei Farben definiert wurden, werden mit der arc-Methode zwei Kreisbögen gezeichnet. Der erste Kreisbogen erhält den Mittelpunkt (50,50) und der dazugehörige Kreis einen Durchmesser von 90 Pixel. Der Startwinkel des Bogens wird auf 180 und der Endwinkel auf 270 festgelegt. Der zweite Kreisbogen erhält als Mittelpunkt den Punkt (150,50) und als Durchmesser ebenfalls 90 Pixel. Der Startwinkel ist diesmal jedoch 0 und der Endwinkel 180. Anschließend erfolgt die Ausgabe im Browser.

Ausgabe des Listing 2.5
Abbildung 2.7: Ausgabe des Listing 2.5

Um nun einen ganzen Kreis oder eine ganze Ellipse zeichnen zu können, müssen Sie einfach nur den Startwinkel auf 0 und den Endwinkel auf 360 setzen. Mit anderen Winkeln funktioniert dies nicht. Der Grund ist folgender: Die Werte 0 und 360 stehen beide für ein und denselben Winkel. Von daher kann die Methode arc von einem zum anderen Winkel einen Bogen zeichnen. Für den Winkel 180 gibt es keinen alternativen Wert. Würden Sie als Start- und als Endwinkel also 180 angeben, wird in der Grafik gar nichts gezeichnet. Versuchen Sie sich dies einfach an einer for-Schleife klarzumachen. Würden Sie eine Angabe wie for($i=0; $i<360; $i++) machen, wird die Schleife 360-mal durchlaufen. Bei einer Angabe wie for($i=180; $i<180; $i++) würde die Schleife kein einziges Mal durchlaufen werden.

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

$image = new GD::Image(300,200);

$white = $image->colorAllocate(255,255,255);
$black = $image->colorAllocate(0,0,0);

$image->arc(50,50,90,90,0,360,$black);
$image->arc(150,50,90,90,0,360,$black);
$image->arc(100,150,180,90,0,360,$black);
$image->arc(250,100,90,180,0,360,$black);

print "Content-Type: image/png\n\n";
print $image->png;

Listing 2.6: Zeichnen von Kreisen und Ellipsen

In Listing 2.6 werden zwei Kreise und zwei Ellipsen gezeichnet. Der erste Kreis erhält den Mittelpunkt (50,50) und einen Durchmesser von 90 Pixel, der zweite Kreis den Mittelpunkt (150,50) und ebenfalls den Durchmesser 90. Die erste Ellipse bekommt den Mittelpunkt (100,150), eine Breite von 180 Pixel und eine Höhe von 90 Pixel. Die zweite Ellipse hingegen erhält den Mittelpunkt (250,100), eine Breite von 90 Pixel und eine Höhe von 180 Pixel. Was beide Kreis- und Ellipsenbögen gemeinsam haben, ist zum einen die Farbe und zum anderen der Start- und Endwinkel, nämlich 0 und 180.

Ausgabe des Listing 2.6 im Browser
Abbildung 2.8: Ausgabe des Listing 2.6 im Browser

Noch wird mit dieser alternativen Lösung nicht die volle Leistungsfähigkeit von PHP erreicht. Dort können immerhin auch flächige Kreise und Ellipsen gezeichnet werden. Wie sich dieses Problem beheben lässt, wird aber im folgenden Abschnitt geklärt.

# Flächen füllen

Die Methode filledRectangle ermöglicht es Ihnen, ein mit Farbe gefülltes Rechteck zu zeichnen. Dabei wird sowohl für den Außenrahmen als auch zum Füllen die gleiche Farbe verwendet. Manchmal ist es aber auch gewollt, das Rechteck mit einer anderen Farbe zu füllen. Bei Kreisen und Ellipsen haben Sie das Problem, dass Sie beide nicht mit einer Farbe gefüllt zeichnen können. Hier kann die Methode fill Abhilfe schaffen. Sie kann eine Fläche mit Farbe füllen. Dafür müssen ihr lediglich der Startpunkt und die gewünschte Farbe übergeben werden.

void image->fill(int x, int y, int color)

Der Startpunkt wird getrennt als Parameter x und y, die Füllfarbe als Parameter color übergeben. Ein Beispiel:

$image->fill(50,50,$black);

Die Methode fill würde nun am Punkt (50,50) beginnen, die Fläche mit der Farbe $black einzufärben. Dabei ist die Farbe des Pixels wichtig, der an diesem Punkt liegt. Die Methode ändert nämlich nur so lange die Farbe der Pixel, bis sie auf einen Pixel stößt, dessen Farbe sich von derjenigen des Ausgangspunkts unterscheidet.

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

$image = new GD::Image(200,200);

$white = $image->colorAllocate(255,255,255);
$black = $image->colorAllocate(0,0,0);
$lightgray = $image->colorAllocate(192,192,192);

$image->rectangle(10,10,190,190,$black);
$image->fill(50,50,$lightgray);

print "Content-Type: image/png\n\n";
print $image->png;

Listing 2.7: Zeichnen eines Rechtecks und Füllen mit einer anderen Farbe

Nachdem die neue leere Grafik erzeugt worden ist, werden die drei Farben $white, $black und $lightgray definiert. Dann wird ein Rechteck in der Farbe $black gezeichnet und mit der Methode fill am Startpunkt (100,100) eine Flächenfüllung mit der Farbe $lightgray gestartet. Das Ergebnis ist in Abbildung 2.9 dargestellt.

Ausgabe des Listing 2.7 im Browser
Abbildung 2.9: Ausgabe des Listing 2.7 im Browser

Um nun auch Kreise und Ellipsen mit einer Farbe zu füllen, müssen Sie zuvor lediglich einen Kreis oder eine Ellipse zeichnen und anschließend eine Flächenfüllung mit fill starten. Der Startpunkt der Flächenfüllung muss dabei innerhalb des Kreises bzw. der Ellipse liegen.

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

$image = new GD::Image(300,200);

$white = $image->colorAllocate(255,255,255);
$black = $image->colorAllocate(0,0,0);
$lightgray = $image->colorAllocate(192,192,192);

$image->arc(50,50,90,90,0,360,$black);
$image->arc(150,50,90,90,0,360,$black);
$image->arc(100,150,180,90,0,360,$black);
$image->arc(250,100,90,180,0,360,$black);

$image->fill(50,50,$black);
$image->fill(150,50,$lightgray);
$image->fill(100,150,$black);
$image->fill(250,100,$lightgray);

print "Content-Type: image/png\n\n";
print $image->png;

Listing 2.8: Zeichnen zweier Kreise und Ellipsen und Füllen mit einer Farbe

Nach dem Erzeugen einer neuen Grafik und dem Definieren von drei Farben werden zwei Kreise und zwei Ellipsen in die Grafik gezeichnet. Anschließend wird an vier verschiedenen Startpunkten eine Flächenfüllung gestartet. Die vier Startpunkte liegen jeweils innerhalb eines anderen Kreises bzw. einer anderen Ellipse. Zum Schluss wird die Grafik noch im Browser ausgegeben.

Ausgabe des Listing 2.8 im Browser
Abbildung 2.10: Ausgabe des Listing 2.8 im Browser

Wie Sie sehen können, ist es durch die Kombination der Methoden arc und fill problemlos möglich, die aus PHP bekannten und fehlenden Funktionen zu ersetzen – auch wenn dies leider mit etwas mehr Tipparbeit verbunden ist.

# Transparenz

Leider ist in Perl die aus PHP bekannte Möglichkeit, Farben einen Transparenzgrad zuzuweisen, nicht vorhanden. In Perl können Sie nur eine Farbe der Farbpalette als transparent definieren. Dafür muss die Grafik jedoch im PNG-Format ausgegeben werden.

Um nun also eine Farbe als transparent zu definieren, benötigen Sie die Methode transparent. Dieser Methode müssen Sie den Index der Farbe übergeben, die transparent werden soll. Anschließend gibt die Methode den Index der Farbe in der Farbpalette der Grafik zurück.

int image->transparent(int color)

Achten Sie dabei darauf, dass Sie zuerst die Farben ganz normal mit colorAllocate definieren. Erst danach können Sie eine der definierten Farben als Parameter an die Methode transparent übergeben.

$lightgray = $image->colorAllocate(192,192,192);  
$transparent = $image->transparent($lightgray);

In diesem kurzen Beispiel wird zuerst die Farbe $lightgray und im nächsten Schritt die Farbe als transparent definiert. Der Index der nun transparenten Farbe wird in der Variablen $transparent gespeichert.

Den Rückgabewert der Funktion zu speichern hat gleich zwei Vorteile. Der erste Vorteil ist, dass Sie unabhängig davon, welche Farbe Sie als transparent definiert haben, auf diese Farbe immer mit $transparent zugreifen können und sie dementsprechend auch an andere Methoden als zu zeichnende Farbe übergeben können. Außerdem ist der Rückgabewert immer dann ungleich –1, wenn eine Farbe als transparent definiert wurde.

Der Wert –1 spielt außerdem noch eine andere wichtige Rolle. Mit ihm können Sie nämlich auch die transparente Farbe wieder deaktivieren, wenn Sie den Wert an die Methode transparent als Parameter übergeben.

$transparent = $image->transparent(-1);

Diese Anweisung würde also die transparente Farbe wieder deaktivieren.

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

$image = new GD::Image(300,200);

$black = $image->colorAllocate(0,0,0);
$darkgray = $image->colorAllocate(92,92,92);
$lightgray = $image->colorAllocate(192,192,192);

$transparent = $image->transparent($darkgray);

$image->arc(50,50,90,90,0,360,$transparent);
$image->arc(150,50,90,90,0,360,$transparent);
$image->arc(100,150,180,90,0,360,$transparent);
$image->arc(250,100,90,180,0,360,$transparent);

$image->fill(50,50,$transparent);
$image->fill(150,50,$lightgray);
$image->fill(100,150,$transparent);
$image->fill(250,100,$lightgray);

print "Content-Type: image/png\n\n";
print $image->png;

Listing 2.9: Transparente Farbe in Perl verwenden

Nach der Definition der neuen leeren Grafik werden die drei Farben $black, $darkgray und $lightgray definiert. Im nächsten Schritt wird $darkgray dann als transparente Farbe festgelegt. Anschließend werden zwei Kreise und zwei Ellipsen gezeichnet, und zwar mit der Farbe, die zuvor als transparent festgelegt wurde und deren Palettenindex in der Variablen $transparent gespeichert wurde. Dann werden zwei der Kreise und Ellipsen mit der transparenten Farbe gefüllt und die anderen beiden mit der Farbe $lightgray.

Ausgabe des Listing 2.9
Abbildung 2.11: Ausgabe des Listing 2.9

Auch wenn es in der Abbildung 2.11 schwer zu erkennen ist, können Sie die transparente Farbe doch sehen, und zwar an den Stellen, wo der weiße Hintergrund des Dokuments durchschimmert.

# Erweiterte Textausgabe

Fünf Schriften stehen Ihnen standardmäßig in der GD-Library zur Auswahl, die sich jedoch alle nur durch ihre Schriftgröße und -dicke unterscheiden. Der Schrifttyp ist immer der gleiche. Sie können in Perl jedoch auch so genannte TrueType-Fonts zur Ausgabe von Text verwenden. Diese TrueType-Fonts enden in der Regel auf .ttf und finden sich auf allen möglichen Betriebssystemen.

Um einen TrueType-Font zur Ausgabe von Text in einer Grafik zu verwenden, müssen Sie die Methode stringTTF einsetzen. Das Besondere an dieser Methode ist außerdem, dass die Kanten des ausgegebenen Textes geglättet werden. Dieses Kantenglätten wird auch als »Antialiasing« bezeichnet. Dadurch ist die Schrift besser zu lesen.

list image->stringTTF(int color, string ttf_font, float size, int angle, int x, int y, string text)

Zunächst müssen Sie der Methode die Farbe mitteilen, in der der Text ausgegeben werden soll. Anschließend folgt als Parameter ttf_font, der relative oder absolute Pfad- und Dateiname der Schriftarten-Datei. Dann folgt als Parameter size die Größe in Punkt, in der die Textausgabe erfolgen soll. Dabei dürfen auch Dezimalzahlen angegeben werden, wobei Sie anstatt eines Kommas einen Punkt als Trennzeichen verwenden müssen. Der Parameter angle definiert den Winkel des Textes. 0 bedeutet, dass der Text ganz normal horizontal ausgegeben wird, und 180 bedeutet, dass der Text um 180 Grad gedreht wurde und somit auf dem Kopf steht. Die Parameter x und y definieren den Punkt, an dem die linke untere Ecke des Textes bei der Ausgabe angesetzt wird. Zum Schluss wird als Parameter text noch der auszugebende Text notiert.

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

$image = new GD::Image(125,100);

$white = $image->colorAllocate(255,255,255);
$black = $image->colorAllocate(0,0,0);

$image->stringTTF($black,'verdana.ttf',12.0,0,10,50,'Verdana');

print "Content-Type: image/png\n\n";
print $image->png;

Listing 2.10: Ausgabe eines Textes mit einer TTF-Schrift

Nach der Instanziierung der Grafik und der Definition zweier Farben wird mit der Methode stringTTF eine Textausgabe erzeugt. Dafür werden die Farbe $black und die Schriftart verdana.ttf verwendet. Die Schriftgröße wird auf 12 Punkt festgelegt und der Winkel auf 0. Die linke untere Ecke des Textes wird am Punkt (10,50) ausgerichtet. Der eigentliche Text, der ausgegeben werden soll, ist in Verdana. Abbildung 2.12 zeigt die Ausgabe des Listings im Browser. Auf der linken Seite der Abbildung finden Sie die 1:1-Ausgabe. Auf der rechten Seite der Abbildung ist die Ausgabe um den Faktor 6 vergrößert dargestellt. Die Vergrößerung stellt die Kantenglättung sehr deutlich dar.

Ausgabe des Listing 2.10: einmal 1:1 (links) und einmal 6:1 (rechts)
Abbildung 2.12: Ausgabe des Listing 2.10: einmal 1:1 (links) und einmal 6:1 (rechts)

Die Ausgabe in Abbildung 2.13 wurde ebenfalls durch das Listing 2.10 erzeugt, nur dass der Winkel des auszugebenden Textes auf 180 gesetzt wurde.

$image->stringTTF($black,'verdana.ttf',12.0,180,10,50,'Verdana');

Ausgabe des Textes aus Listing 2.10 mit dem Winkel 180
Abbildung 2.13: Ausgabe des Textes aus Listing 2.10 mit dem Winkel 180

Eine Drehung des Textes können Sie auch mit den in der GD-Library enthaltenen Schriften erzeugen, indem Sie die Methode stringUp verwenden. Diese gibt den übergebenen Text, um 90 Grad gegen den Uhrzeigersinn gedreht, in der Grafik aus.

void image->stringUp(int font, int x, int y, string text, int color)

Der Parameter font erwartet die zu verwendende Schrift, während x und y den Punkt angeben, an dem die linke untere Ecke des Textes ausgegeben wird. Der Parameter text nimmt den auszugebenden Text und color inmmt die zu verwendende Farbe entgegen.

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

$width = 175;
$height = 25;
$font = gdMediumBoldFont;
$str = 'Ein Text';

$image = new GD::Image($width,$height);

$yellow = $image->colorAllocate(255,255,0);
$blue = $image->colorAllocate(0,0,255);

$str_x = ($width - ($font->width * length($str))) / 2;
$str_y = ($height - $font->height) / 2;
$image->string($font,$str_x,$str_y,$str,$blue);

print "Content-Type: image/png\n\n";
print $image->png;

Listing 2.11: Ausgabe eines um 90 Grad gedrehten Textes

Ausgabe des Listing 2.11 im Browser
Abbildung 2.14: Ausgabe des Listing 2.11 im Browser

# Thumbnails erzeugen

Thumbnails sind kleinere Varianten von größeren Bildern. Sie werden häufig für Galerien verwendet, um dem Benutzer eine Möglichkeit zu bieten, eine kleine Vorschau eines Bildes anzusehen und dann zu entscheiden, ob er sich das größere und meist ladeintensivere Pendant anzeigen lassen möchte. Da es sehr mühselig ist, zu jedem großen Bild ein kleineres von Hand zu erzeugen, wird bei großen Bildergalerien häufig auf Scripts gesetzt, die diese Arbeit übernehmen. So stellt z. B. das Perl-Modul GD extra eine Methode zur Verfügung, die solche Arbeiten übernehmen kann: copyResized. Mit dieser Funktion können Sie eine Quellgrafik kopieren und verkleinern.

void image->copyResized(object src_im, int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h)

Beachten Sie unbedingt, dass die Methode copyResized von dem Objekt aufgerufen werden muss, das als Ziel des Kopier- und Verkleinerungsvorgangs gilt.

Als Parameter src_im müssen Sie das Objekt angeben, das die Quellgrafik enthält. Danach folgen die beiden Parameter dst_x und dst_x. Diese beiden definieren den Punkt, an dem die Quellgrafik mit der linken oberen Ecke in die Zielgrafik eingefügt werden soll. Dementsprechend definieren die Parameter src_x und src_y, von welchem Punkt in der Quellgrafik an der zu kopierende Abschnitt beginnen soll. Die nun folgenden Parameter dst_w und dst_h legen die Breite der Quellgrafik in der Zielgrafik fest. src_w und src_h wiederum legen fest, wie breit und wie hoch der zu kopierende Ausschnitt sein soll.

Betrachten Sie den zu kopierenden Bildausschnitt als Rechteck. Die linke obere Ecke des Rechtecks wird durch die Parameter src_x und src_y festgelegt. Die Breite und die Höhe des Rechtecks definieren sich durch die Parameter src_w und src_h. Das Gleiche gilt natürlich für die Zielgrafik, nur dass die Parameter hier dst_x, dst_y, dst_w und dst_h lauten. Je nachdem, wie sich die Breite und die Höhe des Quellausschnitts von denjenigen des Zielausschnitts unterscheiden, wird der Quellausschnitt entsprechend angepasst, also entweder verkleinert oder vergrößert.

$dst_im->copyResized($src_im,10,10,0,0,80,40,160,80);

Dieser Aufruf würde aus der Quellgrafik $src_im einen Ausschnitt von der Größe 160 × 80 Pixel herauskopieren, der an der Position (0,0) beginnt, ihn auf die Größe 80 × 40 Pixel verkleinern und in die Zielgrafik $dst_im an der Position (10,10) einfügen. Der Ausschnitt wird dadurch verkleinert.

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

# Variablen setzen
$src_file = param('file');
$max_px = 100;
$dst_w = 0;
$dst_h = 0;

# Dateityp ermitteln und öffnen
open(IMG,"<$src_file");
$pos = index($src_file,'.') +  1;
$file_ext = substr($src_file,$pos);
if(lc($file_ext) == 'png')
{
  $image_src = newFromPng GD::Image(IMG);
}
elsif(lc($file_ext) == 'jpg')
{
  $image_src = newFromJpeg GD::Image(IMG);
}
close(IMG);

# Bildgröße ermitteln
($src_w,$src_h) = $image_src->getBounds();

# Abmessungen festlegen
if($src_w > $src_h)
{
  $dst_w = $max_px;
  $dst_h = $max_px / ($src_w / $src_h);
}
elsif($src_w < $src_h)
{
  $dst_h = $max_px;
  $dst_w = $max_px / ($src_h / $src_w);
}
else
{
  $dst_h = $max_px;
  $dst_w = $max_px;
}

# Bild kopieren und ausgeben
$image_dst = new GD::Image($dst_w,$dst_h);
$image_dst->copyResized($image_src,0,0,0,0,$dst_w,$dst_h,$src_w,$src_h);
print "Content-Type: image/png\n\n";
print $image_dst->png;

Listing 2.12: Perl-Script zum Erzeugen von Thumbnails

In Listing 2.12 werden nach dem Einbinden der beiden Module CGI und GD erst einmal vier Variablen definiert. Die erste, $src_file, erhält den Wert, der beim Aufruf des Scripts als Parameter file übergeben wurde. Die Variable $max_px legt fest, wie breit oder wie hoch das Thumbnail maximal sein darf – in diesem Fall 100 Pixel. Dann wird noch den beiden Variablen $dst_w und $dst_h der Wert 0 zugewiesen. Diese beiden Variablen werden später benötigt, um die errechnete Größe der Zielgrafik zu speichern.

Dann beginnt der Abschnitt, der sich um das Laden und Erzeugen der Quellgrafik kümmert. Zunächst wird die Quellgrafik geöffnet und das Dateihandle IMG erzeugt. Dann wird versucht, die Dateiendung der Grafik zu ermitteln. In $pos wird das erste Vorkommen des Zeichens . plus 1 im Dateinamen gespeichert. Dann wird mit der Funktion substr der Teil der Zeichenkette $src_file von $pos bis zum Ende der Zeichenkette kopiert und in $file_ext gespeichert. Nun folgt ein relativ kurzes if-Konstrukt. In diesem Konstrukt wird überprüft, ob $file_ext entweder der Zeichenkette png oder der Zeichenkette jpg entspricht. Entspricht $file_ext der Zeichenkette png, wird das Objekt $image_src erzeugt, indem der Konstruktor newFromPng des GD-Moduls aufgerufen wird und das Dateihandle IMG als Parameter übergeben wird. Ist die Quellgrafik vom Typ JPEG, wird der Konstruktor newFromJpeg aufgerufen und das Dateihandle IMG ebenfalls übergeben.

Im nächsten Schritt wird den Variablen $src_w und $src_h die Breite und Höhe der Quellgrafik zugewiesen, indem sie mit der getBounds-Methode ausgelesen werden. Dann folgt die Berechnung der Größe der Zielgrafik. Damit auch das Seitenverhältnis der Quellgrafik beibehalten wird, werden ebenfalls in einer if-Anweisung die Relationen der beiden Variablen $src_w und $src_h bestimmt. Ist die Quellgrafik breiter als hoch, dann bekommt die Zielgrafik die maximale Breite. Ist die Quellgrafik höher als breit, dann bekommt die Zielgrafik die maximale Höhe. Der entsprechend andere Wert wird dann aus dem Seitenverhältnis der Quellgrafik errechnet. Sollte die Quellgrafik quadratisch, also so breit wie hoch sein, erhält die Zielgrafik die maximale Breite und Höhe.

Zum Schluss wird eine neue Grafik mit dem Objektnamen $image_dst erzeugt. Diese Grafik bekommt die Breite und Höhe zugewiesen, die zuvor errechnet und in $dst_w und $dst_h gespeichert wurden. Anschließend wird die Methode copyResized des $image_dst-Objekts ausgeführt. Die Methode bekommt die Parameter übergeben, die erforderlich sind, um die vollständige Quellgrafik zu kopieren, zu verkleinern und in die neue Grafik einzufügen. Dann erfolgt die Ausgabe des MIME-Typs und der Grafikdaten.

Um nun in einem HTML-Dokument anstelle der Originalgrafik das Thumbnail auszugeben, müssen Sie ein img-Element einfügen und als Wert für das Attribut src das Perl-Script notieren (in diesem Fall wurde das Script aus Listing 2.12 unter dem Namen thumbs.pl gespeichert) und natürlich die zu verkleinernde und darzustellende Grafik angeben.

<img src="thumbs.pl?file=eisberg.png">

Und so könnte dann die Ausgabe im Browser aussehen:

Thumbnails mit Perl
Abbildung 2.15: Thumbnails mit Perl

# Anwendungsbeispiele

In diesem Abschnitt möchte ich Ihnen zwei weitere Anwendungsbeispiele der GD-Library geben. Äquivalent zu PHP, wo ein Kreisdiagramm und ein Bild als HTML-Dokument ausgegeben wurden, werde ich Ihnen in diesem Kapitel zeigen, wie Sie ein Verlaufsdiagramm erzeugen und eine Grafik als Textdatei ausgeben können.

# Verlaufsdiagramm

Während im PHP-Kapitel bereits das Kreisdiagramm besprochen wurde, möchte ich Ihnen an dieser Stelle zeigen, wie Sie einen weiteren Diagrammtyp erzeugen können. Verlaufsdiagramme eignen sich immer dann besonders gut zum Darstellen von Daten, wenn Sie zu bestimmten Zeitpunkten eine Messung vornehmen. So können Sie mit einem Verlaufsdiagramm die Fieberkurve eines Patienten, den Temperaturverlauf eines Monats oder den Verlauf eines Kontos darstellen.

Das Schöne an dem Diagrammtyp ist, dass Sie alle erforderlichen Funktionen bzw. Methoden bereits kennen. Ich wollte bei diesem Diagramm diesmal einen leicht anderen Weg einschlagen. Anstatt nun alle Daten wie eine Überschrift und die Achsenbeschriftungen in dieses Diagramm hineinzuzeichnen, soll das Diagramm jetzt möglichst flexibel bleiben. Sowohl die Breite als auch die Höhe des Diagramms sollen mit Parametern festgelegt werden können. Auch die Datei, aus der die Daten geholt werden, soll als Parameter übergeben werden können. Schlussendlich wird auch noch der Rahmen zwischen dem Grafikrand und dem Diagrammrand auf diese Weise festgesetzt. Den genauen Grund dafür werde ich Ihnen an späterer Stelle noch erläutern. Das Script sieht folgendermaßen aus:

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

# Parameter entgegennehmen
$datafile = param('file');
$img_w = param('width');
$img_h = param('height');
$border = param('border');

# Datendatei einlesen
open(DATAFILE,"<$datafile");
@datarows = <DATAFILE>;
close(DATAFILE);
chomp(@datarows);

# Datenzeilen in x und y trennen
for($i=0; $i<@datarows; $i++)
{
  ($x,$y) = split(/:/,$datarows[$i]);
  push(@data_x,$x);
  push(@data_y,$y);
}

# Höchstwerte für x und y ermitteln
$data_max_x = 0;
$data_max_y = 0;
foreach(@data_x)
{
  $data_max_x = $_ if($_ > $data_max_x);
}
foreach(@data_y)
{
  $data_max_y = $_ if($_ > $data_max_y);
}

# Grafik erzeugen
$image = new GD::Image($img_w,$img_h);

# Farben definieren
$bg = $image->colorAllocate(255,255,255);
$black = $image->colorAllocate(0,0,0);
$green = $image->colorAllocate(51,204,51);

# Diagrammbasis zeichnen
$image->rectangle($border,$border,$img_w-$border-1,$img_h-$border-1,$black);

# Werte zum Darstellen des Verlaufs berechnen
$diag_x = $border;
$diag_y = $img_h-$border-1;
$step_x = ($img_w-($border*2)-1) / $data_max_x;
$step_y = ($img_h-($border*2)-1) / $data_max_y;

# Verlauf zeichnen
$last_x = $data_x[0];
$last_y = $data_y[0];
for($i=1; $i<@data_x; $i++)
{
  $x1 = $diag_x + ($last_x * $step_x);
  $y1 = $diag_y - ($last_y * $step_y);
  $x2 = $diag_x + ($data_x[$i] * $step_x);
  $y2 = $diag_y - ($data_y[$i] * $step_y);
  $image->line($x1,$y1,$x2,$y2,$black);
  $image->string(gdMediumBoldFont,$x2,$y2-gdSmallFont->height,$data_y[$i],$green);
  $last_x = $data_x[$i];
  $last_y = $data_y[$i];
}

print "Content-Type: image/png\n\n";
print $image->png;

Listing 2.13: Perl-Script zum Zeichnen eines Verlaufsdiagramms

Dieses Script ist nicht ganz so lang wie das PHP-Script, das das Kreisdiagramm zeichnet (siehe Abschnitt 1.7.1, Kreisdiagramm). Dennoch bedarf es einer Erklärung.

Sobald das Script aufgerufen wird, erwartet es vier Parameter, die über die URI übergeben werden sollen. Diese werden zu Beginn des Scripts in vier verschiedenen Variablen gespeichert. Die Datei, die die darzustellenden Daten enthält, wird als Parameter file übergeben und in der Variablen $datafile gespeichert. Die Breite und Höhe der Grafik wird anhand der Parameter width und height festgelegt. Diese werden den Variablen $img_w und $img_h zugewiesen. Der vierte Parameter, border, legt fest, wie breit der Abstand zwischen der Diagrammfläche und dem Außenrand der Grafik sein soll. Der Wert wird in $border gespeichert.

Im nächsten Schritt wird nun die Datendatei eingelesen. Dafür wird mit der open-Funktion lediglich das Dateihandle DATAFILE erzeugt und die Datei in die Liste @datarows eingelesen. Dann wird das Dateihandle wieder freigegeben und die Funktion chomp aufgerufen. Sie entfernt am Ende einer Zeichenkette die Zeilenumbrüche \n. Dies ist notwendig, da die Zeilenumbrüche zuvor ebenfalls mit aus der Datei ausgelesen wurden.

Die Datendatei ist folgendermaßen aufgebaut:

x-wert:y-wert

Es wird also zuerst der Wert auf der x-Achse und dann – durch einen Doppelpunkt getrennt – der Wert auf der y-Achse notiert. Jeder dieser Punkte wird durch einen Zeilenumbruch voneinander getrennt. Eine solche Datendatei könnte folgendermaßen aussehen:

0:0  
1:1000  
2:1250  
3:1300

In einer for-Schleife werden diese Datenzeilen nun in x und y getrennt. Dafür wird einfach die Funktion split aufgerufen. Die beiden zurückgegebenen Werte werden dann an die beiden Listen @data_x und @data_y angehängt. Zum Hinzufügen der Werte wird die Funktion pop verwendet, die einen Wert an das Ende einer Liste anhängt. Nun enthalten die beiden Listen alle Werte. Die zusammengehörigen Werte besitzen dabei für x und y den gleichen Index. $data_x[2] gehört also zu $data_y[2].

Die Diagrammfläche ist vom Platz her begrenzt. Aus diesem Grund muss nun der jeweils höchste Wert von x und y ermittelt werden, damit der Verlauf später nicht über das Diagramm- bzw. Grafikende hinaus gezeichnet wird. Der höchste x-Wert wird in $data_max_x und der höchste y‑Wert in $data_max_y gespeichert. In jeweils einer Schleife wird einmal die Liste @data_x und einmal die Liste @data_y durchlaufen. $data_max_x und $data_max_y wird jeweils nur dann der Wert von $_ zugewiesen, wenn dieser größer ist als der momentane Wert von $data_max_x und $data_max_y.

Nun folgt das Erzeugen der Grafik. Dabei werden die Variablen $img_w und $img_h an den Konstruktor übergeben, um festzulegen, wie groß die neue Grafik werden soll. Dann werden die drei Farben $bg, $black und $green definiert.

An dieser Stelle wird nun der Wert der Variablen $border interessant. Es soll nämlich ein Rechteck gezeichnet werden. Dieses Rechteck stellt dann die eigentliche Diagrammfläche optisch dar. Für die linke obere Ecke des Rechtecks wird $border angegeben. Für die rechte untere Ecke wird die Breite bzw. die Höhe der Grafik abzüglich $border und abzüglich 1 notiert. Dadurch ist nun auf jeder der vier Seiten zwischen Grafikrand und Diagrammrand ein Abstand von $border Pixel zu sehen.

Im nächsten Schritt werden vier Werte berechnet, die zum Zeichnen des Verlaufs nötig sind. $diag_x und $diag_y werden benötigt, um die Eckpunkte des Verlaufs innerhalb der Diagrammfläche zu zeichnen. Dabei definieren Sie den Datenpunkt (0,0). Der Verlauf wird von links nach rechts gezeichnet. Der Datenpunkt (0,0) muss also in der linken unteren Ecke der Diagrammfläche liegen. $diag_x erhält darum den Wert von $border, und $diag_y erhält den Wert von $img_h abzüglich $border.

Die beiden Variablen $step_x und $step_y werden benötigt, um die Datenpunkte in das Koordinatensystem der Diagrammfläche umzurechnen. Dafür wird einfach errechnet, wie viele Pixel im Koordinatensystem der Diagrammfläche dem Wert 1 eines Datenpunktes entsprechen würden. Diese Berechnung ist relativ einfach. Zunächst einmal wird ermittelt, wie viele Pixel für die Diagrammfläche zur Verfügung stehen. Dafür wird von der Breite bzw. der Höhe der Grafik einfach zweimal der Wert von $border abgezogen. Damit nicht über die Diagrammfläche hinausgezeichnet wird, wird zusätzlich noch jeweils 1 abgezogen. Die Breite der Diagrammfläche in Pixel wird nun durch den höchsten Wert der x-Datenreihe geteilt und die Höhe der Diagrammfläche durch den höchsten Wert der y-Datenreihe.

Nun geht es ans Eingemachte. Der Verlauf wird gezeichnet. Da zwischen den einzelnen Datenpunkten eine Linie gezeichnet werden soll, werden immer zwei benötigt. Der letzte Datenpunkt wird deshalb in der Variablen $last_x und $last_y gespeichert. Da jedoch ein »letzter Datenpunkt« schon für den ersten Schleifendurchlauf erforderlich ist, wird $last_x und $last_y der erste Datenpunkt zugewiesen. Die for-Schleife beginnt deshalb mit 1 zu zählen und wird durchlaufen, solange $i kleiner als die Anzahl der Werte ist. Innerhalb der Schleife werden nun die Koordinaten der Datenpunkte im System des Diagramms errechnet. $x1 und $y1 erhalten die Werte für den letzten Datenpunkt und $x2 und $y2 diejenigen für den aktuellen Datenpunkt. Die Werte errechnen sich folgendermaßen:

  • $x1 ergibt sich aus dem x-Wert des letzten Datenpunktes mal dem Wert von $step_x. Außerdem muss noch $diag_x hinzuaddiert werden, damit der Punkt innerhalb der Diagrammfläche gezeichnet wird.
  • $y1 ergibt sich aus dem y-Wert des letzten Datenpunktes mal dem Wert von $step_y. Das Ergebnis wird dann von $diag_y abgezogen.
  • $x2 ergibt sich aus dem x-Wert des aktuellen Datenpunktes mal $step_x plus $diag_x.
  • $y2 schlussendlich ergibt sich aus dem y-Wert des aktuellen Datenpunktes mal $step_y. Das Ergebnis wird wiederum von $diag_y abgezogen.

Dann wird die Linie zwischen den Punkten ($x1,$y1) und ($x2,$y2) gezeichnet. Die nun folgende Anweisung gibt den y-Wert des Datenpunktes aus. Damit der Text oberhalb des Datenpunktes ausgegeben wird, wird von $y2 nochmals 10 abgezogen. Die Textausgabe der y-Werte führt auch dazu, dass der Grafik ein Rahmen hinzugefügt wird. Sonst ließen sich diese Werte nämlich am Rand der Diagrammfläche nicht lesen. Nachdem die Linie gezeichnet und der Text ausgegeben worden ist, wird $last_x und $last_y noch der aktuelle Datenpunkt zugewiesen, und es beginnt ein neuer Schleifendurchlauf.

Zum Schluss werden lediglich noch der Header und die Grafik selbst ausgegeben.

Beispielausgabe des Listing 2.13
Abbildung 2.16: Beispielausgabe des Listing 2.13

Zum Ausgeben des Diagramms aus Abbildung 2.16 wurden die folgenden Datenpunkte verwendet:

0:0  
1:1000  
2:1250  
3:1300  
4:900  
5:600  
6:10  
7:2000  
8:2900  
9:2600  
10:1400  
11:1350  
12:1000

Die Datenpunkte wurden in der Datei kontostand.dat gespeichert. Das Script wurde mit der folgenden URI aufgerufen:

list2.13.pl?file=kontostand.dat&width=640&height=480&border=40

Natürlich ist das Script aus Listing 2.13 noch nicht perfekt. So erwartet es in der Datei mit den Datenpunkten als ersten Datenpunkt immer 0:0. Auch wäre eine Unterteilung der x- und y-Achsen empfehlenswert, damit sich die Werte besser ablesen lassen. Da eine solche Beschriftung zwar nicht kompliziert ist, aber trotzdem sehr viel Zeilen Code benötigt, habe ich hier darauf verzichtet, um nur das Wesentlichste hervorzuheben. Der Vorteil ist momentan, dass die Datenpunkte keine regelmäßigen Intervalle aufweisen müssen. So könnten Sie problemlos die Werte 1:1000 bis 7:2000 entfernen. Trotzdem würde der Verlauf korrekt dargestellt werden – nur eben mit weniger Eckpunkten.

# Grafik als Textdatei

Auch das nun folgende Beispiel ist wie schon der »Zeichensalat« aus Abschnitt 1.7.2 als Spielerei zu betrachten. Das »Zeichensalat«-Script wurde verwendet, um eine Grafik als HTML-Dokument auszugeben. Damit Sie hier nicht ein und dasselbe Beispiel in Perl-Form vorfinden, wird diesmal eine Grafik als Textdatei ausgegeben.

In Perl heißt die Methode, mit der Sie den Palettenindex einer Farbe ermitteln können, getPixel. Die Methode erwartet dabei die Position des Pixels, dessen Farbindex Sie auslesen möchten, und gibt diesen dann zurück.

int image->getPixel(int x, int y)

Die Koordinaten des Pixels werden dabei wieder nach x und y getrennt übergeben. Da Sie nun aber lediglich den Palettenindex der Farbe erhalten, benötigen Sie noch eine zweite Methode, die Ihnen die entsprechenden roten, grünen und blauen Farbanteile ermittelt. Diese Methode lautet rgb. Sie erwartet den Palettenindex der Farbe als Parameter und gibt dann eine Liste mit den Farbanteilen zurück.

list image->rgb(int color)

Das erste Element der Liste entspricht dem roten Farbanteil der Farbe, das zweite Element dem grünen und das dritte Element dem blauen Farbanteil.

Ein Beispiel:

$color = $image->getPixel(10,50);  
@rgb = $image->rgb($color);  
print "Rot: $rgb[0] / Grün: $rgb[1] / Blau: $rgb[2]";

Dieses Beispiel würde zuerst den Palettenindex der Farbe des Pixels an Position (10,50) ermitteln. Dann werden die einzelnen Farbanteile der Farbe ausgelesen und zum Schluss ausgegeben.

Das folgende Listing verwendet diese beiden Methoden, um die Farbanteile eines Pixels zu ermitteln und dann ein entsprechendes Zeichen auszugeben:

#!/usr/bin/perl -w
use CGI qw(:standard);
use GD;

# Variablen definieren
$img_file = param('imgfile');
$quality = param('quality');

# Dateityp ermitteln und öffnen
open(IMG,"<$img_file");
$pos = index($img_file,'.') +  1;
$file_ext = substr($img_file,$pos);
if(lc($file_ext) == 'png')
{
  $image = newFromPng GD::Image(IMG);
}
elsif(lc($file_ext) == 'jpg')
{
  $image = newFromJpeg GD::Image(IMG);
}
close(IMG);

# Grafikabmessungen ermitteln
($img_w,$img_h) = $image->getBounds();

# Ausgabequalität festlegen
$quality = 1 if($quality < 1);
$quality = 5 if($quality > 5);
$qual_h = $quality * 2;
$qual_w = $quality;

# Zeichenausgabe
print "Content-Type: text/html\n\n";
print '<pre>';
for($i=0; $i<$img_h; $i+=$qual_h)
{
  for($j=0; $j<$img_w; $j+=$qual_w)
  {
    $color = $image->getPixel($j,$i);
    @rgb = $image->rgb($color);
    $mono = ($rgb[0] + $rgb[1] + $rgb[2]) / 3;
    if($mono <= 63)
    {
      $char = "@";
    }
    elsif($mono <= 127)
    {
      $char = "o";
    }
    elsif($mono <= 191)
    {
      $char = ":";
    }
    else
    {
      $char = " ";
    }
    print $char;
  }
  print "\n";
}
print '</pre>';

Listing 2.14: Perl-Script zum Ausgeben einer Grafik als Textdatei

Beim Aufruf erwartet das Script aus Listing 2.14 zwei Parameter. Zum einen ist dies die Grafik, die ausgegeben werden soll, und zum anderen die Höhe der Ausgabequalität. Der Pfad- und Dateiname der Grafik wird als Parameter imgfile übergeben und in der Variablen $img_file gespeichert. Die Qualität wird als Parameter quality angegeben und in der Variablen $quality gespeichert.

Im nächsten Schritt wird versucht, die Grafikdatei zu öffnen, und der Dateityp anhand der Endung ermittelt. Je nachdem, ob es sich bei der Grafik um den Typ PNG oder JPEG handelt, wird der entsprechende Konstruktor aufgerufen und das erzeugte Dateihandle IMG an den Konstruktor übergeben. Der Name des neuen Objekts lautet $image. Anschließend wird die Datei wieder geschlossen.

Da die Abmessungen der Grafik später benötigt werden, um die Grafik Pixel für Pixel auszulesen, werden die Abmessungen mit getBounds ermittelt. Die Breite wird in der Variablen $img_w und die Höhe in der Variablen $img_h gespeichert.

Als Qualität kann eine Zahl zwischen 1 und 5 angegeben werden, wobei 1 der höchsten Qualität entspricht und 5 der geringsten. Sollte die Zahl kleiner als 1 sein, wird sie auf 1 gesetzt, und falls sie größer als 5 ist, auf 5. In $qual_h wird nun der Wert von $quality × 2 gespeichert, in $qual_w hingegen nur der Wert von $quality. Dies führt später dazu, dass einige Pixel einfach übersprungen und nicht eingelesen oder gar ausgegeben werden.

Nun folgt die Zeichenausgabe. Zuerst wird ein HTML-Header ausgegeben. Dies mag Sie nun vielleicht irritieren, da ich zuvor geschrieben habe, dass die Grafik als Textdokument ausgegeben wird. Der Grund dafür ist jedoch lediglich, dass einige Browser Dateien vom Typ text/plain nicht im Browser, sondern in einem Editor darstellen. Um dies zu umgehen, wird ein HTML-Header gesendet und anschließend das Start-Tag eines pre-Elements ausgegeben. So verhält sich das HTML-Dokument wie ein reines Textdokument.

Dann beginnt die erste for-Schleife. Diese wird so lange durchlaufen, wie $i kleiner als die Zeilenanzahl der Grafik ist. Bei jedem Durchlauf wird $i um $qual_h erhöht. Innerhalb der ersten for-Schleife beginnt dann die zweite for-Schleife, die durchlaufen wird, solange $j kleiner als die Spaltenzahl der Grafik ist. Bei jedem Durchlauf wird $j um $qual_w erhöht.

Im Anweisungsblock der zweiten for-Schleife wird nun der Palettenindex der Farbe des Pixels an der Position ($j,$i) ausgelesen und in $color gespeichert. Dann wird zu der Farbe in $color mit der Methode rgb eine Liste mit den Farbanteilen ausgelesen und in @rgb gespeichert. Da in einer reinen Textdatei keine Farben verwendet werden können, muss die Farbe nun zuerst in eine entsprechende Graustufe umgerechnet werden. Dafür werden alle drei Farbanteile einfach addiert und anschließend durch 3 geteilt. Das Ergebnis wird in $mono gespeichert. Das Ergebnis der Rechnung liegt immer zwischen 0 und 255.

Nun wird mit einer if-Anweisung überprüft, wie hoch der Wert von $mono ist. Abhängig davon wird ein bestimmtes Zeichen ermittelt, das ausgegeben werden soll. Ist der Wert kleiner gleich 63, wird in $char ein @-Zeichen gespeichert. Ist der Wert größer als 63, aber kleiner gleich 127, wird in $char ein o gespeichert. Ist $mono größer als 127, aber kleiner gleich 191, erhält $char einen :. Sollte $mono schlussendlich größer als 191 sein, wird $char ein Leerzeichen zugewiesen. Am Ende der inneren for-Schleife wird $char mit einer print-Anweisung ausgegeben, und am Ende der äußeren for-Schleife ein Zeilenumbruch.

Nachdem alle Zeilen und Spalten der Grafik abgearbeitet worden sind, wird noch das Ende-Tag des pre-Elements ausgegeben.

Abbildung 2.17 zeigt eine Beispielausgabe des Scripts. Die Ausgabequalität wurde auf 1 gesetzt.

Beispielausgabe des Listing 2.14
Abbildung 2.17: Beispielausgabe des Listing 2.14

Die Originalgrafik finden Sie in Abbildung 2.18.

Originaldarstellung der Grafik, die mit dem Script aus Listing 2.14 ausgegeben wurde
Abbildung 2.18: Originaldarstellung der Grafik, die mit dem Script aus Listing 2.14 ausgegeben wurde

Wie gesagt ist dies eine nette Spielerei. Außerdem ist es eine gute Übung, um sich mit dem Umgang und der Ausgabe von Grafiken vertraut zu machen.

# Zusammenfassung

  • Das GD-Modul stellt in Perl den Zugriff auf die GD-Library zur Verfügung.
  • Eine neue leere Grafik kann mit dem Konstruktor new GD::Image erzeugt werden.
  • Eine Grafik auf Basis einer vorhandenen wird entweder durch newFromPng oder newFromJpeg erzeugt.
  • Zeichenketten werden mit der Methode string oder stringUp sowie stringTTF ausgegeben.
  • Farben werden durch colorAllocate definiert.
  • Linien und Rechtecke werden mit den Methoden line, dashedLine, rectangle und filledRectangle erzeugt. Einzelne Punkte werden durch setPixel gezeichnet.
  • Kreise, Ellipsen und Kreis- und Ellipsenbögen werden mit Hilfe der Methode arc gezeichnet.
  • Mit den Methoden png oder jpeg wird eine Grafik im Browser oder in einer Datei ausgegeben.

# Fragen und Übungen

  1. Worin unterscheiden sich die Konstruktoren newFromPng und newFromJpeg?
  2. Was müssen Sie zuvor unternehmen, damit Sie mit den in Frage 1 genannten Konstruktoren eine neue Grafik auf Basis einer bereits existierenden erzeugen können?
  3. Was bewirkt die Methode copyResized?
  4. Wie könnten Sie eine farbige Grafik in Graustufen umwandeln und im Browser ausgeben? Ein Tipp: Die Lösung ist in Listing 2.14 zu finden.
  5. Setzen Sie die Überlegung aus Aufgabe 4 in ein Perl-Script um, nur dass die Grafik in Schwarz-Weiß ausgegeben wird. Es sollen also nur Schwarz oder Weiß und keinerlei Graustufen oder Farbe verwendet werden.