From http://www.jwz.org/xscreensaver/xscreensaver-5.30.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_COCOA
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   Colormap cmap;
71
72   st->dpy = dpy;
73   st->window = window;
74
75   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
76   cmap = st->xgwa.colormap;
77
78   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
79   if (st->delay < 1) st->delay = 1;
80
81   st->outline_p = get_boolean_resource (st->dpy, "outline", "Boolean");
82   st->cache_p   = get_boolean_resource (st->dpy, "cache", "Boolean");
83   st->fill_p    = get_boolean_resource (st->dpy, "fillScreen", "Boolean");
84
85   st->max_depth = get_integer_resource (st->dpy, "maxDepth", "MaxDepth");
86   if (st->max_depth < 100) st->max_depth = 100;
87
88   st->duration = get_float_resource (st->dpy, "duration", "Seconds");
89   if (st->duration < 1) st->duration = 1;
90
91   st->duration2 = get_float_resource (st->dpy, "duration2", "Seconds");
92   if (st->duration2 < 0.001) st->duration = 0.001;
93
94   XClearWindow(st->dpy, st->window);
95
96   return st;
97 }
98
99
100 /* Given a bitmask, returns the position and width of the field.
101  */
102 static void
103 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
104 {
105   int i;
106   for (i = 0; i < 32; i++)
107     if (mask & (1L << i))
108       {
109         int j = 0;
110         *pos_ret = i;
111         for (; i < 32; i++, j++)
112           if (! (mask & (1L << i)))
113             break;
114         *size_ret = j;
115         return;
116       }
117 }
118
119
120 static unsigned long
121 pixel_distance (Visual *v, unsigned long p1, unsigned long p2)
122 {
123   static int initted_p = 0;
124   static unsigned int rmsk=0, gmsk=0, bmsk=0;
125   static unsigned int rpos=0, gpos=0, bpos=0;
126   static unsigned int rsiz=0, gsiz=0, bsiz=0;
127
128   unsigned char r1, g1, b1;
129   unsigned char r2, g2, b2;
130   long distance;
131
132   if (!p1 && !p2) return 0;
133
134   if (! initted_p) {
135     rmsk = v->red_mask;
136     gmsk = v->green_mask;
137     bmsk = v->blue_mask;
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.visual, pixels[0], pixels[i]);
327           distance /= countof(pixels)-1;
328           XPutPixel (st->delta, x, y, distance);
329         }
330     }
331
332   /* Collect a histogram of every distance value.
333    */
334   memset (histo, 0, sizeof(histo));
335   for (y = 0; y < st->delta->height; y++)
336     for (x = 0; x < st->delta->width; x++)
337       {
338         unsigned long p = XGetPixel (st->delta, x, y);
339         if (p > sizeof(histo)) abort();
340         histo[p]++;
341       }
342
343   /* Convert that from "occurrences of N" to ">= N".
344    */
345   for (i = countof(histo) - 1; i > 0; i--)
346     histo[i-1] += histo[i];
347
348 # if 0
349   fprintf (stderr, "%s: histo: ", progname);
350   for (i = 0; i < countof(histo); i++)
351     fprintf(stderr, "%d:%lu ", i, histo[i]);
352   fprintf(stderr, "\n");
353 # endif
354
355   /* Collect a useful set of threshold values, ignoring thresholds that
356      result in a very similar number of control points (since those images
357      probably won't look very different).
358    */
359
360   {
361     int max_vsize = st->max_depth;
362     int min_vsize = 20;
363     int min_delta = 100;
364
365     if (min_vsize > max_vsize/100)
366       min_vsize = max_vsize/100;
367
368     if (min_delta > max_vsize/1000)
369       min_delta = max_vsize/1000;
370
371     st->nthreshes = 0;
372     for (i = countof(histo)-1; i >= 0; i--)
373       {
374         unsigned long vsize = histo[i];
375
376         /* If this is a different vsize, push it. */
377         if (vsize >= min_vsize &&
378             vsize <= max_vsize &&
379             (st->nthreshes == 0 ||
380              vsize >= st->vsizes[st->nthreshes-1] + min_delta))
381           {
382             st->threshes[st->nthreshes] = i;
383             st->vsizes[st->nthreshes] = vsize;
384             st->nthreshes++;
385           }
386       }
387   }
388   
389   st->thresh = 0;   /* startup */
390   st->dthresh = 1;  /* forward */
391
392   if (st->output)
393     {
394       XFreePixmap (st->dpy, st->output);
395       st->output = 0;
396     }
397
398
399 # if 0
400   fprintf (stderr, "%s: threshes:", progname);
401   for (i = 0; i < st->nthreshes; i++)
402     fprintf (stderr, " %d=%d", st->threshes[i], st->vsizes[i]);
403   fprintf (stderr, "\n");
404 # endif
405 }
406
407
408 #ifndef DO_VORONOI
409 /* True if the distance between any two corners is too small for it to
410    make sense to draw an outline around this triangle.
411  */
412 static Bool
413 small_triangle_p (const XPoint *p)
414 {
415   int min = 4;
416   if (abs (p[0].x - p[1].x) < min) return True;
417   if (abs (p[0].y - p[1].y) < min) return True;
418   if (abs (p[1].x - p[2].x) < min) return True;
419   if (abs (p[1].y - p[2].y) < min) return True;
420   if (abs (p[2].x - p[0].x) < min) return True;
421   if (abs (p[2].y - p[0].y) < min) return True;
422   return False;
423 }
424 #endif /* DO_VORONOI */
425
426 #ifdef DO_VORONOI
427
428 typedef struct {
429   int npoints;
430   XPoint *p;
431 } voronoi_polygon;
432
433 static voronoi_polygon *
434 delaunay_to_voronoi (int np, XYZ *p, int nv, ITRIANGLE *v)
435 {
436   struct tri_list {
437     int count, size;
438     int *tri;
439   };
440
441   int i, j;
442   struct tri_list *vert_to_tri = (struct tri_list *)
443     calloc (np + 1, sizeof(*vert_to_tri));
444   voronoi_polygon *out = (voronoi_polygon *) calloc (np + 1, sizeof(*out));
445
446 /*
447   for (i = 0; i < np; i++)
448     printf("# p %d = %d %d\n", i, (int)p[i].x, (int)p[i].y);
449   printf("\n");
450   for (i = 0; i < nv; i++)
451     printf("@ t %d = %d %d %d\n", i, (int)v[i].p1, (int)v[i].p2, (int)v[i].p3);
452   printf("\n");
453 */
454
455   /* Iterate the triangles to construct a map of vertices to the
456      triangles that contain them.
457    */
458   for (i = 0; i < nv; i++)
459     {
460       for (j = 0; j < 3; j++)   /* iterate points in each triangle */
461         {
462           int p = *((&v[i].p1) + j);
463           struct tri_list *t = &vert_to_tri[p];
464           if (p < 0 || p >= np) abort();
465           if (t->size <= t->count + 1)
466             {
467               t->size += 3;
468               t->size *= 1.3;
469               t->tri = realloc (t->tri, t->size * sizeof(*t->tri));
470               if (! t->tri) abort();
471             }
472           t->tri[t->count++] = i;
473         }
474     }
475
476 /*
477   for (i = 0; i < nv; i++)
478     {
479       struct tri_list *t = &vert_to_tri[i];
480       printf("p %d [%d %d]:", i, (int)p[i].x, (int)p[i].y);
481       for (j = 0; j < t->count; j++) {
482         int tt = t->tri[j];
483         printf(" t %d [%d(%d %d) %d(%d %d) %d(%d %d)]",
484                tt,
485                (int)v[tt].p1,
486                (int)p[v[tt].p1].x, (int)p[v[tt].p1].y,
487                (int)v[tt].p2,
488                (int)p[v[tt].p2].x, (int)p[v[tt].p2].y,
489                (int)v[tt].p3,
490                (int)p[v[tt].p3].x, (int)p[v[tt].p3].y
491                );
492         if (tt < 0 || tt >= nv) abort();
493       }
494       printf("\n");
495     }
496 */
497
498   /* For every vertex, compose a polygon whose corners are the centers
499      of each triangle using that vertex.  Skip any with less than 3 points.
500    */
501   for (i = 0; i < np; i++)
502     {
503       struct tri_list *t = &vert_to_tri[i];
504       int n = t->count;
505       if (n < 3) n = 0;
506       out[i].npoints = n;
507       out[i].p = (n > 0
508                   ? (XPoint *) calloc (out[i].npoints + 1, sizeof (*out[i].p))
509                   : 0);
510 //printf("%d: ", i);
511       for (j = 0; j < out[i].npoints; j++)
512         {
513           ITRIANGLE *tt = &v[t->tri[j]];
514           out[i].p[j].x = (p[tt->p1].x + p[tt->p2].x + p[tt->p3].x) / 3;
515           out[i].p[j].y = (p[tt->p1].y + p[tt->p2].y + p[tt->p3].y) / 3;
516 //printf(" [%d: %d %d]", j, out[i].p[j].x, out[i].p[j].y);
517         }
518 //printf("\n");
519     }
520
521   free (vert_to_tri);
522   return out;
523 }
524
525 #endif /* DO_VORONOI */
526
527
528
529
530 static void
531 tessellate (struct state *st)
532 {
533   Bool ticked_p = False;
534
535   if (! st->image) return;
536
537   if (! st->wgc)
538     {
539       XGCValues gcv;
540       gcv.function = GXcopy;
541       gcv.subwindow_mode = IncludeInferiors;
542       st->wgc = XCreateGC(st->dpy, st->window, GCFunction, &gcv);
543       st->pgc = XCreateGC(st->dpy, st->image, GCFunction, &gcv);
544     }
545
546   /* If duration2 has expired, switch to the next threshold. */
547
548   if (! st->button_down_p)
549     {
550       double t2 = double_time();
551       if (st->start_time2 + st->duration2 < t2)
552         {
553           st->start_time2 = t2;
554           st->thresh += st->dthresh;
555           ticked_p = True;
556           if (st->thresh >= st->nthreshes)
557             {
558               st->thresh = st->nthreshes - 1;
559               st->dthresh = -1;
560             }
561           else if (st->thresh < 0)
562             {
563               st->thresh = 0;
564               st->dthresh = 1;
565             }
566         }
567     }
568
569   if (! st->output)
570     ticked_p = True;
571
572   /* If we've picked a new threshold, regenerate the output image. */
573
574   if (ticked_p && st->cache[st->thresh])
575     {
576       if (st->output)
577         XCopyArea (st->dpy, 
578                    st->cache[st->thresh],
579                    st->output, st->pgc,
580                    0, 0, st->delta->width, st->delta->height, 
581                    0, 0);
582     }
583   else if (ticked_p)
584     {
585       int threshold = st->threshes[st->thresh];
586       int vsize = st->vsizes[st->thresh];
587       ITRIANGLE *v;
588       XYZ *p = 0;
589       int nv = 0;
590       int ntri = 0;
591       int x, y, i;
592
593 #if 0
594       fprintf(stderr, "%s: thresh %d/%d = %d=%d\n", 
595               progname, st->thresh, st->nthreshes, threshold, vsize);
596 #endif
597
598       /* Create a control point at every pixel where the delta is above
599          the current threshold.  Triangulate from those. */
600
601       vsize += 8;  /* corners of screen + corners of image */
602
603       p = (XYZ *) calloc (vsize+4, sizeof(*p));
604       v = (ITRIANGLE *) calloc (3*(vsize+4), sizeof(*v));
605       if (!p || !v)
606         {
607           fprintf (stderr, "%s: out of memory (%d)\n", progname, vsize);
608           abort();
609         }
610
611       /* Add control points for the corners of the screen, and for the
612          corners of the image.
613        */
614       if (st->geom.width  <= 0) st->geom.width  = st->delta->width;
615       if (st->geom.height <= 0) st->geom.height = st->delta->height;
616
617       for (y = 0; y <= 1; y++)
618         for (x = 0; x <= 1; x++)
619           {
620             p[nv].x = x ? st->delta->width-1  : 0;
621             p[nv].y = y ? st->delta->height-1 : 0;
622             p[nv].z = XGetPixel (st->delta, (int) p[nv].x, (int) p[nv].y);
623             nv++;
624             p[nv].x = st->geom.x + (x ? st->geom.width-1  : 0);
625             p[nv].y = st->geom.y + (y ? st->geom.height-1 : 0);
626             p[nv].z = XGetPixel (st->delta, (int) p[nv].x, (int) p[nv].y);
627             nv++;
628           }
629
630       /* Add control points for every pixel that exceeds the threshold.
631        */
632       for (y = 0; y < st->delta->height; y++)
633         for (x = 0; x < st->delta->width; x++)
634           {
635             unsigned long px = XGetPixel (st->delta, x, y);
636             if (px >= threshold)
637               {
638                 if (nv >= vsize) abort();
639                 p[nv].x = x;
640                 p[nv].y = y;
641                 p[nv].z = px;
642                 nv++;
643               }
644           }
645
646       if (nv != vsize) abort();
647
648       qsort (p, nv, sizeof(*p), delaunay_xyzcompare);
649       if (delaunay (nv, p, v, &ntri))
650         {
651           fprintf (stderr, "%s: out of memory\n", progname);
652           abort();
653         }
654
655       /* Create the output pixmap based on that triangulation. */
656
657       if (st->output)
658         XFreePixmap (st->dpy, st->output);
659       st->output = XCreatePixmap (st->dpy, st->window,
660                                   st->delta->width, st->delta->height,
661                                   st->xgwa.depth);
662       XFillRectangle (st->dpy, st->output, st->pgc, 
663                       0, 0, st->delta->width, st->delta->height);
664
665 #ifdef DO_VORONOI
666
667       voronoi_polygon *polys = delaunay_to_voronoi (nv, p, ntri, v);
668       for (i = 0; i < nv; i++)
669         {
670           if (polys[i].npoints >= 3)
671             {
672               unsigned long color = XGetPixel (st->img, p[i].x, p[i].y);
673               XSetForeground (st->dpy, st->pgc, color);
674               XFillPolygon (st->dpy, st->output, st->pgc,
675                             polys[i].p, polys[i].npoints,
676                             Convex, CoordModeOrigin);
677
678               if (st->outline_p)
679                 {
680                   XColor bd;
681                   double scale = 0.8;
682                   bd.pixel = color;
683                   XQueryColor (st->dpy, st->xgwa.colormap, &bd);
684                   bd.red   *= scale;
685                   bd.green *= scale;
686                   bd.blue  *= scale;
687
688                   /* bd.red = 0xFFFF; bd.green = 0; bd.blue = 0; */
689
690                   XAllocColor (st->dpy, st->xgwa.colormap, &bd);
691                   XSetForeground (st->dpy, st->pgc, bd.pixel);
692                   XDrawLines (st->dpy, st->output, st->pgc,
693                               polys[i].p, polys[i].npoints,
694                               CoordModeOrigin);
695                   XFreeColors (st->dpy, st->xgwa.colormap, &bd.pixel, 1, 0);
696                 }
697             }
698           if (polys[i].p) free (polys[i].p);
699           polys[i].p = 0;
700         }
701       free (polys);
702
703 #else /* !DO_VORONOI */
704
705       for (i = 0; i < ntri; i++)
706         {
707           XPoint xp[3];
708           unsigned long color;
709           xp[0].x = p[v[i].p1].x; xp[0].y = p[v[i].p1].y;
710           xp[1].x = p[v[i].p2].x; xp[1].y = p[v[i].p2].y;
711           xp[2].x = p[v[i].p3].x; xp[2].y = p[v[i].p3].y;
712
713           /* Set the color of this triangle to the pixel at its midpoint. */
714           color = XGetPixel (st->img,
715                              (xp[0].x + xp[1].x + xp[2].x) / 3,
716                              (xp[0].y + xp[1].y + xp[2].y) / 3);
717
718           XSetForeground (st->dpy, st->pgc, color);
719           XFillPolygon (st->dpy, st->output, st->pgc, xp, countof(xp),
720                         Convex, CoordModeOrigin);
721
722           if (st->outline_p && !small_triangle_p(xp))
723             {   /* Border the triangle with a color that is darker */
724               XColor bd;
725               double scale = 0.8;
726               bd.pixel = color;
727               XQueryColor (st->dpy, st->xgwa.colormap, &bd);
728               bd.red   *= scale;
729               bd.green *= scale;
730               bd.blue  *= scale;
731
732               /* bd.red = 0xFFFF; bd.green = 0; bd.blue = 0; */
733
734               XAllocColor (st->dpy, st->xgwa.colormap, &bd);
735               XSetForeground (st->dpy, st->pgc, bd.pixel);
736               XDrawLines (st->dpy, st->output, st->pgc,
737                           xp, countof(xp), CoordModeOrigin);
738               XFreeColors (st->dpy, st->xgwa.colormap, &bd.pixel, 1, 0);
739             }
740         }
741 #endif /* !DO_VORONOI */
742
743       free (p);
744       free (v);
745
746       if (st->cache_p && !st->cache[st->thresh])
747         {
748           st->cache[st->thresh] =
749             XCreatePixmap (st->dpy, st->window,
750                            st->delta->width, st->delta->height,
751                            st->xgwa.depth);
752           if (! st->cache[st->thresh])
753             {
754               fprintf (stderr, "%s: out of memory\n", progname);
755               abort();
756             }
757           if (st->output)
758             XCopyArea (st->dpy, 
759                        st->output,
760                        st->cache[st->thresh],
761                        st->pgc,
762                        0, 0, st->delta->width, st->delta->height, 
763                        0, 0);
764         }
765     }
766
767   if (! st->output) abort();
768 }
769
770
771 /* Convert the delta map into a displayable pixmap.
772  */
773 static Pixmap
774 get_deltap (struct state *st)
775 {
776   int x, y;
777   int w = st->delta->width;
778   int h = st->delta->height;
779   XImage *dimg;
780
781   Visual *v = st->xgwa.visual;
782   unsigned int rmsk=0, gmsk=0, bmsk=0;
783   unsigned int rpos=0, gpos=0, bpos=0;
784   unsigned int rsiz=0, gsiz=0, bsiz=0;
785
786   if (st->deltap) return st->deltap;
787
788   rmsk = v->red_mask;
789   gmsk = v->green_mask;
790   bmsk = v->blue_mask;
791   decode_mask (rmsk, &rpos, &rsiz);
792   decode_mask (gmsk, &gpos, &gsiz);
793   decode_mask (bmsk, &bpos, &bsiz);
794
795   dimg = XCreateImage (st->dpy, st->xgwa.visual, st->xgwa.depth,
796                        ZPixmap, 0, NULL, w, h, 8, 0);
797   if (! dimg) abort();
798   dimg->data = (char *) calloc (dimg->height, dimg->bytes_per_line);
799   if (! dimg->data) abort();
800
801   for (y = 0; y < h; y++)
802     for (x = 0; x < w; x++)
803       {
804         unsigned long v = XGetPixel (st->delta, x, y) << 5;
805         unsigned long p = (((v << rpos) & rmsk) |
806                            ((v << gpos) & gmsk) |
807                            ((v << bpos) & bmsk));
808         XPutPixel (dimg, x, y, p);
809       }
810
811   st->deltap = XCreatePixmap (st->dpy, st->window, w, h, st->xgwa.depth);
812   XPutImage (st->dpy, st->deltap, st->pgc, dimg, 0, 0, 0, 0, w, h);
813   XDestroyImage (dimg);
814   return st->deltap;
815 }
816
817
818 static unsigned long
819 tessellimage_draw (Display *dpy, Window window, void *closure)
820 {
821   struct state *st = (struct state *) closure;
822
823   if (st->img_loader)   /* still loading */
824     {
825       st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0,
826                                                 &st->geom);
827       if (! st->img_loader) {  /* just finished */
828         analyze (st);
829         st->start_time = double_time();
830         st->start_time2 = st->start_time;
831       }
832       goto DONE;
833     }
834
835   if (!st->img_loader &&
836       st->start_time + st->duration < double_time()) {
837     XClearWindow (st->dpy, st->window);
838     if (st->image) XFreePixmap (dpy, st->image);
839     st->image = XCreatePixmap (st->dpy, st->window,
840                                st->xgwa.width, st->xgwa.height,
841                                st->xgwa.depth);
842     st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
843                                               st->image, 0, &st->geom);
844     goto DONE;
845   }
846
847   tessellate (st);
848
849   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
850   XClearWindow (st->dpy, st->window);
851
852   if (st->output)
853     XCopyArea (st->dpy, 
854                (st->button_down_p ? get_deltap (st) : st->output),
855                st->window, st->wgc,
856                0, 0, st->delta->width, st->delta->height, 
857                (st->xgwa.width  - st->delta->width)  / 2,
858                (st->xgwa.height - st->delta->height) / 2);
859
860  DONE:
861   return st->delay;
862 }
863   
864 static void
865 tessellimage_reshape (Display *dpy, Window window, void *closure, 
866                  unsigned int w, unsigned int h)
867 {
868   struct state *st = (struct state *) closure;
869   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
870 }
871
872 static Bool
873 tessellimage_event (Display *dpy, Window window, void *closure, XEvent *event)
874 {
875   struct state *st = (struct state *) closure;
876   if (event->xany.type == ButtonPress)
877     {
878       st->button_down_p = True;
879       return True;
880     }
881   else if (event->xany.type == ButtonRelease)
882     {
883       st->button_down_p = False;
884       return True;
885     }
886   else if (screenhack_event_helper (dpy, window, event))
887     {
888       st->start_time = 0;   /* load next image */
889       return True;
890     }
891
892   return False;
893 }
894
895
896 static void
897 tessellimage_free (Display *dpy, Window window, void *closure)
898 {
899   struct state *st = (struct state *) closure;
900   flush_cache (st);
901   if (st->wgc) XFreeGC (dpy, st->wgc);
902   if (st->pgc) XFreeGC (dpy, st->pgc);
903   if (st->image)  XFreePixmap (dpy, st->image);
904   if (st->output) XFreePixmap (dpy, st->output);
905   if (st->delta)  XDestroyImage (st->delta);
906   free (st);
907 }
908
909
910 \f
911
912 static const char *tessellimage_defaults [] = {
913   ".background:                 black",
914   ".foreground:                 white",
915   "*dontClearRoot:              True",
916   "*fpsSolid:                   true",
917   "*delay:                      30000",
918   "*duration:                   120",
919   "*duration2:                  0.4",
920   "*maxDepth:                   30000",
921   "*outline:                    True",
922   "*fillScreen:                 True",
923   "*cache:                      True",
924 #ifdef USE_IPHONE
925   "*ignoreRotation:             True",
926   "*rotateImages:               True",
927 #endif
928   0
929 };
930
931 static XrmOptionDescRec tessellimage_options [] = {
932   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
933   { "-duration",        ".duration",            XrmoptionSepArg, 0 },
934   { "-duration2",       ".duration2",           XrmoptionSepArg, 0 },
935   { "-max-depth",       ".maxDepth",            XrmoptionSepArg, 0 },
936   { "-outline",         ".outline",             XrmoptionNoArg, "True"  },
937   { "-no-outline",      ".outline",             XrmoptionNoArg, "False" },
938   { "-fill-screen",     ".fillScreen",          XrmoptionNoArg, "True"  },
939   { "-no-fill-screen",  ".fillScreen",          XrmoptionNoArg, "False" },
940   { "-cache",           ".cache",               XrmoptionNoArg, "True"  },
941   { "-no-cache",        ".cache",               XrmoptionNoArg, "False" },
942   { 0, 0, 0, 0 }
943 };
944
945 XSCREENSAVER_MODULE ("Tessellimage", tessellimage)