1 /* xscreensaver, Copyright (c) 1997 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 (wanted_writable && !got_writable)
82 "%s: wanted %d writable colors; got %d read-only colors.\n",
83 progname, wanted_colors, got_colors);
85 else if (wanted_colors > (got_colors + 10))
86 /* don't bother complaining if we're within ten pixels. */
87 fprintf(stderr, "%s: wanted %d%s colors; got %d.\n",
88 progname, wanted_colors, (got_writable ? " writable" : ""),
95 make_color_ramp (Display *dpy, Colormap cmap,
96 int h1, double s1, double v1, /* 0-360, 0-1.0, 0-1.0 */
97 int h2, double s2, double v2, /* 0-360, 0-1.0, 0-1.0 */
98 XColor *colors, int *ncolorsP,
103 Bool verbose_p = True; /* argh. */
105 int total_ncolors = *ncolorsP;
107 Bool wanted_writable = (allocate_p && writable_p);
108 double dh, ds, dv; /* deltas */
110 wanted = total_ncolors;
112 wanted = (wanted / 2) + 1;
115 ncolors = total_ncolors;
117 memset (colors, 0, (*ncolorsP) * sizeof(*colors));
120 ncolors = (ncolors / 2) + 1;
122 /* Note: unlike other routines in this module, this function assumes that
123 if h1 and h2 are more than 180 degrees apart, then the desired direction
124 is always from h1 to h2 (rather than the shorter path.) make_uniform
127 dh = ((double)h2 - (double)h1) / ncolors;
128 ds = (s2 - s1) / ncolors;
129 dv = (v2 - v1) / ncolors;
131 for (i = 0; i < ncolors; i++)
133 colors[i].flags = DoRed|DoGreen|DoBlue;
134 hsv_to_rgb ((int) (h1 + (i*dh)), (s1 + (i*ds)), (v1 + (i*dv)),
135 &colors[i].red, &colors[i].green, &colors[i].blue);
139 for (i = ncolors; i < *ncolorsP; i++)
140 colors[i] = colors[(*ncolorsP)-i];
147 unsigned long *pixels = (unsigned long *)
148 malloc(sizeof(*pixels) * ((*ncolorsP) + 1));
150 /* allocate_writable_colors() won't do here, because we need exactly this
151 number of cells, or the color sequence we've chosen won't fit. */
152 if (! XAllocColorCells(dpy, cmap, False, 0, 0, pixels, *ncolorsP))
158 for (i = 0; i < *ncolorsP; i++)
159 colors[i].pixel = pixels[i];
162 XStoreColors (dpy, cmap, colors, *ncolorsP);
166 for (i = 0; i < *ncolorsP; i++)
170 if (XAllocColor (dpy, cmap, &color))
172 colors[i].pixel = color.pixel;
176 free_colors (dpy, cmap, colors, i);
185 /* we weren't able to allocate all the colors we wanted;
186 decrease the requested number and try again.
188 total_ncolors = (total_ncolors > 170 ? total_ncolors - 20 :
189 total_ncolors > 100 ? total_ncolors - 10 :
190 total_ncolors > 75 ? total_ncolors - 5 :
191 total_ncolors > 25 ? total_ncolors - 3 :
192 total_ncolors > 10 ? total_ncolors - 2 :
193 total_ncolors > 2 ? total_ncolors - 1 :
195 *ncolorsP = total_ncolors;
196 ncolors = total_ncolors;
197 if (total_ncolors > 0)
203 /* don't warn if we got 0 writable colors -- probably TrueColor. */
204 (ncolors != 0 || !wanted_writable))
205 complain (wanted, ncolors, wanted_writable, wanted_writable && writable_p);
209 #define MAXPOINTS 50 /* yeah, so I'm lazy */
213 make_color_path (Display *dpy, Colormap cmap,
214 int npoints, int *h, double *s, double *v,
215 XColor *colors, int *ncolorsP,
220 int total_ncolors = *ncolorsP;
222 int ncolors[MAXPOINTS]; /* number of pixels per edge */
223 double dh[MAXPOINTS]; /* distance between pixels, per edge (0 - 360.0) */
224 double ds[MAXPOINTS]; /* distance between pixels, per edge (0 - 1.0) */
225 double dv[MAXPOINTS]; /* distance between pixels, per edge (0 - 1.0) */
232 else if (npoints == 2) /* using make_color_ramp() will be faster */
234 make_color_ramp (dpy, cmap,
235 h[0], s[0], v[0], h[1], s[1], v[1],
238 allocate_p, writable_p);
241 else if (npoints >= MAXPOINTS)
243 npoints = MAXPOINTS-1;
249 double DH[MAXPOINTS]; /* Distance between H values in the shortest
250 direction around the circle, that is, the
251 distance between 10 and 350 is 20.
252 (Range is 0 - 360.0.)
254 double edge[MAXPOINTS]; /* lengths of edges in unit HSV space. */
255 double ratio[MAXPOINTS]; /* proportions of the edges (total 1.0) */
257 double one_point_oh = 0; /* (debug) */
259 for (i = 0; i < npoints; i++)
261 int j = (i+1) % npoints;
262 double d = ((double) (h[i] - h[j])) / 360;
264 if (d > 0.5) d = 0.5 - (d - 0.5);
268 for (i = 0; i < npoints; i++)
270 int j = (i+1) % npoints;
271 edge[i] = sqrt((DH[i] * DH[j]) +
272 ((s[j] - s[i]) * (s[j] - s[i])) +
273 ((v[j] - v[i]) * (v[j] - v[i])));
278 fprintf(stderr, "\ncolors:");
279 for (i=0; i < npoints; i++)
280 fprintf(stderr, " (%d, %.3f, %.3f)", h[i], s[i], v[i]);
281 fprintf(stderr, "\nlengths:");
282 for (i=0; i < npoints; i++)
283 fprintf(stderr, " %.3f", edge[i]);
289 for (i = 0; i < npoints; i++)
291 ratio[i] = edge[i] / circum;
292 one_point_oh += ratio[i];
296 fprintf(stderr, "\nratios:");
297 for (i=0; i < npoints; i++)
298 fprintf(stderr, " %.3f", ratio[i]);
301 if (one_point_oh < 0.99999 || one_point_oh > 1.00001)
304 /* space the colors evenly along the circumference -- that means that the
305 number of pixels on a edge is proportional to the length of that edge
306 (relative to the lengths of the other edges.)
308 for (i = 0; i < npoints; i++)
309 ncolors[i] = total_ncolors * ratio[i];
313 fprintf(stderr, "\npixels:");
314 for (i=0; i < npoints; i++)
315 fprintf(stderr, " %d", ncolors[i]);
316 fprintf(stderr, " (%d)\n", total_ncolors);
319 for (i = 0; i < npoints; i++)
321 int j = (i+1) % npoints;
325 dh[i] = 360 * (DH[i] / ncolors[i]);
326 ds[i] = (s[j] - s[i]) / ncolors[i];
327 dv[i] = (v[j] - v[i]) / ncolors[i];
332 memset (colors, 0, (*ncolorsP) * sizeof(*colors));
335 for (i = 0; i < npoints; i++)
337 int distance, direction;
338 distance = h[(i+1) % npoints] - h[i];
339 direction = (distance >= 0 ? -1 : 1);
342 distance = 180 - (distance - 180);
343 else if (distance < -180)
344 distance = -(180 - ((-distance) - 180));
346 direction = -direction;
349 fprintf (stderr, "point %d: %3d %.2f %.2f\n",
350 i, h[i], s[i], v[i]);
351 fprintf(stderr, " h[i]=%d dh[i]=%.2f ncolors[i]=%d\n",
352 h[i], dh[i], ncolors[i]);
354 for (j = 0; j < ncolors[i]; j++, k++)
356 double hh = (h[i] + (j * dh[i] * direction));
357 if (hh < 0) hh += 360;
358 else if (hh > 360) hh -= 0;
359 colors[k].flags = DoRed|DoGreen|DoBlue;
362 (s[i] + (j * ds[i])),
363 (v[i] + (j * dv[i])),
364 &colors[k].red, &colors[k].green, &colors[k].blue);
366 fprintf (stderr, "point %d+%d: %.2f %.2f %.2f %04X %04X %04X\n",
369 (s[i] + (j * ds[i])),
370 (v[i] + (j * dv[i])),
371 colors[k].red, colors[k].green, colors[k].blue);
376 /* Floating-point round-off can make us decide to use fewer colors. */
389 unsigned long *pixels = (unsigned long *)
390 malloc(sizeof(*pixels) * ((*ncolorsP) + 1));
392 /* allocate_writable_colors() won't do here, because we need exactly this
393 number of cells, or the color sequence we've chosen won't fit. */
394 if (! XAllocColorCells(dpy, cmap, False, 0, 0, pixels, *ncolorsP))
400 for (i = 0; i < *ncolorsP; i++)
401 colors[i].pixel = pixels[i];
404 XStoreColors (dpy, cmap, colors, *ncolorsP);
408 for (i = 0; i < *ncolorsP; i++)
412 if (XAllocColor (dpy, cmap, &color))
414 colors[i].pixel = color.pixel;
418 free_colors (dpy, cmap, colors, i);
427 /* we weren't able to allocate all the colors we wanted;
428 decrease the requested number and try again.
430 total_ncolors = (total_ncolors > 170 ? total_ncolors - 20 :
431 total_ncolors > 100 ? total_ncolors - 10 :
432 total_ncolors > 75 ? total_ncolors - 5 :
433 total_ncolors > 25 ? total_ncolors - 3 :
434 total_ncolors > 10 ? total_ncolors - 2 :
435 total_ncolors > 2 ? total_ncolors - 1 :
437 *ncolorsP = total_ncolors;
438 if (total_ncolors > 0)
444 make_color_loop (Display *dpy, Colormap cmap,
445 int h0, double s0, double v0, /* 0-360, 0-1.0, 0-1.0 */
446 int h1, double s1, double v1, /* 0-360, 0-1.0, 0-1.0 */
447 int h2, double s2, double v2, /* 0-360, 0-1.0, 0-1.0 */
448 XColor *colors, int *ncolorsP,
454 h[0] = h0; h[1] = h1; h[2] = h2;
455 s[0] = s0; s[1] = s1; s[2] = s2;
456 v[0] = v0; v[1] = v1; v[2] = v2;
457 make_color_path(dpy, cmap,
460 allocate_p, writable_p);
465 make_smooth_colormap (Display *dpy, Visual *visual, Colormap cmap,
466 XColor *colors, int *ncolorsP,
472 int ncolors = *ncolorsP;
473 Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
480 Screen *screen = DefaultScreenOfDisplay(dpy); /* #### WRONG! */
482 if (*ncolorsP <= 0) return;
485 int n = random() % 20;
486 if (n <= 5) npoints = 2; /* 30% of the time */
487 else if (n <= 15) npoints = 3; /* 50% of the time */
488 else if (n <= 18) npoints = 4; /* 15% of the time */
489 else npoints = 5; /* 5% of the time */
493 for (i = 0; i < npoints; i++)
496 h[i] = random() % 360;
498 v[i] = frand(0.8) + 0.2;
500 /* Make sure that no two adjascent colors are *too* close together.
501 If they are, try again.
505 int j = (i+1 == npoints) ? 0 : (i-1);
506 double hi = ((double) h[i]) / 360;
507 double hj = ((double) h[j]) / 360;
510 if (dh < 0) dh = -dh;
511 if (dh > 0.5) dh = 0.5 - (dh - 0.5);
512 distance = sqrt ((dh * dh) +
513 ((s[j] - s[i]) * (s[j] - s[i])) +
514 ((v[j] - v[i]) * (v[j] - v[i])));
516 goto REPICK_THIS_COLOR;
522 /* If the average saturation or intensity are too low, repick the colors,
523 so that we don't end up with a black-and-white or too-dark map.
525 if (total_s / npoints < 0.2)
526 goto REPICK_ALL_COLORS;
527 if (total_v / npoints < 0.3)
528 goto REPICK_ALL_COLORS;
530 /* If this visual doesn't support writable cells, don't bother trying.
532 if (wanted_writable && !has_writable_cells(screen, visual))
533 *writable_pP = False;
536 make_color_path (dpy, cmap, npoints, h, s, v, colors, &ncolors,
537 allocate_p, (writable_pP && *writable_pP));
539 /* If we tried for writable cells and got none, try for non-writable. */
540 if (allocate_p && *ncolorsP == 0 && *writable_pP)
542 *writable_pP = False;
543 goto RETRY_NON_WRITABLE;
547 complain(*ncolorsP, ncolors, wanted_writable,
548 wanted_writable && *writable_pP);
555 make_uniform_colormap (Display *dpy, Visual *visual, Colormap cmap,
556 XColor *colors, int *ncolorsP,
561 int ncolors = *ncolorsP;
562 Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
563 Screen *screen = DefaultScreenOfDisplay(dpy); /* #### WRONG! */
565 double S = ((double) (random() % 34) + 66) / 100.0; /* range 66%-100% */
566 double V = ((double) (random() % 34) + 66) / 100.0; /* range 66%-100% */
568 if (*ncolorsP <= 0) return;
570 /* If this visual doesn't support writable cells, don't bother trying. */
571 if (wanted_writable && !has_writable_cells(screen, visual))
572 *writable_pP = False;
575 make_color_ramp(dpy, cmap,
580 (writable_pP && *writable_pP));
582 /* If we tried for writable cells and got none, try for non-writable. */
583 if (allocate_p && *ncolorsP == 0 && writable_pP && *writable_pP)
586 *writable_pP = False;
587 goto RETRY_NON_WRITABLE;
591 complain(*ncolorsP, ncolors, wanted_writable,
592 wanted_writable && *writable_pP);
599 make_random_colormap (Display *dpy, Visual *visual, Colormap cmap,
600 XColor *colors, int *ncolorsP,
606 Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
607 int ncolors = *ncolorsP;
609 Screen *screen = DefaultScreenOfDisplay(dpy); /* #### WRONG! */
611 if (*ncolorsP <= 0) return;
613 /* If this visual doesn't support writable cells, don't bother trying. */
614 if (wanted_writable && !has_writable_cells(screen, visual))
615 *writable_pP = False;
617 for (i = 0; i < ncolors; i++)
619 colors[i].flags = DoRed|DoGreen|DoBlue;
622 int H = random() % 360; /* range 0-360 */
623 double S = ((double) (random()%70) + 30)/100.0; /* range 30%-100% */
624 double V = ((double) (random()%34) + 66)/100.0; /* range 66%-100% */
626 &colors[i].red, &colors[i].green, &colors[i].blue);
630 colors[i].red = random() % 0xFFFF;
631 colors[i].green = random() % 0xFFFF;
632 colors[i].blue = random() % 0xFFFF;
640 if (writable_pP && *writable_pP)
642 unsigned long *pixels = (unsigned long *)
643 malloc(sizeof(*pixels) * (ncolors + 1));
645 allocate_writable_colors (dpy, cmap, pixels, &ncolors);
647 for (i = 0; i < ncolors; i++)
648 colors[i].pixel = pixels[i];
651 XStoreColors (dpy, cmap, colors, ncolors);
655 for (i = 0; i < ncolors; i++)
659 if (!XAllocColor (dpy, cmap, &color))
661 colors[i].pixel = color.pixel;
666 /* If we tried for writable cells and got none, try for non-writable. */
667 if (allocate_p && ncolors == 0 && writable_pP && *writable_pP)
670 *writable_pP = False;
671 goto RETRY_NON_WRITABLE;
675 complain(*ncolorsP, ncolors, wanted_writable,
676 wanted_writable && *writable_pP);
683 rotate_colors (Display *dpy, Colormap cmap,
684 XColor *colors, int ncolors, int distance)
687 XColor *colors2 = (XColor *) malloc(sizeof(*colors2) * ncolors);
688 if (ncolors < 2) return;
689 distance = distance % ncolors;
690 for (i = 0; i < ncolors; i++)
692 int j = i - distance;
693 if (j >= ncolors) j -= ncolors;
694 if (j < 0) j += ncolors;
695 colors2[i] = colors[j];
696 colors2[i].pixel = colors[i].pixel;
698 XStoreColors (dpy, cmap, colors2, ncolors);
700 memcpy(colors, colors2, sizeof(*colors) * ncolors);