from .. import util
import colorsys
[docs]def hsv2rgb_raw(hsv):
"""
Converts an HSV tuple to RGB. Intended for internal use.
You should use hsv2rgb_spectrum or hsv2rgb_rainbow instead.
"""
HSV_SECTION_3 = 0x40
h, s, v = hsv
# The brightness floor is minimum number that all of
# R, G, and B will be set to.
invsat = 255 - s
brightness_floor = (v * invsat) // 256
# The color amplitude is the maximum amount of R, G, and B
# that will be added on top of the brightness_floor to
# create the specific hue desired.
color_amplitude = v - brightness_floor
# figure out which section of the hue wheel we're in,
# and how far offset we are within that section
section = h // HSV_SECTION_3 # 0..2
offset = h % HSV_SECTION_3 # 0..63
rampup = offset
rampdown = (HSV_SECTION_3 - 1) - offset
# compute color-amplitude-scaled-down versions of rampup and rampdown
rampup_amp_adj = (rampup * color_amplitude) // (256 // 4)
rampdown_amp_adj = (rampdown * color_amplitude) // (256 // 4)
# add brightness_floor offset to everything
rampup_adj_with_floor = rampup_amp_adj + brightness_floor
rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor
r, g, b = (0, 0, 0)
if section:
if section == 1:
# section 1: 0x40..0x7F
r = brightness_floor
g = rampdown_adj_with_floor
b = rampup_adj_with_floor
else:
# section 2; 0x80..0xBF
r = rampup_adj_with_floor
g = brightness_floor
b = rampdown_adj_with_floor
else:
# section 0: 0x00..0x3F
r = rampdown_adj_with_floor
g = rampup_adj_with_floor
b = brightness_floor
return (r, g, b)
[docs]def hsv2rgb_spectrum(hsv):
"""Generates RGB values from HSV values in line with a typical light
spectrum."""
h, s, v = hsv
return hsv2rgb_raw(((h * 192) >> 8, s, v))
[docs]def hsv2rgb_rainbow(hsv):
"""Generates RGB values from HSV that have an even visual
distribution. Be careful as this method is only have as fast as
hsv2rgb_spectrum."""
def nscale8x3_video(r, g, b, scale):
nonzeroscale = 0
if scale != 0:
nonzeroscale = 1
if r != 0:
r = ((r * scale) >> 8) + nonzeroscale
if g != 0:
g = ((g * scale) >> 8) + nonzeroscale
if b != 0:
b = ((b * scale) >> 8) + nonzeroscale
return (r, g, b)
def scale8_video_LEAVING_R1_DIRTY(i, scale):
nonzeroscale = 0
if scale != 0:
nonzeroscale = 1
if i != 0:
i = ((i * scale) >> 8) + nonzeroscale
return i
h, s, v = hsv
offset = h & 0x1F # 0..31
offset8 = offset * 8
third = (offset8 * (256 // 3)) >> 8
r, g, b = (0, 0, 0)
if not (h & 0x80):
if not (h & 0x40):
if not (h & 0x20):
r = 255 - third
g = third
b = 0
else:
r = 171
g = 85 + third
b = 0x00
else:
if not (h & 0x20):
twothirds = (third << 1)
r = 171 - twothirds
g = 171 + third
b = 0
else:
r = 0
g = 255 - third
b = third
else:
if not (h & 0x40):
if not (h & 0x20):
r = 0x00
twothirds = (third << 1)
g = 171 - twothirds
b = 85 + twothirds
else:
r = third
g = 0
b = 255 - third
else:
if not (h & 0x20):
r = 85 + third
g = 0
b = 171 - third
else:
r = 171 + third
g = 0x00
b = 85 - third
if s != 255:
r, g, b = nscale8x3_video(r, g, b, s)
desat = 255 - s
desat = (desat * desat) >> 8
brightness_floor = desat
r = r + brightness_floor
g = g + brightness_floor
b = b + brightness_floor
if v != 255:
v = scale8_video_LEAVING_R1_DIRTY(v, v)
r, g, b = nscale8x3_video(r, g, b, v)
return (r, g, b)
[docs]def hsv2rgb_360(hsv):
"""Python default hsv to rgb conversion for when hue values in the
range 0-359 are preferred. Due to requiring float math, this method
is slower than hsv2rgb_rainbow and hsv2rgb_spectrum."""
h, s, v = hsv
r, g, b = colorsys.hsv_to_rgb(h / 360.0, s, v)
return (int(r * 255.0), int(g * 255.0), int(b * 255.0))
# pre-generated spectra for the sake of speed
HUE_RAW = [hsv2rgb_raw((hue, 255, 255)) for hue in range(256)]
HUE_RAINBOW = [hsv2rgb_rainbow((hue, 255, 255)) for hue in range(256)]
HUE_SPECTRUM = [hsv2rgb_spectrum((hue, 255, 255)) for hue in range(256)]
HUE_360 = [hsv2rgb_360((hue, 1.0, 1.0)) for hue in range(360)]
[docs]def hue2rgb_raw(hue):
if hue >= 0 or hue < 256:
return HUE_RAW[int(hue)]
else:
raise ValueError("hue must be between 0 and 255")
[docs]def hue2rgb_rainbow(hue):
if hue >= 0 or hue < 256:
return HUE_RAINBOW[int(hue)]
else:
raise ValueError("hue must be between 0 and 255")
[docs]def hue2rgb_spectrum(hue):
if hue >= 0 or hue < 256:
return HUE_SPECTRUM[int(hue)]
else:
raise ValueError("hue must be between 0 and 255")
[docs]def hue2rgb_360(hue):
if hue >= 0 or hue < 360:
return HUE_360[int(hue)]
else:
raise ValueError("hue must be between 0 and 359")
hsv2rgb = hsv2rgb_rainbow
hue2rgb = hue2rgb_rainbow
[docs]def hue_gradient(start, stop, steps):
if not (0 <= start <= 255 and 0 <= stop <= 255):
util.log.error(
'hue must be between 0 and 255; start=%s, stop=%s', start, stop)
start = min(255, max(0, start))
stop = min(255, max(0, stop))
flip = False
if start > stop:
start, stop = stop, start
flip = True
stops = util.even_dist(start, stop, steps)
if flip:
stops = stops[::-1]
return stops
[docs]def hue_helper(pos, length, cycle_step):
return hue2rgb(((pos * 255 // length) + cycle_step) % 255)
[docs]def hue_helper360(pos, length, cycle_step):
return hue2rgb_360(((pos * 360 // length) + cycle_step) % 360)
[docs]def rgb_to_hsv(pixel):
return colorsys.rgb_to_hsv(*(p / 255 for p in pixel))
[docs]def color_cmp(a, b):
"""Order colors by hue, saturation and value, in that order.
Returns -1 if a < b, 0 if a == b and 1 if a < b.
"""
if a == b:
return 0
a, b = rgb_to_hsv(a), rgb_to_hsv(b)
return -1 if a < b else 1