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