893 lines
28 KiB
Python
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)
|