1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-98 Artsoft Entertainment *
8 * phone: ++49 +521 290471 *
9 * email: aeglos@valinor.owl.de *
10 *----------------------------------------------------------*
12 ***********************************************************/
29 /* extra colors to try allocating in private color maps to minimise flashing */
30 #define NOFLASH_COLORS 256
32 static void Image_to_Mask(Image *image)
34 unsigned char *src_ptr, *dst_ptr, *dst_ptr2;
35 unsigned int bytes_per_row;
37 unsigned char bitmask;
39 bytes_per_row = (image->width + 7) / 8;
40 src_ptr = image->data;
41 dst_ptr = image->data_mask;
43 /* create bitmap data which can be used by 'XCreateBitmapFromData()'
44 * directly to create a pixmap of depth 1 for use as a clip mask for
45 * the corresponding image pixmap
48 for (y=0; y<image->height; y++)
50 bitmask = 0x01; /* start with leftmost bit in the byte */
51 dst_ptr2 = dst_ptr; /* start with leftmost byte in the row */
53 for (x=0; x<image->width; x++)
55 if (*src_ptr++) /* source pixel solid? (pixel index != 0) */
56 *dst_ptr2 |= bitmask; /* then write a bit into the image mask */
58 if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
60 bitmask = 0x01; /* start again with leftmost bit position */
61 dst_ptr2++; /* continue with next byte in image mask */
65 dst_ptr += bytes_per_row; /* continue with leftmost byte of next row */
69 static boolean XImage_to_Pixmap(Display *display, Window parent,
70 XImageInfo *ximageinfo, Image *image)
72 ximageinfo->pixmap = XCreatePixmap(display, parent,
73 ximageinfo->ximage->width,
74 ximageinfo->ximage->height,
77 /* build and cache the GC */
83 gcv.function = GXcopy;
84 ximageinfo->gc = XCreateGC(ximageinfo->display, ximageinfo->pixmap,
88 XPutImage(ximageinfo->display, ximageinfo->pixmap, ximageinfo->gc,
89 ximageinfo->ximage, 0, 0, 0, 0,
90 ximageinfo->ximage->width, ximageinfo->ximage->height);
92 ximageinfo->pixmap_mask = XCreateBitmapFromData(ximageinfo->display,
98 return (ximageinfo->pixmap != None && ximageinfo->pixmap_mask != None);
101 /* find the best pixmap depth supported by the server for a particular
102 * visual and return that depth.
105 static unsigned int bitsPerPixelAtDepth(Display *display, int screen,
108 XPixmapFormatValues *xf;
111 xf = XListPixmapFormats(display, &nxf);
112 for (a = 0; a < nxf; a++)
114 if (xf[a].depth == depth)
117 bpp = xf[a].bits_per_pixel;
119 return (unsigned int) bpp;
124 /* this should never happen; if it does, we're in trouble */
126 fprintf(stderr, "bitsPerPixelAtDepth: Can't find pixmap depth info!\n");
130 XImageInfo *Image_to_XImage(Display *display, int screen, Visual *visual,
131 unsigned int ddepth, Image *image)
133 static XColor xcolor_private[NOFLASH_COLORS];
134 static int colorcell_used[NOFLASH_COLORS];
135 static Colormap global_cmap = 0;
136 static Pixel *global_cmap_index;
137 static int num_cmap_entries, free_cmap_entries;
138 static boolean private_cmap = FALSE;
139 Pixel *redvalue, *greenvalue, *bluevalue;
140 unsigned int a, c = 0, x, y, dpixlen, dbits;
142 XImageInfo *ximageinfo;
144 /* for building image */
145 byte *data, *src_ptr, *dst_ptr;
149 if (visual == DefaultVisual(display, screen))
150 global_cmap = DefaultColormap(display, screen);
153 global_cmap = XCreateColormap(display, RootWindow(display, screen),
159 xcolor.flags = DoRed | DoGreen | DoBlue;
160 redvalue = greenvalue = bluevalue = NULL;
161 ximageinfo = (XImageInfo *)checked_malloc(sizeof(XImageInfo));
162 ximageinfo->display = display;
163 ximageinfo->screen = screen;
164 ximageinfo->depth = 0;
165 ximageinfo->drawable = None;
166 ximageinfo->index = NULL;
167 ximageinfo->rootimage = FALSE;
168 ximageinfo->gc = NULL;
169 ximageinfo->ximage = NULL;
171 switch (visual->class)
177 unsigned int redcolors, greencolors, bluecolors;
178 unsigned int redstep, greenstep, bluestep;
179 unsigned int redbottom, greenbottom, bluebottom;
180 unsigned int redtop, greentop, bluetop;
182 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
183 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
184 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
186 ximageinfo->cmap = global_cmap;
188 retry_direct: /* tag we hit if a DirectColor allocation fails on
189 * default colormap */
191 /* calculate number of distinct colors in each band */
193 redcolors = greencolors = bluecolors = 1;
194 for (pixval=1; pixval; pixval <<= 1)
196 if (pixval & visual->red_mask)
198 if (pixval & visual->green_mask)
200 if (pixval & visual->blue_mask)
206 if (redcolors > visual->map_entries ||
207 greencolors > visual->map_entries ||
208 bluecolors > visual->map_entries)
209 Error(ERR_WARN, "inconsistency in color information");
211 redstep = 256 / redcolors;
212 greenstep = 256 / greencolors;
213 bluestep = 256 / bluecolors;
214 redbottom = greenbottom = bluebottom = 0;
215 redtop = greentop = bluetop = 0;
216 for (a=0; a<visual->map_entries; a++)
219 redtop = redbottom + redstep;
220 if (greenbottom < 256)
221 greentop = greenbottom + greenstep;
222 if (bluebottom < 256)
223 bluetop = bluebottom + bluestep;
225 xcolor.red = (redtop - 1) << 8;
226 xcolor.green = (greentop - 1) << 8;
227 xcolor.blue = (bluetop - 1) << 8;
228 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
230 /* if an allocation fails for a DirectColor default visual then
231 * we should create a private colormap and try again.
234 if ((visual->class == DirectColor) &&
235 (visual == DefaultVisual(display, screen)))
237 global_cmap = XCopyColormapAndFree(display, global_cmap);
238 ximageinfo->cmap = global_cmap;
244 /* something completely unexpected happened
247 fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n");
248 free((byte *)redvalue);
249 free((byte *)greenvalue);
250 free((byte *)bluevalue);
251 free((byte *)ximageinfo);
255 /* fill in pixel values for each band at this intensity
258 while ((redbottom < 256) && (redbottom < redtop))
259 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
260 while ((greenbottom < 256) && (greenbottom < greentop))
261 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
262 while ((bluebottom < 256) && (bluebottom < bluetop))
263 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
270 ximageinfo->cmap = global_cmap;
272 (Pixel *)checked_malloc(sizeof(Pixel) * image->rgb.used);
275 for (a=0; a<image->rgb.used; a++)
278 for (a=0; a<MAX_COLORS; a++)
285 if (!image->rgb.color_used[a])
288 xcolor.red = *(image->rgb.red + a);
289 xcolor.green = *(image->rgb.green + a);
290 xcolor.blue = *(image->rgb.blue + a);
292 /* look if this color already exists in our colormap */
293 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
298 Error(ERR_RETURN, "switching to private colormap");
300 /* we just filled up the default colormap -- get a private one
301 which contains all already allocated colors */
303 global_cmap = XCopyColormapAndFree(display, global_cmap);
304 ximageinfo->cmap = global_cmap;
307 /* allocate the rest of the color cells read/write */
309 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
310 for (i=0; i<NOFLASH_COLORS; i++)
311 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
312 global_cmap_index + i, 1))
314 num_cmap_entries = free_cmap_entries = i;
317 printf("We've got %d free colormap entries.\n", free_cmap_entries);
320 /* to minimize colormap flashing, copy default colors and try
321 to keep them as near as possible to the old values */
323 for(i=0; i<num_cmap_entries; i++)
325 xcolor2.pixel = *(global_cmap_index + i);
326 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
327 XStoreColor(display, global_cmap, &xcolor2);
328 xcolor_private[xcolor2.pixel] = xcolor2;
329 colorcell_used[xcolor2.pixel] = FALSE;
332 /* now we have the default colormap private: all colors we
333 successfully allocated so far are read-only, which is okay,
334 because we don't want to change them anymore -- if we need
335 an existing color again, we get it by XAllocColor; all other
336 colors are read/write and we can set them by XStoreColor,
337 but we will try to overwrite those color cells with our new
338 color which are as close as possible to our new color */
341 /* look for an existing default color close the one we want */
348 for (i=num_cmap_entries-1; i>=0; i--)
350 xcolor2.pixel = *(global_cmap_index + i);
351 xcolor2 = xcolor_private[xcolor2.pixel];
353 if (colorcell_used[xcolor2.pixel])
356 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
357 (xcolor.green & mask) == (xcolor2.green & mask) &&
358 (xcolor.blue & mask) == (xcolor2.blue & mask))
361 printf("replacing color cell %ld with a close color\n",
372 mask = (mask << 1) & 0xffff;
375 if (!color_found) /* no more free color cells */
377 printf("Sorry, cannot allocate enough colors!\n");
381 xcolor.pixel = xcolor2.pixel;
382 xcolor_private[xcolor.pixel] = xcolor;
383 colorcell_used[xcolor.pixel] = TRUE;
384 XStoreColor(display, ximageinfo->cmap, &xcolor);
388 *(ximageinfo->index + a) = xcolor.pixel;
392 printf("still %d free colormap entries\n", free_cmap_entries);
395 ximageinfo->no = a; /* number of pixels allocated for this image */
399 Error(ERR_RETURN, "display type not supported");
400 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
406 printf(" CONVERTING IMAGE TO XIMAGE (IMAGE COLORMAP) IN %.2f SECONDS\n",
407 (float)(count2-count1)/1000.0);
411 /* CREATE IMAGE ITSELF */
412 /* modify image data to match visual and colormap */
414 dbits = bitsPerPixelAtDepth(display, screen, ddepth);/* bits per pixel */
415 dpixlen = (dbits + 7) / 8; /* bytes per pixel */
417 ximageinfo->ximage = XCreateImage(display, visual, ddepth, ZPixmap, 0,
418 NULL, image->width, image->height,
419 8, image->width * dpixlen);
421 data = checked_malloc(image->width * image->height * dpixlen);
422 ximageinfo->depth = ddepth;
423 ximageinfo->ximage->data = (char *)data;
424 ximageinfo->ximage->byte_order = MSBFirst;
425 src_ptr = image->data;
428 switch (visual->class)
435 for (y=0; y<image->height; y++)
437 for (x=0; x<image->width; x++)
441 redvalue[image->rgb.red[pixval] >> 8] |
442 greenvalue[image->rgb.green[pixval] >> 8] |
443 bluevalue[image->rgb.blue[pixval] >> 8];
444 valToMem(pixval, dst_ptr, dpixlen);
453 if (dpixlen == 1) /* most common */
455 for (y=0; y<image->height; y++)
456 for (x=0; x<image->width; x++)
457 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
459 else /* less common */
461 for (y=0; y<image->height; y++)
463 for (x=0; x<image->width; x++)
465 valToMem(ximageinfo->index[c + *src_ptr++], dst_ptr, dpixlen);
474 Error(ERR_RETURN, "display type not supported");
475 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
481 free((byte *)redvalue);
482 free((byte *)greenvalue);
483 free((byte *)bluevalue);
489 /* free up anything cached in the local Ximage structure.
492 void freeXImage(Image *image, XImageInfo *ximageinfo)
494 if (ximageinfo->index != NULL) /* if we allocated colors */
496 if (ximageinfo->no > 0 && !ximageinfo->rootimage) /* don't free root colors */
497 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
499 free(ximageinfo->index);
502 XFreeGC(ximageinfo->display, ximageinfo->gc);
503 free((byte *)ximageinfo->ximage->data);
504 ximageinfo->ximage->data= NULL;
505 XDestroyImage(ximageinfo->ximage);
506 free((byte *)ximageinfo);
507 /* should we free private color map to ??? */
510 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
513 unsigned int bytes_per_row;
514 const unsigned int bytes_per_pixel = 1;
518 Error(ERR_EXIT, "images with more than 256 colors are not supported");
521 image = checked_malloc(sizeof(Image));
522 image->data = checked_malloc(width * height * bytes_per_pixel);
523 image->width = width;
524 image->height = height;
525 image->depth = depth;
527 for (i=0; i<MAX_COLORS; i++)
528 image->rgb.color_used[i] = FALSE;
530 bytes_per_row = (width + 7) / 8;
531 image->data_mask = checked_calloc(bytes_per_row * height);
536 void freeImage(Image *image)
539 free(image->data_mask);
543 /* ------------------------------------------------------------------------- */
547 int Read_PCX_to_Pixmaps(Display *display, Window window, char *filename,
548 Pixmap *pixmap, Pixmap *pixmap_mask)
551 XImageInfo *ximageinfo;
554 Image *image, *image_mask;
555 XImageInfo *ximageinfo, *ximageinfo_mask;
566 if ((image = Read_PCX_to_Image(filename)) == NULL)
567 return PCX_FileInvalid;
571 printf(" LOADING '%s' IN %.2f SECONDS\n",
572 filename, (float)(count2-count1)/1000.0);
576 /* create image mask */
577 Image_to_Mask(image);
581 printf(" CONVERTING IMAGE TO MASK IN %.2f SECONDS\n",
582 (float)(count2-count1)/1000.0);
586 screen = DefaultScreen(display);
587 visual = DefaultVisual(display, screen);
588 depth = DefaultDepth(display, screen);
590 /* convert internal image structure to X11 XImage */
591 if (!(ximageinfo = Image_to_XImage(display, screen, visual, depth, image)))
593 fprintf(stderr, "Cannot convert Image to XImage.\n");
599 printf(" CONVERTING IMAGE TO XIMAGE IN %.2f SECONDS\n",
600 (float)(count2-count1)/1000.0);
604 if (ximageinfo->cmap != DefaultColormap(display, screen))
605 XSetWindowColormap(display, window, ximageinfo->cmap);
607 /* convert XImage to Pixmap */
608 if (!(XImage_to_Pixmap(display, window, ximageinfo, image)))
610 fprintf(stderr, "Cannot convert XImage to Pixmap.\n");
616 printf(" CONVERTING IMAGE TO PIXMAP IN %.2f SECONDS\n",
617 (float)(count2-count1)/1000.0);
621 *pixmap = ximageinfo->pixmap;
622 *pixmap_mask = ximageinfo->pixmap_mask;