範例:ASCII 藝術

這個「ASCII 藝術」範例會說明在 ActionScript 3.0 中使用 String 類別的多項功能,其中包括:

「ASCII 藝術」是指用文字來呈現影像,利用一組等寬字型的字元 (如 Courier New 字元) 來繪製影像。下圖便是應用程式所產生的 ASCII 藝術範例:


顯示樣本 ASCII 藝術輸出的影像:一張梨子圖旁邊並列著用 ASCII 字元建立的梨子影像。

圖片的 ASCII 藝術版本顯示在右邊。


若要取得此樣本的應用程式檔案,請參閱 www.adobe.com/go/learn_programmingAS3samples_flash_tw。您可以在 Samples/AsciiArt 檔案夾中找到 ASCIIArt 應用程式檔案,它是由下列檔案組成:

檔案

說明

AsciiArtApp.mxml

AsciiArtApp.fla

Flash (FLA) 或 Flex (MXML) 中的主應用程式檔案。

com/example/programmingas3/asciiArt/AsciiArtBuilder.as

提供應用程式主要功能的類別,這些功能包括從文字檔擷取影像中繼資料、載入影像,以及管理影像轉為文字的程序。

com/example/programmingas3/asciiArt/BitmapToAsciiConverter.as

提供 parseBitmapData() 方法的類別,這個方法會將影像資料轉換為 String 版本。

com/example/programmingas3/asciiArt/Image.as

代表所載入點陣圖影像的類別。

com/example/programmingas3/asciiArt/ImageInfo.as

代表 ASCII 藝術影像中繼資料 (如標題、影像檔 URL 等) 的類別。

image/

檔案夾,其中包含應用程式所使用的影像。

txt/ImageData.txt

以定位點分隔的文字檔,其中包含應用程式將載入的影像的相關資訊。

副主題

擷取定位點分隔值
使用 String 方法標準化影像標題
產生 ASCII 藝術文字

擷取定位點分隔值

這個範例會採取一般將應用程式資料與應用程式本身分開儲存的做法,這樣一來,如果資料變更 (例如加入其它影像或影像的標題變更),就不需要重新建立 SWF 檔。在此例中,影像中繼資料 (包括影像標題、實際影像檔的 URL,以及用來操作影像的一些值) 會儲存在文字檔中 (即專案中的 txt/ImageData.txt 檔案)。文字檔的內容如下:

FILENAME    TITLE    WHITE_THRESHHOLD    BLACK_THRESHHOLD
FruitBasket.jpg    Pear, apple, orange, and banana    d8    10
Banana.jpg    A picture of a banana    C8    20
Orange.jpg    orange    FF    20
Apple.jpg    picture of an apple    6E    10

檔案使用特定的定位點分隔格式。第一行 (列) 是標題列。其它行則包含要載入的每個點陣圖的下列資料:

當應用程式一啟動,AsciiArtBuilder 類別就會使用 parseImageInfo() 方法的下列程式碼,以載入並剖析文字檔的內容,然後建立將顯示的影像「堆疊」:

var lines:Array = _imageInfoLoader.data.split("\n");
var numLines:uint = lines.length;
for (var i:uint = 1; i < numLines; i++)
{
    var imageInfoRaw:String = lines[i];
    ...
    if (imageInfoRaw.length > 0)
    {
        // 建立新影像資訊記錄,然後將記錄加入影像資訊陣列中。
        var imageInfo:ImageInfo = new ImageInfo();

        // 將目前這一行分為多個值 (用定位點 (\t)
        // 字元隔開),然後擷取個別屬性:
        var imageProperties:Array = imageInfoRaw.split("\t");
        imageInfo.fileName = imageProperties[0];
        imageInfo.title = normalizeTitle(imageProperties[1]);
        imageInfo.whiteThreshold = parseInt(imageProperties[2], 16);
        imageInfo.blackThreshold = parseInt(imageProperties[3], 16);
        result.push(imageInfo);
    }
}

文字檔的整個內容包含在單一 String 實體 (即 _imageInfoLoader.data 屬性) 中。使用 split() 方法並將換行字元 ("\n") 設為參數,String 實體會分割成陣列 (lines),其元素即為文字檔中的每一行。程式碼接著會使用迴圈來處理每一行 (第一行除外,因為這只是標題,無實質內容)。在迴圈內,會再次使用 split() 方法,將單行內容分成一組值 (即名為 imageProperties 的 Array 物件)。split() 方法這次使用的參數是定位點 ("\t") 字元,因為各行的值都是用定位點字元隔開。

使用 String 方法標準化影像標題

這個應用程式的其中一項設計決定是,所有影像標題都會以標準格式顯示,也就是將每個字的第一個字母都改為大寫 (只有少數通常在英文標題裡不大寫的字不改)。應用程式不會假設文字檔包含格式正確的標題,而會在擷取文字檔中的標題時予以格式化。

在前面列出的程式碼中,當擷取個別影像中繼資料值時,會使用下面這一行程式碼:

        imageInfo.title = normalizeTitle(imageProperties[1]);

在此程式碼中,從文字檔擷取的影像標題會傳遞至 normalizeTitle() 方法處理,然後再儲存到 ImageInfo 物件中:

private function normalizeTitle(title:String):String
{
    var words:Array = title.split(" ");
    var len:uint = words.length;
    for (var i:uint; i < len; i++)
    {
        words[i] = capitalizeFirstLetter(words[i]);
    }
    
    return words.join(" ");
}

這個方法會使用 split() 方法,將標題分為幾個單字 (用空格字元隔開)、將每個字傳遞至 capitalizeFirstLetter() 方法處理,然後再使用 Array 類別的 join() 方法,將這些字組合回單一字串。

顧名思義,capitalizeFirstLetter() 方法就是將每個字的第一個字母改為大寫:

    /**
     * 將單字的第一個字母改為大寫,除非這個字
     * 通常在英文中不大寫。
     */
    private function capitalizeFirstLetter(word:String):String
    {
        switch (word)
        {
            case "and":
            case "the":
            case "in":
            case "an":
            case "or":
            case "at":
            case "of":
            case "a":
                // 不對這些字做任何處理。
                break;
            default:
                // 針對其它字,將第一個字母改為大寫。
                var firstLetter:String = word.substr(0, 1);
                firstLetter = firstLetter.toUpperCase();
                var otherLetters:String = word.substring(1);
                word = firstLetter + otherLetters;
        }
        return word;
    }

如果下列字出現在英文標題中,第一個字母「不」大寫:"and"、"the"、"in"、"an"、"or"、"at"、"of" 或 "a" (這是簡化過的規則)。為執行此邏輯,程式碼會先使用 switch 陳述式,檢查單字是否為首字母不應大寫的字。如果是的話,程式碼就會跳出 switch 陳述式。相反地,如果單字的首字母應大寫,那麼就會執行下列步驟來改為大寫:

  1. 使用 substr(0, 1) 擷取單字的第一個字母,這個方法會從索引位置為 0 的字元 (即字串中的第一個字母,因為第一個參數為 0) 開始擷取子字串。子字串的長度為一個字元 (因為第二個參數為 1)。
  2. 使用 toUpperCase() 方法,將該字母改為大寫。
  3. 原字的其它字元則使用 substring(1) 擷取,這個方法會從索引位置為 1 的字元 (即第二個字母) 開始擷取子字串,一直擷取到字串的結尾 (因為 substring() 方法的第二個參數沒有指定)。
  4. 最後會使用字串連接,將剛改為大寫的首字母與其它字母結合來產生單字:firstLetter + otherLetters

產生 ASCII 藝術文字

BitmapToAsciiConverter 類別可將點陣圖影像轉換為其 ASCII 文字表示。這是由 parseBitmapData() 方法執行,其部分內容顯示如下:

    var result:String = "";
    
    // 由上而下循序處理像素列:
    for (var y:uint = 0; y < _data.height; y += verticalResolution)
    {
        // 在各列內,從左到右循序處理像素:
        for (var x:uint = 0; x < _data.width; x += horizontalResolution)
        {
            ...

            // 將 0 到 255 之間的灰值轉換為
            // 0 到 64 之間的值 (因為這是可用字元組的「灰階」
            // 數字):
            index = Math.floor(grayVal / 4);
            result += palette.charAt(index);
        }
        result += "\n";
    }
    return result;

這段程式碼先定義一個名為 result 的 String 實體,以用來建立點陣圖影像的 ASCII 藝術版本。接下來便循序處理原始點陣圖影像的個別像素。利用多項色彩操作技巧 (因篇幅有限,不多詳述),程式碼將個別像素的紅、綠和藍色值轉換為單一灰階值 (0 到 255 之間的數字)。接著程式碼再將該值除以 4 (如上所示),轉換為 0 到 63 色階之間的值,然後將值儲存在變數 index 中(使用 0-63 色階的原因是,這個應用程式所用 ASCII 字元的「面板」包含 64 個值)。字元面板定義為 BitmapToAsciiConverter 類別中的 String 實體:

// 字元從最深到最淺依序排列,使其
// 在字串中的位置 (索引) 與相關顏色值相對應
// (0 = 黑色)
private static const palette:String = "@#$%&8BMW*mwqpdbkhaoQ0OZXYUJCLtfjzxnuvcr[]{}1()|/?Il!i><+_~-;,. ";

由於 index 變數會定義面板中的哪一個 ASCII 字元與點陣圖影像中的目前像素相對應,因此便使用 charAt() 方法,從 palette String 擷取該字元。這個字元接著會用連接指定運算子 (+=),附加到 result String 實體。此外,在每列像素結尾,會有換行字元連接到 result String 的結尾,以強制換行,產生一列新的字元「像素」。


Flash CS3

 

有新的意見加入至這個頁面時,傳送電子郵件給我 | 意見報告

目前頁面: http://livedocs.adobe.com/flash/9.0_tw/main/00000086.html