Gira y amplía la postal

CBeginner
Practicar Ahora

Introducción

El taller quiere la misma postal con otra orientación y un tamaño mayor en píxeles. Seguirás el recorrido de un píxel, implementarás el giro horario y añadirás una ampliación entera sin mezclar colores.

Este laboratorio comienza de forma independiente con sus propios dibujos y archivos fuente. Necesitas los conocimientos de cuadrículas, paletas, P3 y terminal del laboratorio anterior. Un lector de comandos proporcionado procesa las opciones; tú implementas las funciones de coordenadas. Conserva intactos el dibujo y el lector. El tiempo de práctica es una estimación del autor.

Gira una postal rectangular en sentido horario

En este paso convertirás una imagen de cinco por tres en otra de tres por cinco sin cambiar sus colores.

Abre Workspace. Este laboratorio tiene su propio /home/labex/project/postcards recién preparado; no utiliza archivos del laboratorio anterior. base.c es el ejemplo completo con la orientación original. Primero compila y ejecuta ese ejemplo normal:

cd /home/labex/project/postcards
gcc -std=c11 -Wall -Wextra base.c -o base
./base > original.ppm

Cambia a Postcard Preview, pulsa Refresh files, elige original.ppm y pulsa Open postcard. El estado 5 × 3 y las filas de colores establecen la orientación inicial. Vuelve a Workspace y lee el nuevo esqueleto:

cat art.h
cat card.c

Conserva art.h, base.c y cli.h sin cambios; edita card.c. El main proporcionado acepta dos argumentos: ROTATE es exactamente 0 (original) o 1 (horario), y SCALE es un entero de 1 a 4. El código de cli.h los guarda en las variables enteras rotation y scale. parse_options solo devuelve verdadero para argumentos admitidos; en otro caso, main devuelve el estado 2 antes de escribir una imagen. Las declaraciones argc y argv forman parte del punto de entrada C proporcionado; no tienes que implementar aquí un analizador basado en punteros.

Un giro horario intercambia anchura y altura. Sigue el píxel superior izquierdo de origen (0,0): se mueve a la esquina superior derecha de la imagen girada, (HEIGHT - 1,0). El bucle de salida pregunta lo contrario: para el destino (x,y), ¿qué píxel de origen debe leer? Sin ampliación, la respuesta es source_x = y y source_y = HEIGHT - 1 - x. Restar de HEIGHT - 1 invierte las filas de origen. Usar WIDTH - 1 aquí sería incorrecto para un rectángulo.

Cada función recibe x e y como parámetros enteros y devuelve un índice entero. Cuando rotation vale cero, devuelve las coordenadas sin cambiar. La expresión proporcionada rotation ? HEIGHT : WIDTH significa «elige HEIGHT si rotation no es cero; de lo contrario, elige WIDTH».

Reemplaza card.c con esta primera versión completa. Igual que en el primer laboratorio, el here-document con delimitador entre comillas copia literalmente el código C hasta la última línea EOF. Esta versión admite el factor 1; el siguiente paso añadirá los factores del 2 al 4.

cat > card.c <<'EOF'
#include <stdio.h>
#include "art.h"
#include "cli.h"

int source_x(int x, int y) {
    if (rotation) return y;
    return x;
}
int source_y(int x, int y) {
    if (rotation) return HEIGHT - 1 - x;
    return y;
}
int main(int argc, char **argv) {
    if (!parse_options(argc, argv)) return 2;
    int out_width = (rotation ? HEIGHT : WIDTH) * scale;
    int out_height = (rotation ? WIDTH : HEIGHT) * scale;
    printf("P3\n%d %d\n255\n", out_width, out_height);
    for (int y = 0; y < out_height; y++) {
        for (int x = 0; x < out_width; x++) {
            int c = pattern[source_y(x, y)][source_x(x, y)] - '0';
            printf("%d %d %d\n", palette[c][0], palette[c][1], palette[c][2]);
        }
    }
    return 0;
}
EOF

Compílala. Ejecuta la orientación original y el giro horario con factor 1, cada uno con un nombre de archivo distinto:

gcc -std=c11 -Wall -Wextra card.c -o card
./card 0 1 > original.ppm
./card 1 1 > rotated.ppm

Examina el archivo girado:

python3 /home/labex/.local/share/pixel-postcards/inspect.py rotated.ppm
3 x 5 pixels; 15 RGB triples; max value 255

Cambia a Postcard Preview, pulsa Refresh files, selecciona rotated.ppm y pulsa Open postcard. La franja oscura izquierda anterior debe ser ahora la fila superior. Las dimensiones por sí solas no demuestran el giro: compara también los píxeles de colores. Si parece una imagen reflejada, revisa la inversión HEIGHT - 1 - x, recompila y vuelve a generar el archivo.

Amplía cada píxel y rechaza opciones no válidas

En este paso ampliarás la postal girada copiando cada píxel de origen en un bloque del mismo tamaño.

Empieza en Workspace. Con factor 2, las columnas de destino 0 y 1 leen ambas la columna 0; las columnas 2 y 3 leen la columna 1. La división entera produce exactamente esa agrupación: 0 / 2 y 1 / 2 valen 0. Dividir primero las coordenadas de destino entre scale deshace la ampliación. Después, la misma fórmula de giro inverso elige la posición de origen. Así se obtiene una imagen girada en sentido horario y ampliada por un entero positivo, sin mezclar ni interpolar colores.

En el caso girado, devuelve y / scale desde source_x y HEIGHT - 1 - x / scale desde source_y. En el caso original, devuelve x / scale e y / scale. El bucle principal proporcionado ya multiplica las dimensiones de salida por scale. Reemplaza el archivo con la versión completa:

cat > card.c <<'EOF'
#include <stdio.h>
#include "art.h"
#include "cli.h"

int source_x(int x, int y) {
    if (rotation) return y / scale;
    return x / scale;
}
int source_y(int x, int y) {
    if (rotation) return HEIGHT - 1 - x / scale;
    return y / scale;
}
int main(int argc, char **argv) {
    if (!parse_options(argc, argv)) return 2;
    int out_width = (rotation ? HEIGHT : WIDTH) * scale;
    int out_height = (rotation ? WIDTH : HEIGHT) * scale;
    printf("P3\n%d %d\n255\n", out_width, out_height);
    for (int y = 0; y < out_height; y++) {
        for (int x = 0; x < out_width; x++) {
            int c = pattern[source_y(x, y)][source_x(x, y)] - '0';
            printf("%d %d %d\n", palette[c][0], palette[c][1], palette[c][2]);
        }
    }
    return 0;
}
EOF

Compila, actualiza los archivos original y girado con este ejecutable y crea la ampliación por dos del giro:

gcc -std=c11 -Wall -Wextra card.c -o card
./card 0 1 > original.ppm
./card 1 1 > rotated.ppm
./card 1 2 > enlarged.ppm
python3 /home/labex/.local/share/pixel-postcards/inspect.py enlarged.ppm
6 x 10 pixels; 60 RGB triples; max value 255

Los 15 píxeles originales se convierten, cada uno, en un bloque de 2 por 2: 60 píxeles en total. Cambia a Postcard Preview, actualiza los archivos, elige enlarged.ppm y pulsa Open postcard. Debe conservar la dirección y las proporciones de rotated.ppm. El visor ajusta ambos a la pantalla, por lo que debes leer la etiqueta de dimensiones para distinguir sus tamaños reales. Descarga el archivo ampliado con Download PPM.

La captura muestra la vista girada en sentido horario con 6 × 10 píxeles guardados. La franja oscura está ahora arriba; la imagen conserva las proporciones del archivo girado.

La postal girada y ampliada en Postcard Preview

Vuelve a Workspace. Un factor cero no produce una imagen útil. El siguiente comando es un fallo intencional. Usa un nombre nuevo porque > abre y trunca el archivo antes de que el ejecutable pueda rechazar las opciones. echo $? imprime inmediatamente el estado del comando anterior: 0 indica éxito y 2 es el error de argumentos de este programa.

./card 1 0 > rejected.ppm
echo $?

Debes ver Scale must be an integer from 1 to 4 y después 2. Examina el archivo rechazado con el decodificador ya aprendido:

python3 /home/labex/.local/share/pixel-postcards/inspect.py rejected.ppm

Este comando también falla intencionalmente con Cannot read postcard: Expected a P3 header. No hay píxeles que decodificar. Es distinto de un archivo ausente: el shell creó un archivo vacío antes de ejecutar el programa C. En Postcard Preview, actualiza la lista y abre rejected.ppm para ver el error en lugar de la imagen anterior. Abre de nuevo enlarged.ppm para recuperar la vista normal. Los tres archivos correctos siguen intactos.

Conserva card.c, el ejecutable card, los archivos proporcionados art.h y cli.h, y original.ppm, rotated.ppm y enlarged.ppm. El generador debe admitir ambos valores de rotación y todos los factores del 1 al 4 para patrones proporcionados de hasta 16 columnas por 16 filas; no debe fijar las dimensiones ni los colores de esta imagen en el código. Se aceptan bucles y expresiones equivalentes. Los argumentos no válidos deben fallar con un mensaje y sin escribir una cabecera. El analizador proporcionado ya ofrece ese comportamiento.

Resumen

Has seguido un píxel durante un giro horario, deshecho la ampliación con división entera y combinado ambas correspondencias en dos funciones C. Has creado imágenes originales, giradas y ampliadas, y distinguido el rechazo de argumentos de los archivos mal formados o ausentes.