#include <simgear/scene/util/parse_color.hxx>
#include <osg/Drawable>
-#include <osg/BlendFunc>
#include <vg/openvg.h>
#include <cassert>
typedef std::vector<VGubyte> CmdList;
typedef std::vector<VGfloat> CoordList;
+
+ static const VGubyte shCoordsPerCommand[] = {
+ 0, /* VG_CLOSE_PATH */
+ 2, /* VG_MOVE_TO */
+ 2, /* VG_LINE_TO */
+ 1, /* VG_HLINE_TO */
+ 1, /* VG_VLINE_TO */
+ 4, /* VG_QUAD_TO */
+ 6, /* VG_CUBIC_TO */
+ 2, /* VG_SQUAD_TO */
+ 4, /* VG_SCUBIC_TO */
+ 5, /* VG_SCCWARC_TO */
+ 5, /* VG_SCWARC_TO */
+ 5, /* VG_LCCWARC_TO */
+ 5 /* VG_LCWARC_TO */
+ };
+ static const VGubyte shNumCommands = sizeof(shCoordsPerCommand)
+ / sizeof(shCoordsPerCommand[0]);
+
/**
* Helper to split and convert comma/whitespace separated floating point
* values
*/
void setFill(const std::string& fill)
{
- if( fill == "none" )
+ if( fill.empty() || fill == "none" )
{
_mode &= ~VG_FILL_PATH;
}
*/
void setStroke(const std::string& stroke)
{
- if( stroke == "none" )
+ if( stroke.empty() || stroke == "none" )
{
_mode &= ~VG_STROKE_PATH;
}
glPopClientAttrib();
}
+ osg::BoundingBox getTransformedBounds(const osg::Matrix& mat) const
+ {
+ osg::BoundingBox bb;
+
+ osg::Vec2f cur(0, 0), // VG "Current point" (in local coordinates)
+ sub(0, 0); // beginning of current sub path
+ VGubyte cmd_index = 0;
+ for( size_t i = 0, ci = 0;
+ i < _cmds.size() && ci < _coords.size();
+ ++i, ci += shCoordsPerCommand[cmd_index] )
+ {
+ VGubyte rel = _cmds[i] & 1,
+ cmd = _cmds[i] & ~1;
+
+ cmd_index = cmd / 2;
+ if( cmd_index >= shNumCommands )
+ return osg::BoundingBox();
+
+ const VGubyte max_coords = 3;
+ osg::Vec2f points[max_coords];
+ VGubyte num_coords = 0;
+
+ switch( cmd )
+ {
+ case VG_CLOSE_PATH:
+ cur = sub;
+ break;
+ case VG_MOVE_TO:
+ case VG_LINE_TO:
+ case VG_SQUAD_TO:
+ // x0, y0
+ points[ num_coords++ ].set(_coords[ci], _coords[ci + 1]);
+ break;
+ case VG_HLINE_TO:
+ // x0
+ points[ num_coords++ ].set( _coords[ci] + (rel ? cur.x() : 0),
+ cur.y() );
+ // We have handled rel/abs already, so no need to do it again...
+ rel = 0;
+ break;
+ case VG_VLINE_TO:
+ // y0
+ points[ num_coords++ ].set( cur.x(),
+ _coords[ci] + (rel ? cur.y() : 0) );
+ // We have handled rel/abs already, so no need to do it again...
+ rel = 0;
+ break;
+ case VG_QUAD_TO:
+ case VG_SCUBIC_TO:
+ // x0,y0,x1,y1
+ points[ num_coords++ ].set(_coords[ci ], _coords[ci + 1]);
+ points[ num_coords++ ].set(_coords[ci + 2], _coords[ci + 3]);
+ break;
+ case VG_CUBIC_TO:
+ // x0,y0,x1,y1,x2,y2
+ points[ num_coords++ ].set(_coords[ci ], _coords[ci + 1]);
+ points[ num_coords++ ].set(_coords[ci + 2], _coords[ci + 3]);
+ points[ num_coords++ ].set(_coords[ci + 4], _coords[ci + 5]);
+ break;
+ case VG_SCCWARC_TO:
+ case VG_SCWARC_TO:
+ case VG_LCCWARC_TO:
+ case VG_LCWARC_TO:
+ // rh,rv,rot,x0,y0
+ points[ num_coords++ ].set(_coords[ci + 3], _coords[ci + 4]);
+ break;
+ default:
+ SG_LOG(SG_GL, SG_WARN, "Unknown VG command: " << (int)cmd);
+ return osg::BoundingBox();
+ }
+
+ assert(num_coords <= max_coords);
+ for(VGubyte i = 0; i < num_coords; ++i)
+ {
+ if( rel )
+ points[i] += cur;
+
+ bb.expandBy( transformPoint(mat, points[i]) );
+ }
+
+ if( num_coords > 0 )
+ {
+ cur = points[ num_coords - 1 ];
+
+ if( cmd == VG_MOVE_TO )
+ sub = cur;
+ }
+ }
+
+ return bb;
+ }
+
/**
* Compute the bounding box
*/
std::vector<VGfloat> _stroke_dash;
VGCapStyle _stroke_linecap;
+ osg::Vec3f transformPoint( const osg::Matrix& m,
+ osg::Vec2f pos ) const
+ {
+ return osg::Vec3
+ (
+ m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
+ m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1),
+ 0
+ );
+ }
+
/**
* Initialize/Update the OpenVG path
*/
void update()
{
+ if( !vgHasContextSH() )
+ return;
+
if( _attributes_dirty & PATH )
{
const VGbitfield caps = VG_PATH_CAPABILITY_APPEND_TO
};
};
+ //----------------------------------------------------------------------------
+ const std::string Path::TYPE_NAME = "path";
+
+ //----------------------------------------------------------------------------
+ void Path::staticInit()
+ {
+ if( isInit<Path>() )
+ return;
+
+ PathDrawableRef Path::*path = &Path::_path;
+
+ addStyle("fill", "color", &PathDrawable::setFill, path);
+ addStyle("fill-rule", "", &PathDrawable::setFillRule, path);
+ addStyle("stroke", "color", &PathDrawable::setStroke, path);
+ addStyle("stroke-width", "numeric", &PathDrawable::setStrokeWidth, path);
+ addStyle("stroke-dasharray", "", &PathDrawable::setStrokeDashArray, path);
+ addStyle("stroke-linecap", "", &PathDrawable::setStrokeLinecap, path);
+ }
+
//----------------------------------------------------------------------------
Path::Path( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
Element(canvas, node, parent_style, parent),
_path( new PathDrawable(this) )
{
- setDrawable(_path);
- PathDrawable *path = _path.get();
-
- addStyle("fill", &PathDrawable::setFill, path);
- addStyle("fill-rule", &PathDrawable::setFillRule, path);
- addStyle("stroke", &PathDrawable::setStroke, path);
- addStyle("stroke-width", &PathDrawable::setStrokeWidth, path);
- addStyle("stroke-dasharray", &PathDrawable::setStrokeDashArray, path);
- addStyle("stroke-linecap", &PathDrawable::setStrokeLinecap, path);
+ staticInit();
+ setDrawable(_path);
setupStyle();
}
Element::update(dt);
}
+ //----------------------------------------------------------------------------
+ osg::BoundingBox Path::getTransformedBounds(const osg::Matrix& m) const
+ {
+ return _path->getTransformedBounds(m);
+ }
+
+ //----------------------------------------------------------------------------
+ Path& Path::moveTo(float x_abs, float y_abs)
+ {
+ return addSegment(VG_MOVE_TO_ABS, x_abs, y_abs);
+ }
+
+ //----------------------------------------------------------------------------
+ Path& Path::move(float x_rel, float y_rel)
+ {
+ return addSegment(VG_MOVE_TO_REL, x_rel, y_rel);
+ }
+
+ //----------------------------------------------------------------------------
+ Path& Path::lineTo(float x_abs, float y_abs)
+ {
+ return addSegment(VG_LINE_TO_ABS, x_abs, y_abs);
+ }
+
+ //----------------------------------------------------------------------------
+ Path& Path::line(float x_rel, float y_rel)
+ {
+ return addSegment(VG_LINE_TO_REL, x_rel, y_rel);
+ }
+
+ //----------------------------------------------------------------------------
+ Path& Path::horizTo(float x_abs)
+ {
+ return addSegment(VG_HLINE_TO_ABS, x_abs);
+ }
+
+ //----------------------------------------------------------------------------
+ Path& Path::horiz(float x_rel)
+ {
+ return addSegment(VG_HLINE_TO_REL, x_rel);
+ }
+
+ //----------------------------------------------------------------------------
+ Path& Path::vertTo(float y_abs)
+ {
+ return addSegment(VG_VLINE_TO_ABS, y_abs);
+ }
+
+ //----------------------------------------------------------------------------
+ Path& Path::vert(float y_rel)
+ {
+ return addSegment(VG_VLINE_TO_REL, y_rel);
+ }
+
+ //----------------------------------------------------------------------------
+ Path& Path::close()
+ {
+ return addSegment(VG_CLOSE_PATH);
+ }
+
//----------------------------------------------------------------------------
void Path::childRemoved(SGPropertyNode* child)
{