Mike's Take

Wednesday, September 19, 2007

Widgetry, Assets and Cairo

I thought I would put together a post on a little work I've done recently with Cairo, Assets and Widgetry. I've been meaning to write about it for a month, but the time slips away.

Cairo Graphics

In my application, I want to have good looking icons. I bought a nice set online. They came in .png and .gif formats. The .png's look really nice, with alpha channel blending. Putting them into VisualWorks resulted in jagged looking edges especially on non-white backgrounds.

So I created a new class in the CairoGraphics package called PngImage that is just a wrapper around an ImageSurface. PngImage implements the necessary interface from the Image class, so it can be displayed in the trippy inspector, and used as the image in Widgetry DisplayImage and ActiveImage classes. PngImages can be instantiated from a file path, from bytes or from a stream.

PngImage pngBytes: aByteArray.
PngImage pngByteStream: anExternalStream.
PngImage pngPath: aFilePathString.

You can inspect one and it displays nicely using Cairo. The code is part of the CairoGraphics package in the Cincom public repository.


Once I had PngImage working well, I wanted a good way to manage all of my icons. Keeping them in the smalltalk image was very desirable, both for deployment and for source code management. I wanted to be able to version the icons and use Store.

Assets

Vassili Bykov started working on a framework for managing external "assets" that Travis Griggs later finished. It can be found in the public store also. You point Assets to a folder, and it reads the files, and based on the file extensions does wonderful things, like install class methods on your Assets subclass with the literal representation of the external asset. By default, Assets deals with external png files and gives cached OpaqueImages (the regular VW way of dealing with alpha channel, basically an image and a mask). So for my work I just made my own subclass of Assets and tweaked the behavior slightly so that it would make a cached CairoGraphics.PngImage for me. This required only one overridden method, and one new method.

MyAssetClass class>>import_png: aFilename
^self importPngImage: aFilename

MyAssetClass class>>importPngImage: aLogicalFile
| bytes |
bytes := aLogicalFile contentsOfEntireBinaryFile.
^'#{CairoGraphics.PngImage} value pngBytes:
(<1s>)'
expandMacrosWith: bytes storeString
Now all you have to do is choose Sync Assets from the Class menu in the Refactoring Browser, and point the file dialog at your folder containing all of your png images. Assets auto-magically creates source code for you to generate your PngImages. One thing to be aware of, is that PngImages are using Cairo surfaces, and the memory for these is handled outside of Smalltalk memory management. These memory locations will survive an image snapshot, but not restarting the image. So you must arrange for the CachedBlockClosures (used by Assets, and really cool by the way) to become un-cached when the image is started so the first call will allocate the Cairo surfaces correctly. My application uses Cincom's Subsystem framework to initialize things properly on image startup, so I just threw in a line to un-cache the blocks.
CachedBlockClosure uncacheAll.
Now when I call
MyAssetClass class>>myIconName
the first time, the Cairo surface is created from the byte array embedded in the method source, and a PngImage is returned. All subsequent calls return the cached PngImage. The caching and the Cairo rendering make this really fast, and the results are great.

Widgetry

Despite the fact that Widgetry is no longer supported, we are moving ahead with it at my company. We have had a really good experience with it so far, and integrating Cairo with it (I implemented a CairoCanvas that lets you do primitive drawing with Cairo) and using Cairo based icons was really easy. I also went ahead and implemented in-place editing for ListBoxes as well. It uses the same api as the in-place editing for TreeView.
We're still really early in our development of the new UI, but so far it has gone quite well. The following screen shot shows PngImages used within Widgetry DisplayImages, and a number of CairoCanvas' within Widgtry Grids.

The combination of Cairo, Assets and Widgetry easy to use and powerful, and is getting me closer to my goals for the UI for KnowledgeScape. I will be working on many more enhancements in the future, hopefully culminating in a Look and Feel that uses Cairo exclusively for rendering and Pango for text layout and fonts.

0 Comments:

Post a Comment

<< Home