Bitmap序列化(將BitmapData保存爲原生Binary/ByteArray),AS3保存圖像的方法

應用程序需要將位圖圖像保存到本地或發送到服務端時, 通常的方法是在發送數據前將圖像通過PNG或JPEG編碼。如果只是想保存位圖圖像,只要序列化BitmapData即可,將圖像轉換爲JPEG/PNG是完全沒有必要的。

BitmapData 轉換爲 ByteArray

獲得BitmapData對應的字節數組, 所要做的只是調用getPixels()方法。getPixels()方法需要指定捕捉區域;最便捷的方法就是使用即將序列化的BitmapData的rect屬性。

// ActionScript 3.0
// 假定“bitmapImage”是需要序列化的位圖對象
var bytes:ByteArray = bitmapImage.bitmapData.getPixels(bitmapImage.bitmapData.rect);
複製代碼

這個方法會返回一個ByteArray對象,BitmapData的每個像素對應ByteArray對象中的一個4字節的無符號整型。這意味着如果是20x20的位圖圖像, 對應的ByteArray對象在壓縮前有1600個字節(20x20x4=1600)
得到ByteArray對象後, 壓縮:

var bytes:ByteArray = bitmapImage.bitmapData.getPixels(bitmapImage.bitmapData.rect);
bytes.compress();
複製代碼

得到了位圖圖像無損壓縮的二進制數據了.

位圖尺寸(寬與高)

這樣看來, 得到位圖圖像對應的ByteArray數據很容易 - 只要調用getPixel()方法即可.當然, 將ByteArray再構造爲位圖圖像才能證明數據是有用的. 除像素數據外, 字節數組不能爲位圖圖像指定尺寸.就是說你得把尺寸信息也要保存在字節數組裏.其實只要保存高度或寬度即可, 因爲已經知道了像素總數, 通過計算便能算出另一個.

下面的代碼中,字節數組前4個字節保存BitmapData的寬度, 接下來再保存圖像字節數組.

var bytes:ByteArray = new ByteArray();
bytes.writeUnsignedInt(bitmapImage.bitmapData.width);
bytes.writeBytes(bitmapImage.bitmapData.getPixels(bitmapImage.bitmapData.rect));
bytes.compress();
複製代碼

保存文件

前面的工作完成後就可以使用常用的方法保存二進制數據了(發送給服務端腳本,AIR本地文件API,SharedObject以及FP10 FileReference等等).這個例子中, 我們通過使用FileReference類的save()方法(需要Flash Player 10)將二進制數據保存到本地存儲器中.由於Flash Player的安全措施,save()方法只有在用戶交互事件中才能夠調用(例如鼠標點擊事件).因此需要新建一個按鈕並附加一個監聽器, 在事件處理方法中調用save()方法.

// ** 需要Flash Player 10以上版本 **
function on_buttonClick(evt:MouseEvent):void
{
        var bytes:ByteArray = new ByteArray();
        bytes.writeUnsignedInt(bitmapImage.bitmapData.width); // 保存圖像寬度
        bytes.writeBytes(bitmapImage.bitmapData.getPixels(bitmapImage.bitmapData.rect)); //保存圖像字節數組
        bytes.compress();
        new FileReference().save(bytes, "image.bmd"); // 默認文件名: "image.bmd"
}
複製代碼

文件可任意命名.上面的例子中, 我使用了".bmd"(BitmapData)做爲文件擴展名,不過這只是一個自己想出的文件類型.最終保存的文件無有效MIME的, 不會當作已知的文件類型運行 - 這是我們自定義的二進制數據格式文件, 僅僅是用來保存圖像數據, 方便以後我們的程序重用.

ByteArray 轉換爲 BitmapData

上面提到過, 我們要將保存的數據重構, 這樣才能還原出原始位圖圖像.
首先, 通過URLLoader加載文件:

var ldr:URLLoader        = new URLLoader();
ldr.dataFormat        = URLLoaderDataFormat.BINARY; // ** 這裏一定要指定dataFormat爲URLLoaderDataFormat.BINARY **
ldr.addEventListener(Event.COMPLETE, on_fileLoad);
ldr.addEventListener(IOErrorEvent.IO_ERROR, on_fileLoadError);
ldr.load(new URLRequest(pathToBitmapDataFile));
複製代碼

事件處理方法on_fileLoad:

function on_fileLoad(evt:Event):void
{
        if (evt.type == Event.COMPLETE)
        {
                var data:ByteArray = URLLoader(evt.target).data as ByteArray;
                if (data)
                {
                        try
                        {
                                data.uncompress();
                        }
                        catch(e:Error)
                        {
                        }
                        // 此時的數據已經是解壓後的字節數組了
                        // ... 處理數據 ...
                }
        }
}
複製代碼

現在我們來取出位圖圖像的尺寸. 還記得之前我們在二進制數據的頭4個字節保存了寬度值吧.

// 數據解壓後
var width:int = data.readUnsignedInteger(); // 起始的4個字節
複製代碼

得到高度:

// after data.uncompress()
var height:int = ((data.length - 4) / 4) / width;
// (data.length - 4) ** 去掉開始的4個字節,其餘的便是位圖的字節數組了 **
// ((data.length - 4) / 4) ** 每個像素4個字節長, 所以要除以4得到總像素數 **
// ((data.length - 4) / 4) / 寬度 ** 記住,因爲是矩形才能這樣計算出高度 **
複製代碼

注意:如果要忽略尺寸計算, 可以把高寬同時保存在二進制數據中.兩種方法都是可行的, 可自行選擇.

得到尺寸後, 就可以使用setPixels()方法重構Bitmap對象了.

var bmd:BitmapData = new BitmapData(width, height, true, 0); // 32位支持alpha通道的位圖
bmd.setPixels(bmd.rect, data); // 數據的position指向第5個字節了

var bm:Bitmap = new Bitmap(bmd);
addChild(bm);
複製代碼

結論

以上方法展現了將BitmapData數據轉換爲ByteArray, 保存ByteArray, 然後再將已保存的ByteArray重新構造爲BitmapData的整個過程.雖然基本目標是能夠把位圖圖像保存到服務器/本地存儲器, 但上述技巧放在其他情況中也是十分有用的.例如, 得到圖像的ByteArray數據後, 可以將其發送(post)到服務器做進一步處理. 也可用來裁減外部的JPEG/PNG圖像文件,去掉所有的JPEG/PNG編碼中含有的元數據信息(meta information), 只留下原始(raw)圖像數據(文件可能更小了).當然了, 最終的二進制文件不能做爲JPEG/PNG打開了, 但應用程序能夠在運行時很容易的重構出相應的圖像來.實際上,也可認爲這是一種保護外部圖片不被盜鏈的好方法.



發佈了29 篇原創文章 · 獲贊 11 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章