I know there's already a good Vexed port for Android, but since I needed a web version, making it compatible with Android seemed like a good idea. It's still missing a lot of details, like a menu, manual or win/lose messages, but if you want you can play it here:
The buttons used to be rendered using CSS3 border-images, which work on Android (at least 2.3+, haven't tested any other versions yet), BUT they are slow as hell. Apparently it's hitting a fallback path that renders the element much like you'd do on legacy-IE with behaviors... meaning that there's also a lot of tearing since the element that you apply the border-image to basically becomes a set of 9 image elements as far as rendering is concerned.
So I decided to implement my own border-image using Canvas (you can find it at line 168 of the HTML file: HTMLImageElement.prototype.renderAsButton)... after all, drawing border-images isn't exactly rocket science.
So far so good: it was working beautifully, but somehow I couldn't get it into my CSS... after basically checking every part of the chain with manually created data urls it really didn't make any more sense, so out of pure desperation I decided to take a look at the data url returned by HTMLCanvasElement.prototype.toDataURL, which was a ... surprise. This thing is wrong on so many levels it never should have shown up in any code. Not only does it not throw and ERR_NOT_IMPLEMENTED, it even returns data to cover the fact that it's not implemented, namely "data:," which doesn't throw an error when used as an image source.
The general workaround seems to be that people use a custom toDataURL implementation that outputs a Windows BMP file... which would be great, if Android supported 32-bit RGBA BMP files. However they usually end up as if they were 24-bit RGB files.
So if I wanted RGBA, i had to create PNG files.
The PNG spec is really very readable and makes it easy to implement a PNG encoder... but there are two shortcomings:
1. The only filter method available requires a filter byte at the beginning of every row. So you can't use the return data of getImageData directly (Array.prototype.splice doesn't work on ImageData, you have to copy it first).
2. It doesn't support uncompressed data directly... and compressing in JS seems like a bad idea. Instead you are supposed to use and ZLIB stream with raw blocks
It also has CRC checks which are annoying for our purposes, but not that hard to implement (you can find my implementation at line 94: Array.prototype.crc32). Costs a bit of performance, but wouldn't be a major problem by itself.
But the reliance on ZLIB proved to be a major annoyance, because the documentation (RFC1950 & RFC1951) is lacking... to put it mildly (the relationship between those two documents is never really mentioned, meaning that you're always guessing if a header belongs to the stream or the block). The endianess is also switched from what PNG uses, meaning that you end up with two different types of integers in the same file. I ended up mostly analyzing THE GIMP's output for uncompressed PNGs (and could have finished everything much earlier if I hadn't tried to work my way through the spec first). The most important part from the spec is the definition of the ADLER32 checksum (line 85: Array.prototype.adler32), which sadly is required for each ZLIB block, meaning that creating a PNG involves calculating two different checksums for (mostly) identical data.
But my little PNG encoder is working now, and in difference to other implementations that I've found, there's no 64kb limit as my implementation splits every stream into 32k blocks correctly. However, it's not as fast as it could be and if there's interest I'll happily put the code in a project if other people want to help improve it or maybe even add proper compression.
EDIT: THE CODE IS NOW PART OF AN OSS PROJECT, SEE THE NEXT POST
I might not check this forum at the time, but you can always reach me at email@example.com