Introduction
The print studio wants the same postcard in a new direction and at a larger pixel size. You will trace where one pixel moves, implement clockwise rotation, then add integer enlargement without blending colors.
This lab starts independently with its own artwork and source files. You need the grid, palette, P3 and terminal skills taught in the preceding lab. A small supplied command-line reader handles option parsing; you implement the coordinate functions. Keep the supplied artwork and option reader unchanged. Practice time is an author estimate.
Rotate a Rectangular Postcard Clockwise
In this step, you will turn a five-by-three image into a three-by-five image without changing its colors.
Open Workspace. This lab has its own freshly prepared /home/labex/project/postcards; it does not use files from the previous lab. base.c is the complete original-orientation example. First compile and run that normal example:
cd /home/labex/project/postcards
gcc -std=c11 -Wall -Wextra base.c -o base
./base > original.ppm
Switch to Postcard Preview, click Refresh files, choose original.ppm, then Open postcard. Its 5 × 3 status and colored rows establish the starting orientation. Return to Workspace and read the new framework:
cat art.h
cat card.c
Keep art.h, base.c, and cli.h unchanged; edit card.c. The supplied main accepts two arguments: ROTATE is exactly 0 (original) or 1 (clockwise), and SCALE is an integer from 1 through 4. The support in cli.h stores them in the integer variables rotation and scale. parse_options returns true only for supported arguments; otherwise main returns status 2 before writing an image. The argc and argv declarations belong to that supplied C entry point; you do not need to implement pointer-based parsing here.
A clockwise turn exchanges width and height. Trace the source top-left pixel (0,0): it moves to the top-right of the turned image, (HEIGHT - 1,0). The output loop asks the reverse question: for destination (x,y), which source pixel should it read? Without enlargement, the answer is source_x = y and source_y = HEIGHT - 1 - x. Subtracting from HEIGHT - 1 reverses the source rows. Using WIDTH - 1 here would be wrong for a rectangle.
Each coordinate function receives x and y as integer parameters and returns one integer index. When rotation is zero, the functions return the unchanged coordinates. The provided rotation ? HEIGHT : WIDTH expression means “choose HEIGHT if rotation is nonzero, otherwise WIDTH.”
Replace card.c with this complete first version. As in the first lab, the quoted here-document copies the C source literally through the final EOF line. This version handles scale 1; the next step adds scale 2 through 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
Compile it. Run original orientation and clockwise rotation with scale 1; each output gets a distinct name:
gcc -std=c11 -Wall -Wextra card.c -o card
./card 0 1 > original.ppm
./card 1 1 > rotated.ppm
Inspect the rotated file:
python3 /home/labex/.local/share/pixel-postcards/inspect.py rotated.ppm
3 x 5 pixels; 15 RGB triples; max value 255
Switch to Postcard Preview, click Refresh files, select rotated.ppm, and click Open postcard. The old left dark strip should now be the top dark row. Dimensions alone do not prove rotation: compare the colored pixels too. If the result looks mirrored, revisit the HEIGHT - 1 - x reversal, then compile and generate the file again.
Enlarge Each Pixel and Reject Invalid Options
In this step, you will enlarge the rotated card by copying each source pixel into an equal-sized block.
Start in Workspace. For scale 2, destination columns 0 and 1 both read column 0; columns 2 and 3 both read column 1. Integer division gives exactly that grouping: 0 / 2 and 1 / 2 both produce 0. Dividing destination coordinates by scale first reverses enlargement. Then the same reverse-rotation formula chooses the source location. This produces a clockwise-rotated image enlarged by a positive integer, with no mixed or interpolated colors.
For the turned case, return y / scale from source_x and HEIGHT - 1 - x / scale from source_y. For the original case, return x / scale and y / scale. The output sizes are already multiplied by scale in the supplied main loop. Replace the file with the complete version:
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
Compile, refresh the original and rotated outputs from this executable, and create the rotated two-times enlargement:
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
The original 15 pixels each became a 2-by-2 block: 60 pixels in total. Switch to Postcard Preview, refresh files, select enlarged.ppm, then Open postcard. It has the same direction and proportions as rotated.ppm. The on-screen preview scales both to fit, so read the dimension label to distinguish actual file sizes. Download the enlarged file using Download PPM.
The screenshot shows the clockwise view with 6 × 10 saved pixels. The dark strip is now the top row; the image keeps the same proportions as the rotated file.
![]()
Return to Workspace. A scale of zero would make no useful image. The next command is an intentional failure. Use a new file name because > opens and truncates a file before the executable can reject its options. echo $? immediately prints the preceding command's exit status; 0 means success, and 2 is this program's argument error.
./card 1 0 > rejected.ppm
echo $?
You should see Scale must be an integer from 1 to 4 and then 2. Check the rejected file with the taught decoder:
python3 /home/labex/.local/share/pixel-postcards/inspect.py rejected.ppm
This command also intentionally fails with Cannot read postcard: Expected a P3 header. There are no pixels to decode. It is different from a missing file: the shell created an empty file before the C program ran. In Postcard Preview, refresh files and open rejected.ppm to see the error rather than a previous image. Open enlarged.ppm again to recover the normal view. The three good output files remain intact.
Keep card.c, executable card, supplied art.h and cli.h, plus original.ppm, rotated.ppm, and enlarged.ppm. The generator must handle both rotation values and every scale from 1 to 4 for supplied patterns up to 16 columns by 16 rows; it must not hardcode this image's dimensions or pixel colors. Equivalent loops and coordinate expressions are welcome. Invalid arguments must fail with an error message and no image header. The supplied parser already supplies that behavior.
Summary
You traced a pixel through a clockwise turn, reversed enlargement with integer division, and combined those mappings in two C functions. You produced original, rotated and enlarged images and distinguished argument rejection from malformed or missing files.


