|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations |
|
|
|
import re |
|
from functools import lru_cache |
|
|
|
from . import Image |
|
|
|
|
|
@lru_cache |
|
def getrgb(color): |
|
""" |
|
Convert a color string to an RGB or RGBA tuple. If the string cannot be |
|
parsed, this function raises a :py:exc:`ValueError` exception. |
|
|
|
.. versionadded:: 1.1.4 |
|
|
|
:param color: A color string |
|
:return: ``(red, green, blue[, alpha])`` |
|
""" |
|
if len(color) > 100: |
|
msg = "color specifier is too long" |
|
raise ValueError(msg) |
|
color = color.lower() |
|
|
|
rgb = colormap.get(color, None) |
|
if rgb: |
|
if isinstance(rgb, tuple): |
|
return rgb |
|
colormap[color] = rgb = getrgb(rgb) |
|
return rgb |
|
|
|
|
|
if re.match("#[a-f0-9]{3}$", color): |
|
return int(color[1] * 2, 16), int(color[2] * 2, 16), int(color[3] * 2, 16) |
|
|
|
if re.match("#[a-f0-9]{4}$", color): |
|
return ( |
|
int(color[1] * 2, 16), |
|
int(color[2] * 2, 16), |
|
int(color[3] * 2, 16), |
|
int(color[4] * 2, 16), |
|
) |
|
|
|
if re.match("#[a-f0-9]{6}$", color): |
|
return int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16) |
|
|
|
if re.match("#[a-f0-9]{8}$", color): |
|
return ( |
|
int(color[1:3], 16), |
|
int(color[3:5], 16), |
|
int(color[5:7], 16), |
|
int(color[7:9], 16), |
|
) |
|
|
|
m = re.match(r"rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) |
|
if m: |
|
return int(m.group(1)), int(m.group(2)), int(m.group(3)) |
|
|
|
m = re.match(r"rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) |
|
if m: |
|
return ( |
|
int((int(m.group(1)) * 255) / 100.0 + 0.5), |
|
int((int(m.group(2)) * 255) / 100.0 + 0.5), |
|
int((int(m.group(3)) * 255) / 100.0 + 0.5), |
|
) |
|
|
|
m = re.match( |
|
r"hsl\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color |
|
) |
|
if m: |
|
from colorsys import hls_to_rgb |
|
|
|
rgb = hls_to_rgb( |
|
float(m.group(1)) / 360.0, |
|
float(m.group(3)) / 100.0, |
|
float(m.group(2)) / 100.0, |
|
) |
|
return ( |
|
int(rgb[0] * 255 + 0.5), |
|
int(rgb[1] * 255 + 0.5), |
|
int(rgb[2] * 255 + 0.5), |
|
) |
|
|
|
m = re.match( |
|
r"hs[bv]\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color |
|
) |
|
if m: |
|
from colorsys import hsv_to_rgb |
|
|
|
rgb = hsv_to_rgb( |
|
float(m.group(1)) / 360.0, |
|
float(m.group(2)) / 100.0, |
|
float(m.group(3)) / 100.0, |
|
) |
|
return ( |
|
int(rgb[0] * 255 + 0.5), |
|
int(rgb[1] * 255 + 0.5), |
|
int(rgb[2] * 255 + 0.5), |
|
) |
|
|
|
m = re.match(r"rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) |
|
if m: |
|
return int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)) |
|
msg = f"unknown color specifier: {repr(color)}" |
|
raise ValueError(msg) |
|
|
|
|
|
@lru_cache |
|
def getcolor(color, mode): |
|
""" |
|
Same as :py:func:`~PIL.ImageColor.getrgb` for most modes. However, if |
|
``mode`` is HSV, converts the RGB value to a HSV value, or if ``mode`` is |
|
not color or a palette image, converts the RGB value to a grayscale value. |
|
If the string cannot be parsed, this function raises a :py:exc:`ValueError` |
|
exception. |
|
|
|
.. versionadded:: 1.1.4 |
|
|
|
:param color: A color string |
|
:param mode: Convert result to this mode |
|
:return: ``(graylevel[, alpha]) or (red, green, blue[, alpha])`` |
|
""" |
|
|
|
color, alpha = getrgb(color), 255 |
|
if len(color) == 4: |
|
color, alpha = color[:3], color[3] |
|
|
|
if mode == "HSV": |
|
from colorsys import rgb_to_hsv |
|
|
|
r, g, b = color |
|
h, s, v = rgb_to_hsv(r / 255, g / 255, b / 255) |
|
return int(h * 255), int(s * 255), int(v * 255) |
|
elif Image.getmodebase(mode) == "L": |
|
r, g, b = color |
|
|
|
|
|
color = (r * 19595 + g * 38470 + b * 7471 + 0x8000) >> 16 |
|
if mode[-1] == "A": |
|
return color, alpha |
|
else: |
|
if mode[-1] == "A": |
|
return color + (alpha,) |
|
return color |
|
|
|
|
|
colormap = { |
|
|
|
|
|
|
|
"aliceblue": "#f0f8ff", |
|
"antiquewhite": "#faebd7", |
|
"aqua": "#00ffff", |
|
"aquamarine": "#7fffd4", |
|
"azure": "#f0ffff", |
|
"beige": "#f5f5dc", |
|
"bisque": "#ffe4c4", |
|
"black": "#000000", |
|
"blanchedalmond": "#ffebcd", |
|
"blue": "#0000ff", |
|
"blueviolet": "#8a2be2", |
|
"brown": "#a52a2a", |
|
"burlywood": "#deb887", |
|
"cadetblue": "#5f9ea0", |
|
"chartreuse": "#7fff00", |
|
"chocolate": "#d2691e", |
|
"coral": "#ff7f50", |
|
"cornflowerblue": "#6495ed", |
|
"cornsilk": "#fff8dc", |
|
"crimson": "#dc143c", |
|
"cyan": "#00ffff", |
|
"darkblue": "#00008b", |
|
"darkcyan": "#008b8b", |
|
"darkgoldenrod": "#b8860b", |
|
"darkgray": "#a9a9a9", |
|
"darkgrey": "#a9a9a9", |
|
"darkgreen": "#006400", |
|
"darkkhaki": "#bdb76b", |
|
"darkmagenta": "#8b008b", |
|
"darkolivegreen": "#556b2f", |
|
"darkorange": "#ff8c00", |
|
"darkorchid": "#9932cc", |
|
"darkred": "#8b0000", |
|
"darksalmon": "#e9967a", |
|
"darkseagreen": "#8fbc8f", |
|
"darkslateblue": "#483d8b", |
|
"darkslategray": "#2f4f4f", |
|
"darkslategrey": "#2f4f4f", |
|
"darkturquoise": "#00ced1", |
|
"darkviolet": "#9400d3", |
|
"deeppink": "#ff1493", |
|
"deepskyblue": "#00bfff", |
|
"dimgray": "#696969", |
|
"dimgrey": "#696969", |
|
"dodgerblue": "#1e90ff", |
|
"firebrick": "#b22222", |
|
"floralwhite": "#fffaf0", |
|
"forestgreen": "#228b22", |
|
"fuchsia": "#ff00ff", |
|
"gainsboro": "#dcdcdc", |
|
"ghostwhite": "#f8f8ff", |
|
"gold": "#ffd700", |
|
"goldenrod": "#daa520", |
|
"gray": "#808080", |
|
"grey": "#808080", |
|
"green": "#008000", |
|
"greenyellow": "#adff2f", |
|
"honeydew": "#f0fff0", |
|
"hotpink": "#ff69b4", |
|
"indianred": "#cd5c5c", |
|
"indigo": "#4b0082", |
|
"ivory": "#fffff0", |
|
"khaki": "#f0e68c", |
|
"lavender": "#e6e6fa", |
|
"lavenderblush": "#fff0f5", |
|
"lawngreen": "#7cfc00", |
|
"lemonchiffon": "#fffacd", |
|
"lightblue": "#add8e6", |
|
"lightcoral": "#f08080", |
|
"lightcyan": "#e0ffff", |
|
"lightgoldenrodyellow": "#fafad2", |
|
"lightgreen": "#90ee90", |
|
"lightgray": "#d3d3d3", |
|
"lightgrey": "#d3d3d3", |
|
"lightpink": "#ffb6c1", |
|
"lightsalmon": "#ffa07a", |
|
"lightseagreen": "#20b2aa", |
|
"lightskyblue": "#87cefa", |
|
"lightslategray": "#778899", |
|
"lightslategrey": "#778899", |
|
"lightsteelblue": "#b0c4de", |
|
"lightyellow": "#ffffe0", |
|
"lime": "#00ff00", |
|
"limegreen": "#32cd32", |
|
"linen": "#faf0e6", |
|
"magenta": "#ff00ff", |
|
"maroon": "#800000", |
|
"mediumaquamarine": "#66cdaa", |
|
"mediumblue": "#0000cd", |
|
"mediumorchid": "#ba55d3", |
|
"mediumpurple": "#9370db", |
|
"mediumseagreen": "#3cb371", |
|
"mediumslateblue": "#7b68ee", |
|
"mediumspringgreen": "#00fa9a", |
|
"mediumturquoise": "#48d1cc", |
|
"mediumvioletred": "#c71585", |
|
"midnightblue": "#191970", |
|
"mintcream": "#f5fffa", |
|
"mistyrose": "#ffe4e1", |
|
"moccasin": "#ffe4b5", |
|
"navajowhite": "#ffdead", |
|
"navy": "#000080", |
|
"oldlace": "#fdf5e6", |
|
"olive": "#808000", |
|
"olivedrab": "#6b8e23", |
|
"orange": "#ffa500", |
|
"orangered": "#ff4500", |
|
"orchid": "#da70d6", |
|
"palegoldenrod": "#eee8aa", |
|
"palegreen": "#98fb98", |
|
"paleturquoise": "#afeeee", |
|
"palevioletred": "#db7093", |
|
"papayawhip": "#ffefd5", |
|
"peachpuff": "#ffdab9", |
|
"peru": "#cd853f", |
|
"pink": "#ffc0cb", |
|
"plum": "#dda0dd", |
|
"powderblue": "#b0e0e6", |
|
"purple": "#800080", |
|
"rebeccapurple": "#663399", |
|
"red": "#ff0000", |
|
"rosybrown": "#bc8f8f", |
|
"royalblue": "#4169e1", |
|
"saddlebrown": "#8b4513", |
|
"salmon": "#fa8072", |
|
"sandybrown": "#f4a460", |
|
"seagreen": "#2e8b57", |
|
"seashell": "#fff5ee", |
|
"sienna": "#a0522d", |
|
"silver": "#c0c0c0", |
|
"skyblue": "#87ceeb", |
|
"slateblue": "#6a5acd", |
|
"slategray": "#708090", |
|
"slategrey": "#708090", |
|
"snow": "#fffafa", |
|
"springgreen": "#00ff7f", |
|
"steelblue": "#4682b4", |
|
"tan": "#d2b48c", |
|
"teal": "#008080", |
|
"thistle": "#d8bfd8", |
|
"tomato": "#ff6347", |
|
"turquoise": "#40e0d0", |
|
"violet": "#ee82ee", |
|
"wheat": "#f5deb3", |
|
"white": "#ffffff", |
|
"whitesmoke": "#f5f5f5", |
|
"yellow": "#ffff00", |
|
"yellowgreen": "#9acd32", |
|
} |
|
|