2012年3月25日 星期日

使用 Flex 程式畫出 QRCode

在入手第一支智慧手機沒多久,就知道有 QR code 這個東東,當時認為它是一個神奇的東西,由於當時手機鏡頭的像素不夠高,許多 QR code 無法順利辨視,而沒有怎麼放在心上。直到最進身邊看到的應用越來越多,也就產生要自己作出 QR code 圖型的念頭。

對於寫程式的我,並不認為 QR code 的產生是不可行的,只要有已經釋出的函數庫,或是不太難理解的演算規則,產生出圖片應該不成問題。尤其是最近因為專案因素對於使用 Action Script 繪圖有不少的實作機會,更是增加自己不少信心,不過現實總是有些不如預期,繞了一些遠路後才找到解法。

在快要下決定研究 QR code 生成的數學公式時,還好發現在 Google Codes 裡發現有人提供的函數庫,讓我免於研究那些可能會讓人煩燥的數學公式,不過函數庫裡有小 Bug,以至不支援中文字,還好最後查到問題點,在這裡作一下記錄…

在 Google Code 上查到一個叫作「zxing」的專案,提供許多種程式語言不同的版本的條碼演算法,由於是 Apache 2.0 的授權方式,所以就安心的放到測試專案中。這次我下載的是 2.0 的版本,由於網路上並沒有關於 Action Script 版本的詳細說明(印象中有看到一個英文的,不過當時沒有看懂,靠自己研究出用法),因此萠生我打這篇文章的念頭。

下載的檔案包含許多程式語言的版本,在這邊我只取我需要的原始碼的部分…


建立一個專案,開始撰寫程式…
因為程式有一點小 Bug ,所以得抓原始碼進來,而不是先作成 swc 變成函數庫引用。

其中一個滿特別的現象,就是套件的命名是由「com.google」開頭,不過在專案中並沒有看到明顯由 Google 主導或贊助的字樣,所以不能判定和 Google 是不是有什麼關係。不過這不是本篇的重點,還是將焦點擺回程式上。

在 com.google.zxing 套件裡有一個叫作 Writer 的介面,判斷應該是所有編碼器的方法定義。同時在 com.google.zxing.qrcode.下看到一個叫作 QRCodeWriter 的類別實作它,所以看起來應該是使用它,底下將介面所定義的唯一的方法作簡單的翻譯…
/**
 *
 * @param contents 要進行編碼的內容
 * @param format 編碼的格式
 * @param width 編碼結果的寬度(像素)
 * @param height 編碼結果的高度(像素)
 * @param hints 其它編輯器所有支援的附加參數
 * @return 使用無號整數組成的矩陣表示編碼結果 (0 == 黑, 255 == 白)
 */
function encode(contents:String,  format:BarcodeFormat=null, width:int=0, height:int=0, hints:HashTable=null):Object;
以 QR Code 來說,只會用到1, 2, 5 三個參數,就實作中猜測 width 和 height 兩個參數應該是沒有影響,不過這兩個參數著實讓我繞了一些遠路,讓我一開始認為回傳回一個類似 BitmapData 之類的傳回值。結果是一個矩陣,由於在介面中定義的傳回值是 Object ,所以得到 QRCodeWriter 的程式碼裡找答案,結果原來是另一個叫作「BitMatrix」的類別,的的確確是一個「矩陣」資料。不過和介面定義的不同,並不是用 byte 而是使用布林值來表示。使用 _get 方法取得每個位置的資料,以下是例子…
0 0 0 0 0 0 0 ░░░░░░░
0 1 1 1 1 1 0 ░█████░
0 1 0 0 0 1 0 ░█░░░█░
0 1 0 1 0 1 0 ░█░█░█░
0 1 0 0 0 1 0 ░█░░░█░
0 1 1 1 1 1 0 ░█████░
0 0 0 0 0 0 0 ░░░░░░░
以上面的例子,由 _get(3, 3) 得到的值將是 false(白色)

以下我是一開始的程式碼…其中使用一個 Group 物件作畫布,將結果畫出來。

/**
* 在「建立」按鈕按下後,進行編碼
*/
private function createQRCode(event:MouseEvent):void {
    var writer:Writer = new QRCodeWriter(); // 建立編碼器
    // 變數 content 是我建立的一個 TextInput 物件,用來輸入要編碼的資料
    var mb:BitMatrix = writer.encode(content.text, BarcodeFormat.QR_CODE) as BitMatrix;

    var width:int = mb.width; // ?向有多少"格"
    var height:int = mb.height; // 縱向有多少"格"
    // 不過因為 QRCode 在規則上是正方形的,所以理論上兩個數值是一樣的
    var color:uint; // 畫圖用的資料

    // 取得畫布大小
    var cw:Number = canvas.width;
    var ch:Number = canvas.height;
    // 這裡使這兩個變數來表繪圖的起始點
    var sx:Number;
    var sy:Number;
    // 由於畫布不見得是正方形的,所以較小的數字為基準
    if(cw > ch) {
       sx = (cw - ch)/2;
       sy = 0;
       cw = ch;
    } else {
        sy = (ch - cw)/2;
        sx = 0;
       ch = cw;
    }
   canvas.graphics.clear();         // 清空之前畫的東西
   for(var y:int=0; y<height; y++) {
      for(var x:int=0; x<width; x++) {
         color = mb._get(x, y)?0:0xFFFFFF;
         canvas.graphics.beginFill(color, 1);
         canvas.graphics.drawRect(
            sx+cw/width*x, sy+ch/height*y,  // 計算格子的位置(座標)
            cw/width, ch/height             // 計算格子的大小
         );
         canvas.graphics.endFill();
      }
   }
}

附上一張程式截圖…


因為程式碼真的很難編輯,下一篇再說明如何解決中文字的問題…

1 則留言: