 |
Card Sharp
Posted by javakiddy on November 21, 2007 at 02:38 AM | Comments (14)
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.
Batik clearly attempts to cover all the bases, even including a JavaScript package (duplicating the one in Java 6.0) to cover the interactive side of the format. Overkill for any application merely wanting to render a few icons into bitmaps! There is a lightweight alternative: SVG Salamander weighs in at only a few hundred K, but I had trouble getting it to render filter effects like blur. I'm sure I could live without blur, but it would be nice not to have to.
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.
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
Forgot to mention, there is already some work on a JavaME SVG library at
JSR 226. And on a related note, I just noticed Terrence Barr blogged about JSR 226 for scalable UIs.
Posted by: javakiddy on November 21, 2007 at 03:34 AM
-
Hello Simon. About one year ago, I started using SVG icons inside my NetBeans RCP application (with some tweaking as I fear I've used some non-official NetBeans APIs). I've gone with the same idea, that is SVG icons are rasterized at startup, then fed in the usual NetBeans stuff. For what concern the used libraries, I went the opposite way: I started with SVGSalamander. But blur is not the only problem - there is a number of SVG icons (taken from the internet) that simply can't be rendered probably because of svgSalamander's bugs. Since svgSalamander appears no more supported (the author is moving it to a new project named Gazelle, whose scope appears a bit different, I had started planning to migrate to iBatik. But it looks like it's not a good idea, after all. Probably it's better to go to Gazelle and try to understand it better - after all, the rendering code should be still there. Maybe kitfox, the author, could clarify these points if he's reading.
Posted by: fabriziogiudici on November 21, 2007 at 04:30 AM
-
At the point of writing that series of blog entries, i have found svgSalamander severely lacking in support of most of the SVG images that i tried (including the bulk of the standard Tango icon set). Batik might weigh in at a few megs, but at least it provides much wider support for the SVG format (i still have some problems with a few Oxygen icons, but i don't know if it's on Batik's side or on Oxygen's side). To address the load time issue, i wrote a custom transcoder that takes an SVG image and produces the matching Java2D code. Then, you can apply any transformation to scale, shear, rotate or any other effect before the actual rendering. The size of Java2D class is roughly the same as the original SVG image, and it's much faster and you don't have to bundle the Batik libraries at runtime. :)
Posted by: kirillcool on November 21, 2007 at 08:29 AM
-
Compiling to Java2D code is a viable solution, assuming your images are always fixed and unchangeable. But if I wanted to offer the ability to customise the icon set, or skin the application, then it becomes difficult -- although not impossible. Certainly users wouldn't be able to just drop icons into a directory, they'd have to be compiled into classes and introduced to the application via a class loader.
I'm wondering if an implementation of 'Basic' or 'Tiny' rather than the full SVG standard might be suitable for icons? I need to investigate what has been stripped from these two sub-formats. Just how much lighter are they?
Posted by: javakiddy on November 21, 2007 at 08:42 AM
-
What's Basic and Tiny? Are these the "light" versions of Batik? And about the skinning - you can do that on AWT images. It's not very simple, but not very complicated - you just need to map the colors in one color scheme to the colors in another color scheme, using the color brightness to make the mapping (so as not to lose too much of the image contrast).
Posted by: kirillcool on November 21, 2007 at 08:49 AM
-
Correct me if I'm wrong, but isn't the W3C SVG 1.1 spec broken up into different profiles? SVG Tiny is the version aimed at small mobile devices, and SVG Basic is aimed at STB and PDA type devices.
Skinning: very true, but what about if the skin included different graphics as well as just a change of colour scheme?
Posted by: javakiddy on November 21, 2007 at 08:58 AM
-
I thought you were talking about lighter versions of Batik. What does it matter what is stripped in Tiny and Basic if the rendering library is still the same? I wonder what NetBeans is using to render the Tiny SVG profile...
Posted by: kirillcool on November 21, 2007 at 09:26 AM
-
I suspect Batik is so large precisely because it targets the full SVG spec, including a lot of power features which aren't really necessary for simple scalable icons in desktop apps (do we really need full interactive JavaScript support for our toolbar icon images?)
If a library was written which targeted only the Basic spec, the code might be light enough to include in the standard Java SE, or at least to include with your application without bumping up the download time (which is important if the app is an applet/RIA and lives on the net rather than your local disk.) The Tiny spec was intended to be used on small mobile devices, so I assume it doesn't demand 3Mb of libs. But is Tiny powerful enough to render decent desktop icons?
Posted by: javakiddy on November 21, 2007 at 09:37 AM
-
I guess then that Tiny is not goof since it only supports full color fills...
Posted by: kirillcool on November 21, 2007 at 11:41 PM
-
Sometime back I designed some screens in SVG(using Inkscape) for my Applet. I experimented with both SVG Salamander and Batik.
Initially I choosed SVG Salamander because of its size.
Later I optioned for Compiling to Java2D code (with kirillcool Tool )
Except for couple of code changes it worked great( the changes were required because I want to run my Applet with JRE1.5 which doesn't have some Multilevel-Gradient's and also 1.6 uses double values instead of floats in 1.5 for some gradient values)
You can find the Applet here which is using SVG to Java2D covertered code.
http://www.gyanlabs.com/java/java.html
Kishore.
Posted by: kishoresjava on November 22, 2007 at 11:04 PM
-
As onyx points out SVG Tiny doesn't have gradient support in version 1.1, but SVG Tiny 1.2 is introducing some of the missing features and libraries like TinyLine have some of the 1.2 features already.
With regard to svgSalamander I would suggest you post Mark McKay (kitfox) some information about the shortcomings you have found. I've used svgSalamander extensively and found it to be a very good library and where there are issues Mark has very quickly provided fixes once he has had some details. The svgSalamander API is also a bit easier to use with Swing and for rasterization. Where svgSalamander is lacking you may also be able to supplement it with parts of Batik.
I used SVG for a Synth based LAF some time ago. Initially I used Batik and rasterized the artwork but this approach is severely limited as you loose almost all the value of the vector graphics and all the capabilities of SVG. Using svgSalamander I was able to render the SVG directly, without having to rasterize the image and the performance of this is pretty good. However, using SVG for UIs presents a number of problems as scaling images doesn't produce the sort of results that would be needed for typical UI components - for example the rounding of corners normally shouldn't scale with the components. At JavaOne I presented some techniques for working around these issues.
Since then Richard Bair and Jasper Potts have done quite a bit of work in this area on the forthcoming Nimbus LAF and it is also worth keeping an eye on the Nimbus project. Nimbus converts SVGs to Java2D and it solves some of the component issues quite nicely :-)
The Open Source XUI framework includes lots of SVG stuff, for example see the SvgViewer and TouchScreen demos. You may be able to use some of XUI's basic components for some of the things you are doing.
-Luan
Posted by: luano on November 23, 2007 at 02:53 AM
-
SImon,
Thanks for the inspiration, finally gave me something to do!
Having lived in Las Vegas for 8 years, and now living in Denver, your article brought back plenty of memories... ding ding ding...clink clink clink.. :-)
I used your code to generate a deck of cards and then used them in a browser-based Video Poker machine.
You can check it out here, if my server's online: Video Poker
Thanks again,
Jeff
Posted by: jeffallen6767 on February 26, 2008 at 02:44 PM
-
@ jeffallen6767 Very nice! I'm glad someone found them useful :)
Posted by: javakiddy on February 28, 2008 at 09:21 AM
|