Visits heute: 41
Aktuelle Seite: Segmentation Fault: Der Algorithmus der Gier - Part 3
Das ist die Krönung von Elaras bisherigem Wissen. Sie kombiniert alles: Bildskalierung, Aspect Ratio, ANSI-Farben und Terminal-Steuerung. Ein Bildbetrachter für die Shell (oft „ASCII-Art“-Viewer genannt, obwohl wir hier echte Farben nutzen) ist ein echtes Hacker-Tool.
Der Clou: Da ein Terminal-Zeichen meistens doppelt so hoch wie breit ist, nutzen wir den Unicode-Block <upper half block> (Upper Half Block \u2580). Ein Terminal-Zeichen kann so zwei vertikale "Pixel" gleichzeitig darstellen (Vordergrundfarbe für oben, Hintergrundfarbe für unten). So erhalten wir ein perfektes 1:1 quadratisches Pixel-Layout trotz rechteckiger Schrift!
Kapitel 23: Das Auge des Terminals (TrueColor Image Viewer)
Die Story: Elara muss Beweisfotos auf einem Server sichten, der keine grafische Oberfläche hat. Sie schreibt view, ein Programm, das Bilder direkt in die SSH-Session rendert. Sie nutzt den "Halbblock-Trick", um die Auflösung zu verdoppeln. Als das erste farbechte Foto in der schwarzen Konsole erscheint, fühlt es sich an, als hätte sie ein Fenster in eine andere Welt geöffnet.
Das Programm: shellview.c
Wir nutzen wieder stb_image.h. Lade sie herunter und lege sie in den Ordner. Dieses Programm unterstützt JPEG, PNG, BMP, GIF und mehr "out of the box".
// Compile: gcc shellview.c -lm -o shellview
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
void print_pixel(uint8_t* top, uint8_t* bottom) {
if (bottom) {
// Vordergrund (oben) und Hintergrund (unten) setzen
printf("\033[38;2;%d;%d;%dm\033[48;2;%d;%d;%dm\u2580",
top[0], top[1], top[2],
bottom[0], bottom[1], bottom[2]);
} else {
// Nur ein Pixel (für ungerade Höhen)
printf("\033[38;2;%d;%d;%dm\u2580", top[0], top[1], top[2]);
}
}
int main(int argc, char** argv) {
if (argc < 2) {
printf("Usage: %s <image_file>\n", argv[0]);
return 1;
}
// 1. Terminal-Größe ermitteln
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
int term_w = w.ws_col;
int term_h = (w.ws_row - 1) * 2; // *2 wegen Halbblock-Trick
// 2. Bild laden
int img_w, img_h, channels;
unsigned char* data = stbi_load(argv[1], &img_w, &img_h, &channels, 3);
if (!data) {
fprintf(stderr, "Fehler beim Laden.\n");
return 1;
}
// 3. Aspect Ratio Scaler (Keep Aspect)
float scale = (float)term_w / img_w;
if ((float)term_h / img_h < scale) scale = (float)term_h / img_h;
int target_w = (int)(img_w * scale);
int target_h = (int)(img_h * scale);
// 4. Rendering mit Halbblock-Trick
// Wir springen immer 2 vertikale Pixel pro Terminal-Zeile
for (int y = 0; y < target_h; y += 2) {
for (int x = 0; x < target_w; x++) {
// Nearest Neighbor Sampling
int src_x = (int)(x / scale);
int src_y_top = (int)(y / scale);
int src_y_bot = (int)((y + 1) / scale);
uint8_t* p_top = &data[(src_y_top * img_w + src_x) * 3];
uint8_t* p_bot = (y + 1 < target_h) ? &data[(src_y_bot * img_w + src_x) * 3] : NULL;
print_pixel(p_top, p_bot);
}
printf("\033[0m\n"); // Reset am Zeilenende
}
stbi_image_free(data);
return 0;
}
Die Technik hinter dem "Shell-Pixel"
Die Magie liegt in der ANSI-Sequenz und dem Unicode-Zeichen:
\033[38;2;R;G;Bmsetzt die Vordergrundfarbe (Textfarbe).\033[48;2;R;G;Bmsetzt die Hintergrundfarbe.- Das Zeichen
upper half blockfüllt genau die obere Hälfte des Zeichens mit der Textfarbe aus. Die untere Hälfte zeigt automatisch die Hintergrundfarbe.
Dadurch wird ein einzelnes Terminal-Zeichen (das 1:2 rechteckig ist) zu zwei quadratischen 1:1 Pixeln.
Elaras Meisterschaft
Das Bild ist gestochen scharf (für Terminal-Verhältnisse) und die Proportionen stimmen. Thorne ist beeindruckt, aber er hat einen letzten Trumpf: Er schickt Elara ein animiertes GIF. Ihr aktuelles Programm zeigt nur den ersten Frame.
Soll Elara im nächsten Kapitel lernen, wie sie GIF-Animationen in der Shell abspielt, indem sie die verschiedenen "Frames" extrahiert und mit ihrem nanosleep-Timing synchronisiert?