[comp.windows.x] Long query about color XOR'ing

aperez@caribe.prime.com (Arturo Perez x6739) (04/11/90)

I'm working on some code from xtank (to get it to work in color) and I'm
running into something I don't understand.  Could some kind soul out there
explain what's happening?


I'm running the MIT X server, release 4 with patches 1-9 installed on a sun
3/60 whose default visual is PseudoColor.

The xtank game creates scads of GC's, one for every combination color,
operation, and font.  That turns out to be about 100 GC's.  There also seems
to be quite a lot of bitmaps, possibly as many as 350.  That's just for 
background.

There are 2 modes of operation, GXxor and GXcopy.  The GXxor operation is
used for most of the drawing operations involving the bitmaps, in order to
draw, erase, move, draw the bitmaps.  

Originally, the code looked like this:

  for(i = 0 ; i < MAX_COLORS ; i++) {
    for(j = 0 ; j < MAX_DRAW_FUNCS ; j++) {
      switch(j) {
      case DRAW_XOR:
        values.function = GXxor;
        values.background = vid->bg;
        values.foreground = vid->color[i] ^ vid->fg;

And to draw a bitmap, like so:

#define draw_picture(w,x,y,pic,func,_color) \
  { \
    int tmp_x = x - pic->offset_x; \
    int tmp_y = y - pic->offset_y; \
      if(x > -pic->offset_x && tmp_x < vid->win[w].width && \
         y > -pic->offset_y && tmp_y < vid->win[w].height) { \
        if(vid->planes == 1)\
          XCopyArea(vid->dpy,vid->pixid[pic->pixmap],vid->win[w].id, \
                    vid->graph_gc[func][_color],0,0,pic->width,pic->height, \
                    tmp_x,tmp_y); \
        else\
	  XCopyPlane(vid->dpy,vid->pixid[pic->pixmap],vid->win[w].id, \
                     vid->graph_gc[func][_color],0,0,pic->width,pic->height, \
                     tmp_x,tmp_y, 1);\
         }\
 }

And that didn't work.  All I got were white blocks instead of the bitmaps.  Now,
on a PseudoColor screen, this is obviously wrong because we're dealing with
colormap entries, not pixel values.  So, I changed it to this:

  for(i = 0 ; i < MAX_COLORS ; i++) {
    for(j = 0 ; j < MAX_DRAW_FUNCS ; j++) {
      switch(j) {
      case DRAW_XOR:
        values.function = GXxor;
        values.background = vid->bg;
        values.foreground = vid->color[i];

And that didn't work.  The bitmaps came out correctly, but lines and text
did not.  The lines and text didn't come out at all, as a matter of fact. Also,
in mono the bitmaps came out fine, but on a color screen the bitmaps came out
in reverse, i.e. black on white, instead of white on black.

This is background for my first bunch of questions.
	How come lines didn't come out when I`m using the GC for xor? All
	I'm doing is using it in an XDrawLine request.

	Why do the bitmaps come out as black on white on a color screen?

So, next I try:

  for(i = 0 ; i < MAX_COLORS ; i++) {
    for(j = 0 ; j < MAX_DRAW_FUNCS ; j++) {
      switch(j) {
      case DRAW_XOR:
        values.function = GXxor;
        values.background = vid->color[i];
        values.foreground = vid->bg;


And that works perfectly in mono but in color the bitmaps come in in black
with a colored box surrounding them.  Why the difference?  I would have 
thought that if the bitmap looked fine in mono, it should have looked fine in
color as well.

So, then I changed it to:

  for(i = 0 ; i < MAX_COLORS ; i++) {
    for(j = 0 ; j < MAX_DRAW_FUNCS ; j++) {
      switch(j) {
      case DRAW_XOR:
        values.function = GXxor;
        if (vid->color[i] == vid->fg) {
           values.background = vid->fg;
           values.foreground = vid->bg;
        } else {
           values.background = vid->fg;
           values.foreground = vid->color[i];
        }

And this sort of works for both color and black/white.  At least, the bitmaps
come out color on black and white on black.  But, out of the set of colors
Black, White, Red, Orange, Yellow, Green, Blue and Violet on my workstation
(3/60 /dev/cgfour0) the orange comes out as dark blue and the blue and violet
don't come out at all, that is, the bitmaps drawn in those 2 colors are
invisible.  Even worse, on other workstations (even other 3/60) none of the
colors look correct.  Why do the colors look right on my machine but no one
else's?  Why don`t the colors come out right if I XOR them onto black?

I reason, probably incorrectly, that the colors are behaving as if they
were being XOR'ed onto white.  So, I change the draw_picture macro to

#define draw_picture(w,x,y,pic,func,_color) \
  { \
    int tmp_x = x - pic->offset_x; \
    int tmp_y = y - pic->offset_y; \
      if(x > -pic->offset_x && tmp_x < vid->win[w].width && \
         y > -pic->offset_y && tmp_y < vid->win[w].height) { \
        if(vid->planes == 1)\
          XCopyArea(vid->dpy,vid->pixid[pic->pixmap],vid->win[w].id, \
                    vid->graph_gc[func][_color],0,0,pic->width,pic->height, \
                    tmp_x,tmp_y); \
        else\
          switch(func) {\
               case DRAW_XOR : \
                 if (_color != WHITE)\
                   XCopyPlane(vid->dpy,vid->pixid[pic->pixmap],vid->win[w].id,\
                     vid->graph_gc[func][WHITE],0,0,pic->width,pic->height, \
                     tmp_x,tmp_y, 1);\
               case DRAW_COPY : \
                  XCopyPlane(vid->dpy,vid->pixid[pic->pixmap],vid->win[w].id, \
                     vid->graph_gc[func][_color],0,0,pic->width,pic->height, \
                     tmp_x,tmp_y, 1);\
         }\
      } \
   }

And everything looks correct, in both color and mono.  And I've just converted
the program into a real pig because CopyPlane has gotta be one of the most
expensive Xlib calls.

Why doesn't the vanilla XOR work?  Is there a more efficient way to get the
d****d bitmaps up on the screen in the correct color?

Arturo Perez
ComputerVision, a division of Prime
aperez@cvbnet.prime.com
Too much information, like a bullet through my brain -- The Police

joel@decwrl.dec.com (Joel McCormack) (04/13/90)

>And everything looks correct, in both color and mono.  And I've just converted
>the program into a real pig because CopyPlane has gotta be one of the most
>expensive Xlib calls.

I won't go into problems with XORing in color.  It's ugly.  However, on
most modern X servers, your statement about CopyPlane is incorrect.  If
the server is careful, most hardware can paint faster using CopyPlane
rather than CopyArea.  For example, here's how many times per second you
can perform a 500x500 CopyArea from 8-bit pixmap to 8-bit screen vs.
500x500 CopyPlane from 1-bit pixmap to 8-bit screen on various machines.
 (These are numbers I know have been achieved in one server or another;
for any machine it may be that no single server achieves both numbers. 
I also know that the Sun cfb CopyPlane numbers are probably better with
the current post-R4 server Keith Packard has.)

Machine				CopyArea	CopyPlane
----------------------------------------------------------
DECStation 3100			20.0		38.0
DECStation 5000/200CX		38.8		59.1
Sun cfb				20.1		23.3
Sun GX				18.2		93.1
Newly announced machine with	 1.4		10.0
really great graphics performance purchased from another company, both
of which will remain anonymous.

- Joel McCormack (decwrl!joel, joel@decwrl.dec.com)

keith@EXPO.LCS.MIT.EDU (Keith Packard) (04/13/90)

To use CopyPlane in XOR mode, you need to set up the fg and bg values in
the GC so that the fg value will result in the desired color in the
destination, while the bg value will not modify the existing bits.

The bg value is simple, the only value which will leave the bits unmolested is
0.

For the fg value, remember that XOR mode effectively inverts the destination
bits which match the portions of the fg which are set, so choose the fg
value so that it transforms the desination from the original background to the
desired pixel value:

	Original desination:	GC fg value:	result:
	background	^	fg	=	color[i]

A simple boolean exercise leaves

	fg = background ^ color[i]

Or, in your original parlance:

  for(i = 0 ; i < MAX_COLORS ; i++) {
    for(j = 0 ; j < MAX_DRAW_FUNCS ; j++) {
      switch(j) {
      case DRAW_XOR:
        values.function = GXxor;
        values.background = 0;
        values.foreground = vid->color[i] ^ vid->bg;

As an aside, your code had a special case for depth-1 images.  Rest assured
that the server performs CopyPlane using the same code as CopyArea when
the destination is 1 bit deep, so you can replace that with:

#define draw_picture(w,x,y,pic,func,_color) \
  { \
    int tmp_x = x - pic->offset_x; \
    int tmp_y = y - pic->offset_y; \
      if(x > -pic->offset_x && tmp_x < vid->win[w].width && \
         y > -pic->offset_y && tmp_y < vid->win[w].height) { \
	XCopyPlane(vid->dpy,vid->pixid[pic->pixmap],vid->win[w].id, \
                   vid->graph_gc[func][_color],0,0,pic->width,pic->height, \
                   tmp_x,tmp_y, 1);\
      } \
 }