Android web browser reduces my images – part 2

This is the second part of this post about how Android browser reduces 32 and 24 bits PNG images to 16 bit images, at loadtime. In a mobile device this may not mean a great visual impact. But when you are using PNGs to store data this is a big problem.

On the first part of this post I talk about how to store data in a bitmap and later using it from the web using javascript and canvas element. That technique failed with all Android device browsers I tested due to that bit depth reduction.

The solution is fairly simple. Instead of using one pixel to store one byte of data you could use two pixels per byte, using the 4 more significant bits of each pixel. A possible java generation code could be now something like this:

// Suppose 'width' and 'height' as image dimensions
// and 'string' as in data

// Create image
int type = BufferedImage.TYPE_BYTE_GRAY;
BufferedImage image = new BufferedImage(width*2, height, type);
// Get raster
WritableRaster raster = image.getRaster();
int index = 0;
for(int y=0; y<height; y++) {
  for(int x=0; x<width; x++) {
    // Get data (filling with zeros at the end)
    int current = index<string.length()? (int)string.charAt(index++): 0;
    // Get data more significant bits setting the others to zero
    // and store them
    int hi = current&0xf0;
    int[] hiArray = { hi };
    raster.setPixel(x*2, y, hiArray);
    // Get data less significant bits moving them to more significant
    // bits position to store in image
    int lo = (current&0x0f)&lh;<4;
    int[] loArray = { lo };
    raster.setPixel(x*2 + 1, y, loArray);
  }
}
// Finally write the image to disk
ImageIO.write(image, "png", new File(filename));

With this piece of code the generated image has doubled in width. You may think this would cause the image to be doubled size. But due to PNG image compression the resulting image gets only a few KBs more. Compressing my AirStrike 10K game with this technique only got a few 100 more bytes. And it worked on Android browsers!

Now, in the javascript side, we have to get image pixels in pairs and create one data byte per pair. But, if you remember the last part of this post, Android browser makes some dithering  when reducing bit depth. So, we need to have this in mind when unpacking data bytes. Simply getting the first pair byte for the most significant bits and the second pair byte for the less significant bits may not work in cases where dithering effect occurs.

Another time the solution is easy. Simply add some increment to image input bytes — something centered between 1 and 15. This will avoid the dithering effect getting the right original value. Then, in the other post’s javascript code, when retrieving data from image data, just make something like this:

// ...
var hi = (data[index]+8)&0xf0;
var lo = (data[index+4]+8)>>4;
index += 8;
var value = hi + lo;
if(value>0) {
  var char = String.fromCharCode(value);
  // ... do something with char value
}
// ...

And that’s all! Now you can use the PNG compression method in Android browsers too.

Taxi Strike for @aneventapart 10K contest

This is my second entry for this year’s an event apart 10k contest. It’s published right here. Go there, try it and put some comments!

Taxi Strike

All taxi drivers went on strike. You are the only one who can satisfy the needs of this city!

It’s a 3D top view car game where you have to take people from one place to another. It’s javascript based and has some beautiful features:

  • 3D inside canvas HTML5 2D context. I had to develop some tricky functions to perform textured polygon rendering in basic canvas. WebGL was not allowed because the game had to work in browsers that don’t suppor that feature.
  • Full physics for cars and city elements (such buildings, lamps, …). Cars move all around the city, trying not to crash with each other. But when they collide (or your taxi crashes with them) they do exceptionally well!
  • Taxi Strike 10k generated city fragment

  • A big fully textured generated city. Due to the 10k contest limit I couldn’t afford using external graphics or city definition. With a couple of simple textures I’d reach the size limit! So I had to generate all the city an its graphics in load time. The generated city is cool! I’ve used some graph theory algorithms for creating roads, greedy algorithms to generate buildings and deterministic random numbers to obtain always the desired city. All graphics are vectorial. Some of them where drawn using inkscape and then converted to HTML5 canvas functions and optimized manually. And I think I’ve got a nice comic style!
  • Some Taxi Strike generated graphics

  • Path finding. When you attend a call you are telling to go some place in a given time. Then, some arrows appear on the roads to help you reaching your goal, like using a GPS. That arrows mark the shortest path to your goal driving using the roads. But of course you can take shortcuts between buildings or through parks, taking less time and getting more points.
  • And so much more. Lots of details to make all things work.
Taxi Strike

Drive your taxi and take people everywhere

I almost went crazy getting all that features in only 10 kilobytes. In the way I had to discard some ideas and remove some things I’ve already developed. For example there were pedestrians walking; the game worked on mobile platforms drawing some buttons to drive the car; and more things. It was a pity to throw away all the mobile logic, but that was too much code for 10k…

I will post some of the techniques used in this game when I have time. So come back soon and maybe yo will find new freaky entries!

Air Strike for @aneventapart 10K contest

My this year 10k an event apart contest entry have just been published! You can try it by clicking here!

Air Strike tutorial

Air Strike tutorial screenshot

“All air traffic controllers are on strike and you are the only one who can make all those aircrafts land”. This is the premise behind this addictive game. You have to make aircrafts going to their respective airstrips for landing, being careful not to crash them.

Air Strike in game

Air strike in game screenshot

The game is less than 10KB and works fine with modern HTML5 capable browsers (IE9/10, Firefox, Chrome, Safari, …) on desktop computers and mobile devices (iPhone/iPod/iPad/Android). I’ve used several compression techniques than I will describe on this blog. Some of these techniques have been Javascript PNG compression – managing to avoid problems with Android web browser image reduction -, SVG to canvas code conversion, Javascript code reduction; and more…

Air Strike on iPhone

Air Strike on iPhone screenshot

Playing is easy. Use your mouse or finger to make aircrafts follow a path to their respective airstrips. Avoid them crashing and land as much as you can.

Have fun with the game and leave some comments on the entry for the contest!

Android web browser reduces my images – part 1

I’ve seen Android web browser reduces the quality of images inside an HTML in load time. I’ve reproduced this behaviour with some Android 2.x devices and various emulator configurations. It seems that when it loads 24 or 32 bits images it reduces them to 16bits (4 bits per component: reed, green, blue and alpha). And you cannot do anything about it (if you know how to avoid it, please tell me!).

That doesn’t have very high visual impact. But you may have problems when trying to read data from PNGs (supposed to be lossless). This is a technique based on HTML5 canvas element and its method getImageData, storing some data in a PNG file and then reading it from the web using javascript. For example, you can write into a PNG some javascript code – using a gray scale image and storing one character per pixel -. Reading from the web and extracting the original javascript code from its image obtaining a code string can be evaluated with eval. This is just an example, you can use it to store anything you want: CSS, more HTML, a love letter, etc.

Why would you do this? The answer is PNG compression. But when we try to use this technique in Android browsers, due to that image bit reduction, we only get useless broken data.

Let’s see what happens from a basic example. We’ll generate a gray scale image with only one horizontal line. Each pixel in that line will have values from 0 to 255. The java generation code could be, for example:

// Create image
int width = 256;
int height = 1;
int type = BufferedImage.TYPE_BYTE_GRAY;
BufferedImage image = new BufferedImage(width, height, type);
// Get raster
WritableRaster raster = image.getRaster();
for(int x = 0; x < width; x++) {
  // and put values
  raster.setPixel(x, 0, new int[]{x});
}
// Write image to disk
ImageIO.write(image, "png", new File("image.png"));

That code above simply creates a 8 bit gray scale image and set its pixels with values from 0 to 255 values. Now, from the client side, we could retrieve this data with this javascript code:

// Create image to store PNG
var image = new Image();
// When image is load this function is called
image.onload = function() {
  // Create canvas
  var width = this.width;
  var height = this.height;
  var canvas = new Element('canvas', {
    width:width,
    height:height
  });
  var context = canvas.getContext('2d');
  // Draw image in that canvas...
  context.drawImage(this, 0, 0);
  // ...to get its imageData
  var imageData = context.getImageData(0, 0, width, height);
  var data = imageData.data;
  var values = "";
  for(var x = 0; x < width; x++) {
    var value = data[x*4];
    values.push(value);
  }
  // Do something with values
  // [...]
};
// Set the image src to load it
image.src = "route_to/image.png";

Look at canvas getImageData method documentation if necessary to understand all the code above.

In all platforms, except Android, you get all the values as expected: values from 0 to 255. But in Android browsers you get something very different. This are the values requested:

0, 0, 0, 0, 0, 0, 8, 0, 8, 8, 8, 8, 8, 8, 16, 8, 16, 8, 16, 16, 16, 16, 24, 16, 24, 16, 24, 24, 24, 24, 33, 24, 33, 24, 33, 24, 33, 33, 33, 33, 41, 33, 41, 33, 41, 41, 41, 41, 49, 41, 49, 41, 49, 49, 49, 49, 57, 49, 57, 49, 57, 57, 57, 57, 66, 57, 66, 57, 66, 66, 66, 66, 66, 66, 74, 66, 74, [... ...], 206, 206, 206, 206, 206, 206, 214, 206, 214, 214, 214, 214, 214, 214, 222, 214, 222, 222, 222, 222, 222, 222, 231, 222, 231, 231, 231, 231, 231, 231, 239, 231, 239, 239, 239, 239, 239, 239, 247, 239, 247, 247, 247, 247, 247, 247, 255, 247, 255, 255, 255

This is like we were lossing the 4 less significant bits. The logic behind it is that Android browser converts all images to 16 bits, 4 bits per component. So, even our image was 8 bits gray scale, it only respects the 4 more significant bits, repeating them for the red, green and blue components.

But you have to be careful, because it also introduce a little dithering while reducing. Let’s see it graphically in this table:

0 0 0 0 0 0 8 0 8 8 8 8 8 8 16 8 16 8 16 16 16 16 24 16 24 16 24 24 24 24
33 24 33 24 33 24 33 33 33 33 41 33 41 33 41 41 41 41 49 41 49 41 49 49 49 49 57 49 57 49
57 57 57 57 66 57 66 57 66 66 66 66 66 66 74 66 74 74 74 74 74 74 82 74 82 82 82 82 82 82
90 82 90 90 90 90 90 90 99 90 99 99 99 99 99 99 107 99 107 107 107 107 107 107 115 107 115
115 115 115 115 115 123 115 123 123 123 123 123 123 132 123 132 123 132 132 132 132 140 132 140 132 140 140 140 140 148
140 148 140 148 148 148 148 156 148 156 148 156 156 156 156 165 156 165 156 165 156 165 165 165 165 173 165 173 165 173 173 173
173 181 173 181 173 181 181 181 181 189 181 189 181 189 189 189 189 189 189 198 189 198 198 198 198 198 198 206 198 206
206 206 206 206 206 214 206 214 214 214 214 214 214 222 214 222 222 222 222 222 222 231 222 231 231 231 231 231 231 239
231 206 206 206 214 206 214 214 214 214 214 214 222 214 222 222 222 222 222 222 231 222 231 231 231 231 231 231 239 231

So if we want to use the PNG data extraction method and doing it in a fully portable way (or at least working on Android) this method is not valid, because our data will be broken!

In the second part of this article we will discuss one relatively easy solution to this problem. Stay tunned.

JsNebulo for @aneventapart 10K contest

This was my last year 10K an event apart contest, where I obtained a notable mention. It’s aclassic Amstrad game version that is less than 10KB (including graphics). It has been developed using javascript and html5 canvas element.

JsNebulo

JsNebulo screenshot

It was hard to fit it in only 10K. I took the character graphics from Amstrad emulator screenshots, cleaned with Gimp and compressed using the awesome PunyPNG tool. It gave me all graphics in only 1KB.

All the code had to be in client side, so everything is javascript. The complete source code was near 50KB. Too much for the contest. Then I had to compress it in someway. After much investigation I found tools in the internet that helped me a lot. The first was Closure Compiler. It parses your javascript, analyzes it, removes dead code and rewrites and minimizes what’s left. It’s quite impressive how it reduces the size of your javascript. But that was not enough. Then I applied the javascript compressor from JavaScript Utility Web Site. It compress all your javascript in a string and introduces some little code to decompress in page load time. It was all ok and my code plus graphics were less than 10KB!

Original Amstrad Nebulus

I’d like to add much more enemies and levels. But that was not possible due to size limitations and contest deadline. So I only could make the first game level. In a near future I’ll optimize all the code to work in mobile devices too and publish it here, allowing people to create new levels and share them.

You can try it in my contest entry, clicking in Lauch button. You have to make the frog reach the top of the tower being careful with enemies and traps. Use arrow keys to move, activate elevators and doors, and space key while walking to jump. Good luck!