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ę:
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:
-
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
).
-
Znak ten jest zamieniany na wielką literę za pomocą metody
toUpperCase()
.
-
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()
).
-
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.
|
|
|