Showing posts with label AS3. Show all posts
Showing posts with label AS3. Show all posts

Sunday, August 19, 2007

Adding metadata to Actionscript 3 PNG encoders

You may have noticed that my pet-peeve du jour is the sorry state of PNG metadata. Right now I'm working in Actionscript 3, and the current crop of PNG encoders doesn't allow metadata.

These all require that the keys and values are in Latin1 (ISO-8859-1) as according to the PNG spec. The spec also requires that the key is 1-79 characters.

AS3CoreLib

The AS3CoreLibs project has a nice PNGEncoder. Here's the drop in replacement that supports metadata.

To use, you do something like this

var meta:Object = {Title: "A big cow", Copyright: "Oh yes"};
var enc:PngEncoder = new PngEncoder();
var bytes:ByteArray = enc.encode(data, meta);

Line by Line

For this patch I'll give a brief explanation on how it works. The other patches are very similar.

This writes out a tEXt chunk

/**
 * write out metadata using Latin1, uncompressed
 *
 * @param png The output bytearray
 * @param key the metadata key.  Must be in latin1, between 1-79 characters
 * @param value the metadata value.  Must be in latin1.
 *
 * the key or value is null or violates some contraints, the metadata
 *  is silently not added
 */
private static function writeChunk_tEXt(png:ByteArray,
                                         key:String, value:String):void
{
    if (key == null || key.length == 0 || key.length > 79) {
        return;
    }
    if (value == null) {
        value = "";
    }

    // the spec says this should be latin1,
    // but UTF8 is probably ok, but be care of overflows
    var tEXt:ByteArray = new ByteArray();
    tEXt.writeMultiByte(key, "iso-8859-1");
    tEXt.writeByte(0x0);
    tEXt.writeMultiByte(value, "iso-8859-1");
    writeChunk(png, 0x74455874, tEXt);
}

Change the encode function to take an optional metadata object (a hash table of key, values)

public function encode(img:BitmapData, meta:Object = null):ByteArray {

Write the tEXt chunks between the IHDR and IDAT chunks

writeChunk(png,0x49484452,IHDR);

// should be before IDAT so ImageMagick can read it
for (var k:String in meta) {
   writeChunk_tEXt(png, k, meta[k]);
}

// Build IDAT chunk
var IDAT:ByteArray= new ByteArray();

Ta - Da!

Better, Faster, PNGEncoder

Hey, it's a better, faster implementation. Besides being faster, it supports RBG or RGBA PNG formats which can save space as well. There is a rumour the improvements will be merged into AS3CoreLib.

The drop in replacement that supports metadata is here. It also cleaned up some warnings/errors spotted by the Flex3-Beta1 compiler and some other tidbits.

AsPngEncoder

There is one other PNG encoder, AsPngEncoder. It's notable since it support palete based PNG files, which can result in dramatic space savings. I got lazy and didn't hack this one. I'll leave it as an exercise for you to add meta data.

License

Hey kids, go nuts. These changes are in the public domain, so the original authors can integrate this without hassle. It would be swell if you gave me some credit (e.g. Thanks to: Nick Galbreath or so), but not required.

WARNING

Always save the best for last. I'm not a flash expert, really. Always check your application before deploying this to production. It's possible quite a few "check for nulls" can be rid of. I'm coming from C++ background and I'm paranoid. ha! Any tips are welcome.

Review of "Actionscript 3 Cookbook"

I'm just learning flash for a variety of reasons, and I already know C++, javascript and few dozen other languages, so I was hopping for something that just highlight the quirks and common use cases of Flash and AS3. The O'Reily texts are normally pretty good, so I turned to Actionscript 3 Cookbook so I went the most excellent Barnes and Noble at Union Square to do some bible dipping

Early on, it has sections on "how to do something repeatedly" (answer "Use for loops). A much later section has "How to disolve between two bitmaps" (answer "Use Bitmap.disolve" -- which was a rewrite of the existing documentation).

More serious problems

"How do you find the minimum or maximum or an array?" "sort then take the first or last element." Uhhh, if one of my guys did this, I would considering firing them.

More problematic is the section on shuffling an array. "Use sort, with a sorting function that returns a random number between -1.0 and 1.0". This will:

  • Leave large parts unshuffled
  • Could run very slowly -- as the sorting algorithm goes into semi-infinite loop
  • Could run slowly -- as you are making function calls for each compare operation
  • Could core dump / abort the program / throw an exception

The exact disaster would depend on the implementation and what sorting algorithm is used. Lots of people get shuffle wrong -- it really should be in the core language or library.

You want something like this (see any algorithm book or the mighty Knuth)

function shuffle(a:Array):void {
  for (var i:int = a.length; i;) {
    var j:int = Math.floor(Math.random() * i--);
    var x:int = a[i]; a[i] = a[j]; a[j] = x;
  }

/*  OR
  var x:int;
  for (var j:int, i:int = a.length; i; 
       j = Math.floor(Math.random() * i), x = a[--i], a[i] = a[j], a[j] = x);
*/
// TBD: probably one can cast to int instead of using Math.floor 
// to get more speed.
}

Summary

Well needless to say I didn't buy the book. Maybe I picked some bad sections. But I get the feeling this is for people new to programming.