How to dynamically load progressive jpeg / jpg in ActionScript 3 using Flash and know its width / height before it is fully loaded

I am trying to dynamically load a progressive jpeg using ActionScript 3. To do this, I created a class called Progressiveloader that creates a URLStream and uses it to stream progressive jpeg bytes to a byte array. Every time byteArray grows, I use Loader to load Bytes byteArray. This works, to some extent, because if I add a Loader loader, I can see jpeg as it streams, but I cannot access the contents of the Loader and, most importantly, I cannot change the width and height of the Loader .

After a lot of testing, I seem to understand the cause of the problem in that until the bootloader fully loads the jpg, that is, until it sees the final byte of jpg, it does not know the width and height, and it does not create DisplayObject content to be associated with Loader content.

My question is, is there a way to find out the width and height of jpeg before loading?

PS: I would believe that this is possible, due to the nature of the progressive jpeg, it is loaded into it in full size, but with less details, so the size should be known. Even when loading a normal jpeg in this way, the size is displayed on the screen, except that pixels that are not already loaded are displayed in gray.

Thanks.

+6
flash actionscript-3
source share
4 answers

I think your problem is that loadBytes is asynchronous ... that means you need to wait until the "full" event before you get (or change) its width and height ... I mean:

var loader:Loader=new Loader(); loader.loadBytes(myBytes); trace(loader.width, loader.contentLoaderInfo.width); //will always output zero loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imgLoaded); function imgLoaded(e) { trace(loader.width, loader.height); //will output the loader dimensions trace(loader.contentLoaderInfo.width, loader.contentLoaderInfo.height); //will always output the original JPEG dimensions } 
+1
source share

I chose Kay’s answer, but I’ll also answer my own question to add some clarification and tell everyone else about what I experienced, did something wrong, and eventually did everything right, in case someone else stumbles upon my question.

If you want to download jpeg gradually, you need two things: URLStream and Loader. Then you need to do the following:

1) You must use URLStream to load jpeg from URLRequest into ByteArray.

2) You need to add the PROGRESS event handler to the URLStream. In this handler, you need to use Loader to loadBytes () the loaded bytes of the URLStream.

3) You need a handler of the COMPLETE Loader handler to access each pass of the loaded jpeg and do whatever you want on it, for example, display it or resize it, etc.

4) You need a URLStream COMPLETE event handler to make sure all bytes are loaded and cleared after you and close the stream.

 var urlStream:URLStream = new URLStream(); //We will use this to progressively stream the bytes of the jpeg var byteArray:ByteArray = new ByteArray(); //Bytes from the URLStream will go here var loader:Loader = new Loader(); //We will use this Loader to load the bytes from the ByteArray var url:String = "http://myAddressToMyJpeg.jpg"; //The url to the jpeg urlStream.load(new URLRequest(url)); urlStream.addEventListener(ProgressEvent.PROGRESS, onStreamProgress, false, 0, true); urlStream.addEventListener(Event.COMPLETE, onStreamComplete, false, 0, true); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete, false, 0, true); function onStreamProgress(evt:ProgressEvent):void { // You could put a condition here to restrain the number of calls to updateBytes(). // Use the evt.bytesTotal and evt.bytesLoaded to help accomplish this. // You will find that by limiting it, it will increase responssivness of your // program and give an overall better result. // Have it call updateBytes() every 100 bytes or so. updateBytes(); } function onStreamComplete(evt:Event):void { updateBytes(); // Call updateBytes one more time to load in the last bytes. urlStream.removeEventListener(ProgressEvent.PROGRESS, onStreamProgress); // Clean after yourself urlStream.removeEventListener(Event.COMPLETE, onStreamComplete); // Clean after yourself // Somehow, without this, it does not work. You will end up with a ~90% loaded image setTimeout(confirmBytesLoaded,500); // Would be nice if someone could tell me why this makes it work!! } function confirmBytesLoaded():void { updateBytes(); // As said earlier, you need to check it one last time it seems. if (urlStream.connected) urlStream.close(); // Close the stream once you're done with it. } function updateBytes():void { // Important step. We copy the bytes from the stream into our byteArray, // but we only want to add the new bytes to our byteArray, so we use the lenght // attribute as an offset so that the new bytes gets added after the bytes that we added before. urlStream.readBytes(byteArray, byteArray.length); if(byteArray.length > 0) // Make sure there are new bytes to load. { loader.loadBytes(byteArray); // Use the Loader to decode the loaded bytes. } } // onLoaderComplete will be called many times. // Every time there is enough new bytes to diplay more of the image // onLoaderComplete will be called. So for every pass of the jpeg one // this will be called. function onLoaderComplete(evt:Event):void { // bm will now contain the bitmapData of the progressively loaded jpeg. var bm:Bitmap = Bitmap(loader); // We make a bitmap object from the loader. bm.width = 400; // Because my goal was to be able to resize the image as it is loaded and display it :). bm.height = 400; // Because my goal was to be able to resize the image as it is loaded and display it :). addChild(bm); // See the result for yourself... } 

Some notes throughout the process:

1) ConfirmBytesLoaded - it's your turn to know when the image has been fully loaded.

2) Loader will not send a full event if the bytes provided do not allow more images to be displayed. Therefore, the Loader progress event is not required if you do not want to know the loading progress of each jpeg pass.

3) In onLoaderComplete, you can do whatever you want. At this point, the event gives you a complete image for work. You can access the loader.content attribute. Remember that if this is not the last Loader completion event, it means that it is a partially loaded CustomActions image that you will have either in the lower layer or with some gray pixels in it.

4) When you use loadBytes, it loads the image in the context of your application. Therefore, make sure that you download only trusted content. I'm still not sure if there is a way around this to make it safe. See: http://onflash.org/ted/2008/01/loaderload-vs-loaderloadbytes.php

PS: Here is a link to the main part of my code:

http://orangeflash.eu/?p=13

Here are some links that actually show you the way to read the width and height yourself, by analyzing each byte, as they are loaded using the jped specification:

http://www.anttikupila.com/flash/getting-jpg-dimensions-with-as3-without-loading-the-entire-file/

http://www.emstris.com/2009/05/extracting-binary-info/

http://blog.onebyonedesign.com/?p=71

+10
source share

Reading metadata would be the best way to read image information without loading in the image itself that I think of.

therefore, you need to make sure that the image contains metadata files embedded in it. then you can read it using the as3 metadata library, for example http://patrickshyu.com/2009/04/jpg-exif-metadata-in-actionscript-3/

or you can create a server side script that reads in the image metadata and sends them to actionscript.

0
source share

You can achieve this with a simple PHP script:

 $imagedata = getimagesize($yourpath); $width = $imagedata[0]; $height = $imagedata[1]; 
-2
source share

All Articles