Published: 09/04/09 02:54 PM
The asynchronous load-methods of flex can really be a pain, because in Flex you can’t control threads.
I needed to display images from a binary file that contains meta-data and a bunch of images.
Here’s an easy way to load a Bitmap from ByteArray without having to deal with events.
The Problem
I read a binary file that contains a bunch of images and store it in memory as a ByteArray for fast access.
Whenever an image is requested to be displayed, I create BitmapData from that ByteArray.
The images are not plain bitmaps, but can be jpg, gif or png. Thus, they are encoded.
You cannot simply call BitmapData::setPixels(myByteArray) because you first would have to decode it. Flex provides a PngEncoder, but no PngDecoder, so the attempt to synchronously convert the ByteArray to BitmapData fails.
You have to use Loader::loadBytes. This method works asynchronously, which means, it doesn’t block. We fall through that step and cannot return a valid BitmapData-Object until the event Event.COMPLETED has been fired by the contentLoaderInfo.
Because I didn’t want my entire application to be concerned about loading-events, I implemented a simple but working BitmapProxy.
Here’s the solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | package com.macrominds { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Loader; import flash.display.LoaderInfo; import flash.events.Event; import flash.geom.Point; import flash.geom.Rectangle; import flash.utils.ByteArray; public class BitmapProxy extends Bitmap { public function BitmapProxy(byteArray:ByteArray, bitmapData:BitmapData=null,pixelSnapping:String="auto", smoothing:Boolean=false){ super(bitmapData,pixelSnapping,smoothing); var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete); loader.loadBytes(byteArray); } public function onLoadComplete(event:Event):void{ var loader:LoaderInfo = event.target as LoaderInfo; var bmp:Bitmap = loader.loader.content as Bitmap; var bmpDat:BitmapData = bmp.bitmapData; this.bitmapData.copyPixels(bmpDat,new Rectangle(0,0,width,height), new Point(0,0)); //no need to close the loader, it's a ByteArray loader } } } |
You would create your Bitmap like that:
1 2 3 4 5 | var width:int = attr[0]; //read from meta-data, you'd have to adapt this var height:int = attr[1];//read from meta-data, you'd have to adapt this var buffer:ByteArray = loadIntoBuffer(idx,null,0,4,size); //custom method to read the part of the binary data which contains the encoded png data. You'd have to adapt this. var bmpData:BitmapData = new BitmapData(width, height); // you could create it as follows to produce a transparent image instead of a white one: new BitmapData(width, height, true,0x00ffffff); return new BitmapProxy(buffer, bmpData); //here we return our proxy, which already has the correct width and height, but an empty (yet white bitmap) |
Hey
What a crazy, but really great solution.
That’s what I was looking for. Thank you!
Alex
You’re welcome:)
Hello Thomas,
I use loader.loadBytes to load images, which the user selected with FileReference from their pc, then were loaded with filereference.load.
It works fine for many files, then at some point, it loads a filereference.data with loader.loadBytes, the data has a normal length, yet when it gets to my complete event, the loader has a width of 0 and height of 0. Once it gets to that point, all images after that have problems. Do you happen to have any idea, what could be causing this? or how I can workaround this? I have error handlers and try catch, but it doesn´t hit any of the error handlers. Though Internet Explorer crashes at the same time with a “runtime error This application has requested the runtime to terminate in an unusual way. Please contact the application´s support team for more information.” Mozilla does not throw a runtime error, but sometimes comes up with the empties too.
Do you happen to have any clue for me, on what could be causing this? or how I can work around this?
I´m making a file uploader, where users can upload up to 100 files at once, they show right away as thumbnails, so the user can start choosing what to do with them.
Hi Susana,
sorry, I don’t know what causes this problem.
Just a hint from (the german page) http://help.adobe.com/de_DE/AS3LCR/Flash_10.0/flash/net/FileReference.html#load():
“Note: When publishing for Flash Player 10 or AIR 1.5, only one of the following operations can be active at one time: FileReference.browse(), FileReference.upload(), FileReference.download(), FileReference.load(), FileReference.save(). Otherwise the Application will throw a Runtime-Error (Code 2174). Abort a running Action with FileReference.cancel(). This limitation applies only to Flashplayer 10 and AIR 1.5. Former versions of Flashplayer or AIR are not affected by this limitation regarding concurrent execution of several operations.”
Maybe you should take care that you explicitely cancel a load-action when an error occurs.
Please feel free to contact me via mail (see “Kontakt / Impressum”). Maybe I can help you if had your sources.
Hello Thomas,
Thanks for answering.
Yes, I´m aware of that. I make sure, I don´t .upload them until all the .load and .loadBytes are done. It doesn´t say anything about not doing a filereference.load and a filereference.data.loadBytes at the same time. Mine do happen at the same time at first, but the .load are all done by the time the first item is done with the .loadBytes. And I don´t have a problem until about the 14th item.
But, maybe it doesn´t do the .load correctly, when it´s in the middle of the .loadBytes. I will change it and let all the .load happen first, then start with the .loadBytes, see, if that changes it. I´ll let you know.
Susana
Hello Susana,
did you try to vary the filesize or filetypes? Maybe the flashplayer just isn’t allowed to use the needed amount of memory.
Greetings,
Thomas
hmmm, I just tried loading them all with the .load before starting any .loadBytes, and now I have empties even sooner. Used to be at 14 – 20 pictures, now at 8 pictures.
I first thought, I got empties, when I had 40mb loaded as thumbnails, but then I tried a different set of pictures (larger ones), and with them it started at 100mb.
With each set of pictures the first empty came at the same picture, unless I added or subtracted one at the end, then it shifted. I have checked, that each picture can load alone
For the set of pictures, where I got empties at 18, I tried loading 19 or loading 30, before I separated the .load and .loadBytes, and almost all were loaded with .load, before the first one was loaded with .loadBytes, and it didn´t matter how many more were queued, from .load.
I´m still puzzled.
It´s not totally random, as I can repeat it with the same set of pictures and it will bomb on the same picture. But the mb, where it bombs changes, and the seconds after start change…
Susanne
The first set of pictures is about 2mb each, and it bombs at 40mb.
The second set of pictures is about 4mb each, and it bombs at 100mb.
all are .jpg. That´s the only type they want to load.
Sorry, if this sounds naive or something but..
As far as I understand it’s always the same picture that causes the problem.
It always crashes at a certain point. You state that it shifts if you remove or add one picture at the end. Are you sure that you don’t read the images in from the end?
It really seems to be a certain “broken” or unreadable image. The larger images: Are these the original images of your first image-set? Ever tried a totally different set of images?
I had an issue myself with an 8-bit png. Only one image. All images were perfectly loaded and have been created in totally the same way. But this very picture caused problems. Can’t tell why though.
Maybe you have a similar problem with your jpg.
Can you identify the filename of the image that causes the crash? I’d suggest to identify the “broken” picture, remove it and run the tests again.
Ah.. sorry, I just read your previous comment again: “I have checked, that each picture can load alone”. Hm.. kinda strange behaviour. Keep reporting your progress. This issue really interests me:)
I tried loading the first one with .load, then with .loadBytes, storing the other filereferences in an array to be loaded after each has loaded as thumbnails, so I can remove the one fully loaded from memory, but that didn´t work. I don´t know, if it no longer has access to doing the .load, if I store it in an array first. Does it have to be done immediately, or it no longer has access to it? From the first one that was in the array waiting, they all came up empty.
Btw, here is the website, where I have it up for testing (though not in the latest version):
http://www.artido.de/manager/calendar/Poster/index.php
It’s hard to tell without sourcecode. If you sent me your sources via e-mail, I’d have a glance at it. (keeping it confidential).
It could be a security-issue, but that wouldn’t fit the problems you reported earlier.
I am not experienced with flash-file-uploads yet. Just used the image-proxy for a game, which loads files of a custom format from a local location.
Thanks for the link. I tried testing it with the firefox extension live-http-headers to have a look at what actually happens. Note that your page keeps trying to load http://www.artido.de/manager/calendar/Poster/images/smWarningDreieck.png which responds with a 404. This caused such a noise in the log, that I didn’t continue testing.
Hi Thomas,
Thanks so much for testing. I uploaded the missing image. I didn´t know about the firefox extension live-http-headers, will check that out.
There is so much to learn. I never had to deal with memory management before this project, I´m working now on adding that to all the code.
Hi Susana,
you’re welcome:)
I have to admit that Live-http-headers didn’t reveal anything. It’s just the wrong tool (but usually a great tool for webdevelopment, check it out).
Just wanted to know the point in time when uploads happen and how they are performed.
I just uploaded two jpgs. As far as I can tell, everything went fine.
Wish you good luck:) Keep reporting:)
I wish I knew, if I would have the same problem, if I brought it into flex. But I haven´t used flex yet and have to learn it first. I read I could use image.src = filereference.data instead of having to use .loadBytes.
So we found out, it needed a loader.unload() after loading each image as a thumbnails. Apparently, it works fine for a few without it, but for a bunch it needs it.
We additionally found out that it may help to add a delay if bitmapdata has width==0 in init/complete events. Just add a delay of a few seconds and try again to read the width. It might work then.
Actually, I tried reading the width after several seconds, even waited a couple of minutes, but didn´t get it. But, when I called unloadAndStop after a 10second delay, and then tried again to loadBytes the same image it worked. If I tried immediately to load it again, it didn´t work.
Well, it gets past a few of those, starts the timer, unloads the loader, then reloads the image, this time successfully, then loads several images without a problem, then I get another runtime error now (in Internet Explorer). Of course no explanation as to why.
At picture #55 or so I now get the runtime error without it showing that the loader.width isn´t available.
“runtime error This application has requested the runtime to terminate in an unusual way. Please contact the application´s support team for more information.”
As we discussed via E-Mail, it might be better to implement the thumbnail-feature with javascript and the upload feature with flash.
This way, you could take advantage of the multi-upload ability of flash without having to deal with images in flash. I’d test if flash is able to load and upload the images just as bytes, without having to know that it handles images.
You could show the thumbnails using Javascript and clientside scaling. Javascript might also be able to detect if the images have valid dimensions and stuff.
Once I changed the framerate from 30 to 12 along with everything else I had done, I was able to get past the problem.