Posted by javakiddy
on November 21, 2007 at 2:38 AM PST
Applications are now running on a wide variety of screen resolutions, and as mobile devices get smarter the range of potential screen sizes increases even further. So in this blog I ask a simple question: what are the issues surrounding Java apps which can scale their graphics from large widescreen to tiny hand-held? (Oh, and why you should never play casino Poker machines!)
It's a creepy thought, but hidden amidst the garish flickering displays and eternal night of some far flung casino there may still stand a Video Poker machine running code written by yours truly. It's been the best part of a decade and half, but the industry as I recall it was never fond of re-inventing the wheel. Y'see for Poker machines 'unit testing' (as we now know it) didn't result in simple pass or fail outcomes, but in a slew of figures detailing how the code had survived each play scenario during overnight simulations. How would it play for novices? How did it stand up to experts? Crucially, how much money was it likely to rake in for the site operator? And just when everything seemed to be working fine each jurisdiction's gambling authority would demand to paw over the source, looking for potential cheats (we're talking tax evasion here, they weren't always bothered if the punters got scammed! :)
Still, at least I didn't have the tedious job of drawing the graphics!
If you've every drawn a pack of 52 playing cards you'll know what a fiddly cut'n'paste job it can be. Cards with the same symbols don't have the same layout, conversely cards with the same layout don't have the same symbols (duh!) At least the Poker machines had a fixed screen size. Anyone targeting modern PCs and mobile devices needs multiple decks to ensure they always look sharp at any resolution.
It's just an extreme example of a dilemma facing many user interface designers, in a world rapidly fragmenting into living room widescreens, pocket hand-helds, and all shades of resolution in between.
SVG (Scalable Vector Graphics) is a W3C backed XML format for vector graphics, attracting increasing attention. This is, I'm sure, due in no small part to the emergence of technologies like Flash, Adobe AIR and Microsoft Silverlight, with their reliance on vector graphics for scalable crispness, efficient bandwidth, and easy animation capabilities.
SVG is a technology I've been meaning to investigate for some time, and revisiting the playing card problem seems like an ideal test case to learn the ins-and-outs of working with vector images in Java.[The code, SVGs and example app (including dependent Batik Jars) are available for download HERE (2.4Mb)]
The problem with vector images has always been one of support — or lack thereof. There were few applications which drew vector images, and even fewer APIs to work with the resulting vector files within one's own code. But thanks to SVG and APIs like Apache's 'Batik' we now have a common vector format which is well supported by both applications and software APIs.
Kirill Grouchnikov touched on using Batik in an excellent series of blogs a year back. His focus was on scalable icons, while I want to merely use SVG as a resolution agnostic graphics format, which ultimately will get rasterised into a bitmap toolkit at runtime.
By simply modifying a few 'glyph' images a designer could create new card styles quickly and conveniently.
After a couple of evenings with Inkscape I managed to produce the necessary images needed for the toolkit. I'm no artist, so you'll have to excuse my cartoony attempts at jacks, queens and the like. I'm also no Inkscape expert, to please forgive the slight highlight bleed on the hearts and spades (fortunately only visible at extremely large resolutions.)
And so, onto the code...
The first stumbling block was to get Batik to output bitmaps. Surprisingly for such a large and fully featured API the ability to rasterise a SVG to a POJBI (plain ol' Java BufferedImages) seems to be missing. Not to worry — two minutes with the documentation revealed a solution in the form of the transcoder API, a package originally designed to translate SVG files to other file formats, including bitmap images. Twenty minutes digging around the source was enough to cobble together my own
BufferedImageTranscoder class, which maintains a reference to the rasterised bitmap instead of writing it to file.
The next problem was defeating the speed of the rasteriser. Batik may be weighed down with features, but it could never be accused of being fast. Perhaps I just didn't understand how to use the API efficiently, or maybe the transcoder classes just aren't optimised (they do process the XML from scratch)? Simple SVG documents were a tad sluggish on my trusty Linux box, but the real hit came when rendering images with sophisticated filters, like blur.
Fortunately I had never intended to create each card entirely in vector form. Once sized appropriately as bitmaps, Java2D would create each card from the glyph toolkit, so the speed bump would be a one-shot penalty when the application started up or resized.
The next problem I encountered regarded the transcoder respecting the aspect ratio of the source SVGs. There seemed no way to get it to render, say, a square image unless the original vector XML was intended for a square image. While this behaviour is perfectly correct, it did cause a few headaches. As Batik's image transcoders scale their output to fit the destination, and always align against top-left, I had to size the destination bitmap appropriately before invoking the transcoder to avoid getting lopsided results (whitespace down one edge.)
And so, having rasterised each glyph, it was mundane Java2D work to lay the cards out.
The bulk of each card is built up using a grid to determine where each symbol is positioned. Because of the lack of control over both dimensions of the bitmaps, each glyph is located around its centre. Even the face images are positioned this way, as one big bitmap centred on the middle of the card. Border and padding settings offer some control over internal spacing, although (as you'll see from the CardDisplay application) more work could be done on this.
Ideally some sort of XML format should be used to specify the layout of each card, allowing decks to be styled without hacking the code. I couldn't be bothered with that, so the geometry is currently hard coded.
All in all the whole process was surprisingly painless. Indeed my only gripe with Batik is it's size. Even after eliminating unneeded packages from the distribution I was still left with approximately 2.5Mb of Batik Jars to ship alongside my 100k of SVGs and 20k of code (and that includes a CardDisplay application for viewing the cards!)
Although originally intended for the web, SVG clearly has applications beyond just HTML pages. Its potential as a resolution agnostic format for icons and logos in desktop, mobile and RIA applications is immediately apparent — but to make an impact we need an API several times lighter and, if possible, faster.
I think it is clear vector images will play a big part in the future of user interfaces, and the Java community should probably start debating whether to include a basic rasteriser in Java SE. As it stands the leading API is a rather oversized kitchen-sink affair, far too powerful for what's needed by 99% of applications. Perhaps Salamander may grow to fill the gap, but until then a 2.5Mb overhead simply to scale icons at runtime is probably too rich for many developers.