My technique for anti-aliased animation and bitmaps

Hey everyone.

I recently built the "Ranger" watchface.
It's a super-crisp watchface that uses anti-aliasing for every part of the screen, including the moving hands. I thought I'd share with you how this was made possible, using an example that I had developed previously (with the source code too!)


Drawing Anti-aliased primitives

As we know, drawing native polygons and lines are not anti-aliased. I actually built a little library that draws AA lines, and simple AA polygons — check out the code, and the screenshot;
https://github.com/sunpazed/garmin-drawaa

However, there's not enough processor time on the watch to draw lots of primitives with this method. Period. So we need another method.


Using lots of Bitmaps

Another option is to use many bitmaps. With 60 frames of animation and at most a 240x240 resolution screen, this quickly becomes an issue with size of the packaged app — up to 1.5Mb of storage! Also, it doesn't solve the anti-aliasing issue, as CIQ does not support bitmaps with an alpha channel (only a single transparent "colour").


Font-based tilemaps

I developed games back in the days of 8-bit systems. It was very common for these systems to use fonts or tilemaps to optimally represent large chunks of bitmapped data. Given CIQs small footprint and font-based features, I thought I'd try my luck replicating this technique.

Not only is this is a more storage-friendly method than Bitmaps, it allows developers to efficiently pack many frames of bitmapped animation in a ~26kb font. Also, the great thing about fonts, its that they do alpha blending with the existing framebuffer on the canvas. So, you get anti-aliasing as a nice by-product.

As an example to showcase this method, I thought I'd replicate a featured watchface from our fruity competitor;



As Mickey's hands have quite a bit of detail, they're represented as a bitmap which is rotated 60 times. Here's how each frame of Mickey's hands are represented as a tile-map;



To encode this tilemap data, there's an array that maps each tile to a position on the screen. From a memory perspective, this is stored as a 32-bit signed integer which has been bit-packed with 4 bytes to be more efficient.

const hands = [
[23068672,41418752,56629248,74979328,90189824,108539904,123750400,140533760],
[158859264,174069760,192419840,207630336,225980416,241190912,259540992,274751488,293101568],
...


Drawing each bitmapped frame on the device is now really simple. All we need to do is decode the array, and draw some text;

for (var i = 0; i < hands.size(); i++)
{
var packed_value = hands;

var pad = packed_value & 255;
packed_value >>= 8;
var ypos = packed_value & 255;
packed_value >>= 8;
var xpos = packed_value & 255;
packed_value >>= 8;
var char = packed_value & 255;

dc.drawText(xpos.toNumber(),ypos.toNumber(),font,char.toNumber().toChar(),Gfx.TEXT_JUSTIFY_LEFT);
}
[/CODE]

The result is a bitmap drawn on the screen (in any colour we want), and perfectly alpha-blended with the frambuffer.

And that's pretty much it. Now, I know this technique isn't for everyone, and yes, it has drawbacks (can only draw flat colours) &#8212; but for the benefit of the developer community, I believe it's an interesting technique to showcase.

To generate the tilemap, font, and tilemap array &#8212; I've built my own custom tool. Once I clean it up, I'll release the source so that other developers can use this technique.

If you're interested in compiling your own version of Mickey (it's not on the app store) &#8212; check out the source code here;

https://github.com/sunpazed/garmin-mickey

Oh, and remember, I'm releasing this source in good faith. So no rip-off's in the app store please (you know who you are).

Happy to hear your thoughts. Cheers.