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