began for maemo
[xscreensaver] / 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   int loop = 0;
484
485   if (*ncolorsP <= 0) return;
486
487   {
488     int n = random() % 20;
489     if      (n <= 5)  npoints = 2;      /* 30% of the time */
490     else if (n <= 15) npoints = 3;      /* 50% of the time */
491     else if (n <= 18) npoints = 4;      /* 15% of the time */
492     else             npoints = 5;       /*  5% of the time */
493   }
494
495  REPICK_ALL_COLORS:
496   for (i = 0; i < npoints; i++)
497     {
498     REPICK_THIS_COLOR:
499       if (++loop > 10000) abort();
500       h[i] = random() % 360;
501       s[i] = frand(1.0);
502       v[i] = frand(0.8) + 0.2;
503
504       /* Make sure that no two adjascent colors are *too* close together.
505          If they are, try again.
506        */
507       if (i > 0)
508         {
509           int j = (i+1 == npoints) ? 0 : (i-1);
510           double hi = ((double) h[i]) / 360;
511           double hj = ((double) h[j]) / 360;
512           double dh = hj - hi;
513           double distance;
514           if (dh < 0) dh = -dh;
515           if (dh > 0.5) dh = 0.5 - (dh - 0.5);
516           distance = sqrt ((dh * dh) +
517                            ((s[j] - s[i]) * (s[j] - s[i])) +
518                            ((v[j] - v[i]) * (v[j] - v[i])));
519           if (distance < 0.2)
520             goto REPICK_THIS_COLOR;
521         }
522       total_s += s[i];
523       total_v += v[i];
524     }
525
526   /* If the average saturation or intensity are too low, repick the colors,
527      so that we don't end up with a black-and-white or too-dark map.
528    */
529   if (total_s / npoints < 0.2)
530     goto REPICK_ALL_COLORS;
531   if (total_v / npoints < 0.3)
532     goto REPICK_ALL_COLORS;
533
534   /* If this visual doesn't support writable cells, don't bother trying.
535    */
536   if (wanted_writable && !has_writable_cells(screen, visual))
537     *writable_pP = False;
538
539  RETRY_NON_WRITABLE:
540   make_color_path (dpy, cmap, npoints, h, s, v, colors, &ncolors,
541                    allocate_p, (writable_pP && *writable_pP));
542
543   /* If we tried for writable cells and got none, try for non-writable. */
544   if (allocate_p && *ncolorsP == 0 && *writable_pP)
545     {
546       *writable_pP = False;
547       goto RETRY_NON_WRITABLE;
548     }
549
550   if (verbose_p)
551     complain(*ncolorsP, ncolors, wanted_writable,
552              wanted_writable && *writable_pP);
553
554   *ncolorsP = ncolors;
555 }
556
557
558 void
559 make_uniform_colormap (Display *dpy, Visual *visual, Colormap cmap,
560                        XColor *colors, int *ncolorsP,
561                        Bool allocate_p,
562                        Bool *writable_pP,
563                        Bool verbose_p)
564 {
565   int ncolors = *ncolorsP;
566   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
567   Screen *screen = (dpy ? DefaultScreenOfDisplay(dpy) : 0); /* #### WRONG! */
568
569   double S = ((double) (random() % 34) + 66) / 100.0;   /* range 66%-100% */
570   double V = ((double) (random() % 34) + 66) / 100.0;   /* range 66%-100% */
571
572   if (*ncolorsP <= 0) return;
573
574   /* If this visual doesn't support writable cells, don't bother trying. */
575   if (wanted_writable && !has_writable_cells(screen, visual))
576     *writable_pP = False;
577
578  RETRY_NON_WRITABLE:
579   make_color_ramp(dpy, cmap,
580                   0,   S, V,
581                   359, S, V,
582                   colors, &ncolors,
583                   False, allocate_p,
584                   (writable_pP && *writable_pP));
585
586   /* If we tried for writable cells and got none, try for non-writable. */
587   if (allocate_p && *ncolorsP == 0 && writable_pP && *writable_pP)
588     {
589       ncolors = *ncolorsP;
590       *writable_pP = False;
591       goto RETRY_NON_WRITABLE;
592     }
593
594   if (verbose_p)
595     complain(*ncolorsP, ncolors, wanted_writable,
596              wanted_writable && *writable_pP);
597
598   *ncolorsP = ncolors;
599 }
600
601
602 void
603 make_random_colormap (Display *dpy, Visual *visual, Colormap cmap,
604                       XColor *colors, int *ncolorsP,
605                       Bool bright_p,
606                       Bool allocate_p,
607                       Bool *writable_pP,
608                       Bool verbose_p)
609 {
610   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
611   int ncolors = *ncolorsP;
612   int i;
613   Screen *screen = (dpy ? DefaultScreenOfDisplay(dpy) : 0); /* #### WRONG! */
614
615   if (*ncolorsP <= 0) return;
616
617   /* If this visual doesn't support writable cells, don't bother trying. */
618   if (wanted_writable && !has_writable_cells(screen, visual))
619     *writable_pP = False;
620
621   for (i = 0; i < ncolors; i++)
622     {
623       colors[i].flags = DoRed|DoGreen|DoBlue;
624       if (bright_p)
625         {
626           int H = random() % 360;                          /* range 0-360    */
627           double S = ((double) (random()%70) + 30)/100.0;  /* range 30%-100% */
628           double V = ((double) (random()%34) + 66)/100.0;  /* range 66%-100% */
629           hsv_to_rgb (H, S, V,
630                       &colors[i].red, &colors[i].green, &colors[i].blue);
631         }
632       else
633         {
634           colors[i].red   = random() % 0xFFFF;
635           colors[i].green = random() % 0xFFFF;
636           colors[i].blue  = random() % 0xFFFF;
637         }
638     }
639
640   if (!allocate_p)
641     return;
642
643  RETRY_NON_WRITABLE:
644   if (writable_pP && *writable_pP)
645     {
646       unsigned long *pixels = (unsigned long *)
647         malloc(sizeof(*pixels) * (ncolors + 1));
648
649       allocate_writable_colors (dpy, cmap, pixels, &ncolors);
650       if (ncolors > 0)
651         for (i = 0; i < ncolors; i++)
652           colors[i].pixel = pixels[i];
653       free (pixels);
654       if (ncolors > 0)
655         XStoreColors (dpy, cmap, colors, ncolors);
656     }
657   else
658     {
659       for (i = 0; i < ncolors; i++)
660         {
661           XColor color;
662           color = colors[i];
663           if (!XAllocColor (dpy, cmap, &color))
664             break;
665           colors[i].pixel = color.pixel;
666         }
667       ncolors = i;
668     }
669
670   /* If we tried for writable cells and got none, try for non-writable. */
671   if (allocate_p && ncolors == 0 && writable_pP && *writable_pP)
672     {
673       ncolors = *ncolorsP;
674       *writable_pP = False;
675       goto RETRY_NON_WRITABLE;
676     }
677
678   if (verbose_p)
679     complain(*ncolorsP, ncolors, wanted_writable,
680              wanted_writable && *writable_pP);
681
682   *ncolorsP = ncolors;
683 }
684
685
686 void
687 rotate_colors (Display *dpy, Colormap cmap,
688                XColor *colors, int ncolors, int distance)
689 {
690   int i;
691   XColor *colors2 = (XColor *) malloc(sizeof(*colors2) * ncolors);
692   if (ncolors < 2) return;
693   distance = distance % ncolors;
694   for (i = 0; i < ncolors; i++)
695     {
696       int j = i - distance;
697       if (j >= ncolors) j -= ncolors;
698       if (j < 0) j += ncolors;
699       colors2[i] = colors[j];
700       colors2[i].pixel = colors[i].pixel;
701     }
702   XStoreColors (dpy, cmap, colors2, ncolors);
703   XFlush(dpy);
704   memcpy(colors, colors2, sizeof(*colors) * ncolors);
705   free(colors2);
706 }