1
#####################################################################
2
# -*- coding: iso-8859-1 -*- #
5
# Copyright (C) 2006 Sami Kyļæ½stilļæ½ #
7
# 2008 evilynux <evilynux@gmail.com> #
9
# This program is free software; you can redistribute it and/or #
10
# modify it under the terms of the GNU General Public License #
11
# as published by the Free Software Foundation; either version 2 #
12
# of the License, or (at your option) any later version. #
14
# This program is distributed in the hope that it will be useful, #
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
17
# GNU General Public License for more details. #
19
# You should have received a copy of the GNU General Public License #
20
# along with this program; if not, write to the Free Software #
21
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, #
22
# MA 02110-1301, USA. #
23
#####################################################################
28
from OpenGL.GL import *
29
from numpy import reshape, dot, transpose, identity, zeros, array, float32
30
from math import sin, cos
34
from Texture import Texture, TextureException
37
import DummyAmanith as amanith
40
# Add support for 'foo in attributes' syntax
41
if not hasattr(sax.xmlreader.AttributesImpl, '__contains__'):
42
sax.xmlreader.AttributesImpl.__contains__ = sax.xmlreader.AttributesImpl.has_key
45
# Bugs and limitations:
47
# - only the translate() and matrix() transforms are supported
48
# - only paths are supported
49
# - only constant color, linear gradient and radial gradient fill supported
52
Config.define("opengl", "svgshaders", bool, False, text = "Use OpenGL SVG shaders", options = {False: "No", True: "Yes"})
55
def __init__(self, gradientDesc, transform):
56
self.gradientDesc = gradientDesc
57
self.transform = transform
59
def applyTransform(self, transform):
60
m = dot(transform.matrix, self.transform.matrix)
61
self.gradientDesc.SetMatrix(transform.getGMatrix(m))
64
def __init__(self, geometry):
65
self.kernel = amanith.GKernel()
66
self.geometry = geometry
67
self.drawBoard = amanith.GOpenGLBoard(geometry[0], geometry[0] + geometry[2],
68
geometry[1], geometry[1] + geometry[3])
69
self.drawBoard.SetShadersEnabled(Config.get("opengl", "svgshaders"))
70
self.transform = SvgTransform()
71
self.setGeometry(geometry)
72
self.setProjection(geometry)
74
# eat any possible OpenGL errors -- we can't handle them anyway
76
glMatrixMode(GL_MODELVIEW)
78
Log.warn("SVG renderer initialization failed; expect corrupted graphics. " +
79
"To fix this, upgrade your OpenGL drivers and set your display " +
80
"to 32 bit color precision.")
82
def setGeometry(self, geometry = None):
83
self.drawBoard.SetViewport(geometry[0], geometry[1],
84
geometry[2], geometry[3])
85
self.transform.reset()
86
self.transform.scale(geometry[2] / 640.0, geometry[3] / 480.0)
88
def setProjection(self, geometry = None):
89
geometry = geometry or self.geometry
90
self.drawBoard.SetProjection(geometry[0], geometry[0] + geometry[2],
91
geometry[1], geometry[1] + geometry[3])
92
self.geometry = geometry
94
def clear(self, r = 0, g = 0, b = 0, a = 0):
95
self.drawBoard.Clear(r, g, b, a)
98
def __init__(self, baseStyle = None):
99
self.strokeColor = None
100
self.strokeWidth = None
101
self.fillColor = None
102
self.strokeLineJoin = None
103
self.strokeOpacity = None
104
self.fillOpacity = None
107
self.__dict__.update(baseStyle.__dict__)
109
def parseStyle(self, style):
111
for m in re.finditer(r"(.+?):\s*(.+?)(;|$)\s*", style):
112
s[m.group(1)] = m.group(2)
115
def parseColor(self, color, defs = None):
116
if color.lower() == "none":
120
return SvgColors.colors[color.lower()]
127
return (int(color[0], 16) / 15.0, int(color[1], 16) / 15.0, int(color[2], 16) / 15.0, 1.0)
128
return (int(color[0:2], 16) / 255.0, int(color[2:4], 16) / 255.0, int(color[4:6], 16) / 255.0, 1.0)
131
Log.warn("No patterns or gradients defined.")
133
m = re.match("url\(#(.+)\)", color)
137
Log.warn("Pattern/gradient %s has not been defined." % id)
140
def __cmp__(self, s):
142
for k, v in self.__dict__.items():
143
if v != getattr(s, k):
149
return "<SvgRenderStyle " + " ".join(["%s:%s" % (k, v) for k, v in self.__dict__.items()]) + ">"
151
def applyAttributes(self, attrs, defs):
152
style = attrs.get("style")
154
style = self.parseStyle(style)
156
if "stroke" in style:
157
self.strokeColor = self.parseColor(style["stroke"], defs)
159
self.fillColor = self.parseColor(style["fill"], defs)
160
if "stroke-width" in style:
161
self.strokeWidth = float(style["stroke-width"].replace("px", ""))
162
if "stroke-opacity" in style:
163
self.strokeOpacity = float(style["stroke-opacity"])
164
if "fill-opacity" in style:
165
self.fillOpacity = float(style["fill-opacity"])
166
if "stroke-linejoin" in style:
167
j = style["stroke-linejoin"].lower()
169
self.strokeLineJoin = amanith.G_MITER_JOIN
171
self.strokeLineJoin = amanith.G_ROUND_JOIN
173
self.strokeLineJoin = amanith.G_BEVEL_JOIN
175
def apply(self, drawBoard, transform):
176
if self.strokeColor is not None:
177
if isinstance(self.strokeColor, SvgGradient):
178
self.strokeColor.applyTransform(transform)
179
drawBoard.SetStrokePaintType(amanith.G_GRADIENT_PAINT_TYPE)
180
drawBoard.SetStrokeGradient(self.strokeColor.gradientDesc)
182
drawBoard.SetStrokePaintType(amanith.G_COLOR_PAINT_TYPE)
183
drawBoard.SetStrokeColor(*self.strokeColor)
184
drawBoard.SetStrokeEnabled(True)
186
drawBoard.SetStrokeEnabled(False)
188
if self.fillColor is not None:
189
if isinstance(self.fillColor, SvgGradient):
190
self.fillColor.applyTransform(transform)
191
drawBoard.SetFillPaintType(amanith.G_GRADIENT_PAINT_TYPE)
192
drawBoard.SetFillGradient(self.fillColor.gradientDesc)
194
drawBoard.SetFillPaintType(amanith.G_COLOR_PAINT_TYPE)
195
drawBoard.SetFillColor(*self.fillColor)
196
drawBoard.SetFillEnabled(True)
198
drawBoard.SetFillEnabled(False)
200
if self.strokeWidth is not None:
201
drawBoard.SetStrokeWidth(self.strokeWidth)
203
if self.strokeOpacity is not None:
204
drawBoard.SetStrokeOpacity(self.strokeOpacity)
206
if self.fillOpacity is not None:
207
drawBoard.SetFillOpacity(self.fillOpacity)
209
if self.strokeLineJoin is not None:
210
drawBoard.SetStrokeJoinStyle(self.strokeLineJoin)
213
def __init__(self, baseTransform = None):
214
self._gmatrix = amanith.GMatrix33()
218
self.matrix = baseTransform.matrix.copy()
220
def applyAttributes(self, attrs, key = "transform"):
221
transform = attrs.get(key)
223
m = re.match(r"translate\(\s*(.+?)\s*,(.+?)\s*\)", transform)
225
dx, dy = [float(c) for c in m.groups()]
226
self.matrix[0, 2] += dx
227
self.matrix[1, 2] += dy
228
m = re.match(r"matrix\(\s*" + "\s*,\s*".join(["(.+?)"] * 6) + r"\s*\)", transform)
230
e = [float(c) for c in m.groups()]
231
e = [e[0], e[2], e[4], e[1], e[3], e[5], 0, 0, 1]
232
m = reshape(e, (3, 3))
233
self.matrix = dot(self.matrix, m)
235
def transform(self, transform):
236
self.matrix = dot(self.matrix, transform.matrix)
239
self.matrix = identity(3, dtype = float32)
241
def translate(self, dx, dy):
247
def rotate(self, angle):
248
m = identity(3, dtype = float32)
255
self.matrix = dot(self.matrix, m)
257
def scale(self, sx, sy):
258
m = identity(3, dtype = float32)
261
self.matrix = dot(self.matrix, m)
264
# Interpret the 2D matrix as 3D
266
m = [m[0, 0], m[1, 0], 0.0, 0.0,
267
m[0, 1], m[1, 1], 0.0, 0.0,
269
m[0, 2], m[1, 2], 0.0, 1.0]
272
def getGMatrix(self, m):
274
m[0, 0], m[0, 1], m[0, 2], \
275
m[1, 0], m[1, 1], m[1, 2], \
276
m[2, 0], m[2, 1], m[2, 2])
279
def apply(self, drawBoard):
280
drawBoard.SetModelViewMatrix(self.getGMatrix(self.matrix))
282
class SvgHandler(sax.ContentHandler):
283
def __init__(self, drawBoard, cache):
284
self.drawBoard = drawBoard
285
self.styleStack = [SvgRenderStyle()]
286
self.contextStack = [None]
287
self.transformStack = [SvgTransform()]
291
def startElement(self, name, attrs):
292
style = SvgRenderStyle(self.style())
293
style.applyAttributes(attrs, self.defs)
294
self.styleStack.append(style)
296
transform = SvgTransform(self.transform())
297
transform.applyAttributes(attrs)
298
self.transformStack.append(transform)
301
f = "start" + name.capitalize()
302
#print f, self.transformStack
303
#print len(self.styleStack)
305
except AttributeError:
309
def endElement(self, name):
311
f = "end" + name.capitalize()
312
#print f, self.contextStack
314
except AttributeError:
316
self.styleStack.pop()
317
self.transformStack.pop()
319
def startG(self, attrs):
320
self.contextStack.append("g")
323
self.contextStack.pop()
325
def startDefs(self, attrs):
326
self.contextStack.append("defs")
329
self.contextStack.pop()
331
def startMarker(self, attrs):
332
self.contextStack.append("marker")
335
self.contextStack.pop()
338
return self.contextStack[-1]
341
return self.styleStack[-1]
344
return self.transformStack[-1]
346
def startPath(self, attrs):
347
if self.context() in ["g", None]:
349
self.style().apply(self.drawBoard, self.transform())
350
self.transform().apply(self.drawBoard)
352
self.cache.addStroke(self.style(), self.transform(), self.drawBoard.DrawPaths(d))
354
def createLinearGradient(self, attrs, keys):
356
if not "x1" in a or not "x2" in a or not "y1" in a or not "y2" in a:
357
a["x1"] = a["y1"] = 0.0
358
a["x2"] = a["y2"] = 1.0
359
if "id" in a and "x1" in a and "x2" in a and "y1" in a and "y2" in a:
360
transform = SvgTransform()
361
if "gradientTransform" in a:
362
transform.applyAttributes(a, key = "gradientTransform")
363
x1, y1, x2, y2 = [float(a[k]) for k in ["x1", "y1", "x2", "y2"]]
364
return a["id"], self.drawBoard.CreateLinearGradient((x1, y1), (x2, y2), keys), transform
365
return None, None, None
367
def createRadialGradient(self, attrs, keys):
369
if not "cx" in a or not "cy" in a or not "fx" in a or not "fy" in a:
370
a["cx"] = a["cy"] = 0.0
371
a["fx"] = a["fy"] = 1.0
372
if "id" in a and "cx" in a and "cy" in a and "fx" in a and "fy" in a and "r" in a:
373
transform = SvgTransform()
374
if "gradientTransform" in a:
375
transform.applyAttributes(a, key = "gradientTransform")
376
cx, cy, fx, fy, r = [float(a[k]) for k in ["cx", "cy", "fx", "fy", "r"]]
377
return a["id"], self.drawBoard.CreateRadialGradient((cx, cy), (fx, fy), r, keys), transform
378
return None, None, None
380
def startLineargradient(self, attrs):
381
if self.context() == "defs":
382
if "xlink:href" in attrs:
383
id = attrs["xlink:href"][1:]
384
if not id in self.defs:
385
Log.warn("Linear gradient %s has not been defined." % id)
387
keys = self.defs[id].gradientDesc.ColorKeys()
388
id, grad, trans = self.createLinearGradient(attrs, keys)
389
self.defs[id] = SvgGradient(grad, trans)
391
self.contextStack.append("gradient")
393
self.gradientAttrs = attrs
395
def startRadialgradient(self, attrs):
396
if self.context() == "defs":
397
if "xlink:href" in attrs:
398
id = attrs["xlink:href"][1:]
399
if not id in self.defs:
400
Log.warn("Radial gradient %s has not been defined." % id)
402
keys = self.defs[id].gradientDesc.ColorKeys()
403
id, grad, trans = self.createRadialGradient(attrs, keys)
404
self.defs[id] = SvgGradient(grad, trans)
406
self.contextStack.append("gradient")
408
self.gradientAttrs = attrs
410
def parseKeys(self, stops):
412
for stop in self.stops:
413
color, opacity, offset = None, None, None
415
style = self.style().parseStyle(stop["style"])
416
if "stop-color" in style:
417
color = self.style().parseColor(style["stop-color"])
418
if "stop-opacity" in style:
419
opacity = float(style["stop-opacity"])
421
offset = float(stop["offset"])
422
if offset is not None and (color is not None or opacity is not None):
423
if opacity is None: opacity = 1.0
424
k = amanith.GKeyValue(offset, (color[0], color[1], color[2], opacity))
428
def endLineargradient(self):
429
if self.context() == "gradient":
430
keys = self.parseKeys(self.stops)
431
id, grad, trans = self.createLinearGradient(self.gradientAttrs, keys)
433
del self.gradientAttrs
435
self.defs[id] = SvgGradient(grad, trans)
436
self.contextStack.pop()
438
def endRadialgradient(self):
439
if self.context() == "gradient":
440
keys = self.parseKeys(self.stops)
441
id, grad, trans = self.createRadialGradient(self.gradientAttrs, keys)
443
del self.gradientAttrs
445
self.defs[id] = SvgGradient(grad, trans)
446
self.contextStack.pop()
448
def startStop(self, attrs):
449
if self.context() == "gradient":
450
self.stops.append(attrs)
453
def __init__(self, drawBoard):
454
self.drawBoard = drawBoard
455
self.displayList = []
457
self.bank = drawBoard.CreateCacheBank()
459
def beginCaching(self):
460
self.drawBoard.SetCacheBank(self.bank)
461
self.drawBoard.SetTargetMode(amanith.G_CACHE_MODE)
463
def endCaching(self):
464
self.drawBoard.SetTargetMode(amanith.G_COLOR_MODE)
465
self.drawBoard.SetCacheBank(None)
467
def addStroke(self, style, transform, slot):
469
lastStyle = self.displayList[-1][0]
473
self.transforms[slot] = transform
475
if lastStyle == style:
476
lastSlotStart, lastSlotEnd = self.displayList[-1][1][-1]
477
if lastSlotEnd == slot - 1:
478
self.displayList[-1][1][-1] = (lastSlotStart, slot)
480
self.displayList[-1][1].append((slot, slot))
482
self.displayList.append((style, [(slot, slot)]))
484
def draw(self, baseTransform):
485
self.drawBoard.SetCacheBank(self.bank)
486
for style, slotList in self.displayList:
487
transform = SvgTransform(baseTransform)
488
transform.transform(self.transforms[slotList[0][0]])
489
transform.apply(self.drawBoard)
490
style.apply(self.drawBoard, transform)
491
for firstSlot, lastSlot in slotList:
492
self.drawBoard.DrawCacheSlots(firstSlot, lastSlot)
493
self.drawBoard.SetCacheBank(None)
495
# eat any possible OpenGL errors -- we can't handle them anyway
497
glMatrixMode(GL_MODELVIEW)
502
def __init__(self, context, ImgData):
505
self.context = context
507
self.transform = SvgTransform()
509
# Detect the type of data passed in
510
if type(ImgData) == file:
511
self.ImgData = ImgData.read()
512
elif type(ImgData) == str:
513
bitmapFile = ImgData.replace(".svg", ".png")
514
# Load PNG files directly
515
if ImgData.endswith(".png"):
516
self.texture = Texture(ImgData)
517
elif ImgData.endswith(".jpg"):
518
self.texture = Texture(ImgData)
519
elif ImgData.endswith(".jpeg"):
520
self.texture = Texture(ImgData)
521
# Check whether we have a prerendered bitmap version of the SVG file
522
elif ImgData.endswith(".svg") and os.path.exists(bitmapFile):
523
Log.debug("Loading cached bitmap '%s' instead of '%s'." % (bitmapFile, ImgData))
524
self.texture = Texture(bitmapFile)
527
e = "PyAmanith support is deprecated and you are trying to load an SVG file."
529
raise RuntimeError(e)
530
Log.debug("Loading SVG file '%s'." % (ImgData))
531
self.ImgData = open(ImgData).read()
532
elif isinstance(ImgData, Image.Image): #stump: let a PIL image be passed in
533
self.texture = Texture()
534
self.texture.loadImage(ImgData)
536
# Make sure we have a valid texture
538
if type(ImgData) == str:
539
e = "Unable to load texture for %s." % ImgData
541
e = "Unable to load texture for SVG file."
543
raise RuntimeError(e)
545
def _cacheDrawing(self, drawBoard):
546
self.cache.beginCaching()
547
parser = sax.make_parser()
548
sax.parseString(self.ImgData, SvgHandler(drawBoard, self.cache))
549
self.cache.endCaching()
552
def convertToTexture(self, width, height):
556
e = "SVG drawing does not have a valid texture image."
558
raise RuntimeError(e)
560
def _getEffectiveTransform(self):
561
transform = SvgTransform(self.transform)
562
transform.transform(self.context.transform)
566
width = self.texture.pixelSize[0]
567
if not width == None:
574
height = self.texture.pixelSize[1]
575
if not height == None:
581
def widthf(self, pixelw):
582
width = self.texture.pixelSize[0]
583
wfactor = pixelw/width
584
if not width == None:
589
def _render(self, transform):
590
glMatrixMode(GL_TEXTURE)
592
glMatrixMode(GL_MODELVIEW)
594
glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_STENCIL_BUFFER_BIT | GL_TRANSFORM_BIT | GL_COLOR_BUFFER_BIT | GL_POLYGON_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT)
596
self.cache = SvgCache(self.context.drawBoard)
597
self._cacheDrawing(self.context.drawBoard)
598
self.cache.draw(transform)
601
glMatrixMode(GL_TEXTURE)
603
glMatrixMode(GL_MODELVIEW)
605
def draw(self, color = (1, 1, 1, 1), rect = (0,1,0,1), lOffset = 0.0, rOffset = 0.0):
606
glMatrixMode(GL_TEXTURE)
608
glMatrixMode(GL_PROJECTION)
610
self.context.setProjection()
611
glMatrixMode(GL_MODELVIEW)
614
transform = self._getEffectiveTransform()
619
glScalef(self.texture.pixelSize[0], self.texture.pixelSize[1], 1)
620
glTranslatef(-.5, -.5, 0)
624
glEnable(GL_TEXTURE_2D)
631
[1.0+rOffset, 0.0]], dtype=float32)
633
textriangVtx = array(
637
[rect[1], rect[2]]], dtype=float32)
639
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
640
glEnableClientState(GL_VERTEX_ARRAY)
641
glTexCoordPointerf(textriangVtx)
642
glVertexPointerf(triangVtx)
643
glDrawArrays(GL_TRIANGLE_STRIP, 0, triangVtx.shape[0])
644
glDisableClientState(GL_VERTEX_ARRAY)
645
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
647
glDisable(GL_TEXTURE_2D)
649
self._render(transform)
650
glMatrixMode(GL_TEXTURE)
652
glMatrixMode(GL_MODELVIEW)
654
glMatrixMode(GL_PROJECTION)
656
glMatrixMode(GL_MODELVIEW)