There are several ways to create images or graphics with PHP. First there are some well known extensions, like ext/GD, or perhaps ext/ming everybody immediately remembers, when it comes to graphics generation. But there are several structural differences not in between the avaliable libraries, but also between the image formats you can create. This article series will give you some insight on the formats and libraries, and then shows how an abstraction layer could be build.

Introduction

General notes, relevant for this article.

Terms

Some terms I use in a special way.

Image

I use it as a generalization of pictures and graphics.

Picture

Images with natural contents, like photos or drawings. Usually there are no or only few clear borders in those images.

Graphic

Computer generated graphics with technical illustrations or charts. They often contain clear borders.

Agenda

The sixth part of the article finally covers additional tool classes which will proof helpful during image creation.

With a small set of tools the automatic generation of images gets a lot simpler and allows you the construction of nice images using the existing APIs.

The code

I provide completely working code within this article, which will not be developed any further, because there are already existing packages, which try to provide such an abstraction layer, like Image_Canvas in PEAR. In the graph component from the eZ Components I personally develop very similar backends under the New BSD license, which will stay limited to the drawing methods we need for the graph component for now.

The complete source code can be downloaded here, or partially copied from the code examples. The source code is not provided under some OpenSource license, but stays under my personal copyright, like the article does. If you want to take the code and continue developing it, please send me a mail and we can discuss this.

The code is written for PHP 5.3, which can currently be compiled from the CVS and makes use of the namespaces features in 5.3.

To run the provided code, you need at least the following extensions installed:

GD (with PNG and FreeType 2 support)

cairo_wrapper 0.2.3-beta

Ming 0.3.0

The default extensions: DOM, SPL

Tools for image creation

At this stage we now got a quite complete and nice abstraction layer, but the very simple API, limited to polygons, bitmaps and texts makes this still not really usable. In this last chapter I want to show some simple tools, to enhance this, using the existing functionality in the backends.

We will create a Tools class, which will provide static methods to create various shapes, which can then be rendered by the backends. Additionally we will provide some methods to modify existing shapes.

The polygons

For now the polygons were just defined by an array of points. This is always sufficient, but starting with this chapter we want to be able to call methods on those polygons, to modify it. For this we create a new class called Polygon, which just contains one array with the Coordinate objects spanning the polygon, and extending the ArrayObject class from SPL to offer easy access to this structure.

As you can see, we only overload three methods here, to fit our purpose. The constructor should accept any amount of Coordinates as parameters, so it is usable in the same way as before. Both methods, which allow to add or modify values to the ArrayObject now implement additional type checks, so that you may only add Coordinate objects, and nothing else. This is another nice side effect, we now have the possibility to ensure the type of the contents, and it is not possible any more, that we end up with some wrong values in the polygon.

Once we got this structure, we can now start implementing modification on those.

Transformation matrices

We already came in touch with transformation matrices in some of the backends, but now it is time to explain what they do, because we will implement them ourselves, to make it possible to move shapes around, rotate them, and so on.

The coordinates we got spread all over the code define points in the two dimensional coordinate vector. As described above, a coordinate is nearly equivalent to its location vector, also consisting of the same two elements. But a vector can now be multiplied with a matrix, and we get a vector as a result. For us this means, we have a coordinate (vector), multiply it with something (matrix), and get back a modified coordinate (vector). And there are several nice things about matrices, which makes this really useful for us.

You can easily specify a matrix, that will just add something to both coordinate values, which semantically means, that we just move the coordinate (translation). You may also easily specify a matrix, that rotates the coordinate by some angle around the center point of your coordinate system - same for all other imaginable transformations, like scaling, sheering, etc.

Having those single matrices, you may multiply them with each other, and you still have a matrix, which now contains all the multiplied transformations in the given order, and you can multiply a coordinate with it which now will be transformed by all these single transformations at once.

Take a look at this short example. We create one transformation matrix out of four existing matrices, and when multiplied with the coordinate this has exactly the same effect, as the following code would have.

This feature of matrices does not only make your code more readable, but you can easily stack sets of transformations this way and optimize the transformations, by reducing the number of required multiplications. As you may notice, matrix multiplication is not commutative.

This is everything you need to know about matrices to use them, if you want to get some more in depth knowledge, you may want to read the wikipedia articles on this topic.

Matrix Implementation

To reduce the required knowledge about creating matrices, we will provide some useful helper methods, so you will be able to just create a rotation matrix by specifying the angle and do not need to worry about its internal structure.

You may check out the actual code from the provided code archive, but as I do not want to tell you how to create and multiply matrices here, I will just skip those code examples. For matrix and vector calculation an extension like pecl/operator, which allows operator overloading would be really useful. But as it wont ever go into the PHP core the code for creating a matrix would look like:

If you want to transform a polygon, you just need to transform all its points, spanning the polygon. So there is nothing special here, but we got one more class, which is required to implement this interface.

Coordinate implementation

Coordinates are the atom of graphics, so here we go with some actual implementation.

To multiply a two dimensional coordinate with a 3x3 matrix, we use to store the transformations, we need to assume a third value in the vector, which defaults to 1. You may remember from matrix multiplication, the number of columns of the first matrix must equal the number of rows in the second matrix, so that is not possible to multiply a coordinate, even with three values, with a 3x3 matrix. But as said before, we can consider a coordinate as its location vector, so that we can actually multiply them.

The implementation above just does, what is defined by the matrix definition, but reduced to the values we actually require. Consider this as a slight optimization. For a complete implementation of matrix multiplication take a look at the Matrix class.

Other implementations

I skip this here, but you may also want to let other structures we use implement Transformable, which are defined by some coordinates, like the radial gradients. This way you could also transform them, which may make sense in some applications.

Usage

So, after we made all those structures transformable, we should use this new feature in our code. The following example is only implemented using the cairo backend, but it would, of course, also work with the other backends.

We again create the canvas in the first line, and add a small background image. To create a polygon we now have to use 'new Polygon' instead of 'array', but else the API stays the same.

Beside the above mentioned convenience factories for transformation matrices, I also created a factory for a more complex matrix, which rotates a shape around any point in the graphic, besides the center point, which I use here.

Simple fractal

With this polygon and the transformation matrix, I start a for loop, which once draws a filled black polygon, then a white border around it. After this, the polygon is transformed using the above defined matrix. With such code we can easily create beautiful fractals.

Tool class

As promised, we also want to create a tool class, which makes creating shapes easier. The static methods only need to return a Polygon object, which then can be rendered transformed etc.

With those parameters available, we can simply create the Polygon, with four edges for the four edges of the rectangle. Simple, isn't it?

Ellipse sector

Ellipses and especially ellipse sectors are an example for a far more complex shape. As said in the section about polygon size reduction, it is quite hard to reduce the size of a real ellipse sector, so that we just use a polygon to roughly interpolate the ellipse sector and our already implemented algorithm works just fine. It also may look bad if you zoom in the vector formats and detect that the outer border is not round, but consists of a lot short lines.

A more complex shape, of course, also requires more complex creation code. We start the polygon with the center point of the ellipse, and then iterate over the outer border in small steps, defined by the $resolution parameter. After we added the coordinate for the last point on the outer border, we can return the completed polygon.

Usage

With this basic tool class, we now got static constructors for easy construction of more complex shapes, which can of course be used again in the code, and also be transformed, as they are only polygons.

The construction of the shapes using the Tools class is quite well readable, even for the unexperienced reader. For one of the rectangles we again create a rotation matrix, so you can see that the transformations still work.

Extensions

The possible extensions of the tool class are endless, because of the unlimited amounts of available shapes. So, just add there, whatever you need.

Shape arithmetics

A very useful, but quite complicated extensions would be basic shape arithmetics, but we limited the classes to polyogns, for now, so this simplifies this a bit.

Bsaic shape arithmetics would mean, that you are able to combine or intersect polygones, which could be really useful in several cases. Another problem you'll get here is, that the diff of one polygon with another may result in "holes" in polygones, which are not possible with out current polygon structure.

But I would welcome such an extension, and implementing this could refresh your mathematical basics and your knowledge of linear algebra.

Comments

Comments are closed. This blog only exists so that all articles can still be referenced. There is no relevant activity any more on this blog. Since spammers still also find this blog comments are shut down entirely.