msspec_python3/msspec/msspecgui/scenegraph2d/scenegraph/paint.py

893 lines
28 KiB
Python

# -*- coding: utf-8 -*-
"""
paint servers
"""
# imports ####################################################################
# from ..opengl import gl as _gl
# from ..opengl.utils import (get_opengl_version, set_uniform)
# from ..opengl.utils import (create_shader, create_program)
from ._common import _Element
from .transform import Translate, Matrix, shrink
# from .transform import Ortho, product
# shaders ####################################################################
_ATTRIB_LOCATIONS = {
b"vertex": 0,
}
_VERT_SHADER = """
#version %(GLSL_VERSION)s
#if __VERSION__ >= 150
#define attribute in
#define varying out
#endif
attribute vec2 vertex;
uniform vec3 color;
uniform float alpha;
uniform mat3 projection_transform;
uniform mat3 modelview_transform;
uniform mat3 paint_transform;
uniform mat3 mask_transform;
varying vec4 front_color;
varying vec2 paint_coord;
varying vec2 mask_coord;
void main() {
front_color = vec4(color, alpha);
vec3 pixel_position = modelview_transform * vec3(vertex, 1.);
paint_coord = (paint_transform * vec3(vertex, 1.)).xy;
mask_coord = (mask_transform * pixel_position).xy;
gl_Position = vec4((projection_transform * pixel_position).xy, 0., 1.);
}
"""
_MAIN_FRAG_SHADER = """
#version %(GLSL_VERSION)s
#if __VERSION__ >= 150
#define varying in
#define texture2D texture
#define gl_FragColor fragment_color
out vec4 fragment_color;
#endif
uniform bool masking;
uniform sampler2D mask;
const vec4 luminance = vec4(.2125, .7154, .0721, 0.);
varying vec4 front_color;
varying vec2 mask_coord;
vec4 color(); // filling color
vec4 frag_color() {
vec4 color = color();
if(masking) {
color.a *= dot(luminance, texture2D(mask, mask_coord));
}
return front_color * color;
}
void main() { gl_FragColor = frag_color(); }
"""
# filling fragment shaders ###################################################
_SOLID_FRAG_SHADER = """
#version %(GLSL_VERSION)s
vec4 color() {
return vec4(1., 1., 1., 1.);
}
"""
_TEXTURE_FRAG_SHADER = """
#version %(GLSL_VERSION)s
#if __VERSION__ >= 150
#define varying in
#define texture2D texture
#endif
uniform sampler2D texture;
varying vec2 paint_coord;
vec4 color() {
return texture2D(texture, paint_coord);
}
"""
MAX_STOPS = 21
_GRADIENT_FRAG_SHADER = """
#version %(GLSL_VERSION)s
#if __VERSION__ >= 150
#define varying in
#endif
const int N = %(MAX_STOPS)s;
uniform int n;
uniform float os[N];
uniform vec4 colors[N];
uniform int spread;
varying vec2 paint_coord;
float o(vec2 p); // offset at point p in the gradient
float s(float o) {
// offset according to spread method
float pad = o;
float repeat = fract(o);
float reflect = mod(o, 2.);
if(reflect > 1.)
reflect = 2.-reflect;
return vec3(pad, reflect, repeat)[spread];
}
vec4 gradient(float o) {
// color at offset o in the gradient
float oa, oi = os[0];
vec4 ca, ci = colors[0];
for(int i = 0; i < n; i++) {
oa = os[i]; ca = colors[i];
if(o <= oa) break;
oi = oa; ci = ca;
}
float ki = (oi == oa) ? 1. : (oa-o)/(oa-oi);
return ki*ci + (1.-ki)*ca;
}
vec4 color() {
float o = o(paint_coord);
float s = s(o);
return gradient(s);
}
"""
_LINEAR_OFFSET_FRAG_SHADER = """
#version %(GLSL_VERSION)s
uniform vec2 p1;
uniform vec2 p2;
float o(vec2 p) {
// offset at point p in the linear gradient
vec2 u = p - p1;
vec2 v = p2 - p1;
return dot(u, v)/dot(v, v);
}
"""
_RADIAL_OFFSET_FRAG_SHADER = """
#version %(GLSL_VERSION)s
uniform vec2 c;
uniform float r;
uniform vec2 f;
float o(vec2 p) {
// offset at point p in the radial gradient
vec2 u = p - f;
vec2 v = c - f;
float a2 = dot(v, v) - r*r;
float a1 = dot(u, v);
float a0 = dot(u, u);
if(a2 == 0.) {
return .5*a0/a1; // at first order sqrt(1+x) == 1+x/2
} else {
return (a1-sqrt(a1*a1-a2*a0))/a2;
}
}
"""
_PATTERN_FRAG_SHADER = """
#version %(GLSL_VERSION)s
#if __VERSION__ >= 150
#define varying in
#endif
uniform vec2 origin;
uniform vec2 period;
varying vec2 paint_coord;
vec4 color() {
vec2 uv = mod(paint_coord + origin, period);
if((uv.x-period.x/2.)*(uv.y-period.y/2.) < 0.) {
return vec4(.75, .75, .75, 1.);
} else {
return vec4(1., 1., 1., 1.);
}
}
"""
# _shaders = {
# "solid_color": [
# (_gl.VERTEX_SHADER, _VERT_SHADER),
# (_gl.FRAGMENT_SHADER, _SOLID_FRAG_SHADER),
# (_gl.FRAGMENT_SHADER, _MAIN_FRAG_SHADER),
# ],
# "texture": [
# (_gl.VERTEX_SHADER, _VERT_SHADER),
# (_gl.FRAGMENT_SHADER, _TEXTURE_FRAG_SHADER),
# (_gl.FRAGMENT_SHADER, _MAIN_FRAG_SHADER),
# ],
# "linear_gradient": [
# (_gl.VERTEX_SHADER, _VERT_SHADER),
# (_gl.FRAGMENT_SHADER, _GRADIENT_FRAG_SHADER),
# (_gl.FRAGMENT_SHADER, _LINEAR_OFFSET_FRAG_SHADER),
# (_gl.FRAGMENT_SHADER, _MAIN_FRAG_SHADER),
# ],
# "radial_gradient": [
# (_gl.VERTEX_SHADER, _VERT_SHADER),
# (_gl.FRAGMENT_SHADER, _GRADIENT_FRAG_SHADER),
# (_gl.FRAGMENT_SHADER, _RADIAL_OFFSET_FRAG_SHADER),
# (_gl.FRAGMENT_SHADER, _MAIN_FRAG_SHADER),
# ],
# "pattern": [
# (_gl.VERTEX_SHADER, _VERT_SHADER),
# (_gl.FRAGMENT_SHADER, _PATTERN_FRAG_SHADER),
# (_gl.FRAGMENT_SHADER, _MAIN_FRAG_SHADER),
# ]
# }
# programs ###################################################################
# _programs = {}
#
#
# def _program(name):
# global _programs
# try:
# program = _programs[name]
# except KeyError:
# shaders = list(create_shader(*shader, MAX_STOPS=MAX_STOPS) for shader in _shaders[name])
# program = _programs[name] = create_program(shaders, attrib_locations=_ATTRIB_LOCATIONS)
# return program
_current_kwargs = {}
_current_program = None
def _create(name, enable_sample_shading=True, **default_uniforms):
# def set_sample_shading():
# """en/dis-able the sample shading
#
# this function specializes itself on first call to avoid checking opengl
# version at each use.
# """
# nonlocal set_sample_shading
# if get_opengl_version() >= (4, 0):
# if enable_sample_shading:
# set_sample_shading = lambda: _gl.Enable(_gl.SAMPLE_SHADING)
# else:
# set_sample_shading = lambda: _gl.Disable(_gl.SAMPLE_SHADING)
# else:
# set_sample_shading = lambda: None
# set_sample_shading()
def _use(**kwargs):
raise NotImplementedError
# global _current_program, _current_uniforms
# program = _program(name)
# if _current_program != program:
# _gl.UseProgram(program)
# _current_program = program
# _current_uniforms = {}
# uniforms = dict(default_uniforms)
# uniforms.update(kwargs)
# uniforms["mask_transform"] = _MaskContext.transforms[-1].uniform()
# uniforms["masking"] = [len(_MaskContext.textures) > 1]
# for k in uniforms:
# v = uniforms[k]
# if v != _current_uniforms.get(k, None):
# set_uniform(program, k, v)
# _current_uniforms = uniforms
# set_sample_shading()
return _use
_use_solid_color = _create("solid_color", mask=[1])
_use_texture = _create("texture", texture=[0], mask=[1], enable_sample_shading=False)
_use_linear_gradient = _create("linear_gradient", mask=[1])
_use_radial_gradient = _create("radial_gradient", mask=[1])
_use_pattern = _create("pattern", mask=[1])
# painting ##################################################################
# def _stencil_op(n, op):
# _gl.StencilOp(_gl.KEEP, _gl.KEEP, op)
# _gl.DrawArrays(_gl.TRIANGLE_STRIP, 0, n)
#
# def _make_stencil(op):
# def _stencil(n):
# _stencil_op(n, op)
# return _stencil
#
# _stencil_one = _make_stencil(_gl.INCR)
# _stencil_evenodd = _make_stencil(_gl.INVERT)
# _stencil_replace = _make_stencil(_gl.REPLACE)
#
# def _stencil_nonzero(n):
# _gl.Enable(_gl.CULL_FACE)
# for cull, op in [(_gl.BACK, _gl.INCR_WRAP),
# (_gl.FRONT, _gl.DECR_WRAP)]:
# _gl.CullFace(cull)
# _stencil_op(n, op)
# _gl.Disable(_gl.CULL_FACE)
#
#
# def _make_paint(_stencil):
# def paint(color, alpha, data, transform, context, origin, bbox):
# paint_transform = product(*color.transform).inverse() * \
# color.units(origin, bbox)
# projection_transform = Ortho(*context.orthos[-1])
# color._use_program(color=[color.rgb], alpha=[float(alpha)],
# modelview_transform=transform.uniform(),
# paint_transform=paint_transform.uniform(),
# projection_transform=projection_transform.uniform())
# n, vbo_id = data
# _gl.BindBuffer(_gl.ARRAY_BUFFER, vbo_id)
# _gl.VertexAttribPointer(_ATTRIB_LOCATIONS[b"vertex"], 2, _gl.FLOAT,
# False, 0, None)
#
# for mask, func, stencil in [(_gl.FALSE, _gl.ALWAYS, _stencil),
# (_gl.TRUE, _gl.NOTEQUAL, _stencil_replace)]:
# _gl.ColorMask(mask, mask, mask, mask)
# _gl.StencilFunc(func, 0, -1)
# stencil(n)
# return paint
# paint base class ###########################################################
def _object_bbox(origin, bbox):
(x_min, y_min), (x_max, y_max) = bbox
try:
return shrink(x_min, y_min, x_max - x_min, y_max - y_min)
except ZeroDivisionError:
return Matrix()
def _user_space(origin, bbox):
return Translate(*origin)
_UNITS = {
"objectBoundingBox": _object_bbox,
"userSpaceOnUse": _user_space,
}
class _Paint(_Element):
_r, _g, _b = 1., 1., 1.
units = staticmethod(_UNITS["objectBoundingBox"])
transform = []
def get_rgb(self):
return self._r, self._g, self._b
def set_rgb(self, rgb):
self._r, self._g, self._b = rgb
rgb = property(get_rgb, set_rgb)
# paint_one = _make_paint(_stencil_one)
# paint_evenodd = _make_paint(_stencil_evenodd)
# paint_nonzero = _make_paint(_stencil_nonzero)
# solid color ################################################################
_BASE = 255.
def _float(i, base=_BASE):
"""convert byte color components to float"""
return float(i / base if isinstance(i, int) else i)
class Color(_Paint):
tag = "solidColor"
none = None
current = "currentColor"
_state_attributes = _Paint._state_attributes + [
"_r", "_g", "_b",
]
def __init__(self, r=0., g=None, b=None, name=""):
self._r = _float(r)
self._g = _float(g) if g is not None else self._r
self._b = _float(b) if b is not None else self._r
self._name = name
def _use_program(self, **kwargs):
_use_solid_color(**kwargs)
def _xml_attr(self, defs):
return self._name or \
"#%02x%02x%02x" % tuple(int(v * _BASE) for v in self.rgb)
# texture ####################################################################
class _Texture(_Paint):
def __init__(self, texture_id=0, color=Color(1., 1., 1.)):
self.texture_id = texture_id
self.get_rgb, self.set_rgb = color.get_rgb, color.set_rgb
def _use_program(self, **kwargs):
pass
# _gl.BindTexture(_gl.TEXTURE_2D, self.texture_id)
# _use_texture(**kwargs)
class _MaskContext:
textures = [0]
transforms = [Matrix()]
def __init__(self, origin, size, texture_id):
x, y = origin
width, height = size
self.transform = shrink(x, y, width, height)
self.texture_id = texture_id
def __enter__(self):
self.textures.append(self.texture_id)
self.transforms.append(self.transform)
# _gl.ActiveTexture(_gl.TEXTURE1)
# _gl.BindTexture(_gl.TEXTURE_2D, self.texture_id)
# _gl.ActiveTexture(_gl.TEXTURE0)
def __exit__(self, *args):
assert self.textures.pop() == self.texture_id
assert self.transforms.pop() == self.transform
# _gl.ActiveTexture(_gl.TEXTURE1)
# _gl.BindTexture(_gl.TEXTURE_2D, self.textures[-1])
# _gl.ActiveTexture(_gl.TEXTURE0)
# pserver ####################################################################
class _PaintServer(_Paint):
_state_attributes = _Paint._state_attributes + [
"parent",
]
def __init__(self, parent=None):
self.parent = parent
def __getattr__(self, attribute):
try:
return getattr(self.parent, attribute)
except AttributeError:
pass
if attribute in self._DEFAULTS:
return self._DEFAULTS[attribute]
return super(_PaintServer, self).__getattribute__(attribute)
@property
def id(self):
return "%X" % id(self)
@property
def href(self):
return self.parent
@property
def attributes(self):
attributes = ["id"]
attributes += list(k for k in self._DEFAULTS if k in dir(self))
if self.parent:
attributes += ["href"]
return attributes
# gradients ##################################################################
_SPREADS = {
"pad": 0,
"reflect": 1,
"repeat": 2,
}
def _stop(o, c, a=1.):
"""add optional alpha to gradient stop definitions"""
if c is Color.none:
c = Color.black
a = 0.
elif c is Color.current: # TODO: should be fixed
c = Color.black
a = 0.
return (float(o), (c._r, c._g, c._b, a))
class _Gradient(_PaintServer):
_DEFAULTS = {
"stops": [(0., Color.none)],
"gradientTransform": [],
"gradientUnits": "objectBoundingBox",
"spreadMethod": "pad",
}
_state_attributes = _PaintServer._state_attributes + list(_DEFAULTS)
def __init__(self, parent=None, stops=None, spreadMethod=None,
gradientUnits=None, gradientTransform=None):
super(_Gradient, self).__init__(parent)
if stops is not None:
self.stops = stops
if spreadMethod is not None:
self.spreadMethod = spreadMethod
if gradientUnits is not None:
self.gradientUnits = gradientUnits
if gradientTransform is not None:
self.gradientTransform = gradientTransform
@property
def units(self):
return _UNITS[self.gradientUnits]
@property
def transform(self):
return self.gradientTransform
def _use_gradient(self, n, os, colors, spread, **kwargs):
raise NotImplementedError
def _use_program(self, **kwargs):
n = len(self.stops)
assert n <= MAX_STOPS, "too much stops in gradient"
os, colors = zip(*(_stop(*stop) for stop in self.stops))
spread = _SPREADS[self.spreadMethod]
self._use_gradient(n, os, colors, spread, **kwargs)
@property
def attributes(self):
attributes = super(_Gradient, self).attributes
if "stops" in attributes:
attributes.remove("stops")
return attributes
def _xml_content(self, defs):
if "stops" not in dir(self):
return ""
stops = []
def _stop(offset, color, opacity=None):
if color is Color.none:
color = "none"
elif color == Color.current:
pass
else:
color = color._xml_attr(defs)
return offset, color, opacity
for stop in self.stops:
offset, color, opacity = _stop(*stop)
if opacity is None:
stop = "<stop offset='%s' stop-color='%s' />" % (offset, color)
else:
stop = "<stop offset='%s' stop-color='%s' stop-opacity='%s' />" % (offset, color, opacity)
stops.append(stop)
return "\n".join(stops)
class LinearGradient(_Gradient):
tag = "linearGradient"
_DEFAULTS = {
"x1": 0.,
"y1": 0.,
"x2": 1.,
"y2": 0.,
}
_state_attributes = _Gradient._state_attributes + list(_DEFAULTS)
_DEFAULTS.update(_Gradient._DEFAULTS)
def __init__(self, parent=None, stops=None, spreadMethod=None,
gradientUnits=None, gradientTransform=None,
x1=None, y1=None, x2=None, y2=None):
super(LinearGradient, self).__init__(parent, stops, spreadMethod,
gradientUnits, gradientTransform)
if x1 is not None:
self.x1 = x1
if y1 is not None:
self.y1 = y1
if x2 is not None:
self.x2 = x2
if y2 is not None:
self.y2 = y2
def _use_gradient(self, n, os, colors, spread, **kwargs):
_use_linear_gradient(p1=[(float(self.x1), float(self.y1))],
p2=[(float(self.x2), float(self.y2))],
n=[n], os=list(os), colors=list(colors),
spread=[spread], **kwargs)
class RadialGradient(_Gradient):
tag = "radialGradient"
_DEFAULTS = {
"cx": .5,
"cy": .5,
"r": .5,
"fx": None,
"fy": None,
}
_state_attributes = _Gradient._state_attributes + list(_DEFAULTS)
_DEFAULTS.update(_Gradient._DEFAULTS)
def __init__(self, parent=None, stops=None, spreadMethod=None,
gradientUnits=None, gradientTransform=None,
cx=None, cy=None, r=None, fx=None, fy=None):
super(RadialGradient, self).__init__(parent, stops, spreadMethod,
gradientUnits, gradientTransform)
if cx is not None:
self.cx = cx
if cy is not None:
self.cy = cy
if r is not None:
self.r = r
if fx is not None:
self.fx = fx
if fy is not None:
self.fy = fy
def _use_gradient(self, n, os, colors, spread, **kwargs):
fx, fy = self.fx, self.fy
if fx is None:
fx = self.cx
if fy is None:
fy = self.cy
_use_radial_gradient(c=[(float(self.cx), float(self.cy))],
r=[float(self.r)],
f=[(float(fx), float(fy))],
n=[n], os=list(os), colors=list(colors),
spread=[spread], **kwargs)
# pattern ####################################################################
class Pattern(_PaintServer):
tag = "pattern"
_r, _g, _b = 1., 1., 1.
_DEFAULTS = {
"patternUnits": "objectBoundingBox",
"patternContentUnits": "userSpaceOnUse",
"patternTransform": [],
"x": 0.,
"y": 0.,
"width": 0.,
"height": 0.,
"viewBox": None,
}
_state_attributes = _Paint._state_attributes + list(_DEFAULTS) + [
"pattern",
]
_DEFAULTS.update(_Gradient._DEFAULTS)
def __init__(self, parent=None, pattern=None,
patternUnits=None, patternContentUnits=None,
patternTransform=None,
x=None, y=None, width=None, height=None,
viewBox=None,
**kwargs):
super(Pattern, self).__init__(parent)
self.pattern = pattern
if patternUnits is not None:
self.patternUnits = patternUnits
if patternContentUnits is not None:
self.patternContentUnits = patternContentUnits
if patternTransform is not None:
self.patternTransform = patternTransform
if x is not None:
self.x = x
if y is not None:
self.y = y
if width is not None:
self.width = width
if height is not None:
self.height = height
self.viewBox = viewBox
@property
def units(self):
return _UNITS[self.patternUnits]
@property
def transform(self):
return self.patternTransform
def _use_program(self, **kwargs):
_use_pattern(origin=[(float(self.x), float(self.y))],
period=[(float(self.width), float(self.height))],
**kwargs)
def _xml_content(self, defs):
return self.pattern._xml_content(defs)
# colors by name #############################################################
# see <http://www.w3.org/TR/SVG/types.html#ColorKeywords>
_color_keywords = {
"aliceblue": (240, 248, 255),
"antiquewhite": (250, 235, 215),
"aqua": ( 0, 255, 255),
"aquamarine": (127, 255, 212),
"azure": (240, 255, 255),
"beige": (245, 245, 220),
"bisque": (255, 228, 196),
"black": ( 0, 0, 0),
"blanchedalmond": (255, 235, 205),
"blue": ( 0, 0, 255),
"blueviolet": (138, 43, 226),
"brown": (165, 42, 42),
"burlywood": (222, 184, 135),
"cadetblue": ( 95, 158, 160),
"chartreuse": (127, 255, 0),
"chocolate": (210, 105, 30),
"coral": (255, 127, 80),
"cornflowerblue": (100, 149, 237),
"cornsilk": (255, 248, 220),
"crimson": (220, 20, 60),
"cyan": ( 0, 255, 255),
"darkblue": ( 0, 0, 139),
"darkcyan": ( 0, 139, 139),
"darkgoldenrod": (184, 134, 11),
"darkgray": (169, 169, 169),
"darkgreen": ( 0, 100, 0),
"darkgrey": (169, 169, 169),
"darkkhaki": (189, 183, 107),
"darkmagenta": (139, 0, 139),
"darkolivegreen": ( 85, 107, 47),
"darkorange": (255, 140, 0),
"darkorchid": (153, 50, 204),
"darkred": (139, 0, 0),
"darksalmon": (233, 150, 122),
"darkseagreen": (143, 188, 143),
"darkslateblue": ( 72, 61, 139),
"darkslategray": ( 47, 79, 79),
"darkslategrey": ( 47, 79, 79),
"darkturquoise": ( 0, 206, 209),
"darkviolet": (148, 0, 211),
"deeppink": (255, 20, 147),
"deepskyblue": ( 0, 191, 255),
"dimgray": (105, 105, 105),
"dimgrey": (105, 105, 105),
"dodgerblue": ( 30, 144, 255),
"firebrick": (178, 34, 34),
"floralwhite": (255, 250, 240),
"forestgreen": ( 34, 139, 34),
"fuchsia": (255, 0, 255),
"gainsboro": (220, 220, 220),
"ghostwhite": (248, 248, 255),
"gold": (255, 215, 0),
"goldenrod": (218, 165, 32),
"gray": (128, 128, 128),
"grey": (128, 128, 128),
"green": ( 0, 128, 0),
"greenyellow": (173, 255, 47),
"honeydew": (240, 255, 240),
"hotpink": (255, 105, 180),
"indianred": (205, 92, 92),
"indigo": ( 75, 0, 130),
"ivory": (255, 255, 240),
"khaki": (240, 230, 140),
"lavender": (230, 230, 250),
"lavenderblush": (255, 240, 245),
"lawngreen": (124, 252, 0),
"lemonchiffon": (255, 250, 205),
"lightblue": (173, 216, 230),
"lightcoral": (240, 128, 128),
"lightcyan": (224, 255, 255),
"lightgoldenrodyellow": (250, 250, 210),
"lightgray": (211, 211, 211),
"lightgreen": (144, 238, 144),
"lightgrey": (211, 211, 211),
"lightpink": (255, 182, 193),
"lightsalmon": (255, 160, 122),
"lightseagreen": ( 32, 178, 170),
"lightskyblue": (135, 206, 250),
"lightslategray": (119, 136, 153),
"lightslategrey": (119, 136, 153),
"lightsteelblue": (176, 196, 222),
"lightyellow": (255, 255, 224),
"lime": ( 0, 255, 0),
"limegreen": ( 50, 205, 50),
"linen": (250, 240, 230),
"magenta": (255, 0, 255),
"maroon": (128, 0, 0),
"mediumaquamarine": (102, 205, 170),
"mediumblue": ( 0, 0, 205),
"mediumorchid": (186, 85, 211),
"mediumpurple": (147, 112, 219),
"mediumseagreen": ( 60, 179, 113),
"mediumslateblue": (123, 104, 238),
"mediumspringgreen": ( 0, 250, 154),
"mediumturquoise": ( 72, 209, 204),
"mediumvioletred": (199, 21, 133),
"midnightblue": ( 25, 25, 112),
"mintcream": (245, 255, 250),
"mistyrose": (255, 228, 225),
"moccasin": (255, 228, 181),
"navajowhite": (255, 222, 173),
"navy": ( 0, 0, 128),
"oldlace": (253, 245, 230),
"olive": (128, 128, 0),
"olivedrab": (107, 142, 35),
"orange": (255, 165, 0),
"orangered": (255, 69, 0),
"orchid": (218, 112, 214),
"palegoldenrod": (238, 232, 170),
"palegreen": (152, 251, 152),
"paleturquoise": (175, 238, 238),
"palevioletred": (219, 112, 147),
"papayawhip": (255, 239, 213),
"peachpuff": (255, 218, 185),
"peru": (205, 133, 63),
"pink": (255, 192, 203),
"plum": (221, 160, 221),
"powderblue": (176, 224, 230),
"purple": (128, 0, 128),
"red": (255, 0, 0),
"rosybrown": (188, 143, 143),
"royalblue": ( 65, 105, 225),
"saddlebrown": (139, 69, 19),
"salmon": (250, 128, 114),
"sandybrown": (244, 164, 96),
"seagreen": ( 46, 139, 87),
"seashell": (255, 245, 238),
"sienna": (160, 82, 45),
"silver": (192, 192, 192),
"skyblue": (135, 206, 235),
"slateblue": (106, 90, 205),
"slategray": (112, 128, 144),
"slategrey": (112, 128, 144),
"snow": (255, 250, 250),
"springgreen": ( 0, 255, 127),
"steelblue": ( 70, 130, 180),
"tan": (210, 180, 140),
"teal": ( 0, 128, 128),
"thistle": (216, 191, 216),
"tomato": (255, 99, 71),
"turquoise": ( 64, 224, 208),
"violet": (238, 130, 238),
"wheat": (245, 222, 179),
"white": (255, 255, 255),
"whitesmoke": (245, 245, 245),
"yellow": (255, 255, 0),
"yellowgreen": (154, 205, 50),
}
for name in _color_keywords:
r, g, b = _color_keywords[name]
color = Color(r=r, g=g, b=b, name=name)
setattr(Color, name, color)