Przykład wykorzystania ciągów: pseudografika ASCII

Flash Player 9 i nowsze wersje, Adobe AIR 1.0 i nowsze wersje

Przykład „Pseudografika ASCII” ilustruje szereg mechanizmów pracy klasą String w języku ActionScript 3.0, a w szczególności:

  • Wyodrębnianie wartości z ciągu znaków zawierającego ograniczniki (informacje o obrazie w pliku tekstowym ze znakami tabulacji ) za pomocą metody split() .

  • Kilka technik manipulowania ciągami znaków, takich jak operacja split() , konkatenacja i wyodrębnianie fragmentu ciągu za pomocą metod substring() i substr() ; techniki te zostały wykorzystane do zamiany pierwszych liter wszystkich słów na wielkie litery.

  • Zastosowanie metody getCharAt() do pobrania pojedynczego znaku z ciągu znaków (w celu określenia znaku ASCII odpowiadającego poziomowi szarości w bitmapie).

  • Konkatenacja ciągów znaków w celu zbudowania pseudografiki ASCII na podstawie obrazu. Pseudografika budowana jest znak po znaku.

Termin pseudografika ASCII oznacza tekstową reprezentację obrazu, w której obraz jest rysowany za pomocą znaków należących do czcionki nieproporcjonalnej, takiej jak Courier New. Na poniższej ilustracji przedstawiono przykładową pseudografikę ASCII wygenerowaną przez aplikację:

Grafika ASCII — obraz zrenderowany ze znakami tekstowymi
Wersja obrazu w postaci pseudografiki ASCII jest widoczna po prawej stronie.

Aby pobrać pliki tej przykładowej aplikacji, należy przejść na stronę www.adobe.com/go/learn_programmingAS3samples_flash_pl . Pliki aplikacji ASCIIArt znajdują się w folderze Samples/AsciiArt. Aplikacja składa się z następujących plików:

File

Opis

AsciiArtApp.mxml

lub

AsciiArtApp.fla

Główny plik aplikacji w formacie Flash (FLA) lub Flex (MXML).

com/example/programmingas3/asciiArt/AsciiArtBuilder.as

Klasa realizująca zasadniczą funkcjonalność aplikacji, w tym wyodrębnianie metadanych obrazu z pliku tekstowego, ładowanie obrazów i zarządzanie procesem konwersji obrazu na tekst.

com/example/programmingas3/asciiArt/BitmapToAsciiConverter.as

Klasa udostępniająca metodę parseBitmapData() służącą do konwersji danych obrazu na dane typu String.

com/example/programmingas3/asciiArt/Image.as

Klasa reprezentująca załadowaną bitmapę.

com/example/programmingas3/asciiArt/ImageInfo.as

Klasa reprezentująca metadane pseudografiki ASCII (np. tytuł, adres URL pliku itd.).

image/

Folder zawierający obrazy używane przez aplikację.

txt/ImageData.txt

Plik tekstowy rozdzielany znakami tabulacji, zawierający informacje o obrazach, które mają być załadowane do aplikacji.

Wyodrębnianie wartości rozdzielonych znakami tabulacji

W omawianym przykładzie zastosowano typową technikę przechowywania danych aplikacji w oddzielnym pliku, niezależnym od samej aplikacji; dzięki temu zmiany danych (np. dodanie nowego obrazu lub zmiana tytułu obrazu) nie wiąże się z koniecznością zrekonstruowania pliku SWF. W tym przypadku metadane obrazów, w tym tytuły, adresy URL właściwych plików z obrazami oraz niektóre wartości potrzebne do manipulowania obrazami są przechowywane w pliku tekstowym (pliku txt/ImageData.txt w projekcie). Oto zawartość tego pliku tekstowego:

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

Plik zawiera dane rozdzielane znakami tabulacji. Pierwszy wiersz jest wierszem nagłówkowym. Pozostałe wiersze zawierają następujące dane poszczególnych bitmap przeznaczonych do załadowania:

  • nazwę pliku bitmapy;

  • wyświetlaną nazwę bitmapy;

  • próg bieli i próg czerni dla bitmapy. Są to wartości szesnastkowe, powyżej i poniżej których piksel jest uznawany za całkowicie biały albo całkowicie czarny.

Od razu po uruchomieniu aplikacji klasa AsciiArtBuilder ładuje i analizuje treść pliku tekstowego w celu utworzenia „stosu” obrazów do wyświetlanie. Oto fragment kodu z metody parseImageInfo() w klasie 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); 
    } 
}

Cała treść pliku tekstowego jest zawarta w jednej instancji klasy String — we właściwości _imageInfoLoader.data . Za pomocą metody split() wywołanej ze znakiem nowego wiersza ( "\n" ) jako parametrem instancja klasy String jest dzielona na wiersze wpisywane do tablicy lines . Elementy tej tablicy odpowiadają poszczególnym wierszom pliku tekstowego. Następnie kod w pętli przetwarza kolejne wiersze (z wyjątkiem pierwszego, bo zawiera on nagłówki, a nie właściwą treść). Wewnątrz pętli ponownie wywoływana jest metoda split() , tym razem w celu podzielenia każdego wiersza na zestaw wartości (obiekt Array o nazwie imageProperties ). Parametrem metody split() jest w tym przypadku znak tabulacji ( "\t" ), ponieważ wartości w każdym wierszu są rozdzielone właśnie znakami tabulacji.

Użycie metod klasy String do normalizacji tytułów obrazów

Jednym z założeń poczynionych przy projektowaniu tej aplikacji było ujednolicenie formatu tytułów obrazów. Pierwsza litera każdego wyrazu w tytule powinna być zapisana wielką literą (z wyłączeniem kilku słów, które w tytułach angielskich nie są nigdy zapisywane wielkimi literami). Ponieważ nie ma pewności, czy plik tekstowy będzie zawierał prawidłowo sformatowane tytuły, aplikacja formatuje je w miarę wyodrębniania z pliku tekstowego.

W poprzednim fragmencie kodu, w procesie wyodrębniania wartości metadanych obrazów, występuje poniższy wiersz:

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

Tytuł obrazu pochodzący z pliku tekstowego jest przetwarzany przez metodę normalizeTitle() , a dopiero potem zapisywany w obiekcie 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(" "); 
}

W metodzie tej zastosowano metodę split() w celu podzielenia tytułu na poszczególne słowa (rozdzielone spacjami); każde słowo przetwarzane jest przez metodę capitalizeFirstLetter() , a następnie używana jest metoda join() klasy Array w celu ponownego połączenia słów w jeden ciąg znaków.

Zgodnie ze swoją nazwą, metoda capitalizeFirstLetter() zamienia pierwszą literę wyrazu na wielką:

    /** 
     * 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; 
    }

W języku angielskim istnieją słowa, które w tytułach nie są nigdy zapisywane z wielkiej litery: „and,” „the”, „in”, „an”, „or”, „at”, „of”, oraz „a”. (Przyjęto uproszczone reguły). Aby zrealizować tę logikę, zastosowano instrukcję switch , która sprawdza, czy wyraz należy do zbioru słów wykluczonych z zapisu wielką literą. Jeśli należy, to następuje wyjście z instrukcji switch . Jeśli jednak wyraz powinien rozpoczynać się od wielkiej litery, proces zamiany odbywa się w następujących etapach:

  1. Pierwsza litera wyrazu jest wyodrębniana za pomocą wywołania substr(0, 1) , które zwraca podciąg rozpoczynający się od znaku o indeksie 0 (pierwsza litera ciągu, ponieważ pierwszy parametr jest równy 0 ). Podciąg będzie miał długość jednego znaku (drugi parametr równy jeden 1 ).

  2. Znak ten jest zamieniany na wielką literę za pomocą metody toUpperCase() .

  3. Pozostałe znaki oryginalnego wyrazu są wyodrębniane za pomocą wywołania substring(1) , które zwraca podciąg rozpoczynający się od indeksu 1 (drugiej listery) aż do końca ciągu znaków (ponieważ pominięto drugi parametr metody substring() ).

  4. Wynikowe słowo jest generowane w wyniku połączenia pierwszej litery zamienionej na wielką z pozostałymi literami, przy użyciu operacji konkatenacji: firstLetter + otherLetters .

Generowanie tekstu pseudografiki ASCII

Klasa BitmapToAsciiConverter zawiera funkcje konwertujące obraz bitmapowy na jego reprezentację tekstową ASCII. Ten proces realizuje metoda parseBitmapData() , której fragment przedstawiono poniżej:

    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;

Najpierw definiowana jest instancja klasy String o nazwie result , która posłuży do zbudowania pseudograficznej wersji bitmapy ze znaków ASCII. Następnie analizowane są w pętli poszczególne piksele źródłowego obrazu bitmapowego. Korzystając z kilku technik manipulacji kolorami (których omówienie tutaj pominiemy) program zamienia wartości czerwieni, zieleni i koloru niebieskiego przypisane do danego piksela na jedną wartość w skali szarości (liczbę z przedziału od 0 do 255). Następnie wartość ta jest dzielona przez 4 (tak jak to przedstawiono) w celu przeliczenia jej na skalę 0-63. Wynik zapisywany jest w zmiennej index . (Skala 0-63 odzwierciedla „paletę” 64 znaków ASCII używanych przez aplikację). Paleta znaków jest zdefiniowana jako instancja klasy String w klasie 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><+_~-;,. ";

Ponieważ zmienna index określa, który znak ASCII w palecie odpowiada wartości bieżącego piksela w bitmapie, możliwe jest pobranie znaku z ciągu palette za pomocą metody charAt() . Następnie znak jest dopisywany do instancji klasy String o nazwie result za pomocą operatora konkatenacji z przypisaniem ( += ). Dodatkowo na końcu każdego rzędu pikseli do ciągu result dopisywany jest znak nowego wiersza, co powoduje zawinięcie tekstu i utworzenie nowego wiersza „pikseli” znakowych.