14.8 Farben mit X
Hinweis Verzeihen Sie mir hierbei gleich zu Beginn, dass ich das Thema Farben ein wenig zusammengeschrumpft habe. Das Ziel liegt hier vorwiegend in der Verwendung der Farben.
|
Die Farbe des Vorder- bzw. Hintergrundes eines Fensters ist Teil der Struktur des grafischen Kontextes GC. Verwendet haben Sie diesen bereits mit den Funktionen XSetForeground() und XSetBackground() wie folgt:
XSetForeground(display, mygc, farbe1);
XSetBackground(display, mygc, farbe2);
Bei farbe1 und farbe2 handelt es sich um einfache Integerzahlen. Was für Werte Sie nun für welche Farbe verwenden, ist wiederum nicht so einfach, da X ja unterschiedliche Server-Hardware unterstützt. Im Folgenden wird daher auf die einfachsten Grundlagen eingegangen.
Um eine Farbe auf dem Monitor darzustellen, wird ein Zahlentripel (Rot, Grün, Blau, kurz RGB) mit Intensitätswerten zwischen 0 und 255 verwendet (als Beispiel 8-Bit-Wert mit 256 Farben). Die Werte (R = 0, G = 0, B = 0) entsprechen der Farbe Schwarz, und die Werte (R = 255, G = 255, B = 255) stehen für eine weiße Farbe.
Realisiert wird diese Farbdarstellung bei X mit einer Colormap. Eine Colormap besteht somit aus 256 RGB-Tripeln, genannt Colorcells. XSetForeground() mit farbe1=11 z. B. schaltet dann um auf Farbe Nr. 11. Wie diese Farbe aussieht, legt der RGB-Wert fest, der in dieser Zelle eingetragen ist. Im einfachsten Fall, den Sie hier behandeln, wird von allen Anwendungen eine gemeinsame Colormap benutzt (Shared Colors) – sie wird auch als Default Colormap bezeichnet. Bei den Shared Colors wird folgender Weg eingeschlagen:
Wenn die Farbzellen von Nr. 0 bis n-1 definiert sind, gibt man dem X-Server eine gewünschte Farbe entweder als RGB-Wert oder in einer Stringform (dazu gleich mehr) an. Wenn eine Farbzelle bereits mit dieser Nummer verwendet wurde, dann wird einfach die Nummer dieser Zelle zurückgegeben. Existiert diese Farbe noch nicht, wird diese Zelle erst noch alloziiert – anschließend mit einem RGB-Wert versehen und zurückgegeben. Mehr als 256 Farben können dabei allerdings nicht pro Colormap definiert werden. Die Zelle kann auch wieder freigegeben und von derselben Anwendung neu belegt werden. Die Belegung der Colormap bleibt auch nach dem Ende der Anwendung erhalten und wird erst bei Beendigung des X-Servers gelöscht.
Jetzt folgen die Funktionen, die den eben beschriebenen Weg in der Praxis möglich machen. Zuerst wird gewöhnlich die Funktion DefaultColormap(display, screen) aufgerufen, womit die Struktur Colormap mit den Standardinformationen belegt wird, die zur weiteren Arbeit nötig sind.
Hinweis Ob Ihre Hardware überhaupt Farbe anzeigen kann (so etwas soll es auch noch geben), können Sie sich mit der Funktion DefaultDeep(display, screen) ermitteln lassen. Solange der Rückgabewert >1 ist, können Sie Farbe darstellen.
|
Jetzt können Sie den RGB-Wert in einer Zelle der Colormap mit der Funktion XAllocColor() eintragen:
Display *display;
Colormap cmap;
XColor color;
Status result;
...
color.red = 16276; color.green=54271; color.blue=2000;
result = XAllocColor(display, cmap, &color);
if (result == 0)
/* Fehler bei XAllocColor() */
XColor ist eine Struktur, bestehend (u. a.) aus den unsigned short-Werten red, green, blue sowie einem unsigned long-Wert pixel. XAllocColor() benutzt red, green, blue als Input und liefert pixel als Nummer einer neuen (oder auch alten, falls schon definiert) Farbzelle zurück.
Es wurde bereits erwähnt, dass die Farbwerte auch als Strings übergeben werden können. Dazu kann entweder die Funktion XAllocNamedColor() oder die Funktion XLookupColor() verwendet werden. Die Funktion XAllocNamedColor() funktioniert im Prinzip wie die Funktion XAllocColor(), nur dass der Name der Farbe hierbei direkt benutzt wird:
Display *display;
Colormap cmap;
XColor exakt, color;
Status result;
...
result=XAllocNamedColor(mydisplay, cmap, "blue", &exact, &color);
Die Funktion trägt das zum Farbnamen gehörige RGB-Tripel wie XAllocColor() in eine Farbzelle ein. exact liefert in den Variablen exact.red, exact.green, exact.blue die exakten RGB-Werte zurück, die dieser Farbe entsprechen – exact.pixel bleibt undefiniert. Die Struktur XColor enthält neben einem möglichst gut an exact angenäherten RGB-Wert die alloziierte Zellennummer in color.pixel.
Eine zweite Alternative stellt die Funktion XLookupColor() zur Verfügung, die anschließend im Beispiel demonstriert wird. Natürlich setzt die Verwendung von XLookupColor() ein anschließendes XAllocColor() voraus.
Die Umsetzung der Namen in RGB-Tripel erfolgt über eine kleine Datenbank mit 8-Bit-RGB-Werten. Die zugehörige Textdatei heißt rgb.txt und befindet sich gewöhnlich im Verzeichnis /usr/lib/X11:
$ cat /usr/lib/X11/rgb.txt | less
255 250 250 snow
248 248 255 ghost white
248 248 255 GhostWhite
...
139 0 0 DarkRed
144 238 144 light green
144 238 144 LightGreen
Benötigen Sie den RGB-Wert einer Zelle, so können Sie diesen mit der Funktion XQueryColor() ermitteln.
Display *display;
Colormap cmap;
XColor color;
...
color.pixel = 117;
XQueryColor(mydisplay, cmap, &color);
Die Parameter werden genau umgekehrt zu XAllocColor() benutzt: Die Zellennummer wird in color.pixel übergeben, zurück kommt das eingetragene RGB-Tripel in color.red, color.green und color.blue.
Jetzt können Sie mithilfe des Grafikkontexts GC die alloziierte Farbe mit Funktionen wie XSetForeground() oder XSetBackground() verwenden.
Um am Ende die Farbzelle einer Colormap wieder freizugeben, müssen Sie die Funktion XFreeColors() verwenden. Es wird lediglich der Speicherplatz freigegeben; der Inhalt bleibt weiterhin unverändert bis zum Ende des X-Servers erhalten.
Display *display;
Colormap cmap;
XColor color;
unsigned long pixels[MAX];
int npixel;
...
XFreeColors(mydisplay, cmap, pixels, MAX, 0);
MAX definiert hier, wie viele Einträge in der Colormap wieder freigegeben werden sollen. Die Zellennummern müssen in dem Feld pixels ab Element 0 eingetragen werden. Ist number = 1, so reicht eine einfache Variable für pixels aus – im Unterprogrammaufruf muss allerdings dann &pixels stehen. Wenn Sie versuchen, eine Zelle freizugeben, die Sie nicht alloziiert haben, bricht der X-Server das Programm mit einer Fehlermeldung ab.
Hier nochmals im Schnellüberblick die einzelnen Schritte, um Farben zu verwenden:
|
Farbe vorhanden – DefaultDeep() (optional) |
|
Struktur Colormap mit Standardinformationen füttern – DefaultColormap() |
|
Umsetzen eines Namens in ein RGB-Tripel – XLookupColor() (falls nötig) |
|
RGB-Wert in einer Zelle der Colormap eintragen - XAllocColor() oder XAllocNamedColor() |
|
Farbe mit Grafikkontext verwenden – z. B. XSetForeground() oder XSetBackground() |
|
Farbzelle einer Colormap wieder freigeben – XFreeColors() |
Das folgende Listing zeigt Ihnen die hier vorgestellten Funktionen in einer Funktion namens setcolourvianame() verpackt im Einsatz. Als Vorlage dient wieder das Listing vom Beispiel zuvor, nur dass diesmal die Grafikprimitiven in Farbe gezeichnet wurden.
/* xfont_Color.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "my_delay.h"
#define WINDOW_WIDTH 600
#define WINDOW_HEIGHT 450
/* Funktionsprototypen */
static int create_window (void);
static void process_event (XEvent report);
static void eventloop (void);
static void close_window (void);
extern int delay (int i);
extern int time_diff (void);
static void draw_graphics (void);
static void print_text (GC, int, int, char *, char *);
static void print_font_demo (void);
static void setcolourvianame (GC, char *);
/* Globale Variablen */
static unsigned int width, height;
static Display *display;
static int screen;
static int depth;
static Window win;
/* Schriftarten */
static char *my_fonts[] = {
"-adobe-times-bold-r-normal--18*",
"-adobe-times-bold-r-normal--12*",
"-b&h-lucida-medium-r-normal-*-*-180-*-*-*-*-*-*",
"-*-times-*-*-*-*-*-240-*-*-*-*-*-*"
};
enum{ ADOBE_BOLD_18, ADOBE_BOLD_12, LUCIA_NORMAL, TIMES };
/* Funktion erzeugt ein Fenster */
static int create_window (void) {
char *window_name = "Farbe kommt ins Spiel";
char *icon_name = "window";
static XSizeHints size_hints;
Window rootwin;
width = WINDOW_WIDTH;
height = WINDOW_HEIGHT;
/* X-Sitzung öffnen */
display = XOpenDisplay (NULL);
/* Fehlerüberprüfung */
if (display == NULL) {
printf ("Verbindung zum X-Server fehlgeschlagen?!?\n");
exit(EXIT_FAILURE);
}
/* Standardbildschirm eines Displays */
screen = XDefaultScreen (display);
/* Standardtiefe des Screens - Anzahl der Ebenen */
depth = XDefaultDepth (display, screen);
rootwin = RootWindow (display, screen);
win = XCreateSimpleWindow ( display, rootwin, 10, 10,
width, height, 5,
BlackPixel (display, screen),
WhitePixel (display, screen) );
size_hints.flags = PSize | PMinSize | PMaxSize;
size_hints.min_width = width;
size_hints.max_width = width;
size_hints.min_height = height;
size_hints.max_height = height;
XSetStandardProperties ( display, win, window_name, icon_name,
None, 0, 0, &size_hints);
XSelectInput (display, win,
ButtonPressMask | KeyPressMask | ExposureMask);
XMapWindow (display, win);
return 1;
}
static void close_window (void) {
XCloseDisplay (display);
}
static void eventloop (void) {
XEvent xev;
int num_events;
XFlush (display);
num_events = XPending (display);
while ((num_events != 0)) {
num_events--;
XNextEvent (display, &xev);
process_event (xev);
}
}
static void process_event (XEvent report) {
KeySym key;
switch (report.type) {
case Expose: /* Expose-Event --> Bild zeichnen */
if (report.xexpose.count == 0) {
draw_graphics ();
}
break;
case KeyPress:
key = XLookupKeysym (&report.xkey, 0);
switch (key) {
case (XK_KP_4):
case (XK_Left):
case (XK_KP_Left):
printf ("Links\n");
break;
case (XK_KP_6):
case (XK_Right):
case (XK_KP_Right):
printf ("Rechts\n");
break;
case (XK_KP_8):
case (XK_Up):
case (XK_KP_Up):
printf ("Nach oben\n");
break;
case (XK_KP_2):
case (XK_Down):
case (XK_KP_Down):
printf ("Nach unten\n");
break;
default:
break;
}
break;
case ButtonPressMask:
/* Rechte Maustaste gedrückt? */
if (report.xbutton.button == 3)
print_font_demo ();
else
printf ("Es wurde gedrückt: %d\n",
report.xbutton.button);
break;
default:
break;
}
}
static void draw_graphics (void) {
GC mygc; /* neu: Grafikkontext */
unsigned long black, white; /* Nummern der Farben */
int i;
XPoint punkte[50]; /* Punkteschar */
XPoint poly[4]; /* 4 Punkte fuer Polygon */
/* Eine Schräge mithilfe von Punkten zeichnen */
for (i = 0; i < 50; i++) {
punkte[i].x = 470 + i;
punkte[i].y = 200 + i;
}
/* geschlossenes Dreieck */
poly[0].x = 200;
poly[0].y = 200;
poly[1].x = 300;
poly[1].y = 300;
poly[2].x = 400;
poly[2].y = 200;
poly[3].x = 200;
poly[3].y = 200;
XMapRaised (display, win);
/* Farbnummern besorgen */
black = BlackPixel (display, screen);
white = WhitePixel (display, screen);
/* Graphical Context erzeugen, Farben eintragen */
mygc = XCreateGC (display, win, 0, 0);
XSetForeground (display, mygc, black);
XSetBackground (display, mygc, white);
/* sicherheitshalber alles löschen */
XClearWindow (display, win);
/* Ein Rechteck zeichnen */
XDrawRectangle (display, win, mygc, 10, 50, 50, 50);
print_text ( mygc, 10, 320, "1. XDrawRectangle()",
my_fonts[ADOBE_BOLD_12]);
/* Blaue Farbe setzen */
setcolourvianame (mygc, "blue");
/* Einen rechteckigen Bereich mit Farbe füllen */
XFillRectangle (display, win, mygc, 110, 50, 50, 50);
print_text ( mygc, 110, 240, "2. XFillRectangle()",
my_fonts[ADOBE_BOLD_12]);
/* Rote Farbe setzen */
setcolourvianame (mygc, "red");
/* Einen Kreis zeichnen */
XDrawArc (display, win, mygc, 210, 50, 50, 50, 0, 360 * 64);
print_text ( mygc, 210, 320, "3. XDrawArc()",
my_fonts[ADOBE_BOLD_12]);
/* Grüne Farbe */
setcolourvianame (mygc, "green");
/* Einen Kreisbereich füllen */
XFillArc (display, win, mygc, 310, 50, 50, 50, 0, 360 * 64);
print_text ( mygc, 310, 240, "4. XFillArc()",
my_fonts[ADOBE_BOLD_12]);
/* Grau */
setcolourvianame (mygc, "grey");
/* Eine Ellipse zeichnen */
XDrawArc (display, win, mygc, 410, 50, 60, 40, 0, 360 * 64);
print_text ( mygc, 410, 320, "5. XDrawArc()",
my_fonts[ADOBE_BOLD_12]);
/* violet */
setcolourvianame (mygc, "violet");
/* Einen Ellipsenbereich füllen */
XFillArc (display, win, mygc, 510, 50, 60, 40, 0, 360 * 64);
print_text ( mygc, 510, 240, "6. XFillArc()",
my_fonts[ADOBE_BOLD_12]);
setcolourvianame (mygc, "grey50");
/* Einen Halbkreis zeichnen */
XDrawArc (display, win, mygc, 10, 200, 60, 40, 90*64, 180*64);
print_text ( mygc, 10, 480, "7. XDrawArc()",
my_fonts[ADOBE_BOLD_12]);
setcolourvianame (mygc, "grey25");
/* Einen Halbkreis füllen */
XFillArc (display, win, mygc, 110, 200, 60, 40, 90*64,180*64);
print_text ( mygc, 110, 380, "8. XFillArc()",
my_fonts[ADOBE_BOLD_12]);
setcolourvianame (mygc, "yellow");
/* ausgefuelltes Polygon bzw. Dreieck */
XFillPolygon(display, win, mygc, poly, 4, 0, CoordModeOrigin);
setcolourvianame (mygc, "black");
print_text ( mygc, 250, 520, "9. XFillPolygon()",
my_fonts[ADOBE_BOLD_12]);
setcolourvianame (mygc, "aquamarine");
/* Eine schräge Linie mit Punkten */
XDrawPoints(display, win, mygc, punkte, 50, CoordModeOrigin);
print_text ( mygc, 470, 380, "10. XDrawPoints()",
my_fonts[ADOBE_BOLD_12]);
setcolourvianame (mygc, "black");
print_text (mygc, 150, 590,
"Mit rechter Maustaste zum Font-Demo",
my_fonts[ADOBE_BOLD_18]);
XFlush (display);
XFreeGC (display, mygc);
}
/* Die Funktionen print_text() und print_font_demo() übernehmen
* Sie bitte aus dem Listing xfont.c, oder Sie können auch das
* komplette Beispiel von der Buch-CD verwenden
*/
static void setcolourvianame (GC myGC, char *colourname) {
/* genaue RGB-Komponenten der Farbe */
XColor rgbcolour;
/* am nächsten liegende RGB-Farben der Hardware */
XColor hwarecolour;
int status;
/* Colormap */
Colormap myColourmap;
int myDepth;
myDepth = DefaultDepth (display, screen);
myColourmap = DefaultColormap (display, screen);
screen = DefaultScreen (display);
if (myDepth > 1) {
status = XLookupColor (
display, myColourmap,colourname,&rgbcolour,&hwarecolour );
if (status != 0) {
/* Farben für die Colormap reservieren */
status = XAllocColor ( display, myColourmap, &hwarecolour);
if (status != 0) {
/* Zeichenfarbe setzen */
XSetForeground (display, myGC, hwarecolour.pixel );
}
else {
printf ("Kann Farbzelle für %s nicht ermitteln\n",
colourname);
printf ("Verwende stattdessen Schwarz\n");
XSetForeground(display, myGC,BlackPixel(display,screen));
}
}
else {
printf ("Kann Farbe %s nicht finden\n", colourname);
printf ("Verwende stattdessen Schwarz\n");
XSetForeground(display, myGC, BlackPixel(display, screen));
}
}
else {
printf("Keine Farbe in in dieser Tiefe:%d ?!\n", myDepth);
printf ("Verwende stattdessen Schwarz \n");
XSetForeground (display, myGC, BlackPixel(display, screen));
}
}
int main (int argc, char **argv) {
int quit = 0;
int pause = 0;
int td;
create_window ();
gettimeofday (&st, NULL);
while (!quit) {
gettimeofday (&rt, NULL);
td = time_diff ();
pause = delay (FRAME_LEN - td + pause);
st = rt;
eventloop ();
}
close_window ();
return EXIT_SUCCESS;
}
Einen Screenshot kann ich mir hierbei ersparen, da die Grafiken im Buch in Schwarz-Weiß gedruckt sind (der Screenshot entspräche dem vom Listing zuvor nur mit farbiger Darstellung).
|