本文將展示如何使用 PHP 構建面向對象的圖形層。使用面向對象的系統可以用來構建復雜的圖形,這比使用標准 PHP 庫中所提供的基本功能來構建圖形簡單很多。
我將圖形編輯程序分為兩類:一類是繪圖程序,利用這種程序可以一個像素一個像素地繪制圖像;另外一類是制圖程序,這種程序提供了一組對象,例如線、橢圓和矩形,您可以使用這些對象來組合成一幅大圖像,例如 JPEG。繪圖程序非常適合進行像素級的控制。但是對於業務圖形來說,制圖程序是比較好的方式,因為大部分圖形都是由矩形、線和橢圓組成的。
PHP 內置的制圖基本操作與繪圖程序非常類似。它們對於繪制圖像來說功能非常強大;但是如果您希望自己的圖像是一組對象集合時,這就不太適合了。本文將向您展示如何在 PHP 圖形庫的基礎上構建一個面向對象的圖形庫。您將使用 PHP V5 中提供的面向對象的擴展。
具有面向對象的圖形支持之後,您的圖形代碼就非常容易理解和維護了。您可能還需要從一種單一的圖形源將圖形合成為多種類型的媒介:Flash 電影、SVG 等等。
目標
創建一個圖形對象庫包括 3 個主要的目標:
從基本操作切換到對象上
它不使用 imageline、imagefilledrectangle 以及其他圖形函數,這個庫應該提供一些對象,例如 Line、Rectangle 和 Oval,它們可以用來制作圖像。它應該還可以支持構建更大的復雜對象或對對象進行分組的功能。
可以進行 z 值排序
制圖程序讓畫家可以在畫面表面上上下移動圖形對象。這個庫應該可以支持將一個對象放到其他對象前後的功能:它使用了一個 z 值,用來定義對象從制圖平面開始的高度。z 值越大的對象被畫得越晚,也就出現在那些 z 值較小的對象之上。
提供 viewport 的轉換
通常,數據的坐標空間與圖像的坐標空間是不同的。PHP 中的圖形基本操作是對圖像的坐標平面進行操作的。這個圖形庫應該支持 viewport 的規范,這樣您就可以在一個程序員熟悉的坐標系統中指定圖形了,並且可以自動進行伸縮來適應任何圖像的大小。
由於這裡有很多特性,您將一步步地編寫代碼來展示這些代碼如何不斷增加功能。
基礎知識
讓我們首先來看一個圖形環境對象和一個名為 GraphicsObject 的接口,它是使用一個 Line 類實現的,功能就是用來畫線。UML 如圖 1 所示。
圖 1. 圖形環境和圖形對象接口
GraphicsEnvironment 類中保存了圖形對象和一組顏色,還包括寬度和高度。saveAsPng 方法負責將當前的圖像輸出到指定的文件中。
GraphicsObject 是任何圖形對象都必須使用的接口。要開始使用這個接口,您所需要做的就是使用 render 方法來畫這個對象。它是由一個 Line 類實現的,它利用 4 個坐標:開始和結束的 x 值,開始和結束的 y 值。它還有一個顏色。當調用 render 時,這個對象從 sx,sy 到 ex,ey 畫一條由名字指定的顏色的線。
這個庫的代碼如清單 1 所示。
清單 1. 基本的圖形庫
<?php class GraphicsEnvironment { public $width; public $height; public $gdo; public $colors = array(); public function __construct( $width, $height ) { $this->width = $width; $this->height = $height; $this->gdo = imagecreatetruecolor( $width, $height ); $this->addColor( "white", 255, 255, 255 ); imagefilledrectangle( $this->gdo, 0, 0, $width, $height, $this->getColor( "white" ) ); } public function width() { return $this->width; } public function height() { return $this->height; } public function addColor( $name, $r, $g, $b ) { $this->colors[ $name ] = imagecolorallocate( $this->gdo, $r, $g, $b ); } public function getGraphicObject() { return $this->gdo; } public function getColor( $name ) { return $this->colors[ $name ]; } public function saveAsPng( $filename ) { imagepng( $this->gdo, $filename ); } } abstract class GraphicsObject { abstract public function render( $ge ); } class Line extends GraphicsObject { private $color; private $sx; private $sy; private $ex; private $ey; public function __construct( $color, $sx, $sy, $ex, $ey ) { $this->color = $color; $this->sx = $sx; $this->sy = $sy; $this->ex = $ex; $this->ey = $ey; } public function render( $ge ) { imageline( $ge->getGraphicObject(), $this->sx, $this->sy, $this->ex, $this->ey, $ge->getColor( $this->color ) ); } } ?>
<?php require_once( "glib.php" ); $ge = new GraphicsEnvironment( 400, 400 ); $ge->addColor( "black", 0, 0, 0 ); $ge->addColor( "red", 255, 0, 0 ); $ge->addColor( "green", 0, 255, 0 ); $ge->addColor( "blue", 0, 0, 255 ); $gobjs = array(); $gobjs []= new Line( "black", 10, 5, 100, 200 ); $gobjs []= new Line( "blue", 200, 150, 390, 380 ); $gobjs []= new Line( "red", 60, 40, 10, 300 ); $gobjs []= new Line( "green", 5, 390, 390, 10 ); foreach( $gobjs as $gobj ) { $gobj->render( $ge ); } $ge->saveAsPng( "test.png" ); ?>
% php test.php %