1 // An OpenVG path on the canvas
3 // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include <Canvas/property_helper.hxx>
22 #include <vg/openvg.h>
24 #include <osg/Drawable>
25 #include <osg/BlendFunc>
31 typedef std::vector<VGubyte> CmdList;
32 typedef std::vector<VGfloat> CoordList;
39 _path(VG_INVALID_HANDLE),
40 _paint(VG_INVALID_HANDLE),
41 _paint_fill(VG_INVALID_HANDLE),
42 _attributes_dirty(~0),
45 _stroke_linecap(VG_CAP_BUTT)
47 setSupportsDisplayList(false);
48 setDataVariance(Object::DYNAMIC);
50 setUpdateCallback(new PathUpdateCallback());
51 setCullCallback(new NoCullCallback());
54 virtual ~PathDrawable()
57 vgDestroyPaint(_paint);
60 virtual const char* className() const { return "PathDrawable"; }
61 virtual osg::Object* cloneType() const { return new PathDrawable; }
62 virtual osg::Object* clone(const osg::CopyOp&) const { return new PathDrawable; }
65 * Replace the current path segments with the new ones
67 * @param cmds List of OpenVG path commands
68 * @param coords List of coordinates/parameters used by #cmds
70 void setSegments(const CmdList& cmds, const CoordList& coords)
75 _attributes_dirty |= PATH;
79 * Set path fill paint ("none" if not filled)
81 void setFill(const std::string& fill)
85 _mode &= ~VG_FILL_PATH;
89 _fill_color = parseColor(fill);
90 _mode |= VG_FILL_PATH;
91 _attributes_dirty |= FILL_COLOR;
96 * Set path stroke paint ("none" if no stroke)
98 void setStroke(const std::string& stroke)
100 if( stroke == "none" )
102 _mode &= ~VG_STROKE_PATH;
106 _stroke_color = parseColor(stroke);
107 _mode |= VG_STROKE_PATH;
108 _attributes_dirty |= STROKE_COLOR;
115 void setStrokeWidth(float width)
117 _stroke_width = width;
118 _attributes_dirty |= BOUNDING_BOX;
122 * Set stroke dash (line stipple)
124 void setStrokeDashArray(const std::string& dash)
126 _stroke_dash = splitAndConvert(",\t\n ", dash);
132 * @see http://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty
134 void setStrokeLinecap(const std::string& linecap)
136 if( linecap == "round" )
137 _stroke_linecap = VG_CAP_ROUND;
138 else if( linecap == "square" )
139 _stroke_linecap = VG_CAP_SQUARE;
141 _stroke_linecap = VG_CAP_BUTT;
147 virtual void drawImplementation(osg::RenderInfo& renderInfo) const
149 if( (_attributes_dirty & PATH) && _vg_initialized )
152 osg::State* state = renderInfo.getState();
155 state->setActiveTextureUnit(0);
156 state->setClientActiveTextureUnit(0);
157 state->disableAllVertexArrays();
159 glPushAttrib(~0u); // Don't use GL_ALL_ATTRIB_BITS as on my machine it
160 // eg. doesn't include GL_MULTISAMPLE_BIT
161 glPushClientAttrib(~0u);
163 // Initialize OpenVG itself
164 if( !_vg_initialized )
167 glGetIntegerv(GL_VIEWPORT, vp);
169 vgCreateContextSH(vp[2], vp[3]);
170 _vg_initialized = true;
174 // Initialize/Update the paint
175 if( _attributes_dirty & STROKE_COLOR )
177 if( _paint == VG_INVALID_HANDLE )
178 _paint = vgCreatePaint();
180 vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color._v);
182 _attributes_dirty &= ~STROKE_COLOR;
185 // Initialize/update fill paint
186 if( _attributes_dirty & FILL_COLOR )
188 if( _paint_fill == VG_INVALID_HANDLE )
189 _paint_fill = vgCreatePaint();
191 vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color._v);
193 _attributes_dirty &= ~FILL_COLOR;
197 if( _mode & VG_STROKE_PATH )
199 vgSetPaint(_paint, VG_STROKE_PATH);
201 vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
202 vgSeti(VG_STROKE_CAP_STYLE, _stroke_linecap);
203 vgSetfv( VG_STROKE_DASH_PATTERN,
205 _stroke_dash.empty() ? 0 : &_stroke_dash[0] );
207 if( _mode & VG_FILL_PATH )
209 vgSetPaint(_paint_fill, VG_FILL_PATH);
212 // And finally draw the path
214 vgDrawPath(_path, _mode);
216 VGErrorCode err = vgGetError();
217 if( err != VG_NO_ERROR )
218 SG_LOG(SG_GL, SG_ALERT, "vgError: " << err);
225 * Compute the bounding box
227 virtual osg::BoundingBox computeBound() const
229 if( _path == VG_INVALID_HANDLE )
230 return osg::BoundingBox();
232 VGfloat min[2], size[2];
233 vgPathBounds(_path, &min[0], &min[1], &size[0], &size[1]);
235 _attributes_dirty &= ~BOUNDING_BOX;
237 // vgPathBounds doesn't take stroke width into account
238 float ext = 0.5 * _stroke_width;
240 return osg::BoundingBox
242 min[0] - ext, min[1] - ext, -0.1,
243 min[0] + size[0] + ext, min[1] + size[1] + ext, 0.1
249 static bool _vg_initialized;
254 STROKE_COLOR = PATH << 1,
255 FILL_COLOR = STROKE_COLOR << 1,
256 BOUNDING_BOX = FILL_COLOR << 1
259 mutable VGPath _path;
260 mutable VGPaint _paint;
261 mutable VGPaint _paint_fill;
262 mutable uint32_t _attributes_dirty;
268 osg::Vec4f _fill_color;
269 osg::Vec4f _stroke_color;
270 VGfloat _stroke_width;
271 std::vector<VGfloat> _stroke_dash;
272 VGCapStyle _stroke_linecap;
275 * Initialize/Update the OpenVG path
279 if( _attributes_dirty & PATH )
281 const VGbitfield caps = VG_PATH_CAPABILITY_APPEND_TO
282 | VG_PATH_CAPABILITY_MODIFY
283 | VG_PATH_CAPABILITY_PATH_BOUNDS;
285 if( _path == VG_INVALID_HANDLE )
286 _path = vgCreatePath(
287 VG_PATH_FORMAT_STANDARD,
289 1.f, 0.f, // scale,bias
290 _cmds.size(), _coords.size(),
294 vgClearPath(_path, caps);
296 if( !_cmds.empty() && !_coords.empty() )
297 vgAppendPathData(_path, _cmds.size(), &_cmds[0], &_coords[0]);
299 _attributes_dirty &= ~PATH;
300 _attributes_dirty |= BOUNDING_BOX;
303 if( _attributes_dirty & BOUNDING_BOX )
308 * Updating the path before drawing is needed to enable correct bounding
309 * box calculations and make culling work.
311 struct PathUpdateCallback:
312 public osg::Drawable::UpdateCallback
314 virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
316 if( !_vg_initialized )
318 static_cast<PathDrawable*>(drawable)->update();
323 * Callback used to prevent culling as long as OpenVG is not initialized.
324 * This is needed because OpenVG needs an active OpenGL context for
325 * initialization which is only available in #drawImplementation.
326 * As soon as OpenVG is correctly initialized the callback automatically
327 * removes itself from the node, so that the normal culling can get
330 struct NoCullCallback:
331 public osg::Drawable::CullCallback
333 virtual bool cull( osg::NodeVisitor*,
334 osg::Drawable* drawable,
337 if( _vg_initialized )
338 drawable->setCullCallback(0);
344 bool PathDrawable::_vg_initialized = false;
346 //----------------------------------------------------------------------------
347 Path::Path(SGPropertyNode_ptr node, const Style& parent_style):
348 Element(node, parent_style, BOUNDING_BOX),
349 _path( new PathDrawable() )
352 PathDrawable *path = _path.get();
354 addStyle("fill", &PathDrawable::setFill, path);
355 addStyle("stroke", &PathDrawable::setStroke, path);
356 addStyle("stroke-width", &PathDrawable::setStrokeWidth, path);
357 addStyle("stroke-dasharray", &PathDrawable::setStrokeDashArray, path);
358 addStyle("stroke-linecap", &PathDrawable::setStrokeLinecap, path);
363 //----------------------------------------------------------------------------
369 //----------------------------------------------------------------------------
370 void Path::update(double dt)
372 if( _attributes_dirty & (CMDS | COORDS) )
376 getVectorFromChildren<VGubyte, int>(_node, "cmd"),
377 getVectorFromChildren<VGfloat, float>(_node, "coord")
380 _attributes_dirty &= ~(CMDS | COORDS);
386 //----------------------------------------------------------------------------
387 void Path::childChanged(SGPropertyNode* child)
389 if( child->getParent() != _node )
392 if( child->getNameString() == "cmd" )
393 _attributes_dirty |= CMDS;
394 else if( child->getNameString() == "coord" )
395 _attributes_dirty |= COORDS;
398 } // namespace canvas