josh@mit-vax.LCS.MIT.EDU (Joshua Marantz) (05/21/88)
I have a program that does some basic things with colors and raster operations. It should work on both a color and monochrome server. It was tested on a Sun 3/260 with X11R2 and with bug fixes 1-10, with the -mono switch on or off. It demonstrates allocating a color map, setting up some colors, drawing in OR mode so that intersecting rectangles form intuitive colors, erasing a rectangle without disturbing any rectangles that it intersects, and highlighting. The code to do this, in my opinion, is non-intuitive. I would not have been able to write it by reading the manual alone. The only distributed example that I found that was worth looking at was "ico". Hopefully, people will find this example easier to understand. By comparison, equivalent programs in UIS (the windowing software for DEC VMS Workstations), and an IBM-PC based graphics package, are trivial. It is possible to write a layer of abstraction over the Xlib color functions that make this sort of thing much easier. In fact, I'll probably do that. Source code follows the sign-off. -Joshua Marantz Viewlogic Systems, Inc. ---------------------------------------------------------------- /* File: COLORS.C Author: Joshua Marantz, Viewlogic Systems, Inc. Date: 5/19/88 Env: Unix/X11 This program demonstrates some basic raster drawing operations. It uses a simple 8-color scheme with a map that is arranged to yield intuitive effects when raster operations such as OR are used. This program should work correctly on a monochrome system, or any color system where it is possible to allocate 3 planes. The program draws three solid boxes in OR mode: one red, one green, one blue. They intersect in a manner such that all 7 colors should be visible. It then does several operations, waiting for a standard keyboard hit (using getchar ()) in between each one: 1. Redraws green box in GXandInverted. This should leave the red and blue boxes untouched on a color system. 2. Redraws green box in OR mode again. 3. Redraws red box in GXandInverted. 4. Redraws red box in OR mode. 5. Redraws blue box in GXandInverted. 6. Redraws blue box in OR mode. The window should now be just as it was when the boxes were initially drawn. 7. In XOR mode, draw a blue solid box over the whole window. 8. Redraw blue solid box, thus undoing effects of #7. 9-20. Repeat 7-8 for green,cyan,red,magenta,yellow,white. On a color system, the rectangles drawn using GXandInverted will erase the rectangle in the foreground color without trashing the rectangles drawn in the other two colors. This is because the pixel values are bitwise mutually exclusive. On a monochrome system, the rectangles drawn in GXandInverted mode will erase the corresponding rectangle on the screen, but it will also trash the other two. This is because all the rectangles were drawn in color 1, and thus are not bitwise mutually exclusive. Another problem with the monochrome system is that it is not straightforward to specify a black background and a white foreground. In the "initmono" procedure, the color map values are explicitly set to pixel values 0 and 1. In order for the OR-mode drawing to work correctly, the background must be 0, and the foreground non-zero. If BlackPixel is 1 and WhitePixel is 0, then we must use different raster operations to achieve the same effect. Thus we must set up a mapping for raster operations: Desired Operation Black==0 rasterop Black==1 rasterop DRAW GXor GXand ERASE GXandInverted GXorInverted HIGHLIGHT GXxor GXequiv */ #include <stdio.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #define BLACK 0 #define BLUE 1 #define GREEN 2 #define CYAN 3 #define RED 4 #define MAGENTA 5 #define YELLOW 6 #define WHITE 7 #define FALSE 0 #define TRUE 1 static char *color_strings[] = {"black", "blue", "green", "cyan", "red", "magenta", "yellow", "white"}; static int color_map[8]; /* Set up the rectangles so they intersect in interesting ways. */ XRectangle red = {10, 10, 300, 400}; XRectangle green = {10, 250, 450, 100}; XRectangle blue = {200, 300, 300, 250}; XRectangle big = {0, 0, 500, 500}; /* These arrays make it easier to do things to each primary color */ static int primary_colors[] = {RED, GREEN, BLUE}; static XRectangle *primary_rects[] = {&red, &green, &blue}; /* Raster operation mapping */ #define DRAW 0 #define ERASE 1 #define HIGHLIGHT 2 /* Raster operations must be specified differently for mono */ static unsigned long raster_map_normal[] = {GXor, GXandInverted, GXxor}; static unsigned long raster_map_reverse[] = {GXand, GXorInverted, GXequiv}; /* The default raster operations are OK if we can set the color map */ static unsigned long *raster_map = raster_map_normal; /* This utility function maps a color number between 0 and 7 into an X pixel value, which can be used as an argument to XSetForeground or as the .pixel member of the XColor structure. */ static unsigned int xlate_color(color_number, plane_mask, pixel) unsigned int color_number; unsigned int plane_mask[]; unsigned int pixel[]; { return (((color_number & 0x1) ? plane_mask[0] : 0) | ((color_number & 0x2) ? plane_mask[1] : 0) | ((color_number & 0x4) ? plane_mask[2] : 0) | pixel[0]); } /* Allocate colors in an X color map and set them up. */ unsigned int initcolor(dpy, scrn) Display *dpy; int scrn; { Colormap map; int status; unsigned int plane_mask[3], pixel[1]; int i; XColor colors[8]; map = DefaultColormap (dpy, scrn); status = XAllocColorCells (dpy, map, FALSE, plane_mask, 3, pixel, 1); printf ("status = %d\n", status); XInstallColormap (dpy, map); /* Initialize the X color structure and store it in the map. */ for (i = 0; i < 8; i++) { XParseColor (dpy, map, color_strings[i], &colors[i]); colors[i].pixel = color_map[i] = xlate_color (i, plane_mask, pixel); colors[i].flags = DoRed | DoGreen | DoBlue; } XStoreColors (dpy, map, colors, 8); return (plane_mask[0] | plane_mask[1] | plane_mask[2]); } /* Set up the mapping of color numbers into monochrome values. Change the raster operations to the logically opposite functions if black is pixel #1. */ static unsigned int initmono(dpy, scrn) Display *dpy; int scrn; { int i; color_map[BLACK] = BlackPixel (dpy, scrn); for (i = 1; i < 8; i++) color_map[i] = WhitePixel (dpy, scrn); /* If the background color is not color 0, then change raster maps */ if (color_map[BLACK] != 0) raster_map = raster_map_reverse; return (AllPlanes); } /* Draw a rectangle in the specified color and raster operation. This routine is inefficient because it flushes after each graphics call, but it is useful for debugging and demos. */ static void drawrect(dpy, win, gc, r, color, rasterop) Display *dpy; Window win; GC gc; XRectangle *r; unsigned int color, rasterop; { XSetForeground (dpy, gc, color_map[color]); XSetFunction (dpy, gc, raster_map[rasterop]); XFillRectangle (dpy, win, gc, r -> x, r -> y, r -> width, r -> height); XFlush (dpy); } /* Main procedure: initialize X display, set up monochrome or color mapping, create a window, wait for it to be mapped, draw rectangles in various forms, and exit. */ int main() { Display *dpy; Window win; int scrn; GC gc; unsigned int plane_mask; XGCValues v; XEvent e; int i; /* Connect to the display and init the color map */ if ((dpy = XOpenDisplay (NULL)) == NULL) { fprintf (stderr, "Could not connect to default X display.\n"); exit (1); } scrn = DefaultScreen (dpy); if (DisplayPlanes (dpy, scrn) >= 3) plane_mask = initcolor (dpy, scrn); else plane_mask = initmono (dpy, scrn); /* Create and map the window. */ win = XCreateSimpleWindow (dpy, DefaultRootWindow (dpy), 100, 100, 500, 500, 1, /* Border width */ color_map[WHITE], color_map[BLACK]); XMapWindow (dpy, win); XSetWindowColormap (dpy, win, DefaultColormap (dpy, scrn)); /* Set up the graphics context for the plane mask and fill style */ v.plane_mask = plane_mask; v.fill_style = FillSolid; gc = XCreateGC (dpy, win, GCPlaneMask | GCFillStyle, &v); /* Wait for the window to be mapped. */ XSelectInput (dpy, win, ExposureMask); XNextEvent (dpy, &e); /* Draw the initial filled rectangles. */ for (i = 0; i < 3; i++) drawrect (dpy, win, gc, primary_rects[i], primary_colors[i], DRAW); /* 1-6: For each primary color, erase its box and redraw it. */ for (i = 0; i < 3; i++) { getchar (); drawrect (dpy, win, gc, primary_rects[i], primary_colors[i], ERASE); getchar (); drawrect (dpy, win, gc, primary_rects[i], primary_colors[i], DRAW); } /* 7-20: For each of the colors, XOR a big box over the window twice. */ for (i = 0; i < 8; i++) { getchar (); drawrect (dpy, win, gc, &big, i, HIGHLIGHT); getchar (); drawrect (dpy, win, gc, &big, i, HIGHLIGHT); } getchar (); }