July 2012

Dev diary: Creation of PNG animation renderer

1 Introduction and prerequsities

An L-systems are developing in iterations. They were developed to simulate plant growth so the progression of some L-system is more important than final state. To capture the progression, we need some sort of animation showing each iteration as an image — animated image. There are many ways how to approach this problem but I've chosen animated PNG (APNG).

All frames from animated L-system

Disclaimer: In this development diary are used animated PNGs (APNG) as some figures. To fully enjoy the reading, use supported browser.

1.1 Possible solutions

There are multiple ways how to create an animation of L-systems iterations. The first two ideas which came to my mind were scripted animation (with JavaScript) and GIF animated image. But I didn't like any of them so I've decided to create PNG animation. Pros and cons of all three techniques are summarized in following sections.

1.1.1 JavaScript animation

Malsys already supports rendering L-systems to vector graphic in SVG format. So the easiest way is to generate bunch of SVG images and animate them using JavaScript code. This solution is pretty easy to implement and it will work in all common browsers.

However, there are problems with such animation. It is hard to save because it is composed of many files. This problem is not only on the client side (if user wants to save it) but also on server side. For example L-systems Gallery can save only one file as main file and another one as thumbnail. It is possible to solve these problems but it would require large changes on server side and also I am not fan of JavaScript at all!

1.1.2 GIF animation

File format which supports animations should be used to overcome problems with multiple files per animation. Graphics Interchange Format (GIF) is one candidate for creating L-system animations. All iterations can be rendered as GIF images and then converted to final animation as one GIF file. However, animation in GIF format has many drawbacks:

  • GIF uses color palette of max. 256 distinct colors (per frame).
  • 24 bit color depth (RGB) which means no semi-transparency (pixel can be fully transparent or not transparent at all).
  • Some software discontinue support of animated GIFs (for example Win7).
  • Gif has relatively complicated binary format.

Limited color palette may cause lossy compression of individual frames. The problem is that each frame is compressed separately which may cause noise in animation which is so typical for animated GIFs.

GIF's binary format is relatively complicated and I want to avoid using any 3rd party libraries (or at least do not use those which are not written in .NET to preserve portability). I'd like to "do it myself" (to be able to tweak the solution to the perfection :).

1.1.3 PNG animation

Portable Network Graphics (PNG) image format was created to improve and replace GIF. It has many advantages over GIF (no limited palette, simple binary format), but PNG itself does not support animation at all. Extension called MNG was designed by members of the PNG Group, but it is relatively complex and has a different file signature, which automatically makes it incompatible with standard PNG decoders. Luckily, developers of the Mozilla Foundation created simpler format for PNG animations called APNG (animated PNG) which offers fallback to single-image display for PNG decoders that do not support APNG.

APNG is by now already supported in Mozilla Firefox and Opera web browsers. Plugins for some other web browsers can be downloaded to display APNGs correctly. It is also possible that APNG format wont be successful and the support will not improve (or it will be deprecated), but I take that risk!

1.1.4 Update on WebP

In the time of writing this (July 2012), I did not know that WebP format exists and I think that WebP would be probably better option than animated PNG because it supports compression of animated images, much better than PNG.

If WebP becomes popular, and if users would appreciate it, I will rewrite animations to WebP. Till then, enjoy APNGs!

1.2 Implementation of Bitmap renderer

To create APNG, PNG renderer is needed to produce images from which will be composed the APNG. Malsys is capable to render L-systems as SVGs, 3D scene and ASCII art (in hexagonal grid) but not as bitmap images, yet. So the first step is creation of bitmap renderer.

Because L-systems processing is highly modular, the only think which is needed is to implement one component for rendering bitmap images. Such component needs to implement IRenderer2D interface which is shown in Code listing 1.

Code listing 1: IRenderer2D interface code.
public interface IRenderer2D : IRenderer {
void InitializeState(Point startPoint, double width, ColorF color);
void MoveTo(Point endPoint, double width, ColorF color);
void DrawTo(Point endPoint, double width, ColorF color);
void DrawPolygon(Polygon2D polygon);
void DrawCircle(double radius, ColorF color);

Implementation of IRenderer2D is simple and straightforward. Drawing itself is done by GDI+ (System.Drawing namespace) which also allows to save the result to many image formats. Bitmap renderer component allows to save the result as JPEG, PNG and GIF.

Lastly, it is necessary to convert coordinates from L-system coordinate space to Bitmap's. Malsys interprets L-systems in Cartesian coordinate system where positive coordinates of x axis are to the right from the origin and positive coordinates of y axis are up from the origin However, GDI+ bitmap have inversed y axis (positive coordinates are down from origin) and image can be drawn only to the quadrant with both coordinates positive. Figure 2 shows the difference.

Malsys coordinates — Difference between Malsys and bitmap coordinate spaces.
(a) Malsys coordinates
Bitmap coordinates — Difference between Malsys and bitmap coordinate spaces.
(b) Bitmap coordinates

The conversion is done by matrix transfromations on GDI Graphics class. Code listing 2 shows the code smippet that sets up the transfromation. Variables minX and minY and minimum coordinates of drawn L-system and height is height of L-system.

Code listing 2: Set-up of Graphics class that automatically transforms drawn image from malsys to image coordinate space.
Graphics g = Graphics.FromImage(bitmap);
g.TranslateTransform(-minX, -minY, MatrixOrder.Append);
g.MultiplyTransform(new Matrix(1f, 0f, 0f, -1f, 0, 0), MatrixOrder.Append);
g.TranslateTransform(0f, height, MatrixOrder.Append);