Posts Tagged ‘actionscript’

Load local image and get its pixels in actionscript

Monday, September 5th, 2011

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
}

actionscript 3 base64

Monday, September 5th, 2011

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);
      }
   }
}