Xref: utzoo comp.windows.x:33105 comp.windows.open-look:669 Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!swrinde!elroy.jpl.nasa.gov!decwrl!pa.dec.com!granite.pa.dec.com!whaley From: whaley@spectre.pa.dec.com (Ken Whaley) Newsgroups: comp.windows.x,comp.windows.open-look Subject: Re: Xor'ing of colors problems Message-ID: Date: 21 Feb 91 22:18:14 GMT References: <26@ftms.UUCP> Sender: news@pa.dec.com (News) Organization: Digital Equipment Corporation Lines: 246 In-Reply-To: brown@ftms.UUCP's message of 20 Feb 91 23:01:34 GMT > draw color background result expect > > black black white black > white white tan(?) black > red red white black > red blue aquamarine(?)/ magenta > cyan > blue blue white black > >Can someone explain why we are not getting the expected results? Is there >a formula that can be used to determine ahead of time what the results will >be? Ah, color XORing, my favorite topic! :) Maybe this should be added to the FAQ list? I'd be glad to write the entry. Anyway, here goes. Basically, if you care about what colors result from XORing pixel values in your application, you must make certain preparations. Otherwise, you'll get apparently random results (as you have) which depend on what colors are already in the colormap. As an example, let's consider the 8-plane PseudoColor case, which is a common color system that most people have. This colormap has 256 entries, each of which can be set to produce any color your system is capable of displaying. Suppose your colors are red and blue and suppose they have pixel values 3 and 5, and your GC has foreground==red, background==blue, function==XOR. red XOR red == 3 XOR 3 == 0, so whatever color has pixel value zero is displayed when you draw over red with red. So, since combining equal colors gives you white (except for white+white = tan, which means you must have more than one pixel value for white), your white has pixel value 0. Now, drawing blue on red or red on blue results in pixel value 3 XOR 5, or 6. Whatever color is at pixel value 6 in your colormap is displayed here. The strange colors you're seeing have a pixel value equal to that of the XOR of the two colors you're combining, and might have been set by a different application which allocated colors from the default colormap. In general, if you'd like to use XOR to flip between two arbitrary colors (fg and bg), set up an XOR-ing GC with function = XOR, foreground = fg ^ bg, and background = 0 ("^" is XOR in C). To apply this to our case, foreground = red ^ blue (== 3 ^ 5 == 6). Then, drawing over red with this GC results in: foreground ^ red == 6 ^ 3 == 5 == blue and drawing over blue results in: foreground ^ blue == 6 ^ 5 == 3 == red. Voila! It's a little more complicated when you want to combine colors rather than switch between them, and if many colors are involved, it gets yet more complicated. Basically, you want to use XAllocColorCells with one pixel and several planes to allocate consecutive color planes, then to store colors into specific pixel values so that XORing works the way you want. If you want to use "overlay planes" instead of XORing, you call XAllocColorCells with several base colors and the number of overlay planes you want, and then set up colors for each combination of base pixel value + overlay plane (+ overlay plane....). Rough Example: using XOR to combine colors (in a reversible way, i.e., drawing again erases the first draw) with XAllocColorCells (one color, 3 planes) 0 is your window background color 1, 2, and 4 are your drawing colors XOR table: draw color background color result --------- ---------------- ----- 1 0 1 (1^1 == 0, also) 1 2 3 (1^3 == 2, also) 1 4 5 (1^5 == 4, also...etc) 2 0 2 2 1 3 2 4 6 4 0 4 4 1 5 4 2 6 Using this table, you allocate 0, 1, 2, 3, 4, 5, 6 to whatever you want them to be, and you can "rubber band" with either color 1, 2, or 4. Now, you don't really get these values literally; you get them allocated to you as consecutive planes + a base pixel, which means the actual pixel values might be (in binary, assuming a base pixel value of 101): 0---0101 where "---" ranges from 000 to 111 in binary. These values correspond to the numbers used in the table above. You select your drawing color through the plane_mask, and set your foreground to 0xff (all ones). Below is an example program that calls XAllocColorCells to get 3 consecutive color planes, sets up the colors, and draws each of the drawing colors on top of each other and the combined colors. It comes up and displays horizontal lines of the drawing colors (1, 2, 4) and the combined colors (3, 5, 6) against a background of color 0. When you press a key in the window, it draws the drawing colors vertically in XOR mode. Press a key again and they vanish (are drawn again), thanks to the magic of XOR. Note that there are a 3 colors here (marked "X" ) that won't occur if you use a "draw then erase" (as in rubber banding) technique (which is what most people use XOR for in the first place). 1 2 4 1 2 4 3 X 5 X 6 X 0 Use the table by looking at the leftmost color on each row, and choosing a column (corresponds to drawing color...look at last row to see which one). Whew! I hope that this is helpful! Ken Save in "file.c", and compile with "cc -o colortest file.c -lX11" ---------------------CUT HERE----------------------- #include #include #define PIXEL(x) (((x) << shift) | pixel) unsigned long plane_masks[6]; unsigned long pixel; Status result; int shift, i; GC gc; XEvent e; Display *d; int scr; Colormap cm; Window w, r; main() { int mode = 0; int flags = {DoRed | DoGreen | DoBlue}; d = XOpenDisplay(""); scr = XDefaultScreen(d); cm = DefaultColormap(d, scr); r = DefaultRootWindow(d); result = XAllocColorCells(d, cm, True, plane_masks, 3, &pixel, 1); if (!result) { printf("allocation failed.\n"); exit(1); } /* find the location of the first bit plane */ for (shift = 0; plane_masks[0] != (1 << shift); shift++) ; XStoreNamedColor(d, cm, "White", PIXEL(0), flags); XStoreNamedColor(d, cm, "Red", PIXEL(1), flags); XStoreNamedColor(d, cm, "Blue", PIXEL(2), flags); XStoreNamedColor(d, cm, "Magenta", PIXEL(3), flags); XStoreNamedColor(d, cm, "Black", PIXEL(4), flags); XStoreNamedColor(d, cm, "Yellow", PIXEL(5), flags); XStoreNamedColor(d, cm, "Green", PIXEL(6), flags); w = XCreateSimpleWindow(d, r, 10, 10, 400, 700, 0, 0, PIXEL(0)); gc = DefaultGC(d, scr); XSelectInput(d, w, ExposureMask | KeyPressMask); XMapWindow(d, w); while(1) { XNextEvent(d, &e); switch(e.type) { case Expose: setup(); if (mode) combine(); break; case KeyPress: mode ^= 1; combine(); break; } } } setup() { XClearWindow(d, w); XSetForeground(d, gc, 0xff); XSetFunction(d, gc, GXxor); /* draw "drawing" colors horizontally against the * background color. */ for (i = 0; i < 3; i++) { XSetPlaneMask(d, gc, plane_masks[i]); XFillRectangle(d, w, gc, 0, i*100, 400, 100); } XSetPlaneMask(d, gc, 0xff); XSetFunction(d, gc, GXcopy); /* draw the "combined" colors to show that the drawing colors * revert them back to the original colors. */ XSetForeground(d, gc, PIXEL(3)); XFillRectangle(d, w, gc, 0, 300, 400, 100); XSetForeground(d, gc, PIXEL(5)); XFillRectangle(d, w, gc, 0, 400, 400, 100); XSetForeground(d, gc, PIXEL(6)); XFillRectangle(d, w, gc, 0, 500, 400, 100); } combine() { XSetForeground(d, gc, 0xff); XSetFunction(d, gc, GXxor); /* Now draw the "drawing" colors over each of the drawing * and combined colors to show the combined ones go back * to the original corresponding drawing color */ for (i = 0; i < 3; i++) { XSetPlaneMask(d, gc, plane_masks[i]); XFillRectangle(d, w, gc, (i+1)*100, 0, 100, 700); } } -- ^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^ /----\// / // Ken Whaley / :) // \ whaley@atd.dec.com | :-)// | | //;-)| Help stamp out smileys! |;)// | | // :) | // :-) / //\____/ ^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v^