1 /* xscreensaver, Copyright (c) 1992-2006 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 /* This file contains code for grabbing an image of the screen to hack its
13 bits. This is a little tricky, since doing this involves the need to tell
14 the difference between drawing on the actual root window, and on the fake
15 root window used by the screensaver, since at this level the illusion
18 The hacks themselves use utils/grabclient.c to invoke the
19 "xscreensaver-getimage" program as a sub-process.
21 This code is linked only into "driver/xscreensaver-getimage". On normal
22 X11 systems, "xscreensaver-getimage.c" invokes the code in this file.
24 However, under X11 on MacOS, "xscreensaver-getimage" instead runs the
25 script "driver/xscreensaver-getimage-desktop", which invokes the MacOS-
26 specific program "/usr/sbin/screencapture" to get the desktop image.
28 However again, for the MacOS-native (Cocoa) build of the screen savers,
29 "utils/grabclient.c" instead links against "OSX/osxgrabscreen.m", which
30 grabs screen images directly without invoking a sub-process to do it.
36 #include <X11/Xatom.h>
37 #include <X11/Xutil.h>
41 # include <X11/Xmu/WinUtil.h>
43 # include <Xmu/WinUtil.h>
49 #include "grabscreen.h"
51 #include "resources.h"
54 #undef RootWindowOfScreen
56 #undef DefaultRootWindow
59 #ifdef HAVE_READ_DISPLAY_EXTENSION
60 # include <X11/extensions/readdisplay.h>
61 static Bool read_display (Screen *, Window, Pixmap, Bool);
62 #endif /* HAVE_READ_DISPLAY_EXTENSION */
65 static void copy_default_colormap_contents (Screen *, Colormap, Visual *);
67 #ifdef HAVE_READ_DISPLAY_EXTENSION
68 static void allocate_cubic_colormap (Screen *, Window, Visual *);
69 void remap_image (Screen *, Window, Colormap, XImage *);
74 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
76 return (event->xany.type == MapNotify &&
77 event->xvisibility.window == (Window) window);
80 extern char *progname;
81 Bool grab_verbose_p = False;
84 grabscreen_verbose(void)
86 grab_verbose_p = True;
91 raise_window(Display *dpy, Window window, Bool dont_wait)
94 fprintf(stderr, "%s: raising window 0x%08lX (%s)\n",
95 progname, (unsigned long) window,
96 (dont_wait ? "not waiting" : "waiting"));
100 XWindowAttributes xgwa;
103 memset(&hints, 0, sizeof(hints));
104 XGetWMNormalHints(dpy, window, &hints, &supplied);
105 XGetWindowAttributes (dpy, window, &xgwa);
108 hints.width = xgwa.width;
109 hints.height = xgwa.height;
110 hints.flags |= (PPosition|USPosition|PSize|USSize);
111 XSetWMNormalHints(dpy, window, &hints);
113 XSelectInput (dpy, window, (xgwa.your_event_mask | StructureNotifyMask));
116 XMapRaised(dpy, window);
121 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
128 xscreensaver_window_p (Display *dpy, Window window)
132 unsigned long nitems, bytesafter;
133 unsigned char *version;
134 if (XGetWindowProperty (dpy, window,
135 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
136 0, 1, False, XA_STRING,
137 &type, &format, &nitems, &bytesafter,
147 /* Whether the given window is:
148 - the real root window;
149 - a direct child of the root window;
150 - a direct child of the window manager's decorations.
153 top_level_window_p (Screen *screen, Window window)
155 Display *dpy = DisplayOfScreen (screen);
156 Window root, parent, *kids;
159 if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
165 /* If our direct parent is the real root window, then yes. */
172 unsigned long nitems, bytesafter;
175 /* If our direct parent has the WM_STATE property, then it is a
176 window manager decoration -- yes.
178 if (XGetWindowProperty (dpy, window,
179 XInternAtom (dpy, "WM_STATE", True),
180 0, 0, False, AnyPropertyType,
181 &type, &format, &nitems, &bytesafter,
182 (unsigned char **) &data)
188 /* Else, no. We're deep in a tree somewhere.
194 static Bool error_handler_hit_p = False;
195 static XErrorHandler old_ehandler = 0;
197 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
199 error_handler_hit_p = True;
200 if (error->error_code == BadWindow || error->error_code == BadDrawable)
202 else if (!old_ehandler)
208 return (*old_ehandler) (dpy, error);
212 /* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
213 on a window whose depth is not the maximal depth of the screen? Or
214 something. Anyway, things don't work unless we: use SubwindowMode for
215 the real root window (or a legitimate virtual root window); but do not
216 use SubwindowMode for the xscreensaver window. I make no attempt to
220 use_subwindow_mode_p(Screen *screen, Window window)
222 if (window != VirtualRootWindowOfScreen(screen))
224 else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
231 /* Install the colormaps of all visible windows, deepest first.
232 This should leave the colormaps of the topmost windows installed
233 (if only N colormaps can be installed at a time, then only the
234 topmost N windows will be shown in the right colors.)
237 install_screen_colormaps (Screen *screen)
240 Display *dpy = DisplayOfScreen (screen);
242 Window parent, *kids = 0;
243 unsigned int nkids = 0;
246 old_ehandler = XSetErrorHandler (BadWindow_ehandler);
247 error_handler_hit_p = False;
249 real_root = XRootWindowOfScreen (screen); /* not vroot */
250 if (XQueryTree (dpy, real_root, &real_root, &parent, &kids, &nkids))
251 for (i = 0; i < nkids; i++)
253 XWindowAttributes xgwa;
256 /* #### need to put XmuClientWindow() in xmu.c, sigh... */
257 if (! (client = XmuClientWindow (dpy, kids[i])))
261 XGetWindowAttributes (dpy, client, &xgwa);
262 if (xgwa.colormap && xgwa.map_state == IsViewable)
263 XInstallColormap (dpy, xgwa.colormap);
265 XInstallColormap (dpy, DefaultColormapOfScreen (screen));
267 XSetErrorHandler (old_ehandler);
271 XFree ((char *) kids);
276 grab_screen_image_internal (Screen *screen, Window window)
278 Display *dpy = DisplayOfScreen (screen);
279 XWindowAttributes xgwa;
283 Bool grab_mouse_p = False;
286 real_root = XRootWindowOfScreen (screen); /* not vroot */
287 root_p = (window == real_root);
288 saver_p = xscreensaver_window_p (dpy, window);
290 XGetWindowAttributes (dpy, window, &xgwa);
291 screen = xgwa.screen;
294 /* I think this is redundant, but just to be safe... */
298 /* The only time grabbing the mouse is important is if this program
299 is being run while the saver is locking the screen. */
307 unmap = get_float_resource(dpy, "grabRootDelay", "Seconds");
308 if (unmap <= 0.00001 || unmap > 20) unmap = 2.5;
312 unmap = get_float_resource(dpy, "grabWindowDelay", "Seconds");
313 if (unmap <= 0.00001 || unmap > 20) unmap = 0.66;
315 unmap_time = unmap * 100000;
321 "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n",
322 progname, (unsigned long) window,
323 root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0);
325 fprintf(stderr, "%s: ", progname);
326 describe_visual(stderr, screen, xgwa.visual, False);
327 fprintf (stderr, "\n");
331 if (!root_p && !top_level_window_p (screen, window))
334 fprintf (stderr, "%s: not a top-level window: 0x%08lX: not grabbing\n",
335 progname, (unsigned long) window);
341 XSetWindowBackgroundPixmap (dpy, window, None);
345 /* prevent random viewer of the screen saver (locker) from messing
346 with windows. We don't check whether it succeeded, because what
347 are our options, really... */
348 XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
349 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
350 XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
356 XUnmapWindow (dpy, window);
357 install_screen_colormaps (screen);
359 usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
364 #ifdef HAVE_READ_DISPLAY_EXTENSION
365 if (! read_display(screen, window, 0, saver_p))
366 #endif /* HAVE_READ_DISPLAY_EXTENSION */
368 #ifdef HAVE_READ_DISPLAY_EXTENSION
370 fprintf(stderr, "%s: read_display() failed\n", progname);
371 #endif /* HAVE_READ_DISPLAY_EXTENSION */
373 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
374 raise_window(dpy, window, saver_p);
376 /* Generally it's bad news to call XInstallColormap() explicitly,
377 but this file does a lot of sleazy stuff already... This is to
378 make sure that the window's colormap is installed, even in the
379 case where the window is OverrideRedirect. */
380 if (xgwa.colormap) XInstallColormap (dpy, xgwa.colormap);
387 pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
389 #ifdef HAVE_READ_DISPLAY_EXTENSION
390 if (! read_display(screen, window, pixmap, True))
393 Window real_root = XRootWindowOfScreen (screen); /* not vroot */
397 #ifdef HAVE_READ_DISPLAY_EXTENSION
399 fprintf(stderr, "%s: read_display() failed\n", progname);
400 #endif /* HAVE_READ_DISPLAY_EXTENSION */
402 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
404 gcv.function = GXcopy;
405 gcv.subwindow_mode = IncludeInferiors;
406 gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
407 XCopyArea (dpy, real_root, pixmap, gc,
408 xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
411 XSetWindowBackgroundPixmap (dpy, window, pixmap);
412 XFreePixmap (dpy, pixmap);
416 fprintf (stderr, "%s: grabbed %d bit screen image to %swindow.\n",
417 progname, xgwa.depth,
418 (root_p ? "real root " : ""));
422 XUngrabPointer (dpy, CurrentTime);
423 XUngrabKeyboard (dpy, CurrentTime);
430 /* When we are grabbing and manipulating a screen image, it's important that
431 we use the same colormap it originally had. So, if the screensaver was
432 started with -install, we need to copy the contents of the default colormap
433 into the screensaver's colormap.
436 copy_default_colormap_contents (Screen *screen,
440 Display *dpy = DisplayOfScreen (screen);
441 Visual *from_visual = DefaultVisualOfScreen (screen);
442 Colormap from_cmap = XDefaultColormapOfScreen (screen);
444 XColor *old_colors, *new_colors;
445 unsigned long *pixels;
446 XVisualInfo vi_in, *vi_out;
448 int from_cells, to_cells, max_cells, got_cells;
451 if (from_cmap == to_cmap)
454 vi_in.screen = XScreenNumberOfScreen (screen);
455 vi_in.visualid = XVisualIDFromVisual (from_visual);
456 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
458 if (! vi_out) abort ();
459 from_cells = vi_out [0].colormap_size;
460 XFree ((char *) vi_out);
462 vi_in.screen = XScreenNumberOfScreen (screen);
463 vi_in.visualid = XVisualIDFromVisual (to_visual);
464 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
466 if (! vi_out) abort ();
467 to_cells = vi_out [0].colormap_size;
468 XFree ((char *) vi_out);
470 max_cells = (from_cells > to_cells ? to_cells : from_cells);
472 old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
473 new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
474 pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
475 for (i = 0; i < max_cells; i++)
476 old_colors[i].pixel = i;
477 XQueryColors (dpy, from_cmap, old_colors, max_cells);
479 got_cells = max_cells;
480 allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
482 if (grab_verbose_p && got_cells != max_cells)
483 fprintf(stderr, "%s: got only %d of %d cells\n", progname,
484 got_cells, max_cells);
486 if (got_cells <= 0) /* we're screwed */
488 else if (got_cells == max_cells && /* we're golden */
489 from_cells == to_cells)
490 XStoreColors (dpy, to_cmap, old_colors, got_cells);
491 else /* try to cope... */
493 for (i = 0; i < got_cells; i++)
495 XColor *c = old_colors + i;
497 for (j = 0; j < got_cells; j++)
498 if (pixels[j] == c->pixel)
500 /* only store this color value if this is one of the pixels
501 we were able to allocate. */
502 XStoreColors (dpy, to_cmap, c, 1);
510 fprintf(stderr, "%s: installing copy of default colormap\n", progname);
519 /* The SGI ReadDisplay extension.
520 This extension lets you get back a 24-bit image of the screen, taking into
521 account the colors with which all windows are *currently* displayed, even
522 if those windows have different visuals. Without this extension, presence
523 of windows with different visuals or colormaps will result in technicolor
524 when one tries to grab the screen image.
527 #ifdef HAVE_READ_DISPLAY_EXTENSION
530 read_display (Screen *screen, Window window, Pixmap into_pixmap,
533 Display *dpy = DisplayOfScreen (screen);
534 XWindowAttributes xgwa;
535 int rd_event_base = 0;
536 int rd_error_base = 0;
537 unsigned long hints = 0;
542 Bool remap_p = False;
544 /* Check to see if the server supports the extension, and bug out if not.
546 if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
549 fprintf(stderr, "%s: no XReadDisplay extension\n", progname);
553 /* If this isn't a visual we know how to handle, bug out. We handle:
554 = TrueColor in depths 8, 12, 15, 16, and 32;
555 = PseudoColor and DirectColor in depths 8 and 12.
557 XGetWindowAttributes(dpy, window, &xgwa);
558 class = visual_class (screen, xgwa.visual);
559 if (class == TrueColor)
561 if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 15 &&
562 xgwa.depth != 16 && xgwa.depth != 24 && xgwa.depth != 32)
565 fprintf(stderr, "%s: TrueColor depth %d unsupported\n",
566 progname, xgwa.depth);
570 else if (class == PseudoColor || class == DirectColor)
572 if (xgwa.depth != 8 && xgwa.depth != 12)
575 fprintf(stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
576 progname, xgwa.depth);
580 /* Allocate a TrueColor-like spread of colors for the image. */
585 /* Try and read the screen.
587 hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
588 image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
593 fprintf(stderr, "%s: XReadDisplay() failed\n", progname);
599 fprintf(stderr, "%s: XReadDisplay() returned no data\n", progname);
600 XDestroyImage(image);
604 /* XReadDisplay tends to LIE about the depth of the image it read.
605 It is returning an XImage which has `depth' and `bits_per_pixel'
608 That is, on a 24-bit display, where all visuals claim depth 24, and
609 where XGetImage would return an XImage with depth 24, and where
610 XPutImage will get a BadMatch with images that are not depth 24,
611 XReadDisplay is returning images with depth 32! Fuckwits!
613 So if the visual is of depth 24, but the image came back as depth 32,
614 hack it to be 24 lest we get a BadMatch from XPutImage.
616 I wonder what happens on an 8-bit SGI... Probably it still returns
617 an image claiming depth 32? Certainly it can't be 8. So, let's just
620 if (image->depth == 32 /* && xgwa.depth == 24 */ )
623 /* If the visual of the window/pixmap into which we're going to draw is
624 less deep than the screen itself, then we need to convert the grabbed bits
625 to match the depth by clipping off the less significant bit-planes of each
628 if (image->depth > xgwa.depth)
631 /* We use the same image->data in both images -- that's ok, because
632 since we're reading from B and writing to A, and B uses more bytes
633 per pixel than A, the write pointer won't overrun the read pointer.
635 XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
636 ZPixmap, 0, image->data,
637 xgwa.width, xgwa.height,
642 fprintf(stderr, "%s: out of memory?\n", progname);
647 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
648 progname, image->depth, xgwa.depth);
650 for (y = 0; y < image->height; y++)
651 for (x = 0; x < image->width; x++)
653 /* #### really these shift values should be determined from the
654 mask values -- but that's a pain in the ass, and anyway,
655 this is an SGI-specific extension so hardcoding assumptions
656 about the SGI server's behavior isn't *too* heinous... */
657 unsigned long pixel = XGetPixel(image, x, y);
658 unsigned int r = (pixel & image->red_mask);
659 unsigned int g = (pixel & image->green_mask) >> 8;
660 unsigned int b = (pixel & image->blue_mask) >> 16;
663 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
664 else if (xgwa.depth == 12)
665 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
666 else if (xgwa.depth == 16 || xgwa.depth == 15)
667 /* Gah! I don't understand why these are in the other order. */
668 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
672 XPutPixel(image2, x, y, pixel);
675 XDestroyImage(image);
681 allocate_cubic_colormap (screen, window, xgwa.visual);
682 remap_image (screen, window, xgwa.colormap, image);
685 /* Now actually put the bits into the window or pixmap -- note the design
686 bogosity of this extension, where we've been forced to take 24 bit data
687 from the server to the client, and then push it back from the client to
688 the server, *without alteration*. We should have just been able to tell
689 the server, "put a screen image in this drawable", instead of having to
690 go through the intermediate step of converting it to an Image. Geez.
691 (Assuming that the window is of screen depth; we happen to handle less
692 deep windows, but that's beside the point.)
694 gcv.function = GXcopy;
695 gc = XCreateGC (dpy, window, GCFunction, &gcv);
699 gcv.function = GXcopy;
700 gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
701 XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
702 xgwa.width, xgwa.height);
706 gcv.function = GXcopy;
707 gc = XCreateGC (dpy, window, GCFunction, &gcv);
709 /* Ok, now we'll be needing that window on the screen... */
710 raise_window(dpy, window, dont_wait);
712 /* Plop down the bits... */
713 XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
722 XDestroyImage(image);
726 #endif /* HAVE_READ_DISPLAY_EXTENSION */
729 #ifdef HAVE_READ_DISPLAY_EXTENSION
731 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
732 visual behave like a TrueColor visual of the same depth.
734 #### Duplicated in driver/xscreensaver-getimage.c
737 allocate_cubic_colormap (Screen *screen, Window window, Visual *visual)
739 Display *dpy = DisplayOfScreen (screen);
740 XWindowAttributes xgwa;
742 int nr, ng, nb, cells;
748 XGetWindowAttributes (dpy, window, &xgwa);
749 cmap = xgwa.colormap;
750 depth = visual_depth (screen, visual);
754 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
755 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
756 default: abort(); break;
759 memset(colors, 0, sizeof(colors));
760 for (r = 0; r < (1 << nr); r++)
761 for (g = 0; g < (1 << ng); g++)
762 for (b = 0; b < (1 << nb); b++)
764 i = (r | (g << nr) | (b << (nr + ng)));
766 colors[i].flags = DoRed|DoGreen|DoBlue;
769 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
770 (r << 4) | (r << 1));
771 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
772 (g << 4) | (g << 1));
773 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
774 (b << 8) | (b << 6) | (b << 4) |
779 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
780 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
781 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
788 int interleave = cells / 8; /* skip around, rather than allocating in
789 order, so that we get better coverage if
790 we can't allocated all of them. */
791 for (j = 0; j < interleave; j++)
792 for (i = 0; i < cells; i += interleave)
793 if (XAllocColor (dpy, cmap, &colors[i + j]))
797 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
798 progname, allocated, cells);
802 /* Find the pixel index that is closest to the given color
803 (using linear distance in RGB space -- which is far from the best way.)
805 #### Duplicated in driver/xscreensaver-getimage.c
808 find_closest_pixel (XColor *colors, int ncolors,
809 unsigned long r, unsigned long g, unsigned long b)
811 unsigned long distance = ~0;
816 for (i = 0; i < ncolors; i++)
821 rd = r - colors[i].red;
822 gd = g - colors[i].green;
823 bd = b - colors[i].blue;
824 if (rd < 0) rd = -rd;
825 if (gd < 0) gd = -gd;
826 if (bd < 0) bd = -bd;
827 d = (rd << 1) + (gd << 2) + bd;
842 /* Given an XImage with 8-bit or 12-bit RGB data, convert it to be
843 displayable with the given X colormap. The farther from a perfect
844 color cube the contents of the colormap are, the lossier the
845 transformation will be. No dithering is done.
847 #### Duplicated in driver/xscreensaver-getimage.c
850 remap_image (Screen *screen, Window window, Colormap cmap, XImage *image)
852 Display *dpy = DisplayOfScreen (screen);
853 unsigned long map[4097];
858 if (image->depth == 8)
860 else if (image->depth == 12)
865 memset(map, -1, sizeof(*map));
866 memset(colors, -1, sizeof(*colors));
868 for (i = 0; i < cells; i++)
870 XQueryColors (dpy, cmap, colors, cells);
873 fprintf(stderr, "%s: building table for %d bit image\n",
874 progname, image->depth);
876 for (i = 0; i < cells; i++)
878 unsigned short r, g, b;
882 /* "RRR GGG BB" In an 8 bit map. Convert that to
883 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
889 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
890 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
891 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
892 (b << 6) | (b << 4) | (b << 2) | b);
896 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
897 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
900 g = (i & 0x0F0) >> 4;
901 b = (i & 0xF00) >> 8;
903 r = (r << 12) | (r << 8) | (r << 4) | r;
904 g = (g << 12) | (g << 8) | (g << 4) | g;
905 b = (b << 12) | (b << 8) | (b << 4) | b;
908 map[i] = find_closest_pixel (colors, cells, r, g, b);
912 fprintf(stderr, "%s: remapping colors in %d bit image\n",
913 progname, image->depth);
915 for (y = 0; y < image->height; y++)
916 for (x = 0; x < image->width; x++)
918 unsigned long pixel = XGetPixel(image, x, y);
919 if (pixel >= cells) abort();
920 XPutPixel(image, x, y, map[pixel]);
925 #endif /* HAVE_READ_DISPLAY_EXTENSION */