1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
|
# Copyright (c) 2005 Gavin E. Crooks
#
# This software is distributed under the MIT Open Source License.
# <http://www.opensource.org/licenses/mit-license.html>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""Color specifications using CSS2 (Cascading Style Sheet) syntax."""
from typing import Any, List
class Color(object):
"""Color specifications using CSS2 (Cascading Style Sheet) syntax.
http://www.w3.org/TR/REC-CSS2/syndata.html#color-units
Usage:
red = Color(255,0,0)
red = Color(1., 0., 0.)
red = Color.by_name("red")
red = Color.from_rgb(1.,0.,0.)
red = Color.from_rgb(255,0,0)
red = Color.from_hsl(0.,1., 0.5)
red = Color.from_string("red")
red = Color.from_string("RED")
red = Color.from_string("#F00")
red = Color.from_string("#FF0000")
red = Color.from_string("rgb(255, 0, 0)")
red = Color.from_string("rgb(100%, 0%, 0%)")
red = Color.from_string("hsl(0, 100%, 50%)")
"""
def __init__(self, red: float, green: float, blue: float) -> None:
if not (type(red) is type(green) is type(blue)):
raise TypeError("Mixed floats and integers?")
# Convert integer RBG values in [0, 255] to floats in [0, 1]
if isinstance(red, int):
red /= 255.0
if isinstance(green, int):
green /= 255.0
if isinstance(blue, int):
blue /= 255.0
# Clip RBG values to [0, 1]
self.red = max(0.0, min(red, 1.0))
self.green = max(0.0, min(green, 1.0))
self.blue = max(0.0, min(blue, 1.0))
@staticmethod
def names() -> List[str]:
"Return a list of standard color names."
return list(_std_colors.keys())
@classmethod
def from_rgb(cls, r: float, g: float, b: float) -> "Color":
return cls(r, g, b)
@classmethod
def from_hsl(cls, hue_angle: float, saturation: float, lightness: float) -> "Color":
def hue_to_rgb(v1: float, v2: float, vH: float) -> float:
if vH < 0.0:
vH += 1.0
if vH > 1.0:
vH -= 1.0 # pragma: no cover (Grandfathered in)
if vH * 6.0 < 1.0:
return v1 + (v2 - v1) * 6.0 * vH
if vH * 2.0 < 1.0:
return v2
if vH * 3.0 < 2.0:
return v1 + (v2 - v1) * ((2.0 / 3.0) - vH) * 6.0 # pragma: no cover
return v1
hue = (((hue_angle % 360.0) + 360.0) % 360.0) / 360.0
if not (saturation >= 0.0 and saturation <= 1.0):
raise ValueError(
"Out-of-range saturation %f" % saturation
) # pragma: no cover
if not (lightness >= 0.0 and lightness <= 1.0):
raise ValueError(
"Out-of-range lightness %f" % lightness
) # pragma: no cover
if saturation == 0:
# greyscale
return cls.from_rgb(lightness, lightness, lightness)
if lightness < 0.5:
v2 = lightness * (1.0 + saturation)
else:
v2 = (lightness + saturation) - (saturation * lightness)
v1 = 2.0 * lightness - v2
r = hue_to_rgb(v1, v2, hue + (1.0 / 3.0))
g = hue_to_rgb(v1, v2, hue)
b = hue_to_rgb(v1, v2, hue - (1.0 / 3.0))
return cls(r, g, b)
@staticmethod
def by_name(string: str) -> "Color":
s = string.strip().lower().replace(" ", "")
try:
return _std_colors[s]
except KeyError:
raise ValueError("Unknown color name: %s" % s)
@classmethod
def from_string(cls, string: str) -> "Color":
def to_frac(string: str) -> float:
# string can be "255" or "100%"
if string[-1] == "%":
return float(string[0:-1]) / 100.0
else:
return float(string) / 255.0
s = string.strip().lower().replace(" ", "").replace("_", "")
if s in _std_colors: # "red"
return _std_colors[s]
if s[0] == "#": # "#fef"
if len(s) == 4:
r = int(s[1] + s[1], 16)
g = int(s[2] + s[2], 16)
b = int(s[3] + s[3], 16)
return cls(r, g, b)
elif len(s) == 7: # "#ff00aa"
r = int(s[1:3], 16)
g = int(s[3:5], 16)
b = int(s[5:7], 16)
return cls(r, g, b)
else:
raise ValueError("Cannot parse string: %s" % s)
if s[0:4] == "rgb(" and s[-1] == ")":
rgb = s[4:-1].split(",")
if len(rgb) != 3:
raise ValueError("Cannot parse string a: %s" % s)
return cls(to_frac(rgb[0]), to_frac(rgb[1]), to_frac(rgb[2]))
if s[0:4] == "hsl(" and s[-1] == ")":
hsl = s[4:-1].split(",")
if len(hsl) != 3:
raise ValueError("Cannot parse string a: %s" % s)
return cls.from_hsl(int(hsl[0]), to_frac(hsl[1]), to_frac(hsl[2]))
raise ValueError("Cannot parse string: %s" % s)
def __eq__(self, other: Any) -> bool:
if not isinstance(other, self.__class__):
return False
req = int(0.5 + 255.0 * self.red) == int(0.5 + 255.0 * other.red)
beq = int(0.5 + 255.0 * self.blue) == int(0.5 + 255.0 * other.blue)
geq = int(0.5 + 255.0 * self.green) == int(0.5 + 255.0 * other.green)
return req and beq and geq
def __repr__(self) -> str:
return "Color(%f,%f,%f)" % (self.red, self.green, self.blue)
_std_colors = dict(
aliceblue=Color(240, 248, 255), # f0f8ff
antiquewhite=Color(250, 235, 215), # faebd7
aqua=Color(0, 255, 255), # 00ffff
aquamarine=Color(127, 255, 212), # 7fffd4
azure=Color(240, 255, 255), # f0ffff
beige=Color(245, 245, 220), # f5f5dc
bisque=Color(255, 228, 196), # ffe4c4
black=Color(0, 0, 0), # 000000
blanchedalmond=Color(255, 235, 205), # ffebcd
blue=Color(0, 0, 255), # 0000ff
blueviolet=Color(138, 43, 226), # 8a2be2
brown=Color(165, 42, 42), # a52a2a
burlywood=Color(222, 184, 135), # deb887
cadetblue=Color(95, 158, 160), # 5f9ea0
chartreuse=Color(127, 255, 0), # 7fff00
chocolate=Color(210, 105, 30), # d2691e
coral=Color(255, 127, 80), # ff7f50
cornflowerblue=Color(100, 149, 237), # 6495ed
cornsilk=Color(255, 248, 220), # fff8dc
crimson=Color(220, 20, 60), # dc143c
cyan=Color(0, 255, 255), # 00ffff
darkblue=Color(0, 0, 139), # 00008b
darkcyan=Color(0, 139, 139), # 008b8b
darkgoldenrod=Color(184, 134, 11), # b8860b
darkgray=Color(169, 169, 169), # a9a9a9
darkgreen=Color(0, 100, 0), # 006400
darkgrey=Color(169, 169, 169), # a9a9a9
darkkhaki=Color(189, 183, 107), # bdb76b
darkmagenta=Color(139, 0, 139), # 8b008b
darkolivegreen=Color(85, 107, 47), # 556b2f
darkorange=Color(255, 140, 0), # ff8c00
darkorchid=Color(153, 50, 204), # 9932cc
darkred=Color(139, 0, 0), # 8b0000
darksalmon=Color(233, 150, 122), # e9967a
darkseagreen=Color(143, 188, 143), # 8fbc8f
darkslateblue=Color(72, 61, 139), # 483d8b
darkslategray=Color(47, 79, 79), # 2f4f4f
darkslategrey=Color(47, 79, 79), # 2f4f4f
darkturquoise=Color(0, 206, 209), # 00ced1
darkviolet=Color(148, 0, 211), # 9400d3
deeppink=Color(255, 20, 147), # ff1493
deepskyblue=Color(0, 191, 255), # 00bfff
dimgray=Color(105, 105, 105), # 696969
dimgrey=Color(105, 105, 105), # 696969
dodgerblue=Color(30, 144, 255), # 1e90ff
firebrick=Color(178, 34, 34), # b22222
floralwhite=Color(255, 250, 240), # fffaf0
forestgreen=Color(34, 139, 34), # 228b22
fuchsia=Color(255, 0, 255), # ff00ff
gainsboro=Color(220, 220, 220), # dcdcdc
ghostwhite=Color(248, 248, 255), # f8f8ff
gold=Color(255, 215, 0), # ffd700
goldenrod=Color(218, 165, 32), # daa520
gray=Color(128, 128, 128), # 808080
green=Color(0, 128, 0), # 008000
greenyellow=Color(173, 255, 47), # adff2f
grey=Color(128, 128, 128), # 808080
honeydew=Color(240, 255, 240), # f0fff0
hotpink=Color(255, 105, 180), # ff69b4
indianred=Color(205, 92, 92), # cd5c5c
indigo=Color(75, 0, 130), # 4b0082
ivory=Color(255, 255, 240), # fffff0
khaki=Color(240, 230, 140), # f0e68c
lavender=Color(230, 230, 250), # e6e6fa
lavenderblush=Color(255, 240, 245), # fff0f5
lawngreen=Color(124, 252, 0), # 7cfc00
lemonchiffon=Color(255, 250, 205), # fffacd
lightblue=Color(173, 216, 230), # add8e6
lightcoral=Color(240, 128, 128), # f08080
lightcyan=Color(224, 255, 255), # e0ffff
lightgoldenrodyellow=Color(250, 250, 210), # fafad2
lightgray=Color(211, 211, 211), # d3d3d3
lightgreen=Color(144, 238, 144), # 90ee90
lightgrey=Color(211, 211, 211), # d3d3d3
lightpink=Color(255, 182, 193), # ffb6c1
lightsalmon=Color(255, 160, 122), # ffa07a
lightseagreen=Color(32, 178, 170), # 20b2aa
lightskyblue=Color(135, 206, 250), # 87cefa
lightslategray=Color(119, 136, 153), # 778899
lightslategrey=Color(119, 136, 153), # 778899
lightsteelblue=Color(176, 196, 222), # b0c4de
lightyellow=Color(255, 255, 224), # ffffe0
lime=Color(0, 255, 0), # 00ff00
limegreen=Color(50, 205, 50), # 32cd32
linen=Color(250, 240, 230), # faf0e6
magenta=Color(255, 0, 255), # ff00ff
maroon=Color(128, 0, 0), # 800000
mediumaquamarine=Color(102, 205, 170), # 66cdaa
mediumblue=Color(0, 0, 205), # 0000cd
mediumorchid=Color(186, 85, 211), # ba55d3
mediumpurple=Color(147, 112, 219), # 9370db
mediumseagreen=Color(60, 179, 113), # 3cb371
mediumslateblue=Color(123, 104, 238), # 7b68ee
mediumspringgreen=Color(0, 250, 154), # 00fa9a
mediumturquoise=Color(72, 209, 204), # 48d1cc
mediumvioletred=Color(199, 21, 133), # c71585
midnightblue=Color(25, 25, 112), # 191970
mintcream=Color(245, 255, 250), # f5fffa
mistyrose=Color(255, 228, 225), # ffe4e1
moccasin=Color(255, 228, 181), # ffe4b5
navajowhite=Color(255, 222, 173), # ffdead
navy=Color(0, 0, 128), # 000080
oldlace=Color(253, 245, 230), # fdf5e6
olive=Color(128, 128, 0), # 808000
olivedrab=Color(107, 142, 35), # 6b8e23
orange=Color(255, 165, 0), # ffa500
orangered=Color(255, 69, 0), # ff4500
orchid=Color(218, 112, 214), # da70d6
palegoldenrod=Color(238, 232, 170), # eee8aa
palegreen=Color(152, 251, 152), # 98fb98
paleturquoise=Color(175, 238, 238), # afeeee
palevioletred=Color(219, 112, 147), # db7093
papayawhip=Color(255, 239, 213), # ffefd5
peachpuff=Color(255, 218, 185), # ffdab9
peru=Color(205, 133, 63), # cd853f
pink=Color(255, 192, 203), # ffc0cb
plum=Color(221, 160, 221), # dda0dd
powderblue=Color(176, 224, 230), # b0e0e6
purple=Color(128, 0, 128), # 800080
red=Color(255, 0, 0), # ff0000
rosybrown=Color(188, 143, 143), # bc8f8f
royalblue=Color(65, 105, 225), # 4169e1
saddlebrown=Color(139, 69, 19), # 8b4513
salmon=Color(250, 128, 114), # fa8072
sandybrown=Color(244, 164, 96), # f4a460
seagreen=Color(46, 139, 87), # 2e8b57
seashell=Color(255, 245, 238), # fff5ee
sienna=Color(160, 82, 45), # a0522d
silver=Color(192, 192, 192), # c0c0c0
skyblue=Color(135, 206, 235), # 87ceeb
slateblue=Color(106, 90, 205), # 6a5acd
slategray=Color(112, 128, 144), # 708090
slategrey=Color(112, 128, 144), # 708090
snow=Color(255, 250, 250), # fffafa
springgreen=Color(0, 255, 127), # 00ff7f
steelblue=Color(70, 130, 180), # 4682b4
tan=Color(210, 180, 140), # d2b48c
teal=Color(0, 128, 128), # 008080
thistle=Color(216, 191, 216), # d8bfd8
tomato=Color(255, 99, 71), # ff6347
turquoise=Color(64, 224, 208), # 40e0d0
violet=Color(238, 130, 238), # ee82ee
wheat=Color(245, 222, 179), # f5deb3
white=Color(255, 255, 255), # ffffff
whitesmoke=Color(245, 245, 245), # f5f5f5
yellow=Color(255, 255, 0), # ffff00
yellowgreen=Color(154, 205, 50), # 9acd32
)
|