Grafik in C (PI4): Unterschied zwischen den Versionen
KKeine Bearbeitungszusammenfassung |
KKeine Bearbeitungszusammenfassung |
||
| Zeile 13: | Zeile 13: | ||
== Auflösungen == | == Auflösungen == | ||
Der Raspberry Pi | Der Raspberry Pi unterstützt viele verschiedene Auflösungen. Eine Übersicht über die gängigsten Auflösungen findest du beispielsweise unter https://de.wikipedia.org/wiki/Bildaufl%C3%B6sung#Computer. Hier ein paar Auflösungen, die verwendet werden könnten: | ||
{| class="wikitable" | {| class="wikitable" | ||
| Zeile 26: | Zeile 26: | ||
| HD || 1920 x 1080 | | HD || 1920 x 1080 | ||
|- | |- | ||
| UHD (only Pi 4) || 4096 x 2160 | | UHD (only Pi 4 or Pi 5) || 4096 x 2160 | ||
|} | |} | ||
Die Auflösung | Die Auflösung allein reicht jedoch nicht aus, um komplexe, farbige Bilder darzustellen. Da Computersysteme nur binäre Zahlen kennen, also nur "1" oder "0", könnte man jedes Pixel lediglich ein- oder ausschalten, was nur ein Schwarz-Weiß-Bild erzeugen würde. Um die Vielzahl der Farben, die wir auf Bildschirmen sehen, darzustellen, werden zusätzliche Standards definiert. | ||
Wird die Anzeige mehrmals hintereinander vertieft, entsteht eine größere Farbtiefe. Wenn ein Bild beispielsweise zwei Bitmaps verwendet, stehen für jeden Punkt zwei Bits zur Verfügung. Das ergibt vier Zustände bzw. vier Farben. Je mehr Bitmaps verwendet werden, desto mehr Farben können angezeigt werden. | |||
== Farbtiefe == | == Farbtiefe == | ||
Der Raspberry Pi unterstützt vier verschiedene Farbtiefen: 8-Bit, 16-Bit, 24-Bit und 32-Bit. In der Praxis wird jedoch von der Verwendung der 8-Bit und 24-Bit Farbtiefe abgeraten, da sie zu Darstellungsfehlern führen können. Siehe hierzu auch die Dokumentation unter https://www.raspberrypi.org/documentation/configuration/config-txt/video.md im Abschnitt framebuffer_depth. Um es einfach zu halten, konzentrieren wir uns auf die 32-Bit Farbtiefe. | |||
Beispiel: Speicheraufwand für Bilder | |||
Um den Speicheraufwand zu veranschaulichen, betrachten wir ein Beispiel. Ein monochromes Bild mit einer Auflösung von 20 x 20 Pixel benötigt folgenden Speicherbedarf: | |||
<syntaxhighlight> | <syntaxhighlight> | ||
| Zeile 45: | Zeile 46: | ||
[[Datei:Koordinaten2.png|rand|400x400px]] | [[Datei:Koordinaten2.png|rand|400x400px]] | ||
Für ein Bild mit 4 Bitmaps beträgt der Speicherbedarf: | |||
<syntaxhighlight> | <syntaxhighlight> | ||
| Zeile 53: | Zeile 54: | ||
[[Datei:Koordinaten3.png|rand|500x500px]] | [[Datei:Koordinaten3.png|rand|500x500px]] | ||
Nun ein Beispiel mit einer HD-Auflösung und RGBA32-Farbtiefe: | |||
<syntaxhighlight> | <syntaxhighlight> | ||
((1920 * 1080) * 32) / 8 = 8294400 Bytes | Speicherbedarf (Bytes) = ((1920 * 1080) * 32) / 8 = 8294400 Bytes | ||
Das entspricht etwa 8,3 Megabytes. | |||
</syntaxhighlight> | </syntaxhighlight> | ||
== Abbildung der Bitmaps im Speicher == | == Abbildung der Bitmaps im Speicher == | ||
Bitmaps werden nicht als eine Reihe von Einzelbildern (Bitmaps) im Speicher abgelegt, sondern die Grafikdaten sind wie in einem dreidimensionalen Raum organisiert. Betrachten wir ein Beispiel mit einer Auflösung von 20 x 20 Pixel und einer Farbtiefe von 16 Bit: | |||
Jeder Bildschirmpunkt wird bei einer Farbtiefe von 16 Bit durch 16 aufeinanderfolgende Bits dargestellt. Der erste Punkt verwendet die Bits 0-15, der nächste die Bits 16-31, und so weiter. Das müssen wir bei den Berechnungen für jeden einzelnen Punkt berücksichtigen. | |||
[[Datei:Koordinaten4.png|rand|600x600px]] | [[Datei:Koordinaten4.png|rand|600x600px]] | ||
Version vom 6. März 2025, 12:39 Uhr
Die bisherigen Programme auf dem Raspberry Pi waren vergleichsweise einfach und ermöglichten lediglich die Steuerung einer LED. Der Raspberry Pi kann jedoch weitaus mehr, insbesondere im Bereich der Grafik, da er über einen HDMI-Ausgang verfügt, der für fortgeschrittene Anwendungen genutzt werden kann. In diesem Abschnitt wollen wir den HDMI-Ausgang des Raspberry Pi verwenden, um grafische Ergebnisse direkt anzuzeigen.
Zu Beginn schließen wir einen Monitor an den Raspberry Pi an, um die Ergebnisse direkt auf dem Bildschirm sehen zu können. Da wir Bare Metal programmieren, müssen wir sämtliche Routinen selbst schreiben, da der Raspberry Pi keine vorinstallierten Weisungen hat, wie er Bilder auf dem Monitor anzeigt.
Der Raspberry Pi 4 besitzt zwei HDMI-Ausgänge, die jeweils Bildschirme mit bis zu UHD-Auflösung (4096 x 2160 Pixel) bei 60 Hz unterstützen. Bei Verwendung beider HDMI-Ports beträgt die Bildwiederholrate maximal 30 Hz. Ältere Raspberry Pi-Modelle unterstützen Bildschirme bis zu einer Auflösung von 1920 x 1200 Pixel bei 60 Hz.
Interessanterweise ist es die GPU des Raspberry Pi, die den Startvorgang initiiert. Sie lädt die Firmware in den Speicher und übergibt danach die Kontrolle an die CPU. Dies ist während des Startvorgangs deutlich sichtbar, wenn ein Bildschirm angeschlossen ist.
Um mit der Grafikprogrammierung zu beginnen, müssen wir zunächst verstehen, wie ein Bild auf dem Bildschirm dargestellt wird. Ein Bildschirm besteht aus einzelnen Punkten, den Pixeln, die jeweils eine eigene Farbe haben. Normalerweise wird der Bildschirm in einem Koordinatensystem (x, y) dargestellt. Der erste Pixel oben links hat die Koordinaten (0,0). Der nachfolgende Pixel rechts davon hat die Koordinate (1,0) und so weiter. Nach unten wird die y-Koordinate jeweils um eins erhöht: (0,1), (0,2) usw.
Auflösungen
Der Raspberry Pi unterstützt viele verschiedene Auflösungen. Eine Übersicht über die gängigsten Auflösungen findest du beispielsweise unter https://de.wikipedia.org/wiki/Bildaufl%C3%B6sung#Computer. Hier ein paar Auflösungen, die verwendet werden könnten:
| Mode | Auflösung |
|---|---|
| VGA | 640 x 480 |
| XGA | 1024 x 768 |
| HD | 1920 x 1080 |
| UHD (only Pi 4 or Pi 5) | 4096 x 2160 |
Die Auflösung allein reicht jedoch nicht aus, um komplexe, farbige Bilder darzustellen. Da Computersysteme nur binäre Zahlen kennen, also nur "1" oder "0", könnte man jedes Pixel lediglich ein- oder ausschalten, was nur ein Schwarz-Weiß-Bild erzeugen würde. Um die Vielzahl der Farben, die wir auf Bildschirmen sehen, darzustellen, werden zusätzliche Standards definiert.
Wird die Anzeige mehrmals hintereinander vertieft, entsteht eine größere Farbtiefe. Wenn ein Bild beispielsweise zwei Bitmaps verwendet, stehen für jeden Punkt zwei Bits zur Verfügung. Das ergibt vier Zustände bzw. vier Farben. Je mehr Bitmaps verwendet werden, desto mehr Farben können angezeigt werden.
Farbtiefe
Der Raspberry Pi unterstützt vier verschiedene Farbtiefen: 8-Bit, 16-Bit, 24-Bit und 32-Bit. In der Praxis wird jedoch von der Verwendung der 8-Bit und 24-Bit Farbtiefe abgeraten, da sie zu Darstellungsfehlern führen können. Siehe hierzu auch die Dokumentation unter https://www.raspberrypi.org/documentation/configuration/config-txt/video.md im Abschnitt framebuffer_depth. Um es einfach zu halten, konzentrieren wir uns auf die 32-Bit Farbtiefe.
Beispiel: Speicheraufwand für Bilder Um den Speicheraufwand zu veranschaulichen, betrachten wir ein Beispiel. Ein monochromes Bild mit einer Auflösung von 20 x 20 Pixel benötigt folgenden Speicherbedarf:
Speicherbedarf (Byte) = (x * y) / 8 (20*20)/8 = 50 BytesFür ein Bild mit 4 Bitmaps beträgt der Speicherbedarf:
Speicherbedarf (Byte) = ((x * y) * tiefe) / 8 ((20*20)*4)/8 = 200 BytesNun ein Beispiel mit einer HD-Auflösung und RGBA32-Farbtiefe:
Speicherbedarf (Bytes) = ((1920 * 1080) * 32) / 8 = 8294400 Bytes
Das entspricht etwa 8,3 Megabytes.Abbildung der Bitmaps im Speicher
Bitmaps werden nicht als eine Reihe von Einzelbildern (Bitmaps) im Speicher abgelegt, sondern die Grafikdaten sind wie in einem dreidimensionalen Raum organisiert. Betrachten wir ein Beispiel mit einer Auflösung von 20 x 20 Pixel und einer Farbtiefe von 16 Bit:
Jeder Bildschirmpunkt wird bei einer Farbtiefe von 16 Bit durch 16 aufeinanderfolgende Bits dargestellt. Der erste Punkt verwendet die Bits 0-15, der nächste die Bits 16-31, und so weiter. Das müssen wir bei den Berechnungen für jeden einzelnen Punkt berücksichtigen.
Wie hier zu sehen ist, wird für den ersten Bildschirmpunkt die Bits 0-15 verwendet. Der nächste Bildschirmpunkt die Bits 16-31 usw… Dies müssen wir bei Berechnungen jeden einzelnen Punktes später berücksichtigen.
Der Postbote und TAGs
Wie ich bereits vorher beschrieben habe, wird die Grafik nicht durch die CPU dargestellt, sondern über einen eigenen Prozessor (GPU). Zunächst muss der GPU mitgeteilt werden, was den gemacht werden soll. Dazu benötigt er Informationen, was und wie er es Darstellen soll. Also wie der Bildschirm aussehen soll. Eine direkte Verbindung zwischen der CPU und GPU exsitiert nicht und damit ist die bereits erlernte Art, wie der Prozessor mit der Pereferie kommuniziert so nicht mehr möglich. Allerdings gibt es einen Vermitler, der das ganze übernimmt. Er wird hier als Postbote genant. Wie im Leben, schreibt die CPU einen Brief, den er an den Postboten übergibt und er übergibt diesen Brief der GPU. Umgekehrt ist dies auch möglich. Also damit können Nachrichten von GPU an die CPU übermittelt werden.
Der Postbote hat für den CPU die Basisadresse 0xB880 und wir verwenden "TAGs" für Informationen. Leider ist die Dokumentations dazu sehr spärlich. Allerdings gibt es eine Gruppe, die Informationen gesammelt hat und diese der Gemeinschaft weiter gegeben haben. Schaut einfach mal dort vorbei:
https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
Diese Informationen habe ich in der zu diesem Teil gehörendem Sourcecode zusammen gesetzt und in der base.inc eingefügt.
Zunächst werden die neuen Register, welches dafür verantwortlich sind, in der “base.inc” definiert.
@Mailbox
.equ MAILBOX_BASE, RPI_BASE + 0xB880
.equ MAIL_WRITE, MAILBOX_BASE + 0x20
.equ MAIL_TAG_WRITE, MAIL_WRITE + 0x8
Wie bereits geschrieben, werden in diesem Beispiel "TAGs" verwendet. Damit verwenden wir die "MAIL_TAG_WRITE" Funktion. Da beide Prozessoren nicht unbedingt die gleiche Sprache sprechen, wurde eine Art Universalsprache zwischen beiden Prozessoren vereinbart, die über TAG-Listen gesteuert wird.
Aufbau der TAG-Struktur
TAG-Listen, oder auch Struktur genannt, sind Listen, die eine bestimmte und festgelegte Struktur aufweisen. Diese Struktur muss der gegenüberliegende Partner, in diesem Fall die GPU verstehen.
Für unsere GPU, die wir verwenden, erwartet der Prozessor folgende Struktur:
Eine TAG-Struktur fängt immer mit einer 32-Bit Zahl an, die die Länge der gesamten Struktur angibt. Als nächstes Benötigt die Struktur einen 32-Bit Puffer, um eventuell eine Antwort abzulegen, der bei einem Aufruf übermittelt wird. Im Anschluß erfolgen die einzelnen TAGs. Jeder TAG folgt wieder einer Grundstruktur. Zunächst wird der Name des TAGs übermittelt, Anschließend die Anzahl der Werte in Bytes (32Bit, entsprechend 4) und wieder ein Puffer, für die Antwort. Im Anschluss erfolgen die Werte, für den jeweiligen einzelnen TAG. Die gesamte Struktur wird mit einem NULL-TAG abgeschlossen.
Mailbox Länge
Mailbox Puffer
TAG1 Name
TAG1 Länge
TAG1 Puffer
TAG1 Wert1
TAG1 Wert2
...
TAG2 Name
...
TAGx NULL (End TAG)Wichtig ist hierbei noch zu wissen, dass jeder einzelne TAG auf 32-Bit ausgerichtet sein muss.
Kommen wir nun zu unserer Struktur, die wir wie folgt aufsetzen. Zunächst müssen wir die Struktur auf 16 Byte ausrichten, da nur die oberen 28 Bits der Adresse an die Mailbox übergeben werden.
u32 pScreen[36] ALIGN(16) = {
...
}
Zunächst geben wir unseren TAG-Struktur einen Namen. Wir nennen sie "pScreen". Als erster Eintrag wir zunächst die Länge der gesamten Struktur angegeben und noch ein Puffer, für mögliche Antworten erzeugt.
u32 pScreen[36] ALIGN(16) = {
34*4, //34 Einträge * 4 Bytes
0,
Damit haben wir nun schon den Kopf geschrieben. Die nächsten Einträge sind nun unsere Daten, was wir denn wollen. Den ersten Eintrag, den wir verwenden, ist die Auflösung unserer Anzeige, die wir verwenden wollen. Der TAG, den wir hierfür benötigen heißt "Set_physical_display" und hat folgendes Aussehen:
TAG Name (Set_physical_display)
Puffergröße in Bytes
Antwort Puffer
Breite des Bildschirms in Pixel
Höhe des Bildschirms in PixelÜbertragen in unsere Struktur sieht es wie folgt aus:
Set_physical_display, //Tag Identifier
0x8,0x8, //Buffer size in bytes
SCREEN_X, //Width in pixels
SCREEN_Y, //Height in pixels
Für die Breite und Höhe habe ich Variablen eingesetzt, damit die Struktur zumindest für die Programmierung variabel bleibt. Die SCREEN_X und SCREEN_Y werden in der "config.h" Datei definieren und hier im ersten Beispiel eine Auflösung von 1920 x 1080 Pixel verwenden.
Die GPU möchte auch Angaben des Virtuellen Puffer haben. Dazu wird der TAG "Set_virtual_buffer" verwendet. Seine Struktur ist ähnlich die, die wir zuvor erstellt haben.
TAG Name (Set_virtual_buffer)
Puffergröße in Bytes
Antwort Puffer
Breite des Bildschirms in Pixel
Höhe des Bildschirms in PixelUnd wieder umgesetz in unsere Struktur:
Set_virtual_buffer,
0x8,0x8,
SCREEN_X,
SCREEN_Y,
Der nächste TAG "Set_depth" definiert die Tiefe des Bildschirms.
TAG Name (Set_depth)
Puffergröße in Bytes
Antwort Puffer
Tiefe der Bitplanes Set_depth, //Tag Identifier
0x4, //Buffer size in bytes
0x4, //Response buffer
BITS_PER_PIXEL, //depth
Auch hier verwenden wir eine Variable (BITS_PER_PIXEL), die wir in der "config.h" Datei auf 32 Bit definieren werden.
Der nächste TAG "Set_virtual_offset" wird verwendet um einen Offset zu erzeugen.
TAG Name (Set_virtual_offset)
Puffergröße in Bytes
Antwort Puffer
Offset x
Offset yEs ist durchaus möglich, dass wir für die physikalische Anzeige oder dem virtuellen Puffer unterschiedliche Dimensionen übergeben haben. Damit ist es möglich, dass auf dem Bildschirm nur ein Ausschnitt der Anzeige angezeigt wird. Mit diesem Offset wird genau dieser Ausschnitt festgelegt. In unserem Fall verwenden wir keinen Offset und haben bereits zuvor die Anzeige und den Puffer gleich dimensioniert. Also wir setzen die Position des Offsets auf NULL.
Set_virtual_offset,
0x8,0x8,
0x0,0x0,
Dann kommen wir zu unseren Farben. Hierfür ist augenscheinlich der TAG "Set_palette" zuständig und wird in der TAG-Liste benötigt.
TAG Name (Set_palette)
Puffergröße in Bytes
Antwort Puffer
erster eingestellter Palettenindex
Anzahl der einzustellenden Paletteneinträge
RGBA PalettenwerteNun haben wir gelernt, dass Anzeigen mit einer Farbtiefe von 8 Bit, Paletten für die Anzeige verwenden, wir aber eigentlich darauf verzichten sollen. Wir werden für unsere Anzeigen nur 16-Bit oder 32-Bit verwenden. Damit ist dieser Eintrag nicht wirklich wichtig. Denoch müssen wir diesen Teil erstellen.
Set_palette,
0x10,0x10,
0x0,
0x2,
0x00000000,
0xffffffff,
Mit dem nächsten TAG (Allocate_buffer) beauftragen wir die GPU Speicher zu reservieren und den Inhalt des Speichers anzuzeigen.
TAG Name (Allocate_buffer)
Puffergröße in Bytes
Antwort Puffer
Frame Buffer Basisadresse in Bytes
Bildpuffergröße in Bytes Allocate_buffer,
0x8,0x8,
0x0, //Bufferadresse (pScreen[31])
0x0,
Der TAG erzeugt einen Puffer, der die Adresse des Speichers für den Screen zurück gibt. Dieser Wert, wird im Label "pScreen[31]" abgelegt, sobald die GPU dies erfolgreich durchgeführt hat. Dies ist dann auch die Adresse, auf die wir später zurückgreifen können.
Unsere TAG-Liste ist damit beendet und wir müssen hier noch den ENDE-TAG eintragen.
0x00000000
Soweit so gut. Diese Struktur beschreibt nun das, was wir gerne hätten. Die GPU interpretiert diese Werte und versucht dies auch darzustellen. Wenn die GPU dies nicht umsetzen kann, versucht die GPU einen annährend Zustand zu erzeugen. Dies bedeutet, dass nicht unbedingt das Angezeigt wird, was wir vorgegeben haben. Dies kann zu Problemen kommen, da manche Funktionen, auch die, die wir später erstellen, von zum Beispiel der Dimension und/oder der Farbtiefe abhängig sind. Unsere erstellte TAG-Liste ist allerdings keine Einbahnstraße. Auch die GPU verwendet diese, um uns die Werte anzugeben, wie den tatsächlich das Ergebnis aussieht. Diese Werte, die dann wichtig sind, sollten dann jedes mal überprüft werden, ob diese so passen.
Der Postbote
Nun kommt der Postbote ins Spiel. Wir geben ihm unsere TAG-Liste und erzeugen damit unseren Bildschirm. Wir werden zunächst eine einfache Methode verwenden und später die Funktionen erweitern.
Damit haben wir dem Postboten zumindest den Auftrag gegeben, dass er diese Informationen an den GPU zu übergeben. Leider laufen die beiden Prozessoren nicht synchron und wir wissen zu diesem Zeitpunkt nicht, ob nun wirklich die Anzeige erzeugt wurde. Für die GPU ist unser Wunsch auch recht aufwendig, welche Zeit benötigt.
Um nun sicher zu sein, dass es geklappt hat, fragen wir einfach nach, ob es eine Adresse gibt, wo den unser Bildschirm abgelegt wurde. Innerhalb unserer Struktur wird unter pScreen[31] dieser Wert abgelegt. Genau dort schreibt die GPU die Adresse hinein, wenn sie erfolgreich war. Solange diese Adresse nicht gesetzt ist, wissen wir, dass die GPU noch nicht fertig ist. Wir wiederholen einfach diese Aufforderung, bis hier ein Wert steht.
uintptr pScreenAddress = (uintptr)pScreen;
while (TRUE) {
BcmMailBox_Write(BCM_MAILBOX_PROP_OUT,(u32)pScreenAddress);
if (pScreen[31] != 0)
{
break;
}
}
Problematisch wird es allerdings, wenn die GPU nie irgendwas zurück gibt, dann sind wir hier in einer Dauerschleife. Aber wollen wir nicht so schwarz sehen und hoffen, dass es hier zu keinem Fehler kommt. Hier verwenden wir später andere Funktionen, damit wir hier nicht in Problemen bei anderen Abfragen kommen.
Die Adresse, die wir zurück bekommen ist ein "byte" Wert, mit dem wir so nicht arbeiten können. Wir müssen diesen zuerst für unsere Zwecke umwandeln, was wir mit einem einfachen "and" durchführen. Anschließend speichern wir den Wert für spätere Zwecke.
#define ADDRESS_MASK 0x3FFFFFFF
graphicsAddress = pScreen[31] & ADDRESS_MASK;
Auf den Bildschirm zeichnen
Nun haben wir einen Screen. Naja, dieser Screen ist leer und man sieht eigentlich nichts. Also müssen wir einen Weg finden, dass wir in irgend einer Weise etwas auf den Bildschirm bekommen.
Dazu werden wir eine Funktion entwickeln, die einen einzelnen Bildpunkt definiert und anzeigt. Diese grundlegende Funktion wird die Ausgangsbasis für alle grafischen Funktionen sein.
Diese Funktion nennen wir “DrawPixel” und übergeben ihr die Koordinate in x und y.
Für verschiedene Farbtiefen, sind verschiedene Berechnungen notwendig, um die richtige Farbe darzustellen. Genau so ist diese Farbtiefe auch verantwortlich, wo den die einzelnen Bildpunkte im Speicher zu finden sind. Zunächst werden wir diese Funktion relativ einfach halten und einfach davon ausgehen, dass wir eine Farbtiefe von 32 Bit haben.
Zunächst überprüfen wir, ob der Pixel überhaupt im sichtbaren Bereichs liegt. Es würde sonst zu unerwünschten Nebeneffekten kommen, denn wir wissen nicht, welche Funktion die entsprechende Speicherstelle im System hat.
Wenn wir es überprüft haben, können wir die richtige Position berechnen. Dies erfolgt nach folgender Formel:
((Gesammtscreenbreite * Position y) + Position x) * Screentiefe in ByteDazu müssen wir die ScreenAdresse noch zu dieser Position hinzuaddieren.
void DrawPixel(u32 x, u32 y)
{
if ((x < SCREEN_X) && (y < SCREEN_Y))
{
write32(DrawColor,graphicsAddress+(((SCREEN_X*y)+x)*4));
}
}
Damit wird bereits der Pixel angezeigt.
Damit ist unsere vorläufige “DrawPixel” Funktion erstmal fertig und erzeugen eine neue Headerdatei in der wir diese Funktionen beschreiben.
//
// 20.02.2025 www.satyria.de
//
// screen.h
//
#ifndef _screen_h
#define _screen_h
#include "types.h"
void Init_Screen(void) ;
void DrawPixel(u32 x, u32 y);
#endif
Anwendung der DrawPixel-Funktion
Wir haben jetzt eine Funktion geschrieben, die es ermöglicht, etwas auf dem Bildschirm zu sehen. Jetzt müssen wir nur noch unsere neuen Funktionen im Hauptprogramm verwenden und wir dürften ein Ergebniss sehen.
Als wir die TAG-Struktur erstellten, haben wir einige Variablen benutzt, die wir erstmal festlegen müssen. Dazu verwenden wir unsere "config.h" Datei und ergänzen folgende Zeilen:
#define SCREEN_X 1920
#define SCREEN_Y 1080
#define BITS_PER_PIXEL 32
Damit definieren wir die Breite, die Höhe und die Tiefe unserer Bildschirmes.
Die erste Funktion, die wir aufrufen müssen, ist die Erstellung des Bildschirms. HIer wird nichts übergeben und wir bekommen nichts zurück.
Init_Screen();
Einen einzelnen Punkt auf einem Bildschirm zu sehen, ist manchmal recht schwierig. Dazu werden wir mehrere Punkte nebeneinander setzen und verwenden eine Schleife, die die Koordinaten entsprechend verändert und lassen uns jedesmal den Punkt zeichnen.
u32 i;
for (i=100;i<=500;i++)
{
DrawPixel(i,i);
}
Der Sourcecode, für dieses Beispiel ist hier zum laden bereit: [1]
| < Zurück (Fehlerbehandlung in C (PI4)) | < Hauptseite > | Weiter (Chars in C (PI4)) > |
