cameron314 / PNGEncoder2

A better PNG encoder for Flash
97 stars 10 forks source link

Overview

This is the quick-start guide to my PNGEncoder2 library, which compresses BitmapData objects into PNG files (stored in ByteArray objects). For a more in-depth explanation of how it works check out my blog post about it.

Features

Usage

PNGEncoder2 supports both synchronous (fast but freezes the UI while it encodes) and asynchronous PNG encoding (a smidgeon slower, but can maintain a target FPS).

Synchronous example:

var bmp : BitmapData = ...;     // The bitmap you want to encode
var png : ByteArray;            // Variable to store the result

PNGEncoder2.level = CompressionLevel.FAST;  // Optional. Defaults to FAST

png = PNGEncoder2.encode(bmp);

Asynchronous example:

var bmp : BitmapData = ...;     // The bitmap you want to encode

PNGEncoder2.level = CompressionLevel.FAST;  // Optional. Defaults to FAST

var encoder : PNGEncoder2 = PNGEncoder2.encodeAsync(bmp);
encoder.targetFPS = 12;     // Optional. Defaults to 20. Lower FPSs yield faster compression

// Because the encoder is guaranteed to fire the COMPLETE event
// after at least one frame, it's safe to attach the listener after
// starting the encoding (as long as it's done before the next frame)
encoder.addEventListener(Event.COMPLETE, function (e) {
    var png : ByteArray = encoder.png;
});

// encoder also dispatches ProgressEvent.PROGRESS events if you
// want to be notified of progress

Memory-constrained environments:

If you're working in a memory-constrained environment, or with truly huge images, the asynchronous method is usually the best choice as it encodes the image one chunk at a time, and therefore requires much less working memory.

After encoding an image, however, the working memory is not freed. This is not a memory leak, but rather an optimization to speed up the next encoding (the memory is reused, which among other things eliminates the need to recalculate the CRC tables each time). With large images, or low available memory, this may be undesirable. To free the working memory, simplpy call PNGEncoder2.freeCachedMemory() after each encoding is complete. This will reduce the resident memory usage to near zero from what could otherwise be potentially several kilobytes or even megabytes (what a savings!).

Bonus: PNG decoding

Update: Support for synchronous decoding of PNGs that were created using PNGEncoder2 has been added:

var bmp : BitmapData = ...;
var png : ByteArray = PNGEncoder2.encode(bmp);
bmp = PNGEncoder2.decode(png);      // Round-trip back to BitmapData

Decoding is about twice as fast as encoding, though it should be stressed that only PNGEncoder2 PNGs can be decoded properly, not arbitrary PNGs. The advantage is that decoding is faster than using the built-in flash.display.Loader object, at the cost of reduced flexibility; if you need to decode arbitrary PNG files, or you want to decode asynchronously, use the Loader as usual:

var png : ByteArray = ...;    // From a file or encoded PNG ByteArray
var loader : Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function (e) {
    var bmp : BitmapData = new BitmapData((int)loader.width, (int)loader.height, true, 0x00FFFFFF);
    bmp.draw(loader);        // Write the decoded PNG to a bitmap data object
});
loader.loadBytes(png);

Bonus: Metadata support

Update: Support for encoding text metadata into the generated PNG has been added:

var bmp : BitmapData =  ...;
var metadata : Object = { };
metadata[PNGKeywords.TITLE] = "My PNG";
metadata["Custom key"] = "Chunky bacon!";
var png : ByteArray = PNGEncoder2.encodeWithMetadata(bmp, metadata);

Every key-value pair in the given metadata object will be encoded into the PNG as either a tEXt chunk (if the value can be represented in Latin-1) or an iTXt chunk (which supports arbitrary Unicode strings).

Installation

Note that this library requires Flash 10 or higher. Unfortunately, until Adobe decides what they're doing with the next version of Alchemy, this library won't work in any SWF compiled to target Flash player 11 (SWF version 13) or above (as of Flash Player 11.2). So, you can target Flash 10 (SWF versions 10-12) only for now. Update: It seems this library works fine even with recent versions of Flash Player (11.7 at the time of writing). Go figure.

ActionScript 3

If you're using ActionScript 3 you can simply download the SWC file and link it to your project. There are also "slim" versions of the library that contain only a single compression method each; these are recommended for production since they are smaller (assuming you only use one compression level).

To link an SWC into a project made with the Flash CS5.5 IDE, the steps are as follows:

  1. In the File menu, select "ActionScript Settings...".
  2. Under the "Library Path" tab, click the little red icon with the "f" logo on it. The tooltip of the button reads: "Browse to SWC file"
  3. Browse to wherever it was that you saved the SWC, and select it
  4. Press OK!

To link an SWC in other versions of the Flash IDE, follow this guide instead.

HaXe

If you're using haXe, just use the source directly instead of fiddling with SWCs (actually, your project won't compile if you use the SWC). All you need are the PNGEncoder2.hx and DeflateStream.hx files. Compiles under both haXe 2 and 3.

If you want to have only one compression method compiled (to cut down on generated code size), add -D FAST_ONLY, -D NORMAL_ONLY, or -D GOOD_ONLY to your compile options (this is equivalent to using the slim SWC versions). The decode method is by default not compiled in; to enable it, add the -D DECODER compile option (which can be combined with the others above).