anwenden:images
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
— | anwenden:images [2020-07-26 17:20] (aktuell) – angelegt - Externe Bearbeitung 127.0.0.1 | ||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
+ | ====== Image : Grafik-Programmierung ohne Grafik-Treiber ====== | ||
+ | |||
+ | > Du sollst dir kein Bildnis noch irgendein Gleichnis machen, | ||
+ | > weder des, das oben im Himmel, | ||
+ | > noch des, das unten auf Erden, | ||
+ | > oder des, das im Wasser unter der Erde ist. | ||
+ | >> | ||
+ | |||
+ | |||
+ | Wir verstoßen ständig gegen das zweite Gebot. | ||
+ | Schlachten wir eine weitere heilige Kuh: | ||
+ | " | ||
+ | Dazu braucht man schnelle Rechner, | ||
+ | teure Grafikkarten und Programmierbibliotheken | ||
+ | mit hohem Einarbeitungsaufwand (OpenGL, DirectX, SDL, ...)." | ||
+ | Nichts da. Es geht nicht um 3D-Ego-Shooter. | ||
+ | Es geht nicht um: | ||
+ | "Mein Grafikprozessorkühler ist 2 cm länger als deiner." | ||
+ | Servergenerierte Aktiencharts brauchen keine Framerate. | ||
+ | |||
+ | Vor dem Jahr 1990 habe ich 9-Nadel-Drucker punktweise angesteuert | ||
+ | im Epson-Escape(P)-Befehlssatz, | ||
+ | weil ich computerberechnete Ergebnisse in Bildform ausdrucken wollte. | ||
+ | Grafikkarte jenseits von Basic-Kleincomputern? | ||
+ | Man braucht einen geordneten Zugriff auf Bildpunktdaten im Hauptspeicher | ||
+ | und einen Speicher, auf dem Bilder abzulegen sind. Alles weitere ist Routine. | ||
+ | Und es gibt eine Menge dabei zu lernen. | ||
+ | Willkommen im Bereich der Retroprogrammierung. | ||
+ | |||
+ | ===== Aufgabe ===== | ||
+ | Erstelle die Grundlagen der Computergrafik. | ||
+ | Gehe in folgenden Schritten vor: | ||
+ | |||
+ | |||
+ | - Definiere 32-Bit-Farben (je 8 Bit für rot, grün, blau, Transparenz). | ||
+ | - Bilder beliebiger Größe sind als Klasse zu vereinbaren. | ||
+ | - Speichere das Bild als Datei (z.B. BMP, einfaches Datenformat). | ||
+ | Der Zugriff auf die Farben einzelner Bildpunkte soll einfach und robust sein. | ||
+ | |||
+ | ===== Lösung ===== | ||
+ | |||
+ | |||
+ | |||
+ | ==== Klasse Color ==== | ||
+ | Die vier Farbinformationen werden in einer Klasse gebündelt. | ||
+ | |||
+ | <code cpp color.h> | ||
+ | //: color.h : Grafik-Bibliothek ohne Grafiktreiber - R.Richter 2011-01-15 | ||
+ | ///////////////////////////////////////////////////////////////////////// | ||
+ | |||
+ | #ifndef COLOR_H | ||
+ | #define COLOR_H | ||
+ | |||
+ | // Farben, 24 bit Farbtiefe mit Transparenz (Alpha-Kanal) | ||
+ | |||
+ | class Color | ||
+ | { | ||
+ | public: | ||
+ | typedef unsigned char Channel; | ||
+ | typedef unsigned long RGBAlpha; | ||
+ | |||
+ | enum | ||
+ | { | ||
+ | NONE = 0xFF000000, | ||
+ | BLACK = 0x00000000, | ||
+ | WHITE = 0x00FFFFFF, | ||
+ | RED = 0x00FF0000, | ||
+ | GREEN = 0x0000FF00, | ||
+ | BLUE = 0x000000FF, | ||
+ | YELLOW = 0x00FFFF00 | ||
+ | }; | ||
+ | |||
+ | Color(Channel red, Channel green, Channel blue, Channel alpha = 0) | ||
+ | : value( rgba(red, green, blue, alpha ) ) | ||
+ | { | ||
+ | } | ||
+ | |||
+ | Color(RGBAlpha color = 0xff000000) // default: transparent | ||
+ | : value(color) | ||
+ | { | ||
+ | } | ||
+ | |||
+ | Channel alpha() const { return Channel(value>> | ||
+ | Channel red() const { return Channel(value>> | ||
+ | Channel green() const { return Channel(value>> | ||
+ | Channel blue() | ||
+ | |||
+ | operator unsigned long() const { return value; } | ||
+ | private: | ||
+ | RGBAlpha value; | ||
+ | |||
+ | static RGBAlpha rgba(Channel red, Channel green, Channel blue, Channel alpha) | ||
+ | { | ||
+ | return | ||
+ | | RGBAlpha(green)<< | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | #endif // COLOR_H | ||
+ | //~ | ||
+ | </ | ||
+ | Intern werden die Daten zu einer Langzahl zusammengefasst. | ||
+ | Für den einfachen Umgang werden einige Standardfarben festgelegt. | ||
+ | Die Farbklasse ist unvollständig: | ||
+ | |||
+ | |||
+ | * Welche weiteren Farben sind als Standardfarben zu definieren? | ||
+ | * Welche Rechengesetze gelten für Farben ? | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ==== Klasse Image ==== | ||
+ | Ein Bild hat eine bestimmte Breite und eine bestimmte Länge, | ||
+ | die nachfolgend nicht mehr änderbar ist. | ||
+ | Solange noch kein Bildpunkt bemalt wurde, ist das Bild durchsichtig, | ||
+ | es sei denn, eine Hintergrundfarbe wurde festgelegt. | ||
+ | Einzelne Bildpunkte können als '' | ||
+ | und auf eine neue Farbe gesetzt werden. | ||
+ | Der Zugriff auf Punkte außerhalb des Bildes bleibt folgenlos. | ||
+ | |||
+ | <code cpp image.h> | ||
+ | //: image.h : Grafik-Bibliothek ohne Grafiktreiber - R.Richter 2011-01-15 | ||
+ | ///////////////////////////////////////////////////////////////////////// | ||
+ | |||
+ | #ifndef IMAGE_H | ||
+ | #define IMAGE_H | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include " | ||
+ | |||
+ | class Image | ||
+ | { | ||
+ | public: | ||
+ | Image(unsigned int width, unsigned int height, Color background = 0) | ||
+ | : width_(width), | ||
+ | { | ||
+ | } | ||
+ | |||
+ | unsigned int width() | ||
+ | unsigned int height() const { return height_; } | ||
+ | |||
+ | Color& pixel(unsigned int x, unsigned int y) | ||
+ | { | ||
+ | return x> | ||
+ | } | ||
+ | | ||
+ | Color const& pixel(unsigned int x, unsigned int y) const | ||
+ | { | ||
+ | return x> | ||
+ | } | ||
+ | private: | ||
+ | unsigned int width_, height_; | ||
+ | std:: | ||
+ | |||
+ | static Color& outside() { static Color c; return c = Color:: | ||
+ | }; | ||
+ | |||
+ | // ===[ Speichern als BMP 24bit unkomprimiert, | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | // Achtung, nicht portabel: Intel Byte-order vorausgesetzt | ||
+ | |||
+ | inline | ||
+ | void saveBMP(std:: | ||
+ | { | ||
+ | unsigned long const linesize | ||
+ | unsigned long const fillbytes = (4-linesize%4)%4; | ||
+ | unsigned long Image constsize = (linesize+fillbytes)*image.height(); | ||
+ | |||
+ | // byte alignment, no padding | ||
+ | // #if defined( _MSC_VER ) || defined( __MINGW32__ ) | ||
+ | #pragma pack(1) | ||
+ | // #endif | ||
+ | struct BMPheader | ||
+ | { | ||
+ | // file info | ||
+ | char ID[2]; | ||
+ | unsigned long filesize, reserved, offset; | ||
+ | |||
+ | // BMP info | ||
+ | unsigned long infosize, width, height; | ||
+ | unsigned short planes, bitsperpixel; | ||
+ | unsigned long compression, | ||
+ | | ||
+ | | ||
+ | } header = | ||
+ | { | ||
+ | // file info | ||
+ | { ' | ||
+ | unsigned(sizeof(header)) + imagesize, // 24 bits per pixel | ||
+ | 0, sizeof(header), | ||
+ | |||
+ | // BMP info | ||
+ | 40, image.width(), | ||
+ | 1, 24, | ||
+ | 0, imagesize, | ||
+ | 1000, 1000, | ||
+ | 0, 0 | ||
+ | }; | ||
+ | // #if defined( _MSC_VER ) || defined( __MINGW32__ ) | ||
+ | #pragma pack() | ||
+ | // #endif | ||
+ | |||
+ | std:: | ||
+ | file.write( (char*)& | ||
+ | |||
+ | for (unsigned y = 0; y < image.height(); | ||
+ | { | ||
+ | for (unsigned x = 0; x < image.width(); | ||
+ | { | ||
+ | Color c= image.pixel(x, | ||
+ | unsigned char b = c.blue(), g = c.green(), r = c.red(); | ||
+ | file.write( (char*) &b, 1); | ||
+ | file.write( (char*) &g, 1); | ||
+ | file.write( (char*) &r, 1); | ||
+ | } | ||
+ | if (fillbytes) | ||
+ | { | ||
+ | int zero = 0; | ||
+ | file.write( (char*) &zero, fillbytes); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | #endif // IMAGE_H | ||
+ | //~ | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ===== Dateiformat ===== | ||
+ | Das BMP-Format wurde hier gewählt, weil es | ||
+ | einen relativ einfachen Aufbau hat (ohne Kompression) und | ||
+ | 24-Bit-Farben erlaubt. Jedoch geht die Transparenz-Infornation verloren. | ||
+ | Für das PNG-Format (mit Transparenz) gibt es einen | ||
+ | [[http:// | ||
+ | welcher die Bytefolge ABGR statt | ||
+ | der in SDL, HTML und hier verwendeten Folge (A)RGB benutzt. | ||
+ | Andere Dateiformate (Targa, GIF, JPEG, ...) | ||
+ | sind weitergehende Übungsaufgaben. Quellen: Günter Born, Dateiformate. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ===== Testprogramm ===== | ||
+ | Das Hauptprogramm zeigt, wie einfach der Umgang mit Bildern ist. | ||
+ | Der Inhalt eines Bildes kann als Zahlenkolonne (plain text) | ||
+ | oder als Bitmap-Datei gespeichert werden: | ||
+ | |||
+ | <code cpp grafik.cpp> | ||
+ | //: grafik.cpp : Grafik-Bibliothek ohne Grafiktreiber - R.Richter 2011-01-15 | ||
+ | //////////////////////////////////////////////////////////////////////////// | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include " | ||
+ | |||
+ | void print(std:: | ||
+ | { | ||
+ | out << " | ||
+ | << image.width() << " x " << image.height() << ' | ||
+ | |||
+ | for (unsigned y = 0; y < image.height(); | ||
+ | { | ||
+ | for (unsigned x = 0; x < image.width(); | ||
+ | { | ||
+ | out << std:: | ||
+ | } | ||
+ | out << ' | ||
+ | } | ||
+ | } | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | Color c(255, 0, 0, 127); // red, 50% transparency | ||
+ | Image image(100, 75); | ||
+ | |||
+ | for (unsigned x = 0; x < image.height(); | ||
+ | { | ||
+ | image.pixel(x, | ||
+ | image.pixel(x, | ||
+ | image.pixel(x, | ||
+ | } | ||
+ | image.pixel(-1, | ||
+ | |||
+ | std:: | ||
+ | print(file, image); | ||
+ | saveBMP(" | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | //~ | ||
+ | </ | ||
+ | Das Testprogramm erzeugt eine Bilddatei mit | ||
+ | |||
+ | |||
+ | * aufsteigender roter Linie | ||
+ | * waagerechter blauer Linie und | ||
+ | * fallender grüner Linie: | ||
+ | |||
+ | {{anwenden: | ||
+ | |||
+ | Weitergehende Übungen führen in verschiedene Richtungen: | ||
+ | |||
+ | |||
+ | * 2D-Grafik (Linien, Balken, Polygone, Kurven, Farbverläufe), | ||
+ | * Bildbearbeitung (Überlagern, | ||
+ | * 3D-Grafik (Projektion, | ||
+ | * Simulation und Visualisierung (2D, 3D), | ||
+ | * Bild-Datei-Konversion (Kompressionsalgorithmen). | ||
+ | Viel Spaß! | ||
+ | |||