Ejemplo de cadenas: Arte ASCII

Flash Player 9 y posterior, Adobe AIR 1.0 y posterior

El ejemplo ASCII Art muestra diversas características del trabajo con la clase String en ActionScript 3.0, como las siguientes:

  • Se utiliza el método split() de la clase String para extraer valores de una cadena delimitada por caracteres (información de imagen en un archivo de texto delimitado por tabulaciones).

  • Se utilizan varias técnicas de manipulación de cadenas, como split() , la concatenación y la extracción de una parte de la cadena mediante substring() y substr() para convertir a mayúsculas la primera letra de cada palabra de los títulos de imagen.

  • El método getCharAt() se utiliza para obtener un solo carácter de una cadena (a fin de determinar el carácter ASCII correspondiente a un valor de mapa de bits de escala de grises).

  • Se utiliza la concatenación de cadenas para generar carácter a carácter la representación ASCII Art de una imagen.

El término ASCII Art (arte ASCII) designa representaciones textuales de una imagen, en las que representa la imagen en una cuadrícula de caracteres con fuente de espacio fijo, como los caracteres Courier New. La imagen siguiente muestra un ejemplo de arte ASCII producido por la aplicación:

ASCII Art: una imagen representada con caracteres de texto
La versión de arte ASCII del gráfico se muestra a la derecha

Para obtener los archivos de la aplicación de este ejemplo, consulte www.adobe.com/go/learn_programmingAS3samples_flash_es . Los archivos de la aplicación ASCIIArt se encuentran en la carpeta Samples/AsciiArt. La aplicación consta de los siguientes archivos:

Archivo

Descripción

AsciiArtApp.mxml

o

AsciiArtApp.fla

El archivo de aplicación principal en Flash (FLA) o Flex (MXML)

com/example/programmingas3/asciiArt/AsciiArtBuilder.as

La clase que proporciona la funcionalidad principal de la aplicación, incluidas la extracción de metadatos de imagen de un archivo de texto, la carga de imágenes y la administración del proceso de conversión de imagen a texto.

com/example/programmingas3/asciiArt/BitmapToAsciiConverter.as

Una clase que proporciona el método parseBitmapData() para convertir datos de imagen en una versión de tipo String.

com/example/programmingas3/asciiArt/Image.as

Una clase que representa una imagen de mapa de bits cargada.

com/example/programmingas3/asciiArt/ImageInfo.as

Una clase que representa metadatos de una imagen de arte ASCII (como el título, el URL del archivo de imagen, etc.).

image/

Una carpeta que contiene imágenes utilizadas por la aplicación.

txt/ImageData.txt

Un archivo de texto delimitado por tabulaciones, que contiene información sobre las imágenes que la aplicación debe cargar.

Extracción de valores delimitados por tabulaciones

En este ejemplo se sigue la práctica común de almacenar datos de aplicación por separado de la aplicación en sí; de esta manera, si los datos cambian (por ejemplo, si se añade otra imagen o cambia el título de una imagen), no es necesario volver a generar el archivo SWF. En este caso, los metadatos de imagen, incluidos el título de la imagen, el URL del archivo de imagen real y algunos valores que se utilizan para manipular la imagen, se almacenan en un archivo de texto (el archivo txt/ImageData.txt file del proyecto). A continuación se describe el contenido del archivo de texto:

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

El archivo utiliza un formato delimitado por tabulaciones específico. La primera línea (fila) es una fila de encabezado. Las restantes líneas contienen los siguientes datos para cada mapa de bits que se va a cargar:

  • El nombre de archivo del mapa de bits.

  • El nombre para mostrar del mapa de bits.

  • Los valores de los umbrales blanco y negro para los mapas de bits. Son valores hexadecimales por encima o por debajo de los cuales, un píxel se considerará completamente blanco o completamente negro.

En cuanto se inicia la aplicación, la clase AsciiArtBuilder carga y analiza el contenido del archivo de texto para crear la "pila" de imágenes que se mostrará, utilizando el código siguiente del método parseImageInfo() de la clase 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); 
    } 
}

Todo el contenido del archivo de texto está en una sola instancia de String, la propiedad _imageInfoLoader.data . Si se utiliza el método split() con el carácter de nueva línea ( "\n" ) como parámetro, la instancia de String se divide en un conjunto ( lines ) cuyos elementos son las líneas individuales del archivo de texto. A continuación, el código utiliza un bucle para trabajar con cada una de las líneas (salvo la primera, ya que contiene solo encabezados, no contenido real). Dentro del bucle, se vuelve a utilizar el método split() para dividir el contenido de la línea individual en un conjunto de valores (el objeto Array denominado imageProperties ). El parámetro utilizado con el método split() en este caso es el carácter de tabulación ( "\t" ), ya que los valores de cada línea están delimitados por tabulaciones.

Uso de métodos String para normalizar títulos de imágenes

Una de las decisiones de diseño para esta aplicación es que todos los títulos de imágenes deben mostrarse con un formato estándar, con la primera letra de cada palabra convertida a mayúsculas (con la excepción de unas pocas palabras que no se suelen escribir en mayúsculas en los títulos en inglés). En lugar de suponer que el archivo de texto contiene títulos con el formato correcto, la aplicación aplica el formato a los títulos cuando los extrae del archivo de texto.

En el listado de código anterior se utiliza la siguiente línea de código como parte de la extracción de valores de metadatos de imagen individuales:

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

En ese código, se aplica al título de imagen del archivo de texto el método normalizeTitle() antes de almacenarlo en el objeto 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(" "); 
}

Este método utiliza el método split() para dividir el título en palabras individuales (separadas por el carácter espacio), aplica a cada palabra el método capitalizeFirstLetter() y después utiliza el método join() de la clase Array para volver a combinar las palabras en una sola cadena.

Como indica su nombre, el método capitalizeFirstLetter() convierte a mayúscula la primera letra de cada palabra:

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

En inglés, el carácter inicial de cada una de las palabras de un título no va en mayúsculas si es alguna de estas palabras: “and”, “the”, “in”, “an”, “or”, “at”, “of”, o “a”. (Esta es una versión simplificada de las reglas.) Para ejecutar esta lógica, el código utiliza primero una sentencia switch para comprobar si la palabra es una de las palabras que no se deben escribir en mayúsculas. Si es así, el código simplemente sale de la sentencia switch . Por otra parte, si hubiera que escribir la palabra en mayúsculas, se hará en varios pasos, como se indica a continuación:

  1. La primera letra de la palabra se extrae mediante substr(0, 1) , que extrae una subcadena que empieza por el carácter correspondiente al índice 0 (la primera letra de la cadena, como indica el primer parámetro 0 ). La subcadena tendrá una longitud de un carácter (como indica el segundo parámetro, 1 ).

  2. El carácter se convierte a mayúscula mediante el método toUpperCase() .

  3. Los caracteres restantes de la palabra original se extraen mediante substring(1) , que extrae una subcadena que empieza en el índice 1 (la segunda letra) hasta el final de la cadena (lo que se indica omitiendo el segundo parámetro del método substring() ).

  4. La última palabra se crea combinando la primera letra que se acaba de convertir en mayúscula con las restantes letras mediante concatenación de cadenas: firstLetter + otherLetters .

Creación de texto de arte ASCII

La clase BitmapToAsciiConverter proporciona la funcionalidad de convertir una imagen de mapa de bits en su representación de texto ASCII. Este proceso, que se muestra aquí parcialmente, lo lleva a cabo el método parseBitmapData() :

    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;

Este código define primero una instancia de String denominada result que se utilizará para generar la versión de arte ASCII de la imagen de mapa de bits. A continuación, recorre píxeles individuales de la imagen de mapa de bits original. Utiliza varias técnicas de manipulación de colores (que se omiten aquí por brevedad), convierte los valores de los colores rojo, verde y azul de un píxel individual en un solo valor de escala de grises (un número entre 0 y 255). A continuación el código divide ese valor por 4 (como se indica) para convertirlo en un valor en la escala 0-63, que se almacena en la variable index . (Se utiliza la escala 0-63 porque la "paleta" de caracteres ASCII disponibles que utiliza esta aplicación contiene 64 valores.) La paleta de caracteres se define como una instancia de String en la clase 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><+_~-;,. ";

Como la variable index define el carácter ASCII de la paleta que corresponde al píxel actual de la imagen de mapa de bits, ese carácter se recupera de la instancia String de la paleta mediante el método charAt() . A continuación se añade result a la instancia de tipo String mediante el operador de concatenación y asignación ( += ). Además, al final de cada fila de píxeles, un carácter de nueva línea se concatena con el final de la cadena result , lo que fuerza un ajuste de línea para crear una nueva fila de "píxeles" de caracteres.