Exempel på strängar: ASCII-bilder

Flash Player 9 och senare, Adobe AIR 1.0 och senare

I det här exemplet med ASCII Art visas följande funktioner som används med klassen String i ActionScript 3.0:

  • Metoden split() i klassen String används för att extrahera värden från en teckenavgränsad sträng (bildinformation i en tabbavgränsad textfil).

  • Flera strängändringstekniker där bl.a. split(), sammanfogning och extrahering av en del av strängen med hjälp av substring() och substr() används för att få inledande versal av alla ord i bildrubriker.

  • Metoden getCharAt() används för att hämta ett enstaka tecken från en sträng (anger ASCII-tecknet som motsvarar ett bitmappsvärde för gråskala).

  • Strängsammanfogning används för att skapa en bild bestående av ASCII-tecken.

Termen ASCII-bildkonst innebär en textrepresenterad bild som byggs upp av tecken från ett typsnitt med fast teckenbredd, t.ex. Courier New. Följande bild är ett exempel på ASCII-bildkonst som skapats av programmet:

ASCII-teckning – en bild som återges med texttecken
ASCII-versionen av bilden visas till höger.

Programfilerna för det här exemplet finns på www.adobe.com/go/learn_programmingAS3samples_flash_se. Programfilerna för ASCIIArt finns i mappen Samples/AsciiArt. Programmet består av följande filer:

Fil

Beskrivning

AsciiArtApp.mxml

eller

AsciiArtApp.fla

Huvudprogramfilen i Flash (FLA) eller Flex (MXML)

com/example/programmingas3/asciiArt/AsciiArtBuilder.as

Klassen som innehåller programmets huvudfunktioner, inklusive extrahering av bildmetadata från en textfil, inläsning av bilder och hantering av konverteringen för bild-till-text.

com/example/programmingas3/asciiArt/BitmapToAsciiConverter.as

En klass som innehåller metoden parseBitmapData() för konvertering av bilddata till en strängversion.

com/example/programmingas3/asciiArt/Image.as

En klass som motsvarar en inläst bitmappsbild.

com/example/programmingas3/asciiArt/ImageInfo.as

En klass som representerar metadata för en ASCII-bild (t.ex. rubrik och bildfilens URL-adress).

bild/

En mapp med bilder som används av programmet.

txt/ImageData.txt

En tabbavgränsad textfil som innehåller information om bilderna som ska läsas in av programmet.

Extrahera tabbavgränsade värden

I detta exempel används en vanlig rutin där programdata lagras separat från själva programmet. På så vis behövs inte SWF-filen återskapas när data ändras (t.ex. en ytterligare bild läggs till eller en bildrubrik ändras). I det här fallet lagras alla bildmetadata (bl.a. bildrubriken, bildfilens URL-adress och värden som används vid ändring av bilden) i en textfil; txt/ImageData.txt i projektet. Textfilen innehåller följande:

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

I filen används ett specifikt tabbavgränsat format. Den första raden är en rubrikrad. Övriga rader innehåller följande data för respektive bitmapp som ska läsas in:

  • Filnamnet på bitmappen.

  • Visningsnamnet på bitmappen.

  • De vita och svarta tröskelvärdena för bitmapparna. Detta är hexvärdena över respektive under vilka en pixel betraktas som helt vit eller helt svart.

När programmet startas görs en inläsning och tolkning av textfilens innehåll via klassen AsciiArtBuilder. På så vis skapas bildstacken som ska visas med hjälp av följande kod från metoden parseImageInfo() i klassen AsciiArtBuilder:

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) 
    { 
        // Create a new image info record and add it to the array of image info. 
        var imageInfo:ImageInfo = new ImageInfo(); 
 
        // Split the current line into values (separated by tab (\t) 
        // characters) and extract the individual properties: 
        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); 
    } 
}

Hela textfilens innehåll finns i en enda String-instans, egenskapen _imageInfoLoader.data. Med hjälp av metoden split() med radmatningstecknet ("\n") som parameter så delas String-instansen upp i en array (lines) vars element utgörs av respektive rad i textfilen. Därefter används en slinga för att bearbeta varje rad (förutom den första som består av enbart rubriker). I den här slingan används metoden split() en gång till för att dela upp innehållet i en enstaka rad till en uppsättning med värden (Array-objektet imageProperties). I det här fallet används tabbtecknet ("\t") som parameter för metoden split(), eftersom värdena i varje rad markeras med tabbtecken.

Normalisera bildrubriker med hjälp av String-metoder

En av de utseendemässiga frågorna i detta program är att alla bildrubriker visas i ett standardformat, där alla ord inleds med versal bokstav. Programmet formaterar rubrikraderna allteftersom de extraheras från textfilen, i stället för att anta att textfilen innehåller korrekt formaterade rubriker.

I föregående kodexempel används följande rad vid extrahering av enstaka bildmetadatavärden:

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

I den koden skickas textfilens bildrubrik via metoden normalizeTitle() innan den sparas i objektet 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(" "); 
}

I metoden delas rubriken upp till separata ord (avgränsade med blanksteg) med metoden split(). Sedan överförs varje ord igenom metoden capitalizeFirstLetter() och slutligen sätts orden samman igen till en sträng med metoden join() från klassen Array.

Med hjälp av metoden capitalizeFirstLetter() ändras varje ord och ges en inledande versal:

    /** 
     * Capitalizes the first letter of a single word, unless it's one of 
     * a set of words that are normally not capitalized in English. 
     */ 
    private function capitalizeFirstLetter(word:String):String 
    { 
        switch (word) 
        { 
            case "and": 
            case "the": 
            case "in": 
            case "an": 
            case "or": 
            case "at": 
            case "of": 
            case "a": 
                // Don't do anything to these words. 
                break; 
            default: 
                // For any other word, capitalize the first character. 
                var firstLetter:String = word.substr(0, 1); 
                firstLetter = firstLetter.toUpperCase(); 
                var otherLetters:String = word.substring(1); 
                word = firstLetter + otherLetters; 
        } 
        return word; 
    }

I det engelska språket kommer inte följande ord att få en inledande versal: ”and”, ”the”, ”in”, ”an”, ”or”, ”at”, ”of” eller ”a”. (Detta är en förenklad version av reglerna.) För att tillämpa den här logiken används först en switch-sats som kontrollerar om ordet är något av de ord som inte ska ha inledande versal. Om detta stämmer hoppar koden ur switch-satsen. Men om ordet ska ges inledande versal görs detta i flera steg:

  1. Den första bokstaven i ordet extraheras med substr(0, 1), vilket ger en delsträng som börjar med tecknet på index 0 (anges med den första parametern 0). Delsträngen blir ett tecken långt (anges med den andra parametern 1).

  2. Detta tecken ändras till en versal med metoden toUpperCase().

  3. Återstående tecken i det ursprungliga ordet extraheras med substring(1), som extraherar en delsträng med början på index 1 (den andra bokstaven) till slutet på strängen (anges med att den andra parametern i metoden substring() utelämnas).

  4. Ordet skapas slutligen genom att den nya versala första bokstaven kombineras med återstående bokstäver med hjälp av strängsammanfogning: firstLetter + otherLetters.

Generera ASCII-bildkonst

Klassen BitmapToAsciiConverter innehåller funktionen som konverterar en bitmappsbild till textrepresentationen i ASCII-tecken. Den här processen utförs med metoden parseBitmapData(), som till en del visas här:

    var result:String = ""; 
     
    // Loop through the rows of pixels top to bottom: 
    for (var y:uint = 0; y < _data.height; y += verticalResolution) 
    { 
        // Within each row, loop through pixels left to right: 
        for (var x:uint = 0; x < _data.width; x += horizontalResolution) 
        { 
            ... 
 
            // Convert the gray value in the 0-255 range to a value 
            // in the 0-64 range (since that's the number of "shades of 
            // gray" in the set of available characters): 
            index = Math.floor(grayVal / 4); 
            result += palette.charAt(index); 
        } 
        result += "\n"; 
    } 
    return result;

Med den här koden definieras först stränginstansen result som används för att skapa ASCII-versionen av bitmappsbilden. Därefter körs de enskilda pixlarna i källans bitmappsbild i en programslinga. Med hjälp av flera färgändringstekniker konverteras de röda, gröna och blå färgvärdena i en enskild pixel till ett värde i gråskala (en siffra från 0 till 255). Sedan divideras värdet med 4 (se koden) för att konvertera det till ett värde i skalan 0–63, som lagras i variabeln index. (Skalan 0–63 används för att ”paletten” med ASCII-tecken som används av programmet innehåller 64 värden.) Den här teckenpaletten definieras som en stränginstans i klassen BitmapToAsciiConverter:

// The characters are in order from darkest to lightest, so that their 
// position (index) in the string corresponds to a relative color value 
// (0 = black). 
private static const palette:String = "@#$%&8BMW*mwqpdbkhaoQ0OZXYUJCLtfjzxnuvcr[]{}1()|/?Il!i><+_~-;,. ";

Eftersom variabeln index anger vilket ASCII-tecken i paletten som motsvarar aktuell pixel i bitmappsbilden så hämtas detta tecken från strängen palette via metoden charAt(). Därefter läggs det till i strängen result med hjälp av operatorn för sammanfogning (+=). Vid slutet av varje pixelrad sammanfogas dessutom ett radmatningstecken i slutet av strängen result, vilket ger en radbrytning för att skapa nästa rad med ”bildtecken”.