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),
44 _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 stroke width and dash (line stipple)
81 void setStroke( float width,
82 const std::vector<float> dash = std::vector<float>() )
84 _stroke_width = width;
87 _attributes_dirty |= BOUNDING_BOX;
93 void setColor(const osg::Vec4& color)
95 for( size_t i = 0; i < 4; ++i )
96 _stroke_color[i] = color[i];
97 _attributes_dirty |= STROKE_COLOR;
101 * Enable/Disable filling of the path
103 void enableFill(bool enable)
111 void setColorFill(const osg::Vec4& color)
113 for( size_t i = 0; i < 4; ++i )
114 _fill_color[i] = color[i];
115 _attributes_dirty |= FILL_COLOR;
121 * @see http://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty
123 void setStrokeLinecap(const std::string& linecap)
125 if( linecap == "round" )
126 _stroke_linecap = VG_CAP_ROUND;
127 else if( linecap == "square" )
128 _stroke_linecap = VG_CAP_SQUARE;
130 _stroke_linecap = VG_CAP_BUTT;
136 virtual void drawImplementation(osg::RenderInfo& renderInfo) const
138 if( (_attributes_dirty & PATH) && _vg_initialized )
141 osg::State* state = renderInfo.getState();
144 state->setActiveTextureUnit(0);
145 state->setClientActiveTextureUnit(0);
146 state->disableAllVertexArrays();
148 glPushAttrib(~0u); // Don't use GL_ALL_ATTRIB_BITS as on my machine it
149 // eg. doesn't include GL_MULTISAMPLE_BIT
150 glPushClientAttrib(~0u);
152 // Initialize OpenVG itself
153 if( !_vg_initialized )
156 glGetIntegerv(GL_VIEWPORT, vp);
158 vgCreateContextSH(vp[2], vp[3]);
159 _vg_initialized = true;
163 // Initialize/Update the paint
164 if( _attributes_dirty & STROKE_COLOR )
166 if( _paint == VG_INVALID_HANDLE )
167 _paint = vgCreatePaint();
169 vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color);
171 _attributes_dirty &= ~STROKE_COLOR;
174 // Initialize/update fill paint
175 if( _attributes_dirty & (FILL_COLOR | FILL) )
177 if( _paint_fill == VG_INVALID_HANDLE )
178 _paint_fill = vgCreatePaint();
180 if( _attributes_dirty & FILL_COLOR )
181 vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color);
183 _attributes_dirty &= ~(FILL_COLOR | FILL);
188 if( _stroke_width > 0 )
190 mode |= VG_STROKE_PATH;
191 vgSetPaint(_paint, VG_STROKE_PATH);
193 vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
194 vgSeti(VG_STROKE_CAP_STYLE, _stroke_linecap);
195 vgSetfv( VG_STROKE_DASH_PATTERN,
197 _stroke_dash.empty() ? 0 : &_stroke_dash[0] );
201 mode |= VG_FILL_PATH;
202 vgSetPaint(_paint_fill, VG_FILL_PATH);
205 // And finally draw the path
207 vgDrawPath(_path, mode);
209 VGErrorCode err = vgGetError();
210 if( err != VG_NO_ERROR )
211 SG_LOG(SG_GL, SG_ALERT, "vgError: " << err);
218 * Compute the bounding box
220 virtual osg::BoundingBox computeBound() const
222 if( _path == VG_INVALID_HANDLE )
223 return osg::BoundingBox();
225 VGfloat min[2], size[2];
226 vgPathBounds(_path, &min[0], &min[1], &size[0], &size[1]);
228 _attributes_dirty &= ~BOUNDING_BOX;
230 // vgPathBounds doesn't take stroke width into account
231 float ext = 0.5 * _stroke_width;
233 return osg::BoundingBox
235 min[0] - ext, min[1] - ext, -0.1,
236 min[0] + size[0] + ext, min[1] + size[1] + ext, 0.1
242 static bool _vg_initialized;
247 STROKE_COLOR = PATH << 1,
248 FILL_COLOR = STROKE_COLOR << 1,
249 FILL = FILL_COLOR << 1,
250 BOUNDING_BOX = FILL << 1
253 mutable VGPath _path;
254 mutable VGPaint _paint;
255 mutable VGPaint _paint_fill;
256 mutable uint32_t _attributes_dirty;
261 VGfloat _stroke_color[4];
262 VGfloat _stroke_width;
263 std::vector<VGfloat> _stroke_dash;
264 VGCapStyle _stroke_linecap;
267 VGfloat _fill_color[4];
270 * Initialize/Update the OpenVG path
274 if( _attributes_dirty & PATH )
276 const VGbitfield caps = VG_PATH_CAPABILITY_APPEND_TO
277 | VG_PATH_CAPABILITY_MODIFY
278 | VG_PATH_CAPABILITY_PATH_BOUNDS;
280 if( _path == VG_INVALID_HANDLE )
281 _path = vgCreatePath(
282 VG_PATH_FORMAT_STANDARD,
284 1.f, 0.f, // scale,bias
285 _cmds.size(), _coords.size(),
289 vgClearPath(_path, caps);
291 if( !_cmds.empty() && !_coords.empty() )
292 vgAppendPathData(_path, _cmds.size(), &_cmds[0], &_coords[0]);
294 _attributes_dirty &= ~PATH;
295 _attributes_dirty |= BOUNDING_BOX;
298 if( _attributes_dirty & BOUNDING_BOX )
303 * Updating the path before drawing is needed to enable correct bounding
304 * box calculations and make culling work.
306 struct PathUpdateCallback:
307 public osg::Drawable::UpdateCallback
309 virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
311 if( !_vg_initialized )
313 static_cast<PathDrawable*>(drawable)->update();
318 * Callback used to prevent culling as long as OpenVG is not initialized.
319 * This is needed because OpenVG needs an active OpenGL context for
320 * initialization which is only available in #drawImplementation.
321 * As soon as OpenVG is correctly initialized the callback automatically
322 * removes itself from the node, so that the normal culling can get
325 struct NoCullCallback:
326 public osg::Drawable::CullCallback
328 virtual bool cull( osg::NodeVisitor*,
329 osg::Drawable* drawable,
332 if( _vg_initialized )
333 drawable->setCullCallback(0);
339 bool PathDrawable::_vg_initialized = false;
341 //----------------------------------------------------------------------------
342 Path::Path(SGPropertyNode_ptr node):
343 Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),
344 _path( new PathDrawable() )
349 //----------------------------------------------------------------------------
355 //----------------------------------------------------------------------------
356 void Path::update(double dt)
358 if( _attributes_dirty & (CMDS | COORDS) )
362 getVectorFromChildren<VGubyte, int>(_node, "cmd"),
363 getVectorFromChildren<VGfloat, float>(_node, "coord")
366 _attributes_dirty &= ~(CMDS | COORDS);
368 if( _attributes_dirty & STROKE )
372 _node->getFloatValue("stroke-width", 1),
373 getVectorFromChildren<VGfloat, float>(_node, "stroke-dasharray")
376 _attributes_dirty &= ~STROKE;
382 //----------------------------------------------------------------------------
383 void Path::childChanged(SGPropertyNode* child)
385 if( child->getNameString() == "cmd" )
386 _attributes_dirty |= CMDS;
387 else if( child->getNameString() == "coord" )
388 _attributes_dirty |= COORDS;
389 else if( child->getNameString() == "stroke-width"
390 || child->getNameString() == "stroke-dasharray" )
391 _attributes_dirty |= STROKE;
392 else if( child->getNameString() == "stroke-linecap" )
393 _path->setStrokeLinecap( child->getStringValue() );
394 else if( child->getNameString() == "fill" )
395 _path->enableFill( child->getBoolValue() );
398 //----------------------------------------------------------------------------
399 void Path::colorChanged(const osg::Vec4& color)
401 _path->setColor(color);
404 //----------------------------------------------------------------------------
405 void Path::colorFillChanged(const osg::Vec4& color)
407 _path->setColorFill(color);
410 } // namespace canvas