From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / tessellimage.c
1 /* tessellimage, Copyright (c) 2014 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 #include "screenhack.h"
13 #include "delaunay.h"
14
15 #undef DO_VORONOI
16
17
18 #ifndef HAVE_JWXYZ
19 # define XK_MISCELLANY
20 # include <X11/keysymdef.h>
21 #endif
22
23 #undef countof
24 #define countof(x) (sizeof((x))/sizeof((*x)))
25
26 struct state {
27   Display *dpy;
28   Window window;
29   XWindowAttributes xgwa;
30   GC wgc, pgc;
31   int delay;
32   Bool outline_p, cache_p, fill_p;
33   double duration, duration2;
34   int max_depth;
35   double start_time, start_time2;
36
37   XImage *img, *delta;
38   Pixmap image, output, deltap;
39   int nthreshes, threshes[256], vsizes[256];
40   int thresh, dthresh;
41   Pixmap cache[256];
42
43   async_load_state *img_loader;
44   XRectangle geom;
45   Bool button_down_p;
46 };
47
48
49 /* Returns the current time in seconds as a double.
50  */
51 static double
52 double_time (void)
53 {
54   struct timeval now;
55 # ifdef GETTIMEOFDAY_TWO_ARGS
56   struct timezone tzp;
57   gettimeofday(&now, &tzp);
58 # else
59   gettimeofday(&now);
60 # endif
61
62   return (now.tv_sec + ((double) now.tv_usec * 0.000001));
63 }
64
65
66 static void *
67 tessellimage_init (Display *dpy, Window window)
68 {
69   struct state *st = (struct state *) calloc (1, sizeof(*st));
70
71   st->dpy = dpy;
72   st->window = window;
73
74   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
75
76   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
77   if (st->delay < 1) st->delay = 1;
78
79   st->outline_p = get_boolean_resource (st->dpy, "outline", "Boolean");
80   st->cache_p   = get_boolean_resource (st->dpy, "cache", "Boolean");
81   st->fill_p    = get_boolean_resource (st->dpy, "fillScreen", "Boolean");
82
83   st->max_depth = get_integer_resource (st->dpy, "maxDepth", "MaxDepth");
84   if (st->max_depth < 100) st->max_depth = 100;
85
86   st->duration = get_float_resource (st->dpy, "duration", "Seconds");
87   if (st->duration < 1) st->duration = 1;
88
89   st->duration2 = get_float_resource (st->dpy, "duration2", "Seconds");
90   if (st->duration2 < 0.001) st->duration = 0.001;
91
92   XClearWindow(st->dpy, st->window);
93
94   return st;
95 }
96
97
98 /* Given a bitmask, returns the position and width of the field.
99  */
100 static void
101 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
102 {
103   int i;
104   for (i = 0; i < 32; i++)
105     if (mask & (1L << i))
106       {
107         int j = 0;
108         *pos_ret = i;
109         for (; i < 32; i++, j++)
110           if (! (mask & (1L << i)))
111             break;
112         *size_ret = j;
113         return;
114       }
115 }
116
117
118 static unsigned long
119 pixel_distance (Visual *v, unsigned long p1, unsigned long p2)
120 {
121   static int initted_p = 0;
122   static unsigned int rmsk=0, gmsk=0, bmsk=0;
123   static unsigned int rpos=0, gpos=0, bpos=0;
124   static unsigned int rsiz=0, gsiz=0, bsiz=0;
125
126   unsigned char r1, g1, b1;
127   unsigned char r2, g2, b2;
128   long distance;
129
130   if (!p1 && !p2) return 0;
131
132   if (! initted_p) {
133     rmsk = v->red_mask;
134     gmsk = v->green_mask;
135     bmsk = v->blue_mask;
136     decode_mask (rmsk, &rpos, &rsiz);
137     decode_mask (gmsk, &gpos, &gsiz);
138     decode_mask (bmsk, &bpos, &bsiz);
139     initted_p = 1;
140   }
141
142   r1 = (p1 & rmsk) >> rpos;
143   g1 = (p1 & gmsk) >> gpos;
144   b1 = (p1 & bmsk) >> bpos;
145
146   r2 = (p2 & rmsk) >> rpos;
147   g2 = (p2 & gmsk) >> gpos;
148   b2 = (p2 & bmsk) >> bpos;
149
150 #if 0
151   /* Compute the distance in linear RGB space.
152    */
153   distance = cbrt (((r2 - r1) * (r2 - r1)) +
154                    ((g2 - g1) * (g2 - g1)) +
155                    ((b2 - b1) * (b2 - b1)));
156
157 # elif 1
158   /* Compute the distance in luminance-weighted RGB space.
159    */
160   {
161     int rd = (r2 - r1) * 0.2989 * (1 / 0.5870);
162     int gd = (g2 - g1) * 0.5870 * (1 / 0.5870);
163     int bd = (b2 - b1) * 0.1140 * (1 / 0.5870);
164     distance = cbrt ((rd * rd) + (gd * gd) + (bd * bd));
165   }
166 # else
167   /* Compute the distance in brightness-weighted HSV space.
168      (Slower, and doesn't seem to look better than luminance RGB.)
169    */
170   {
171     int h1, h2;
172     double s1, s2;
173     double v1, v2;
174     double hd, sd, vd, dd;
175     rgb_to_hsv (r1, g1, b1, &h1, &s1, &v1);
176     rgb_to_hsv (r2, g2, b2, &h2, &s2, &v2);
177
178     hd = abs (h2 - h1);
179     if (hd >= 180) hd -= 180;
180     hd /= 180.0;
181     sd = fabs (s2 - s1);
182     vd = fabs (v2 - v1);
183
184     /* [hsv]d are now the distance as 0.0 - 1.0. */
185     /* Compute the overall distance, giving more weight to V. */
186     dd = (hd * 0.25 + sd * 0.25 + vd * 0.5);
187
188     if (dd < 0 || dd > 1.0) abort();
189     distance = dd * 255;
190   }
191 # endif
192
193   if (distance < 0) distance = -distance;
194   return distance;
195 }
196
197
198 static void
199 flush_cache (struct state *st)
200 {
201   int i;
202   for (i = 0; i < countof(st->cache); i++)
203     if (st->cache[i])
204       {
205         XFreePixmap (st->dpy, st->cache[i]);
206         st->cache[i] = 0;
207       }
208   if (st->deltap)
209     {
210       XFreePixmap (st->dpy, st->deltap);
211       st->deltap = 0;
212     }
213 }
214
215
216 /* Scale up the bits in st->img so that it fills the screen, centered.
217  */
218 static void
219 scale_image (struct state *st)
220 {
221   double scale, s1, s2;
222   XImage *img2;
223   int x, y, cx, cy;
224
225   if (st->geom.width <= 0 || st->geom.height <= 0)
226     return;
227
228   s1 = st->geom.width  / (double) st->img->width;
229   s2 = st->geom.height / (double) st->img->height;
230   scale = (s1 < s2 ? s1 : s2);
231
232   img2 = XCreateImage (st->dpy, st->xgwa.visual, st->img->depth,
233                        ZPixmap, 0, NULL,
234                        st->img->width, st->img->height, 8, 0);
235   if (! img2) abort();
236   img2->data = (char *) calloc (img2->height, img2->bytes_per_line);
237   if (! img2->data) abort();
238
239   cx = st->img->width  / 2;
240   cy = st->img->height / 2;
241
242   if (st->geom.width < st->geom.height)  /* portrait: aim toward the top */
243     cy = st->img->height / (2 / scale);
244
245   for (y = 0; y < img2->height; y++)
246     for (x = 0; x < img2->width; x++)
247       {
248         int x2 = cx + ((x - cx) * scale);
249         int y2 = cy + ((y - cy) * scale);
250         unsigned long p = 0;
251         if (x2 >= 0 && y2 >= 0 &&
252             x2 < st->img->width && y2 < st->img->height)
253           p = XGetPixel (st->img, x2, y2);
254         XPutPixel (img2, x, y, p);
255       }
256   free (st->img->data);
257   st->img->data = 0;
258   XDestroyImage (st->img);
259   st->img = img2;
260
261   st->geom.x = 0;
262   st->geom.y = 0;
263   st->geom.width = st->img->width;
264   st->geom.height = st->img->height;
265 }
266
267
268
269 static void
270 analyze (struct state *st)
271 {
272   Window root;
273   int x, y, i;
274   unsigned int w, h, bw, d;
275   unsigned long histo[256];
276
277   flush_cache (st);
278
279   /* Convert the loaded pixmap to an XImage.
280    */
281   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
282   XGetGeometry (st->dpy, st->image, &root, &x, &y, &w, &h, &bw, &d);
283
284   if (st->img)
285     {
286       free (st->img->data);
287       st->img->data = 0;
288       XDestroyImage (st->img);
289     }
290   st->img = XGetImage (st->dpy, st->image, 0, 0, w, h, ~0L, ZPixmap);
291
292   if (st->fill_p) scale_image (st);
293
294   /* Create the delta map: color space distance between each pixel.
295      Maybe doing running a Sobel Filter matrix on this would be a
296      better idea.  That might be a bit faster, but I think it would
297      make no visual difference.
298    */
299   if (st->delta)
300     {
301       free (st->delta->data);
302       st->delta->data = 0;
303       XDestroyImage (st->delta);
304     }
305   st->delta = XCreateImage (st->dpy, st->xgwa.visual, d, ZPixmap, 0, NULL,
306                             w, h, 32, 0);
307   st->delta->data = (char *)
308     calloc (st->delta->height, st->delta->bytes_per_line);
309
310   for (y = 0; y < st->delta->height; y++)
311     {
312       for (x = 0; x < st->delta->width; x++)
313         {
314           unsigned long pixels[5];
315           int i = 0;
316           int distance = 0;
317           pixels[i++] =                     XGetPixel (st->img, x,   y);
318           pixels[i++] = (x > 0 && y > 0   ? XGetPixel (st->img, x-1, y-1) : 0);
319           pixels[i++] = (         y > 0   ? XGetPixel (st->img, x,   y-1) : 0);
320           pixels[i++] = (x > 0            ? XGetPixel (st->img, x-1, y)   : 0);
321           pixels[i++] = (x > 0 && y < h-1 ? XGetPixel (st->img, x-1, y+1) : 0);
322
323           for (i = 1; i < countof(pixels); i++)
324             distance += pixel_distance (st->xgwa.visual, pixels[0], pixels[i]);
325           distance /= countof(pixels)-1;
326           XPutPixel (st->delta, x, y, distance);
327         }
328     }
329
330   /* Collect a histogram of every distance value.
331    */
332   memset (histo, 0, sizeof(histo));
333   for (y = 0; y < st->delta->height; y++)
334     for (x = 0; x < st->delta->width; x++)
335       {
336         unsigned long p = XGetPixel (st->delta, x, y);
337         if (p > sizeof(histo)) abort();
338         histo[p]++;
339       }
340
341   /* Convert that from "occurrences of N" to ">= N".
342    */
343   for (i = countof(histo) - 1; i > 0; i--)
344     histo[i-1] += histo[i];
345
346 # if 0
347   fprintf (stderr, "%s: histo: ", progname);
348   for (i = 0; i < countof(histo); i++)
349     fprintf(stderr, "%d:%lu ", i, histo[i]);
350   fprintf(stderr, "\n");
351 # endif
352
353   /* Collect a useful set of threshold values, ignoring thresholds that
354      result in a very similar number of control points (since those images
355      probably won't look very different).
356    */
357
358   {
359     int max_vsize = st->max_depth;
360     int min_vsize = 20;
361     int min_delta = 100;
362
363     if (min_vsize > max_vsize/100)
364       min_vsize = max_vsize/100;
365
366     if (min_delta > max_vsize/1000)
367       min_delta = max_vsize/1000;
368
369     st->nthreshes = 0;
370     for (i = countof(histo)-1; i >= 0; i--)
371       {
372         unsigned long vsize = histo[i];
373
374         /* If this is a different vsize, push it. */
375         if (vsize >= min_vsize &&
376             vsize <= max_vsize &&
377             (st->nthreshes == 0 ||
378              vsize >= st->vsizes[st->nthreshes-1] + min_delta))
379           {
380             st->threshes[st->nthreshes] = i;
381             st->vsizes[st->nthreshes] = vsize;
382             st->nthreshes++;
383           }
384       }
385   }
386   
387   st->thresh = 0;   /* startup */
388   st->dthresh = 1;  /* forward */
389
390   if (st->output)
391     {
392       XFreePixmap (st->dpy, st->output);
393       st->output = 0;
394     }
395
396
397 # if 0
398   fprintf (stderr, "%s: threshes:", progname);
399   for (i = 0; i < st->nthreshes; i++)
400     fprintf (stderr, " %d=%d", st->threshes[i], st->vsizes[i]);
401   fprintf (stderr, "\n");
402 # endif
403 }
404
405
406 #ifndef DO_VORONOI
407 /* True if the distance between any two corners is too small for it to
408    make sense to draw an outline around this triangle.
409  */
410 static Bool
411 small_triangle_p (const XPoint *p)
412 {
413   int min = 4;
414   if (abs (p[0].x - p[1].x) < min) return True;
415   if (abs (p[0].y - p[1].y) < min) return True;
416   if (abs (p[1].x - p[2].x) < min) return True;
417   if (abs (p[1].y - p[2].y) < min) return True;
418   if (abs (p[2].x - p[0].x) < min) return True;
419   if (abs (p[2].y - p[0].y) < min) return True;
420   return False;
421 }
422 #endif /* DO_VORONOI */
423
424 #ifdef DO_VORONOI
425
426 typedef struct {
427   int npoints;
428   XPoint *p;
429 } voronoi_polygon;
430
431 static voronoi_polygon *
432 delaunay_to_voronoi (int np, XYZ *p, int nv, ITRIANGLE *v)
433 {
434   struct tri_list {
435     int count, size;
436     int *tri;
437   };
438
439   int i, j;
440   struct tri_list *vert_to_tri = (struct tri_list *)
441     calloc (np + 1, sizeof(*vert_to_tri));
442   voronoi_polygon *out = (voronoi_polygon *) calloc (np + 1, sizeof(*out));
443
444 /*
445   for (i = 0; i < np; i++)
446     printf("# p %d = %d %d\n", i, (int)p[i].x, (int)p[i].y);
447   printf("\n");
448   for (i = 0; i < nv; i++)
449     printf("@ t %d = %d %d %d\n", i, (int)v[i].p1, (int)v[i].p2, (int)v[i].p3);
450   printf("\n");
451 */
452
453   /* Iterate the triangles to construct a map of vertices to the
454      triangles that contain them.
455    */
456   for (i = 0; i < nv; i++)
457     {
458       for (j = 0; j < 3; j++)   /* iterate points in each triangle */
459         {
460           int p = *((&v[i].p1) + j);
461           struct tri_list *t = &vert_to_tri[p];
462           if (p < 0 || p >= np) abort();
463           if (t->size <= t->count + 1)
464             {
465               t->size += 3;
466               t->size *= 1.3;
467               t->tri = realloc (t->tri, t->size * sizeof(*t->tri));
468               if (! t->tri) abort();
469             }
470           t->tri[t->count++] = i;
471         }
472     }
473
474 /*
475   for (i = 0; i < nv; i++)
476     {
477       struct tri_list *t = &vert_to_tri[i];
478       printf("p %d [%d %d]:", i, (int)p[i].x, (int)p[i].y);
479       for (j = 0; j < t->count; j++) {
480         int tt = t->tri[j];
481         printf(" t %d [%d(%d %d) %d(%d %d) %d(%d %d)]",
482                tt,
483                (int)v[tt].p1,
484                (int)p[v[tt].p1].x, (int)p[v[tt].p1].y,
485                (int)v[tt].p2,
486                (int)p[v[tt].p2].x, (int)p[v[tt].p2].y,
487                (int)v[tt].p3,
488                (int)p[v[tt].p3].x, (int)p[v[tt].p3].y
489                );
490         if (tt < 0 || tt >= nv) abort();
491       }
492       printf("\n");
493     }
494 */
495
496   /* For every vertex, compose a polygon whose corners are the centers
497      of each triangle using that vertex.  Skip any with less than 3 points.
498    */
499   for (i = 0; i < np; i++)
500     {
501       struct tri_list *t = &vert_to_tri[i];
502       int n = t->count;
503       if (n < 3) n = 0;
504       out[i].npoints = n;
505       out[i].p = (n > 0
506                   ? (XPoint *) calloc (out[i].npoints + 1, sizeof (*out[i].p))
507                   : 0);
508 //printf("%d: ", i);
509       for (j = 0; j < out[i].npoints; j++)
510         {
511           ITRIANGLE *tt = &v[t->tri[j]];
512           out[i].p[j].x = (p[tt->p1].x + p[tt->p2].x + p[tt->p3].x) / 3;
513           out[i].p[j].y = (p[tt->p1].y + p[tt->p2].y + p[tt->p3].y) / 3;
514 //printf(" [%d: %d %d]", j, out[i].p[j].x, out[i].p[j].y);
515         }
516 //printf("\n");
517     }
518
519   free (vert_to_tri);
520   return out;
521 }
522
523 #endif /* DO_VORONOI */
524
525
526
527
528 static void
529 tessellate (struct state *st)
530 {
531   Bool ticked_p = False;
532
533   if (! st->image) return;
534
535   if (! st->wgc)
536     {
537       XGCValues gcv;
538       gcv.function = GXcopy;
539       gcv.subwindow_mode = IncludeInferiors;
540       st->wgc = XCreateGC(st->dpy, st->window, GCFunction, &gcv);
541       st->pgc = XCreateGC(st->dpy, st->image, GCFunction, &gcv);
542     }
543
544   if (! st->nthreshes) return;
545
546
547   /* If duration2 has expired, switch to the next threshold. */
548
549   if (! st->button_down_p)
550     {
551       double t2 = double_time();
552       if (st->start_time2 + st->duration2 < t2)
553         {
554           st->start_time2 = t2;
555           st->thresh += st->dthresh;
556           ticked_p = True;
557           if (st->thresh >= st->nthreshes)
558             {
559               st->thresh = st->nthreshes - 1;
560               st->dthresh = -1;
561             }
562           else if (st->thresh < 0)
563             {
564               st->thresh = 0;
565               st->dthresh = 1;
566             }
567         }
568     }
569
570   if (! st->output)
571     ticked_p = True;
572
573   /* If we've picked a new threshold, regenerate the output image. */
574
575   if (ticked_p && st->cache[st->thresh])
576     {
577       if (st->output)
578         XCopyArea (st->dpy, 
579                    st->cache[st->thresh],
580                    st->output, st->pgc,
581                    0, 0, st->delta->width, st->delta->height, 
582                    0, 0);
583     }
584   else if (ticked_p)
585     {
586       int threshold = st->threshes[st->thresh];
587       int vsize = st->vsizes[st->thresh];
588       ITRIANGLE *v;
589       XYZ *p = 0;
590       int nv = 0;
591       int ntri = 0;
592       int x, y, i;
593
594 #if 0
595       fprintf(stderr, "%s: thresh %d/%d = %d=%d\n", 
596               progname, st->thresh, st->nthreshes, threshold, vsize);
597 #endif
598
599       /* Create a control point at every pixel where the delta is above
600          the current threshold.  Triangulate from those. */
601
602       vsize += 8;  /* corners of screen + corners of image */
603
604       p = (XYZ *) calloc (vsize+4, sizeof(*p));
605       v = (ITRIANGLE *) calloc (3*(vsize+4), sizeof(*v));
606       if (!p || !v)
607         {
608           fprintf (stderr, "%s: out of memory (%d)\n", progname, vsize);
609           abort();
610         }
611
612       /* Add control points for the corners of the screen, and for the
613          corners of the image.
614        */
615       if (st->geom.width  <= 0) st->geom.width  = st->delta->width;
616       if (st->geom.height <= 0) st->geom.height = st->delta->height;
617
618       for (y = 0; y <= 1; y++)
619         for (x = 0; x <= 1; x++)
620           {
621             p[nv].x = x ? st->delta->width-1  : 0;
622             p[nv].y = y ? st->delta->height-1 : 0;
623             p[nv].z = XGetPixel (st->delta, (int) p[nv].x, (int) p[nv].y);
624             nv++;
625             p[nv].x = st->geom.x + (x ? st->geom.width-1  : 0);
626             p[nv].y = st->geom.y + (y ? st->geom.height-1 : 0);
627             p[nv].z = XGetPixel (st->delta, (int) p[nv].x, (int) p[nv].y);
628             nv++;
629           }
630
631       /* Add control points for every pixel that exceeds the threshold.
632        */
633       for (y = 0; y < st->delta->height; y++)
634         for (x = 0; x < st->delta->width; x++)
635           {
636             unsigned long px = XGetPixel (st->delta, x, y);
637             if (px >= threshold)
638               {
639                 if (nv >= vsize) abort();
640                 p[nv].x = x;
641                 p[nv].y = y;
642                 p[nv].z = px;
643                 nv++;
644               }
645           }
646
647       if (nv != vsize) abort();
648
649       qsort (p, nv, sizeof(*p), delaunay_xyzcompare);
650       if (delaunay (nv, p, v, &ntri))
651         {
652           fprintf (stderr, "%s: out of memory\n", progname);
653           abort();
654         }
655
656       /* Create the output pixmap based on that triangulation. */
657
658       if (st->output)
659         XFreePixmap (st->dpy, st->output);
660       st->output = XCreatePixmap (st->dpy, st->window,
661                                   st->delta->width, st->delta->height,
662                                   st->xgwa.depth);
663       XFillRectangle (st->dpy, st->output, st->pgc, 
664                       0, 0, st->delta->width, st->delta->height);
665
666 #ifdef DO_VORONOI
667
668       voronoi_polygon *polys = delaunay_to_voronoi (nv, p, ntri, v);
669       for (i = 0; i < nv; i++)
670         {
671           if (polys[i].npoints >= 3)
672             {
673               unsigned long color = XGetPixel (st->img, p[i].x, p[i].y);
674               XSetForeground (st->dpy, st->pgc, color);
675               XFillPolygon (st->dpy, st->output, st->pgc,
676                             polys[i].p, polys[i].npoints,
677                             Convex, CoordModeOrigin);
678
679               if (st->outline_p)
680                 {
681                   XColor bd;
682                   double scale = 0.8;
683                   bd.pixel = color;
684                   XQueryColor (st->dpy, st->xgwa.colormap, &bd);
685                   bd.red   *= scale;
686                   bd.green *= scale;
687                   bd.blue  *= scale;
688
689                   /* bd.red = 0xFFFF; bd.green = 0; bd.blue = 0; */
690
691                   XAllocColor (st->dpy, st->xgwa.colormap, &bd);
692                   XSetForeground (st->dpy, st->pgc, bd.pixel);
693                   XDrawLines (st->dpy, st->output, st->pgc,
694                               polys[i].p, polys[i].npoints,
695                               CoordModeOrigin);
696                   XFreeColors (st->dpy, st->xgwa.colormap, &bd.pixel, 1, 0);
697                 }
698             }
699           if (polys[i].p) free (polys[i].p);
700           polys[i].p = 0;
701         }
702       free (polys);
703
704 #else /* !DO_VORONOI */
705
706       for (i = 0; i < ntri; i++)
707         {
708           XPoint xp[3];
709           unsigned long color;
710           xp[0].x = p[v[i].p1].x; xp[0].y = p[v[i].p1].y;
711           xp[1].x = p[v[i].p2].x; xp[1].y = p[v[i].p2].y;
712           xp[2].x = p[v[i].p3].x; xp[2].y = p[v[i].p3].y;
713
714           /* Set the color of this triangle to the pixel at its midpoint. */
715           color = XGetPixel (st->img,
716                              (xp[0].x + xp[1].x + xp[2].x) / 3,
717                              (xp[0].y + xp[1].y + xp[2].y) / 3);
718
719           XSetForeground (st->dpy, st->pgc, color);
720           XFillPolygon (st->dpy, st->output, st->pgc, xp, countof(xp),
721                         Convex, CoordModeOrigin);
722
723           if (st->outline_p && !small_triangle_p(xp))
724             {   /* Border the triangle with a color that is darker */
725               XColor bd;
726               double scale = 0.8;
727               bd.pixel = color;
728               XQueryColor (st->dpy, st->xgwa.colormap, &bd);
729               bd.red   *= scale;
730               bd.green *= scale;
731               bd.blue  *= scale;
732
733               /* bd.red = 0xFFFF; bd.green = 0; bd.blue = 0; */
734
735               XAllocColor (st->dpy, st->xgwa.colormap, &bd);
736               XSetForeground (st->dpy, st->pgc, bd.pixel);
737               XDrawLines (st->dpy, st->output, st->pgc,
738                           xp, countof(xp), CoordModeOrigin);
739               XFreeColors (st->dpy, st->xgwa.colormap, &bd.pixel, 1, 0);
740             }
741         }
742 #endif /* !DO_VORONOI */
743
744       free (p);
745       free (v);
746
747       if (st->cache_p && !st->cache[st->thresh])
748         {
749           st->cache[st->thresh] =
750             XCreatePixmap (st->dpy, st->window,
751                            st->delta->width, st->delta->height,
752                            st->xgwa.depth);
753           if (! st->cache[st->thresh])
754             {
755               fprintf (stderr, "%s: out of memory\n", progname);
756               abort();
757             }
758           if (st->output)
759             XCopyArea (st->dpy, 
760                        st->output,
761                        st->cache[st->thresh],
762                        st->pgc,
763                        0, 0, st->delta->width, st->delta->height, 
764                        0, 0);
765         }
766     }
767
768   if (! st->output) abort();
769 }
770
771
772 /* Convert the delta map into a displayable pixmap.
773  */
774 static Pixmap
775 get_deltap (struct state *st)
776 {
777   int x, y;
778   int w = st->delta->width;
779   int h = st->delta->height;
780   XImage *dimg;
781
782   Visual *v = st->xgwa.visual;
783   unsigned int rmsk=0, gmsk=0, bmsk=0;
784   unsigned int rpos=0, gpos=0, bpos=0;
785   unsigned int rsiz=0, gsiz=0, bsiz=0;
786
787   if (st->deltap) return st->deltap;
788
789   rmsk = v->red_mask;
790   gmsk = v->green_mask;
791   bmsk = v->blue_mask;
792   decode_mask (rmsk, &rpos, &rsiz);
793   decode_mask (gmsk, &gpos, &gsiz);
794   decode_mask (bmsk, &bpos, &bsiz);
795
796   dimg = XCreateImage (st->dpy, st->xgwa.visual, st->xgwa.depth,
797                        ZPixmap, 0, NULL, w, h, 8, 0);
798   if (! dimg) abort();
799   dimg->data = (char *) calloc (dimg->height, dimg->bytes_per_line);
800   if (! dimg->data) abort();
801
802   for (y = 0; y < h; y++)
803     for (x = 0; x < w; x++)
804       {
805         unsigned long v = XGetPixel (st->delta, x, y) << 5;
806         unsigned long p = (((v << rpos) & rmsk) |
807                            ((v << gpos) & gmsk) |
808                            ((v << bpos) & bmsk));
809         XPutPixel (dimg, x, y, p);
810       }
811
812   st->deltap = XCreatePixmap (st->dpy, st->window, w, h, st->xgwa.depth);
813   XPutImage (st->dpy, st->deltap, st->pgc, dimg, 0, 0, 0, 0, w, h);
814   XDestroyImage (dimg);
815   return st->deltap;
816 }
817
818
819 static unsigned long
820 tessellimage_draw (Display *dpy, Window window, void *closure)
821 {
822   struct state *st = (struct state *) closure;
823
824   if (st->img_loader)   /* still loading */
825     {
826       st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0,
827                                                 &st->geom);
828       if (! st->img_loader) {  /* just finished */
829         analyze (st);
830         st->start_time = double_time();
831         st->start_time2 = st->start_time;
832       }
833       goto DONE;
834     }
835
836   if (!st->img_loader &&
837       st->start_time + st->duration < double_time()) {
838     XClearWindow (st->dpy, st->window);
839     if (st->image) XFreePixmap (dpy, st->image);
840     st->image = XCreatePixmap (st->dpy, st->window,
841                                st->xgwa.width, st->xgwa.height,
842                                st->xgwa.depth);
843     st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
844                                               st->image, 0, &st->geom);
845     goto DONE;
846   }
847
848   tessellate (st);
849
850   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
851   XClearWindow (st->dpy, st->window);
852
853   if (st->output)
854     XCopyArea (st->dpy, 
855                (st->button_down_p ? get_deltap (st) : st->output),
856                st->window, st->wgc,
857                0, 0, st->delta->width, st->delta->height, 
858                (st->xgwa.width  - st->delta->width)  / 2,
859                (st->xgwa.height - st->delta->height) / 2);
860   else if (!st->nthreshes)
861     XCopyArea (st->dpy,
862                st->image,
863                st->window, st->wgc,
864                0, 0, st->xgwa.width, st->xgwa.height,
865                0,
866                0);
867
868
869  DONE:
870   return st->delay;
871 }
872   
873 static void
874 tessellimage_reshape (Display *dpy, Window window, void *closure, 
875                  unsigned int w, unsigned int h)
876 {
877   struct state *st = (struct state *) closure;
878   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
879 }
880
881 static Bool
882 tessellimage_event (Display *dpy, Window window, void *closure, XEvent *event)
883 {
884   struct state *st = (struct state *) closure;
885   if (event->xany.type == ButtonPress)
886     {
887       st->button_down_p = True;
888       return True;
889     }
890   else if (event->xany.type == ButtonRelease)
891     {
892       st->button_down_p = False;
893       return True;
894     }
895   else if (screenhack_event_helper (dpy, window, event))
896     {
897       st->start_time = 0;   /* load next image */
898       return True;
899     }
900
901   return False;
902 }
903
904
905 static void
906 tessellimage_free (Display *dpy, Window window, void *closure)
907 {
908   struct state *st = (struct state *) closure;
909   flush_cache (st);
910   if (st->wgc) XFreeGC (dpy, st->wgc);
911   if (st->pgc) XFreeGC (dpy, st->pgc);
912   if (st->image)  XFreePixmap (dpy, st->image);
913   if (st->output) XFreePixmap (dpy, st->output);
914   if (st->delta)  XDestroyImage (st->delta);
915   free (st);
916 }
917
918
919 \f
920
921 static const char *tessellimage_defaults [] = {
922   ".background:                 black",
923   ".foreground:                 white",
924   "*dontClearRoot:              True",
925   "*fpsSolid:                   true",
926   "*delay:                      30000",
927   "*duration:                   120",
928   "*duration2:                  0.4",
929   "*maxDepth:                   30000",
930   "*outline:                    True",
931   "*fillScreen:                 True",
932   "*cache:                      True",
933 #ifdef HAVE_MOBILE
934   "*ignoreRotation:             True",
935   "*rotateImages:               True",
936 #endif
937   0
938 };
939
940 static XrmOptionDescRec tessellimage_options [] = {
941   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
942   { "-duration",        ".duration",            XrmoptionSepArg, 0 },
943   { "-duration2",       ".duration2",           XrmoptionSepArg, 0 },
944   { "-max-depth",       ".maxDepth",            XrmoptionSepArg, 0 },
945   { "-outline",         ".outline",             XrmoptionNoArg, "True"  },
946   { "-no-outline",      ".outline",             XrmoptionNoArg, "False" },
947   { "-fill-screen",     ".fillScreen",          XrmoptionNoArg, "True"  },
948   { "-no-fill-screen",  ".fillScreen",          XrmoptionNoArg, "False" },
949   { "-cache",           ".cache",               XrmoptionNoArg, "True"  },
950   { "-no-cache",        ".cache",               XrmoptionNoArg, "False" },
951   { 0, 0, 0, 0 }
952 };
953
954 XSCREENSAVER_MODULE ("Tessellimage", tessellimage)