1 /* gifx.c - Functions to turn GIFs in memory into X Pixmaps.
2    Copyright (C) 1997-2021 Eddie Kohler, ekohler@gmail.com
3    This file is part of the LCDF GIF library.
4 
5    The LCDF GIF library is free software. It is distributed under the GNU
6    General Public License, version 2; you can copy, distribute, or alter it at
7    will, as long as this notice is kept intact and this source code is made
8    available. There is no warranty, express or implied. */
9 
10 #if HAVE_CONFIG_H
11 # include <config.h>
12 #endif
13 #include <lcdfgif/gifx.h>
14 #include <X11/Xutil.h>
15 #include <assert.h>
16 #include <string.h>
17 #ifdef __cplusplus
18 extern "C" {
19 #endif
20 
21 #define SAFELS(a,b) ((b) < 0 ? (a) >> -(b) : (a) << (b))
22 
23 struct Gif_XColormap {
24 
25   Gif_XContext *x_context;
26   Gif_Colormap *colormap;
27 
28   int allocated;
29   int claimed;
30   uint16_t npixels;
31   unsigned long *pixels;
32 
33   Gif_XColormap *next;
34 
35 };
36 
37 static unsigned long crap_pixels[256];
38 
39 
40 static void
load_closest(Gif_XContext * gfx)41 load_closest(Gif_XContext *gfx)
42 {
43   XColor *color;
44   uint16_t ncolor;
45   uint16_t ncolormap;
46   int i;
47 
48   if (gfx->closest) return;
49 
50   ncolormap = ncolor = gfx->ncolormap;
51   if (ncolor > 256) ncolor = 256;
52   color = Gif_NewArray(XColor, ncolor);
53 
54   if (ncolormap > 256)
55     for (i = 0; i < ncolor; i++)
56       color[i].pixel = (rand() >> 4) % ncolormap;
57   else
58     for (i = 0; i < ncolor; i++)
59       color[i].pixel = i;
60   XQueryColors(gfx->display, gfx->colormap, color, ncolor);
61 
62   gfx->closest = Gif_NewArray(Gif_Color, ncolor);
63   for (i = 0; i < ncolor; i++) {
64     Gif_Color *c = &gfx->closest[i];
65     c->haspixel = 1;
66     c->gfc_red = color[i].red >> 8;
67     c->gfc_green = color[i].green >> 8;
68     c->gfc_blue = color[i].blue >> 8;
69     c->pixel = color[i].pixel;
70   }
71   gfx->nclosest = ncolor;
72 
73   Gif_DeleteArray(color);
74 }
75 
76 
77 static unsigned long
allocate_closest(Gif_XContext * gfx,Gif_Color * c)78 allocate_closest(Gif_XContext *gfx, Gif_Color *c)
79 {
80   Gif_Color *closer;
81   Gif_Color *got = 0;
82   uint32_t distance = 0x4000000;
83   int i;
84 
85   load_closest(gfx);
86 
87   for (i = 0, closer = gfx->closest; i < gfx->nclosest; i++, closer++) {
88     int redd = c->gfc_red - closer->gfc_red;
89     int greend = c->gfc_green - closer->gfc_green;
90     int blued = c->gfc_blue - closer->gfc_blue;
91     uint32_t d = redd * redd + greend * greend + blued * blued;
92     if (d < distance) {
93       distance = d;
94       got = closer;
95     }
96   }
97 
98   if (!got) return 0;
99   if (!got->haspixel) {
100     XColor xcol;
101     xcol.red = got->gfc_red | (got->gfc_red << 8);
102     xcol.green = got->gfc_green | (got->gfc_green << 8);
103     xcol.blue = got->gfc_blue | (got->gfc_blue << 8);
104     if (XAllocColor(gfx->display, gfx->colormap, &xcol) == 0) {
105       /* Probably was a read/write color cell. Get rid of it!! */
106       *got = gfx->closest[gfx->nclosest - 1];
107       gfx->nclosest--;
108       return allocate_closest(gfx, c);
109     }
110     got->pixel = xcol.pixel;
111     got->haspixel = 1;
112   }
113 
114   return got->pixel;
115 }
116 
117 
118 static void
allocate_colors(Gif_XColormap * gfxc)119 allocate_colors(Gif_XColormap *gfxc)
120 {
121   Gif_XContext *gfx = gfxc->x_context;
122   uint16_t size = gfxc->colormap->ncol;
123   Gif_Color *c = gfxc->colormap->col;
124   unsigned long *pixels = gfxc->pixels;
125   XColor xcol;
126   int i;
127   if (!gfxc->allocated) {
128     if (size > gfxc->npixels) size = gfxc->npixels;
129     for (i = 0; i < size; i++, c++) {
130       xcol.red = c->gfc_red | (c->gfc_red << 8);
131       xcol.green = c->gfc_green | (c->gfc_green << 8);
132       xcol.blue = c->gfc_blue | (c->gfc_blue << 8);
133       if (XAllocColor(gfx->display, gfx->colormap, &xcol))
134 	pixels[i] = xcol.pixel;
135       else
136 	pixels[i] = allocate_closest(gfx, c);
137     }
138     gfxc->allocated = 1;
139     gfxc->claimed = 0;
140   }
141 }
142 
143 static void
deallocate_colors(Gif_XColormap * gfxc)144 deallocate_colors(Gif_XColormap *gfxc)
145 {
146   Gif_XContext *gfx = gfxc->x_context;
147   if (gfxc->allocated && !gfxc->claimed) {
148     XFreeColors(gfx->display, gfx->colormap, gfxc->pixels, gfxc->npixels, 0);
149     gfxc->allocated = 0;
150   }
151 }
152 
153 
154 static Gif_XColormap *
create_x_colormap_extension(Gif_XContext * gfx,Gif_Colormap * gfcm)155 create_x_colormap_extension(Gif_XContext *gfx, Gif_Colormap *gfcm)
156 {
157   Gif_XColormap *gfxc;
158   unsigned long *pixels;
159   if (!gfcm) return 0;
160   gfxc = Gif_New(Gif_XColormap);
161   pixels = gfxc ? Gif_NewArray(unsigned long, 256) : 0;
162   if (pixels) {
163     gfxc->x_context = gfx;
164     gfxc->colormap = gfcm;
165     gfxc->allocated = 0;
166     gfxc->npixels = gfcm->ncol;
167     gfxc->pixels = pixels;
168     gfxc->next = gfx->xcolormap;
169     gfx->xcolormap = gfxc;
170     return gfxc;
171   } else {
172     Gif_Delete(gfxc);
173     Gif_DeleteArray(pixels);
174     return 0;
175   }
176 }
177 
178 static Gif_XColormap *
find_x_colormap_extension(Gif_XContext * gfx,Gif_Colormap * gfcm,int create)179 find_x_colormap_extension(Gif_XContext *gfx, Gif_Colormap *gfcm, int create)
180 {
181   Gif_XColormap *gfxc = gfx->xcolormap;
182   if (!gfcm) return 0;
183   while (gfxc) {
184     if (gfxc->colormap == gfcm)
185       return gfxc;
186     gfxc = gfxc->next;
187   }
188   if (create)
189     return create_x_colormap_extension(gfx, gfcm);
190   else
191     return 0;
192 }
193 
194 int
Gif_XAllocateColors(Gif_XContext * gfx,Gif_Colormap * gfcm)195 Gif_XAllocateColors(Gif_XContext *gfx, Gif_Colormap *gfcm)
196 {
197   Gif_XColormap *gfxc = find_x_colormap_extension(gfx, gfcm, 1);
198   if (gfxc) {
199     allocate_colors(gfxc);
200     return 1;
201   } else
202     return 0;
203 }
204 
205 void
Gif_XDeallocateColors(Gif_XContext * gfx,Gif_Colormap * gfcm)206 Gif_XDeallocateColors(Gif_XContext *gfx, Gif_Colormap *gfcm)
207 {
208   Gif_XColormap *gfxc = find_x_colormap_extension(gfx, gfcm, 0);
209   if (gfxc)
210     deallocate_colors(gfxc);
211 }
212 
213 
214 unsigned long *
Gif_XClaimStreamColors(Gif_XContext * gfx,Gif_Stream * gfs,int * np_store)215 Gif_XClaimStreamColors(Gif_XContext *gfx, Gif_Stream *gfs, int *np_store)
216 {
217   int i;
218   int npixels = 0;
219   unsigned long *pixels;
220   Gif_Colormap *global = gfs->global;
221   *np_store = 0;
222 
223   for (i = 0; i < gfs->nimages; i++) {
224     Gif_Image *gfi = gfs->images[i];
225     Gif_Colormap *gfcm = (gfi->local ? gfi->local : global);
226     Gif_XColormap *gfxc = find_x_colormap_extension(gfx, gfcm, 0);
227     if (gfxc && gfxc->allocated && gfxc->claimed == 0) {
228       gfxc->claimed = 2;
229       npixels += gfxc->npixels;
230       if (gfcm == global) global = 0;
231     }
232   }
233 
234   if (!npixels) return 0;
235 
236   pixels = Gif_NewArray(unsigned long, npixels);
237   if (!pixels) return 0;
238   *np_store = npixels;
239 
240   npixels = 0;
241   global = gfs->global;
242   for (i = 0; i < gfs->nimages; i++) {
243     Gif_Image *gfi = gfs->images[i];
244     Gif_Colormap *gfcm = (gfi->local ? gfi->local : global);
245     Gif_XColormap *gfxc = find_x_colormap_extension(gfx, gfcm, 0);
246     if (gfxc && gfxc->allocated && gfxc->claimed == 2) {
247       memcpy(pixels + npixels, gfxc->pixels, gfxc->npixels);
248       npixels += gfxc->npixels;
249       gfxc->claimed = 1;
250       if (gfcm == global) global = 0;
251     }
252   }
253 
254   return pixels;
255 }
256 
257 
258 /* Getting pixmaps */
259 
260 #define BYTESIZE 8
261 
262 static int
put_sub_image_colormap(Gif_XContext * gfx,Gif_Stream * gfs,Gif_Image * gfi,Gif_Colormap * gfcm,int left,int top,int width,int height,Pixmap pixmap,int pixmap_x,int pixmap_y)263 put_sub_image_colormap(Gif_XContext *gfx,
264                        Gif_Stream* gfs, Gif_Image *gfi, Gif_Colormap *gfcm,
265 		       int left, int top, int width, int height,
266 		       Pixmap pixmap, int pixmap_x, int pixmap_y)
267 {
268   XImage *ximage;
269   uint8_t *xdata;
270 
271   int i, j, k;
272   size_t bytes_per_line;
273 
274   unsigned long saved_transparent = 0;
275   int release_uncompressed = 0;
276   uint16_t nct;
277   unsigned long *pixels;
278 
279   /* Find the correct image and colormap */
280   if (!gfi) return 0;
281   if (!gfx->image_gc)
282     gfx->image_gc = XCreateGC(gfx->display, pixmap, 0, 0);
283   if (!gfx->image_gc)
284     return 0;
285 
286   /* Make sure the image is uncompressed */
287   if (!gfi->img && !gfi->image_data && gfi->compressed) {
288       Gif_UncompressImage(gfs, gfi);
289       release_uncompressed = 1;
290   }
291 
292   /* Check subimage dimensions */
293   if (width <= 0 || height <= 0 || left < 0 || top < 0
294       || left+width <= 0 || top+height <= 0
295       || left+width > gfi->width || top+height > gfi->height)
296     return 0;
297 
298   /* Allocate colors from the colormap; make sure the transparent color
299    * has the given pixel value */
300   if (gfcm) {
301     Gif_XColormap *gfxc = find_x_colormap_extension(gfx, gfcm, 1);
302     if (!gfxc) return 0;
303     allocate_colors(gfxc);
304     pixels = gfxc->pixels;
305     nct = gfxc->npixels;
306   } else {
307     for (i = 0; i < 256; i++) crap_pixels[i] = gfx->foreground_pixel;
308     pixels = crap_pixels;
309     nct = 256;
310   }
311   if (gfi->transparent >= 0 && gfi->transparent < 256) {
312     saved_transparent = pixels[ gfi->transparent ];
313     pixels[ gfi->transparent ] = gfx->transparent_pixel;
314   }
315 
316   /* Set up the X image */
317   if (gfx->depth <= 8) i = 8;
318   else if (gfx->depth <= 16) i = 16;
319   else i = 32;
320   ximage =
321     XCreateImage(gfx->display, gfx->visual, gfx->depth,
322 		 gfx->depth == 1 ? XYBitmap : ZPixmap, 0, NULL,
323 		 width, height, i, 0);
324 
325   ximage->bitmap_bit_order = ximage->byte_order = LSBFirst;
326   bytes_per_line = ximage->bytes_per_line;
327   xdata = Gif_NewArray(uint8_t, bytes_per_line * height);
328   ximage->data = (char *)xdata;
329 
330   /* The main loop */
331   if (ximage->bits_per_pixel % 8 == 0) {
332     /* Optimize for cases where a pixel is exactly one or more bytes */
333     int bytes_per_pixel = ximage->bits_per_pixel / 8;
334 
335     for (j = 0; j < height; j++) {
336       uint8_t *line = gfi->img[top + j] + left;
337       uint8_t *writer = xdata + bytes_per_line * j;
338       for (i = 0; i < width; i++) {
339 	unsigned long pixel;
340 	if (line[i] < nct)
341 	  pixel = pixels[line[i]];
342 	else
343 	  pixel = pixels[0];
344 	for (k = 0; k < bytes_per_pixel; k++) {
345 	  *writer++ = pixel;
346 	  pixel >>= 8;
347 	}
348       }
349     }
350 
351   } else {
352     /* Other bits-per-pixel */
353     int bits_per_pixel = ximage->bits_per_pixel;
354     uint32_t bits_per_pixel_mask = (1UL << bits_per_pixel) - 1;
355 
356     for (j = 0; j < height; j++) {
357       int imshift = 0;
358       uint32_t impixel = 0;
359       uint8_t *line = gfi->img[top + j] + left;
360       uint8_t *writer = xdata + bytes_per_line * j;
361 
362       for (i = 0; i < width; i++) {
363 	unsigned long pixel;
364 	if (line[i] < nct)
365 	  pixel = pixels[line[i]];
366 	else
367 	  pixel = pixels[0];
368 
369 	impixel |= SAFELS(pixel & bits_per_pixel_mask, imshift);
370 	while (imshift + bits_per_pixel >= BYTESIZE) {
371 	  *writer++ = impixel;
372 	  imshift -= BYTESIZE;
373 	  impixel = SAFELS(pixel, imshift);
374 	}
375 	imshift += bits_per_pixel;
376       }
377 
378       if (imshift)
379 	*writer++ = impixel;
380     }
381   }
382 
383   /* Restore saved transparent pixel value */
384   if (gfi->transparent >= 0 && gfi->transparent < 256)
385     pixels[ gfi->transparent ] = saved_transparent;
386 
387   /* Put it onto the pixmap */
388   XPutImage(gfx->display, pixmap, gfx->image_gc, ximage, 0, 0,
389 	    pixmap_x, pixmap_y, width, height);
390 
391   Gif_DeleteArray(xdata);
392   ximage->data = 0; /* avoid freeing it again in XDestroyImage */
393   XDestroyImage(ximage);
394 
395   if (release_uncompressed)
396     Gif_ReleaseUncompressedImage(gfi);
397 
398   return 1;
399 }
400 
401 
402 Pixmap
Gif_XSubImageColormap(Gif_XContext * gfx,Gif_Stream * gfs,Gif_Image * gfi,Gif_Colormap * gfcm,int left,int top,int width,int height)403 Gif_XSubImageColormap(Gif_XContext *gfx,
404                       Gif_Stream* gfs, Gif_Image *gfi, Gif_Colormap *gfcm,
405 		      int left, int top, int width, int height)
406 {
407   Pixmap pixmap =
408     XCreatePixmap(gfx->display, gfx->drawable,
409                   width ? width : 1, height ? height : 1, gfx->depth);
410   if (pixmap) {
411     if (put_sub_image_colormap(gfx, gfs, gfi, gfcm, left, top, width, height,
412 			       pixmap, 0, 0))
413       return pixmap;
414     else
415       XFreePixmap(gfx->display, pixmap);
416   }
417   return None;
418 }
419 
420 Pixmap
Gif_XImage(Gif_XContext * gfx,Gif_Stream * gfs,Gif_Image * gfi)421 Gif_XImage(Gif_XContext *gfx, Gif_Stream *gfs, Gif_Image *gfi)
422 {
423   Gif_Colormap *gfcm;
424   if (!gfi && gfs->nimages) gfi = gfs->images[0];
425   if (!gfi) return None;
426   gfcm = gfi->local;
427   if (!gfcm) gfcm = gfs->global;
428   return Gif_XSubImageColormap(gfx, gfs, gfi, gfcm,
429 			       0, 0, gfi->width, gfi->height);
430 }
431 
432 Pixmap
Gif_XImageColormap(Gif_XContext * gfx,Gif_Stream * gfs,Gif_Colormap * gfcm,Gif_Image * gfi)433 Gif_XImageColormap(Gif_XContext *gfx, Gif_Stream *gfs, Gif_Colormap *gfcm,
434 		   Gif_Image *gfi)
435 {
436   if (!gfi && gfs->nimages) gfi = gfs->images[0];
437   if (!gfi) return None;
438   return Gif_XSubImageColormap(gfx, gfs, gfi, gfcm,
439 			       0, 0, gfi->width, gfi->height);
440 }
441 
442 Pixmap
Gif_XSubImage(Gif_XContext * gfx,Gif_Stream * gfs,Gif_Image * gfi,int left,int top,int width,int height)443 Gif_XSubImage(Gif_XContext *gfx, Gif_Stream *gfs, Gif_Image *gfi,
444 	      int left, int top, int width, int height)
445 {
446   Gif_Colormap *gfcm;
447   if (!gfi && gfs->nimages) gfi = gfs->images[0];
448   if (!gfi) return None;
449   gfcm = gfi->local;
450   if (!gfcm) gfcm = gfs->global;
451   return Gif_XSubImageColormap(gfx, gfs, gfi, gfcm,
452 			       left, top, width, height);
453 }
454 
455 
456 Pixmap
Gif_XSubMask(Gif_XContext * gfx,Gif_Stream * gfs,Gif_Image * gfi,int left,int top,int width,int height)457 Gif_XSubMask(Gif_XContext* gfx, Gif_Stream* gfs, Gif_Image* gfi,
458 	     int left, int top, int width, int height)
459 {
460   Pixmap pixmap = None;
461   XImage *ximage;
462   uint8_t *xdata;
463 
464   int i, j;
465   int transparent;
466   size_t bytes_per_line;
467   int release_uncompressed = 0;
468 
469   /* Find the correct image */
470   if (!gfi) return None;
471 
472   /* Check subimage dimensions */
473   if (width <= 0 || height <= 0 || left < 0 || top < 0
474       || left+width <= 0 || top+height <= 0
475       || left+width > gfi->width || top+height > gfi->height)
476     return None;
477 
478   /* Make sure the image is uncompressed */
479   if (!gfi->img && !gfi->image_data && gfi->compressed) {
480       Gif_UncompressImage(gfs, gfi);
481       release_uncompressed = 1;
482   }
483 
484   /* Create the X image */
485   ximage =
486     XCreateImage(gfx->display, gfx->visual, 1,
487 		 XYBitmap, 0, NULL,
488 		 width, height,
489 		 8, 0);
490 
491   ximage->bitmap_bit_order = ximage->byte_order = LSBFirst;
492   bytes_per_line = ximage->bytes_per_line;
493   xdata = Gif_NewArray(uint8_t, bytes_per_line * height);
494   ximage->data = (char *)xdata;
495 
496   transparent = gfi->transparent;
497 
498   /* The main loop */
499   for (j = 0; j < height; j++) {
500     int imshift = 0;
501     uint32_t impixel = 0;
502     uint8_t *line = gfi->img[top + j] + left;
503     uint8_t *writer = xdata + bytes_per_line * j;
504 
505     for (i = 0; i < width; i++) {
506       if (line[i] == transparent)
507 	impixel |= 1 << imshift;
508 
509       if (++imshift >= BYTESIZE) {
510 	*writer++ = impixel;
511 	imshift = 0;
512 	impixel = 0;
513       }
514     }
515 
516     if (imshift)
517       *writer++ = impixel;
518   }
519 
520   /* Create the pixmap */
521   pixmap =
522     XCreatePixmap(gfx->display, gfx->drawable,
523                   width ? width : 1, height ? height : 1, 1);
524   if (!gfx->mask_gc)
525     gfx->mask_gc = XCreateGC(gfx->display, pixmap, 0, 0);
526 
527   if (pixmap && gfx->mask_gc)
528     XPutImage(gfx->display, pixmap, gfx->mask_gc, ximage, 0, 0, 0, 0,
529 	      width, height);
530 
531   Gif_DeleteArray(xdata);
532   ximage->data = 0; /* avoid freeing it again in XDestroyImage */
533   XDestroyImage(ximage);
534 
535   if (release_uncompressed)
536     Gif_ReleaseUncompressedImage(gfi);
537 
538   return pixmap;
539 }
540 
541 
542 Pixmap
Gif_XMask(Gif_XContext * gfx,Gif_Stream * gfs,Gif_Image * gfi)543 Gif_XMask(Gif_XContext *gfx, Gif_Stream *gfs, Gif_Image *gfi)
544 {
545   if (!gfi && gfs->nimages) gfi = gfs->images[0];
546   if (!gfi) return None;
547   return Gif_XSubMask(gfx, gfs, gfi, 0, 0, gfi->width, gfi->height);
548 }
549 
550 static Pixmap
screen_pixmap(Gif_XContext * gfx,Gif_Stream * gfs)551 screen_pixmap(Gif_XContext *gfx, Gif_Stream *gfs)
552 {
553   return XCreatePixmap(gfx->display, gfx->drawable,
554 		       gfs->screen_width, gfs->screen_height, gfx->depth);
555 }
556 
557 static int
apply_background(Gif_XContext * gfx,Gif_Stream * gfs,int i,Pixmap pixmap)558 apply_background(Gif_XContext *gfx, Gif_Stream *gfs, int i, Pixmap pixmap)
559 {
560   Gif_Image *gfi = gfs->images[i >= 0 ? i : 0];
561   Gif_Colormap *gfcm = (gfi->local ? gfi->local : gfs->global);
562   unsigned long bg_pixel;
563 
564   /* find bg_pixel */
565   if (gfs->global && gfs->background < gfs->global->ncol
566       && gfs->images[0]->transparent < 0) {
567     Gif_XColormap *gfxc = find_x_colormap_extension(gfx, gfcm, 1);
568     if (!gfxc)
569       return -1;
570     allocate_colors(gfxc);
571     bg_pixel = gfxc->pixels[gfs->background];
572   } else
573     bg_pixel = gfx->transparent_pixel;
574 
575   /* install it as the foreground color on gfx->image_gc */
576   if (!gfx->image_gc)
577     gfx->image_gc = XCreateGC(gfx->display, pixmap, 0, 0);
578   if (!gfx->image_gc)
579     return -1;
580   XSetForeground(gfx->display, gfx->image_gc, bg_pixel);
581   gfx->transparent_pixel = bg_pixel;
582 
583   /* clear the image portion */
584   if (i < 0)
585     XFillRectangle(gfx->display, pixmap, gfx->image_gc,
586 		   0, 0, gfs->screen_width, gfs->screen_height);
587   else /*if (gfi->transparent < 0)*/
588     XFillRectangle(gfx->display, pixmap, gfx->image_gc,
589 		   gfi->left, gfi->top, gfi->width, gfi->height);
590   /*else {
591     Pixmap mask = Gif_XMask(gfx, gfs, gfi);
592     if (mask == None)
593       return -1;
594     XSetClipMask(gfx->display, gfx->image_gc, mask);
595     XSetClipOrigin(gfx->display, gfx->image_gc, gfi->left, gfi->top);
596     XFillRectangle(gfx->display, pixmap, gfx->image_gc,
597 		   gfi->left, gfi->top, gfi->width, gfi->height);
598     XSetClipMask(gfx->display, gfx->image_gc, None);
599     XFreePixmap(gfx->display, mask);
600   }*/
601 
602   return 0;
603 }
604 
605 static int
apply_image(Gif_XContext * gfx,Gif_Stream * gfs,Gif_Image * gfi,Pixmap pixmap)606 apply_image(Gif_XContext *gfx, Gif_Stream *gfs, Gif_Image *gfi, Pixmap pixmap)
607 {
608   Pixmap image = Gif_XImage(gfx, gfs, gfi), mask;
609   if (image == None)
610     return -1;
611 
612   if (gfi->transparent >= 0) {
613     mask = Gif_XMask(gfx, gfs, gfi);
614     if (mask == None) {
615       XFreePixmap(gfx->display, image);
616       return -1;
617     }
618 
619     XSetClipMask(gfx->display, gfx->image_gc, mask);
620     XSetClipOrigin(gfx->display, gfx->image_gc, gfi->left, gfi->top);
621     XCopyArea(gfx->display, image, pixmap, gfx->image_gc,
622 	      0, 0, gfi->width, gfi->height, gfi->left, gfi->top);
623     XSetClipMask(gfx->display, gfx->image_gc, None);
624     XFreePixmap(gfx->display, mask);
625   } else {
626     XCopyArea(gfx->display, image, pixmap, gfx->image_gc,
627 	      0, 0, gfi->width, gfi->height, gfi->left, gfi->top);
628   }
629 
630   XFreePixmap(gfx->display, image);
631   return 0;
632 }
633 
634 static int
fullscreen(Gif_Stream * gfs,Gif_Image * gfi,int require_opaque)635 fullscreen(Gif_Stream *gfs, Gif_Image *gfi, int require_opaque)
636 {
637   return (gfi->left == 0 && gfi->top == 0 && gfi->width == gfs->screen_width
638 	  && gfi->height == gfs->screen_height
639 	  && (!require_opaque || gfi->transparent < 0));
640 }
641 
642 Gif_XFrame *
Gif_NewXFrames(Gif_Stream * gfs)643 Gif_NewXFrames(Gif_Stream *gfs)
644 {
645   int i, last_postdisposal = -1;
646   Gif_XFrame *fs = Gif_NewArray(Gif_XFrame, gfs->nimages);
647   if (!fs)
648     return 0;
649   for (i = 0; i < gfs->nimages; ++i) {
650     Gif_Image *gfi = gfs->images[i];
651     fs[i].pixmap = None;
652     if (gfi->disposal == GIF_DISPOSAL_PREVIOUS)
653       fs[i].postdisposal = last_postdisposal;
654     else
655       fs[i].postdisposal = i;
656     last_postdisposal = fs[i].postdisposal;
657   }
658   return fs;
659 }
660 
661 void
Gif_DeleteXFrames(Gif_XContext * gfx,Gif_Stream * gfs,Gif_XFrame * fs)662 Gif_DeleteXFrames(Gif_XContext *gfx, Gif_Stream *gfs, Gif_XFrame *fs)
663 {
664   int i;
665   for (i = 0; i < gfs->nimages; ++i)
666     if (fs[i].pixmap)
667       XFreePixmap(gfx->display, fs[i].pixmap);
668   Gif_DeleteArray(fs);
669 }
670 
671 Pixmap
Gif_XNextImage(Gif_XContext * gfx,Gif_Stream * gfs,int i,Gif_XFrame * frames)672 Gif_XNextImage(Gif_XContext *gfx, Gif_Stream *gfs, int i, Gif_XFrame *frames)
673 {
674   Pixmap result = None;
675   unsigned long old_transparent = gfx->transparent_pixel;
676   Gif_Image *gfi;
677   int previ, scani;
678 
679   /* return already rendered pixmap if any */
680   if (frames[i].pixmap != None)
681     return frames[i].pixmap;
682 
683   /* render fullscreen image */
684   gfi = gfs->images[i];
685   if (fullscreen(gfs, gfi, 1)) {
686     frames[i].pixmap = Gif_XImage(gfx, gfs, gfi);
687     return frames[i].pixmap;
688   }
689 
690   /* image is not full screen, need to find background */
691   previ = i - 1;
692   if (previ >= 0)
693     previ = frames[previ].postdisposal;
694 
695   /* scan backwards for a renderable image */
696   scani = previ;
697   while (scani >= 0
698 	 && frames[scani].pixmap == None
699 	 && !fullscreen(gfs, gfs->images[scani], 1))
700     --scani;
701 
702   /* create the pixmap */
703   result = screen_pixmap(gfx, gfs);
704   if (result == None)
705     return None;
706 
707   /* scan forward to produce background */
708   gfi = (scani >= 0 ? gfs->images[scani] : 0);
709   if (gfi && (gfi->disposal != GIF_DISPOSAL_BACKGROUND
710 	      || !fullscreen(gfs, gfi, 1))) {
711     /* perhaps we need to create an image (if so, must be fullscreen) */
712     if (frames[scani].pixmap == None) {
713       frames[scani].pixmap = Gif_XImage(gfx, gfs, gfi);
714       if (frames[scani].pixmap == None)
715 	goto error_exit;
716     }
717     XCopyArea(gfx->display, frames[scani].pixmap, result, gfx->image_gc,
718 	      0, 0, gfs->screen_width, gfs->screen_height, 0, 0);
719   }
720   if (!gfi || gfi->disposal == GIF_DISPOSAL_BACKGROUND) {
721     if (apply_background(gfx, gfs, scani, result) < 0)
722       goto error_exit;
723   }
724 
725   while (scani < previ) {
726     ++scani;
727     gfi = gfs->images[scani];
728     if (gfi->disposal == GIF_DISPOSAL_BACKGROUND) {
729       if (apply_background(gfx, gfs, scani, result) < 0)
730 	goto error_exit;
731     } else if (gfi->disposal != GIF_DISPOSAL_PREVIOUS) {
732       if (apply_image(gfx, gfs, gfs->images[scani], result) < 0)
733 	goto error_exit;
734     }
735   }
736 
737   /* apply image */
738   if (gfs->screen_width != 0 && gfs->screen_height != 0) {
739     if (apply_image(gfx, gfs, gfs->images[i], result) < 0)
740       goto error_exit;
741   }
742 
743   frames[i].pixmap = result;
744   return frames[i].pixmap;
745 
746  error_exit:
747   XFreePixmap(gfx->display, result);
748   gfx->transparent_pixel = old_transparent;
749   return None;
750 }
751 
752 
753 /** CREATING AND DESTROYING XCONTEXTS **/
754 
755 static void
delete_xcolormap(Gif_XColormap * gfxc)756 delete_xcolormap(Gif_XColormap *gfxc)
757 {
758   Gif_XContext *gfx = gfxc->x_context;
759   Gif_XColormap *prev = 0, *trav = gfx->xcolormap;
760   while (trav != gfxc && trav) {
761     prev = trav;
762     trav = trav->next;
763   }
764   if (gfx->free_deleted_colormap_pixels)
765     deallocate_colors(gfxc);
766   if (prev)
767     prev->next = gfxc->next;
768   else
769     gfx->xcolormap = gfxc->next;
770   Gif_DeleteArray(gfxc->pixels);
771   Gif_Delete(gfxc);
772 }
773 
774 static void
delete_colormap_hook(int dummy,void * colormap_x,void * callback_x)775 delete_colormap_hook(int dummy, void *colormap_x, void *callback_x)
776 {
777   Gif_Colormap *gfcm = (Gif_Colormap *)colormap_x;
778   Gif_XContext *gfx = (Gif_XContext *)callback_x;
779   Gif_XColormap *gfxc;
780   (void) dummy;
781   for (gfxc = gfx->xcolormap; gfxc; gfxc = gfxc->next)
782     if (gfxc->colormap == gfcm) {
783       delete_xcolormap(gfxc);
784       return;
785     }
786 }
787 
788 
789 Gif_XContext *
Gif_NewXContextFromVisual(Display * display,int screen_number,Visual * visual,int depth,Colormap colormap)790 Gif_NewXContextFromVisual(Display *display, int screen_number,
791 			  Visual *visual, int depth, Colormap colormap)
792 {
793   Gif_XContext *gfx;
794 
795   gfx = Gif_New(Gif_XContext);
796   gfx->display = display;
797   gfx->screen_number = screen_number;
798   gfx->drawable = RootWindow(display, screen_number);
799 
800   gfx->visual = visual;
801   gfx->colormap = colormap;
802   gfx->ncolormap = visual->map_entries;
803   gfx->depth = depth;
804 
805   gfx->closest = 0;
806   gfx->nclosest = 0;
807 
808   gfx->free_deleted_colormap_pixels = 0;
809   gfx->xcolormap = 0;
810 
811   gfx->image_gc = None;
812   gfx->mask_gc = None;
813 
814   gfx->transparent_pixel = 0UL;
815   gfx->foreground_pixel = 1UL;
816   gfx->refcount = 0;
817 
818   Gif_AddDeletionHook(GIF_T_COLORMAP, delete_colormap_hook, gfx);
819   return gfx;
820 }
821 
822 
823 Gif_XContext *
Gif_NewXContext(Display * display,Window window)824 Gif_NewXContext(Display *display, Window window)
825 {
826   XWindowAttributes attr;
827   XGetWindowAttributes(display, window, &attr);
828   return Gif_NewXContextFromVisual(display, XScreenNumberOfScreen(attr.screen),
829 				   attr.visual, attr.depth, attr.colormap);
830 }
831 
832 
833 void
Gif_DeleteXContext(Gif_XContext * gfx)834 Gif_DeleteXContext(Gif_XContext *gfx)
835 {
836   if (!gfx) return;
837   if (--gfx->refcount > 0) return;
838   while (gfx->xcolormap)
839     delete_xcolormap(gfx->xcolormap);
840   if (gfx->image_gc)
841     XFreeGC(gfx->display, gfx->image_gc);
842   if (gfx->mask_gc)
843     XFreeGC(gfx->display, gfx->mask_gc);
844   Gif_DeleteArray(gfx->closest);
845   Gif_RemoveDeletionHook(GIF_T_COLORMAP, delete_colormap_hook, gfx);
846   Gif_Delete(gfx);
847 }
848 
849 
850 #ifdef __cplusplus
851 }
852 #endif
853