+ map[i] = find_closest_pixel (colors, cells, r, g, b);
+ }
+
+ if (verbose_p)
+ fprintf(stderr, "%s: remapping colors in %d bit image\n",
+ progname, image->depth);
+
+ for (y = 0; y < image->height; y++)
+ for (x = 0; x < image->width; x++)
+ {
+ unsigned long pixel = XGetPixel(image, x, y);
+ if (pixel >= cells) abort();
+ XPutPixel(image, x, y, map[pixel]);
+ }
+}
+
+
+/* If the file has a PPM (P6) on it, read it and return an XImage.
+ Otherwise, rewind the fd back to the beginning, and return 0.
+ */
+static XImage *
+maybe_read_ppm (Screen *screen, Visual *visual,
+ const char *filename, FILE *in, Bool verbose_p)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ int depth = visual_depth (screen, visual);
+ struct stat st;
+ char *buf = 0;
+ int bufsiz = 0;
+ char *s, dummy;
+ int i, j;
+ int x, y, w, h, maxval;
+ XImage *ximage = 0;
+
+ if (fstat (fileno (in), &st))
+ goto FAIL;
+
+ bufsiz = st.st_size;
+ buf = (char *) malloc (bufsiz + 1);
+ if (!buf)
+ {
+ fprintf (stderr, "%s: out of memory loading %d byte PPM file %s\n",
+ progname, bufsiz, filename);
+ goto FAIL;
+ }
+
+ if (! (s = fgets (buf, bufsiz, in))) /* line 1 */
+ goto FAIL;
+
+ if (!strncmp (buf, "\107\111", 2))
+ {
+ fprintf (stderr, "%s: %s: sorry, GIF files not supported"
+ " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
+ progname, filename);
+ goto FAIL;
+ }
+ else if (!strncmp (buf, "\211\120", 2))
+ {
+ fprintf (stderr, "%s: %s: sorry, PNG files not supported"
+ " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
+ progname, filename);
+ goto FAIL;
+ }
+
+ if (strncmp (s, "P6", 2))
+ goto FAIL;
+
+ if (! (s = fgets (buf, bufsiz, in))) /* line 2 */
+ goto FAIL;
+ if (2 != sscanf (s, " %d %d %c", &w, &h, &dummy))
+ {
+ fprintf (stderr, "%s: %s: invalid PPM (line 2)\n", progname, filename);
+ goto FAIL;
+ }
+
+ if (! (s = fgets (buf, bufsiz, in))) /* line 3 */
+ goto FAIL;
+ if (1 != sscanf (s, " %d %c", &maxval, &dummy))
+ {
+ fprintf (stderr, "%s: %s: invalid PPM (line 3)\n", progname, filename);
+ goto FAIL;
+ }
+ if (maxval != 255)
+ {
+ fprintf (stderr, "%s: %s: unparsable PPM: maxval is %d\n",
+ progname, filename, maxval);
+ goto FAIL;
+ }
+
+ ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
+ w, h, 8, 0);
+ if (ximage)
+ ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
+ if (!ximage || !ximage->data)
+ {
+ fprintf (stderr, "%s: out of memory loading %dx%d PPM file %s\n",
+ progname, ximage->width, ximage->height, filename);
+ goto FAIL;
+ }
+
+ s = buf;
+ j = bufsiz;
+ while ((i = fread (s, 1, j, in)) > 0)
+ s += i, j -= i;
+
+ i = 0;
+ for (y = 0; y < ximage->height; y++)
+ for (x = 0; x < ximage->width; x++)
+ {
+ unsigned char r = buf[i++];
+ unsigned char g = buf[i++];
+ unsigned char b = buf[i++];
+ unsigned long pixel;
+
+ if (depth > 16)
+ pixel = (r << 16) | (g << 8) | b;
+ else if (depth == 8)
+ pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
+ else if (depth == 12)
+ pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
+ else if (depth == 16 || depth == 15)
+ pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
+ else
+ abort();
+
+ XPutPixel (ximage, x, y, pixel);
+ }
+
+ free (buf);
+ return ximage;
+
+ FAIL:
+ if (buf) free (buf);
+ if (ximage && ximage->data)
+ {
+ free (ximage->data);
+ ximage->data = 0;
+ }
+ if (ximage) XDestroyImage (ximage);
+ fseek (in, 0, SEEK_SET);
+ return 0;
+}
+
+
+typedef struct {
+ struct jpeg_error_mgr pub; /* this is what passes for subclassing in C */
+ const char *filename;
+ Screen *screen;
+ Visual *visual;
+ Drawable drawable;
+ Colormap cmap;
+} getimg_jpg_error_mgr;
+
+
+static void
+jpg_output_message (j_common_ptr cinfo)
+{
+ getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
+ char buf[JMSG_LENGTH_MAX];
+ cinfo->err->format_message (cinfo, buf);
+ fprintf (stderr, "%s: %s: %s\n", progname, err->filename, buf);
+}
+
+
+static void
+jpg_error_exit (j_common_ptr cinfo)
+{
+ getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
+ cinfo->err->output_message (cinfo);
+ draw_colorbars (err->screen, err->visual, err->drawable, err->cmap,
+ 0, 0, 0, 0);
+ XSync (DisplayOfScreen (err->screen), False);
+ exit (1);
+}
+
+
+/* Reads a JPEG file, returns an RGB XImage of it.
+ */
+static XImage *
+read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
+ Colormap cmap, const char *filename, Bool verbose_p)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ int depth = visual_depth (screen, visual);
+
+ FILE *in = 0;
+ XImage *ximage = 0;
+ struct jpeg_decompress_struct cinfo;
+ getimg_jpg_error_mgr jerr;
+ JSAMPARRAY scanbuf = 0;
+ int y;
+
+ jerr.filename = filename;
+ jerr.screen = screen;
+ jerr.visual = visual;
+ jerr.drawable = drawable;
+ jerr.cmap = cmap;
+
+ if (! (depth >= 15 || depth == 12 || depth == 8))
+ {
+ fprintf (stderr, "%s: unsupported depth: %d\n", progname, depth);
+ goto FAIL;
+ }
+
+ in = fopen (filename, "rb");
+ if (!in)
+ {
+ fprintf (stderr, "%s: %s: unreadable\n", progname, filename);
+ goto FAIL;
+ }
+
+ /* Check to see if it's a PPM, and if so, read that instead of using
+ the JPEG library. Yeah, this is all modular and stuff.
+ */
+ if ((ximage = maybe_read_ppm (screen, visual, filename, in, verbose_p)))
+ {
+ fclose (in);
+ return ximage;
+ }
+
+ cinfo.err = jpeg_std_error (&jerr.pub);
+ jerr.pub.output_message = jpg_output_message;
+ jerr.pub.error_exit = jpg_error_exit;
+
+ jpeg_create_decompress (&cinfo);
+ jpeg_stdio_src (&cinfo, in);
+ jpeg_read_header (&cinfo, TRUE);
+
+ /* set some decode parameters */
+ cinfo.out_color_space = JCS_RGB;
+ cinfo.quantize_colors = FALSE;
+
+ jpeg_start_decompress (&cinfo);
+
+ ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
+ cinfo.output_width, cinfo.output_height,
+ 8, 0);
+ if (ximage)
+ ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
+
+ if (ximage && ximage->data)
+ scanbuf = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
+ cinfo.rec_outbuf_height *
+ cinfo.output_width *
+ cinfo.output_components,
+ 1);
+ if (!ximage || !ximage->data || !scanbuf)
+ {
+ fprintf (stderr, "%s: out of memory loading %dx%d file %s\n",
+ progname, ximage->width, ximage->height, filename);
+ goto FAIL;
+ }
+
+ y = 0;
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ int n = jpeg_read_scanlines (&cinfo, scanbuf, 1);
+ int i;
+ for (i = 0; i < n; i++)