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