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