mobile internet age is coming, cloud app age is coming
Monday, September 5th, 2011what these mean for game 2.0?
need to think.
what these mean for game 2.0?
need to think.
After 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
}
Didn’t get satisfied actionscript base64 code snippet by google. So made one by self.
package
{
public class Base64
{
public static const Base64Chars:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
private static var Base64Char2Index:Array = null;
private static function GetBase64Char2IndexTable ():Array
{
if (Base64Char2Index == null)
{
Base64Char2Index = new Array (128); // all char codes in Base64Chars are smaller than 128
for (var i_char:int = Base64Chars.length - 2; i_char >= 0; -- i_char) // "Base64Chars.length - 2" is to ignore the "=" cahr
{
Base64Char2Index [Base64Chars.charCodeAt (i_char)] = i_char;
}
}
return Base64Char2Index;
}
public static function DecodeString2ByteArray (text:String):ByteArray
{
if (text == null)
{
return null;
}
var num_chars:int = text.length;
var num_triples:int = num_chars / 4;
var num_extras:int = num_chars - num_triples * 4;
if (num_extras != 0)
{
return null;
}
var data:ByteArray = new ByteArray ();
var b0:int, b1:int, b2:int, b3:int;
var table_char2index:Array = GetBase64Char2IndexTable ();
var i_char:int = 0;
data.position = 0;
while (i_char < num_chars)
{
b0 = table_char2index [text.charCodeAt (i_char ++)];
b1 = table_char2index [text.charCodeAt (i_char ++)];
b2 = table_char2index [text.charCodeAt (i_char ++)];
b3 = table_char2index [text.charCodeAt (i_char ++)];
data.writeByte ((b0 << 2) | ((b1 & 0x30) >> 4));
data.writeByte (((b1 & 0x0f) << 4) | ((b2 & 0x3c) >> 2));
data.writeByte (((b2 & 0x03) << 6) | b3);
}
data.position = 0;
return data;
}
public static function EncodeByteArray2String(data:ByteArray):String
{
if (data == null)
{
return null;
}
var num_triples:int = data.length / 3;
var num_extras:int = data.length - num_triples * 3;
var num_chars:int = (num_extras > 0 ? num_triples + 1 : num_triples) * 4;
var char_indexes:ByteArray = new ByteArray ();
char_indexes.length = num_chars;
var b0:int, b1:int, b2:int;
data.position = 0;
char_indexes.length = 0;
for (var i_triple:int = 0; i_triple < num_triples; ++ i_triple)
{
b0 = data.readByte () & 0xFF; // "& 0xFF" is essential for negative values
b1 = data.readByte () & 0xFF;
b2 = data.readByte () & 0xFF;
char_indexes.writeByte ((b0 & 0xFC) >> 2);
char_indexes.writeByte (((b0 & 0x03) << 4) | (b1 >> 4));
char_indexes.writeByte (((b1 & 0x0F) << 2) | (b2 >> 6));
char_indexes.writeByte (b2 & 0x3F);
}
if (num_extras > 0)
{
b0 = data.readByte () & 0xFF; // "& 0xFF" is essential for negative values
if (num_extras == 2)
{
b1 = data.readByte () & 0xFF; // "& 0xFF" is essential for negative values
char_indexes.writeByte ((b0 & 0xFC) >> 2);
char_indexes.writeByte (((b0 & 0x03) << 4) | (b1 >> 4));
char_indexes.writeByte ((b1 & 0x0F) << 2);
char_indexes.writeByte (64); // "="
}
else // num_extras == 1
{
char_indexes.writeByte ((b0 & 0xFC) >> 2);
char_indexes.writeByte ((b0 & 0x03) << 4);
char_indexes.writeByte (64); // "="
char_indexes.writeByte (64); // "="
}
}
var char_index:int;
for (var i_char:int = 0; i_char < num_chars; ++ i_char)
{
char_index = char_indexes [i_char];
char_indexes [i_char] = Base64Chars.charCodeAt(char_index);
}
char_indexes.position = 0;
return char_indexes.readUTFBytes (num_chars);
}
}
}