1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * image.c                                                  *
12 ***********************************************************/
13 
14 #include "image.h"
15 #include "pcx.h"
16 #include "misc.h"
17 #include "setup.h"
18 
19 
20 /* ========================================================================= */
21 /* PLATFORM SPECIFIC IMAGE FUNCTIONS                                         */
22 /* ========================================================================= */
23 
24 #if defined(TARGET_X11)
25 
26 /* for MS-DOS/Allegro, exclude all except newImage() and freeImage() */
27 
newImage(unsigned int width,unsigned int height,unsigned int depth)28 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
29 {
30   Image *image;
31   unsigned int bytes_per_pixel = (depth + 7) / 8;
32   int i;
33 
34   image = checked_calloc(sizeof(Image));
35   image->data = checked_calloc(width * height * bytes_per_pixel);
36   image->width = width;
37   image->height = height;
38   image->depth = depth;
39   image->bytes_per_pixel = bytes_per_pixel;
40   image->bytes_per_row = width * bytes_per_pixel;
41 
42   image->rgb.used = 0;
43   for (i = 0; i < MAX_COLORS; i++)
44     image->rgb.color_used[i] = FALSE;
45 
46   image->type = (depth < 8 ? IMAGETYPE_BITMAP :
47 		 depth > 8 ? IMAGETYPE_TRUECOLOR : IMAGETYPE_RGB);
48 
49   return image;
50 }
51 
freeImage(Image * image)52 void freeImage(Image *image)
53 {
54   free(image->data);
55   free(image);
56 }
57 
58 #if defined(PLATFORM_UNIX)
59 
60 /* extra colors to try allocating in private color maps to minimize flashing */
61 #define NOFLASH_COLORS 256
62 
63 /* architecture independent value <-> memory conversions;
64    note: the internal format is big endian */
65 
66 #define memory_to_value(ptr, len) (					    \
67 (len) == 1 ? (unsigned long)(                 *( (byte *)(ptr))         ) : \
68 (len) == 2 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr))   ))<< 8)   \
69 			  + (                 *(((byte *)(ptr))+1)      ) : \
70 (len) == 3 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr))   ))<<16)   \
71 			  + (((unsigned long)(*(((byte *)(ptr))+1)))<< 8)   \
72 			  + (                 *(((byte *)(ptr))+2)      ) : \
73 	     (unsigned long)(((unsigned long)(*( (byte *)(ptr))   ))<<24)   \
74 			  + (((unsigned long)(*(((byte *)(ptr))+1)))<<16)   \
75 			  + (((unsigned long)(*(((byte *)(ptr))+2)))<< 8)   \
76 			  + (                 *(((byte *)(ptr))+3)      ) )
77 
78 
79 #define value_to_memory(value, ptr, len) (				\
80 (len) == 1 ? (*( (byte *)(ptr)   ) = ( value     ) ) :			\
81 (len) == 2 ? (*( (byte *)(ptr)   ) = (((unsigned long)(value))>> 8),	\
82 	      *(((byte *)(ptr))+1) = ( value     ) ) :			\
83 (len) == 3 ? (*( (byte *)(ptr)   ) = (((unsigned long)(value))>>16),	\
84 	      *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8),	\
85 	      *(((byte *)(ptr))+2) = ( value     ) ) :			\
86              (*( (byte *)(ptr)   ) = (((unsigned long)(value))>>24),	\
87 	      *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16),	\
88 	      *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8),	\
89 	      *(((byte *)(ptr))+3) = ( value     ) ))
90 
Image_to_Mask(Image * image,Display * display,Window window)91 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
92 {
93   byte *src_ptr, *dst_ptr, *dst_ptr2;
94   unsigned int bytes_per_row;
95   unsigned int x, y, i;
96   byte bitmask;
97   byte *mask_data;
98   Pixmap mask_pixmap;
99 
100   bytes_per_row = (image->width + 7) / 8;
101   mask_data = checked_calloc(bytes_per_row * image->height);
102 
103   src_ptr = image->data;
104   dst_ptr = mask_data;
105 
106   /* create bitmap data which can be used by 'XCreateBitmapFromData()'
107    * directly to create a pixmap of depth 1 for use as a clip mask for
108    * the corresponding image pixmap
109    */
110 
111   for (y = 0; y < image->height; y++)
112   {
113     bitmask = 0x01;		/* start with leftmost bit in the byte     */
114     dst_ptr2 = dst_ptr;		/* start with leftmost byte in the row     */
115 
116     for (x = 0; x < image->width; x++)
117     {
118       for (i = 0; i < image->bytes_per_pixel; i++)
119 	if (*src_ptr++)		/* source pixel solid? (pixel index != 0)  */
120 	  *dst_ptr2 |= bitmask;	/* then write a bit into the image mask    */
121 
122       if ((bitmask <<= 1) == 0)	/* bit at rightmost byte position reached? */
123       {
124 	bitmask = 0x01;		/* start again with leftmost bit position  */
125 	dst_ptr2++;		/* continue with next byte in image mask   */
126       }
127     }
128 
129     dst_ptr += bytes_per_row;	/* continue with leftmost byte of next row */
130   }
131 
132   if ((mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
133 					   image->width, image->height))
134       == None)
135     Error(ERR_EXIT, "Image_to_Mask(): XCreateBitmapFromData() failed");
136 
137   free(mask_data);
138 
139   return mask_pixmap;
140 }
141 
Pixmap_to_Mask(Pixmap src_pixmap,int src_width,int src_height)142 Pixmap Pixmap_to_Mask(Pixmap src_pixmap, int src_width, int src_height)
143 {
144   XImage *src_ximage;
145   byte *src_ptr, *dst_ptr, *dst_ptr2;
146   int bits_per_pixel;
147   int bytes_per_pixel;
148   unsigned int bytes_per_row;
149   unsigned int x, y, i;
150   byte bitmask;
151   byte *mask_data;
152   Pixmap mask_pixmap;
153 
154   /* copy source pixmap to temporary image */
155   if ((src_ximage = XGetImage(display, src_pixmap, 0, 0, src_width, src_height,
156 			      AllPlanes, ZPixmap)) == NULL)
157     Error(ERR_EXIT, "Pixmap_to_Mask(): XGetImage() failed");
158 
159   bits_per_pixel = src_ximage->bits_per_pixel;
160   bytes_per_pixel = (bits_per_pixel + 7) / 8;
161 
162   bytes_per_row = (src_width + 7) / 8;
163   mask_data = checked_calloc(bytes_per_row * src_height);
164 
165   src_ptr = (byte *)src_ximage->data;
166   dst_ptr = mask_data;
167 
168   /* create bitmap data which can be used by 'XCreateBitmapFromData()'
169    * directly to create a pixmap of depth 1 for use as a clip mask for
170    * the corresponding image pixmap
171    */
172 
173   for (y = 0; y < src_height; y++)
174   {
175     bitmask = 0x01;		/* start with leftmost bit in the byte     */
176     dst_ptr2 = dst_ptr;		/* start with leftmost byte in the row     */
177 
178     for (x = 0; x < src_width; x++)
179     {
180       for (i = 0; i < bytes_per_pixel; i++)
181 	if (*src_ptr++)		/* source pixel solid? (pixel index != 0)  */
182 	  *dst_ptr2 |= bitmask;	/* then write a bit into the image mask    */
183 
184       if ((bitmask <<= 1) == 0)	/* bit at rightmost byte position reached? */
185       {
186 	bitmask = 0x01;		/* start again with leftmost bit position  */
187 	dst_ptr2++;		/* continue with next byte in image mask   */
188       }
189     }
190 
191     dst_ptr += bytes_per_row;	/* continue with leftmost byte of next row */
192   }
193 
194   if ((mask_pixmap = XCreateBitmapFromData(display, window->drawable,
195 					   (char *)mask_data,
196 					   src_width, src_height)) == None)
197     Error(ERR_EXIT, "Pixmap_to_Mask(): XCreateBitmapFromData() failed");
198 
199   free(mask_data);
200 
201   return mask_pixmap;
202 }
203 
bitsPerPixelAtDepth(Display * display,int screen,int depth)204 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
205 {
206   XPixmapFormatValues *pixmap_format;
207   int i, num_pixmap_formats, bits_per_pixel = -1;
208 
209   /* get Pixmap formats supported by the X server */
210   pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
211 
212   /* find format that matches the given depth */
213   for (i = 0; i < num_pixmap_formats; i++)
214     if (pixmap_format[i].depth == depth)
215       bits_per_pixel = pixmap_format[i].bits_per_pixel;
216 
217   XFree(pixmap_format);
218 
219   if (bits_per_pixel == -1)
220     Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
221 
222   return bits_per_pixel;
223 }
224 
Image_to_Pixmap(Display * display,int screen,Visual * visual,Window window,GC gc,int depth,Image * image)225 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
226 			    Window window, GC gc, int depth, Image *image)
227 {
228   static XColor xcolor_private[NOFLASH_COLORS];
229   static int colorcell_used[NOFLASH_COLORS];
230   static Colormap global_cmap = 0;
231   static Pixel *global_cmap_index;
232   static int num_cmap_entries, free_cmap_entries;
233   static boolean private_cmap = FALSE;
234   Pixel *redvalue, *greenvalue, *bluevalue;
235   unsigned int display_bytes_per_pixel, display_bits_per_pixel;
236   unsigned int a, c = 0, x, y;
237   XColor xcolor;
238   XImage *ximage;
239   XImageInfo *ximageinfo;
240   byte *src_ptr, *dst_ptr;
241   char *error = "Image_to_Pixmap(): %s";
242 
243   if (image->type == IMAGETYPE_TRUECOLOR && depth == 8)
244   {
245     SetError(error, "cannot handle true-color images on 8-bit display");
246     return NULL;
247   }
248 
249   if (!global_cmap)
250   {
251     if (visual == DefaultVisual(display, screen))
252       global_cmap = DefaultColormap(display, screen);
253     else
254     {
255       global_cmap = XCreateColormap(display, RootWindow(display, screen),
256 				    visual, AllocNone);
257       private_cmap = TRUE;
258     }
259   }
260 
261   xcolor.flags = DoRed | DoGreen | DoBlue;
262   redvalue = greenvalue = bluevalue = NULL;
263   ximageinfo = checked_malloc(sizeof(XImageInfo));
264   ximageinfo->display = display;
265   ximageinfo->depth = depth;
266 
267   switch (visual->class)
268   {
269     case TrueColor:
270     case DirectColor:
271     {
272       Pixel pixval;
273       unsigned int redcolors, greencolors, bluecolors;
274       unsigned int redstep, greenstep, bluestep;
275       unsigned int redbottom, greenbottom, bluebottom;
276       unsigned int redtop, greentop, bluetop;
277 
278       redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
279       greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
280       bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
281 
282       ximageinfo->cmap = global_cmap;
283 
284       retry_direct: /* tag we hit if a DirectColor allocation fails on
285 		     * default colormap */
286 
287       /* calculate number of distinct colors in each band */
288 
289       redcolors = greencolors = bluecolors = 1;
290       for (pixval = 1; pixval; pixval <<= 1)
291       {
292 	if (pixval & visual->red_mask)
293 	  redcolors <<= 1;
294 	if (pixval & visual->green_mask)
295 	  greencolors <<= 1;
296 	if (pixval & visual->blue_mask)
297 	  bluecolors <<= 1;
298       }
299 
300       /* consistency check */
301       if (redcolors > visual->map_entries ||
302 	  greencolors > visual->map_entries ||
303 	  bluecolors > visual->map_entries)
304 	Error(ERR_WARN, "inconsistency in color information");
305 
306       redstep = 256 / redcolors;
307       greenstep = 256 / greencolors;
308       bluestep = 256 / bluecolors;
309       redbottom = greenbottom = bluebottom = 0;
310       redtop = greentop = bluetop = 0;
311 
312       for (a = 0; a < visual->map_entries; a++)
313       {
314 	if (redbottom < 256)
315 	  redtop = redbottom + redstep;
316 	if (greenbottom < 256)
317 	  greentop = greenbottom + greenstep;
318 	if (bluebottom < 256)
319 	  bluetop = bluebottom + bluestep;
320 
321 	xcolor.red = (redtop - 1) << 8;
322 	xcolor.green = (greentop - 1) << 8;
323 	xcolor.blue = (bluetop - 1) << 8;
324 	if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
325 	{
326 	  /* if an allocation fails for a DirectColor default visual then
327 	     we should create a private colormap and try again. */
328 
329 	  if ((visual->class == DirectColor) &&
330 	      (visual == DefaultVisual(display, screen)))
331 	  {
332 	    global_cmap = XCopyColormapAndFree(display, global_cmap);
333 	    ximageinfo->cmap = global_cmap;
334 	    private_cmap = TRUE;
335 
336 	    goto retry_direct;
337 	  }
338 
339 	  /* something completely unexpected happened */
340 
341 	  fprintf(stderr, "Image_to_Pixmap: XAllocColor failed on a TrueColor/Directcolor visual\n");
342 
343           free(redvalue);
344           free(greenvalue);
345           free(bluevalue);
346           free(ximageinfo);
347 
348 	  return NULL;
349 	}
350 
351 	/* fill in pixel values for each band at this intensity */
352 
353 	while ((redbottom < 256) && (redbottom < redtop))
354 	  redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
355 	while ((greenbottom < 256) && (greenbottom < greentop))
356 	  greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
357 	while ((bluebottom < 256) && (bluebottom < bluetop))
358 	  bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
359       }
360 
361       break;
362     }
363 
364     case PseudoColor:
365 
366       ximageinfo->cmap = global_cmap;
367 
368       for (a = 0; a < MAX_COLORS; a++)
369       {
370 	XColor xcolor2;
371 	unsigned short mask;
372 	int color_found;
373   	int i;
374 
375 	if (!image->rgb.color_used[a])
376 	  continue;
377 
378   	xcolor.red = *(image->rgb.red + a);
379   	xcolor.green = *(image->rgb.green + a);
380   	xcolor.blue = *(image->rgb.blue + a);
381 
382   	/* look if this color already exists in our colormap */
383 	if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
384 	{
385 	  if (!private_cmap)
386 	  {
387 	    if (options.verbose)
388 	      Error(ERR_INFO, "switching to private colormap");
389 
390 	    /* we just filled up the default colormap -- get a private one
391 	       which contains all already allocated colors */
392 
393 	    global_cmap = XCopyColormapAndFree(display, global_cmap);
394 	    ximageinfo->cmap = global_cmap;
395 	    private_cmap = TRUE;
396 
397 	    /* allocate the rest of the color cells read/write */
398 	    global_cmap_index =
399 	      (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
400 	    for (i = 0; i < NOFLASH_COLORS; i++)
401 	      if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
402 				    global_cmap_index + i, 1))
403 		break;
404 	    num_cmap_entries = free_cmap_entries = i;
405 
406 	    /*
407 	    printf("We've got %d free colormap entries.\n", free_cmap_entries);
408 	    */
409 
410 	    /* to minimize colormap flashing, copy default colors and try
411 	       to keep them as near as possible to the old values */
412 
413 	    for (i = 0; i < num_cmap_entries; i++)
414 	    {
415 	      xcolor2.pixel = *(global_cmap_index + i);
416 	      XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
417 	      XStoreColor(display, global_cmap, &xcolor2);
418 	      xcolor_private[xcolor2.pixel] = xcolor2;
419 	      colorcell_used[xcolor2.pixel] = FALSE;
420 	    }
421 
422 	    /* now we have the default colormap private: all colors we
423 	       successfully allocated so far are read-only, which is okay,
424 	       because we don't want to change them anymore -- if we need
425 	       an existing color again, we get it by XAllocColor; all other
426 	       colors are read/write and we can set them by XStoreColor,
427 	       but we will try to overwrite those color cells with our new
428 	       color which are as close as possible to our new color */
429 	  }
430 
431   	  /* look for an existing default color close the one we want */
432 
433 	  mask = 0xf000;
434 	  color_found = FALSE;
435 
436 	  while (!color_found)
437 	  {
438 	    for (i = num_cmap_entries - 1; i >= 0; i--)
439 	    {
440 	      xcolor2.pixel = *(global_cmap_index + i);
441 	      xcolor2 = xcolor_private[xcolor2.pixel];
442 
443 	      if (colorcell_used[xcolor2.pixel])
444 		continue;
445 
446 	      if ((xcolor.red & mask) == (xcolor2.red & mask) &&
447 		  (xcolor.green & mask) == (xcolor2.green & mask) &&
448 		  (xcolor.blue & mask) == (xcolor2.blue & mask))
449 	      {
450 		/*
451 		printf("replacing color cell %ld with a close color\n",
452 		       xcolor2.pixel);
453 		       */
454 		color_found = TRUE;
455 		break;
456 	      }
457 	    }
458 
459 	    if (mask == 0x0000)
460 	      break;
461 
462 	    mask = (mask << 1) & 0xffff;
463 	  }
464 
465 	  if (!color_found)		/* no more free color cells */
466 	  {
467 	    SetError(error, "cannot allocate enough color cells");
468 	    return NULL;
469 	  }
470 
471 	  xcolor.pixel = xcolor2.pixel;
472 	  xcolor_private[xcolor.pixel] = xcolor;
473 	  colorcell_used[xcolor.pixel] = TRUE;
474 	  XStoreColor(display, ximageinfo->cmap, &xcolor);
475 	  free_cmap_entries--;
476 	}
477 
478 	*(ximageinfo->index + a) = xcolor.pixel;
479       }
480 
481       /*
482       printf("still %d free colormap entries\n", free_cmap_entries);
483       */
484 
485       ximageinfo->no = a;	/* number of pixels allocated for this image */
486       break;
487 
488     default:
489       Error(ERR_INFO,"DirectColor, TrueColor or PseudoColor display needed");
490       SetError(error, "display class not supported");
491 
492       return NULL;
493   }
494 
495 #if DEBUG_TIMING
496   debug_print_timestamp(2, "   ALLOCATING IMAGE COLORS:   ");
497 #endif
498 
499   /* create XImage from internal image structure and convert it to Pixmap */
500 
501   display_bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
502   display_bytes_per_pixel = (display_bits_per_pixel + 7) / 8;
503 
504   ximage = XCreateImage(display, visual, depth, ZPixmap,
505 			0, NULL, image->width, image->height,
506 			8, image->width * display_bytes_per_pixel);
507   ximage->data =
508     checked_malloc(image->width * image->height * display_bytes_per_pixel);
509   ximage->byte_order = MSBFirst;
510 
511   src_ptr = image->data;
512   dst_ptr = (byte *)ximage->data;
513 
514   switch (visual->class)
515   {
516     case DirectColor:
517     case TrueColor:
518     {
519       Pixel pixval;
520 
521       switch (image->type)
522       {
523         case IMAGETYPE_RGB:
524 	{
525 	  for (y = 0; y < image->height; y++)		/* general case */
526 	  {
527 	    for (x = 0; x < image->width; x++)
528 	    {
529 	      pixval = *src_ptr++;
530 	      pixval =
531 		redvalue[image->rgb.red[pixval] >> 8] |
532 		greenvalue[image->rgb.green[pixval] >> 8] |
533 		bluevalue[image->rgb.blue[pixval] >> 8];
534 	      value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
535 	      dst_ptr += display_bytes_per_pixel;
536 	    }
537 	  }
538 
539 	  break;
540 	}
541 
542         case IMAGETYPE_TRUECOLOR:
543 	{
544 	  for (y = 0; y < image->height; y++)		/* general case */
545 	  {
546 	    for (x = 0; x < image->width; x++)
547 	    {
548 	      pixval = memory_to_value(src_ptr, image->bytes_per_pixel);
549 	      pixval =
550 		redvalue[TRUECOLOR_RED(pixval)] |
551 		greenvalue[TRUECOLOR_GREEN(pixval)] |
552 		bluevalue[TRUECOLOR_BLUE(pixval)];
553 	      value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
554 	      src_ptr += image->bytes_per_pixel;
555 	      dst_ptr += display_bytes_per_pixel;
556 	    }
557 	  }
558 
559 	  break;
560 	}
561 
562         default:
563 	  Error(ERR_INFO, "RGB or TrueColor image needed");
564 	  SetError(error, "image type not supported");
565 
566 	  return NULL;
567       }
568 
569       break;
570     }
571 
572     case PseudoColor:
573     {
574       if (display_bytes_per_pixel == 1)		/* special case */
575       {
576 	for (y = 0; y < image->height; y++)
577 	  for (x = 0; x < image->width; x++)
578 	    *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
579       }
580       else					/* general case */
581       {
582 	for (y = 0; y < image->height; y++)
583 	{
584 	  for (x = 0; x < image->width; x++)
585 	  {
586 	    value_to_memory(ximageinfo->index[c + *src_ptr++],
587 			    dst_ptr, display_bytes_per_pixel);
588 	    dst_ptr += display_bytes_per_pixel;
589 	  }
590 	}
591       }
592 
593       break;
594     }
595 
596     default:
597       Error(ERR_INFO,"DirectColor, TrueColor or PseudoColor display needed");
598       SetError(error, "display class not supported");
599 
600       return NULL;
601   }
602 
603   if (redvalue)
604   {
605     free((byte *)redvalue);
606     free((byte *)greenvalue);
607     free((byte *)bluevalue);
608   }
609 
610 #if DEBUG_TIMING
611   debug_print_timestamp(2, "   CONVERTING IMAGE TO XIMAGE:");
612 #endif
613 
614   ximageinfo->pixmap = XCreatePixmap(display, window,
615 				     ximage->width, ximage->height,
616 				     ximageinfo->depth);
617 
618   XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
619 	    ximage, 0, 0, 0, 0, ximage->width, ximage->height);
620 
621   X11DestroyImage(ximage);
622 
623   return ximageinfo;
624 }
625 
626 /*
627   -----------------------------------------------------------------------------
628   ZoomPixmap
629 
630   Important note: The scaling code currently only supports scaling of the image
631   up or down by a power of 2 -- other scaling factors currently not supported!
632   Also not supported is scaling of pixmap masks (with depth 1); to scale them,
633   better use Pixmap_to_Mask() for now.
634   -----------------------------------------------------------------------------
635 */
636 
ZoomPixmap(Display * display,GC gc,Pixmap src_pixmap,Pixmap dst_pixmap,int src_width,int src_height,int dst_width,int dst_height)637 void ZoomPixmap(Display *display, GC gc, Pixmap src_pixmap, Pixmap dst_pixmap,
638 		int src_width, int src_height,
639 		int dst_width, int dst_height)
640 {
641   XImage *src_ximage, *dst_ximage;
642   byte *src_ptr, *dst_ptr;
643   int bits_per_pixel;
644   int bytes_per_pixel;
645   int x, y, xx, yy, i;
646   int row_skip, col_skip;
647   int zoom_factor;
648   boolean scale_down = (src_width > dst_width);
649 
650   if (scale_down)
651   {
652 #if 1
653     zoom_factor = MIN(src_width / dst_width, src_height / dst_height);
654 #else
655     zoom_factor = src_width / dst_width;
656 #endif
657 
658     /* adjust source image size to integer multiple of destination size */
659     src_width  = dst_width  * zoom_factor;
660     src_height = dst_height * zoom_factor;
661   }
662   else
663   {
664 #if 1
665     zoom_factor = MIN(dst_width / src_width, dst_height / src_height);
666 #else
667     zoom_factor = dst_width / src_width;
668 #endif
669 
670     /* no adjustment needed when scaling up (some pixels may be left blank) */
671   }
672 
673   /* copy source pixmap to temporary image */
674   if ((src_ximage = XGetImage(display, src_pixmap, 0, 0, src_width, src_height,
675 			      AllPlanes, ZPixmap)) == NULL)
676     Error(ERR_EXIT, "ZoomPixmap(): XGetImage() failed");
677 
678   bits_per_pixel = src_ximage->bits_per_pixel;
679   bytes_per_pixel = (bits_per_pixel + 7) / 8;
680 
681   if ((dst_ximage = XCreateImage(display, visual, src_ximage->depth, ZPixmap,
682 				 0, NULL, dst_width, dst_height,
683 				 8, dst_width * bytes_per_pixel)) == NULL)
684     Error(ERR_EXIT, "ZoomPixmap(): XCreateImage() failed");
685 
686   dst_ximage->data =
687     checked_malloc(dst_width * dst_height * bytes_per_pixel);
688   dst_ximage->byte_order = src_ximage->byte_order;
689 
690   src_ptr = (byte *)src_ximage->data;
691   dst_ptr = (byte *)dst_ximage->data;
692 
693   if (scale_down)
694   {
695     col_skip = (zoom_factor - 1) * bytes_per_pixel;
696     row_skip = col_skip * src_width;
697 
698     /* scale image down by scaling factor 'zoom_factor' */
699     for (y = 0; y < src_height; y += zoom_factor, src_ptr += row_skip)
700       for (x = 0; x < src_width; x += zoom_factor, src_ptr += col_skip)
701 	for (i = 0; i < bytes_per_pixel; i++)
702 	  *dst_ptr++ = *src_ptr++;
703   }
704   else
705   {
706     row_skip = src_width * bytes_per_pixel;
707 
708     /* scale image up by scaling factor 'zoom_factor' */
709     for (y = 0; y < src_height; y++)
710     {
711       for (yy = 0; yy < zoom_factor; yy++)
712       {
713 	if (yy > 0)
714 	  src_ptr -= row_skip;
715 
716 	for (x = 0; x < src_width; x++)
717 	{
718 	  for (xx = 0; xx < zoom_factor; xx++)
719 	    for (i = 0; i < bytes_per_pixel; i++)
720 	      *dst_ptr++ = *(src_ptr + i);
721 
722 	  src_ptr += bytes_per_pixel;
723 	}
724       }
725     }
726   }
727 
728   /* copy scaled image to destination pixmap */
729   XPutImage(display, dst_pixmap, gc, dst_ximage, 0, 0, 0, 0,
730 	    dst_width, dst_height);
731 
732   /* free temporary images */
733   X11DestroyImage(src_ximage);
734   X11DestroyImage(dst_ximage);
735 }
736 
freeXImage(Image * image,XImageInfo * ximageinfo)737 void freeXImage(Image *image, XImageInfo *ximageinfo)
738 {
739   if (ximageinfo->index != NULL && ximageinfo->no > 0)
740     XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
741 		ximageinfo->no, 0);
742   /* this       ^^^^^^^^^^^^^^ is wrong, because the used color cells
743    * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
744    * used color cells, but they are not at array position 0 - 'ximageinfo->no'
745    */
746 
747   free(ximageinfo);
748 }
749 
Read_PCX_to_Pixmap(Display * display,Window window,GC gc,char * filename,Pixmap * pixmap,Pixmap * pixmap_mask)750 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
751 		       Pixmap *pixmap, Pixmap *pixmap_mask)
752 {
753   Image *image;
754   XImageInfo *ximageinfo;
755   int screen;
756   Visual *visual;
757   int depth;
758 
759 #if DEBUG_TIMING
760   debug_print_timestamp(2, NULL);	/* initialize timestamp function */
761 #endif
762 
763   /* read the graphic file in PCX format to image structure */
764   if ((image = Read_PCX_to_Image(filename)) == NULL)
765     return errno_pcx;
766 
767 #if DEBUG_TIMING
768   printf("%s:\n", filename);
769   debug_print_timestamp(2, "   READING PCX FILE TO IMAGE: ");
770 #endif
771 
772   screen = DefaultScreen(display);
773   visual = DefaultVisual(display, screen);
774   depth = DefaultDepth(display, screen);
775 
776   /* convert image structure to X11 Pixmap */
777   if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
778 				     window, gc, depth, image)))
779   {
780     freeImage(image);
781 
782     return PCX_OtherError;
783   }
784 
785   /* if a private colormap has been created, install it */
786   if (ximageinfo->cmap != DefaultColormap(display, screen))
787     XSetWindowColormap(display, window, ximageinfo->cmap);
788 
789 #if DEBUG_TIMING
790   debug_print_timestamp(2, "   CONVERTING IMAGE TO PIXMAP:");
791 #endif
792 
793   /* create clip mask for the image */
794   ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
795 
796 #if DEBUG_TIMING
797   debug_print_timestamp(2, "   CONVERTING IMAGE TO MASK:  ");
798 #endif
799 
800   *pixmap = ximageinfo->pixmap;
801   *pixmap_mask = ximageinfo->pixmap_mask;
802 
803   /* free generic image and ximageinfo after native Pixmap has been created */
804   free(ximageinfo);
805   freeImage(image);
806 
807   return PCX_Success;
808 }
809 
810 #endif	/* PLATFORM_UNIX */
811 #endif	/* TARGET_X11 */
812 
813 
814 /* ========================================================================= */
815 /* PLATFORM INDEPENDENT IMAGE FUNCTIONS                                      */
816 /* ========================================================================= */
817 
818 struct ImageInfo
819 {
820   char *source_filename;
821   int num_references;
822 
823   Bitmap *bitmap;
824 
825   int original_width;			/* original image file width */
826   int original_height;			/* original image file height */
827 
828   boolean contains_small_images;	/* set after adding small images */
829   boolean scaled_up;			/* set after scaling up */
830 };
831 typedef struct ImageInfo ImageInfo;
832 
833 static struct ArtworkListInfo *image_info = NULL;
834 
Load_PCX(char * filename)835 static void *Load_PCX(char *filename)
836 {
837   ImageInfo *img_info;
838 
839 #if 0
840   printf("::: loading PCX file '%s'\n", filename);
841 #endif
842 
843   img_info = checked_calloc(sizeof(ImageInfo));
844 
845   if ((img_info->bitmap = LoadImage(filename)) == NULL)
846   {
847     Error(ERR_WARN, "cannot load image file '%s': LoadImage() failed: %s",
848 	  filename, GetError());
849     free(img_info);
850     return NULL;
851   }
852 
853   img_info->source_filename = getStringCopy(filename);
854 
855   img_info->original_width  = img_info->bitmap->width;
856   img_info->original_height = img_info->bitmap->height;
857 
858   img_info->contains_small_images = FALSE;
859   img_info->scaled_up = FALSE;
860 
861   return img_info;
862 }
863 
FreeImage(void * ptr)864 static void FreeImage(void *ptr)
865 {
866   ImageInfo *image = (ImageInfo *)ptr;
867 
868   if (image == NULL)
869     return;
870 
871   if (image->bitmap)
872     FreeBitmap(image->bitmap);
873 
874   if (image->source_filename)
875     free(image->source_filename);
876 
877   free(image);
878 }
879 
getImageListSize()880 int getImageListSize()
881 {
882   return (image_info->num_file_list_entries +
883 	  image_info->num_dynamic_file_list_entries);
884 }
885 
getImageListEntryFromImageID(int pos)886 struct FileInfo *getImageListEntryFromImageID(int pos)
887 {
888   int num_list_entries = image_info->num_file_list_entries;
889   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
890 
891   return (pos < num_list_entries ? &image_info->file_list[list_pos] :
892 	  &image_info->dynamic_file_list[list_pos]);
893 }
894 
getImageInfoEntryFromImageID(int pos)895 static ImageInfo *getImageInfoEntryFromImageID(int pos)
896 {
897   int num_list_entries = image_info->num_file_list_entries;
898   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
899   ImageInfo **img_info =
900     (ImageInfo **)(pos < num_list_entries ? image_info->artwork_list :
901 		   image_info->dynamic_artwork_list);
902 
903   return img_info[list_pos];
904 }
905 
getBitmapFromImageID(int pos)906 Bitmap *getBitmapFromImageID(int pos)
907 {
908   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
909 
910   return (img_info != NULL ? img_info->bitmap : NULL);
911 }
912 
getOriginalImageWidthFromImageID(int pos)913 int getOriginalImageWidthFromImageID(int pos)
914 {
915   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
916 
917   return (img_info != NULL ? img_info->original_width : 0);
918 }
919 
getOriginalImageHeightFromImageID(int pos)920 int getOriginalImageHeightFromImageID(int pos)
921 {
922   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
923 
924   return (img_info != NULL ? img_info->original_height : 0);
925 }
926 
getTokenFromImageID(int graphic)927 char *getTokenFromImageID(int graphic)
928 {
929   struct FileInfo *file_list = getImageListEntryFromImageID(graphic);
930 
931   return (file_list != NULL ? file_list->token : NULL);
932 }
933 
getImageIDFromToken(char * token)934 int getImageIDFromToken(char *token)
935 {
936   struct FileInfo *file_list = image_info->file_list;
937   int num_list_entries = image_info->num_file_list_entries;
938   int i;
939 
940   for (i = 0; i < num_list_entries; i++)
941     if (strEqual(file_list[i].token, token))
942       return i;
943 
944   return -1;
945 }
946 
getImageConfigFilename()947 char *getImageConfigFilename()
948 {
949   return getCustomArtworkConfigFilename(image_info->type);
950 }
951 
getImageListPropertyMappingSize()952 int getImageListPropertyMappingSize()
953 {
954   return image_info->num_property_mapping_entries;
955 }
956 
getImageListPropertyMapping()957 struct PropertyMapping *getImageListPropertyMapping()
958 {
959   return image_info->property_mapping;
960 }
961 
InitImageList(struct ConfigInfo * config_list,int num_file_list_entries,struct ConfigTypeInfo * config_suffix_list,char ** base_prefixes,char ** ext1_suffixes,char ** ext2_suffixes,char ** ext3_suffixes,char ** ignore_tokens)962 void InitImageList(struct ConfigInfo *config_list, int num_file_list_entries,
963 		   struct ConfigTypeInfo *config_suffix_list,
964 		   char **base_prefixes, char **ext1_suffixes,
965 		   char **ext2_suffixes, char **ext3_suffixes,
966 		   char **ignore_tokens)
967 {
968   int i;
969 
970   image_info = checked_calloc(sizeof(struct ArtworkListInfo));
971   image_info->type = ARTWORK_TYPE_GRAPHICS;
972 
973   /* ---------- initialize file list and suffix lists ---------- */
974 
975   image_info->num_file_list_entries = num_file_list_entries;
976   image_info->num_dynamic_file_list_entries = 0;
977 
978   image_info->file_list =
979     getFileListFromConfigList(config_list, config_suffix_list, ignore_tokens,
980 			      num_file_list_entries);
981   image_info->dynamic_file_list = NULL;
982 
983   image_info->num_suffix_list_entries = 0;
984   for (i = 0; config_suffix_list[i].token != NULL; i++)
985     image_info->num_suffix_list_entries++;
986 
987   image_info->suffix_list = config_suffix_list;
988 
989   /* ---------- initialize base prefix and suffixes lists ---------- */
990 
991   image_info->num_base_prefixes = 0;
992   for (i = 0; base_prefixes[i] != NULL; i++)
993     image_info->num_base_prefixes++;
994 
995   image_info->num_ext1_suffixes = 0;
996   for (i = 0; ext1_suffixes[i] != NULL; i++)
997     image_info->num_ext1_suffixes++;
998 
999   image_info->num_ext2_suffixes = 0;
1000   for (i = 0; ext2_suffixes[i] != NULL; i++)
1001     image_info->num_ext2_suffixes++;
1002 
1003   image_info->num_ext3_suffixes = 0;
1004   for (i = 0; ext3_suffixes[i] != NULL; i++)
1005     image_info->num_ext3_suffixes++;
1006 
1007   image_info->num_ignore_tokens = 0;
1008   for (i = 0; ignore_tokens[i] != NULL; i++)
1009     image_info->num_ignore_tokens++;
1010 
1011   image_info->base_prefixes = base_prefixes;
1012   image_info->ext1_suffixes = ext1_suffixes;
1013   image_info->ext2_suffixes = ext2_suffixes;
1014   image_info->ext3_suffixes = ext3_suffixes;
1015   image_info->ignore_tokens = ignore_tokens;
1016 
1017   image_info->num_property_mapping_entries = 0;
1018 
1019   image_info->property_mapping = NULL;
1020 
1021   /* ---------- initialize artwork reference and content lists ---------- */
1022 
1023   image_info->sizeof_artwork_list_entry = sizeof(ImageInfo *);
1024 
1025   image_info->artwork_list =
1026     checked_calloc(num_file_list_entries * sizeof(ImageInfo *));
1027   image_info->dynamic_artwork_list = NULL;
1028 
1029   image_info->content_list = NULL;
1030 
1031   /* ---------- initialize artwork loading/freeing functions ---------- */
1032 
1033   image_info->load_artwork = Load_PCX;
1034   image_info->free_artwork = FreeImage;
1035 }
1036 
ReloadCustomImages()1037 void ReloadCustomImages()
1038 {
1039 #if 0
1040   printf("::: reloading images '%s' ...\n", artwork.gfx_current_identifier);
1041 #endif
1042 
1043   LoadArtworkConfig(image_info);
1044   ReloadCustomArtworkList(image_info);
1045 }
1046 
CreateImageWithSmallImages(int pos,int zoom_factor)1047 void CreateImageWithSmallImages(int pos, int zoom_factor)
1048 {
1049   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
1050 
1051   if (img_info == NULL || img_info->contains_small_images)
1052     return;
1053 
1054   CreateBitmapWithSmallBitmaps(img_info->bitmap, zoom_factor);
1055 
1056   img_info->contains_small_images = TRUE;
1057   img_info->scaled_up = TRUE;
1058 }
1059 
ScaleImage(int pos,int zoom_factor)1060 void ScaleImage(int pos, int zoom_factor)
1061 {
1062   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
1063 
1064   if (img_info == NULL || img_info->scaled_up)
1065     return;
1066 
1067   if (zoom_factor != 1)
1068     ScaleBitmap(img_info->bitmap, zoom_factor);
1069 
1070   img_info->scaled_up = TRUE;
1071 }
1072 
FreeAllImages()1073 void FreeAllImages()
1074 {
1075   FreeCustomArtworkLists(image_info);
1076 }
1077