http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.02.tar.gz
[xscreensaver] / utils / colors.c
1 /* xscreensaver, Copyright (c) 1997, 2002 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 /* This file contains some utility routines for randomly picking the colors
13    to hack the screen with.
14  */
15
16 #include "utils.h"
17 #include "hsv.h"
18 #include "yarandom.h"
19 #include "visual.h"
20 #include "colors.h"
21
22 extern char *progname;
23
24 void
25 free_colors(Display *dpy, Colormap cmap, XColor *colors, int ncolors)
26 {
27   int i;
28   if (ncolors > 0)
29     {
30       unsigned long *pixels = (unsigned long *)
31         malloc(sizeof(*pixels) * ncolors);
32       for (i = 0; i < ncolors; i++)
33         pixels[i] = colors[i].pixel;
34       XFreeColors (dpy, cmap, pixels, ncolors, 0L);
35       free(pixels);
36     }
37 }
38
39
40 void
41 allocate_writable_colors (Display *dpy, Colormap cmap,
42                           unsigned long *pixels, int *ncolorsP)
43 {
44   int desired = *ncolorsP;
45   int got = 0;
46   int requested = desired;
47   unsigned long *new_pixels = pixels;
48
49   *ncolorsP = 0;
50   while (got < desired
51          && requested > 0)
52     {
53       if (desired - got < requested)
54         requested = desired - got;
55
56       if (XAllocColorCells (dpy, cmap, False, 0, 0, new_pixels, requested))
57         {
58           /* Got all the pixels we asked for. */
59           new_pixels += requested;
60           got += requested;
61         }
62       else
63         {
64           /* We didn't get all/any of the pixels we asked for.  This time, ask
65              for half as many.  (If we do get all that we ask for, we ask for
66              the same number again next time, so we only do O(log(n)) server
67              roundtrips.)
68           */
69           requested = requested / 2;
70         }
71     }
72   *ncolorsP += got;
73 }
74
75
76 static void
77 complain (int wanted_colors, int got_colors,
78           Bool wanted_writable, Bool got_writable)
79 {
80   if (got_colors > wanted_colors - 10)
81     /* don't bother complaining if we're within ten pixels. */
82     return;
83
84   if (wanted_writable && !got_writable)
85     fprintf (stderr,
86              "%s: wanted %d writable colors; got %d read-only colors.\n",
87              progname, wanted_colors, got_colors);
88   else
89     fprintf (stderr, "%s: wanted %d%s colors; got %d.\n",
90              progname, wanted_colors, (got_writable ? " writable" : ""),
91              got_colors);
92 }
93
94
95
96 void
97 make_color_ramp (Display *dpy, Colormap cmap,
98                  int h1, double s1, double v1,   /* 0-360, 0-1.0, 0-1.0 */
99                  int h2, double s2, double v2,   /* 0-360, 0-1.0, 0-1.0 */
100                  XColor *colors, int *ncolorsP,
101                  Bool closed_p,
102                  Bool allocate_p,
103                  Bool writable_p)
104 {
105   Bool verbose_p = True;  /* argh. */
106   int i;
107   int total_ncolors = *ncolorsP;
108   int ncolors, wanted;
109   Bool wanted_writable = (allocate_p && writable_p);
110   double dh, ds, dv;            /* deltas */
111
112   wanted = total_ncolors;
113   if (closed_p)
114     wanted = (wanted / 2) + 1;
115
116  AGAIN:
117   ncolors = total_ncolors;
118
119   memset (colors, 0, (*ncolorsP) * sizeof(*colors));
120
121   if (closed_p)
122     ncolors = (ncolors / 2) + 1;
123
124   /* Note: unlike other routines in this module, this function assumes that
125      if h1 and h2 are more than 180 degrees apart, then the desired direction
126      is always from h1 to h2 (rather than the shorter path.)  make_uniform
127      depends on this.
128    */
129   dh = ((double)h2 - (double)h1) / ncolors;
130   ds = (s2 - s1) / ncolors;
131   dv = (v2 - v1) / ncolors;
132
133   for (i = 0; i < ncolors; i++)
134     {
135       colors[i].flags = DoRed|DoGreen|DoBlue;
136       hsv_to_rgb ((int) (h1 + (i*dh)), (s1 + (i*ds)), (v1 + (i*dv)),
137                   &colors[i].red, &colors[i].green, &colors[i].blue);
138     }
139
140   if (closed_p)
141     for (i = ncolors; i < *ncolorsP; i++)
142       colors[i] = colors[(*ncolorsP)-i];
143
144   if (!allocate_p)
145     return;
146
147   if (writable_p)
148     {
149       unsigned long *pixels = (unsigned long *)
150         malloc(sizeof(*pixels) * ((*ncolorsP) + 1));
151
152       /* allocate_writable_colors() won't do here, because we need exactly this
153          number of cells, or the color sequence we've chosen won't fit. */
154       if (! XAllocColorCells(dpy, cmap, False, 0, 0, pixels, *ncolorsP))
155         {
156           free(pixels);
157           goto FAIL;
158         }
159
160       for (i = 0; i < *ncolorsP; i++)
161         colors[i].pixel = pixels[i];
162       free (pixels);
163
164       XStoreColors (dpy, cmap, colors, *ncolorsP);
165     }
166   else
167     {
168       for (i = 0; i < *ncolorsP; i++)
169         {
170           XColor color;
171           color = colors[i];
172           if (XAllocColor (dpy, cmap, &color))
173             {
174               colors[i].pixel = color.pixel;
175             }
176           else
177             {
178               free_colors (dpy, cmap, colors, i);
179               goto FAIL;
180             }
181         }
182     }
183
184   goto WARN;
185
186  FAIL:
187   /* we weren't able to allocate all the colors we wanted;
188      decrease the requested number and try again.
189    */
190   total_ncolors = (total_ncolors > 170 ? total_ncolors - 20 :
191                    total_ncolors > 100 ? total_ncolors - 10 :
192                    total_ncolors >  75 ? total_ncolors -  5 :
193                    total_ncolors >  25 ? total_ncolors -  3 :
194                    total_ncolors >  10 ? total_ncolors -  2 :
195                    total_ncolors >   2 ? total_ncolors -  1 :
196                    0);
197   *ncolorsP = total_ncolors;
198   ncolors = total_ncolors;
199   if (total_ncolors > 0)
200     goto AGAIN;
201
202  WARN:
203   
204   if (verbose_p &&
205       /* don't warn if we got 0 writable colors -- probably TrueColor. */
206       (ncolors != 0 || !wanted_writable))
207     complain (wanted, ncolors, wanted_writable, wanted_writable && writable_p);
208 }
209
210
211 #define MAXPOINTS 50    /* yeah, so I'm lazy */
212
213
214 static void
215 make_color_path (Display *dpy, Colormap cmap,
216                  int npoints, int *h, double *s, double *v,
217                  XColor *colors, int *ncolorsP,
218                  Bool allocate_p,
219                  Bool writable_p)
220 {
221   int i, j, k;
222   int total_ncolors = *ncolorsP;
223
224   int ncolors[MAXPOINTS];  /* number of pixels per edge */
225   double dh[MAXPOINTS];    /* distance between pixels, per edge (0 - 360.0) */
226   double ds[MAXPOINTS];    /* distance between pixels, per edge (0 - 1.0) */
227   double dv[MAXPOINTS];    /* distance between pixels, per edge (0 - 1.0) */
228
229   if (npoints == 0)
230     {
231       *ncolorsP = 0;
232       return;
233     }
234   else if (npoints == 2)        /* using make_color_ramp() will be faster */
235     {
236       make_color_ramp (dpy, cmap,
237                        h[0], s[0], v[0], h[1], s[1], v[1],
238                        colors, ncolorsP,
239                        True,  /* closed_p */
240                        allocate_p, writable_p);
241       return;
242     }
243   else if (npoints >= MAXPOINTS)
244     {
245       npoints = MAXPOINTS-1;
246     }
247
248  AGAIN:
249
250   {
251     double DH[MAXPOINTS];       /* Distance between H values in the shortest
252                                    direction around the circle, that is, the
253                                    distance between 10 and 350 is 20.
254                                    (Range is 0 - 360.0.)
255                                 */
256     double edge[MAXPOINTS];     /* lengths of edges in unit HSV space. */
257     double ratio[MAXPOINTS];    /* proportions of the edges (total 1.0) */
258     double circum = 0;
259     double one_point_oh = 0;    /* (debug) */
260
261     for (i = 0; i < npoints; i++)
262       {
263         int j = (i+1) % npoints;
264         double d = ((double) (h[i] - h[j])) / 360;
265         if (d < 0) d = -d;
266         if (d > 0.5) d = 0.5 - (d - 0.5);
267         DH[i] = d;
268       }
269
270     for (i = 0; i < npoints; i++)
271       {
272         int j = (i+1) % npoints;
273         edge[i] = sqrt((DH[i] * DH[j]) +
274                        ((s[j] - s[i]) * (s[j] - s[i])) +
275                        ((v[j] - v[i]) * (v[j] - v[i])));
276         circum += edge[i];
277       }
278
279 #ifdef DEBUG
280     fprintf(stderr, "\ncolors:");
281     for (i=0; i < npoints; i++)
282       fprintf(stderr, " (%d, %.3f, %.3f)", h[i], s[i], v[i]);
283     fprintf(stderr, "\nlengths:");
284     for (i=0; i < npoints; i++)
285       fprintf(stderr, " %.3f", edge[i]);
286 #endif /* DEBUG */
287
288     if (circum < 0.0001)
289       goto FAIL;
290
291     for (i = 0; i < npoints; i++)
292       {
293         ratio[i] = edge[i] / circum;
294         one_point_oh += ratio[i];
295       }
296
297 #ifdef DEBUG
298     fprintf(stderr, "\nratios:");
299     for (i=0; i < npoints; i++)
300       fprintf(stderr, " %.3f", ratio[i]);
301 #endif /* DEBUG */
302
303     if (one_point_oh < 0.99999 || one_point_oh > 1.00001)
304       abort();
305
306     /* space the colors evenly along the circumference -- that means that the
307        number of pixels on a edge is proportional to the length of that edge
308        (relative to the lengths of the other edges.)
309      */
310     for (i = 0; i < npoints; i++)
311       ncolors[i] = total_ncolors * ratio[i];
312
313
314 #ifdef DEBUG
315     fprintf(stderr, "\npixels:");
316     for (i=0; i < npoints; i++)
317       fprintf(stderr, " %d", ncolors[i]);
318     fprintf(stderr, "  (%d)\n", total_ncolors);
319 #endif /* DEBUG */
320
321     for (i = 0; i < npoints; i++)
322       {
323         int j = (i+1) % npoints;
324
325         if (ncolors[i] > 0)
326           {
327             dh[i] = 360 * (DH[i] / ncolors[i]);
328             ds[i] = (s[j] - s[i]) / ncolors[i];
329             dv[i] = (v[j] - v[i]) / ncolors[i];
330           }
331       }
332   }
333
334   memset (colors, 0, (*ncolorsP) * sizeof(*colors));
335
336   k = 0;
337   for (i = 0; i < npoints; i++)
338     {
339       int distance, direction;
340       distance = h[(i+1) % npoints] - h[i];
341       direction = (distance >= 0 ? -1 : 1);
342
343       if (distance > 180)
344         distance = 180 - (distance - 180);
345       else if (distance < -180)
346         distance = -(180 - ((-distance) - 180));
347       else
348         direction = -direction;
349
350 #ifdef DEBUG
351       fprintf (stderr, "point %d: %3d %.2f %.2f\n",
352                i, h[i], s[i], v[i]);
353       fprintf(stderr, "  h[i]=%d  dh[i]=%.2f  ncolors[i]=%d\n",
354               h[i], dh[i], ncolors[i]);
355 #endif /* DEBUG */
356       for (j = 0; j < ncolors[i]; j++, k++)
357         {
358           double hh = (h[i] + (j * dh[i] * direction));
359           if (hh < 0) hh += 360;
360           else if (hh > 360) hh -= 0;
361           colors[k].flags = DoRed|DoGreen|DoBlue;
362           hsv_to_rgb ((int)
363                       hh,
364                       (s[i] + (j * ds[i])),
365                       (v[i] + (j * dv[i])),
366                       &colors[k].red, &colors[k].green, &colors[k].blue);
367 #ifdef DEBUG
368           fprintf (stderr, "point %d+%d: %.2f %.2f %.2f  %04X %04X %04X\n",
369                    i, j,
370                    hh,
371                    (s[i] + (j * ds[i])),
372                    (v[i] + (j * dv[i])),
373                    colors[k].red, colors[k].green, colors[k].blue);
374 #endif /* DEBUG */
375         }
376     }
377
378   /* Floating-point round-off can make us decide to use fewer colors. */
379   if (k < *ncolorsP)
380     {
381       *ncolorsP = k;
382       if (k <= 0)
383         return;
384     }
385
386   if (!allocate_p)
387     return;
388
389   if (writable_p)
390     {
391       unsigned long *pixels = (unsigned long *)
392         malloc(sizeof(*pixels) * ((*ncolorsP) + 1));
393
394       /* allocate_writable_colors() won't do here, because we need exactly this
395          number of cells, or the color sequence we've chosen won't fit. */
396       if (! XAllocColorCells(dpy, cmap, False, 0, 0, pixels, *ncolorsP))
397         {
398           free(pixels);
399           goto FAIL;
400         }
401
402       for (i = 0; i < *ncolorsP; i++)
403         colors[i].pixel = pixels[i];
404       free (pixels);
405
406       XStoreColors (dpy, cmap, colors, *ncolorsP);
407     }
408   else
409     {
410       for (i = 0; i < *ncolorsP; i++)
411         {
412           XColor color;
413           color = colors[i];
414           if (XAllocColor (dpy, cmap, &color))
415             {
416               colors[i].pixel = color.pixel;
417             }
418           else
419             {
420               free_colors (dpy, cmap, colors, i);
421               goto FAIL;
422             }
423         }
424     }
425
426   return;
427
428  FAIL:
429   /* we weren't able to allocate all the colors we wanted;
430      decrease the requested number and try again.
431    */
432   total_ncolors = (total_ncolors > 170 ? total_ncolors - 20 :
433                    total_ncolors > 100 ? total_ncolors - 10 :
434                    total_ncolors >  75 ? total_ncolors -  5 :
435                    total_ncolors >  25 ? total_ncolors -  3 :
436                    total_ncolors >  10 ? total_ncolors -  2 :
437                    total_ncolors >   2 ? total_ncolors -  1 :
438                    0);
439   *ncolorsP = total_ncolors;
440   if (total_ncolors > 0)
441     goto AGAIN;
442 }
443
444
445 void
446 make_color_loop (Display *dpy, Colormap cmap,
447                  int h0, double s0, double v0,   /* 0-360, 0-1.0, 0-1.0 */
448                  int h1, double s1, double v1,   /* 0-360, 0-1.0, 0-1.0 */
449                  int h2, double s2, double v2,   /* 0-360, 0-1.0, 0-1.0 */
450                  XColor *colors, int *ncolorsP,
451                  Bool allocate_p,
452                  Bool writable_p)
453 {
454   int h[3];
455   double s[3], v[3];
456   h[0] = h0; h[1] = h1; h[2] = h2;
457   s[0] = s0; s[1] = s1; s[2] = s2;
458   v[0] = v0; v[1] = v1; v[2] = v2;
459   make_color_path(dpy, cmap,
460                   3, h, s, v,
461                   colors, ncolorsP,
462                   allocate_p, writable_p);
463 }
464
465
466 void
467 make_smooth_colormap (Display *dpy, Visual *visual, Colormap cmap,
468                       XColor *colors, int *ncolorsP,
469                       Bool allocate_p,
470                       Bool *writable_pP,
471                       Bool verbose_p)
472 {
473   int npoints;
474   int ncolors = *ncolorsP;
475   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
476   int i;
477   int h[MAXPOINTS];
478   double s[MAXPOINTS];
479   double v[MAXPOINTS];
480   double total_s = 0;
481   double total_v = 0;
482   Screen *screen = (dpy ? DefaultScreenOfDisplay(dpy) : 0); /* #### WRONG! */
483
484   if (*ncolorsP <= 0) return;
485
486   {
487     int n = random() % 20;
488     if      (n <= 5)  npoints = 2;      /* 30% of the time */
489     else if (n <= 15) npoints = 3;      /* 50% of the time */
490     else if (n <= 18) npoints = 4;      /* 15% of the time */
491     else             npoints = 5;       /*  5% of the time */
492   }
493
494  REPICK_ALL_COLORS:
495   for (i = 0; i < npoints; i++)
496     {
497     REPICK_THIS_COLOR:
498       h[i] = random() % 360;
499       s[i] = frand(1.0);
500       v[i] = frand(0.8) + 0.2;
501
502       /* Make sure that no two adjascent colors are *too* close together.
503          If they are, try again.
504        */
505       if (i > 0)
506         {
507           int j = (i+1 == npoints) ? 0 : (i-1);
508           double hi = ((double) h[i]) / 360;
509           double hj = ((double) h[j]) / 360;
510           double dh = hj - hi;
511           double distance;
512           if (dh < 0) dh = -dh;
513           if (dh > 0.5) dh = 0.5 - (dh - 0.5);
514           distance = sqrt ((dh * dh) +
515                            ((s[j] - s[i]) * (s[j] - s[i])) +
516                            ((v[j] - v[i]) * (v[j] - v[i])));
517           if (distance < 0.2)
518             goto REPICK_THIS_COLOR;
519         }
520       total_s += s[i];
521       total_v += v[i];
522     }
523
524   /* If the average saturation or intensity are too low, repick the colors,
525      so that we don't end up with a black-and-white or too-dark map.
526    */
527   if (total_s / npoints < 0.2)
528     goto REPICK_ALL_COLORS;
529   if (total_v / npoints < 0.3)
530     goto REPICK_ALL_COLORS;
531
532   /* If this visual doesn't support writable cells, don't bother trying.
533    */
534   if (wanted_writable && !has_writable_cells(screen, visual))
535     *writable_pP = False;
536
537  RETRY_NON_WRITABLE:
538   make_color_path (dpy, cmap, npoints, h, s, v, colors, &ncolors,
539                    allocate_p, (writable_pP && *writable_pP));
540
541   /* If we tried for writable cells and got none, try for non-writable. */
542   if (allocate_p && *ncolorsP == 0 && *writable_pP)
543     {
544       *writable_pP = False;
545       goto RETRY_NON_WRITABLE;
546     }
547
548   if (verbose_p)
549     complain(*ncolorsP, ncolors, wanted_writable,
550              wanted_writable && *writable_pP);
551
552   *ncolorsP = ncolors;
553 }
554
555
556 void
557 make_uniform_colormap (Display *dpy, Visual *visual, Colormap cmap,
558                        XColor *colors, int *ncolorsP,
559                        Bool allocate_p,
560                        Bool *writable_pP,
561                        Bool verbose_p)
562 {
563   int ncolors = *ncolorsP;
564   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
565   Screen *screen = (dpy ? DefaultScreenOfDisplay(dpy) : 0); /* #### WRONG! */
566
567   double S = ((double) (random() % 34) + 66) / 100.0;   /* range 66%-100% */
568   double V = ((double) (random() % 34) + 66) / 100.0;   /* range 66%-100% */
569
570   if (*ncolorsP <= 0) return;
571
572   /* If this visual doesn't support writable cells, don't bother trying. */
573   if (wanted_writable && !has_writable_cells(screen, visual))
574     *writable_pP = False;
575
576  RETRY_NON_WRITABLE:
577   make_color_ramp(dpy, cmap,
578                   0,   S, V,
579                   359, S, V,
580                   colors, &ncolors,
581                   False, allocate_p,
582                   (writable_pP && *writable_pP));
583
584   /* If we tried for writable cells and got none, try for non-writable. */
585   if (allocate_p && *ncolorsP == 0 && writable_pP && *writable_pP)
586     {
587       ncolors = *ncolorsP;
588       *writable_pP = False;
589       goto RETRY_NON_WRITABLE;
590     }
591
592   if (verbose_p)
593     complain(*ncolorsP, ncolors, wanted_writable,
594              wanted_writable && *writable_pP);
595
596   *ncolorsP = ncolors;
597 }
598
599
600 void
601 make_random_colormap (Display *dpy, Visual *visual, Colormap cmap,
602                       XColor *colors, int *ncolorsP,
603                       Bool bright_p,
604                       Bool allocate_p,
605                       Bool *writable_pP,
606                       Bool verbose_p)
607 {
608   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
609   int ncolors = *ncolorsP;
610   int i;
611   Screen *screen = (dpy ? DefaultScreenOfDisplay(dpy) : 0); /* #### WRONG! */
612
613   if (*ncolorsP <= 0) return;
614
615   /* If this visual doesn't support writable cells, don't bother trying. */
616   if (wanted_writable && !has_writable_cells(screen, visual))
617     *writable_pP = False;
618
619   for (i = 0; i < ncolors; i++)
620     {
621       colors[i].flags = DoRed|DoGreen|DoBlue;
622       if (bright_p)
623         {
624           int H = random() % 360;                          /* range 0-360    */
625           double S = ((double) (random()%70) + 30)/100.0;  /* range 30%-100% */
626           double V = ((double) (random()%34) + 66)/100.0;  /* range 66%-100% */
627           hsv_to_rgb (H, S, V,
628                       &colors[i].red, &colors[i].green, &colors[i].blue);
629         }
630       else
631         {
632           colors[i].red   = random() % 0xFFFF;
633           colors[i].green = random() % 0xFFFF;
634           colors[i].blue  = random() % 0xFFFF;
635         }
636     }
637
638   if (!allocate_p)
639     return;
640
641  RETRY_NON_WRITABLE:
642   if (writable_pP && *writable_pP)
643     {
644       unsigned long *pixels = (unsigned long *)
645         malloc(sizeof(*pixels) * (ncolors + 1));
646
647       allocate_writable_colors (dpy, cmap, pixels, &ncolors);
648       if (ncolors > 0)
649         for (i = 0; i < ncolors; i++)
650           colors[i].pixel = pixels[i];
651       free (pixels);
652       if (ncolors > 0)
653         XStoreColors (dpy, cmap, colors, ncolors);
654     }
655   else
656     {
657       for (i = 0; i < ncolors; i++)
658         {
659           XColor color;
660           color = colors[i];
661           if (!XAllocColor (dpy, cmap, &color))
662             break;
663           colors[i].pixel = color.pixel;
664         }
665       ncolors = i;
666     }
667
668   /* If we tried for writable cells and got none, try for non-writable. */
669   if (allocate_p && ncolors == 0 && writable_pP && *writable_pP)
670     {
671       ncolors = *ncolorsP;
672       *writable_pP = False;
673       goto RETRY_NON_WRITABLE;
674     }
675
676   if (verbose_p)
677     complain(*ncolorsP, ncolors, wanted_writable,
678              wanted_writable && *writable_pP);
679
680   *ncolorsP = ncolors;
681 }
682
683
684 void
685 rotate_colors (Display *dpy, Colormap cmap,
686                XColor *colors, int ncolors, int distance)
687 {
688   int i;
689   XColor *colors2 = (XColor *) malloc(sizeof(*colors2) * ncolors);
690   if (ncolors < 2) return;
691   distance = distance % ncolors;
692   for (i = 0; i < ncolors; i++)
693     {
694       int j = i - distance;
695       if (j >= ncolors) j -= ncolors;
696       if (j < 0) j += ncolors;
697       colors2[i] = colors[j];
698       colors2[i].pixel = colors[i].pixel;
699     }
700   XStoreColors (dpy, cmap, colors2, ncolors);
701   XFlush(dpy);
702   memcpy(colors, colors2, sizeof(*colors) * ncolors);
703   free(colors2);
704 }