当前位置: 首页 > 图文教程 > 网络编程 > PHP > 使用 PHP 5.0创建图形的巧妙方法

PHP
《PHP设计模式介绍》第十三章 适配器模式
《PHP设计模式介绍》第十四章 动态记录模式
《PHP设计模式介绍》第十五章 表数据网关模式
《PHP设计模式介绍》第十六章 数据映射模式
《PHP设计模式介绍》第十七章 MVC 模式
Zend Framework 入门——快速上手
Zend Framework 入门——多国语言支持
Zend Framework 入门——错误处理
Zend Framework 入门——页面布局
详细介绍php5编程中的异常处理
PHP5 OOP编程中的代理与异常
PHP程序的常见漏洞攻击分析
PHP.MVC的模板标签系统
PHP教程:PHP编码书写规范
PHP开发大型项目的方法:OOP思想
php使用curl模拟用户登陆
php对gb编码动态转utf-8编码的几种方法评测
php设计模式介绍之章代理模式
“在phpMyAdmin使用用户口令登陆”补充
PHP入门速成

使用 PHP 5.0创建图形的巧妙方法


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-09-23   浏览: 124 ::
收藏到网摘: n/a

本文将展示如何使用PHP构建面向对象的图形层。使用面向对象的系统可以用来构建复杂的图形,这比使用标准PHP库中所提供的基本功能来构建图形简单很多。

我将图形编辑程序分为两类:一类是绘图程序,利用这种程序可以一个像素一个像素地绘制图像;另外一类是制图程序,这种程序提供了一组对象,例如线、椭圆和矩形,您可以使用这些对象来组合成一幅大图像,例如JPEG。绘图程序非常适合进行像素级的控制。但是对于业务图形来说,制图程序是比较好的方式,因为大部分图形都是由矩形、线和椭圆组成的。

PHP内置的制图基本操作与绘图程序非常类似。它们对于绘制图像来说功能非常强大;但是如果您希望自己的图像是一组对象集合时,这就不太适合了。本文将向您展示如何在PHP图形库的基础上构建一个面向对象的图形库。您将使用PHPV5中提供的面向对象的扩展。

具有面向对象的图形支持之后,您的图形代码就非常容易理解和维护了。您可能还需要从一种单一的图形源将图形合成为多种类型的媒介: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.基本的图形库

<?phpclassGraphicsEnvironment{public$width;public$height;public$gdo;public$colors=array();publicfunction__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"));}publicfunctionwidth(){return$this->width;}publicfunctionheight(){return$this->height;}publicfunctionaddColor($name,$r,$g,$b){$this->colors[$name]=imagecolorallocate($this->gdo,$r,$g,$b);}publicfunctiongetGraphicObject(){return$this->gdo;}publicfunctiongetColor($name){return$this->colors[$name];}publicfunctionsaveAsPng($filename){imagepng($this->gdo,$filename);}}abstractclassGraphicsObject{abstractpublicfunctionrender($ge);}classLineextendsGraphicsObject{private$color;private$sx;private$sy;private$ex;private$ey;publicfunction__construct($color,$sx,$sy,$ex,$ey){$this->color=$color;$this->sx=$sx;$this->sy=$sy;$this->ex=$ex;$this->ey=$ey;}publicfunctionrender($ge){imageline($ge->getGraphicObject(),$this->sx,$this->sy,$this->ex,$this->ey,$ge->getColor($this->color));}}?>

测试代码如清单2所示:

清单2.基本图形库的测试代码

<?phprequire_once("glib.php");$ge=newGraphicsEnvironment(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[]=newLine("black",10,5,100,200);$gobjs[]=newLine("blue",200,150,390,380);$gobjs[]=newLine("red",60,40,10,300);$gobjs[]=newLine("green",5,390,390,10);foreach($gobjsas$gobj){$gobj->render($ge);}$ge->saveAsPng("test.png");?>

这个测试程序创建了一个图形环境。然后创建几条线,它们指向不同的方向,具有不同的颜色。然后,render方法可以将它们画到图形平面上。最后,这段代码将这个图像保存为test.png。

在本文中,都是使用下面的命令行解释程序来运行这段代码,如下所示:

%phptest.php%


图2显示了所生成的test.png文件在Firefox中的样子。

图2.简单的图形对象测试



这当然不如蒙娜丽莎漂亮,但是可以满足目前的工作需要。

添加维数

我们的第一个需求——提供图形对象的能力——已经满足了,现在应该开始满足第二个需求了:可以使用一个z值将一个对象放到其他对象的上面或下面。

我们可以将每个z值当作是原始图像的一个面。所画的元素是按照z值从最小到最大的顺序来画的。例如,让我们画两个图形元素:一个红色的圆和一个黑色的方框。圆的z值是100,而黑方框的z值是200。这样会将圆放到方框之后,如图3所示:

图3.不同z值的面



我们只需要修改一下z值就可以将这个红圆放到黑方框之上。要实现这种功能,我们需要让每个GraphicsObject都具有一个z()方法,它返回一个数字,就是z值。由于您需要创建不同的图形对象(Line、Oval和Rectangle),您还需要创建一个基本的类BoxObject,其他3个类都使用它来维护起点和终点的坐标、z值和这个对象的颜色(请参看图4)。

图4.给系统添加另外一维:z值


这个图形库的新代码如清单3所示:

清单3.可以处理z信息的图形库

<?phpclassGraphicsEnvironment{public$width;public$height;public$gdo;public$colors=array();publicfunction__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"));}publicfunctionwidth(){return$this->width;}publicfunctionheight(){return$this->height;}publicfunctionaddColor($name,$r,$g,$b){$this->colors[$name]=imagecolorallocate($this->gdo,$r,$g,$b);}publicfunctiongetGraphicObject(){return$this->gdo;}publicfunctiongetColor($name){return$this->colors[$name];}publicfunctionsaveAsPng($filename){imagepng($this->gdo,$filename);}}abstractclassGraphicsObject{abstractpublicfunctionrender($ge);abstractpublicfunctionz();}abstractclassBoxObjectextendsGraphicsObject{protected$color;protected$sx;protected$sy;protected$ex;protected$ey;protected$z;publicfunction__construct($z,$color,$sx,$sy,$ex,$ey){$this->z=$z;$this->color=$color;$this->sx=$sx;$this->sy=$sy;$this->ex=$ex;$this->ey=$ey;}publicfunctionz(){return$this->z;}}classLineextendsBoxObject{publicfunctionrender($ge){imageline($ge->getGraphicObject(),$this->sx,$this->sy,$this->ex,$this->ey,$ge->getColor($this->color));}}classRectangleextendsBoxObject{publicfunctionrender($ge){imagefilledrectangle($ge->getGraphicObject(),$this->sx,$this->sy,$this->ex,$this->ey,$ge->getColor($this->color));}}classOvalextendsBoxObject{publicfunctionrender($ge){$w=$this->ex-$this->sx;$h=$this->ey-$this->sy;imagefilledellipse($ge->getGraphicObject(),$this->sx+($w/2),$this->sy+($h/2),$w,$h,$ge->getColor($this->color));}}?>

测试代码也需要进行更新,如清单4所示。

清单4.更新后的测试代码

<?phprequire_once("glib.php");functionzsort($a,$b){if($a->z()<$b->z())return-1;if($a->z()>$b->z())return1;return0;}$ge=newGraphicsEnvironment(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[]=newOval(100,"red",50,50,150,150);$gobjs[]=newRectangle(200,"black",100,100,300,300);usort($gobjs,"zsort");foreach($gobjsas$gobj){$gobj->render($ge);}$ge->saveAsPng("test.png");?>

此处需要注意两件事情。首先是我们添加了创建Oval和Rectangle对象的过程,其中第一个参数是z值。其次是调用了usort,它使用了zsort函数来对图形对象根据z值进行排序。

在运行这个程序时,test.png文件应该如图5所示。

图5.红圆在黑方框之后



现在修改下面的代码:

$gobjs[]=newOval(200,"red",50,50,150,150);$gobjs[]=newRectangle(100,"black",100,100,300,300);

再次运行这个代码,突然这个椭圆就在这个方框上面了,如图6所示。

图6.红圆现在在黑方框上面了



红圆现在就出现在黑方框上面了,尽管它是先创建的,也是首先添加到数组中的。这就是z值的实际价值:您可以按照任何顺序来创建对象,并可以通过调整每个对象的z值来调整彼此之间的相对位置。

在这段代码中,z值排序是在这个库之外实现的。让我们通过创建一个新容器对象Group来实现这种功能,其中保存了一组GraphicsObject对象。Group对象然后再处理排序的问题。

Group类的代码如清单5所示。

清单5.Group类

functionzsort($a,$b){if($a->z()<$b->z())return-1;if($a->z()>$b->z())return1;return0;}classGroupextendsGraphicsObject{private$z;protected$members=array();publicfunction__construct($z){$this->z=$z;}publicfunctionadd($member){$this->members[]=$member;}publicfunctionrender($ge){usort($this->members,"zsort");foreach($this->membersas$gobj){$gobj->render($ge);}}publicfunctionz(){return$this->z;}}

Group对象的任务是保持一个对象数组,然后在画图时,逐个对对象zo进行排序和画图。

更新后的测试代码如清单6所示。

清单6.更新后的测试代码

<?phprequire_once("glib.php");$ge=newGraphicsEnvironment(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);$g1=newGroup(0);$g1->add(newOval(200,"red",50,50,150,150));$g1->add(newRectangle(100,"black",100,100,300,300));$g1->render($ge);$ge->saveAsPng("test.png");?>


现在所有的客户机需要做的是创建一个Group对象。它会处理排序和其他操作。

创建viewport

viewport是一个人造的坐标系统,可以转换成图像的物理坐标系统。viewport的扩展可以是您希望的任何东西。例如,x和y轴的起点和终点可以是-2和2,这样viewport坐标平面的中心就是0,0。这对于三角图形(例如sin和cosine)来说是很好的一个viewport。或者,这个viewport也可以是不对称的,其中y值的范围从-1到1,x值的范围是从0到10,000,这取决于您的需要。

这个viewport的其他值可以确保构建一个400X400的图像所采用的逻辑与构建一个4000X2000的图像所采用的逻辑是相同的。代码负责向这个viewport中写入数据,然后这个viewport自动实现到图像的物理尺寸的自动映射。

要让您的viewport正常工作,您需要将这个viewport的范围从0,0修改为1,1,这可以让图形对象回调图形环境,从而将viewport的坐标转换成物理坐标。您可以将所有的代码都放到BoxObject基类中进行简化。

图7显示了有关新添加的代码的两个内容。首先是添加的tx和ty方法,这会将x和y坐标从viewport转换成物理图像的坐标。第二个是对BoxObject增加了draw方法,它的派生类应该用来进行制图。BoxObject在render方法中实现viewport的转换,并使用物理坐标来调用draw方法。使用这种方法,Line、Oval和Rectangle类都可以利用viewport坐标,而不需要担心坐标转换的问题。

图7.所添加的图形环境viewport转换


这个新库的代码如清单7所示:

清单7.具有viewport支持的图形库

<?phpclassGraphicsEnvironment{public$width;public$height;public$gdo;public$colors=array();publicfunction__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"));}publicfunctionwidth(){return$this->width;}publicfunctionheight(){return$this->height;}publicfunctionaddColor($name,$r,$g,$b){$this->colors[$name]=imagecolorallocate($this->gdo,$r,$g,$b);}publicfunctiongetGraphicObject(){return$this->gdo;}publicfunctiongetColor($name){return$this->colors[$name];}publicfunctionsaveAsPng($filename){imagepng($this->gdo,$filename);}publicfunctiontx($x){return$x*$this->width;}publicfunctionty($y){return$y*$this->height;}}abstractclassGraphicsObject{abstractpublicfunctionrender($ge);abstractpublicfunctionz();}functionzsort($a,$b){if($a->z()<$b->z())return-1;if($a->z()>$b->z())return1;return0;}classGroupextendsGraphicsObject{private$z;protected$members=array();publicfunction__construct($z){$this->z=$z;}publicfunctionadd($member){$this->members[]=$member;}publicfunctionrender($ge){usort($this->members,"zsort");foreach($this->membersas$gobj){$gobj->render($ge);}}publicfunctionz(){return$this->z;}}abstractclassBoxObjectextendsGraphicsObject{protected$color;protected$sx;protected$sy;protected$ex;protected$ey;protected$z;publicfunction__construct($z,$color,$sx,$sy,$ex,$ey){$this->z=$z;$this->color=$color;$this->sx=$sx;$this->sy=$sy;$this->ex=$ex;$this->ey=$ey;}publicfunctionrender($ge){$rsx=$ge->tx($this->sx);$rsy=$ge->ty($this->sy);$rex=$ge->tx($this->ex);$rey=$ge->ty($this->ey);$this->draw($rsx,$rsy,$rex,$rey,$ge->getGraphicObject(),$ge->getColor($this->color));}abstractpublicfunctiondraw($sx,$sy,$ex,$ey,$gobj,$color);publicfunctionz(){return$this->z;}}classLineextendsBoxObject{publicfunctiondraw($sx,$sy,$ex,$ey,$gobj,$color){imageline($gobj,$sx,$sy,$ex,$ey,$color);}}classRectangleextendsBoxObject{publicfunctiondraw($sx,$sy,$ex,$ey,$gobj,$color){imagefilledrectangle($gobj,$sx,$sy,$ex,$ey,$color);}}classOvalextendsBoxObject{publicfunctiondraw($sx,$sy,$ex,$ey,$gobj,$color){$w=$ex-$sx;$h=$ey-$sy;imagefilledellipse($gobj,$sx+($w/2),$sy+($h/2),$w,$h,$color);}}?>

GraphicsEnvironment类中的viewport转换代码是高亮显示的,正如GraphicsObject中的render代码一样,这会回调图形环境来进行坐标转换的工作。

测试代码只需要稍加修改即可(请参看清单8)。这些对象现在需要在0,0和1,1之间的viewport中进行指定。

清单8.使用新viewport坐标的测试代码

$g1=newGroup(0);$g1->add(newOval(200,"red",0.1,0.1,0.5,0.5));$g1->add(newRectangle(100,"black",0.4,0.4,0.9,0.9));


这非常不错,但是您可能实际上并不希望使用一个0,0与1,1之间的viewport;而是希望使用任意的viewport——例如,在-1000,-1000到1000,1000之间。要让这成为可能,这个图形环境就需要知道viewport的起点和终点。

图8显示了更新后的GraphicsEnvironment类,它具有几个成员变量,用来存储viewport的起点和终点坐标:vsx,vsy和vex,vey。图形对象并不需要进行修改。

图8.具有灵活viewport规范的图形环境



清单9显示了更新后的GraphicsEnvironment代码。

清单9.更新后的GraphicsEnvironment代码

classGraphicsEnvironment{public$vsx;public$vsy;public$vex;public$vey;public$width;public$height;public$gdo;public$colors=array();publicfunction__construct($width,$height,$vsx,$vsy,$vex,$vey){$this->vsx=$vsx;$this->vsy=$vsy;$this->vex=$vex;$this->vey=$vey;$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"));}publicfunctionwidth(){return$this->width;}publicfunctionheight(){return$this->height;}publicfunctionaddColor($name,$r,$g,$b){$this->colors[$name]=imagecolorallocate($this->gdo,$r,$g,$b);}publicfunctiongetGraphicObject(){return$this->gdo;}publicfunctiongetColor($name){return$this->colors[$name];}publicfunctionsaveAsPng($filename){imagepng($this->gdo,$filename);}publicfunctiontx($x){$r=$this->width/($this->vex-$this->vsx);return($x-$this->vsx)*$r;}publicfunctionty($y){$r=$this->height/($this->vey-$this->vsy);return($y-$this->vsy)*$r;}}

现在这个构造函数可以利用另外4个参数了,它们分别是viewport的起点和终点。tx和ty函数使用新的viewport坐标,并将viewport坐标转换成物理坐标。

测试代码如清单10所示。

清单10.viewport测试代码

<?phprequire_once("glib.php");$ge=newGraphicsEnvironment(400,400,-1000,-1000,1000,1000);$ge->addColor("black",0,0,0);$ge->addColor("red",255,0,0);$ge->addColor("green",0,255,0);$ge->addColor("blue",0,0,255);$g1=newGroup(0);$g1->add(newOval(200,"red",-800,-800,0,0));$g1->add(newRectangle(100,"black",-400,-400,900,900));$g1->render($ge);$ge->saveAsPng("test.png");?>

这段测试代码会在-1000,-1000与1000,000之间创建一个viewport。对象会被重新放置,以适合这个新的坐标系统。

测试代码的输出如图9所示。

图9.viewport绘制的图像转换为一个400X400的图像



如果您希望图像的大小是400X200,就可以采用下面的方法:

$ge=newGraphicsEnvironment(400,200,-1000,-1000,1000,1000);

您会得到一个纵向缩小后的图像,如图10所示。

图10.图形的400X200版本



这展示了代码如何自动调整图像的大小来适合所请求的图像。

结束语

动态图可以为应用程序添加一个新的交互层。使用这种面向对象的系统可以让构建复杂图形变得非常简单,比使用标准的PHP库中的基本操作来画图更加简单。另外,您还可以实现画不同大小或类型的图像,并且可以长期使用相同的代码来画不同类型的媒介,例如SVG、PDF、Flash和其他类型的媒介。