Sunday, February 18, 2007

GWT ClippedImage - Optimizing Image Loading

If you have been watching the news groups you will have heard about the upcoming GWT 1.4 release, and the long list of features it will include. One of these features is the ClippedImage class. This article is meant to explain how to use ClippedImage, and the details of how it can optimize image loading in your GWT application.

What is ClippedImage, and what is it for?

ClippedImage allows you to show just a part of an image instead of the whole image, by hiding or "clipping", the rest. The ultimate purpose of this new image type is for optimizing the load time of your GWT appliciation. Curious as to why? Then read on...

First some background information...

We use images in web pages for icons, logos, art, photos, and to make the user-interface look beautiful. There is no escaping it, we like to use lots of images! As you might guess, the flip side of the coin is that lots of images means longer download times, but perhaps not for the reason you think. Of course the larger the image the longer the download time, but what about the overhead of the HTTP protocol? Due to the way browsers and HTTP works, there is a bit of overhead for each image downloaded, and although it is rather small, it can quickly add up if you have a lot of images.

As an example may I present a sample icon bar. The bar includes a set of 14 icons (12 shown in the image), each 22 pixels by 22 pixels, with the total size of all of the images a mere 15 kB.



To see how well the browser performs when loading these images I created a simple HTML page that included 14 image tags, one for each icon. I then put the images and the test HTML page on a web site and viewed it in the browser. I use the FireBug plug-in (a must have!) for Firefox, and it shows me that the total time to download the page and all of the images is a mere 610 milliseconds. In FireBug you can get a clue as to what is going on, the images don't all download at the same time, and as we will see, this is by design.



In section 8.1.4 of the HTTP 1.1 specification it states,
"Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy."
I think that this makes it clear why we see really much longer times for some of the images on the page even though they are roughly the same size. So if the problem is the connection limit that HTTP specifies, what is the solution? Considering this article is about the new ClippedImage class, I will bet you know what comes next. The solution is to reduce the number of external images that we need to load by combining all of out icon files into a single file.

In order to test this I created a single PNG image that contained all of the 14 icons, and reran the test. Below is the FireBug output for the single image, which is the same size as the 14 icons combined.



The results are pretty amazing, with a page load time of roughly 38% of the of the original. In our example the difference is less than 400 milliseconds, but if we needed to load more icons this could easily add several seconds to the time it takes for the page to load, which can be significant depending on the application.

Now that we have some background information, lets get on to using ClippedImage to increase load time.

Using ClippedImage

Using clipped image is very simple. You simply specify the X/Y coordinates of the top-left pixel of the part of the image where you want the clipping to occur, followed by the width and height of the image. The following code snipped shows the creation of four clipped images.

ClippedImage unlkIcon = new ClippedImage("icons.png", 0, 0, 22, 22);
ClippedImage timeIcon = new ClippedImage("icons.png", 22, 0, 22, 22);
ClippedImage warnIcon = new ClippedImage("icons.png", 44, 0, 22, 22);
ClippedImage recyIcon = new ClippedImage("icons.png", 66, 0, 22, 22);


In the code snippet we highlighted, we create a warning icon. The diagram below shows where the coordinate measurements come from. The top corner of the icon is 44 pixels from the left edge, and zero pixels from the top. The next two arguments are the width and height, both are 22 pixels.




Oh, and you might have noticed that we used a PNG in the example. It is a known issue that Internet Explorer 6 doesn't support the PNG alpha channel without having to jump though some hoops. The PNGImage class from the GWT Widget Library jumps through these hoops, and it is rumored that the new ClippedImage class will also support this.

In order to add ClippedImage into the GWT API there were some other modifications made. If you think about it, now that there is the old Image class, and the new ClippedImage class, it only made sense to add a parent class to hold shared functionality. This new class is called AbstractImage, and you guessed it, it is abstract. This super class includes mouse event handling, simplifying any subclasses a little.

Automatic bundling of images

Along with ClippedImage is the new ImageBundle. I won't cover ImageBundle, but if you want to read up on it, there is a ImageBundle design document. The ImageBundle is similar to the GWT internationalization support, except that it is for images. The idea is that you create an extention of the ImageBundle interface, with one method specified for each image, and each image being its own separate file. When you compile the GWT code the GWT complier will automatically grab all of the individual images and combine them into a single image. In the GWT code you then use the methods of the interface to get the image objects. The whole purpose is to make image clipping easy to do.

I hope you enjoyed this article. I am not sure what I might write about next as there are a lot of cool new things in GWT 1.4. The complete list of new features can be found in the GWT 1.4 Development Plan.

Attribution: The icons used in the sample images are from the Tango Icon Library.

6 comments:

Anonymous said...

[RH: I needed to delete and repost this entry. Below is a comment that was on the article prior to deletion]

Reinier Zwitserloot said...

Trés cool. Do keep writing about these things, it's very informative.

The next step would be a convert script kind of job: Compile images into one larger image, and replace uses of just the image with the proper coordinates of a ClippedImage.

In fact, such a tool can take existing (GWT 1.3) code and convert it into using ClippedImage. Replacing a single icon in a 'collection image' is a real pain in the tusch without such a step.

Robert Hanson said...

Glad you liked it. It definitely fuels my writing when I know that there is interest in it.

> Compile images into one larger
> image, and replace uses of just
> the image

The new ImageBundle in 1.4 will do that for you... but you need to explicitly set it up. It isn't automatic, and probably shouldn't be.

Reinier Zwitserloot said...

So that ImageBundle device will allow me, at compile-time, to fully automatically multiplex a bundle of separate image files into a single larger one?

There's more "resource compilation" I'd like to see. For example, if you use more than 1 stylesheet tag, for example because you include some other .gwt.xml files which each handle their own styles, IE barfs and doesn't render anything until you click in the browser window half the time.

Optimally, then, GWT should 'compile' all css linked in all included .gwt.xml files, and produce one big single CSS file.

Anonymous said...

Now I understand what this ClippedImage is.. will be useful in my project. Thank you for writing this!

Anonymous said...

How does this feature work internally and what are the RAM requirements? Does it copy the full image N times (where N is the number of the subimages)? If so - do current browsers somehow optimize this case, or do they require more space when using ClippedImage?

Robert Hanson said...

> How does this feature work
> internally and what are the RAM
> requirements?

I don't know off the top of my head, and because this feature still has not been released the code is still subject to change pending QA.