Load local image and get its pixels in actionscript
Monday, September 5th, 2011After some researches, I found this problem only occurs for local swfs with url starts with “file:///”. So this article would be not useful for you if you run your swfs on the web.
Flash player 10 introduces FileReference, which makes load local files possible. But for unknown secure reason, you can only display a local image but can’t get the pixels of local images. When you try to cast the Loader.content to a Bitmap instance, a #2148 security error will be thrown. Here is a solution for how to bypass this secure restriction. This solution also works for loading any image file data embedded with ByteArray forms.
Firstly, a template.swf needs to be created for embedding:
package
{
import flash.display.Sprite;
import flash.display.Bitmap;
public class Template extends Sprite
{
[Embed(source="small.jpg")]
public var BitmapClass:Class;
public function GetBitmap ():Bitmap
{
return new BitmapClass ();
}
}
}
Note there is a GetBitmap function in it. And you MUST embed a jpg file so that the solution supports 3 image formats: jpg, png and gif. If you are lazy to compile this file, here is one: template.swf
Now you can use the following LocalImageLoader class to replace the core Loader class.
package com.tapirgames.util
{
import flash.utils.ByteArray;
import flash.utils.Endian;
import flash.display.Loader;
import flash.system.LoaderContext;
import flash.events.Event;
import flash.events.ProgressEvent;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
public class LocalImageLoader extends Loader
{
// template swf file
[Embed(source="template.swf", mimeType="application/octet-stream")]
private static var SwfFile:Class;
// original data
private var mOriginalData:ByteArray = null;
private var mIsBadSwfFile:Boolean; // valid when mOriginalData != null
private var mStartOffset_TagDefineBitsJPEG2:int;
private var mEndOffset_TagDefineBitsJPEG2:int;
private var mCharacterID_TagDefineBitsJPEG2:int;
// load
override public function loadBytes (imageFileData:ByteArray, context:LoaderContext = null):void
{
try
{
// parse
if (mOriginalData == null)
{
var swfFile:ByteArray = new SwfFile ();
swfFile.endian = Endian.LITTLE_ENDIAN;
swfFile.position = 8;
mOriginalData = new ByteArray ();
mOriginalData.endian = Endian.LITTLE_ENDIAN;
swfFile.readBytes (mOriginalData);
mOriginalData.uncompress ();
var numBits:int = (mOriginalData [0] & 0xFF) >> 3;
mOriginalData.position = (((numBits << 2) + 5 + 7) >> 3) + 4;
while (true)
{
var startOffset:int = mOriginalData.position;
var tagTypeAndLength:int = mOriginalData.readUnsignedShort ();
var tagType:int = tagTypeAndLength >> 6;
if (tagType == 0) // end tag
{
mIsBadSwfFile = true;
break;
}
var tagLength:int = tagTypeAndLength & 0x3f;
if (tagLength == 0x3f)
{
tagLength = mOriginalData.readInt ();
}
var endOffset:int = mOriginalData.position + tagLength;
if (tagType == 21) // tag DefineBitsJPEG2
{
mCharacterID_TagDefineBitsJPEG2 = mOriginalData.readShort ();
mStartOffset_TagDefineBitsJPEG2 = startOffset;
mEndOffset_TagDefineBitsJPEG2 = endOffset;
mIsBadSwfFile = false;
break;
}
mOriginalData.position = endOffset;
}
}
mOriginalData.position = 0;
if (mIsBadSwfFile)
throw new Error ("bad template swf file");
// write
var newData:ByteArray = new ByteArray ();
newData.endian = Endian.LITTLE_ENDIAN;
newData.writeBytes (mOriginalData, 0, mStartOffset_TagDefineBitsJPEG2);
newData.writeShort ((21 << 6) | 0x3f);
newData.writeInt (2 + imageFileData.length); // 2 for CharacterID
newData.writeShort (mCharacterID_TagDefineBitsJPEG2);
if (imageFileData.length > 0)
{
newData.writeBytes (imageFileData);
}
newData.writeBytes (mOriginalData, mEndOffset_TagDefineBitsJPEG2);
var newFileLength:int = newData.length + 8;
newData.compress ();
var newSwfFile:ByteArray = new ByteArray ();
newSwfFile.endian = Endian.LITTLE_ENDIAN;
newSwfFile.writeByte (0x43);
newSwfFile.writeByte (0x57);
newSwfFile.writeByte (0x53);
newSwfFile.writeByte (10);
newSwfFile.writeUnsignedInt (newFileLength);
newSwfFile.writeBytes (newData);
newSwfFile.position = 0;
}
catch (error:Error)
{
dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR, false, false, error.message));
}
// load, now, no secure errors. Why adobe makes some troubles for developers?
super.loadBytes (newSwfFile, context);
}
}
}
Here is the example on how to use it:
public function LoadLocalImage (imageFileData:ByteArray):void
{
//var loader:Loader = new Loader(); // Don't use this
var loader:LocalImageLoader = new LocalImageLoader ();
loader.contentLoaderInfo.addEventListener (Event.COMPLETE, OnLoadLocalImageComplete);
loader.loadBytes (imageFileData);
}
private function OnLoadLocalImageComplete (event:Event):void
{
// var newBitmap:Bitmap = event.target.content as Bitmap; // Don't use this
var newBitmap:Bitmap = ((event.target.content.GetBitmap as Function) ()) as Bitmap;
mBitmap = new Bitmap (newBitmap.bitmapData); // done! no error #2148 will be thrown
}