1 /* xscreensaver, Copyright (c) 1997, 2002 Jamie Zawinski <jwz@jwz.org>
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
12 /* This file contains some utility routines for randomly picking the colors
13 to hack the screen with.
22 extern char *progname;
25 free_colors(Display *dpy, Colormap cmap, XColor *colors, int ncolors)
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);
41 allocate_writable_colors (Display *dpy, Colormap cmap,
42 unsigned long *pixels, int *ncolorsP)
44 int desired = *ncolorsP;
46 int requested = desired;
47 unsigned long *new_pixels = pixels;
53 if (desired - got < requested)
54 requested = desired - got;
56 if (XAllocColorCells (dpy, cmap, False, 0, 0, new_pixels, requested))
58 /* Got all the pixels we asked for. */
59 new_pixels += requested;
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
69 requested = requested / 2;
77 complain (int wanted_colors, int got_colors,
78 Bool wanted_writable, Bool got_writable)
80 if (got_colors > wanted_colors - 10)
81 /* don't bother complaining if we're within ten pixels. */
84 if (wanted_writable && !got_writable)
86 "%s: wanted %d writable colors; got %d read-only colors.\n",
87 progname, wanted_colors, got_colors);
89 fprintf (stderr, "%s: wanted %d%s colors; got %d.\n",
90 progname, wanted_colors, (got_writable ? " writable" : ""),
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,
105 Bool verbose_p = True; /* argh. */
107 int total_ncolors = *ncolorsP;
109 Bool wanted_writable = (allocate_p && writable_p);
110 double dh, ds, dv; /* deltas */
112 wanted = total_ncolors;
114 wanted = (wanted / 2) + 1;
117 ncolors = total_ncolors;
119 memset (colors, 0, (*ncolorsP) * sizeof(*colors));
122 ncolors = (ncolors / 2) + 1;
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
129 dh = ((double)h2 - (double)h1) / ncolors;
130 ds = (s2 - s1) / ncolors;
131 dv = (v2 - v1) / ncolors;
133 for (i = 0; i < ncolors; i++)
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);
141 for (i = ncolors; i < *ncolorsP; i++)
142 colors[i] = colors[(*ncolorsP)-i];
149 unsigned long *pixels = (unsigned long *)
150 malloc(sizeof(*pixels) * ((*ncolorsP) + 1));
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))
160 for (i = 0; i < *ncolorsP; i++)
161 colors[i].pixel = pixels[i];
164 XStoreColors (dpy, cmap, colors, *ncolorsP);
168 for (i = 0; i < *ncolorsP; i++)
172 if (XAllocColor (dpy, cmap, &color))
174 colors[i].pixel = color.pixel;
178 free_colors (dpy, cmap, colors, i);
187 /* we weren't able to allocate all the colors we wanted;
188 decrease the requested number and try again.
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 :
197 *ncolorsP = total_ncolors;
198 ncolors = total_ncolors;
199 if (total_ncolors > 0)
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);
211 #define MAXPOINTS 50 /* yeah, so I'm lazy */
215 make_color_path (Display *dpy, Colormap cmap,
216 int npoints, int *h, double *s, double *v,
217 XColor *colors, int *ncolorsP,
222 int total_ncolors = *ncolorsP;
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) */
234 else if (npoints == 2) /* using make_color_ramp() will be faster */
236 make_color_ramp (dpy, cmap,
237 h[0], s[0], v[0], h[1], s[1], v[1],
240 allocate_p, writable_p);
243 else if (npoints >= MAXPOINTS)
245 npoints = MAXPOINTS-1;
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.)
256 double edge[MAXPOINTS]; /* lengths of edges in unit HSV space. */
257 double ratio[MAXPOINTS]; /* proportions of the edges (total 1.0) */
259 double one_point_oh = 0; /* (debug) */
261 for (i = 0; i < npoints; i++)
263 int j = (i+1) % npoints;
264 double d = ((double) (h[i] - h[j])) / 360;
266 if (d > 0.5) d = 0.5 - (d - 0.5);
270 for (i = 0; i < npoints; i++)
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])));
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]);
291 for (i = 0; i < npoints; i++)
293 ratio[i] = edge[i] / circum;
294 one_point_oh += ratio[i];
298 fprintf(stderr, "\nratios:");
299 for (i=0; i < npoints; i++)
300 fprintf(stderr, " %.3f", ratio[i]);
303 if (one_point_oh < 0.99999 || one_point_oh > 1.00001)
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.)
310 for (i = 0; i < npoints; i++)
311 ncolors[i] = total_ncolors * ratio[i];
315 fprintf(stderr, "\npixels:");
316 for (i=0; i < npoints; i++)
317 fprintf(stderr, " %d", ncolors[i]);
318 fprintf(stderr, " (%d)\n", total_ncolors);
321 for (i = 0; i < npoints; i++)
323 int j = (i+1) % npoints;
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];
334 memset (colors, 0, (*ncolorsP) * sizeof(*colors));
337 for (i = 0; i < npoints; i++)
339 int distance, direction;
340 distance = h[(i+1) % npoints] - h[i];
341 direction = (distance >= 0 ? -1 : 1);
344 distance = 180 - (distance - 180);
345 else if (distance < -180)
346 distance = -(180 - ((-distance) - 180));
348 direction = -direction;
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]);
356 for (j = 0; j < ncolors[i]; j++, k++)
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;
364 (s[i] + (j * ds[i])),
365 (v[i] + (j * dv[i])),
366 &colors[k].red, &colors[k].green, &colors[k].blue);
368 fprintf (stderr, "point %d+%d: %.2f %.2f %.2f %04X %04X %04X\n",
371 (s[i] + (j * ds[i])),
372 (v[i] + (j * dv[i])),
373 colors[k].red, colors[k].green, colors[k].blue);
378 /* Floating-point round-off can make us decide to use fewer colors. */
391 unsigned long *pixels = (unsigned long *)
392 malloc(sizeof(*pixels) * ((*ncolorsP) + 1));
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))
402 for (i = 0; i < *ncolorsP; i++)
403 colors[i].pixel = pixels[i];
406 XStoreColors (dpy, cmap, colors, *ncolorsP);
410 for (i = 0; i < *ncolorsP; i++)
414 if (XAllocColor (dpy, cmap, &color))
416 colors[i].pixel = color.pixel;
420 free_colors (dpy, cmap, colors, i);
429 /* we weren't able to allocate all the colors we wanted;
430 decrease the requested number and try again.
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 :
439 *ncolorsP = total_ncolors;
440 if (total_ncolors > 0)
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,
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,
462 allocate_p, writable_p);
467 make_smooth_colormap (Display *dpy, Visual *visual, Colormap cmap,
468 XColor *colors, int *ncolorsP,
474 int ncolors = *ncolorsP;
475 Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
482 Screen *screen = (dpy ? DefaultScreenOfDisplay(dpy) : 0); /* #### WRONG! */
484 if (*ncolorsP <= 0) return;
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 */
495 for (i = 0; i < npoints; i++)
498 h[i] = random() % 360;
500 v[i] = frand(0.8) + 0.2;
502 /* Make sure that no two adjascent colors are *too* close together.
503 If they are, try again.
507 int j = (i+1 == npoints) ? 0 : (i-1);
508 double hi = ((double) h[i]) / 360;
509 double hj = ((double) h[j]) / 360;
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])));
518 goto REPICK_THIS_COLOR;
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.
527 if (total_s / npoints < 0.2)
528 goto REPICK_ALL_COLORS;
529 if (total_v / npoints < 0.3)
530 goto REPICK_ALL_COLORS;
532 /* If this visual doesn't support writable cells, don't bother trying.
534 if (wanted_writable && !has_writable_cells(screen, visual))
535 *writable_pP = False;
538 make_color_path (dpy, cmap, npoints, h, s, v, colors, &ncolors,
539 allocate_p, (writable_pP && *writable_pP));
541 /* If we tried for writable cells and got none, try for non-writable. */
542 if (allocate_p && *ncolorsP == 0 && *writable_pP)
544 *writable_pP = False;
545 goto RETRY_NON_WRITABLE;
549 complain(*ncolorsP, ncolors, wanted_writable,
550 wanted_writable && *writable_pP);
557 make_uniform_colormap (Display *dpy, Visual *visual, Colormap cmap,
558 XColor *colors, int *ncolorsP,
563 int ncolors = *ncolorsP;
564 Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
565 Screen *screen = (dpy ? DefaultScreenOfDisplay(dpy) : 0); /* #### WRONG! */
567 double S = ((double) (random() % 34) + 66) / 100.0; /* range 66%-100% */
568 double V = ((double) (random() % 34) + 66) / 100.0; /* range 66%-100% */
570 if (*ncolorsP <= 0) return;
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;
577 make_color_ramp(dpy, cmap,
582 (writable_pP && *writable_pP));
584 /* If we tried for writable cells and got none, try for non-writable. */
585 if (allocate_p && *ncolorsP == 0 && writable_pP && *writable_pP)
588 *writable_pP = False;
589 goto RETRY_NON_WRITABLE;
593 complain(*ncolorsP, ncolors, wanted_writable,
594 wanted_writable && *writable_pP);
601 make_random_colormap (Display *dpy, Visual *visual, Colormap cmap,
602 XColor *colors, int *ncolorsP,
608 Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
609 int ncolors = *ncolorsP;
611 Screen *screen = (dpy ? DefaultScreenOfDisplay(dpy) : 0); /* #### WRONG! */
613 if (*ncolorsP <= 0) return;
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;
619 for (i = 0; i < ncolors; i++)
621 colors[i].flags = DoRed|DoGreen|DoBlue;
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% */
628 &colors[i].red, &colors[i].green, &colors[i].blue);
632 colors[i].red = random() % 0xFFFF;
633 colors[i].green = random() % 0xFFFF;
634 colors[i].blue = random() % 0xFFFF;
642 if (writable_pP && *writable_pP)
644 unsigned long *pixels = (unsigned long *)
645 malloc(sizeof(*pixels) * (ncolors + 1));
647 allocate_writable_colors (dpy, cmap, pixels, &ncolors);
649 for (i = 0; i < ncolors; i++)
650 colors[i].pixel = pixels[i];
653 XStoreColors (dpy, cmap, colors, ncolors);
657 for (i = 0; i < ncolors; i++)
661 if (!XAllocColor (dpy, cmap, &color))
663 colors[i].pixel = color.pixel;
668 /* If we tried for writable cells and got none, try for non-writable. */
669 if (allocate_p && ncolors == 0 && writable_pP && *writable_pP)
672 *writable_pP = False;
673 goto RETRY_NON_WRITABLE;
677 complain(*ncolorsP, ncolors, wanted_writable,
678 wanted_writable && *writable_pP);
685 rotate_colors (Display *dpy, Colormap cmap,
686 XColor *colors, int ncolors, int distance)
689 XColor *colors2 = (XColor *) malloc(sizeof(*colors2) * ncolors);
690 if (ncolors < 2) return;
691 distance = distance % ncolors;
692 for (i = 0; i < ncolors; i++)
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;
700 XStoreColors (dpy, cmap, colors2, ncolors);
702 memcpy(colors, colors2, sizeof(*colors) * ncolors);