Encoding PNGs

For a server side app I’ll have to create lots of PNG.
I’ve written a little benchmark that simulates some aspects of what it’ll have to do.
The benchmark creates a single BufferedImage (currently TYPE_INT_ARGB) clears it each frame and writes some text (slightly different for each frame) into it and encodes it as a PNG into a ByteArrayOutputStream. (I’ve verified that writing to files takes only slightly longer, which means that the JIT don’t try to fool me).
I’ve ported the Benchmark to .NET 2.0, Java 1.5. and Java 1.6B Java’s ImageIO PNG encoder is very slow and I’ve already found PngEncoder from Objectplanet (commercial, but 100$ would fit into the budget) which is faster.

For 100 Images in 300 x 300 with some ĺines of text I got the following results (in milliseconds):

.NET 2.0 Beta
Rendering 100 frames: 296,875
PNG encoding 100 frames: 578,125

JDK 1.5, ImageIO
Rendering 100 frames: 230
PNG encoding 100 frames: 14111

JDK 1.5, JAI 1.1.3-alpha
Rendering 100 frames: 230
PNG encoding 100 frames: 20211

JDK 1.5 -server, PngEncoder
Rendering 100 frames: 223
PNG encoding 100 frames: 2043

JDK 1.6 -server B39, PngEncoder
Rendering 100 frames: 246
PNG encoding 100 frames: 1568

(Conslusion: .NET 2.0’s PNG encoder is much faster than the Java alternatives )

So here come my questions:

  1. Are there any tricks to make ImageIO’s PNGEncoder faster? My code for saving looks like that:
	
        public PNGSaver() 
	{
		Iterator<ImageWriter> it=ImageIO.getImageWritersByFormatName(format);
		if (!it.hasNext())
		{
			throw new IllegalStateException("No PNG ImageWriter found");
		}
		writer=it.next();
	}
	
	public void saveImage(OutputStream os,BufferedImage img) throws IOException
	{
		image = new IIOImage(img, null, null);
		writer.setOutput(new MemoryCacheImageOutputStream(os));
		writer.write(null,image,iwp);
	}
  1. Can yourecommend other fast Java PNG encoders?

  2. Do you have any other performance tricks for fast server side image generators?

Thanks,
Stefan

It might be due to the compression factor in each encoder. I’ve seen reports of ImageIO being very aggresive on compression.

So to be fair, you should also measure the file-size (or byte[].length)

You’re right. I couldn’t set the compression for any implementation (I didn’t really search for .NET) except for PngEncoder which was set to compression level 1. Nevertheless all generated files were around 14kb small (and thus hopefully comparable). For my purposes anything smaller than 30kb would be fine.
Is there a possibility to set the compression level for ImageIO or JAI and PNG?

btw. PngEncoder is very inefficient in some areas, it does copying arround memory-blocks which are not really needed and creates useless arrays. I’ve a tuned version, maybe you could try it?

lg Clemens

I’ve sent you a private message with my e-mail adress. I’m looking forward to seeing a faster encoder :wink:

for now I am quiet busy and since its non profit work you’ll have to wait arround 3 weeks till I’ve more time.
But I cannot promise how much this tuning would help…

lg Clemens

This might help:

http://catcode.com/pngencoder/

Kev

Check out the imageio codecs from Java Advanced Imaging https://jai.dev.java.net and https://jai-imageio.dev.java.net/ in particular. JAI has optimized imageio codecs.

You can ask questions here: http://www.javadesktop.org/forums/forum.jspa?forumID=59

Thanks,
Dmitri
Java2D Team

Thanks for your help so far.
Here are my results for 100 PNGs (On a P4 2.4 MHz, 2GB RAM, Java runs with server hotspot and -Xmx256M):


.NET 1.1: PNG - Standard - 11 kb
Total 984,3561 [msec]
Time for rendering 218,7458
Time for writing 734,3609

JDK 1.5 PNG - com.sun.media.imageioimpl.plugins.png.CLibPNGImageWriter - 15 kb (quality=0.8)
Time for rendering 183.7688860000001
Time for writing 5999.278679999998

JDK 1.5 PNG - com.sun.media.imageioimpl.plugins.png.CLibPNGImageWriter - 11 kb (quality=0.5)
Time for rendering 191.49081999999999
Time for writing 6993.622377999999

JDK 1.5 PNG -  com.sun.imageio.plugins.png.PNGImageWriter - 7kb
Time for rendering 182.23461700000004
Time for writing 7528.899063000002

JDK 1.5 PNG -  objectPlanet PNGEncoder - 14 kb
Time for rendering 177.66280500000002
Time for writing 1427.1216050000003

JDK 1.5 PNG - com.keypoint.PngEcoder, TYPE_INT_ARGB, compression level=1, filter=none - 12 kb
Time for rendering 196.32187699999994
Time for writing 2398.2550889999998

The native acceleration of imageio-tools doesn’t really help. Is this the correcty way to set it up?


	public PNGSaver() 
	{
		Iterator<ImageWriter> it=ImageIO.getImageWritersByFormatName(format);
		if (!it.hasNext())
		{
			throw new IllegalStateException("No PNG ImageWriter found");
		}
		while (it.hasNext())
		{
			writer=it.next();
			System.out.println("PNG Writer :"+writer.getClass());
			if (writer.getClass().getName().equals("com.sun.media.imageioimpl.plugins.png.CLibPNGImageWriter"))
			{
				System.out.println("Using PNG Writer :"+writer.getClass());
				break;
			}
		}		
		iwp = writer.getDefaultWriteParam();
		iwp.setCompressionMode( ImageWriteParam.MODE_EXPLICIT);
		iwp.setCompressionQuality( 0.5f);
	}

        /* Invoke each frame */
	public void saveImage(OutputStream os,BufferedImage img) throws IOException
	{
		image = new IIOImage(img, null, null);
		MemoryCacheImageOutputStream mos=new MemoryCacheImageOutputStream(os);
		writer.setOutput(mos);
		writer.write(null,image,iwp);
		mos.close();
       }

Any further tips?

Tada, I have not tuned it fully but results look promising:

Machine: P4/2600/512kb - 256mb - 300x300 image - 100 encodings:
Mustang-server: 700ms
IBM: 897ms
JRockit: 1045ms

I still go to school so I am happy about every peanuts.
If you like I could write a version which does encoding on mustang-server in 470ms on my machine, maybe for the price the objectplanet-stuff does cost?

lg Clemens