Async PNG encoding and merging

As you may read in my Twitter last week I was experimenting with asynchronous PNG image encoding and saving. It is very easy task until you need to save a really large/huge image file (for print purpose or whatever). As far as Flash don’t have ability to compress/deflate ByteArrays in append way or simply partly we surely will crash flash player while compressing image data for image larger then 10.000×10.000 px.

Joa provided a link to a flashzlib by Ed McManus and that way the “sign” to use Alchemy to process compression via ZLIB.
But what was my surprise when I found that we easily can compile original ZLIB sources in alchemy and use it in our Alchemy projects without any changes! And that was possible since December 2008! Can you believe it? ;-)
So I downloaded ZLIB sources and compile them in alchemy as the result I got static library I can append to any Alchemy project. All you need is to write wrapper C class to call functions in ZLIB and get the result back.

I also found brilliant optimized PNG class at wonderfl that used different filters for the image data. I’ve encapsulated that filters in my PNG encoder in both synchronous and asynchronous way. Alchemy is used only for the compress of image data all other operations done in Flash. It is also possible to compile PNGLIB for the Alchemy (as easy as ZLIB) but it will result in really huge *.swc file. So it is to you to decide.

See small demo app where you can encode large PNG files asynchronous with compress level and filter options.


Get Adobe Flash player

You can find the package and usage example here
Direct PNGEncoder SWC Download

22 Responses to “Async PNG encoding and merging”


  1. 1 dominic

    this is very cool! nice work once again.

  2. 2 wonderwhy-er

    Hmm I guess it still uses one CPU so performance wise make not so much difference.
    Tough not hanging main app while you do that is nice.

  3. 3 Mario Klingemann

    Of course I knew that you would not disappoint – great job!

  4. 4 paul

    brilliant! this will certainly push forward web-generative art …
    I hope I get some time soon to try this out :)

  5. 5 Mark

    Very cool and handy! Love this solution very much! Any change for a bigass jpg-encoder too?

    I think the biggest problem with this, is you cannot use that large bitmapdata sizes in real-time. If I create a bitmap (with tiles) at 24000×24000 and start generation art, my computer almost burns away.

  6. 6 max troost

    Hey Eugene, i really liked your JPGEncoder Class adaptation. I also tried your PNGEncoder but it seems to give a Error
    ReferenceError: Error #1065: Variable cmodule.gzip::CLibInit is not defined.
    at ru.inspirit.encoders.png::PNGEncoder$cinit()
    at global$init()
    can you help me out with this one?

    thanx

  7. 7 Eugene

    @max you should include gzip.swc file in your project. I guess thats the problem.

  8. 8 Steve

    This tool seems to solve all of the problems I experienced with Adobe’s PNGEncoder, thanks!

    That being said, my biggest complaint is the use of a static property to retrieve the encoded output. Using static in this fashion makes it easy for multiple encoder instances to conflict with each other, making it difficult to integrate this class in an OOP-friendly way.

  9. 9 BoyWonder

    Hi there,
    I can’t get the async method of encoding to work. I get errors in the files encoded with the encodeAsync function. This both happens with your demo app on this page, and when I implement the class on my own. The synchronous option works great.

    Smaller pictures are cut off, and colors and pixels are shifted. Larger images are not displayed at all with the error message “The image cannot be displayed, because it contains errors”.

    Any idea why this happens?

  10. 10 Eugene

    @boyWonder if u are talking about too large images this may be limitation of program that u use to open them. as for me i was able to open that files via ACDSEE without any errors. however photoshop wasn’t able to open big files but correctly shows 4000×4000 png file without any errors.

  11. 11 BoyWonder

    Thanks for getting back to me :)

    Check this picture: http://i.imgur.com/dqJqu.jpg
    This is what the async version produces when I try it.

    My code:
    var _loader:Loader;
    var png:PNGEncoder = new PNGEncoder();
    png.addEventListener(Event.COMPLETE, testFunc);
    png.encodeAsync(_pic.bitmapData);

    protected function testFunc(evt:Event):void
    {
    _loader = new Loader();
    _loader.contentLoaderInfo.addEventListener(Event.INIT, down);
    _loader.loadBytes(PNGEncoder.encodedPNGData);
    }

    public function down(evt:Event):void
    {
    addChild(_loader.content);
    }

    Am i doing something wrong here? As I said, the encode function produces the expected result.

  12. 12 Eugene

    @BoyWonder you were right – it turns out there was a bug in zlib compression process. now it is fixed and i also moved the package to my google code repo. Please download it there and check out if it is OK now. Thanx for pointing out that nasty bug!

  13. 13 BoyWonder

    Works great now.
    Thanks for the help!

  14. 14 Marco

    Hi Eugene;

    it seems to be great, but i couldn’t make it work.
    I’ve included the pngencoder.swc in my Library Path.
    and import ru.inspirit.encoders.png.PNGEncoder
    but i still have Errors :
    ReferenceError: Error #1065: La variable MainTimeline n’est pas définie.

    this is my (/your) code:

    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.events.Event;
    import flash.events.ProgressEvent;
    import flash.utils.ByteArray;
    // comment or uncomments this line doesn’t affect nothing
    // import ru.inspirit.encoders.png.PNGEncoder;
    // or
    // import PNGEncoder;

    var bmd:BitmapData = new BitmapData(400, 300, true, 0xFF990000);
    addChild(new Bitmap(bmd));

    var png:PNGEncoder = new PNGEncoder();
    png.encodeAsync(bmd, true, -1, 0);
    function onEncoded(e:Event = null):void
    {
    // here is our encoded PNG file ByteArray
    var _data:ByteArray = PNGEncoder.encodedPNGData;
    }
    function onEncodeProgress(e:ProgressEvent):void
    {
    trace(‘ENCODING PNG: ‘ + String( Math.round(e.bytesLoaded / e.bytesTotal * 100) ) + ‘%’);
    }

  15. 15 Paul-André Belle-Isle

    i got this error :

    RangeError: Error #1506: La plage indiquée n’est pas valide.
    at cmodule.gzip::FSM_imalloc$/start()
    at cmodule.gzip::FSM_pubrealloc/work()
    at ()
    at ()
    at ru.inspirit.encoders.png::PNGEncoder$/encode()[E:\_exp\PNGEncoder\src\ru\inspirit\encoders\png\PNGEncoder.as:161]
    at ZaailLibDemo_fla::MainTimeline/onSaveClick()

    You got an idea why ?

  16. 16 smival

    abc bytecode decoding failed!

  17. 17 Anton

    Hi.
    I use your encoder to combine multiple BitmpData into one. Use a method encodeMultiToOne. Encoding result – http://i.imgur.com/VkjoF.png .

    Please tell my what went wrong.

  18. 18 Karatchi

    Hi,

    I’m looking for a way to read in PNG files at runtime and have access to it’s layer data. I assume layers are an Adobe Fireworks feature and not stictly native to a standard PNG file.

    Any ideas?

  1. 1 uberVU - social comments
  2. 2 Encoding PNGs in AS3, asynchronously | BIT-101 Blog
  3. 3 AS3 Diferentes clases para leer/escribir ficheros de imagen. | xperiments.es
  4. 4 Quick tip: Fastest ActionScript PNGEncoder at Jozef Chúťka's blog

Leave a Reply