Using transparent images

The previous two pages are about how to use images, either by reading them from file or including into the source code. There is one point left: how to use transparent images. These are images in which some points have the special color None. When the image is copied to the screen, points colored with None are assumed to maintain the same color (that is, copying the image left them with the color they had before). This is like saying that these points are transparent: copying them do not cover the previous color.

This is how, by convention, the color None should be treated. However, the operation of reading images, and copying them to the screen, do not behave this way. Namely, when reading the image, the color None is simply converted to some color (for example, black). This mean that points in the image that should be transparent are actually drawn in black.

In order to use transparent images, we need two images: the first one represent the ``regular'' colors of the original images, while the second one tells which points of the image are transparent. This second image can be read at the same time the first one is read. We use the image star5.xpm, which contains some transparent points.

  /* read the image */
  if (XpmReadFileToImage (dpy, "star5.xpm", &img, &clp, NULL)) {
      printf ("Error reading file\n");
      exit (1);
  }

The variable clp is of type XImage *. In words, the second-last argument of XpmReadFileToImage is now the address of an image, instead of NULL. To be precise, after reading we should check whether clp is not null, as it may happen if the image does not contain transparent points.

With this change, we now have in memory both parts of the image: the description of the colors are in img, while clp tells which points are transparent. Using these data for drawing the image, however, is a bit complicated. The first thing to do is to define a pixmap. A pixmap is a sort of canvas: it can be used for drawing, but is not displayed into the screen. It can be copied into a window, and this is the way we make it visible.

In order to use a pixmap, we have to create it: first we declare a variabile Pixmap pix;. Second, we actually create the memory needed to store the image.

  pix = XCreatePixmap(dpy, root, clp->width, clp->height, clp->depth);

Now, pix is a pixmap we can draw into. In order to do this, we need a graphic context, which defines how drawing is to be made (for example, it defines the foreground color):

  gc = XCreateGC (dpy, pix, 0, NULL);

It is now possible, for example, to draw a line into this pixmap using the function XDrawLine, passing pix and gc to it. For the purpose of drawing transparent colors, we just need to copy the image in clp into it. This is done by using the usual XPutImage function.

  XPutImage(dpy, pix, gc, clp, 0, 0, 0, 0, clp->width, clp->height);

Finally, each time we want to copy the image img into a point x, y, we have to tell that the pixmap represent the transparent points of the image.

      /* set clip origin and copy */
      XSetClipMask(dpy, g, pix);
      XSetClipOrigin(dpy, g, x, y);
      XPutImage(dpy, root, g, img, 0, 0, x, y, img->width, img->height );

The complete code trans.c is not much different from image.c, but allows for the use of images which are (partly) transparent.

This sequence of operations may look complicated/meaningless. We now summarize what is really needed for the the purpose of copying a transparent images to the screen. We need three variabiles more: an image, a pixmap, and a graphic context.

  GC gc;
  XImage *clp;
  Pixmap pix;

Reading the image is now done with:

  /* read the image */
  if (XpmReadFileToImage (dpy, "star5.xpm", &img, &clp, NULL)) {
      printf ("Error reading file\n");
      exit (1);
  }


  /* copy the transparent image into the pixmap */
  pix = XCreatePixmap(dpy, root, clp->width, clp->height, clp->depth);
  gc = XCreateGC (dpy, pix, 0, NULL);
  XPutImage(dpy, pix, gc, clp, 0, 0, 0, 0, clp->width, clp->height);

To drawn the image into position x, y, execute:

      /* set clip origin and copy */
      XSetClipMask(dpy, g, pix);
      XSetClipOrigin(dpy, g, x, y);
      XPutImage(dpy, root, g, img, 0, 0, x, y, img->width, img->height );

The result of the XPutImage is to copy the non-trasparent points of the image into the screen, while leaving all others unchanged. If we want to draw something else into the screen, we also have to run:

      XSetClipMask(dpy, g, None);

This is needed to tell X that the next drawing operation does not involve transparent points. This is what is typically expected when drawing lines, rectangles, etc, so this command has to be executed before calling functions like XDrawLine, XDrawRectangle, etc.

There are other ways for drawing transparent images. See for example the documentation of the function XpmReadFileToPixmap, that copies files directly into pixmaps.


Next: animation