Tuesday, April 28, 2009

[Flash] as3crypto

  • as3crypto
  • as3crypto是由henrit等人合力完成的Actionscript 3.0 Cryptography Library,提供了RSA、Data Encryption、Message-Digest等功能,讓Flash使用者可以輕鬆完成加密的工作,你可以從下面的網頁下載到這個API

    http://code.google.com/p/as3crypto/

    官方提供了一個很好的範例如下:

    http://crypto.hurlant.com/demo/

    我則使用as3crypto簡單做了一個Data Encryption/Decryption的demo(理論上來說,如果要為了效能應該是要用C+OpenSSL),程式碼裡直接指定使用Triple-DES+ECB Mode,而Key的長度預設為128bits,原始碼如下:

    package{
    import flash.display.Sprite;
    import flash.net.FileReference;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.utils.ByteArray;

    import fl.controls.Button;

    import com.hurlant.crypto.symmetric.ICipher;
    import com.hurlant.crypto.symmetric.IVMode;
    import com.hurlant.crypto.symmetric.IMode;
    import com.hurlant.crypto.symmetric.NullPad;
    import com.hurlant.crypto.symmetric.PKCS5;
    import com.hurlant.crypto.symmetric.IPad;
    import com.hurlant.crypto.prng.Random;
    import com.hurlant.crypto.hash.HMAC;
    import com.hurlant.util.Base64;
    import com.hurlant.util.Hex;
    import com.hurlant.crypto.Crypto;
    import com.hurlant.crypto.hash.IHash;

    public class CryptoDemo extends Sprite{
    private var loadFile:FileReference;
    private var saveFile:FileReference;

    private var plainData:ByteArray;
    private var cipherData:ByteArray;

    private var option:String;

    public function CryptoDemo(){
    encryBtn.label= "Encrypt";
    encryBtn.addEventListener(MouseEvent.CLICK, onClicked);

    decryBtn.label = "Decrypt";
    decryBtn.addEventListener(MouseEvent.CLICK, onClicked);

    keyLabel.text = "Key:";
    infoLabel.text = "Info:";

    genKeyBtn.label = "Generate 128 Bits";
    genKeyBtn.addEventListener(MouseEvent.CLICK, onClicked);

    loadFile = new FileReference();
    loadFile.addEventListener(Event.SELECT, onFileSelected);
    loadFile.addEventListener(Event.COMPLETE, onFileLoaded);

    saveFile = new FileReference();
    }

    private function onClicked(event:MouseEvent):Boolean{
    if(event.target.label == "Generate 128 Bits"){
    genKey(128);
    }else{
    if(keyIn.text == ""){
    infoTxt.text = "Please give a key!";
    return false;
    }
    option = event.target.label;
    loadFile.browse();
    }

    return true;
    }

    private function onFileSelected(event:Event):void{
    event.target.load();
    }

    private function onFileLoaded(event:Event):void{
    infoTxt.text = "Filename: "+event.target.name+"\nSize: "+event.target.size+" Bytes";

    switch(option){
    case "Encrypt":
    plainData = event.target.data;
    encrypt();
    saveFile.save(plainData, "encrypt.dat");
    break;
    case "Decrypt":
    cipherData = event.target.data;
    decrypt();
    saveFile.save(cipherData, "decrypt.dat");
    break;
    }

    plainData = cipherData = null;
    }

    private function genKey(v:int):void {
    var r:Random = new Random;
    var b:ByteArray = new ByteArray
    r.nextBytes(b, v/8);
    keyIn.text = Hex.fromArray(b);
    }

    private function encrypt():void {
    // key
    var k:String = keyIn.text;
    var kdata:ByteArray;
    kdata = Hex.toArray(Hex.fromString(k));

    // algorithm..
    var name:String = "simple-des3-ecb";

    // encryption
    var pad:IPad = new PKCS5;
    var mode:ICipher = Crypto.getCipher(name, kdata, pad);
    pad.setBlockSize(mode.getBlockSize());
    mode.encrypt(plainData);
    }

    private function decrypt():void {
    // key
    var k:String = keyIn.text;
    var kdata:ByteArray;
    kdata = Hex.toArray(Hex.fromString(k));

    // algorithm..
    var name:String = "simple-des3-ecb";

    // decryption
    var pad:IPad = new PKCS5;
    var mode:ICipher = Crypto.getCipher(name, kdata, pad);
    pad.setBlockSize(mode.getBlockSize());
    mode.decrypt(cipherData);
    }
    }
    }

    預覽畫面如下:

    由於Flash Player基於安全性的考量,FileReference物件的save()函式必須由mouse click等event來觸發,所以上面的範例將無法讓你正常儲存。

Monday, April 27, 2009

[C/C++] Bitmap Grayscale Conversion

  • Bitmap Grayscale Conversion
  • Download:bmpGrayConvertor.zip

    繼上次的[C/C++] Bitmap Bit Conversion之後,心血來潮再做一個Grayscale的。Grayscale比bit conversion容易實做多了,一個簡易的Grayscale的演算如下:

    ----------------------------------------------

    newRGB = (R*2 + G*5 + B) / 8

    ----------------------------------------------

    一個24bits全彩的pixel裡的紅色乘上2+綠色乘上5+藍色的總和再除以8,存到一個為8bits的pixel裡,所以技巧跟bitConversion很像,只是調色盤換了而已。

    這個範例裡所使用的調色盤:

    輸出結果如下:










    [左圖]是24bits全彩原始圖,[右圖]則是轉換成8bits grayscale的圖。雖然與完美的grayscale相比仍有些差距,但整體來說已經不錯了。

    這次的程式還有稍微處理一下width padding的問題,理論上應該可以正常轉換非4倍數大小的圖片。

Thursday, April 23, 2009

[Flash] TweenLite/Max

Wednesday, April 22, 2009

[Flash] HashMap

  • HashMap
  • Download:HashMap.zip

    原先遇到HashMap結構時我都直接使用polygonal網站上所提供的,但因為設計上的需求,需要更改HashMap裡的key/value pair的次序性,所以只好自己重新實做一個。下載包裡面包含了以下五個原始檔:

    -------------------------------------------------------

    • HashMapNode.as:A double linkedlist node for hash structure
    • Map.as:Interface for Map structure
    • HashMap.as:HashMap Class
    • Iterator.as:Interface for Iterator
    • HashIterator.as:HashIterator Class

    -------------------------------------------------------

    使用Double LinkedList Node結構比較方便之後的功能擴充

    /* HashMapNode.as */
    package xinyu.collection{
    public class HashMapNode{
    public var key:*;
    public var obj:*;

    public var prev:HashMapNode;
    public var next:HashMapNode;

    public function HashMapNode(key:* = null, obj:* = null, prev:HashMapNode = null, next:HashMapNode = null){
    this.key = key;
    this.obj = obj;
    this.prev = prev;
    this.next = next;
    }
    }
    }

    Map結構的介面,定義了一些Map結構該有的基本功能。

    /* Map.as */
    package xinyu.collection{
    public interface Map{
    function insert(key:*, obj:*):Boolean;
    function find(key:*):*;
    function remove(key:*):Boolean;
    function keySet():Array;
    function entrySet():Array;
    function clear():void;
    function isEmpty():Boolean;
    function iterator():Iterator;
    function containsValue(obj:*):Boolean;
    function containsKey(key:*):Boolean;
    }
    }

    有別於ploygonal的HashMap,我將Dictionary給移除,僅只有在entrySet()裡有使用到(回傳不重複的entrySet),其餘的操作皆以Double LinkedList結構來運作,目的是要控制key/value 的次序性,所以我還多了moveUp()跟moveDown()這兩個方法來移動entry的位置。

    /* HashMap.as */
    package xinyu.collection{
    import flash.utils.Dictionary;

    import xinyu.collection.Map;
    import xinyu.collection.HashMapNode;
    import xinyu.collection.Iterator;
    import xinyu.collection.HashIterator;

    public class HashMap implements Map {
    private var head:HashMapNode = new HashMapNode();
    private var tail:HashMapNode = new HashMapNode();

    public function HashMap() {
    head.next = tail;
    tail.prev = head;
    }

    public function insert(key:*, obj:*):Boolean {
    if (key == null || obj == null) return false;
    if (searchByKey(key) != null) return false;

    var node:HashMapNode = new HashMapNode(key, obj, tail.prev, tail);
    node.prev.next = node;
    tail.prev = node;

    return true;
    }

    public function find(key:*):*{
    var node:HashMapNode = searchByKey(key);
    if(node) return node.obj;
    return null;
    }

    public function remove(key:*):Boolean{
    var node:HashMapNode = searchByKey(key);
    if(node == null) return false;

    node.prev.next = node.next;
    node.next.prev = node.prev;
    return true;
    }

    public function keySet():Array{
    var array:Array = new Array();
    var curr:HashMapNode = head.next;

    while(curr!=tail){
    array.push(curr.key);
    curr = curr.next;
    }

    return array;
    }

    public function entrySet():Array{
    var array:Array = new Array();
    var node:HashMapNode = head.next;
    var dict:Dictionary = new Dictionary(true);

    while(node != tail){
    if(dict[node.obj] == undefined){
    dict[node.obj] = 1;
    array.push(node.obj);
    }
    node = node.next;
    }
    return array;
    }

    public function clear():void{
    var curr:HashMapNode = head.next;
    var nextPtr:*;
    while(curr!=tail){
    curr.key = null;
    curr.obj = null;
    nextPtr = curr.next;
    curr.next = curr.prev = null;
    curr = nextPtr;
    }

    head.next = tail;
    tail.prev = head;
    }

    public function swap(key1:*, key2:*):Boolean{
    if (key1 == null||key2 == null) return false;

    var node1:HashMapNode = searchByKey(key1);
    var node2:HashMapNode = searchByKey(key2);
    if(node1 == null || node2 == null) return false;

    var temp:HashMapNode = new HashMapNode();
    temp.key = node1.key;
    temp.obj = node1.obj;

    node1.key = node2.key;
    node1.obj = node2.obj;

    node2.key = temp.key;
    node2.obj = temp.obj;

    return true;
    }

    public function isEmpty():Boolean {
    if (head.next == tail) return true;
    return false;
    }

    public function moveUp(key:*):Boolean{
    if(key == null) return false;

    var node:HashMapNode = searchByKey(key);
    if(node == null) return false;
    if(node.prev == head) return false;

    return swap(key, node.prev.key);
    }

    public function moveDown(key:*):Boolean{
    if(key == null) return false;

    var node:HashMapNode = searchByKey(key);
    if(node == null) return false;
    if(node.next == tail) return false;

    return swap(key, node.next.key);
    }

    public function get size():int{
    var count:int = 0;
    var curr:HashMapNode = head.next;

    while(curr!=tail){
    count++;
    curr=curr.next;
    }

    return count;
    }

    public function containsKey(key:*):Boolean{
    if(searchByKey(key)) return true;
    return false;
    }

    public function containsValue(obj:*):Boolean{
    var curr:HashMapNode = new HashMapNode();
    curr = head.next;

    while(curr!=tail){
    if(curr.obj === obj) return true;
    curr = curr.next;
    }

    return false;
    }

    private function searchByKey(key:*):HashMapNode{
    var curr:HashMapNode = new HashMapNode();
    curr = head.next;

    while(curr!=tail){
    if(curr.key === key) return curr;
    curr = curr.next;
    }

    return null;
    }

    public function iterator():Iterator{
    return new HashIterator(head,tail);
    }
    }
    }

    Iterator介面的部份只有簡單定義幾個基本的功能。

    /* Iterator */
    package xinyu.collection{
    public interface Iterator{
    function hasNext():Boolean;
    function next():*;
    function remove():void;
    function rewind():void;
    }
    }

    HashIterator就依照著Iterator介面去實做,並沒有增加額外的功能,有別於Java的Iterator,我多了rewind()。因為我在其他設計專案中需要將iterator重新指向一開始,所以多了這個功能。

    /* HashIterator */
    package xinyu.collection{
    import xinyu.collection.Iterator;
    import xinyu.collection.HashMapNode;

    public class HashIterator implements Iterator{
    private var head:HashMapNode;
    private var tail:HashMapNode;
    private var curr:HashMapNode;

    public function HashIterator(head:HashMapNode, tail:HashMapNode){
    this.head = head;
    this.tail = tail;
    curr = head.next;
    }

    public function hasNext():Boolean{
    return (curr != tail);
    }

    public function next():*{
    if(curr != tail){
    var obj:* = curr.obj;
    curr = curr.next;
    return obj;
    }
    return null;
    }

    public function rewind():void{
    curr = head.next;
    }

    public function remove():void{
    tail.prev = tail.prev.prev;
    tail.prev.next = tail;
    }
    }
    }

    底下是一個很簡單的範例用來展示上面的HashMap的用法

    package{
    import flash.display.Sprite;

    import xinyu.collection.HashMap;
    import xinyu.collection.Iterator;
    import xinyu.collection.HashIterator;

    public class HashMapDemo extends Sprite{
    public function HashMapDemo(){
    var hashMap:HashMap = new HashMap();

    var obj1:Object = new Object();
    obj1.name = "object_1";
    var obj2:Object = new Object();
    obj2.name = "object_2";

    hashMap.insert("1", obj1);
    hashMap.insert("2", obj2);
    trace("isEmpty (after insertion): "+hashMap.isEmpty());
    trace("Size of hashmap: "+hashMap.size);

    trace("\nKeySet (before swap): "+hashMap.keySet());
    trace("Key 1's Object: "+hashMap.find("1").name);
    trace("Key 2's Object: "+hashMap.find("2").name);
    trace("EntrySet"+hashMap.entrySet());

    hashMap.swap("1","2");
    trace("\nKeySet (after swap): "+hashMap.keySet());
    trace("Key 1's Object: "+hashMap.find("1").name);
    trace("Key 2's Object: "+hashMap.find("2").name);


    trace("\nIteration result before move up the key 1:");
    var iter:Iterator = hashMap.iterator();
    while(iter.hasNext()){
    trace(iter.next().name);
    }

    hashMap.moveUp("1");

    trace("\nIteration result after move up the key 1:");
    iter.rewind();
    while(iter.hasNext()){
    trace(iter.next().name);
    }

    iter.remove();
    trace("\nSize of hashmap(after remove a node): "+hashMap.size);

    hashMap.clear();
    trace("\nisEmpty (after clear): "+hashMap.isEmpty());
    trace("Size of hashmap: "+hashMap.size);
    }
    }
    }

    輸出畫面如下:

    isEmpty (after insertion): false
    Size of hashmap: 2

    KeySet (before swap): 1,2
    Key 1's Object: object_1
    Key 2's Object: object_2
    EntrySet[object Object],[object Object]

    KeySet (after swap): 2,1
    Key 1's Object: object_1
    Key 2's Object: object_2

    Iteration result before move up the key 1:
    object_2
    object_1

    Iteration result after move up the key 1:
    object_1
    object_2

    Size of hashmap(after remove a node): 1

    isEmpty (after clear): true
    Size of hashmap: 0

Tuesday, April 21, 2009

[UTips. 67] BitDefender Antivirus

  • BitDefender Antivirus
  • 一套來自於羅馬尼亞的防毒軟體 - BitDefender,個人使用者可以在 EULA license的授權限制下免費使用BitDefender Antivirus Scanner for Unices,你可以從下面的網站與BitDefender官方取得授權序號和軟體。

    BitDefender Antivirus Scanner for Unices

    系統需求:

    Linux Kernel: 2.4.x or 2.6.x (recommended)
    FreeBSD: 5.4 (or newer with compat5x)
    glibc: version 2.3.1 or newer, and libstdc++5 from gcc 3.2.2 or newer
    Processor: x86 compatible 300 MHz; i686 500MHz; amd64(x86_64)
    Minimum Memory: 64MB (128MB recommended)
    Minimum Free Disk Space: 100MB

    系統環境:

    RedHat Enterprise Linux 3 or newer
    SuSE Linux Enterprise Server 9 or newer
    Fedora Core 1 or newer
    Debian GNU/Linux 3.1 or newer
    Slackware 9.x or newer
    Mandrake/Mandriva 9.1 or newer
    FreeBSD 5.4 or newer

    對於多系統的使用者不訪可以使用BitDefender來降低病毒散播。

Sunday, April 12, 2009

[Qt] Qt 4.5 QGroupBox with QGTkStyle

Wednesday, April 08, 2009

[Flash] Unduplicated Random Numbers

  • Unduplicated Random Numbers
  • 最近看到有人使用陣列元素移除的方式來產生不重複亂數,下面就是一個簡單的不重複亂數產生的範例,亂數範圍0~99,產生10個不重複亂數。

    Method One (Array Splice):

    var numSet:Array = new Array();
    var numArr:Array = new Array();
    var randNum:int;
    var n:int = 0;

    for (var i:int = 0; i < 100; i++) {
    numSet[n++] = i;
    }


    for (i = 0; i < 10; i++) {
    randNum = Math.floor(Math.random()* 99);
    numArr[i] = numSet[randNum];
    numSet.splice(randNum, 1);
    }

    上面的程式碼裡,只要有出現過的數字就在集合裡把他給移除,這樣的作法很直觀易懂,但是所付出的運算時間代價也比較高。下面是另一個我比較常用的手法:

    Method Two:

    var numSet:Array = new Array();
    var numArr:Array = new Array();
    var randNum:int;

    for (var i:int = 0; i < 100; i++) {
    numSet[i] = 0;
    }

    for (i = 0; i < 10; i++) {
    do {
    randNum = Math.floor(Math.random()* 99);
    } while (numSet[randNum]);
    numSet[randNum] = 1;
    numArr[i] = randNum;
    }

    numSet裡面所存的並不是亂數數字的集合,而是對應到每個數字的flag,用來標示此數字是否已經產生,這樣的作法乍看之下好像比耗空間(例如數字集合是在10000~20000時),但其實你可以透過一些簡單的運算技巧讓空間的消耗跟數字集合數成比例。

    使用第二個方法的效能會比第一個快許多,尤其當集合數增大時,差距會更明顯。

Saturday, April 04, 2009

[C/C++] Timer

  • Timer
  • 最近在做一些效能的測試需要用到比較精準的Timer,分別查了一下給Windows和Linux平台的寫法。

    For Linux:

    在Linux裡,則是使用timeval來計算時間,理論上精準到micro second

    #include <stdio.h>
    #include <sys/time.h>

    int main()
    {
    int i;
    struct timeval t1, t2;
    double elapsedTime;

    gettimeofday(&t1, NULL);
    gettimeofday(&t2, NULL);

    elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0;
    elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0;
    printf("%.3f ms\n",elapsedTime);
    return 0;
    }

    For Windows:

    Windows部份使用QueryPerformanceFrequency()這個指令,理論上是精準到跟時脈值一樣

    #include <stdio.h>
    #include <windows.h>
    int main()
    {
    LARGE_INTEGER frequency;
    LARGE_INTEGER t1, t2;
    double elapsedTime;

    QueryPerformanceFrequency(&frequency);

    QueryPerformanceCounter(&t1);
    QueryPerformanceCounter(&t2);

    elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
    printf("%.3f ms\n", elapsedTime);
    return 0;
    }

Friday, April 03, 2009

[Flash] FIDbg10.ocx crashed while loading file

  • FIDbg10.ocx crashed while loading file
  • 原以為FileReference的物件可以像在使用save()函式時宣告在method裡,沒想到這樣子會造成FIDbg10.ocx掛掉然後連帶著瀏覽器也被關閉。下面是一個會當掉的寫法

    package{
    import flash.display.Sprite;
    import flash.net.FileReference;
    import flash.events.Event;
    import flash.events.MouseEvent;

    import com.xinyu.button.TextButton;

    public class LoadFileDemo extends Sprite{
    private var loadBtn:TextButton;

    public function LoadFileDemo(){
    loadBtn = new TextButton(80,20,10,1,0xCCCCCC,"Load",0x000000);
    addChild(loadBtn);
    loadBtn.addEventListener(MouseEvent.CLICK, onClicked);
    }

    private function onClicked(event:MouseEvent):void{
    //declare the FileReference object inside the method that would lead to browser crash
    var file:FileReference = new FileReference();
    file.addEventListener(Event.SELECT, onFileSelected);
    file.addEventListener(Event.COMPLETE, onFileLoaded);

    file.browse();
    }

    private function onFileSelected(event:Event):void{
    event.target.load();
    }

    private function onFileLoaded(event:Event):void{
    trace(event.target.size);
    }
    }
    }

    可以看到上面的範例程式碼裡,我將FileReference的物件宣告在onClicked()這個Event Handler裡,拿去編譯執行後程式就會當掉。為了要解決這個問題,寫法要改成這樣:

    package{
    import flash.display.Sprite;
    import flash.net.FileReference;
    import flash.events.Event;
    import flash.events.MouseEvent;

    import com.xinyu.button.TextButton;

    public class LoadFileDemo extends Sprite{
    private var loadBtn:TextButton;
    private var file:FileReference;

    public function LoadFileDemo(){
    loadBtn = new TextButton(80,20,10,1,0xCCCCCC,"Load",0x000000);
    addChild(loadBtn);
    loadBtn.addEventListener(MouseEvent.CLICK, onClicked);
    }

    private function onClicked(event:MouseEvent):void{
    file = new FileReference();
    file.addEventListener(Event.SELECT, onFileSelected);
    file.addEventListener(Event.COMPLETE, onFileLoaded);

    file.browse();
    }

    private function onFileSelected(event:Event):void{
    event.target.load();
    }

    private function onFileLoaded(event:Event):void{
    trace(event.target.size);
    }
    }
    }