[comp.windows.x] X11 Color basics.

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 ();
}