Dr. O. Hoffmann
Ein Bild sagt mehr als tausend Wörter,
doch die richtigen Worte vermögen
mehr als tausend Bilder zu beschreiben.
Olaf Hoffmann
Wenn bei PHP eine sogenannte GD-Bibliothek installiert ist, kann man damit auch ganz wunderbar Pixelgraphikbilder erstellen. Vektorgraphik im Format SVG kann ohne besondere Voraussetzungen erzeugt werden.
Zu beachten ist, daß man nur ein paar Sekunden Rechenzeit
hat und nur begrenzten Arbeitsspeicher (einige Megabyte).
Aufwendige Rechnungen mit Zwischenspeicherung in arrays und
sehr große Bildformate sind damit also nicht drin, aber
mit den schnellen Rechnern von heute wundert man sich, daß
selbst dreidimensionale Bilder damit noch recht schnell erstellt werden
können.
Abwägen kann man, ob statt der Pixelgraphik nicht besser
Vektorgraphik (SVG) verwendet werden sollte.
SVG benötigt keine besondere Bibliothek und
die konkrete Umsetzung der Graphik erfolgt durch das Darstellungsprogramm,
spart also meist Arbeitsspeicher und Rechenleistung beim server, zudem
hängt die Dateigröße nicht von Höhe und Breite
des Bildes ab, sondern vor allem von der Anzahl (unterschiedlicher)
Elemente. Pixelgraphik ist also vorteilhaft, wenn viele einzelne Pixel gesetzt
werden oder allgemein sehr viele Elemente in einem relativ kleinen Bild
dargestellt werden sollen (etwa bei bestimmten Fraktaltypen). Vektorgraphik
ist sehr vorteilhaft bei klar strukturierten, wiederholten Inhalten, kann aber
auch selbst Dateien referenzieren, die Pixelgraphik enthalten. Wenn also
wiederum wenige Elemente zu einer Pixelgraphik hinzugefügt werden
sollen, kann es wiederum vorteilhaft sein, dafür Vektorgraphik zu
verwenden. Auf SVG wird in anderen Bereichen dieses
Projektes näher eingegangen, daher wird es hier im ersten Beispiel
nur um Pixelgraphik gehen.
Damit das Darstellungsprogramm weiß, welches Format dargestellt werden
soll, wird dies bei einer mittels PHP erzeugten Graphik mit
der Funktion header
mitgeteilt, bevor eine Ausgabe von Inhalt
erfolgt, gesendet wird der Inhaltstyp (englisch: content-type), ehemals auch
MIME-Typ.
Den header
sende ich uebrigens extra so spät, damit
man gegebenenfalls den Fehlerbericht sehen kann. Damit das
Bild funktioniert, darf vor dem header
allerdings keine
Ausgabe erfolgen.
Bei der Erzeugung von Pixelgraphik wird
das Bild erst in einem String abgelegt/erzeugt und
dann mit einer speziellen Funktion als Bildtyp JPEG oder
PNG direkt nach dem dafür korrekten header
rausgelassen.
Bei diesem ersten Bild lernen wir neben der prinzipiellen
Bilderstellung vor allem, wie man die Kernelemente Punkt,
Linie, Rechteck, Text malt.
Die Bildgröße kann übrigens durch Übergabe
der GET-Parameter x und y festgelegt werden.
Hier ist das Beispiel (anklickern für ein weiteres):
Schauen wir uns einfach einmal an, wie es geht:
<?php # php-Labor: erstes Bild # Fehlerbericht anschalten: error_reporting(E_ALL); # gd-version ermitteln fuer jegliche php-version # ab php 4.3 geht es einfacher mit gd_info # dazu: puffer einschalten ob_start(); # teil der phpinfo laden phpinfo(8); $info=ob_get_contents(); # puffer loeschen und beenden ob_end_clean(); # reststring hinter gd version finden $info=stristr($info, 'gd version'); # suche nach dezimalzahl preg_match('/\d/', $info, $gd); # da steht sie jetzt, die gdversion: $gdversion=$gd[0]; # info und gd loeschen unset ($info); unset ($gd); # Zufallsgerator initialisieren: # (ab PHP 4.2 kann man darauf verzichten) mt_srand ((double)microtime()*1000000); # Bildgroesse einlesen oder selbst festlegen: $horizontal=400; if(isset($_GET['x'])) { $h=$_GET['x']; # Groesse begrenzen: if (($h >= 100) AND ($h <=1000)) { $horizontal=$h; } } $vertikal = 300; if(isset($_GET['y'])) { $v=$_GET['y']; # Groesse begrenzen: if (($v >= 100) AND ($v <=1000)) { $vertikal=$v; } } # Bild erzeugen: $im = imagecreate($horizontal, $vertikal); # schon mal schwarz und weiss festlegen $white = imagecolorallocate($im,255,255,255); $black = imagecolorallocate($im,0,0,0); # jetzt zum Beispiel noch rot, gruen, blau: $f[1] = imagecolorallocate($im,255,0,0); $f[2] = imagecolorallocate($im,0,255,0); $f[3] = imagecolorallocate($im,0,0,255); # Bild erst mal weiss fuellen: imagefill ($im, 1, 1, $white); # Punkte zufaellig malen: for ($i = 0; $i <= 100; $i++) { $x1 = mt_rand(3,$horizontal-3); $y1 = mt_rand(3,$vertikal-3); $x2 = mt_rand(3,$horizontal-3); $y2 = mt_rand(3,$vertikal-3); $c = $f[mt_rand(1,3)]; imagesetpixel ($im, $x1, $y1, $c); imagesetpixel ($im, $x2, $y2, $c); } # Linien zufaellig malen: # Liniendicke kann man auch aendern, wenn # die PHP-Version und die GD-Version # hinreichend frisch ist, sonst das zu Fuss erledigen # (sieht so gerechnet sogar besser aus, als die GD # es macht...) # PHP ab 4.0.6., GD ab 2.0.1 for ($i = 0; $i <= 20; $i++) { $x1 = mt_rand(3,$horizontal-3); $y1 = mt_rand(3,$vertikal-3); $x2 = mt_rand(3,$horizontal-3); $y2 = mt_rand(3,$vertikal-3); $c = $f[mt_rand(1,3)]; $dick = mt_rand(1,10); if ($gdversion >=2) { imagesetthickness ($im, $dick); imageline ($im, $x1, $y1, $x2, $y2, $c); } else { $eex=$x1-$x2; $eey=$y1-$y2; $betrag=max(sqrt($eex*$eex+$eey*$eey),1.0); $ex=$eex/$betrag; $ey=-$eey/$betrag; $mess_p[0]=$x1+$dick/2.0*$ey; $mess_p[1]=$y1+$dick/2.0*$ex; $mess_p[2]=$x1-$dick/2.0*$ey; $mess_p[3]=$y1-$dick/2.0*$ex; $mess_p[6]=$x2+$dick/2.0*$ey; $mess_p[7]=$y2+$dick/2.0*$ex; $mess_p[4]=$x2-$dick/2.0*$ey; $mess_p[5]=$y2-$dick/2.0*$ex; imagefilledpolygon($im, $mess_p, 4 , $c); } } # Rechtecke zufaellig malen: # bei der nicht GD 2 Version muss man # bei nicht zufaelligen Bildern genau # nachrechnen/denken, wie die Dicke # konstruiert werden soll... for ($i = 0; $i <= 5; $i++) { $x1 = mt_rand(3,$horizontal-3); $y1 = mt_rand(3,$vertikal-3); $x2 = mt_rand(3,$horizontal-3); $y2 = mt_rand(3,$vertikal-3); $c = $f[mt_rand(1,3)]; $dick = mt_rand(1,5); if ($gdversion >=2) { imagesetthickness ($im, $dick); imagerectangle ($im, $x1, $y1, $x2, $y2, $c); } else { for ($j = 0; $j <= $dick; $j++) { imagerectangle ($im, $x1-$j, $y1-$j, $x2+$j, $y2+$j, $c); } } } # Text ins Bild schreiben: imagestring($im, 4, 2, $vertikal-20, 'O', $black); imagestring($im, 4, 5, $vertikal-15, 'H', $black); imagestring($im, 1, 15, $vertikal-10, '2004', $black); # header senden Header("Content-type: image/png"); # Bild raushauen ImagePNG($im); # es ginge auch alternativ: # Header("Content-type: image/jpeg"); # ImageJPEG($im); # vorsichtshalber Speicher leeren und Bearbeitung beenden: imagedestroy ($im); exit; ?>
Obgleich das für ältere Prozessoren eine Herausforderung werden kann und
alte Darstellungsprogramme wie netscape4 oder der microsoft internet explorer 6 teiltransparente Bilder
nicht darstellen können, ist es oft gut, für ein CSS-Design oder -Layout,
teiltransparente einfarbige Hintergrundbilder parat zu haben. Mittels PHP ist die Erzeugung solcher
Bilder recht einfach, ab einer Version 4.3.0 und einer GD-Bibliothek ab Version 2.0.1.
Beispiel: Teiltransparentes PNG-Bild mit PHP
Als GET-Parameter können angegeben werden:
x,y: Breite, Höhe zwischen 10 und 1000
opacity: Teiltransparenz zwischen 0 und 127
rot,gruen,blau: Farbenangaben zwischen 0 und 255
Statt die Bilder bei jedem Nutzeraufruf neu zu erzeugen, spart es meist Ressourcen, wenn
Bilder einmal erzeugt werden und dann abgespeichert, statt die jedes mal neu zu erzeugen,
es sei denn natürlich, Farbe oder Transparenz sollen sich bei jedem Aufruf
ändern können.
<?php # php-Labor: teiltransparente Bilder erzeugen # Fehlerbericht anschalten: error_reporting(E_ALL); # Bildgroesse einlesen oder selbst festlegen: $horizontal=100; if(isset($_GET['x'])) { $h=$_GET['x']; # Groesse begrenzen: if (($h >= 10) AND ($h <=1000)) { $horizontal=$h; } } $vertikal = 100; if(isset($_GET['y'])) { $v=$_GET['y']; # Groesse begrenzen: if (($v >= 10) AND ($v <=1000)) { $vertikal=$v; } } # Farben einlesen oder selbst festlegen: $rot=0 ; if(isset($_GET['rot'])) { $farbe=$_GET['rot']; if ($farbe > 255) { $farbe=255; } else if ($farbe < 0) { $farbe=0; } $rot=$farbe; } $gruen=0 ; if(isset($_GET['gruen'])) { $farbe=$_GET['gruen']; if ($farbe > 255) { $farbe=255; } else if ($farbe < 0) { $farbe=0; } $gruen=$farbe; } $blau=0 ; if(isset($_GET['blau'])) { $farbe=$_GET['blau']; if ($farbe > 255) { $farbe=255; } else if ($farbe < 0) { $farbe=0; } $blau=$farbe; } # Teiltransparenz einlesen oder selbst festlegen: $opacity ="63" ; if(isset($_GET['opacity'])) { $opacity=$_GET['opacity']; if ($opacity > 127) { $opacity=127; } else if ($opacity < 0) { $opacity=0; } } # Bild erzeugen: $im = imagecreate($horizontal, $vertikal); # Farbe festlegen $farbe = imagecolorallocatealpha($im,$rot,$gruen,$blau,$opacity); # Bild fuellen: imagefill ($im, 1, 1, $farbe); # header senden Header("Content-type: image/png"); # Bild raushauen ImagePNG($im); # vorsichtshalber Speicher leeren und Bearbeitung beenden: imagedestroy ($im); exit; ?>
Meist wird man eines der Elemente image oder object nehmen und dort per Attribut src oder data eine
externe Datei referenzieren, wenn man eine Graphik in eine XHTML-Datei integrieren will.
Es ist aber auch möglich, über eine spezielle URI-Angabe die Bilddaten direkt in die
XHTML-Datei zu integrieren, was praktisch nur sinnvoll bei kleineren Bildern ist,
dazu sollte Pixelgraphik vor allem mit base64
kodiert sein, weil es sonst kaum
wieder dekodiert werden kann. Diese Kodierung kann etwa mit PHP vorgenommen werden (wenn
das Bild öfter so verwendet wird, sollte das Ergebnis natürlich statisch abgespeichert
werden und nicht immer wieder neu berechnet werden. Eingebunden wird das dann mit einem
PNG als Beispiel etwa so:
<img src="data:image/png;base64,...Bilddaten..." alt="Bsp." />
Wenn base64
nicht vorliegt, wird ASCII-Kodierung verwendet oder die
Kodierung ist zuvor anzugeben, etwa so:
<object data="data:image/svg+xml;charset=iso-8859-1,...Bilddaten..." type="image/svg+xml" width="200" height="200"> <p>Alternativtext oder -bild</p> </object>
Beispiel: in XHTML eingebettetes SVG.
Statt ...Bilddaten... kommt dann eben das kodierte Bild. Da wird dann nicht einmal PHP gebraucht,
kann aber natürlich verwendet werden, um den Bildinhalt dynamisch zu erzeugen. Weil das SVG
innerhalb von data
auftaucht, sind Sonderzeichen natürlich wie in href
zu maskieren.
Um Chaos zu vermeiden, muß man bei Klartextformaten wie SVG auf die Anführungszeichen
aufpassen, um zum Ziel zu kommen, im Beispiel werden für den Wert des
data
-Attributes einfache
Anführungszeichen verwendet, innerhalb der SVG-Daten nur große.
All das kann durch geeignete Konvertierung umgangen werden.
Beispiel: in XHTML eingebettetes SVG mit base64
.
<?php # php-Labor: Bild einbetten # Fehlerbericht anschalten: error_reporting(E_ALL); # Bild in Speicher schreiben ob_start(); echo '<?xml version="1.0" encoding="iso-8859-1"?>'." \n"; ?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="0 0 300 300"> <rect x="100" y="100" width="100" height="100" rx="10" ry="10" fill="#aaf" stroke="#008" stroke-width="4" fill-opacity="0.5" stroke-opacity="0.7" /> <rect x="20" y="20" width="100" height="100" rx="10" ry="10" fill="#aaf" stroke="#008" stroke-width="4" fill-opacity="0.5" stroke-opacity="0.7" /> <rect x="180" y="180" width="100" height="100" rx="10" ry="10" fill="#aaf" stroke="#008" stroke-width="4" fill-opacity="0.5" stroke-opacity="0.7" /> <rect x="20" y="180" width="100" height="100" rx="10" ry="10" fill="#aaf" stroke="#008" stroke-width="4" fill-opacity="0.5" stroke-opacity="0.7" /> <rect x="180" y="20" width="100" height="100" rx="10" ry="10" fill="#aaf" stroke="#008" stroke-width="4" fill-opacity="0.5" stroke-opacity="0.7" /> </svg> <?php $im=ob_get_contents(); # Speicher leeren: ob_clean(); # Bild base64-kodieren: $im2=base64_encode ($im); $im=""; $content="Content-type: application/xhtml+xml"; header($content); echo '<?xml version="1.0" encoding="iso-8859-1"?>'." \n"; ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de"> <head> <title>Eingebettetes SVG in XHTML</title> </head> <body> <h1>Eingebettetes SVG in XHTML</h1> <div> <object width="300" height="300" type="image/svg+xml" data="data:image/svg+xml;charset=iso-8859-1;base64, <?php echo $im2 ?>"> Bei realem Beispiel Alternative nicht vergessen! </object> </div> </body> </html>
Bei XHTML kann nun auch SVG direkt eingebettet werden, ein Beispiel dafür ist etwa folgendes Dokument: Polar (deutsch), Polar (englisch).
Beispiel für eingebettete Pixelgraphik: in XHTML eingebettetes PNG mit base64
. Prinzipiell geht das auch bei HTML, da habe ich mir das Beispiel
gespart.
Das funktioniert so bei technisch aktuellen browsern, das Schema ist in RFC 2397 spezifiziert,
getestet mit Konqueror (3.3), Opera 8 und Gecko/Mozilla 1.7.8. Angeblich soll der MSIE6 zu
alt sein, um das zu verstehen, allerdings ist die RFC 2397 vom August 1998 und der MSIE6
ist erst später aufgetaucht, aber vermutlich wurde bei microsoft einfach vergessen, solche
URIs zu interpretieren, getestet habe ich es selber nicht. Das PHP sieht dann wie folgt aus:
<?php # php-Labor: Bild einbetten # Fehlerbericht anschalten: error_reporting(E_ALL); $im=file_get_contents ('./trans.png'); $im2=base64_encode ($im); $content="Content-type: application/xhtml+xml"; header($content); echo "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?> \n"; ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de"> <head> <title>Beispiel data:image</title> </head> <body> <h1>Pixelgraphik in XHTML-Datei integriert.</h1> <p> <?php echo '<img src="data:image/png;base64,'.$im2.'" alt="Bsp." />'; ?> </p> </body> </html>
Soll das Bild im gleichen Skript erst erzeugt werden, wie obige Bilder, so ergibt sich die
Schwierigkeit, daß eigentlich nur eine Ausgabe direkt an das Darstellungsprogramm oder das
Abspeichern in eine Datei vorgesehen ist. Wie ich nunmehr gelernt habe, kann man
die Ausgabe an das Darstsellungsprogramm aber auch in eine Variable umbiegen und diese dann
weiterverarbeiten, dafür stellt PHP einige Funktionen zur Verfügung, die
mit der Zeichenfolge ob_
beginnen. Mozilla, Apple und Opera möchten
ja eine Methode namens 'canvas
' etablieren, mit der man per java-script Bilder malen
kann, dies ist da das server-seitige Pendant, welches dann aber auch im Gegensatz
zu 'canvas
' wirklich funktioniert, auch ohne proprietären Unfug und ohne daß
der Nutzer java-script aktiviert haben muß.
Beispiel: in XHTML eingebettete zufällig erzeugte
PNGs mit base64
.
Das PHP sieht dann wie folgt aus:
<?php # php-Labor: Bild einbetten # Fehlerbericht anschalten: error_reporting(E_ALL); # Zufallsgerator initialisieren: # (ab PHP 4.2 kann man darauf verzichten) mt_srand ((double)microtime()*1000000); # Bildgroesse einlesen oder selbst festlegen: $horizontal=300; if(isset($_GET['x'])) { $h=$_GET['x']; # Groesse begrenzen: if (($h >= 10) AND ($h <=1000)) { $horizontal=$h; } } $vertikal = 200; if(isset($_GET['y'])) { $v=$_GET['y']; # Groesse begrenzen: if (($v >= 10) AND ($v <=1000)) { $vertikal=$v; } } # Bild erzeugen: $im = imagecreate($horizontal, $vertikal); # Farbe festlegen $rot=mt_rand(0,255); $gruen=mt_rand(0,255); $blau=mt_rand(0,255); $opacity=mt_rand(10,110); $farbe = imagecolorallocatealpha($im,$rot,$gruen,$blau,$opacity); # Bild fuellen: imagefill ($im, 1, 1, $farbe); # paar Rechtecke malen: for ($i = 0; $i <= 5; $i++) { $x1 = mt_rand(3,$horizontal-3); $y1 = mt_rand(3,$vertikal-3); $x2 = mt_rand(3,$horizontal-3); $y2 = mt_rand(3,$vertikal-3); $rot=mt_rand(0,255); $gruen=mt_rand(0,255); $blau=mt_rand(0,255); $opacity=mt_rand(10,110); $c = imagecolorallocatealpha($im,$rot,$gruen,$blau,$opacity); $dick = mt_rand(1,5); imagesetthickness ($im, $dick); imagerectangle ($im, $x1, $y1, $x2, $y2, $c); } # Bild in Speicher schreiben ob_start(); ImagePNG($im); # Speicher leeren: imagedestroy ($im); $im=ob_get_contents(); # Speicher leeren: ob_clean(); # Bild base64-kodieren: $im1=base64_encode ($im); # Bild 2 erzeugen: $im = imagecreate($horizontal, $vertikal); # Farbe festlegen $rot=mt_rand(0,255); $gruen=mt_rand(0,255); $blau=mt_rand(0,255); $opacity=mt_rand(50,110); $farbe = imagecolorallocatealpha($im,$rot,$gruen,$blau,$opacity); # Bild fuellen: imagefill ($im, 1, 1, $farbe); # paar Rechtecke malen: for ($i = 0; $i <= 5; $i++) { $x1 = mt_rand(3,$horizontal-3); $y1 = mt_rand(3,$vertikal-3); $x2 = mt_rand(3,$horizontal-3); $y2 = mt_rand(3,$vertikal-3); $rot=mt_rand(0,255); $gruen=mt_rand(0,255); $blau=mt_rand(0,255); $opacity=mt_rand(10,110); $c = imagecolorallocatealpha($im,$rot,$gruen,$blau,$opacity); $dick = mt_rand(1,5); imagesetthickness ($im, $dick); imagerectangle ($im, $x1, $y1, $x2, $y2, $c); } # Bild in Speicher schreiben ob_start(); ImagePNG($im); # Speicher leeren: imagedestroy ($im); $im=ob_get_contents(); # Speicher leeren: ob_clean(); # Bild base64-kodieren: $im2=base64_encode ($im); $content="Content-type: application/xhtml+xml"; header($content); echo '<?xml version="1.0" encoding="iso-8859-1"?>'." \n"; ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de"> <head> <title>Beispiel data:image</title> </head> <body> <h1>Zufällig erzeugte Pixelgraphiken in xhtml-Datei integriert.</h1> <p> <?php echo '<img style="position: fixed; top: 5em; left: 5em" src="data:image/png;base64,'.$im1.'" alt="Bsp. 1" />'."\n"; echo '<img style="position: fixed; top: 9em; left: 10em" src="data:image/png;base64,'.$im2.'" alt="Bsp. 2" />'; ?> </p> </body> </html>