From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / tessellimage.c
1 /* tessellimage, Copyright (c) 2014 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 #include "screenhack.h"
13 #include "delaunay.h"
14
15 #undef DO_VORONOI
16
17
18 #ifndef HAVE_JWXYZ
19 # define XK_MISCELLANY
20 # include <X11/keysymdef.h>
21 #endif
22
23 #undef countof
24 #define countof(x) (sizeof((x))/sizeof((*x)))
25
26 struct state {
27   Display *dpy;
28   Window window;
29   XWindowAttributes xgwa;
30   GC wgc, pgc;
31   int delay;
32   Bool outline_p, cache_p, fill_p;
33   double duration, duration2;
34   int max_depth;
35   double start_time, start_time2;
36
37   XImage *img, *delta;
38   Pixmap image, output, deltap;
39   int nthreshes, threshes[256], vsizes[256];
40   int thresh, dthresh;
41   Pixmap cache[256];
42
43   async_load_state *img_loader;
44   XRectangle geom;
45   Bool button_down_p;
46 };
47
48
49 /* Returns the current time in seconds as a double.
50  */
51 static double
52 double_time (void)
53 {
54   struct timeval now;
55 # ifdef GETTIMEOFDAY_TWO_ARGS
56   struct timezone tzp;
57   gettimeofday(&now, &tzp);
58 # else
59   gettimeofday(&now);
60 # endif
61
62   return (now.tv_sec + ((double) now.tv_usec * 0.000001));
63 }
64
65
66 static void *
67 tessellimage_init (Display *dpy, Window window)
68 {
69   struct state *st = (struct state *) calloc (1, sizeof(*st));
70
71   st->dpy = dpy;
72   st->window = window;
73
74   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
75
76   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
77   if (st->delay < 1) st->delay = 1;
78
79   st->outline_p = get_boolean_resource (st->dpy, "outline", "Boolean");
80   st->cache_p   = get_boolean_resource (st->dpy, "cache", "Boolean");
81   st->fill_p    = get_boolean_resource (st->dpy, "fillScreen", "Boolean");
82
83   st->max_depth = get_integer_resource (st->dpy, "maxDepth", "MaxDepth");
84   if (st->max_depth < 100) st->max_depth = 100;
85
86   st->duration = get_float_resource (st->dpy, "duration", "Seconds");
87   if (st->duration < 1) st->duration = 1;
88
89   st->duration2 = get_float_resource (st->dpy, "duration2", "Seconds");
90   if (st->duration2 < 0.001) st->duration = 0.001;
91
92   XClearWindow(st->dpy, st->window);
93
94   return st;
95 }
96
97
98 /* Given a bitmask, returns the position and width of the field.
99  */
100 static void
101 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
102 {
103   int i;
104   for (i = 0; i < 32; i++)
105     if (mask & (1L << i))
106       {
107         int j = 0;
108         *pos_ret = i;
109         for (; i < 32; i++, j++)
110           if (! (mask & (1L << i)))
111             break;
112         *size_ret = j;
113         return;
114       }
115 }
116
117
118 static unsigned long
119 pixel_distance (Screen *s, Visual *v, unsigned long p1, unsigned long p2)
120 {
121   static int initted_p = 0;
122   static unsigned long rmsk=0, gmsk=0, bmsk=0;
123   static unsigned int rpos=0, gpos=0, bpos=0;
124   static unsigned int rsiz=0, gsiz=0, bsiz=0;
125
126   unsigned char r1, g1, b1;
127   unsigned char r2, g2, b2;
128   long distance;
129
130   if (!p1 && !p2) return 0;
131
132   if (! initted_p) {
133     visual_rgb_masks (s, v, &rmsk, &gmsk, &bmsk);
134     decode_mask (rmsk, &rpos, &rsiz);
135     decode_mask (gmsk, &gpos, &gsiz);
136     decode_mask (bmsk, &bpos, &bsiz);
137     initted_p = 1;
138   }
139
140   r1 = (p1 & rmsk) >> rpos;
141   g1 = (p1 & gmsk) >> gpos;
142   b1 = (p1 & bmsk) >> bpos;
143
144   r2 = (p2 & rmsk) >> rpos;
145   g2 = (p2 & gmsk) >> gpos;
146   b2 = (p2 & bmsk) >> bpos;
147
148 #if 0
149   /* Compute the distance in linear RGB space.
150    */
151   distance = cbrt (((r2 - r1) * (r2 - r1)) +
152                    ((g2 - g1) * (g2 - g1)) +
153                    ((b2 - b1) * (b2 - b1)));
154
155 # elif 1
156   /* Compute the distance in luminance-weighted RGB space.
157    */
158   {
159     int rd = (r2 - r1) * 0.2989 * (1 / 0.5870);
160     int gd = (g2 - g1) * 0.5870 * (1 / 0.5870);
161     int bd = (b2 - b1) * 0.1140 * (1 / 0.5870);
162     distance = cbrt ((rd * rd) + (gd * gd) + (bd * bd));
163   }
164 # else
165   /* Compute the distance in brightness-weighted HSV space.
166      (Slower, and doesn't seem to look better than luminance RGB.)
167    */
168   {
169     int h1, h2;
170     double s1, s2;
171     double v1, v2;
172     double hd, sd, vd, dd;
173     rgb_to_hsv (r1, g1, b1, &h1, &s1, &v1);
174     rgb_to_hsv (r2, g2, b2, &h2, &s2, &v2);
175
176     hd = abs (h2 - h1);
177     if (hd >= 180) hd -= 180;
178     hd /= 180.0;
179     sd = fabs (s2 - s1);
180     vd = fabs (v2 - v1);
181
182     /* [hsv]d are now the distance as 0.0 - 1.0. */
183     /* Compute the overall distance, giving more weight to V. */
184     dd = (hd * 0.25 + sd * 0.25 + vd * 0.5);
185
186     if (dd < 0 || dd > 1.0) abort();
187     distance = dd * 255;
188   }
189 # endif
190
191   if (distance < 0) distance = -distance;
192   return distance;
193 }
194
195
196 static void
197 flush_cache (struct state *st)
198 {
199   int i;
200   for (i = 0; i < countof(st->cache); i++)
201     if (st->cache[i])
202       {
203         XFreePixmap (st->dpy, st->cache[i]);
204         st->cache[i] = 0;
205       }
206   if (st->deltap)
207     {
208       XFreePixmap (st->dpy, st->deltap);
209       st->deltap = 0;
210     }
211 }
212
213
214 /* Scale up the bits in st->img so that it fills the screen, centered.
215  */
216 static void
217 scale_image (struct state *st)
218 {
219   double scale, s1, s2;
220   XImage *img2;
221   int x, y, cx, cy;
222
223   if (st->geom.width <= 0 || st->geom.height <= 0)
224     return;
225
226   s1 = st->geom.width  / (double) st->img->width;
227   s2 = st->geom.height / (double) st->img->height;
228   scale = (s1 < s2 ? s1 : s2);
229
230   img2 = XCreateImage (st->dpy, st->xgwa.visual, st->img->depth,
231                        ZPixmap, 0, NULL,
232                        st->img->width, st->img->height, 8, 0);
233   if (! img2) abort();
234   img2->data = (char *) calloc (img2->height, img2->bytes_per_line);
235   if (! img2->data) abort();
236
237   cx = st->img->width  / 2;
238   cy = st->img->height / 2;
239
240   if (st->geom.width < st->geom.height)  /* portrait: aim toward the top */
241     cy = st->img->height / (2 / scale);
242
243   for (y = 0; y < img2->height; y++)
244     for (x = 0; x < img2->width; x++)
245       {
246         int x2 = cx + ((x - cx) * scale);
247         int y2 = cy + ((y - cy) * scale);
248         unsigned long p = 0;
249         if (x2 >= 0 && y2 >= 0 &&
250             x2 < st->img->width && y2 < st->img->height)
251           p = XGetPixel (st->img, x2, y2);
252         XPutPixel (img2, x, y, p);
253       }
254   free (st->img->data);
255   st->img->data = 0;
256   XDestroyImage (st->img);
257   st->img = img2;
258
259   st->geom.x = 0;
260   st->geom.y = 0;
261   st->geom.width = st->img->width;
262   st->geom.height = st->img->height;
263 }
264
265
266
267 static void
268 analyze (struct state *st)
269 {
270   Window root;
271   int x, y, i;
272   unsigned int w, h, bw, d;
273   unsigned long histo[256];
274
275   flush_cache (st);
276
277   /* Convert the loaded pixmap to an XImage.
278    */
279   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
280   XGetGeometry (st->dpy, st->image, &root, &x, &y, &w, &h, &bw, &d);
281
282   if (st->img)
283     {
284       free (st->img->data);
285       st->img->data = 0;
286       XDestroyImage (st->img);
287     }
288   st->img = XGetImage (st->dpy, st->image, 0, 0, w, h, ~0L, ZPixmap);
289
290   if (st->fill_p) scale_image (st);
291
292   /* Create the delta map: color space distance between each pixel.
293      Maybe doing running a Sobel Filter matrix on this would be a
294      better idea.  That might be a bit faster, but I think it would
295      make no visual difference.
296    */
297   if (st->delta)
298     {
299       free (st->delta->data);
300       st->delta->data = 0;
301       XDestroyImage (st->delta);
302     }
303   st->delta = XCreateImage (st->dpy, st->xgwa.visual, d, ZPixmap, 0, NULL,
304                             w, h, 32, 0);
305   st->delta->data = (char *)
306     calloc (st->delta->height, st->delta->bytes_per_line);
307
308   for (y = 0; y < st->delta->height; y++)
309     {
310       for (x = 0; x < st->delta->width; x++)
311         {
312           unsigned long pixels[5];
313           int i = 0;
314           int distance = 0;
315           pixels[i++] =                     XGetPixel (st->img, x,   y);
316           pixels[i++] = (x > 0 && y > 0   ? XGetPixel (st->img, x-1, y-1) : 0);
317           pixels[i++] = (         y > 0   ? XGetPixel (st->img, x,   y-1) : 0);
318           pixels[i++] = (x > 0            ? XGetPixel (st->img, x-1, y)   : 0);
319           pixels[i++] = (x > 0 && y < h-1 ? XGetPixel (st->img, x-1, y+1) : 0);
320
321           for (i = 1; i < countof(pixels); i++)
322             distance += pixel_distance (st->xgwa.screen, st->xgwa.visual,
323                                         pixels[0], pixels[i]);
324           distance /= countof(pixels)-1;
325           XPutPixel (st->delta, x, y, distance);
326         }
327     }
328
329   /* Collect a histogram of every distance value.
330    */
331   memset (histo, 0, sizeof(histo));
332   for (y = 0; y < st->delta->height; y++)
333     for (x = 0; x < st->delta->width; x++)
334       {
335         unsigned long p = XGetPixel (st->delta, x, y);
336         if (p > sizeof(histo)) abort();
337         histo[p]++;
338       }
339
340   /* Convert that from "occurrences of N" to ">= N".
341    */
342   for (i = countof(histo) - 1; i > 0; i--)
343     histo[i-1] += histo[i];
344
345 # if 0
346   fprintf (stderr, "%s: histo: ", progname);
347   for (i = 0; i < countof(histo); i++)
348     fprintf(stderr, "%d:%lu ", i, histo[i]);
349   fprintf(stderr, "\n");
350 # endif
351
352   /* Collect a useful set of threshold values, ignoring thresholds that
353      result in a very similar number of control points (since those images
354      probably won't look very different).
355    */
356
357   {
358     int max_vsize = st->max_depth;
359     int min_vsize = 20;
360     int min_delta = 100;
361
362     if (min_vsize > max_vsize/100)
363       min_vsize = max_vsize/100;
364
365     if (min_delta > max_vsize/1000)
366       min_delta = max_vsize/1000;
367
368     st->nthreshes = 0;
369     for (i = countof(histo)-1; i >= 0; i--)
370       {
371         unsigned long vsize = histo[i];
372
373         /* If this is a different vsize, push it. */
374         if (vsize >= min_vsize &&
375             vsize <= max_vsize &&
376             (st->nthreshes == 0 ||
377              vsize >= st->vsizes[st->nthreshes-1] + min_delta))
378           {
379             st->threshes[st->nthreshes] = i;
380             st->vsizes[st->nthreshes] = vsize;
381             st->nthreshes++;
382           }
383       }
384   }
385   
386   st->thresh = 0;   /* startup */
387   st->dthresh = 1;  /* forward */
388
389   if (st->output)
390     {
391       XFreePixmap (st->dpy, st->output);
392       st->output = 0;
393     }
394
395
396 # if 0
397   fprintf (stderr, "%s: threshes:", progname);
398   for (i = 0; i < st->nthreshes; i++)
399     fprintf (stderr, " %d=%d", st->threshes[i], st->vsizes[i]);
400   fprintf (stderr, "\n");
401 # endif
402 }
403
404
405 #ifndef DO_VORONOI
406 /* True if the distance between any two corners is too small for it to
407    make sense to draw an outline around this triangle.
408  */
409 static Bool
410 small_triangle_p (const XPoint *p)
411 {
412   int min = 4;
413   if (abs (p[0].x - p[1].x) < min) return True;
414   if (abs (p[0].y - p[1].y) < min) return True;
415   if (abs (p[1].x - p[2].x) < min) return True;
416   if (abs (p[1].y - p[2].y) < min) return True;
417   if (abs (p[2].x - p[0].x) < min) return True;
418   if (abs (p[2].y - p[0].y) < min) return True;
419   return False;
420 }
421 #endif /* DO_VORONOI */
422
423 #ifdef DO_VORONOI
424
425 typedef struct {
426   int npoints;
427   XPoint *p;
428 } voronoi_polygon;
429
430 static voronoi_polygon *
431 delaunay_to_voronoi (int np, XYZ *p, int nv, ITRIANGLE *v)
432 {
433   struct tri_list {
434     int count, size;
435     int *tri;
436   };
437
438   int i, j;
439   struct tri_list *vert_to_tri = (struct tri_list *)
440     calloc (np + 1, sizeof(*vert_to_tri));
441   voronoi_polygon *out = (voronoi_polygon *) calloc (np + 1, sizeof(*out));
442
443 /*
444   for (i = 0; i < np; i++)
445     printf("# p %d = %d %d\n", i, (int)p[i].x, (int)p[i].y);
446   printf("\n");
447   for (i = 0; i < nv; i++)
448     printf("@ t %d = %d %d %d\n", i, (int)v[i].p1, (int)v[i].p2, (int)v[i].p3);
449   printf("\n");
450 */
451
452   /* Iterate the triangles to construct a map of vertices to the
453      triangles that contain them.
454    */
455   for (i = 0; i < nv; i++)
456     {
457       for (j = 0; j < 3; j++)   /* iterate points in each triangle */
458         {
459           int p = *((&v[i].p1) + j);
460           struct tri_list *t = &vert_to_tri[p];
461           if (p < 0 || p >= np) abort();
462           if (t->size <= t->count + 1)
463             {
464               t->size += 3;
465               t->size *= 1.3;
466               t->tri = realloc (t->tri, t->size * sizeof(*t->tri));
467               if (! t->tri) abort();
468             }
469           t->tri[t->count++] = i;
470         }
471     }
472
473 /*
474   for (i = 0; i < nv; i++)
475     {
476       struct tri_list *t = &vert_to_tri[i];
477       printf("p %d [%d %d]:", i, (int)p[i].x, (int)p[i].y);
478       for (j = 0; j < t->count; j++) {
479         int tt = t->tri[j];
480         printf(" t %d [%d(%d %d) %d(%d %d) %d(%d %d)]",
481                tt,
482                (int)v[tt].p1,
483                (int)p[v[tt].p1].x, (int)p[v[tt].p1].y,
484                (int)v[tt].p2,
485                (int)p[v[tt].p2].x, (int)p[v[tt].p2].y,
486                (int)v[tt].p3,
487                (int)p[v[tt].p3].x, (int)p[v[tt].p3].y
488                );
489         if (tt < 0 || tt >= nv) abort();
490       }
491       printf("\n");
492     }
493 */
494
495   /* For every vertex, compose a polygon whose corners are the centers
496      of each triangle using that vertex.  Skip any with less than 3 points.
497    */
498   for (i = 0; i < np; i++)
499     {
500       struct tri_list *t = &vert_to_tri[i];
501       int n = t->count;
502       if (n < 3) n = 0;
503       out[i].npoints = n;
504       out[i].p = (n > 0
505                   ? (XPoint *) calloc (out[i].npoints + 1, sizeof (*out[i].p))
506                   : 0);
507 //printf("%d: ", i);
508       for (j = 0; j < out[i].npoints; j++)
509         {
510           ITRIANGLE *tt = &v[t->tri[j]];
511           out[i].p[j].x = (p[tt->p1].x + p[tt->p2].x + p[tt->p3].x) / 3;
512           out[i].p[j].y = (p[tt->p1].y + p[tt->p2].y + p[tt->p3].y) / 3;
513 //printf(" [%d: %d %d]", j, out[i].p[j].x, out[i].p[j].y);
514         }
515 //printf("\n");
516     }
517
518   free (vert_to_tri);
519   return out;
520 }
521
522 #endif /* DO_VORONOI */
523
524
525
526
527 static void
528 tessellate (struct state *st)
529 {
530   Bool ticked_p = False;
531
532   if (! st->image) return;
533
534   if (! st->wgc)
535     {
536       XGCValues gcv;
537       gcv.function = GXcopy;
538       gcv.subwindow_mode = IncludeInferiors;
539       st->wgc = XCreateGC(st->dpy, st->window, GCFunction, &gcv);
540       st->pgc = XCreateGC(st->dpy, st->image, GCFunction, &gcv);
541     }
542
543   if (! st->nthreshes) return;
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 long 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   visual_rgb_masks (st->xgwa.screen, v, &rmsk, &gmsk, &bmsk);
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   else if (!st->nthreshes)
858     XCopyArea (st->dpy,
859                st->image,
860                st->window, st->wgc,
861                0, 0, st->xgwa.width, st->xgwa.height,
862                0,
863                0);
864
865
866  DONE:
867   return st->delay;
868 }
869   
870 static void
871 tessellimage_reshape (Display *dpy, Window window, void *closure, 
872                  unsigned int w, unsigned int h)
873 {
874   struct state *st = (struct state *) closure;
875   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
876 }
877
878 static Bool
879 tessellimage_event (Display *dpy, Window window, void *closure, XEvent *event)
880 {
881   struct state *st = (struct state *) closure;
882   if (event->xany.type == ButtonPress)
883     {
884       st->button_down_p = True;
885       return True;
886     }
887   else if (event->xany.type == ButtonRelease)
888     {
889       st->button_down_p = False;
890       return True;
891     }
892   else if (screenhack_event_helper (dpy, window, event))
893     {
894       st->start_time = 0;   /* load next image */
895       return True;
896     }
897
898   return False;
899 }
900
901
902 static void
903 tessellimage_free (Display *dpy, Window window, void *closure)
904 {
905   struct state *st = (struct state *) closure;
906   flush_cache (st);
907   if (st->wgc) XFreeGC (dpy, st->wgc);
908   if (st->pgc) XFreeGC (dpy, st->pgc);
909   if (st->image)  XFreePixmap (dpy, st->image);
910   if (st->output) XFreePixmap (dpy, st->output);
911   if (st->delta)  XDestroyImage (st->delta);
912   free (st);
913 }
914
915
916 \f
917
918 static const char *tessellimage_defaults [] = {
919   ".background:                 black",
920   ".foreground:                 white",
921   "*dontClearRoot:              True",
922   "*fpsSolid:                   true",
923   "*delay:                      30000",
924   "*duration:                   120",
925   "*duration2:                  0.4",
926   "*maxDepth:                   30000",
927   "*outline:                    True",
928   "*fillScreen:                 True",
929   "*cache:                      True",
930 #ifdef HAVE_MOBILE
931   "*ignoreRotation:             True",
932   "*rotateImages:               True",
933 #endif
934   0
935 };
936
937 static XrmOptionDescRec tessellimage_options [] = {
938   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
939   { "-duration",        ".duration",            XrmoptionSepArg, 0 },
940   { "-duration2",       ".duration2",           XrmoptionSepArg, 0 },
941   { "-max-depth",       ".maxDepth",            XrmoptionSepArg, 0 },
942   { "-outline",         ".outline",             XrmoptionNoArg, "True"  },
943   { "-no-outline",      ".outline",             XrmoptionNoArg, "False" },
944   { "-fill-screen",     ".fillScreen",          XrmoptionNoArg, "True"  },
945   { "-no-fill-screen",  ".fillScreen",          XrmoptionNoArg, "False" },
946   { "-cache",           ".cache",               XrmoptionNoArg, "True"  },
947   { "-no-cache",        ".cache",               XrmoptionNoArg, "False" },
948   { 0, 0, 0, 0 }
949 };
950
951 XSCREENSAVER_MODULE ("Tessellimage", tessellimage)