svg: added tiny doc, back to the original gradient version (this one works) + some docfixes

This commit is contained in:
Mathieu Virbel 2014-09-20 18:28:08 +02:00
parent 95dd9cca0e
commit d15b999731
5 changed files with 75 additions and 155 deletions

View File

@ -10,11 +10,10 @@ from kivy.uix.floatlayout import FloatLayout
class SvgWidget(Scatter):
def __init__(self, filename):
super(SvgWidget, self).__init__()
def __init__(self, filename, **kwargs):
super(SvgWidget, self).__init__(**kwargs)
with self.canvas:
svg = Svg(filename)
self.size = svg.width, svg.height
class SvgApp(App):
@ -27,7 +26,7 @@ class SvgApp(App):
filenames = glob(join(dirname(__file__), '*.svg'))
for filename in filenames:
svg = SvgWidget(filename)
svg = SvgWidget(filename, size_hint=(None, None))
self.root.add_widget(svg)
svg.scale = 5.
svg.center = Window.center

View File

@ -75,6 +75,7 @@ r('SmoothLine', module='kivy.graphics.vertex_instructions')
r('Point', module='kivy.graphics.vertex_instructions')
r('Bezier', module='kivy.graphics.vertex_instructions')
r('Mesh', module='kivy.graphics.vertex_instructions')
r('Svg', module='kivy.graphics.svg')
r('MotionEventFactory', module='kivy.input.factory')
r('MotionEventProvider', module='kivy.input.provider')
r('Shape', module='kivy.input.shape')

View File

@ -331,8 +331,6 @@ cdef class BindTexture(ContextInstruction):
texture = get_default_texture()
if self._texture is texture:
return
#Logger.trace('BindTexture: setting texture %r (previous is %r)' % (
# texture, self._texture))
self._texture = texture
self.flag_update()
@ -351,7 +349,6 @@ cdef class BindTexture(ContextInstruction):
def __get__(self):
return self._source
def __set__(self, filename):
#Logger.trace('BindTexture: setting source: <%s>' % filename)
self._source = resource_find(filename)
if self._source:
tex = Cache.get('kv.texture', filename)

View File

@ -2,9 +2,26 @@
SVG
===
Load an SVG as a graphics instruction
.. versionadded:: 1.8.1
.. warning::
This is highly experimental and subject to change. Don't use it in
production.
Load an SVG as a graphics instruction::
from kivy.graphics.svg import Svg
with widget.canvas:
svg = Svg("image.svg")
There is no widget that can display Svg directly, you have to make your own for
now. Check the `examples/svg` for more informations.
'''
__all__ = ("Svg", )
include "common.pxi"
import re
@ -308,13 +325,17 @@ cdef class Matrix(object):
if isinstance(string, str):
if string.startswith('matrix('):
i = 0
for f in parse_list(string[7:-1]):
self.mat[i] = f
for sf in parse_list(string[7:-1]):
self.mat[i] = float(sf)
i += 1
elif string.startswith('translate('):
self.mat[4], self.mat[5] = parse_list(string[10:-1])
a, b = parse_list(string[10:-1])
self.mat[4] = float(a)
self.mat[5] = float(b)
elif string.startswith('scale('):
self.mat[0], self.mat[3] = parse_list(string[6:-1])
a, b = parse_list(string[6:-1])
self.mat[0] = float(a)
self.mat[3] = float(b)
elif string is not None:
i = 0
for f in string:
@ -371,11 +392,7 @@ class GradientContainer(dict):
callback(val)
cdef class Gradient(object):
cdef dict stops
cdef object element
cdef Matrix inv_transform
class Gradient(object):
def __init__(self, element, svg):
self.element = element
self.stops = {}
@ -407,11 +424,10 @@ cdef class Gradient(object):
self.get_params(parent)
def interp(self, float x, float y):
cdef float t, u, v, alpha
cdef int n
cdef Matrix m = self.inv_transform
if not self.stops:
return [255, 0, 255, 255]
self.inv_transform.transform(x, y, &x, &y)
m.transform(x, y, &x, &y)
t = self.grad_value(x, y)
if t < self.stops[0][0]:
return self.stops[0][1]
@ -438,26 +454,24 @@ cdef class Gradient(object):
def tardy_gradient_parsed(self, gradient):
self.get_params(gradient)
cdef float grad_value(self, float x, float y):
return 0.
cdef class LinearGradient(Gradient):
cdef float x1, x2, y1, y2
class LinearGradient(Gradient):
params = ['x1', 'x2', 'y1', 'y2', 'stops']
cdef float grad_value(self, float x, float y):
return ((x - self.x1)*(self.x2 - self.x1) + (x - self.y1)*(self.y2 - self.y1)) / ((self.x1 - self.x2)**2 + (self.y1 - self.y2)**2)
def grad_value(self, x, y):
return ((x - self.x1)*(self.x2 - self.x1) + (y - self.y1)*(self.y2 - self.y1)) / ((self.x1 - self.x2)**2 + (self.y1 - self.y2)**2)
cdef class RadialGradient(Gradient):
cdef float cx, cy, r
class RadialGradient(Gradient):
params = ['cx', 'cy', 'r', 'stops']
cdef float grad_value(self, float x, float y):
return sqrt((x - self.cx) ** 2 + (x - self.cy) ** 2)/self.r
def grad_value(self, x, y):
return sqrt((x - self.cx) ** 2 + (y - self.cy) ** 2)/self.r
cdef class Svg(RenderContext):
"""Svg class. See module for more informations about the usage.
"""
cdef:
public double width
@ -484,21 +498,20 @@ cdef class Svg(RenderContext):
def __init__(self, filename, anchor_x=0, anchor_y=0,
bezier_points=BEZIER_POINTS, circle_points=CIRCLE_POINTS):
'''Creates an SVG object from a .svg or .svgz file.
'''
Creates an SVG object from a .svg or .svgz file.
`filename`: str
The name of the file to be loaded.
`anchor_x`: float
The horizontal anchor position for scaling and rotations. Defaults to 0. The symbolic
values 'left', 'center' and 'right' are also accepted.
`anchor_y`: float
The vertical anchor position for scaling and rotations. Defaults to 0. The symbolic
values 'bottom', 'center' and 'top' are also accepted.
`bezier_points`: int
The number of line segments into which to subdivide Bezier splines. Defaults to 10.
`circle_points`: int
The number of line segments into which to subdivide circular and elliptic arcs.
Defaults to 10.
:param str filename: The name of the file to be loaded.
:param float anchor_x: The horizontal anchor position for scaling and
rotations. Defaults to 0. The symbolic values 'left', 'center' and
'right' are also accepted.
:param float anchor_y: The vertical anchor position for scaling and
rotations. Defaults to 0. The symbolic values 'bottom', 'center' and
'top' are also accepted.
:param int bezier_points: The number of line segments into which to
subdivide Bezier splines. Defaults to 10.
:param int circle_points: The number of line segments into which to
subdivide circular and elliptic arcs. Defaults to 10.
'''
super(Svg, self).__init__(fs=SVG_FS, vs=SVG_VS,
@ -521,6 +534,10 @@ cdef class Svg(RenderContext):
self.filename = filename
property anchor_x:
'''
Horizontal anchor position for scaling and rotations. Defaults to 0. The
symbolic values 'left', 'center' and 'right' are also accepted.
'''
def __set__(self, anchor_x):
self._anchor_x = anchor_x
@ -538,6 +555,10 @@ cdef class Svg(RenderContext):
property anchor_y:
'''
Vertical anchor position for scaling and rotations. Defaults to 0. The
symbolic values 'bottom', 'center' and 'top' are also accepted.
'''
def __set__(self, anchor_y):
self._anchor_y = anchor_y
@ -555,6 +576,10 @@ cdef class Svg(RenderContext):
property filename:
'''Filename to load.
The parsing and rendering is done as soon as you set the filename.
'''
def __set__(self, filename):
print 'loading', filename
# check gzip
@ -581,7 +606,7 @@ cdef class Svg(RenderContext):
print "{}: Parsed in {:.2f}s, rendered in {:.2f}s".format(
filename, end1 - start, end2 - end1)
def parse_tree(self, tree):
cdef parse_tree(self, tree):
root = tree._root
self.paths = []
self.width = parse_float(root.get('width'))
@ -598,7 +623,7 @@ cdef class Svg(RenderContext):
for e in root.getchildren():
self.parse_element(e)
def parse_element(self, e):
cdef parse_element(self, e):
self.fill = parse_color(e.get('fill'))
self.stroke = parse_color(e.get('stroke'))
oldopacity = self.opacity
@ -711,7 +736,7 @@ cdef class Svg(RenderContext):
self.transform = oldtransform
self.opacity = oldopacity
def parse_path(self, pathdef):
cdef parse_path(self, pathdef):
# In the SVG specs, initial movetos are absolute, even if
# specified as 'm'. This is the default behavior here as well.
# But if you pass in a current_pos variable, the initial moveto
@ -901,7 +926,7 @@ cdef class Svg(RenderContext):
self.loop.append(self.x)
self.loop.append(self.y)
def arc_to(self, float rx, float ry, float phi, float large_arc,
cdef arc_to(self, float rx, float ry, float phi, float large_arc,
float sweep, float x, float y):
# This function is made out of magical fairy dust
# http://www.w3.org/TR/2003/REC-SVG11-20030114/implnote.html#ArcImplementationNotes
@ -951,7 +976,7 @@ cdef class Svg(RenderContext):
float x, float y):
cdef int bp_count = self.bezier_points + 1
cdef int i, count, ilast
cdef float t, t0, t1, t2, t3, px, py
cdef float t, t0, t1, t2, t3, px = 0, py = 0
cdef list bc
cdef array.array loop
cdef float* f_loop
@ -1029,11 +1054,11 @@ cdef class Svg(RenderContext):
vindex = 0
if isinstance(fill, str):
g = self.gradients[fill]
gradient = self.gradients[fill]
for index in range(count):
x = path[index * 2]
y = path[index * 2 + 1]
r, g, b, a = g.interp(x, y)
r, g, b, a = gradient.interp(x, y)
transform.transform(x, y, &x, &y)
vertices[vindex] = x
vertices[vindex + 1] = y
@ -1064,109 +1089,6 @@ cdef class Svg(RenderContext):
mesh.build_triangle_fan(vertices, vindex, count)
free(vertices)
"""
# Tentative to use smooth line, doesn't work.
cdef void push_line_mesh(self, float[:] path, fill, Matrix transform):
cdef int index, vindex
cdef float ax, ay, bx, by, r, g, b, a
cdef int count = len(path) / 2
cdef list indices = []
cdef list vertices = []
vindex = 0
# build internal smooth line
cdef float line_width = 0
cdef float line_owidth = 1.2
width = max(0, (line_width - 1.))
owidth = width + line_owidth
vertices = []
indices = []
if not isinstance(fill, str):
r, g, b, a = fill
for index in range(0, len(path) - 2, 2):
ax = path[index]
ay = path[index + 1]
bx = path[index + 2]
by = path[index + 3]
transform.transform(ax, ay, &ax, &ay)
transform.transform(bx, by, &bx, &by)
rx = bx - ax
ry = by - ay
angle = atan2(ry, rx)
a1 = angle - PI2
a2 = angle + PI2
cos1 = cos(a1) * width
sin1 = sin(a1) * width
cos2 = cos(a2) * width
sin2 = sin(a2) * width
ocos1 = cos(a1) * owidth
osin1 = sin(a1) * owidth
ocos2 = cos(a2) * owidth
osin2 = sin(a2) * owidth
x1 = ax + cos1
y1 = ay + sin1
x4 = ax + cos2
y4 = ay + sin2
x2 = bx + cos1
y2 = by + sin1
x3 = bx + cos2
y3 = by + sin2
ox1 = ax + ocos1
oy1 = ay + osin1
ox4 = ax + ocos2
oy4 = ay + osin2
ox2 = bx + ocos1
oy2 = by + osin1
ox3 = bx + ocos2
oy3 = by + osin2
if isinstance(fill, str):
g = self.gradients[fill]
r, g, b, a = g.interp(ax, ay)
vindex = len(vertices) / 8
vertices += [
# x, y, u, v, r, g, b, a
x1, y1, 0, 0, r, g, b, a,
x2, y2, 0, 0, r, g, b, a,
x3, y3, 0, 0, r, g, b, a,
x4, y4, 0, 0, r, g, b, a,
ox1, oy1, 1, 1, r, g, b, a,
ox2, oy2, 1, 1, r, g, b, a,
ox3, oy3, 1, 1, r, g, b, a,
ox4, oy4, 1, 1, r, g, b, a,
]
indices += [vindex + i for i in (
0, 4, 5,
0, 5, 1,
3, 0, 1,
3, 1, 2,
7, 3, 2,
7, 2, 6
)]
if len(indices) > 65000:
self._push_line_mesh(vertices, indices)
vertices = []
indices = []
if indices:
self._push_line_mesh(vertices, indices)
cdef void _push_line_mesh(self, vertices, indices):
Mesh(fmt=VERTEX_FORMAT, mode='triangles',
vertices=vertices, indices=indices, texture=self.line_texture)
"""
cdef void render(self):
for path, stroke, tris, fill, transform in self.paths:
if tris:

View File

@ -193,7 +193,8 @@ cdef class Tesselator:
@property
def vertices(self):
"""Iterate through the result of the :meth:`tesselate` in order to give
"""
Iterate through the result of the :meth:`tesselate` in order to give
only a list of `[x, y, x2, y2, ...]` polygons.
"""
return self.iterate_vertices(0)