/* xscreensaver, Copyright (c) 1997-2018 Jamie Zawinski <jwz@jwz.org>
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
/* This file contains some utility routines for randomly picking the colors
free_colors (Screen
*screen
, Colormap cmap
, XColor
*colors
, int ncolors
)
Display
*dpy
= screen
? DisplayOfScreen (screen
) : 0;
unsigned long *pixels
= (unsigned long *)
malloc(sizeof(*pixels
) * ncolors
);
for (i
= 0; i
< ncolors
; i
++)
pixels
[i
] = colors
[i
].pixel
;
XFreeColors (dpy
, cmap
, pixels
, ncolors
, 0L);
allocate_writable_colors (Screen
*screen
, Colormap cmap
,
unsigned long *pixels
, int *ncolorsP
)
Display
*dpy
= screen
? DisplayOfScreen (screen
) : 0;
unsigned long *new_pixels
= pixels
;
if (desired
- got
< requested
)
requested
= desired
- got
;
if (XAllocColorCells (dpy
, cmap
, False
, 0, 0, new_pixels
, requested
))
/* Got all the pixels we asked for. */
/* We didn't get all/any of the pixels we asked for. This time, ask
for half as many. (If we do get all that we ask for, we ask for
the same number again next time, so we only do O(log(n)) server
requested
= requested
/ 2;
complain (int wanted_colors
, int got_colors
,
Bool wanted_writable
, Bool got_writable
)
if (got_colors
> wanted_colors
- 10)
/* don't bother complaining if we're within ten pixels. */
if (wanted_writable
&& !got_writable
)
"%s: wanted %d writable colors; got %d read-only colors.\n",
progname
, wanted_colors
, got_colors
);
fprintf (stderr
, "%s: wanted %d%s colors; got %d.\n",
progname
, wanted_colors
, (got_writable
? " writable" : ""),
make_color_ramp (Screen
*screen
, Visual
*visual
, Colormap cmap
,
int h1
, double s1
, double v1
, /* 0-360, 0-1.0, 0-1.0 */
int h2
, double s2
, double v2
, /* 0-360, 0-1.0, 0-1.0 */
XColor
*colors
, int *ncolorsP
,
Display
*dpy
= screen
? DisplayOfScreen (screen
) : 0;
Bool verbose_p
= True
; /* argh. */
int total_ncolors
= *ncolorsP
;
Bool wanted_writable
= (allocate_p
&& writable_pP
&& *writable_pP
);
double dh
, ds
, dv
; /* deltas */
wanted
= (wanted
/ 2) + 1;
/* If this visual doesn't support writable cells, don't bother trying.
if (wanted_writable
&& !has_writable_cells(screen
, visual
))
memset (colors
, 0, (*ncolorsP
) * sizeof(*colors
));
ncolors
= (ncolors
/ 2) + 1;
/* Note: unlike other routines in this module, this function assumes that
if h1 and h2 are more than 180 degrees apart, then the desired direction
is always from h1 to h2 (rather than the shorter path.) make_uniform
dh
= ((double)h2
- (double)h1
) / ncolors
;
ds
= (s2
- s1
) / ncolors
;
dv
= (v2
- v1
) / ncolors
;
for (i
= 0; i
< ncolors
; i
++)
colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
hsv_to_rgb ((int) (h1
+ (i
*dh
)), (s1
+ (i
*ds
)), (v1
+ (i
*dv
)),
&colors
[i
].red
, &colors
[i
].green
, &colors
[i
].blue
);
for (i
= ncolors
; i
< *ncolorsP
; i
++)
colors
[i
] = colors
[(*ncolorsP
)-i
];
if (writable_pP
&& *writable_pP
)
unsigned long *pixels
= (unsigned long *)
malloc(sizeof(*pixels
) * ((*ncolorsP
) + 1));
/* allocate_writable_colors() won't do here, because we need exactly this
number of cells, or the color sequence we've chosen won't fit. */
if (! XAllocColorCells(dpy
, cmap
, False
, 0, 0, pixels
, *ncolorsP
))
for (i
= 0; i
< *ncolorsP
; i
++)
colors
[i
].pixel
= pixels
[i
];
XStoreColors (dpy
, cmap
, colors
, *ncolorsP
);
for (i
= 0; i
< *ncolorsP
; i
++)
if (XAllocColor (dpy
, cmap
, &color
))
colors
[i
].pixel
= color
.pixel
;
free_colors (screen
, cmap
, colors
, i
);
/* we weren't able to allocate all the colors we wanted;
decrease the requested number and try again.
total_ncolors
= (total_ncolors
> 170 ? total_ncolors
- 20 :
total_ncolors
> 100 ? total_ncolors
- 10 :
total_ncolors
> 75 ? total_ncolors
- 5 :
total_ncolors
> 25 ? total_ncolors
- 3 :
total_ncolors
> 10 ? total_ncolors
- 2 :
total_ncolors
> 2 ? total_ncolors
- 1 :
*ncolorsP
= total_ncolors
;
/* don't warn if we got 0 writable colors -- probably TrueColor. */
(ncolors
!= 0 || !wanted_writable
))
complain (wanted
, ncolors
, wanted_writable
,
(wanted_writable
&& writable_pP
&& *writable_pP
));
#define MAXPOINTS 50 /* yeah, so I'm lazy */
make_color_path (Screen
*screen
, Visual
*visual
, Colormap cmap
,
int npoints
, int *h
, double *s
, double *v
,
XColor
*colors
, int *ncolorsP
,
Display
*dpy
= screen
? DisplayOfScreen (screen
) : 0;
int total_ncolors
= *ncolorsP
;
int ncolors
[MAXPOINTS
]; /* number of pixels per edge */
double dh
[MAXPOINTS
]; /* distance between pixels, per edge (0 - 360.0) */
double ds
[MAXPOINTS
]; /* distance between pixels, per edge (0 - 1.0) */
double dv
[MAXPOINTS
]; /* distance between pixels, per edge (0 - 1.0) */
else if (npoints
== 2) /* using make_color_ramp() will be faster */
make_color_ramp (screen
, visual
, cmap
,
h
[0], s
[0], v
[0], h
[1], s
[1], v
[1],
allocate_p
, writable_pP
);
else if (npoints
>= MAXPOINTS
)
double DH
[MAXPOINTS
]; /* Distance between H values in the shortest
direction around the circle, that is, the
distance between 10 and 350 is 20.
double edge
[MAXPOINTS
]; /* lengths of edges in unit HSV space. */
double ratio
[MAXPOINTS
]; /* proportions of the edges (total 1.0) */
double one_point_oh
= 0; /* (debug) */
for (i
= 0; i
< npoints
; i
++)
double d
= ((double) (h
[i
] - h
[j
])) / 360;
if (d
> 0.5) d
= 0.5 - (d
- 0.5);
for (i
= 0; i
< npoints
; i
++)
edge
[i
] = sqrt((DH
[i
] * DH
[j
]) +
((s
[j
] - s
[i
]) * (s
[j
] - s
[i
])) +
((v
[j
] - v
[i
]) * (v
[j
] - v
[i
])));
fprintf(stderr
, "\ncolors:");
for (i
=0; i
< npoints
; i
++)
fprintf(stderr
, " (%d, %.3f, %.3f)", h
[i
], s
[i
], v
[i
]);
fprintf(stderr
, "\nlengths:");
for (i
=0; i
< npoints
; i
++)
fprintf(stderr
, " %.3f", edge
[i
]);
for (i
= 0; i
< npoints
; i
++)
ratio
[i
] = edge
[i
] / circum
;
one_point_oh
+= ratio
[i
];
fprintf(stderr
, "\nratios:");
for (i
=0; i
< npoints
; i
++)
fprintf(stderr
, " %.3f", ratio
[i
]);
if (one_point_oh
< 0.99999 || one_point_oh
> 1.00001)
/* space the colors evenly along the circumference -- that means that the
number of pixels on a edge is proportional to the length of that edge
(relative to the lengths of the other edges.)
for (i
= 0; i
< npoints
; i
++)
ncolors
[i
] = total_ncolors
* ratio
[i
];
fprintf(stderr
, "\npixels:");
for (i
=0; i
< npoints
; i
++)
fprintf(stderr
, " %d", ncolors
[i
]);
fprintf(stderr
, " (%d)\n", total_ncolors
);
for (i
= 0; i
< npoints
; i
++)
dh
[i
] = 360 * (DH
[i
] / ncolors
[i
]);
ds
[i
] = (s
[j
] - s
[i
]) / ncolors
[i
];
dv
[i
] = (v
[j
] - v
[i
]) / ncolors
[i
];
memset (colors
, 0, (*ncolorsP
) * sizeof(*colors
));
for (i
= 0; i
< npoints
; i
++)
int distance
= h
[(i
+1) % npoints
] - h
[i
];
int direction
= (distance
>= 0 ? -1 : 1);
if (distance
<= 180 && distance
>= -180)
fprintf (stderr
, "point %d: %3d %.2f %.2f\n",
fprintf(stderr
, " h[i]=%d dh[i]=%.2f ncolors[i]=%d\n",
h
[i
], dh
[i
], ncolors
[i
]);
for (j
= 0; j
< ncolors
[i
]; j
++, k
++)
double hh
= (h
[i
] + (j
* dh
[i
] * direction
));
else if (hh
> 360) hh
-= 0;
colors
[k
].flags
= DoRed
|DoGreen
|DoBlue
;
&colors
[k
].red
, &colors
[k
].green
, &colors
[k
].blue
);
fprintf (stderr
, "point %d+%d: %.2f %.2f %.2f %04X %04X %04X\n",
colors
[k
].red
, colors
[k
].green
, colors
[k
].blue
);
/* Floating-point round-off can make us decide to use fewer colors. */
/* We used to just return the smaller set of colors, but that meant
that after re-generating the color map repeatedly, the number of
colors in use would tend toward 0, which not only looked bad but
also often caused crashes. So instead, just duplicate the last
color to pad things out. */
for (i
= k
; i
< *ncolorsP
; i
++)
/* #### Should duplicate the allocation of the color cell here
to avoid a double-color-free on PseudoColor, but it's 2018
if (writable_pP
&& *writable_pP
)
unsigned long *pixels
= (unsigned long *)
malloc(sizeof(*pixels
) * ((*ncolorsP
) + 1));
/* allocate_writable_colors() won't do here, because we need exactly this
number of cells, or the color sequence we've chosen won't fit. */
if (! XAllocColorCells(dpy
, cmap
, False
, 0, 0, pixels
, *ncolorsP
))
for (i
= 0; i
< *ncolorsP
; i
++)
colors
[i
].pixel
= pixels
[i
];
XStoreColors (dpy
, cmap
, colors
, *ncolorsP
);
for (i
= 0; i
< *ncolorsP
; i
++)
if (XAllocColor (dpy
, cmap
, &color
))
colors
[i
].pixel
= color
.pixel
;
free_colors (screen
, cmap
, colors
, i
);
/* we weren't able to allocate all the colors we wanted;
decrease the requested number and try again.
total_ncolors
= (total_ncolors
> 170 ? total_ncolors
- 20 :
total_ncolors
> 100 ? total_ncolors
- 10 :
total_ncolors
> 75 ? total_ncolors
- 5 :
total_ncolors
> 25 ? total_ncolors
- 3 :
total_ncolors
> 10 ? total_ncolors
- 2 :
total_ncolors
> 2 ? total_ncolors
- 1 :
*ncolorsP
= total_ncolors
;
make_color_loop (Screen
*screen
, Visual
*visual
, Colormap cmap
,
int h0
, double s0
, double v0
, /* 0-360, 0-1.0, 0-1.0 */
int h1
, double s1
, double v1
, /* 0-360, 0-1.0, 0-1.0 */
int h2
, double s2
, double v2
, /* 0-360, 0-1.0, 0-1.0 */
XColor
*colors
, int *ncolorsP
,
Bool wanted_writable
= (allocate_p
&& writable_pP
&& *writable_pP
);
h
[0] = h0
; h
[1] = h1
; h
[2] = h2
;
s
[0] = s0
; s
[1] = s1
; s
[2] = s2
;
v
[0] = v0
; v
[1] = v1
; v
[2] = v2
;
/* If this visual doesn't support writable cells, don't bother trying.
if (wanted_writable
&& !has_writable_cells(screen
, visual
))
make_color_path (screen
, visual
, cmap
,
allocate_p
, writable_pP
);
make_smooth_colormap (Screen
*screen
, Visual
*visual
, Colormap cmap
,
XColor
*colors
, int *ncolorsP
,
Bool wanted_writable
= (allocate_p
&& writable_pP
&& *writable_pP
);
if (*ncolorsP
<= 0) return;
if (n
<= 5) npoints
= 2; /* 30% of the time */
else if (n
<= 15) npoints
= 3; /* 50% of the time */
else if (n
<= 18) npoints
= 4; /* 15% of the time */
else npoints
= 5; /* 5% of the time */
for (i
= 0; i
< npoints
; i
++)
if (++loop
> 10000) abort();
/* Make sure that no two adjascent colors are *too* close together.
int j
= (i
+1 == npoints
) ? 0 : (i
-1);
double hi
= ((double) h
[i
]) / 360;
double hj
= ((double) h
[j
]) / 360;
if (dh
> 0.5) dh
= 0.5 - (dh
- 0.5);
distance
= sqrt ((dh
* dh
) +
((s
[j
] - s
[i
]) * (s
[j
] - s
[i
])) +
((v
[j
] - v
[i
]) * (v
[j
] - v
[i
])));
/* If the average saturation or intensity are too low, repick the colors,
so that we don't end up with a black-and-white or too-dark map.
if (total_s
/ npoints
< 0.2)
if (total_v
/ npoints
< 0.3)
/* If this visual doesn't support writable cells, don't bother trying.
if (wanted_writable
&& !has_writable_cells(screen
, visual
))
make_color_path (screen
, visual
, cmap
, npoints
, h
, s
, v
, colors
, &ncolors
,
allocate_p
, writable_pP
);
/* If we tried for writable cells and got none, try for non-writable. */
if (allocate_p
&& *ncolorsP
== 0 && writable_pP
&& *writable_pP
)
complain(*ncolorsP
, ncolors
, wanted_writable
,
wanted_writable
&& *writable_pP
);
make_uniform_colormap (Screen
*screen
, Visual
*visual
, Colormap cmap
,
XColor
*colors
, int *ncolorsP
,
Bool wanted_writable
= (allocate_p
&& writable_pP
&& *writable_pP
);
double S
= ((double) (random() % 34) + 66) / 100.0; /* range 66%-100% */
double V
= ((double) (random() % 34) + 66) / 100.0; /* range 66%-100% */
if (*ncolorsP
<= 0) return;
/* If this visual doesn't support writable cells, don't bother trying. */
if (wanted_writable
&& !has_writable_cells(screen
, visual
))
make_color_ramp(screen
, visual
, cmap
,
False
, allocate_p
, writable_pP
);
/* If we tried for writable cells and got none, try for non-writable. */
if (allocate_p
&& *ncolorsP
== 0 && writable_pP
&& *writable_pP
)
complain(*ncolorsP
, ncolors
, wanted_writable
,
wanted_writable
&& *writable_pP
);
make_random_colormap (Screen
*screen
, Visual
*visual
, Colormap cmap
,
XColor
*colors
, int *ncolorsP
,
Display
*dpy
= screen
? DisplayOfScreen (screen
) : 0;
Bool wanted_writable
= (allocate_p
&& writable_pP
&& *writable_pP
);
if (*ncolorsP
<= 0) return;
/* If this visual doesn't support writable cells, don't bother trying. */
if (wanted_writable
&& !has_writable_cells(screen
, visual
))
for (i
= 0; i
< ncolors
; i
++)
colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
int H
= random() % 360; /* range 0-360 */
double S
= ((double) (random()%70) + 30)/100.0; /* range 30%-100% */
double V
= ((double) (random()%34) + 66)/100.0; /* range 66%-100% */
&colors
[i
].red
, &colors
[i
].green
, &colors
[i
].blue
);
colors
[i
].red
= random() % 0xFFFF;
colors
[i
].green
= random() % 0xFFFF;
colors
[i
].blue
= random() % 0xFFFF;
/* If there are a small number of colors, make sure at least the first
if (!bright_p
&& ncolors
<= 4)
rgb_to_hsv (colors
[0].red
, colors
[0].green
, colors
[0].blue
, &h0
,&s0
,&v0
);
rgb_to_hsv (colors
[1].red
, colors
[1].green
, colors
[1].blue
, &h1
,&s1
,&v1
);
if (writable_pP
&& *writable_pP
)
unsigned long *pixels
= (unsigned long *)
malloc(sizeof(*pixels
) * (ncolors
+ 1));
allocate_writable_colors (screen
, cmap
, pixels
, &ncolors
);
for (i
= 0; i
< ncolors
; i
++)
colors
[i
].pixel
= pixels
[i
];
XStoreColors (dpy
, cmap
, colors
, ncolors
);
for (i
= 0; i
< ncolors
; i
++)
if (!XAllocColor (dpy
, cmap
, &color
))
colors
[i
].pixel
= color
.pixel
;
/* If we tried for writable cells and got none, try for non-writable. */
if (allocate_p
&& ncolors
== 0 && writable_pP
&& *writable_pP
)
complain(*ncolorsP
, ncolors
, wanted_writable
,
wanted_writable
&& *writable_pP
);
rotate_colors (Screen
*screen
, Colormap cmap
,
XColor
*colors
, int ncolors
, int distance
)
Display
*dpy
= screen
? DisplayOfScreen (screen
) : 0;
colors2
= (XColor
*) malloc(sizeof(*colors2
) * ncolors
);
distance
= distance
% ncolors
;
for (i
= 0; i
< ncolors
; i
++)
if (j
>= ncolors
) j
-= ncolors
;
colors2
[i
].pixel
= colors
[i
].pixel
;
XStoreColors (dpy
, cmap
, colors2
, ncolors
);
memcpy(colors
, colors2
, sizeof(*colors
) * ncolors
);