Поверните и увеличьте открытку

CBeginner
Практиковаться сейчас

Введение

Мастерская хочет ту же открытку в другом направлении и с большим числом пикселей. Вы проследите перемещение одного пикселя, реализуете поворот по часовой стрелке, затем добавите целочисленное увеличение без смешивания цветов.

Эта лабораторная начинается независимо, со своим рисунком и исходниками. Нужны знания сеток, палитры, P3 и терминала из предыдущей лабораторной. Небольшой предоставленный обработчик читает параметры командной строки, а функции координат реализуете вы. Не меняйте предоставленные рисунок и обработчик. Продолжительность практики оценена автором.

Поверните прямоугольную открытку по часовой стрелке

На этом шаге вы превратите изображение пять на три в изображение три на пять, не меняя цвета.

Откройте Workspace. Здесь подготовлен собственный новый /home/labex/project/postcards; файлы предыдущей лабораторной не используются. base.c — полный пример для исходной ориентации. Сначала скомпилируйте и запустите этот рабочий пример:

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

Перейдите в Postcard Preview, нажмите Refresh files, выберите original.ppm, затем Open postcard. Состояние 5 × 3 и цветные строки задают исходную ориентацию. Вернитесь в Workspace и прочитайте новый каркас:

cat art.h
cat card.c

Не меняйте art.h, base.c и cli.h; редактируйте card.c. Предоставленный main принимает два аргумента: ROTATE равен строго 0 (исходная ориентация) или 1 (по часовой стрелке), а SCALE — целое число от 1 до 4. Код в cli.h записывает их в целочисленные переменные rotation и scale. parse_options возвращает истину только для поддерживаемых аргументов; иначе main возвращает статус 2 до записи изображения. Объявления argc и argv относятся к предоставленной точке входа C; реализовывать здесь разбор аргументов с указателями не требуется.

Поворот по часовой стрелке меняет местами ширину и высоту. Проследите исходный верхний левый пиксель (0,0): он перемещается в верхний правый угол повёрнутого изображения, (HEIGHT - 1,0). Цикл вывода задаёт обратный вопрос: какой исходный пиксель читать для позиции назначения (x,y)? Без увеличения ответ — source_x = y и source_y = HEIGHT - 1 - x. Вычитая столбец назначения из HEIGHT - 1, вы обращаете порядок исходных строк. Использование WIDTH - 1 здесь неверно для прямоугольника.

Каждая функция координат принимает целочисленные параметры x и y и возвращает один целочисленный индекс. Когда rotation равен нулю, функции возвращают неизменённые координаты. Выражение rotation ? HEIGHT : WIDTH означает: «выбрать HEIGHT, если rotation не равен нулю, иначе WIDTH».

Замените card.c следующей полной первой версией. Как и в первой лабораторной, here-document с разделителем в кавычках буквально копирует исходник C до последней строки EOF. Эта версия работает с масштабом 1; следующий шаг добавит масштабы 2–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

Скомпилируйте её. Получите исходную ориентацию и поворот по часовой стрелке с масштабом 1, используя отдельное имя для каждого результата:

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

Проверьте повёрнутый файл:

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

Перейдите в Postcard Preview, нажмите Refresh files, выберите rotated.ppm, затем Open postcard. Прежняя тёмная полоса слева теперь должна стать верхней строкой. Одних размеров недостаточно для доказательства поворота: сравните и цветные пиксели. Если результат похож на отражение, проверьте обращение HEIGHT - 1 - x, затем перекомпилируйте и создайте файл снова.

Увеличьте каждый пиксель и отклоните неверные параметры

На этом шаге вы увеличите повёрнутую открытку, копируя каждый исходный пиксель в блок одинакового размера.

Начните в Workspace. При масштабе 2 столбцы назначения 0 и 1 оба читают исходный столбец 0; столбцы 2 и 3 читают столбец 1. Целочисленное деление даёт именно такую группировку: 0 / 2 и 1 / 2 оба равны 0. Сначала разделите координаты назначения на scale, чтобы отменить увеличение. Затем та же формула обратного поворота выбирает исходную позицию. Получается изображение, повёрнутое по часовой стрелке и увеличенное на положительный целочисленный коэффициент, без смешивания и интерполяции цветов.

Для повёрнутого варианта source_x возвращает y / scale, а source_y — HEIGHT - 1 - x / scale. Для исходного варианта возвращаются x / scale и y / scale. Предоставленный основной цикл уже умножает выходные размеры на scale. Замените файл полной версией:

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

Скомпилируйте, обновите исходное и повёрнутое изображения этим исполняемым файлом и создайте двукратное увеличение повёрнутой открытки:

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

Каждый из 15 исходных пикселей превратился в блок два на два: всего 60 пикселей. Переключитесь в Postcard Preview, обновите список, выберите enlarged.ppm, затем Open postcard. Направление и пропорции должны совпадать с rotated.ppm. Просмотрщик подгоняет оба изображения под экран, поэтому реальные размеры файлов различайте по подписи размеров. Скачайте увеличенный файл через Download PPM.

Снимок показывает поворот по часовой стрелке с сохранёнными 6 × 10 пикселями. Тёмная полоса теперь находится сверху; пропорции совпадают с повёрнутым файлом.

Повёрнутая и увеличенная открытка в Postcard Preview

Вернитесь в Workspace. Нулевой масштаб не создаёт полезного изображения. Следующая команда — намеренная ошибка. Используйте новое имя, поскольку > открывает файл и обрезает его до нулевой длины до того, как исполняемая программа сможет отклонить параметры. Следующий сразу за командой echo $? выводит её статус завершения: 0 означает успех, а 2 — ошибку аргументов этой программы.

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

Вы должны увидеть Scale must be an integer from 1 to 4, затем 2. Проверьте отклонённый файл уже изученным декодером:

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

Эта команда тоже намеренно завершается ошибкой Cannot read postcard: Expected a P3 header. Декодировать нечего. Это отличается от отсутствующего файла: оболочка создала пустой файл ещё до запуска C-программы. В Postcard Preview обновите список и откройте rejected.ppm, чтобы увидеть ошибку, а не прежнее изображение. Снова откройте enlarged.ppm для восстановления нормального просмотра. Три правильных выходных файла остаются целыми.

Сохраните card.c, исполняемый card, предоставленные art.h и cli.h, а также original.ppm, rotated.ppm и enlarged.ppm. Генератор должен обрабатывать оба значения поворота и все масштабы от 1 до 4 для предоставленных рисунков до 16 столбцов на 16 строк; нельзя жёстко прописывать размеры или цвета этой картинки. Допустимы эквивалентные циклы и выражения координат. Неверные аргументы должны приводить к ошибке с сообщением и без заголовка изображения. Предоставленный обработчик уже обеспечивает это поведение.

Резюме

Вы проследили пиксель при повороте по часовой стрелке, отменили увеличение целочисленным делением и объединили эти преобразования в двух функциях C. Вы получили исходные, повёрнутые и увеличенные изображения и отличили отклонение аргументов от повреждённого или отсутствующего файла.