source: trunk/third/xscreensaver/utils/colors.c @ 20148

Revision 20148, 18.0 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
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
22extern char *progname;
23
24void
25free_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
40void
41allocate_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
76static void
77complain (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
96void
97make_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
214static void
215make_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
445void
446make_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
466void
467make_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
556void
557make_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
600void
601make_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
684void
685rotate_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}
Note: See TracBrowser for help on using the repository browser.