d018f0cde4927ec531a693d815c401e0dc98049e
[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 (! st->nthreshes) return;
547
548
549   /* If duration2 has expired, switch to the next threshold. */
550
551   if (! st->button_down_p)
552     {
553       double t2 = double_time();
554       if (st->start_time2 + st->duration2 < t2)
555         {
556           st->start_time2 = t2;
557           st->thresh += st->dthresh;
558           ticked_p = True;
559           if (st->thresh >= st->nthreshes)
560             {
561               st->thresh = st->nthreshes - 1;
562               st->dthresh = -1;
563             }
564           else if (st->thresh < 0)
565             {
566               st->thresh = 0;
567               st->dthresh = 1;
568             }
569         }
570     }
571
572   if (! st->output)
573     ticked_p = True;
574
575   /* If we've picked a new threshold, regenerate the output image. */
576
577   if (ticked_p && st->cache[st->thresh])
578     {
579       if (st->output)
580         XCopyArea (st->dpy, 
581                    st->cache[st->thresh],
582                    st->output, st->pgc,
583                    0, 0, st->delta->width, st->delta->height, 
584                    0, 0);
585     }
586   else if (ticked_p)
587     {
588       int threshold = st->threshes[st->thresh];
589       int vsize = st->vsizes[st->thresh];
590       ITRIANGLE *v;
591       XYZ *p = 0;
592       int nv = 0;
593       int ntri = 0;
594       int x, y, i;
595
596 #if 0
597       fprintf(stderr, "%s: thresh %d/%d = %d=%d\n", 
598               progname, st->thresh, st->nthreshes, threshold, vsize);
599 #endif
600
601       /* Create a control point at every pixel where the delta is above
602          the current threshold.  Triangulate from those. */
603
604       vsize += 8;  /* corners of screen + corners of image */
605
606       p = (XYZ *) calloc (vsize+4, sizeof(*p));
607       v = (ITRIANGLE *) calloc (3*(vsize+4), sizeof(*v));
608       if (!p || !v)
609         {
610           fprintf (stderr, "%s: out of memory (%d)\n", progname, vsize);
611           abort();
612         }
613
614       /* Add control points for the corners of the screen, and for the
615          corners of the image.
616        */
617       if (st->geom.width  <= 0) st->geom.width  = st->delta->width;
618       if (st->geom.height <= 0) st->geom.height = st->delta->height;
619
620       for (y = 0; y <= 1; y++)
621         for (x = 0; x <= 1; x++)
622           {
623             p[nv].x = x ? st->delta->width-1  : 0;
624             p[nv].y = y ? st->delta->height-1 : 0;
625             p[nv].z = XGetPixel (st->delta, (int) p[nv].x, (int) p[nv].y);
626             nv++;
627             p[nv].x = st->geom.x + (x ? st->geom.width-1  : 0);
628             p[nv].y = st->geom.y + (y ? st->geom.height-1 : 0);
629             p[nv].z = XGetPixel (st->delta, (int) p[nv].x, (int) p[nv].y);
630             nv++;
631           }
632
633       /* Add control points for every pixel that exceeds the threshold.
634        */
635       for (y = 0; y < st->delta->height; y++)
636         for (x = 0; x < st->delta->width; x++)
637           {
638             unsigned long px = XGetPixel (st->delta, x, y);
639             if (px >= threshold)
640               {
641                 if (nv >= vsize) abort();
642                 p[nv].x = x;
643                 p[nv].y = y;
644                 p[nv].z = px;
645                 nv++;
646               }
647           }
648
649       if (nv != vsize) abort();
650
651       qsort (p, nv, sizeof(*p), delaunay_xyzcompare);
652       if (delaunay (nv, p, v, &ntri))
653         {
654           fprintf (stderr, "%s: out of memory\n", progname);
655           abort();
656         }
657
658       /* Create the output pixmap based on that triangulation. */
659
660       if (st->output)
661         XFreePixmap (st->dpy, st->output);
662       st->output = XCreatePixmap (st->dpy, st->window,
663                                   st->delta->width, st->delta->height,
664                                   st->xgwa.depth);
665       XFillRectangle (st->dpy, st->output, st->pgc, 
666                       0, 0, st->delta->width, st->delta->height);
667
668 #ifdef DO_VORONOI
669
670       voronoi_polygon *polys = delaunay_to_voronoi (nv, p, ntri, v);
671       for (i = 0; i < nv; i++)
672         {
673           if (polys[i].npoints >= 3)
674             {
675               unsigned long color = XGetPixel (st->img, p[i].x, p[i].y);
676               XSetForeground (st->dpy, st->pgc, color);
677               XFillPolygon (st->dpy, st->output, st->pgc,
678                             polys[i].p, polys[i].npoints,
679                             Convex, CoordModeOrigin);
680
681               if (st->outline_p)
682                 {
683                   XColor bd;
684                   double scale = 0.8;
685                   bd.pixel = color;
686                   XQueryColor (st->dpy, st->xgwa.colormap, &bd);
687                   bd.red   *= scale;
688                   bd.green *= scale;
689                   bd.blue  *= scale;
690
691                   /* bd.red = 0xFFFF; bd.green = 0; bd.blue = 0; */
692
693                   XAllocColor (st->dpy, st->xgwa.colormap, &bd);
694                   XSetForeground (st->dpy, st->pgc, bd.pixel);
695                   XDrawLines (st->dpy, st->output, st->pgc,
696                               polys[i].p, polys[i].npoints,
697                               CoordModeOrigin);
698                   XFreeColors (st->dpy, st->xgwa.colormap, &bd.pixel, 1, 0);
699                 }
700             }
701           if (polys[i].p) free (polys[i].p);
702           polys[i].p = 0;
703         }
704       free (polys);
705
706 #else /* !DO_VORONOI */
707
708       for (i = 0; i < ntri; i++)
709         {
710           XPoint xp[3];
711           unsigned long color;
712           xp[0].x = p[v[i].p1].x; xp[0].y = p[v[i].p1].y;
713           xp[1].x = p[v[i].p2].x; xp[1].y = p[v[i].p2].y;
714           xp[2].x = p[v[i].p3].x; xp[2].y = p[v[i].p3].y;
715
716           /* Set the color of this triangle to the pixel at its midpoint. */
717           color = XGetPixel (st->img,
718                              (xp[0].x + xp[1].x + xp[2].x) / 3,
719                              (xp[0].y + xp[1].y + xp[2].y) / 3);
720
721           XSetForeground (st->dpy, st->pgc, color);
722           XFillPolygon (st->dpy, st->output, st->pgc, xp, countof(xp),
723                         Convex, CoordModeOrigin);
724
725           if (st->outline_p && !small_triangle_p(xp))
726             {   /* Border the triangle with a color that is darker */
727               XColor bd;
728               double scale = 0.8;
729               bd.pixel = color;
730               XQueryColor (st->dpy, st->xgwa.colormap, &bd);
731               bd.red   *= scale;
732               bd.green *= scale;
733               bd.blue  *= scale;
734
735               /* bd.red = 0xFFFF; bd.green = 0; bd.blue = 0; */
736
737               XAllocColor (st->dpy, st->xgwa.colormap, &bd);
738               XSetForeground (st->dpy, st->pgc, bd.pixel);
739               XDrawLines (st->dpy, st->output, st->pgc,
740                           xp, countof(xp), CoordModeOrigin);
741               XFreeColors (st->dpy, st->xgwa.colormap, &bd.pixel, 1, 0);
742             }
743         }
744 #endif /* !DO_VORONOI */
745
746       free (p);
747       free (v);
748
749       if (st->cache_p && !st->cache[st->thresh])
750         {
751           st->cache[st->thresh] =
752             XCreatePixmap (st->dpy, st->window,
753                            st->delta->width, st->delta->height,
754                            st->xgwa.depth);
755           if (! st->cache[st->thresh])
756             {
757               fprintf (stderr, "%s: out of memory\n", progname);
758               abort();
759             }
760           if (st->output)
761             XCopyArea (st->dpy, 
762                        st->output,
763                        st->cache[st->thresh],
764                        st->pgc,
765                        0, 0, st->delta->width, st->delta->height, 
766                        0, 0);
767         }
768     }
769
770   if (! st->output) abort();
771 }
772
773
774 /* Convert the delta map into a displayable pixmap.
775  */
776 static Pixmap
777 get_deltap (struct state *st)
778 {
779   int x, y;
780   int w = st->delta->width;
781   int h = st->delta->height;
782   XImage *dimg;
783
784   Visual *v = st->xgwa.visual;
785   unsigned int rmsk=0, gmsk=0, bmsk=0;
786   unsigned int rpos=0, gpos=0, bpos=0;
787   unsigned int rsiz=0, gsiz=0, bsiz=0;
788
789   if (st->deltap) return st->deltap;
790
791   rmsk = v->red_mask;
792   gmsk = v->green_mask;
793   bmsk = v->blue_mask;
794   decode_mask (rmsk, &rpos, &rsiz);
795   decode_mask (gmsk, &gpos, &gsiz);
796   decode_mask (bmsk, &bpos, &bsiz);
797
798   dimg = XCreateImage (st->dpy, st->xgwa.visual, st->xgwa.depth,
799                        ZPixmap, 0, NULL, w, h, 8, 0);
800   if (! dimg) abort();
801   dimg->data = (char *) calloc (dimg->height, dimg->bytes_per_line);
802   if (! dimg->data) abort();
803
804   for (y = 0; y < h; y++)
805     for (x = 0; x < w; x++)
806       {
807         unsigned long v = XGetPixel (st->delta, x, y) << 5;
808         unsigned long p = (((v << rpos) & rmsk) |
809                            ((v << gpos) & gmsk) |
810                            ((v << bpos) & bmsk));
811         XPutPixel (dimg, x, y, p);
812       }
813
814   st->deltap = XCreatePixmap (st->dpy, st->window, w, h, st->xgwa.depth);
815   XPutImage (st->dpy, st->deltap, st->pgc, dimg, 0, 0, 0, 0, w, h);
816   XDestroyImage (dimg);
817   return st->deltap;
818 }
819
820
821 static unsigned long
822 tessellimage_draw (Display *dpy, Window window, void *closure)
823 {
824   struct state *st = (struct state *) closure;
825
826   if (st->img_loader)   /* still loading */
827     {
828       st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0,
829                                                 &st->geom);
830       if (! st->img_loader) {  /* just finished */
831         analyze (st);
832         st->start_time = double_time();
833         st->start_time2 = st->start_time;
834       }
835       goto DONE;
836     }
837
838   if (!st->img_loader &&
839       st->start_time + st->duration < double_time()) {
840     XClearWindow (st->dpy, st->window);
841     if (st->image) XFreePixmap (dpy, st->image);
842     st->image = XCreatePixmap (st->dpy, st->window,
843                                st->xgwa.width, st->xgwa.height,
844                                st->xgwa.depth);
845     st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
846                                               st->image, 0, &st->geom);
847     goto DONE;
848   }
849
850   tessellate (st);
851
852   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
853   XClearWindow (st->dpy, st->window);
854
855   if (st->output)
856     XCopyArea (st->dpy, 
857                (st->button_down_p ? get_deltap (st) : st->output),
858                st->window, st->wgc,
859                0, 0, st->delta->width, st->delta->height, 
860                (st->xgwa.width  - st->delta->width)  / 2,
861                (st->xgwa.height - st->delta->height) / 2);
862   else if (!st->nthreshes)
863     XCopyArea (st->dpy,
864                st->image,
865                st->window, st->wgc,
866                0, 0, st->xgwa.width, st->xgwa.height,
867                0,
868                0);
869
870
871  DONE:
872   return st->delay;
873 }
874   
875 static void
876 tessellimage_reshape (Display *dpy, Window window, void *closure, 
877                  unsigned int w, unsigned int h)
878 {
879   struct state *st = (struct state *) closure;
880   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
881 }
882
883 static Bool
884 tessellimage_event (Display *dpy, Window window, void *closure, XEvent *event)
885 {
886   struct state *st = (struct state *) closure;
887   if (event->xany.type == ButtonPress)
888     {
889       st->button_down_p = True;
890       return True;
891     }
892   else if (event->xany.type == ButtonRelease)
893     {
894       st->button_down_p = False;
895       return True;
896     }
897   else if (screenhack_event_helper (dpy, window, event))
898     {
899       st->start_time = 0;   /* load next image */
900       return True;
901     }
902
903   return False;
904 }
905
906
907 static void
908 tessellimage_free (Display *dpy, Window window, void *closure)
909 {
910   struct state *st = (struct state *) closure;
911   flush_cache (st);
912   if (st->wgc) XFreeGC (dpy, st->wgc);
913   if (st->pgc) XFreeGC (dpy, st->pgc);
914   if (st->image)  XFreePixmap (dpy, st->image);
915   if (st->output) XFreePixmap (dpy, st->output);
916   if (st->delta)  XDestroyImage (st->delta);
917   free (st);
918 }
919
920
921 \f
922
923 static const char *tessellimage_defaults [] = {
924   ".background:                 black",
925   ".foreground:                 white",
926   "*dontClearRoot:              True",
927   "*fpsSolid:                   true",
928   "*delay:                      30000",
929   "*duration:                   120",
930   "*duration2:                  0.4",
931   "*maxDepth:                   30000",
932   "*outline:                    True",
933   "*fillScreen:                 True",
934   "*cache:                      True",
935 #ifdef USE_IPHONE
936   "*ignoreRotation:             True",
937   "*rotateImages:               True",
938 #endif
939   0
940 };
941
942 static XrmOptionDescRec tessellimage_options [] = {
943   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
944   { "-duration",        ".duration",            XrmoptionSepArg, 0 },
945   { "-duration2",       ".duration2",           XrmoptionSepArg, 0 },
946   { "-max-depth",       ".maxDepth",            XrmoptionSepArg, 0 },
947   { "-outline",         ".outline",             XrmoptionNoArg, "True"  },
948   { "-no-outline",      ".outline",             XrmoptionNoArg, "False" },
949   { "-fill-screen",     ".fillScreen",          XrmoptionNoArg, "True"  },
950   { "-no-fill-screen",  ".fillScreen",          XrmoptionNoArg, "False" },
951   { "-cache",           ".cache",               XrmoptionNoArg, "True"  },
952   { "-no-cache",        ".cache",               XrmoptionNoArg, "False" },
953   { 0, 0, 0, 0 }
954 };
955
956 XSCREENSAVER_MODULE ("Tessellimage", tessellimage)