1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import re
24 import os
25 from xml import sax
26 from OpenGL.GL import *
27 from numpy import reshape, dot, transpose, identity, zeros, float32
28 from math import sin, cos
29
30 import Log
31 import Config
32 from Texture import Texture, TextureException
33
34 try:
35 import amanith
36 import SvgColors
37 haveAmanith = True
38 except ImportError:
39 Log.warn("PyAmanith not found, SVG support disabled.")
40 import DummyAmanith as amanith
41 haveAmanith = False
42
43
44 if not hasattr(sax.xmlreader.AttributesImpl, '__contains__'):
45 sax.xmlreader.AttributesImpl.__contains__ = sax.xmlreader.AttributesImpl.has_key
46
47
48
49
50
51
52
53
54
55 Config.define("opengl", "svgshaders", bool, False, text = "Use OpenGL SVG shaders", options = {False: "No", True: "Yes"})
56
57 LOW_QUALITY = amanith.G_LOW_RENDERING_QUALITY
58 NORMAL_QUALITY = amanith.G_NORMAL_RENDERING_QUALITY
59 HIGH_QUALITY = amanith.G_HIGH_RENDERING_QUALITY
60
62 - def __init__(self, gradientDesc, transform):
65
69
71 - def __init__(self, geometry):
72 self.kernel = amanith.GKernel()
73 self.geometry = geometry
74 self.drawBoard = amanith.GOpenGLBoard(geometry[0], geometry[0] + geometry[2],
75 geometry[1], geometry[1] + geometry[3])
76 self.drawBoard.SetShadersEnabled(Config.get("opengl", "svgshaders"))
77 self.transform = SvgTransform()
78 self.setGeometry(geometry)
79 self.setProjection(geometry)
80
81
82 try:
83 glMatrixMode(GL_MODELVIEW)
84 except:
85 Log.warn("SVG renderer initialization failed; expect corrupted graphics. " +
86 "To fix this, upgrade your OpenGL drivers and set your display " +
87 "to 32 bit color precision.")
88
89 - def setGeometry(self, geometry = None):
90 self.drawBoard.SetViewport(geometry[0], geometry[1],
91 geometry[2], geometry[3])
92 self.transform.reset()
93 self.transform.scale(geometry[2] / 640.0, geometry[3] / 480.0)
94
95 - def setProjection(self, geometry = None):
96 geometry = geometry or self.geometry
97 self.drawBoard.SetProjection(geometry[0], geometry[0] + geometry[2],
98 geometry[1], geometry[1] + geometry[3])
99 self.geometry = geometry
100
101 - def setRenderingQuality(self, quality):
102 if quality == LOW_QUALITY:
103 q = amanith.G_LOW_RENDERING_QUALITY
104 elif quality == NORMAL_QUALITY:
105 q = amanith.G_NORMAL_RENDERING_QUALITY
106 elif quality == HIGH_QUALITY:
107 q = amanith.G_HIGH_RENDERING_QUALITY
108 else:
109 raise RaiseValueError("Bad rendering quality.")
110 self.drawBoard.SetRenderingQuality(q)
111
113 q = self.drawBoard.RenderingQuality()
114 if q == amanith.G_LOW_RENDERING_QUALITY:
115 return LOW_QUALITY
116 elif q == amanith.G_NORMAL_RENDERING_QUALITY:
117 return NORMAL_QUALITY
118 return HIGH_QUALITY
119
120 - def clear(self, r = 0, g = 0, b = 0, a = 0):
121 self.drawBoard.Clear(r, g, b, a)
122
125 self.strokeColor = None
126 self.strokeWidth = None
127 self.fillColor = None
128 self.strokeLineJoin = None
129 self.strokeOpacity = None
130 self.fillOpacity = None
131
132 if baseStyle:
133 self.__dict__.update(baseStyle.__dict__)
134
136 s = {}
137 for m in re.finditer(r"(.+?):\s*(.+?)(;|$)\s*", style):
138 s[m.group(1)] = m.group(2)
139 return s
140
142 if color.lower() == "none":
143 return None
144
145 try:
146 return SvgColors.colors[color.lower()]
147 except KeyError:
148 pass
149
150 if color[0] == "#":
151 color = color[1:]
152 if len(color) == 3:
153 return (int(color[0], 16) / 15.0, int(color[1], 16) / 15.0, int(color[2], 16) / 15.0, 1.0)
154 return (int(color[0:2], 16) / 255.0, int(color[2:4], 16) / 255.0, int(color[4:6], 16) / 255.0, 1.0)
155 else:
156 if not defs:
157 Log.warn("No patterns or gradients defined.")
158 return None
159 m = re.match("url\(#(.+)\)", color)
160 if m:
161 id = m.group(1)
162 if not id in defs:
163 Log.warn("Pattern/gradient %s has not been defined." % id)
164 return defs.get(id)
165
167 if s:
168 for k, v in self.__dict__.items():
169 if v != getattr(s, k):
170 return 1
171 return 0
172 return 1
173
175 return "<SvgRenderStyle " + " ".join(["%s:%s" % (k, v) for k, v in self.__dict__.items()]) + ">"
176
178 style = attrs.get("style")
179 if style:
180 style = self.parseStyle(style)
181
182 if "stroke" in style:
183 self.strokeColor = self.parseColor(style["stroke"], defs)
184 if "fill" in style:
185 self.fillColor = self.parseColor(style["fill"], defs)
186 if "stroke-width" in style:
187 self.strokeWidth = float(style["stroke-width"].replace("px", ""))
188 if "stroke-opacity" in style:
189 self.strokeOpacity = float(style["stroke-opacity"])
190 if "fill-opacity" in style:
191 self.fillOpacity = float(style["fill-opacity"])
192 if "stroke-linejoin" in style:
193 j = style["stroke-linejoin"].lower()
194 if j == "miter":
195 self.strokeLineJoin = amanith.G_MITER_JOIN
196 elif j == "round":
197 self.strokeLineJoin = amanith.G_ROUND_JOIN
198 elif j == "bevel":
199 self.strokeLineJoin = amanith.G_BEVEL_JOIN
200
201 - def apply(self, drawBoard, transform):
202 if self.strokeColor is not None:
203 if isinstance(self.strokeColor, SvgGradient):
204 self.strokeColor.applyTransform(transform)
205 drawBoard.SetStrokePaintType(amanith.G_GRADIENT_PAINT_TYPE)
206 drawBoard.SetStrokeGradient(self.strokeColor.gradientDesc)
207 else:
208 drawBoard.SetStrokePaintType(amanith.G_COLOR_PAINT_TYPE)
209 drawBoard.SetStrokeColor(*self.strokeColor)
210 drawBoard.SetStrokeEnabled(True)
211 else:
212 drawBoard.SetStrokeEnabled(False)
213
214 if self.fillColor is not None:
215 if isinstance(self.fillColor, SvgGradient):
216 self.fillColor.applyTransform(transform)
217 drawBoard.SetFillPaintType(amanith.G_GRADIENT_PAINT_TYPE)
218 drawBoard.SetFillGradient(self.fillColor.gradientDesc)
219 else:
220 drawBoard.SetFillPaintType(amanith.G_COLOR_PAINT_TYPE)
221 drawBoard.SetFillColor(*self.fillColor)
222 drawBoard.SetFillEnabled(True)
223 else:
224 drawBoard.SetFillEnabled(False)
225
226 if self.strokeWidth is not None:
227 drawBoard.SetStrokeWidth(self.strokeWidth)
228
229 if self.strokeOpacity is not None:
230 drawBoard.SetStrokeOpacity(self.strokeOpacity)
231
232 if self.fillOpacity is not None:
233 drawBoard.SetFillOpacity(self.fillOpacity)
234
235 if self.strokeLineJoin is not None:
236 drawBoard.SetStrokeJoinStyle(self.strokeLineJoin)
237
308
311 self.drawBoard = drawBoard
312 self.styleStack = [SvgRenderStyle()]
313 self.contextStack = [None]
314 self.transformStack = [SvgTransform()]
315 self.defs = {}
316 self.cache = cache
317
335
337 try:
338 f = "end" + name.capitalize()
339
340 getattr(self, f)()
341 except AttributeError:
342 pass
343 self.styleStack.pop()
344 self.transformStack.pop()
345
347 self.contextStack.append("g")
348
350 self.contextStack.pop()
351
353 self.contextStack.append("defs")
354
356 self.contextStack.pop()
357
359 self.contextStack.append("marker")
360
362 self.contextStack.pop()
363
365 return self.contextStack[-1]
366
368 return self.styleStack[-1]
369
372
380
382 a = dict(attrs)
383 if not "x1" in a or not "x2" in a or not "y1" in a or not "y2" in a:
384 a["x1"] = a["y1"] = 0.0
385 a["x2"] = a["y2"] = 1.0
386 if "id" in a and "x1" in a and "x2" in a and "y1" in a and "y2" in a:
387 transform = SvgTransform()
388 if "gradientTransform" in a:
389 transform.applyAttributes(a, key = "gradientTransform")
390 x1, y1, x2, y2 = [float(a[k]) for k in ["x1", "y1", "x2", "y2"]]
391 return a["id"], self.drawBoard.CreateLinearGradient((x1, y1), (x2, y2), keys), transform
392 return None, None, None
393
395 a = dict(attrs)
396 if not "cx" in a or not "cy" in a or not "fx" in a or not "fy" in a:
397 a["cx"] = a["cy"] = 0.0
398 a["fx"] = a["fy"] = 1.0
399 if "id" in a and "cx" in a and "cy" in a and "fx" in a and "fy" in a and "r" in a:
400 transform = SvgTransform()
401 if "gradientTransform" in a:
402 transform.applyAttributes(a, key = "gradientTransform")
403 cx, cy, fx, fy, r = [float(a[k]) for k in ["cx", "cy", "fx", "fy", "r"]]
404 return a["id"], self.drawBoard.CreateRadialGradient((cx, cy), (fx, fy), r, keys), transform
405 return None, None, None
406
408 if self.context() == "defs":
409 if "xlink:href" in attrs:
410 id = attrs["xlink:href"][1:]
411 if not id in self.defs:
412 Log.warn("Linear gradient %s has not been defined." % id)
413 else:
414 keys = self.defs[id].gradientDesc.ColorKeys()
415 id, grad, trans = self.createLinearGradient(attrs, keys)
416 self.defs[id] = SvgGradient(grad, trans)
417 else:
418 self.contextStack.append("gradient")
419 self.stops = []
420 self.gradientAttrs = attrs
421
423 if self.context() == "defs":
424 if "xlink:href" in attrs:
425 id = attrs["xlink:href"][1:]
426 if not id in self.defs:
427 Log.warn("Radial gradient %s has not been defined." % id)
428 else:
429 keys = self.defs[id].gradientDesc.ColorKeys()
430 id, grad, trans = self.createRadialGradient(attrs, keys)
431 self.defs[id] = SvgGradient(grad, trans)
432 else:
433 self.contextStack.append("gradient")
434 self.stops = []
435 self.gradientAttrs = attrs
436
438 keys = []
439 for stop in self.stops:
440 color, opacity, offset = None, None, None
441 if "style" in stop:
442 style = self.style().parseStyle(stop["style"])
443 if "stop-color" in style:
444 color = self.style().parseColor(style["stop-color"])
445 if "stop-opacity" in style:
446 opacity = float(style["stop-opacity"])
447 if "offset" in stop:
448 offset = float(stop["offset"])
449 if offset is not None and (color is not None or opacity is not None):
450 if opacity is None: opacity = 1.0
451 k = amanith.GKeyValue(offset, (color[0], color[1], color[2], opacity))
452 keys.append(k)
453 return keys
454
456 if self.context() == "gradient":
457 keys = self.parseKeys(self.stops)
458 id, grad, trans = self.createLinearGradient(self.gradientAttrs, keys)
459 del self.stops
460 del self.gradientAttrs
461 if id and grad:
462 self.defs[id] = SvgGradient(grad, trans)
463 self.contextStack.pop()
464
466 if self.context() == "gradient":
467 keys = self.parseKeys(self.stops)
468 id, grad, trans = self.createRadialGradient(self.gradientAttrs, keys)
469 del self.stops
470 del self.gradientAttrs
471 if id and grad:
472 self.defs[id] = SvgGradient(grad, trans)
473 self.contextStack.pop()
474
476 if self.context() == "gradient":
477 self.stops.append(attrs)
478
481 self.drawBoard = drawBoard
482 self.displayList = []
483 self.transforms = {}
484 self.bank = drawBoard.CreateCacheBank()
485
487 self.drawBoard.SetCacheBank(self.bank)
488 self.drawBoard.SetTargetMode(amanith.G_CACHE_MODE)
489
491 self.drawBoard.SetTargetMode(amanith.G_COLOR_MODE)
492 self.drawBoard.SetCacheBank(None)
493
494 - def addStroke(self, style, transform, slot):
495 if self.displayList:
496 lastStyle = self.displayList[-1][0]
497 else:
498 lastStyle = None
499
500 self.transforms[slot] = transform
501
502 if lastStyle == style:
503 lastSlotStart, lastSlotEnd = self.displayList[-1][1][-1]
504 if lastSlotEnd == slot - 1:
505 self.displayList[-1][1][-1] = (lastSlotStart, slot)
506 else:
507 self.displayList[-1][1].append((slot, slot))
508 else:
509 self.displayList.append((style, [(slot, slot)]))
510
511 - def draw(self, baseTransform):
512 self.drawBoard.SetCacheBank(self.bank)
513 for style, slotList in self.displayList:
514 transform = SvgTransform(baseTransform)
515 transform.transform(self.transforms[slotList[0][0]])
516 transform.apply(self.drawBoard)
517 style.apply(self.drawBoard, transform)
518 for firstSlot, lastSlot in slotList:
519 self.drawBoard.DrawCacheSlots(firstSlot, lastSlot)
520 self.drawBoard.SetCacheBank(None)
521
522
523 try:
524 glMatrixMode(GL_MODELVIEW)
525 except:
526 pass
527
530 self.svgData = None
531 self.texture = None
532 self.context = context
533 self.cache = None
534 self.transform = SvgTransform()
535
536
537 if type(svgData) == file:
538 self.svgData = svgData.read()
539 elif type(svgData) == str:
540
541 bitmapFile = svgData.replace(".svg", ".png")
542 if svgData.endswith(".svg") and os.path.exists(bitmapFile) and 0:
543 Log.debug("Loading cached bitmap '%s' instead of '%s'." % (bitmapFile, svgData))
544 self.texture = Texture(bitmapFile)
545 else:
546 if not haveAmanith:
547 e = "PyAmanith is not installed and you are trying to load an SVG file."
548 Log.error(e)
549 raise RuntimeError(e)
550 Log.debug("Loading SVG file '%s'." % (svgData))
551 self.svgData = open(svgData).read()
552
554 self.cache.beginCaching()
555 parser = sax.make_parser()
556 sax.parseString(self.svgData, SvgHandler(drawBoard, self.cache))
557 self.cache.endCaching()
558 del self.svgData
559
560 - def convertToTexture(self, width, height):
561 if self.texture:
562 return
563
564 try:
565 self.texture = Texture()
566 self.texture.bind()
567 self.texture.prepareRenderTarget(width, height)
568 self.texture.setAsRenderTarget()
569 quality = self.context.getRenderingQuality()
570 self.context.setRenderingQuality(HIGH_QUALITY)
571 geometry = self.context.geometry
572 self.context.setProjection((0, 0, width, height))
573 glViewport(0, 0, width, height)
574 self.context.clear()
575 transform = SvgTransform()
576 transform.translate(width / 2, height / 2)
577 self._render(transform)
578 self.texture.resetDefaultRenderTarget()
579 self.context.setProjection(geometry)
580 glViewport(*geometry)
581 self.context.setRenderingQuality(quality)
582 except TextureException, e:
583 Log.warn("Unable to convert SVG drawing to texture: %s" % str(e))
584
589
591 glMatrixMode(GL_TEXTURE)
592 glPushMatrix()
593 glMatrixMode(GL_MODELVIEW)
594
595 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 if not self.cache:
597 self.cache = SvgCache(self.context.drawBoard)
598 self._cacheDrawing(self.context.drawBoard)
599 self.cache.draw(transform)
600 glPopAttrib()
601
602 glMatrixMode(GL_TEXTURE)
603 glPopMatrix()
604 glMatrixMode(GL_MODELVIEW)
605
606 - def draw(self, color = (1, 1, 1, 1)):
607 glMatrixMode(GL_TEXTURE)
608 glPushMatrix()
609 glMatrixMode(GL_PROJECTION)
610 glPushMatrix()
611 self.context.setProjection()
612 glMatrixMode(GL_MODELVIEW)
613 glPushMatrix()
614
615 transform = self._getEffectiveTransform()
616 if self.texture:
617 glLoadIdentity()
618 transform.applyGL()
619
620 glScalef(self.texture.pixelSize[0], self.texture.pixelSize[1], 1)
621 glTranslatef(-.5, -.5, 0)
622 glColor4f(*color)
623
624 self.texture.bind()
625 glEnable(GL_TEXTURE_2D)
626 glBegin(GL_TRIANGLE_STRIP)
627 glTexCoord2f(0.0, 1.0)
628 glVertex2f(0.0, 1.0)
629 glTexCoord2f(1.0, 1.0)
630 glVertex2f(1.0, 1.0)
631 glTexCoord2f(0.0, 0.0)
632 glVertex2f(0.0, 0.0)
633 glTexCoord2f(1.0, 0.0)
634 glVertex2f(1.0, 0.0)
635 glEnd()
636 glDisable(GL_TEXTURE_2D)
637 else:
638 self._render(transform)
639 glMatrixMode(GL_TEXTURE)
640 glPopMatrix()
641 glMatrixMode(GL_MODELVIEW)
642 glPopMatrix()
643 glMatrixMode(GL_PROJECTION)
644 glPopMatrix()
645 glMatrixMode(GL_MODELVIEW)
646