Introduction
L’atelier souhaite la même carte dans une nouvelle direction et avec davantage de pixels. Vous suivrez le déplacement d’un pixel, réaliserez une rotation horaire, puis ajouterez un agrandissement entier sans mélanger les couleurs.
Ce laboratoire démarre indépendamment avec son propre motif et ses propres sources. Vous devez connaître les grilles, palettes, fichiers P3 et commandes de terminal du laboratoire précédent. Un petit lecteur d’options fourni analyse les arguments ; vous implémentez les fonctions de coordonnées. Conservez le motif et le lecteur d’options sans modification. La durée de pratique est une estimation de l’auteur.
Tourner une carte rectangulaire dans le sens horaire
Dans cette étape, vous transformerez une image de cinq pixels sur trois en une image de trois sur cinq, sans changer ses couleurs.
Ouvrez Workspace. Ce laboratoire possède son propre /home/labex/project/postcards nouvellement préparé ; il n’utilise aucun fichier du laboratoire précédent. base.c est l’exemple complet dans la direction d’origine. Commencez par compiler et exécuter cet exemple fonctionnel :
cd /home/labex/project/postcards
gcc -std=c11 -Wall -Wextra base.c -o base
./base > original.ppm
Passez à Postcard Preview, cliquez sur Refresh files, choisissez original.ppm, puis Open postcard. L’état 5 × 3 et les lignes colorées établissent l’orientation initiale. Revenez à Workspace et lisez le nouveau squelette :
cat art.h
cat card.c
Laissez art.h, base.c et cli.h inchangés ; modifiez card.c. Le main fourni accepte deux arguments : ROTATE vaut exactement 0 (origine) ou 1 (sens horaire), et SCALE est un entier de 1 à 4. Le support de cli.h les stocke dans les variables entières rotation et scale. parse_options ne renvoie vrai que pour des arguments pris en charge ; sinon, main renvoie l’état 2 avant d’écrire une image. Les déclarations argc et argv appartiennent au point d’entrée C fourni ; vous n’avez pas à implémenter ici une analyse fondée sur les pointeurs.
Une rotation horaire échange largeur et hauteur. Suivez le pixel supérieur gauche de la source (0,0) : il arrive en haut à droite de l’image tournée, en (HEIGHT - 1,0). La boucle de sortie pose la question inverse : pour la destination (x,y), quel pixel source faut-il lire ? Sans agrandissement, la réponse est source_x = y et source_y = HEIGHT - 1 - x. Soustraire à HEIGHT - 1 inverse les lignes sources. Utiliser WIDTH - 1 ici serait incorrect pour un rectangle.
Chaque fonction reçoit x et y comme paramètres entiers et renvoie un indice entier. Quand rotation vaut zéro, les fonctions renvoient les coordonnées inchangées. L’expression fournie rotation ? HEIGHT : WIDTH signifie « choisir HEIGHT si rotation est non nul, sinon WIDTH ».
Remplacez card.c par cette première version complète. Comme dans le premier laboratoire, le here-document avec délimiteur entre apostrophes copie littéralement le C jusqu’à la dernière ligne EOF. Cette version gère le facteur 1 ; l’étape suivante ajoutera les facteurs 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
Compilez. Exécutez la direction d’origine et la rotation horaire avec un facteur de 1, en donnant à chaque sortie un nom distinct :
gcc -std=c11 -Wall -Wextra card.c -o card
./card 0 1 > original.ppm
./card 1 1 > rotated.ppm
Inspectez le fichier tourné :
python3 /home/labex/.local/share/pixel-postcards/inspect.py rotated.ppm
3 x 5 pixels; 15 RGB triples; max value 255
Passez à Postcard Preview, cliquez sur Refresh files, sélectionnez rotated.ppm, puis Open postcard. L’ancienne bande sombre de gauche doit maintenant former la ligne du haut. Les dimensions seules ne prouvent pas la rotation : comparez aussi les pixels colorés. Si l’image semble réfléchie, revérifiez l’inversion HEIGHT - 1 - x, puis compilez et générez à nouveau le fichier.
Agrandir chaque pixel et refuser les options invalides
Dans cette étape, vous agrandirez la carte tournée en copiant chaque pixel source dans un bloc de taille identique.
Commencez dans Workspace. Avec un facteur de 2, les colonnes de destination 0 et 1 lisent toutes deux la colonne 0 ; les colonnes 2 et 3 lisent la colonne 1. La division entière donne précisément ce regroupement : 0 / 2 et 1 / 2 valent tous deux 0. Diviser d’abord les coordonnées de destination par scale annule l’agrandissement. La même formule de rotation inverse choisit ensuite la position source. On obtient une image tournée dans le sens horaire puis agrandie par un entier positif, sans mélange ni interpolation des couleurs.
Dans le cas tourné, renvoyez y / scale depuis source_x et HEIGHT - 1 - x / scale depuis source_y. Dans le cas d’origine, renvoyez x / scale et y / scale. La boucle principale fournie multiplie déjà les dimensions de sortie par scale. Remplacez le fichier par la version complète :
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
Compilez, actualisez les sorties d’origine et tournée avec cet exécutable, puis créez l’agrandissement par deux de la carte tournée :
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
Chacun des 15 pixels d’origine devient un bloc de 2 sur 2 : 60 pixels au total. Passez à Postcard Preview, actualisez la liste, sélectionnez enlarged.ppm, puis Open postcard. L’image garde la direction et les proportions de rotated.ppm. L’aperçu ajuste les deux à l’écran ; lisez donc l’étiquette de dimensions pour distinguer la taille réelle des fichiers. Téléchargez le fichier agrandi avec Download PPM.
La capture montre la vue horaire avec 6 × 10 pixels enregistrés. La bande sombre forme désormais la ligne du haut ; l’image garde les proportions du fichier tourné.
![]()
Revenez à Workspace. Un facteur zéro ne produit aucune image utile. La commande suivante est un échec intentionnel. Utilisez un nouveau nom, car > ouvre et tronque le fichier avant que l’exécutable puisse refuser ses options. echo $? affiche immédiatement l’état de la commande précédente : 0 indique une réussite, et 2 l’erreur d’arguments de ce programme.
./card 1 0 > rejected.ppm
echo $?
Vous devez voir Scale must be an integer from 1 to 4, puis 2. Inspectez le fichier refusé avec le décodeur déjà enseigné :
python3 /home/labex/.local/share/pixel-postcards/inspect.py rejected.ppm
Cette commande échoue elle aussi volontairement avec Cannot read postcard: Expected a P3 header. Aucun pixel ne peut être décodé. Ce n’est pas un fichier manquant : le shell a créé un fichier vide avant d’exécuter le programme C. Dans Postcard Preview, actualisez les fichiers et ouvrez rejected.ppm pour voir l’erreur plutôt que l’image précédente. Rouvrez enlarged.ppm pour retrouver la vue normale. Les trois bonnes sorties restent intactes.
Conservez card.c, l’exécutable card, les fichiers fournis art.h et cli.h, ainsi que original.ppm, rotated.ppm et enlarged.ppm. Le générateur doit gérer les deux valeurs de rotation et tous les facteurs de 1 à 4 pour les motifs fournis allant jusqu’à 16 colonnes sur 16 lignes ; il ne doit pas coder en dur les dimensions ou couleurs de cette image. Des boucles et expressions de coordonnées équivalentes sont acceptées. Les arguments invalides doivent échouer avec un message et sans en-tête d’image. L’analyseur fourni assure déjà ce comportement.
Résumé
Vous avez suivi un pixel lors d’une rotation horaire, annulé l’agrandissement par division entière et combiné ces transformations dans deux fonctions C. Vous avez produit des images d’origine, tournées et agrandies, et distingué le refus d’arguments d’un fichier mal formé ou manquant.

