[DEV] first study of rendering directly

This commit is contained in:
Edouard DUPIN 2015-11-17 21:15:40 +01:00
parent 14eea2f916
commit 402b29ca36
32 changed files with 1460 additions and 638 deletions

View File

@ -41,36 +41,36 @@ void esvg::Base::parseTransform(const std::shared_ptr<exml::Element>& _element)
const char * pointerOnData = inputString.c_str();
while (*pointerOnData) {
if (sscanf(pointerOnData, "matrix (%lf %lf %lf %lf %lf %lf) %n", &matrix[0], &matrix[1], &matrix[2], &matrix[3], &matrix[4], &matrix[5], &n) == 6) {
m_transformMatrix.load_from(matrix);
m_transformMatrix = mat2(matrix);
} else if (sscanf(pointerOnData, "translate (%f %f) %n", &xxx, &yyy, &n) == 2) {
m_transformMatrix *= agg::trans_affine_translation(xxx, yyy);
m_transformMatrix *= etk::mat2Translate(vec2(xxx, yyy));
SVG_VERBOSE("Translate : " << xxx << ", " << yyy);
} else if (sscanf(pointerOnData, "translate (%f) %n", &xxx, &n) == 1) {
m_transformMatrix *= agg::trans_affine_translation(xxx, 0);
m_transformMatrix *= etk::mat2Translate(vec2(xxx, 0));
SVG_VERBOSE("Translate : " << xxx << ", " << 0);
} else if (sscanf(pointerOnData, "scale (%f %f) %n", &xxx, &yyy, &n) == 2) {
m_transformMatrix *= agg::trans_affine_scaling(xxx, yyy);
m_transformMatrix *= etk::mat2Scale(vec2(xxx, yyy));
SVG_VERBOSE("Scale : " << xxx << ", " << yyy);
} else if (sscanf(pointerOnData, "scale (%f) %n", &xxx, &n) == 1) {
m_transformMatrix *= agg::trans_affine_scaling(xxx, xxx);
m_transformMatrix *= etk::mat2Scale(xxx);
SVG_VERBOSE("Scale : " << xxx << ", " << xxx);
} else if (sscanf(pointerOnData, "rotate (%f %f %f) %n", &angle, &xxx, &yyy, &n) == 3) {
angle = angle / 180 * M_PI;
m_transformMatrix *= agg::trans_affine_translation(-xxx, -yyy);
m_transformMatrix *= agg::trans_affine_rotation(angle);
m_transformMatrix *= agg::trans_affine_translation(xxx, yyy);
m_transformMatrix *= etk::mat2Translate(vec2(-xxx, -yyy));
m_transformMatrix *= etk::mat2Rotate(angle);
m_transformMatrix *= etk::mat2Translate(vec2(xxx, yyy));
} else if (sscanf(pointerOnData, "rotate (%f) %n", &angle, &n) == 1) {
angle = angle / 180 * M_PI;
SVG_VERBOSE("rotate : " << angle << "rad, " << (angle/M_PI*180) << "°");
m_transformMatrix *= agg::trans_affine_rotation(angle);
m_transformMatrix *= etk::mat2Rotate(angle);
} else if (sscanf(pointerOnData, "skewX (%f) %n", &angle, &n) == 1) {
angle = angle / 180 * M_PI;
SVG_VERBOSE("skewX : " << angle << "rad, " << (angle/M_PI*180) << "°");
m_transformMatrix *= agg::trans_affine_skewing(angle, 0.0);
m_transformMatrix *= etk::mat2Skew(vec2(angle, 0.0f));
} else if (sscanf(pointerOnData, "skewY (%f) %n", &angle, &n) == 1) {
angle = angle / 180 * M_PI;
SVG_VERBOSE("skewY : " << angle << "rad, " << (angle/M_PI*180) << "°");
m_transformMatrix *= agg::trans_affine_skewing(0.0, angle);
m_transformMatrix *= etk::mat2Skew(vec2(0.0f, angle));
} else {
break;
}
@ -85,7 +85,7 @@ void esvg::Base::parseTransform(const std::shared_ptr<exml::Element>& _element)
* @param[out] _pos parsed position
* @param[out] _size parsed dimention
*/
void esvg::Base::parsePosition(const std::shared_ptr<const exml::Element>& _element, etk::Vector2D<float> &_pos, etk::Vector2D<float> &_size) {
void esvg::Base::parsePosition(const std::shared_ptr<const exml::Element>& _element, vec2 &_pos, vec2 &_size) {
_pos.setValue(0,0);
_size.setValue(0,0);
@ -203,14 +203,14 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr<const exml::Element>& _ele
std::string content = _element->getAttribute("fill");
if (content.size()!=0) {
m_paint.fill = parseColor(content);
if (m_paint.fill.a == 0) {
if (m_paint.fill.a() == 0) {
fillNone = true;
}
}
content = _element->getAttribute("stroke");
if (content.size()!=0) {
m_paint.stroke = parseColor(content);
if (m_paint.stroke.a == 0) {
if (m_paint.stroke.a() == 0) {
strokeNone = true;
}
}
@ -221,21 +221,21 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr<const exml::Element>& _ele
content = _element->getAttribute("opacity");
if (content.size()!=0) {
float opacity = parseLength(content);
opacity = std::avg(0.0f, opacity, 1.0f);
m_paint.fill.a = opacity*0xFF;
m_paint.stroke.a = opacity*0xFF;
opacity = std::avg(0.0f, opacity, 1.0f);
m_paint.fill.setA(opacity*0xFF);
m_paint.stroke.setA(opacity*0xFF);
}
content = _element->getAttribute("fill-opacity");
if (content.size()!=0) {
float opacity = parseLength(content);
opacity = std::avg(0.0f, opacity, 1.0f);
m_paint.fill.a = opacity*0xFF;
m_paint.fill.setA(opacity*0xFF);
}
content = _element->getAttribute("stroke-opacity");
if (content.size()!=0) {
float opacity = parseLength(content);
opacity = std::avg(0.0f, opacity, 1.0f);
m_paint.stroke.a = opacity*0xFF;
m_paint.stroke.setA(opacity*0xFF);
}
content = _element->getAttribute("fill-rule");
if (content.size()!=0) {
@ -250,26 +250,26 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr<const exml::Element>& _ele
content = _element->getAttribute("stroke-linecap");
if (content.size()!=0) {
if (content == "butt" ) {
m_paint.lineCap = esvg::lineCapButt;
m_paint.lineCap = esvg::cap_butt;
} else if (content == "round" ) {
m_paint.lineCap = esvg::lineCapRound;
m_paint.lineCap = esvg::cap_round;
} else if (content == "square" ) {
m_paint.lineCap = esvg::lineCapSquare;
m_paint.lineCap = esvg::cap_square;
} else {
m_paint.lineCap = esvg::lineCapButt;
m_paint.lineCap = esvg::cap_butt;
SVG_ERROR("not know stroke-linecap value : \"" << content << "\", not in [butt,round,square]");
}
}
content = _element->getAttribute("stroke-linejoin");
if (content.size()!=0) {
if (content == "miter" ) {
m_paint.lineJoin = esvg::lineJoinMiter;
m_paint.lineJoin = esvg::join_miter;
} else if (content == "round" ) {
m_paint.lineJoin = esvg::lineJoinRound;
m_paint.lineJoin = esvg::join_round;
} else if (content == "bevel" ) {
m_paint.lineJoin = esvg::lineJoinBevel;
m_paint.lineJoin = esvg::join_bevel;
} else {
m_paint.lineJoin = esvg::lineJoinMiter;
m_paint.lineJoin = esvg::join_miter;
SVG_ERROR("not know stroke-linejoin value : \"" << content << "\", not in [miter,round,bevel]");
}
}
@ -285,13 +285,13 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr<const exml::Element>& _ele
if (outputType == "fill") {
m_paint.fill = parseColor(outputValue);
SVG_VERBOSE(" input : \"" << outputValue << "\" == > " << m_paint.fill);
if (m_paint.fill.a == 0) {
if (m_paint.fill.a() == 0) {
fillNone = true;
}
} else if (outputType == "stroke") {
m_paint.stroke = parseColor(outputValue);
SVG_VERBOSE(" input : \"" << outputValue << "\" == > " << m_paint.stroke);
if (m_paint.stroke.a == 0) {
if (m_paint.stroke.a() == 0) {
strokeNone = true;
}
} else if (outputType == "stroke-width" ) {
@ -300,18 +300,18 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr<const exml::Element>& _ele
} else if (outputType == "opacity" ) {
float opacity = parseLength(outputValue);
opacity = std::avg(0.0f, opacity, 1.0f);
m_paint.fill.a = opacity*0xFF;
m_paint.stroke.a = opacity*0xFF;
m_paint.fill.setA(opacity*0xFF);
m_paint.stroke.setA(opacity*0xFF);
SVG_VERBOSE(" input : \"" << outputValue << "\" == > " << m_paint.fill);
} else if (outputType == "fill-opacity") {
float opacity = parseLength(outputValue);
opacity = std::avg(0.0f, opacity, 1.0f);
m_paint.fill.a = opacity*0xFF;
m_paint.fill.setA(opacity*0xFF);
SVG_VERBOSE(" input : \"" << outputValue << "\" == > " << m_paint.fill);
} else if (outputType == "stroke-opacity") {
float opacity = parseLength(outputValue);
opacity = std::avg(0.0f, opacity, 1.0f);
m_paint.stroke.a = opacity*0xFF;
m_paint.stroke.setA(opacity*0xFF);
SVG_VERBOSE(" input : \"" << outputValue << "\" == > " << m_paint.stroke);
} else if (outputType == "fill-rule" ) {
if (outputValue == "nonzero" ) {
@ -323,24 +323,24 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr<const exml::Element>& _ele
}
} else if (outputType == "stroke-linecap") {
if (outputValue == "butt") {
m_paint.lineCap = esvg::lineCapButt;
m_paint.lineCap = esvg::cap_butt;
} else if (outputValue == "round") {
m_paint.lineCap = esvg::lineCapRound;
m_paint.lineCap = esvg::cap_round;
} else if (outputValue == "square") {
m_paint.lineCap = esvg::lineCapSquare;
m_paint.lineCap = esvg::cap_square;
} else {
m_paint.lineCap = esvg::lineCapButt;
m_paint.lineCap = esvg::cap_butt;
SVG_ERROR("not know " << outputType << " value : \"" << outputValue << "\", not in [butt,round,square]");
}
} else if (outputType == "stroke-linejoin") {
if (outputValue == "miter") {
m_paint.lineJoin = esvg::lineJoinMiter;
m_paint.lineJoin = esvg::join_miter;
} else if (outputValue == "round") {
m_paint.lineJoin = esvg::lineJoinRound;
m_paint.lineJoin = esvg::join_round;
} else if (outputValue == "bevel") {
m_paint.lineJoin = esvg::lineJoinBevel;
m_paint.lineJoin = esvg::join_bevel;
} else {
m_paint.lineJoin = esvg::lineJoinMiter;
m_paint.lineJoin = esvg::join_miter;
SVG_ERROR("not know " << outputType << " value : \"" << outputValue << "\", not in [miter,round,bevel]");
}
} else if (outputType == "marker-start") {
@ -352,10 +352,10 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr<const exml::Element>& _ele
}
// check if somewere none is set to the filling:
if (true == fillNone) {
m_paint.fill.a = 0;
m_paint.fill.setA(0);
}
if (true == strokeNone) {
m_paint.stroke.a = 0;
m_paint.stroke.setA(0);
}
}
@ -364,8 +364,8 @@ void esvg::Base::parsePaintAttr(const std::shared_ptr<const exml::Element>& _ele
* @param[in] _inputData Data C String with the xml definition
* @return the parsed color
*/
draw::Color esvg::Base::parseColor(const std::string& _inputData) {
draw::Color localColor = draw::color::white;
etk::Color<uint8_t,4> esvg::Base::parseColor(const std::string& _inputData) {
etk::Color<uint8_t,4> localColor = etk::color::white;
if( _inputData.size() > 4
&& _inputData[0] == 'u'
@ -377,7 +377,7 @@ draw::Color esvg::Base::parseColor(const std::string& _inputData) {
}
SVG_ERROR(" pb in parsing the color : \"" << _inputData << "\" == > url(XXX) is not supported now ...");
} else {
localColor = _inputData.c_str();
localColor = etk::Color<uint8_t,4>(_inputData);
}
SVG_VERBOSE("Parse color : \"" << _inputData << "\" == > " << localColor);
return localColor;
@ -389,7 +389,7 @@ draw::Color esvg::Base::parseColor(const std::string& _inputData) {
* @param[in] _element standart XML node
* @return true if no problem arrived
*/
bool esvg::Base::parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax) {
bool esvg::Base::parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax) {
SVG_ERROR("NOT IMPLEMENTED");
_sizeMax.setValue(0,0);
return false;
@ -404,3 +404,6 @@ const char * esvg::Base::spacingDist(int32_t _spacing) {
return tmpValue + 20*4 - _spacing*4;
}
void esvg::Base::aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level) {
SVG_WARNING(spacingDist(_level) << "DRAW esvg::Base ... ==> No drawing availlable");
}

View File

@ -12,42 +12,73 @@
#include <etk/types.h>
#include <vector>
#include <etk/math/Vector2D.h>
#include <draw/Color.h>
#include <etk/math/Matrix2.h>
#include <etk/Color.h>
#include <exml/exml.h>
#include <esvg/Renderer.h>
#include <agg/agg_basics.h>
#include <agg/agg_rendering_buffer.h>
#include <agg/agg_rasterizer_scanline_aa.h>
#include <agg/agg_scanline_p.h>
#include <agg/agg_renderer_scanline.h>
#include <agg/agg_path_storage.h>
#include <agg/agg_conv_transform.h>
#include <agg/agg_bounding_rect.h>
#include <agg/agg_color_rgba.h>
#include <agg/agg_pixfmt_rgba.h>
namespace esvg {
/**
* @brief Painting mode of the Object:
*/
enum paint {
paint_none, //!< No painting.
paint_color, //!< Painting a color.
paint_gradientLinear, //!< Painting a linear gradient.
paint_gradientRadial //!< Painting a radial gradient.
};
/**
* @brief Indicates what happens if the gradient starts or ends inside the bounds of the target rectangle.
*/
enum spread {
spread_pad, //!< 'pad' spread.
spread_reflect, //!< 'reflect' spread.
spread_repead, //!< 'repead' spread.
};
enum cap {
cap_butt,
cap_round,
cap_square
};
enum join {
join_miter,
join_round,
join_bevel
};
class PaintState {
public:
etk::Color<uint8_t,4> fill;
etk::Color<uint8_t,4> stroke;
float strokeWidth;
bool flagEvenOdd; //!< Fill rules
enum esvg::cap lineCap;
enum esvg::join lineJoin;
vec2 viewPort;
};
class Base {
protected:
PaintState m_paint;
agg::trans_affine m_transformMatrix; //!< specific render of the curent element
mat2 m_transformMatrix; //!< specific render of the curent element
const char * spacingDist(int32_t _spacing);
public:
Base() {};
Base(PaintState _parentPaintState);
virtual ~Base() { };
virtual bool parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax);
virtual bool parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax);
//specific drawing for AAG librairy ...
virtual void aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans) { };
virtual void aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level=1);
virtual void display(int32_t _spacing) { };
void parseTransform(const std::shared_ptr<exml::Element>& _element);
void parsePosition(const std::shared_ptr<const exml::Element>& _element, etk::Vector2D<float> &_pos, etk::Vector2D<float> &_size);
void parsePosition(const std::shared_ptr<const exml::Element>& _element, vec2 &_pos, vec2 &_size);
float parseLength(const std::string& _dataInput);
void parsePaintAttr(const std::shared_ptr<const exml::Element>& _element);
draw::Color parseColor(const std::string& _inputData);
etk::Color<uint8_t,4> parseColor(const std::string& _inputData);
};
};

View File

@ -8,8 +8,6 @@
#include <esvg/debug.h>
#include <esvg/Circle.h>
#include <agg/agg_conv_stroke.h>
#include <agg/agg_ellipse.h>
#undef __class__
#define __class__ "Circle"
@ -22,7 +20,7 @@ esvg::Circle::~Circle() {
}
bool esvg::Circle::parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax) {
bool esvg::Circle::parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax) {
m_radius = 0.0;
m_position.setValue(0,0);
if (_element == nullptr) {
@ -58,27 +56,27 @@ bool esvg::Circle::parse(const std::shared_ptr<exml::Element>& _element, agg::tr
return true;
}
void esvg::Circle::display(int32_t _spacing)
{
void esvg::Circle::display(int32_t _spacing) {
SVG_DEBUG(spacingDist(_spacing) << "Circle " << m_position << " radius=" << m_radius);
}
void esvg::Circle::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans)
{
void esvg::Circle::aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level) {
SVG_VERBOSE(spacingDist(_level) << "DRAW esvg::Circle");
/*
_myRenderer.m_renderArea->color(agg::rgba8(m_paint.fill.r, m_paint.fill.g, m_paint.fill.b, m_paint.fill.a));
// Creating an ellipse
agg::ellipse myCircle(m_position.x(), m_position.y(), m_radius, m_radius, 0);
// Calculate transformation matrix ...
agg::trans_affine mtx = m_transformMatrix;
mat2 mtx = m_transformMatrix;
mtx *= _basicTrans;
// set the filling mode :
_myRenderer.m_rasterizer.filling_rule((m_paint.flagEvenOdd)?agg::fill_even_odd:agg::fill_non_zero);
if (m_paint.fill.a != 0x00) {
agg::conv_transform<agg::ellipse, agg::trans_affine> trans(myCircle, mtx);
agg::conv_transform<agg::ellipse, mat2> trans(myCircle, mtx);
_myRenderer.m_rasterizer.add_path(trans);
agg::render_scanlines(_myRenderer.m_rasterizer, _myRenderer.m_scanLine, *_myRenderer.m_renderArea);
}
@ -88,12 +86,12 @@ void esvg::Circle::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basi
// drawing as an outline
agg::conv_stroke<agg::ellipse> myCircleStroke(myCircle);
myCircleStroke.width(m_paint.strokeWidth);
agg::conv_transform<agg::conv_stroke<agg::ellipse>, agg::trans_affine> transStroke(myCircleStroke, mtx);
agg::conv_transform<agg::conv_stroke<agg::ellipse>, mat2> transStroke(myCircleStroke, mtx);
// set the filling mode :
_myRenderer.m_rasterizer.filling_rule(agg::fill_non_zero);
_myRenderer.m_rasterizer.add_path(transStroke);
agg::render_scanlines(_myRenderer.m_rasterizer, _myRenderer.m_scanLine, *_myRenderer.m_renderArea);
}
*/
}

View File

@ -14,14 +14,14 @@
namespace esvg {
class Circle : public esvg::Base {
private:
etk::Vector2D<float> m_position; //!< Position of the Circle
vec2 m_position; //!< Position of the Circle
float m_radius; //!< Radius of the Circle
public:
Circle(PaintState _parentPaintState);
~Circle();
virtual bool parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax);
virtual bool parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax);
virtual void display(int32_t _spacing);
virtual void aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans);
virtual void aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level);
};
};

View File

@ -8,8 +8,6 @@
#include <esvg/debug.h>
#include <esvg/Ellipse.h>
#include <agg/agg_conv_stroke.h>
#include <agg/agg_ellipse.h>
#undef __class__
#define __class__ "Ellipse"
@ -22,7 +20,7 @@ esvg::Ellipse::~Ellipse() {
}
bool esvg::Ellipse::parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax) {
bool esvg::Ellipse::parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax) {
if (_element == nullptr) {
return false;
}
@ -67,20 +65,22 @@ void esvg::Ellipse::display(int32_t _spacing) {
}
void esvg::Ellipse::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans) {
void esvg::Ellipse::aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level) {
SVG_VERBOSE(spacingDist(_level) << "DRAW esvg::Ellipse");
/*
_myRenderer.m_renderArea->color(agg::rgba8(m_paint.fill.r, m_paint.fill.g, m_paint.fill.b, m_paint.fill.a));
// Creating an ellipse
agg::ellipse myEllipse(m_c.x(), m_c.y(), m_r.x(), m_r.y(), 0);
// Calculate transformation matrix ...
agg::trans_affine mtx = m_transformMatrix;
mat2 mtx = m_transformMatrix;
mtx *= _basicTrans;
// set the filling mode :
_myRenderer.m_rasterizer.filling_rule((m_paint.flagEvenOdd)?agg::fill_even_odd:agg::fill_non_zero);
if (m_paint.fill.a != 0x00) {
agg::conv_transform<agg::ellipse, agg::trans_affine> trans(myEllipse, mtx);
agg::conv_transform<agg::ellipse, mat2> trans(myEllipse, mtx);
_myRenderer.m_rasterizer.add_path(trans);
agg::render_scanlines(_myRenderer.m_rasterizer, _myRenderer.m_scanLine, *_myRenderer.m_renderArea);
}
@ -89,12 +89,13 @@ void esvg::Ellipse::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _bas
// drawing as an outline
agg::conv_stroke<agg::ellipse> myEllipseStroke(myEllipse);
myEllipseStroke.width(m_paint.strokeWidth);
agg::conv_transform<agg::conv_stroke<agg::ellipse>, agg::trans_affine> transStroke(myEllipseStroke, mtx);
agg::conv_transform<agg::conv_stroke<agg::ellipse>, mat2> transStroke(myEllipseStroke, mtx);
// set the filling mode :
_myRenderer.m_rasterizer.filling_rule(agg::fill_non_zero);
_myRenderer.m_rasterizer.add_path(transStroke);
agg::render_scanlines(_myRenderer.m_rasterizer, _myRenderer.m_scanLine, *_myRenderer.m_renderArea);
}
*/
}

View File

@ -14,14 +14,14 @@
namespace esvg {
class Ellipse : public esvg::Base {
private:
etk::Vector2D<float> m_c; //!< Center property of the ellipse
etk::Vector2D<float> m_r; //!< Radius property of the ellipse
vec2 m_c; //!< Center property of the ellipse
vec2 m_r; //!< Radius property of the ellipse
public:
Ellipse(PaintState _parentPaintState);
~Ellipse();
virtual bool parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax);
virtual bool parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax);
virtual void display(int32_t _spacing);
virtual void aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans);
virtual void aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level);
};
};

View File

@ -31,22 +31,22 @@ esvg::Group::~Group() {
}
bool esvg::Group::parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax) {
bool esvg::Group::parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax) {
if (_element == nullptr) {
return false;
}
// parse ...
etk::Vector2D<float> pos(0,0);
etk::Vector2D<float> size(0,0);
vec2 pos(0,0);
vec2 size(0,0);
parseTransform(_element);
parsePosition(_element, pos, size);
parsePaintAttr(_element);
SVG_VERBOSE("parsed G1. trans : (" << m_transformMatrix.sx << "," << m_transformMatrix.shy << "," << m_transformMatrix.shx << "," << m_transformMatrix.sy << "," << m_transformMatrix.tx << "," << m_transformMatrix.ty << ")");
SVG_VERBOSE("parsed G1. trans : " << m_transformMatrix);
// add the property of the parrent modifications ...
m_transformMatrix *= _parentTrans;
SVG_VERBOSE("parsed G2. trans : (" << m_transformMatrix.sx << "," << m_transformMatrix.shy << "," << m_transformMatrix.shx << "," << m_transformMatrix.sy << "," << m_transformMatrix.tx << "," << m_transformMatrix.ty << ")");
SVG_VERBOSE("parsed G2. trans : " << m_transformMatrix);
_sizeMax.setValue(0,0);
vec2 tmpPos(0,0);
@ -109,11 +109,11 @@ void esvg::Group::display(int32_t _spacing) {
SVG_DEBUG(spacingDist(_spacing) << "Group (STOP)");
}
void esvg::Group::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans)
{
void esvg::Group::aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level) {
SVG_VERBOSE(spacingDist(_level) << "DRAW esvg::group");
for (int32_t iii=0; iii<m_subElementList.size(); iii++) {
if (NULL != m_subElementList[iii]) {
m_subElementList[iii]->aggDraw(_myRenderer, _basicTrans);
m_subElementList[iii]->aggDraw(_myRenderer, _basicTrans, _level+1);
}
}
}

View File

@ -19,9 +19,9 @@ namespace esvg {
public:
Group(PaintState _parentPaintState);
~Group();
virtual bool parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax);
virtual bool parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax);
virtual void display(int32_t spacing);
virtual void aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans);
virtual void aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level);
};
};

View File

@ -8,8 +8,6 @@
#include <esvg/debug.h>
#include <esvg/Line.h>
#include <agg/agg_conv_stroke.h>
#include <agg/agg_path_storage.h>
#undef __class__
#define __class__ "Line"
@ -23,7 +21,7 @@ esvg::Line::~Line() {
}
bool esvg::Line::parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax) {
bool esvg::Line::parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax) {
// line must have a minimum size...
m_paint.strokeWidth = 1;
if (_element == nullptr) {
@ -60,8 +58,10 @@ void esvg::Line::display(int32_t _spacing) {
SVG_DEBUG(spacingDist(_spacing) << "Line " << m_startPos << " to " << m_stopPos);
}
void esvg::Line::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans) {
agg::path_storage path;
void esvg::Line::aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level) {
SVG_VERBOSE(spacingDist(_level) << "DRAW esvg::Line");
# if 0
esvg::RenderPath path;
path.start_new_path();
path.move_to(m_startPos.x(), m_startPos.y());
path.line_to(m_stopPos.x(), m_stopPos.y());
@ -90,20 +90,21 @@ void esvg::Line::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicT
break;
}
*/
agg::trans_affine mtx = m_transformMatrix;
mat2 mtx = m_transformMatrix;
mtx *= _basicTrans;
if (m_paint.strokeWidth > 0) {
_myRenderer.m_renderArea->color(agg::rgba8(m_paint.stroke.r, m_paint.stroke.g, m_paint.stroke.b, m_paint.stroke.a));
// drawing as an outline
agg::conv_stroke<agg::path_storage> myPolygonStroke(path);
agg::conv_stroke<esvg::RenderPath> myPolygonStroke(path);
myPolygonStroke.width(m_paint.strokeWidth);
agg::conv_transform<agg::conv_stroke<agg::path_storage>, agg::trans_affine> transStroke(myPolygonStroke, mtx);
agg::conv_transform<agg::conv_stroke<esvg::RenderPath>, mat2> transStroke(myPolygonStroke, mtx);
// set the filling mode :
_myRenderer.m_rasterizer.filling_rule(agg::fill_non_zero);
_myRenderer.m_rasterizer.add_path(transStroke);
agg::render_scanlines(_myRenderer.m_rasterizer, _myRenderer.m_scanLine, *_myRenderer.m_renderArea);
}
#endif
}

View File

@ -14,14 +14,14 @@
namespace esvg {
class Line : public esvg::Base {
private:
etk::Vector2D<float> m_startPos; //!< Start line position
etk::Vector2D<float> m_stopPos; //!< Stop line position
vec2 m_startPos; //!< Start line position
vec2 m_stopPos; //!< Stop line position
public:
Line(PaintState _parentPaintState);
~Line();
virtual bool parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax);
virtual bool parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax);
virtual void display(int32_t _spacing);
virtual void aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans);
virtual void aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level);
};
};

View File

@ -8,11 +8,6 @@
#include <esvg/debug.h>
#include <esvg/Path.h>
#include <agg/agg_conv_stroke.h>
#include <agg/agg_conv_dash.h>
#include <agg/agg_conv_curve.h>
#include <agg/agg_conv_contour.h>
#include <agg/agg_conv_smooth_poly1.h>
#undef __class__
#define __class__ "Path"
@ -65,7 +60,7 @@ const char * extractCmd(const char* _input, char& _cmd, std::vector<float>& _out
return outputPointer;
}
bool esvg::Path::parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax) {
bool esvg::Path::parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax) {
if (_element == nullptr) {
return false;
}
@ -117,13 +112,13 @@ bool esvg::Path::parse(const std::shared_ptr<exml::Element>& _element, agg::tran
SVG_WARNING("the PATH command "<< command << " has not the good number of element = " << listDot.size() );
break;
}
pathElement.m_cmd = esvg::pathMoveTo;
pathElement.m_cmd = esvg::path_moveTo;
if (listDot.size() >= 2) {
pathElement.m_element[0] = listDot[0];
pathElement.m_element[1] = listDot[1];
m_listElement.push_back(pathElement);
}
pathElement.m_cmd = esvg::pathLineTo;
pathElement.m_cmd = esvg::path_lineTo;
for(int32_t iii=2; iii<listDot.size(); iii+=2) {
pathElement.m_element[0] = listDot[iii];
pathElement.m_element[1] = listDot[iii+1];
@ -138,7 +133,7 @@ bool esvg::Path::parse(const std::shared_ptr<exml::Element>& _element, agg::tran
SVG_WARNING("the PATH command "<< command << " has not the good number of element = " << listDot.size() );
break;
}
pathElement.m_cmd = esvg::pathLineTo;
pathElement.m_cmd = esvg::path_lineTo;
for(int32_t iii=0; iii<listDot.size(); iii+=2) {
pathElement.m_element[0] = listDot[iii];
pathElement.m_element[1] = listDot[iii+1];
@ -153,7 +148,7 @@ bool esvg::Path::parse(const std::shared_ptr<exml::Element>& _element, agg::tran
SVG_WARNING("the PATH command "<< command << " has not the good number of element = " << listDot.size() );
break;
}
pathElement.m_cmd = esvg::pathLineToV;
pathElement.m_cmd = esvg::path_lineToV;
for(int32_t iii=0; iii<listDot.size(); iii+=1) {
pathElement.m_element[0] = listDot[iii];
m_listElement.push_back(pathElement);
@ -167,7 +162,7 @@ bool esvg::Path::parse(const std::shared_ptr<exml::Element>& _element, agg::tran
SVG_WARNING("the PATH command "<< command << " has not the good number of element = " << listDot.size() );
break;
}
pathElement.m_cmd = esvg::pathLineToH;
pathElement.m_cmd = esvg::path_lineToH;
for(int32_t iii=0; iii<listDot.size(); iii+=1) {
pathElement.m_element[0] = listDot[iii];
m_listElement.push_back(pathElement);
@ -181,7 +176,7 @@ bool esvg::Path::parse(const std::shared_ptr<exml::Element>& _element, agg::tran
SVG_WARNING("the PATH command "<< command << " has not the good number of element = " << listDot.size() );
break;
}
pathElement.m_cmd = esvg::pathBesizeCurveTo;
pathElement.m_cmd = esvg::path_bezierCurveTo;
for(int32_t iii=0; iii<listDot.size(); iii+=4) {
pathElement.m_element[0] = listDot[iii];
pathElement.m_element[1] = listDot[iii+1];
@ -198,7 +193,7 @@ bool esvg::Path::parse(const std::shared_ptr<exml::Element>& _element, agg::tran
SVG_WARNING("the PATH command "<< command << " has not the good number of element = " << listDot.size() );
break;
}
pathElement.m_cmd = esvg::pathBesizeSmothCurveTo;
pathElement.m_cmd = esvg::path_bezierSmothCurveTo;
for(int32_t iii=0; iii<listDot.size(); iii+=2) {
pathElement.m_element[0] = listDot[iii];
pathElement.m_element[1] = listDot[iii+1];
@ -213,7 +208,7 @@ bool esvg::Path::parse(const std::shared_ptr<exml::Element>& _element, agg::tran
SVG_WARNING("the PATH command "<< command << " has not the good number of element = " << listDot.size() );
break;
}
pathElement.m_cmd = esvg::pathCurveTo;
pathElement.m_cmd = path_curveTo;
for(int32_t iii=0; iii<listDot.size(); iii+=6) {
pathElement.m_element[0] = listDot[iii];
pathElement.m_element[1] = listDot[iii+1];
@ -232,7 +227,7 @@ bool esvg::Path::parse(const std::shared_ptr<exml::Element>& _element, agg::tran
SVG_WARNING("the PATH command "<< command << " has not the good number of element = " << listDot.size() );
break;
}
pathElement.m_cmd = esvg::pathSmothCurveTo;
pathElement.m_cmd = esvg::path_smothCurveTo;
for(int32_t iii=0; iii<listDot.size(); iii+=4) {
pathElement.m_element[0] = listDot[iii];
pathElement.m_element[1] = listDot[iii+1];
@ -249,7 +244,7 @@ bool esvg::Path::parse(const std::shared_ptr<exml::Element>& _element, agg::tran
SVG_WARNING("the PATH command "<< command << " has not the good number of element = " << listDot.size() );
break;
}
pathElement.m_cmd = esvg::pathElliptic;
pathElement.m_cmd = esvg::path_elliptic;
for(int32_t iii=0; iii<listDot.size(); iii+=7) {
pathElement.m_element[0] = listDot[iii];
pathElement.m_element[1] = listDot[iii+1];
@ -268,7 +263,7 @@ bool esvg::Path::parse(const std::shared_ptr<exml::Element>& _element, agg::tran
SVG_WARNING("the PATH command "<< command << " has not the good number of element = " << listDot.size() );
break;
}
pathElement.m_cmd = esvg::pathStop;
pathElement.m_cmd = esvg::path_stop;
m_listElement.push_back(pathElement);
break;
default:
@ -283,22 +278,22 @@ void esvg::Path::display(int32_t _spacing) {
SVG_DEBUG(spacingDist(_spacing) << "Path");
for(int32_t iii=0; iii<m_listElement.size(); iii++) {
switch (m_listElement[iii].m_cmd) {
case esvg::pathStop:
case esvg::path_stop:
SVG_DEBUG(spacingDist(_spacing+4) << "STOP");
break;
case esvg::pathMoveTo:
case esvg::path_moveTo:
SVG_DEBUG(spacingDist(_spacing+4) << "MOVETO (" << m_listElement[iii].m_element[0] << "," << m_listElement[iii].m_element[1] << ")" );
break;
case esvg::pathLineTo:
case esvg::path_lineTo:
SVG_DEBUG(spacingDist(_spacing+4) << "LINETO (" << m_listElement[iii].m_element[0] << "," << m_listElement[iii].m_element[1] << ")" );
break;
case esvg::pathLineToH:
case esvg::path_lineToH:
SVG_DEBUG(spacingDist(_spacing+4) << "LINETO_H (" << m_listElement[iii].m_element[0] << ")" );
break;
case esvg::pathLineToV:
case esvg::path_lineToV:
SVG_DEBUG(spacingDist(_spacing+4) << "LINETO_V (" << m_listElement[iii].m_element[0] << ")" );
break;
case esvg::pathCurveTo:
case esvg::path_curveTo:
SVG_DEBUG(spacingDist(_spacing+4) << "CURVETO (" << m_listElement[iii].m_element[0] <<
"," << m_listElement[iii].m_element[1] <<
"," << m_listElement[iii].m_element[2] <<
@ -306,23 +301,29 @@ void esvg::Path::display(int32_t _spacing) {
"," << m_listElement[iii].m_element[4] <<
"," << m_listElement[iii].m_element[5] << ")" );
break;
case esvg::pathSmothCurveTo:
case esvg::path_smothCurveTo:
SVG_DEBUG(spacingDist(_spacing+4) << "SMOTH_CURVETO (" << m_listElement[iii].m_element[0] <<
"," << m_listElement[iii].m_element[1] <<
"," << m_listElement[iii].m_element[2] <<
"," << m_listElement[iii].m_element[3] << ")" );
break;
case esvg::pathBesizeCurveTo:
case esvg::path_bezierCurveTo:
SVG_DEBUG(spacingDist(_spacing+4) << "BEZIER_CURVETO (" << m_listElement[iii].m_element[0] <<
"," << m_listElement[iii].m_element[1] <<
"," << m_listElement[iii].m_element[2] <<
"," << m_listElement[iii].m_element[3] << ")" );
break;
case esvg::pathBesizeSmothCurveTo:
case esvg::path_bezierSmothCurveTo:
SVG_DEBUG(spacingDist(_spacing+4) << "BEZIER_SMOTH_CURVETO (" << m_listElement[iii].m_element[0] << "," << m_listElement[iii].m_element[1] << ")" );
break;
case esvg::pathElliptic:
SVG_DEBUG(spacingDist(_spacing+4) << "ELLIPTIC (TODO...)" );
case esvg::path_elliptic:
SVG_DEBUG(spacingDist(_spacing+4) << "ELLIPTIC (" << m_listElement[iii].m_element[0] <<
"," << m_listElement[iii].m_element[1] <<
"," << m_listElement[iii].m_element[2] <<
"," << m_listElement[iii].m_element[3] <<
"," << m_listElement[iii].m_element[4] <<
"," << m_listElement[iii].m_element[5] <<
"," << m_listElement[iii].m_element[6] << ")" );
// show explanation at : http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
break;
default:
@ -332,70 +333,560 @@ void esvg::Path::display(int32_t _spacing) {
}
}
void esvg::Path::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans) {
_myRenderer.m_renderArea->color(agg::rgba8(m_paint.fill.r, m_paint.fill.g, m_paint.fill.b, m_paint.fill.a));
agg::path_storage path;
path.start_new_path();
class Segment {
public:
Segment(const vec2& _p0, const vec2& _p1) {
// segment register all time the lower at P0n then we need to register the sens of the path
if (_p0.y() < _p1.y()) {
p0 = _p0;
p1 = _p1;
direction = 1.0f; // direction like clock
} else {
p0 = _p1;
p1 = _p0;
direction = -1.0f; // direction like anti-clock
}
}
vec2 p0;
vec2 p1;
float direction;
};
class Scanline {
private:
std::vector<float> m_data;
public:
// constructor :
Scanline(size_t _size=32) {
float tmp(0);
m_data.resize(_size, tmp);
}
// destructor
~Scanline() { };
// -----------------------------------------------
// -- basic tools :
// -----------------------------------------------
public:
size_t size() const {
return m_data.size();
};
void clear(float _fill) {
for (auto &it : m_data) {
it = _fill;
}
}
float get(int32_t _pos) const {
if( _pos>=0
&& _pos<m_data.size()) {
return m_data[_pos];
}
return 0;
}
void set(int32_t _pos, float _newColor) {
if( _pos>=0
&& _pos<m_data.size()) {
m_data[_pos] = _newColor;
}
}
};
class Weighter {
private:
ivec2 m_size;
std::vector<float> m_data;
public:
// constructor :
Weighter(const ivec2& _size=ivec2(32,32)):
m_size(_size) {
float tmp(0);
m_data.resize(m_size.x()*m_size.y(), tmp);
if ((uint32_t)m_size.x()*m_size.y() > m_data.size()) {
return;
}
}
// destructor
~Weighter() { };
// -----------------------------------------------
// -- basic tools :
// -----------------------------------------------
public:
const ivec2& getSize() const {
return m_size;
};
int32_t getWidth() const {
return m_size.x();
};
int32_t getHeight() const {
return m_size.y();
};
void clear(float _fill) {
for (int32_t iii=0; iii<m_size.x()*m_size.y(); iii++) {
m_data[iii] = _fill;
}
}
float get(const ivec2& _pos) const {
if ( _pos.x()>0 && _pos.x()<m_size.x()
&& _pos.y()>0 && _pos.y()<m_size.y()) {
return m_data[_pos.x()+_pos.y()*m_size.x()];
}
return 0;
}
void set(const ivec2& _pos, float _newColor) {
if ( _pos.x()>=0 && _pos.x()<m_size.x()
&& _pos.y()>=0 && _pos.y()<m_size.y()) {
m_data[_pos.x()+_pos.y()*m_size.x()] = _newColor;
}
}
void set(int32_t _posY, const Scanline& _data) {
if ( _posY>=0
&& _posY<m_size.y()) {
for (size_t xxx=0; xxx<m_size.x(); ++xxx) {
m_data[xxx+_posY*m_size.x()] = _data.get(xxx);
}
}
}
void append(int32_t _posY, const Scanline& _data) {
if ( _posY>=0
&& _posY<m_size.y()) {
for (size_t xxx=0; xxx<m_size.x(); ++xxx) {
m_data[xxx+_posY*m_size.x()] += _data.get(xxx);
}
}
}
};
bool sortSegmentFunction(const Segment& _e1, const Segment& _e2) {
return _e1.p0.y() < _e2.p0.y();
}
bool sortXPosFunction(const std::pair<float,float>& _e1, const std::pair<float,float>& _e2) {
return _e1.first < _e2.first;
}
class PointRender {
public:
enum typePoint {
typePoint_single, //!< Point type is single, this mean that it start and stop of a path
typePoint_start, //!< Point type is starting of a path
typePoint_stop, //!< Point type is stoping of a path
typePoint_join, //!< Point type in an user point provided inside a path
typePoint_interpolation, //!< This point is dynamicly calculated to create an interpolation
};
public:
vec2 m_pos; //!< position of the point
enum typePoint m_type;
vec2 m_miterAxe;
vec2 m_delta;
float m_len;
PointRender(const vec2& _pos, enum typePoint _type = PointRender::typePoint_join) :
m_pos(_pos),
m_type(_type) {
// nothing to do ...
}
void normalize(const vec2& _nextPoint) {
m_delta = _nextPoint - m_pos;
m_len = m_delta.length();
}
};
class Transformation {
public:
std::vector<PointRender> m_listPoints;
std::vector<Segment> m_listSegment;
float m_threshold;
int32_t m_recursionMax;
int32_t m_subSamplingCount;
public:
Transformation() :
m_threshold(0.25f),
m_recursionMax(10),
m_subSamplingCount(8) {
}
void addSegment(const PointRender& _pos0, const PointRender& _pos1) {
// Skip horizontal Segments
if (_pos0.m_pos.y() == _pos1.m_pos.y()) {
// remove /0 operation
return;
}
m_listSegment.push_back(Segment(_pos0.m_pos, _pos1.m_pos));
}
void flattenCubicBez(vec2 _pos1,
vec2 _pos2,
vec2 _pos3,
vec2 _pos4,
int32_t _level,
enum PointRender::typePoint _type) {
if (_level > m_recursionMax) {
return;
}
vec2 pos12 = (_pos1+_pos2)*0.5f;
vec2 pos23 = (_pos2+_pos3)*0.5f;
vec2 pos34 = (_pos3+_pos4)*0.5f;
vec2 delta = _pos4 - _pos1;
float distance2 = std::abs(((_pos2.x() - _pos4.x()) * delta.y() - (_pos2.y() - _pos4.y()) * delta.x() ));
float distance3 = std::abs(((_pos3.x() - _pos4.x()) * delta.y() - (_pos3.y() - _pos4.y()) * delta.x() ));
if ((distance2 + distance3)*(distance2 + distance3) < m_threshold * delta.length2()) {
m_listPoints.push_back(PointRender(_pos4, _type) );
return;
}
vec2 pos123 = (pos12+pos23)*0.5f;
vec2 pos234 = (pos23+pos34)*0.5f;
vec2 pos1234 = (pos123+pos234)*0.5f;
flattenCubicBez(_pos1, pos12, pos123, pos1234, _level+1, PointRender::typePoint_interpolation);
flattenCubicBez(pos1234, pos234, pos34, _pos4, _level+1, _type);
}
void flattenShape(const esvg::RenderPath& _path, const mat2& _matrix) {
// Flatten path
m_listPoints.push_back(PointRender(_path.m_points[0], PointRender::typePoint_join));
for (int32_t iii=0;
iii<_path.m_points.size()-1;
iii+=3) {
flattenCubicBez(_path.m_points[iii+0], _path.m_points[iii+1], _path.m_points[iii+2], _path.m_points[iii+3], 0, PointRender::typePoint_interpolation);
}
// Close path (all time in a background element)
m_listPoints.push_back(PointRender(_path.m_points[0], PointRender::typePoint_join));
// Build Segments
for (int32_t iii=0, jjj=m_listPoints.size()-1;
iii < m_listPoints.size();
jjj = iii++) {
addSegment(m_listPoints[jjj], m_listPoints[iii]);
}
}
void flattenShapeStroke(const esvg::RenderPath& _path, const mat2& _matrix) {
// Flatten path
m_listPoints.push_back(PointRender(_path.m_points[0], PointRender::typePoint_start));
for (int32_t iii=0;
iii<_path.m_points.size()-1;
iii+=3) {
flattenCubicBez(_path.m_points[iii+0], _path.m_points[iii+1], _path.m_points[iii+2], _path.m_points[iii+3], 0, PointRender::typePoint_join);
}
// When we stroke , We need to have a minimum of 2 points:
if (_path.m_points.size() < 2) {
// no stroke ...
return;
}
// generate for every point all the orthogonal elements
// normal edge * end path
// * | * * * * * * * * * * * * * *
// * |<--*----this | *
// * | * this -->| *
// * * * | *
// * . | . * . . . . . . . . * *
// * . | . * | *
// * A . | . B * | *
// . * . | *
// . * * . * * * * * * * * * * * * *
// * *
// * *
// TODO : Start and stop of the path ...
for (int32_t idPevious=-1, idCurrent=0, idNext=1;
idCurrent < m_listPoints.size();
idPevious++, idCurrent++, idNext++) {
if ( m_listPoints[idCurrent].m_type == PointRender::typePoint_join
|| m_listPoints[idCurrent].m_type == PointRender::typePoint_interpolation) {
if (idPevious < 0 ) {
SVG_ERROR("an error occure a previous ID is < 0.... ");
continue;
}
if (idNext >= m_listPoints.size()) {
SVG_ERROR("an error occure a next ID is >= nbPoint len .... ");
continue;
}
vec2 vecA = m_listPoints[idCurrent].m_pos - m_listPoints[idPevious].m_pos;
vecA.safeNormalize();
vec2 vecB = m_listPoints[idNext].m_pos - m_listPoints[idCurrent].m_pos;
vecB.safeNormalize();
vec2 vecC = vecA - vecB;
if (vecC == vec2(0,0)) {
// special case: 1 line ...
m_listPoints[idCurrent].m_miterAxe = vec2(vecA.y(), vecA.x());
} else {
vecC.safeNormalize();
m_listPoints[idCurrent].m_miterAxe = vecC;
}
} else if (m_listPoints[idCurrent].m_type == PointRender::typePoint_start) {
vec2 vecB = m_listPoints[idNext].m_pos - m_listPoints[idCurrent].m_pos;
vecB.safeNormalize();
m_listPoints[idCurrent].m_miterAxe = vec2(vecB.y(), vecB.x());
} else if (m_listPoints[idCurrent].m_type == PointRender::typePoint_stop) {
if (idPevious < 0 ) {
SVG_ERROR("an error occure a previous ID is < 0.... ");
continue;
}
vec2 vecA = m_listPoints[idCurrent].m_pos - m_listPoints[idPevious].m_pos;
vecA.safeNormalize();
m_listPoints[idCurrent].m_miterAxe = vec2(vecA.y(), vecA.x());
} else {
SVG_TODO("lklklklklkl");
}
}
float lineWidth = 5.0f;
// create segment list:
bool haveStartLine;
vec2 leftPoint;
vec2 rightPoint;
for (int32_t iii=0;
iii < m_listPoints.size();
++iii) {
switch (m_listPoints[iii].m_type) {
case PointRender::typePoint_single:
// just do nothing ....
SVG_VERBOSE("[" << iii << "] Find Single " << m_listPoints[iii].m_pos);
break;
case PointRender::typePoint_start:
{
SVG_VERBOSE("[" << iii << "] Find Start " << m_listPoints[iii].m_pos);
if (haveStartLine == true) {
// close previous :
SVG_WARNING(" find a non close path ...");
addSegment(leftPoint, rightPoint);
}
haveStartLine = true;
// TODO : Calculate intersection ... (now we do a simple fast test of path display ...)
leftPoint = m_listPoints[iii].m_pos
+ m_listPoints[iii].m_miterAxe*lineWidth*0.5f;
rightPoint = m_listPoints[iii].m_pos
- m_listPoints[iii].m_miterAxe*lineWidth*0.5f;
addSegment(leftPoint, rightPoint);
SVG_VERBOSE(" segment :" << leftPoint << " -> " << rightPoint);
}
break;
case PointRender::typePoint_stop:
{
SVG_VERBOSE("[" << iii << "] Find Stop " << m_listPoints[iii].m_pos);
if (haveStartLine == true) {
SVG_WARNING("find close path without start part ...");
break;
}
haveStartLine = false;
// TODO : Calculate intersection ... (now we do a simple fast test of path display ...)
vec2 left = m_listPoints[iii].m_pos
+ m_listPoints[iii].m_miterAxe*lineWidth*0.5f;
vec2 right = m_listPoints[iii].m_pos
- m_listPoints[iii].m_miterAxe*lineWidth*0.5f;
//Draw from previous point:
addSegment(leftPoint, left);
SVG_VERBOSE(" segment :" << leftPoint << " -> " << left);
addSegment(right, rightPoint);
SVG_VERBOSE(" segment :" << right << " -> " << rightPoint);
leftPoint = left;
rightPoint = right;
// end line ...
addSegment(rightPoint, leftPoint);
SVG_VERBOSE(" segment :" << rightPoint << " -> " << leftPoint);
}
break;
case PointRender::typePoint_interpolation:
{
SVG_VERBOSE("[" << iii << "] Find interpolation " << m_listPoints[iii].m_pos);
// TODO : Calculate intersection ... (now we do a simple fast test of path display ...)
vec2 left = m_listPoints[iii].m_pos
+ m_listPoints[iii].m_miterAxe*lineWidth*0.5f;
vec2 right = m_listPoints[iii].m_pos
- m_listPoints[iii].m_miterAxe*lineWidth*0.5f;
//Draw from previous point:
addSegment(leftPoint, left);
SVG_VERBOSE(" segment :" << leftPoint << " -> " << left);
addSegment(right, rightPoint);
SVG_VERBOSE(" segment :" << right << " -> " << rightPoint);
leftPoint = left;
rightPoint = right;
}
break;
case PointRender::typePoint_join:
{
SVG_VERBOSE("[" << iii << "] Find Join " << m_listPoints[iii].m_pos);
// TODO : Calculate intersection ... (now we do a simple fast test of path display ...)
vec2 left = m_listPoints[iii].m_pos
+ m_listPoints[iii].m_miterAxe*lineWidth*0.5f;
vec2 right = m_listPoints[iii].m_pos
- m_listPoints[iii].m_miterAxe*lineWidth*0.5f;
//Draw from previous point:
addSegment(leftPoint, left);
SVG_VERBOSE(" segment :" << leftPoint << " -> " << left);
addSegment(right, rightPoint);
SVG_VERBOSE(" segment :" << right << " -> " << rightPoint);
leftPoint = left;
rightPoint = right;
}
break;
}
}
}
void sortSegment() {
std::sort(m_listSegment.begin(), m_listSegment.end(), sortSegmentFunction);
}
Weighter createWeighter(ivec2 _size) {
Weighter out(_size);
// for each lines:
for (int32_t yyy=0; yyy<_size.y(); ++yyy) {
// Reduce the number of lines in the subsampling parsing:
std::vector<Segment> availlableSegmentPixel;
for (auto &it : m_listSegment) {
if ( it.p0.y() <= float(yyy+1)
&& it.p1.y() >= float(yyy)) {
availlableSegmentPixel.push_back(it);
}
}
// This represent the pondaration on the subSampling
float deltaSize = 1.0f/m_subSamplingCount;
for (int32_t kkk=0; kkk<m_subSamplingCount ; ++kkk) {
Scanline scanline(_size.x());
//find all the segment that cross the middle of the line of the center of the pixel line:
float subSamplingCenterPos = yyy + deltaSize*0.5f + deltaSize*kkk;
std::vector<Segment> availlableSegment;
// find in the subList ...
for (auto &it : availlableSegmentPixel) {
if ( it.p0.y() <= subSamplingCenterPos
&& it.p1.y() >= subSamplingCenterPos) {
availlableSegment.push_back(it);
}
}
// x position, angle
std::vector<std::pair<float, float>> listPosition;
for (auto &it : availlableSegment) {
vec2 delta = it.p0 - it.p1;
// x = coefficent*y+bbb;
float coefficient = delta.x()/delta.y();
float bbb = it.p0.x() - coefficient*it.p0.y();
float xpos = coefficient * subSamplingCenterPos + bbb;
listPosition.push_back(std::pair<float,float>(xpos, it.direction));
}
// now we order position of the xPosition:
std::sort(listPosition.begin(), listPosition.end(), sortXPosFunction);
// move through all element in the point:
float lastState = 0.0f;
float currentValue = 0.0f;
int32_t lastPos = -1;
int32_t currentPos = -1;
float lastX = 0.0f;
// * | \---------------/ |
// * current pos
// * pos ...
// TODO : Code the Odd/even and non-zero ...
for (auto &it : listPosition) {
if (currentPos != int32_t(it.first)) {
// fill to the new pos -1:
float endValue = std::min(1.0f,std::abs(lastState)) * deltaSize;
for (int32_t iii=currentPos+1; iii<int32_t(it.first); ++iii) {
scanline.set(iii, endValue);
}
currentPos = int32_t(it.first);
currentValue = endValue;
}
float oldState = lastState;
lastState += it.second;
if (oldState == 0.0f) {
// nothing to draw before ...
float ratio = 1.0f - (it.first - float(int32_t(it.first)));
currentValue += ratio * deltaSize;
} else if (lastState == 0.0f) {
// something new to draw ...
float ratio = 1.0f - (it.first - float(int32_t(it.first)));
currentValue -= ratio * deltaSize;
} else {
// nothing to do ...
}
if (currentPos == int32_t(it.first)) {
scanline.set(currentPos, currentValue);
}
}
out.append(yyy, scanline);
}
}
return out;
}
};
void esvg::Path::aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level) {
SVG_VERBOSE(spacingDist(_level) << "DRAW esvg::Path");
esvg::RenderPath path;
path.clear();
for(int32_t iii=0; iii<m_listElement.size(); iii++) {
switch (m_listElement[iii].m_cmd) {
case esvg::pathStop:
abstractCloseSubpath(path);
case esvg::path_stop:
SVG_VERBOSE(spacingDist(_level+1) << "Draw : esvg::path_stop");
path.stop();
break;
case esvg::pathMoveTo:
abstractMoveTo(path, m_listElement[iii].m_relative,
m_listElement[iii].m_element[0],
m_listElement[iii].m_element[1] );
case esvg::path_moveTo:
SVG_VERBOSE(spacingDist(_level+1) << "Draw : esvg::path_moveTo");
path.moveTo(m_listElement[iii].m_relative,
vec2(m_listElement[iii].m_element[0],
m_listElement[iii].m_element[1]) );
break;
case esvg::pathLineTo:
abstractLineTo(path, m_listElement[iii].m_relative,
m_listElement[iii].m_element[0],
m_listElement[iii].m_element[1] );
case esvg::path_lineTo:
SVG_VERBOSE(spacingDist(_level+1) << "Draw : esvg::path_lineTo");
path.lineTo(m_listElement[iii].m_relative,
vec2(m_listElement[iii].m_element[0],
m_listElement[iii].m_element[1]) );
break;
case esvg::pathLineToH:
abstractHLineTo(path, m_listElement[iii].m_relative,
m_listElement[iii].m_element[0] );
case esvg::path_lineToH:
SVG_VERBOSE(spacingDist(_level+1) << "Draw : esvg::path_lineToH");
path.lineTo(m_listElement[iii].m_relative,
vec2(m_listElement[iii].m_element[0], 0.0f) );
break;
case esvg::pathLineToV:
abstractVLineTo(path, m_listElement[iii].m_relative,
m_listElement[iii].m_element[0] );
case esvg::path_lineToV:
SVG_VERBOSE(spacingDist(_level+1) << "Draw : esvg::path_lineToV");
path.lineTo(m_listElement[iii].m_relative,
vec2(0.0f, m_listElement[iii].m_element[0]) );
break;
case esvg::pathCurveTo:
abstractCurve4(path, m_listElement[iii].m_relative,
m_listElement[iii].m_element[0],
m_listElement[iii].m_element[1],
m_listElement[iii].m_element[2],
m_listElement[iii].m_element[3],
m_listElement[iii].m_element[4],
m_listElement[iii].m_element[5] );
//SVG_INFO(" draw : esvg::pathCurveTo");
case esvg::path_curveTo:
SVG_VERBOSE(spacingDist(_level+1) << "Draw : esvg::path_curveTo");
path.curve4To(m_listElement[iii].m_relative,
vec2(m_listElement[iii].m_element[0],
m_listElement[iii].m_element[1]),
vec2(m_listElement[iii].m_element[2],
m_listElement[iii].m_element[3]),
vec2(m_listElement[iii].m_element[4],
m_listElement[iii].m_element[5]) );
break;
case esvg::pathSmothCurveTo:
abstractCurve4(path, m_listElement[iii].m_relative,
m_listElement[iii].m_element[0],
m_listElement[iii].m_element[1],
m_listElement[iii].m_element[2],
m_listElement[iii].m_element[3] );
//SVG_INFO(" draw : esvg::pathSmothCurveTo");
case esvg::path_smothCurveTo:
SVG_VERBOSE(spacingDist(_level+1) << "Draw : esvg::path_smothCurveTo");
path.curve4SmoothTo(m_listElement[iii].m_relative,
vec2(m_listElement[iii].m_element[0],
m_listElement[iii].m_element[1]),
vec2(m_listElement[iii].m_element[2],
m_listElement[iii].m_element[3]) );
break;
case esvg::pathBesizeCurveTo:
abstractCurve3(path, m_listElement[iii].m_relative,
m_listElement[iii].m_element[0],
m_listElement[iii].m_element[1],
m_listElement[iii].m_element[2],
m_listElement[iii].m_element[3] );
//SVG_INFO(" draw : esvg::pathBesizeCurveTo");
case esvg::path_bezierCurveTo:
SVG_VERBOSE(spacingDist(_level+1) << "Draw : esvg::path_bezierCurveTo");
path.curve3To(m_listElement[iii].m_relative,
vec2(m_listElement[iii].m_element[0],
m_listElement[iii].m_element[1]),
vec2(m_listElement[iii].m_element[2],
m_listElement[iii].m_element[3]) );
break;
case esvg::pathBesizeSmothCurveTo:
abstractCurve3(path, m_listElement[iii].m_relative,
m_listElement[iii].m_element[0],
m_listElement[iii].m_element[1] );
//SVG_INFO(" draw : esvg::pathBesizeSmothCurveTo");
case esvg::path_bezierSmothCurveTo:
SVG_VERBOSE(spacingDist(_level+1) << "Draw : esvg::path_bezierSmothCurveTo");
path.curve3SmoothTo(m_listElement[iii].m_relative,
vec2(m_listElement[iii].m_element[0],
m_listElement[iii].m_element[1]) );
break;
case esvg::pathElliptic:
SVG_TODO("Elliptic arc is not implemented NOW ...");
case esvg::path_elliptic:
/*
SVG_VERBOSE(spacingDist(_level+1) << "Draw : esvg::path_elliptic");
path.ellipticTo(m_listElement[iii].m_relative,
m_listElement[iii].m_element[0],
m_listElement[iii].m_element[1],
m_listElement[iii].m_element[2],
m_listElement[iii].m_element[3],
m_listElement[iii].m_element[4],
m_listElement[iii].m_element[5],
m_listElement[iii].m_element[6] );
*/
SVG_TODO(spacingDist(_level+1) << "Draw : esvg::path_elliptic");
break;
default:
SVG_ERROR("Unknow PATH commant (internal error)");
@ -403,102 +894,49 @@ void esvg::Path::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicT
}
}
agg::trans_affine mtx = m_transformMatrix;
mat2 mtx = m_transformMatrix;
mtx *= _basicTrans;
agg::conv_curve<agg::path_storage> curve(path);
if (m_paint.fill.a != 0x00) {
agg::conv_transform<agg::conv_curve<agg::path_storage>, agg::trans_affine> trans(curve, mtx);
// set the filling mode :
_myRenderer.m_rasterizer.filling_rule((m_paint.flagEvenOdd)?agg::fill_even_odd:agg::fill_non_zero);
_myRenderer.m_rasterizer.add_path(trans);
agg::render_scanlines(_myRenderer.m_rasterizer, _myRenderer.m_scanLine, *_myRenderer.m_renderArea);
path.display();
Weighter tmpFill;
Weighter tmpStroke;
//agg::conv_curve<esvg::RenderPath> curve(path);
// Check if we need to display background
if (m_paint.fill.a() != 0x00) {
Transformation ttt;
ttt.flattenShape(path, mtx);
// Rasterize Segments
ttt.sortSegment();
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
tmpFill = ttt.createWeighter(ivec2(128,128));
}
if (m_paint.strokeWidth > 0 && m_paint.stroke.a!=0x00 ) {
_myRenderer.m_renderArea->color(agg::rgba8(m_paint.stroke.r, m_paint.stroke.g, m_paint.stroke.b, m_paint.stroke.a));
// drawing as an outline
agg::conv_stroke<agg::conv_curve<agg::path_storage> > myPolygonStroke(curve);
myPolygonStroke.width(m_paint.strokeWidth);
agg::conv_transform<agg::conv_stroke<agg::conv_curve<agg::path_storage> >, agg::trans_affine> transStroke(myPolygonStroke, mtx);
// set the filling mode :
_myRenderer.m_rasterizer.filling_rule(agg::fill_non_zero);
_myRenderer.m_rasterizer.add_path(transStroke);
agg::render_scanlines(_myRenderer.m_rasterizer, _myRenderer.m_scanLine, *_myRenderer.m_renderArea);
// check if we need to display stroke:
if ( m_paint.strokeWidth > 0
&& m_paint.stroke.a() != 0x00) {
Transformation ttt;
ttt.flattenShapeStroke(path, mtx);
// Rasterize Segments
ttt.sortSegment();
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
tmpStroke = ttt.createWeighter(ivec2(128,128));
}
}
void esvg::Path::abstractMoveTo(agg::path_storage& _path, bool _rel, double _x, double _y) {
if(true == _rel) {
_path.rel_to_abs(&_x, &_y);
}
_path.move_to(_x, _y);
}
void esvg::Path::abstractLineTo(agg::path_storage& _path, bool _rel, double _x, double _y) {
if(true == _rel) {
_path.rel_to_abs(&_x, &_y);
}
_path.line_to(_x, _y);
}
void esvg::Path::abstractHLineTo(agg::path_storage& _path, bool _rel, double _x) {
double x2 = 0.0;
double y2 = 0.0;
if(0!=_path.total_vertices()) {
_path.vertex(_path.total_vertices() - 1, &x2, &y2);
if(true == _rel) {
_x += x2;
// add on images:
for (int32_t yyy=0; yyy<_myRenderer.m_size.y(); ++yyy) {
for (int32_t xxx=0; xxx<_myRenderer.m_size.x(); ++xxx) {
ivec2 pos(xxx, yyy);
float value = tmpFill.get(pos);
float valueStroke = tmpStroke.get(pos);
if (valueStroke != 0.0f) {
_myRenderer.m_buffer[(_myRenderer.m_size.x()*yyy + xxx)*4 ] = uint8_t(valueStroke*m_paint.stroke.r());
_myRenderer.m_buffer[(_myRenderer.m_size.x()*yyy + xxx)*4 + 1] = uint8_t(valueStroke*m_paint.stroke.g());
_myRenderer.m_buffer[(_myRenderer.m_size.x()*yyy + xxx)*4 + 2] = uint8_t(valueStroke*m_paint.stroke.b());
_myRenderer.m_buffer[(_myRenderer.m_size.x()*yyy + xxx)*4 + 3] = uint8_t(valueStroke*m_paint.stroke.a());
} else {
_myRenderer.m_buffer[(_myRenderer.m_size.x()*yyy + xxx)*4 ] = uint8_t(value*m_paint.fill.r());
_myRenderer.m_buffer[(_myRenderer.m_size.x()*yyy + xxx)*4 + 1] = uint8_t(value*m_paint.fill.g());
_myRenderer.m_buffer[(_myRenderer.m_size.x()*yyy + xxx)*4 + 2] = uint8_t(value*m_paint.fill.b());
_myRenderer.m_buffer[(_myRenderer.m_size.x()*yyy + xxx)*4 + 3] = uint8_t(value*m_paint.fill.a());
}
}
_path.line_to(_x, y2);
}
}
void esvg::Path::abstractVLineTo(agg::path_storage& _path, bool _rel, double _y) {
double x2 = 0.0;
double y2 = 0.0;
if(_path.total_vertices()) {
_path.vertex(_path.total_vertices() - 1, &x2, &y2);
if(true == _rel) {
_y += y2;
}
_path.line_to(x2, _y);
}
}
void esvg::Path::abstractCurve3(agg::path_storage& _path, bool _rel, double _x1, double _y1, double _x, double _y) {
if(true == _rel) {
_path.rel_to_abs(&_x1, &_y1);
_path.rel_to_abs(&_x, &_y);
}
_path.curve3(_x1, _y1, _x, _y);
}
void esvg::Path::abstractCurve3(agg::path_storage& _path, bool _rel, double _x, double _y) {
if(true == _rel) {
_path.curve3_rel(_x, _y);
} else {
_path.curve3(_x, _y);
}
}
void esvg::Path::abstractCurve4(agg::path_storage& _path, bool _rel, double _x1, double _y1, double _x2, double _y2, double _x, double _y) {
if(true == _rel) {
_path.rel_to_abs(&_x1, &_y1);
_path.rel_to_abs(&_x2, &_y2);
_path.rel_to_abs(&_x, &_y);
}
_path.curve4(_x1, _y1, _x2, _y2, _x, _y);
}
void esvg::Path::abstractCurve4(agg::path_storage& _path, bool _rel, double _x2, double _y2, double _x, double _y) {
if(true == _rel) {
_path.curve4_rel(_x2, _y2, _x, _y);
} else {
_path.curve4(_x2, _y2, _x, _y);
}
}
void esvg::Path::abstractCloseSubpath(agg::path_storage& _path) {
_path.end_poly(agg::path_flags_close);
}

View File

@ -10,24 +10,24 @@
#define __ESVG_PATH_H__
#include <esvg/Base.h>
#include <agg/agg_path_storage.h>
#include <esvg/RenderPath.h>
namespace esvg {
enum pathProperty{
pathStop,
pathMoveTo,
pathLineTo,
pathLineToH,
pathLineToV,
pathCurveTo,
pathSmothCurveTo,
pathBesizeCurveTo,
pathBesizeSmothCurveTo,
pathElliptic
path_stop,
path_moveTo,
path_lineTo,
path_lineToH,
path_lineToV,
path_curveTo,
path_smothCurveTo,
path_bezierCurveTo,
path_bezierSmothCurveTo,
path_elliptic
};
class PathBasic {
public:
PathBasic() : m_cmd(esvg::pathStop), m_relative(false) {
PathBasic() : m_cmd(esvg::path_stop), m_relative(false) {
for(int32_t iii=0; iii<7; ++iii) {
m_element[iii] = 0;
}
@ -42,19 +42,19 @@ namespace esvg {
public:
Path(PaintState _parentPaintState);
~Path();
virtual bool parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax);
virtual bool parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax);
virtual void display(int32_t _spacing);
virtual void aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans);
virtual void aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level);
private:
void abstractMoveTo(agg::path_storage& _path, bool _rel, double _x, double _y);
void abstractLineTo(agg::path_storage& _path, bool _rel, double _x, double _y);
void abstractHLineTo(agg::path_storage& _path, bool _rel, double _x);
void abstractVLineTo(agg::path_storage& _path, bool _rel, double _y);
void abstractCurve3(agg::path_storage& _path, bool _rel, double _x1, double _y1, double _x, double _y);
void abstractCurve3(agg::path_storage& _path, bool _rel, double _x, double _y);
void abstractCurve4(agg::path_storage& _path, bool _rel, double _x1, double _y1, double _x2, double _y2, double _x, double _y);
void abstractCurve4(agg::path_storage& _path, bool _rel, double _x2, double _y2, double _x, double _y);
void abstractCloseSubpath(agg::path_storage& _path);
void abstractMoveTo(esvg::RenderPath& _path, bool _rel, double _x, double _y);
void abstractLineTo(esvg::RenderPath& _path, bool _rel, double _x, double _y);
void abstractHLineTo(esvg::RenderPath& _path, bool _rel, double _x);
void abstractVLineTo(esvg::RenderPath& _path, bool _rel, double _y);
void abstractCurve3(esvg::RenderPath& _path, bool _rel, double _x1, double _y1, double _x, double _y);
void abstractCurve3(esvg::RenderPath& _path, bool _rel, double _x, double _y);
void abstractCurve4(esvg::RenderPath& _path, bool _rel, double _x1, double _y1, double _x2, double _y2, double _x, double _y);
void abstractCurve4(esvg::RenderPath& _path, bool _rel, double _x2, double _y2, double _x, double _y);
void abstractCloseSubpath(esvg::RenderPath& _path);
};
};

View File

@ -8,8 +8,6 @@
#include <esvg/debug.h>
#include <esvg/Polygon.h>
#include <agg/agg_conv_stroke.h>
#include <agg/agg_path_storage.h>
#undef __class__
#define __class__ "Polygon"
@ -22,19 +20,19 @@ esvg::Polygon::~Polygon() {
}
bool esvg::Polygon::parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax) {
bool esvg::Polygon::parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax) {
if (_element == nullptr) {
return false;
}
parseTransform(_element);
parsePaintAttr(_element);
SVG_VERBOSE("parsed P1. trans : (" << m_transformMatrix.sx << "," << m_transformMatrix.shy << "," << m_transformMatrix.shx << "," << m_transformMatrix.sy << "," << m_transformMatrix.tx << "," << m_transformMatrix.ty << ")");
SVG_VERBOSE("parsed P1. trans: " << m_transformMatrix);
// add the property of the parrent modifications ...
m_transformMatrix *= _parentTrans;
SVG_VERBOSE("parsed P2. trans : (" << m_transformMatrix.sx << "," << m_transformMatrix.shy << "," << m_transformMatrix.shx << "," << m_transformMatrix.sy << "," << m_transformMatrix.tx << "," << m_transformMatrix.ty << ")");
SVG_VERBOSE("parsed P2. trans: " << m_transformMatrix);
const std::string sss1 = _element->getAttribute("points");
if (sss1.size() == 0) {
@ -66,10 +64,13 @@ void esvg::Polygon::display(int32_t _spacing) {
SVG_DEBUG(spacingDist(_spacing) << "Polygon nbPoint=" << m_listPoint.size());
}
void esvg::Polygon::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans) {
void esvg::Polygon::aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level) {
SVG_VERBOSE(spacingDist(_level) << "DRAW esvg::Polygon");
#if 0
_myRenderer.m_renderArea->color(agg::rgba8(m_paint.fill.r, m_paint.fill.g, m_paint.fill.b, m_paint.fill.a));
agg::path_storage path;
esvg::RenderPath path;
path.start_new_path();
path.move_to(m_listPoint[0].x(), m_listPoint[0].y());
@ -78,7 +79,7 @@ void esvg::Polygon::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _bas
}
path.close_polygon();
/*
// configure the end of the line :
// configure the end of the line:
switch (m_paint.lineCap) {
case esvg::LINECAP_SQUARE:
path.line_cap(agg::square_cap);
@ -103,11 +104,11 @@ void esvg::Polygon::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _bas
}
*/
agg::trans_affine mtx = m_transformMatrix;
mat2 mtx = m_transformMatrix;
mtx *= _basicTrans;
if (m_paint.fill.a != 0x00) {
agg::conv_transform<agg::path_storage, agg::trans_affine> trans(path, mtx);
agg::conv_transform<esvg::RenderPath, mat2> trans(path, mtx);
// set the filling mode :
_myRenderer.m_rasterizer.filling_rule((m_paint.flagEvenOdd)?agg::fill_even_odd:agg::fill_non_zero);
_myRenderer.m_rasterizer.add_path(trans);
@ -117,13 +118,14 @@ void esvg::Polygon::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _bas
if (m_paint.strokeWidth > 0 && m_paint.stroke.a!=0x00 ) {
_myRenderer.m_renderArea->color(agg::rgba8(m_paint.stroke.r, m_paint.stroke.g, m_paint.stroke.b, m_paint.stroke.a));
// drawing as an outline
agg::conv_stroke<agg::path_storage> myPolygonStroke(path);
agg::conv_stroke<esvg::RenderPath> myPolygonStroke(path);
myPolygonStroke.width(m_paint.strokeWidth);
agg::conv_transform<agg::conv_stroke<agg::path_storage>, agg::trans_affine> transStroke(myPolygonStroke, mtx);
agg::conv_transform<agg::conv_stroke<esvg::RenderPath>, mat2> transStroke(myPolygonStroke, mtx);
// set the filling mode :
_myRenderer.m_rasterizer.filling_rule(agg::fill_non_zero);
_myRenderer.m_rasterizer.add_path(transStroke);
agg::render_scanlines(_myRenderer.m_rasterizer, _myRenderer.m_scanLine, *_myRenderer.m_renderArea);
}
#endif
}

View File

@ -21,14 +21,14 @@ namespace esvg {
*/
class Polygon : public esvg::Base {
private:
std::vector<etk::Vector2D<float> > m_listPoint; //!< list of all point of the polygone
std::vector<vec2 > m_listPoint; //!< list of all point of the polygone
//enum esvg::polygonMode m_diplayMode; //!< polygone specific display mode
public:
Polygon(PaintState parentPaintState);
~Polygon();
virtual bool parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& parentTrans, etk::Vector2D<float>& sizeMax);
virtual void display(int32_t spacing);
virtual void aggDraw(esvg::Renderer& myRenderer, agg::trans_affine& basicTrans);
virtual bool parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax);
virtual void display(int32_t _spacing);
virtual void aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level);
};
};

View File

@ -8,8 +8,6 @@
#include <esvg/debug.h>
#include <esvg/Polyline.h>
#include <agg/agg_conv_stroke.h>
#include <agg/agg_path_storage.h>
esvg::Polyline::Polyline(PaintState _parentPaintState) : esvg::Base(_parentPaintState) {
@ -19,7 +17,7 @@ esvg::Polyline::~Polyline() {
}
bool esvg::Polyline::parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax) {
bool esvg::Polyline::parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax) {
// line must have a minimum size...
m_paint.strokeWidth = 1;
if (_element == nullptr) {
@ -40,7 +38,7 @@ bool esvg::Polyline::parse(const std::shared_ptr<exml::Element>& _element, agg::
SVG_VERBOSE("Parse polyline : \"" << sss1 << "\"");
const char* sss = sss1.c_str();
while ('\0' != sss[0]) {
etk::Vector2D<float> pos;
vec2 pos;
int32_t n;
if (sscanf(sss, "%f,%f %n", &pos.m_floats[0], &pos.m_floats[1], &n) == 2) {
m_listPoint.push_back(pos);
@ -59,8 +57,12 @@ void esvg::Polyline::display(int32_t _spacing) {
}
void esvg::Polyline::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans) {
agg::path_storage path;
void esvg::Polyline::aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level) {
SVG_VERBOSE(spacingDist(_level) << "DRAW esvg::Polyline");
#if 0
esvg::RenderPath path;
path.start_new_path();
path.move_to(m_listPoint[0].x(), m_listPoint[0].y());
for( int32_t iii=1; iii< m_listPoint.size(); iii++) {
@ -92,20 +94,21 @@ void esvg::Polyline::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _ba
}
*/
agg::trans_affine mtx = m_transformMatrix;
mat2 mtx = m_transformMatrix;
mtx *= _basicTrans;
if (m_paint.strokeWidth > 0) {
_myRenderer.m_renderArea->color(agg::rgba8(m_paint.stroke.r, m_paint.stroke.g, m_paint.stroke.b, m_paint.stroke.a));
// drawing as an outline
agg::conv_stroke<agg::path_storage> myPolygonStroke(path);
agg::conv_stroke<esvg::RenderPath> myPolygonStroke(path);
myPolygonStroke.width(m_paint.strokeWidth);
agg::conv_transform<agg::conv_stroke<agg::path_storage>, agg::trans_affine> transStroke(myPolygonStroke, mtx);
agg::conv_transform<agg::conv_stroke<esvg::RenderPath>, mat2> transStroke(myPolygonStroke, mtx);
// set the filling mode :
_myRenderer.m_rasterizer.filling_rule(agg::fill_non_zero);
_myRenderer.m_rasterizer.add_path(transStroke);
agg::render_scanlines(_myRenderer.m_rasterizer, _myRenderer.m_scanLine, *_myRenderer.m_renderArea);
}
#endif
}

View File

@ -15,13 +15,13 @@
namespace esvg {
class Polyline : public esvg::Base {
private:
std::vector<etk::Vector2D<float> > m_listPoint; //!< list of all point of the polyline
std::vector<vec2 > m_listPoint; //!< list of all point of the polyline
public:
Polyline(PaintState _parentPaintState);
~Polyline();
virtual bool parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax);
virtual bool parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax);
virtual void display(int32_t _spacing);
virtual void aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans);
virtual void aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level);
};
};

View File

@ -8,8 +8,6 @@
#include <esvg/debug.h>
#include <esvg/Rectangle.h>
#include <agg/agg_rounded_rect.h>
#include <agg/agg_conv_stroke.h>
#undef __class__
#define __class__ "Rectangle"
@ -25,7 +23,7 @@ esvg::Rectangle::~Rectangle() {
}
bool esvg::Rectangle::parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax) {
bool esvg::Rectangle::parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax) {
if (_element == nullptr) {
return false;
}
@ -58,19 +56,22 @@ void esvg::Rectangle::display(int32_t _spacing) {
SVG_DEBUG(spacingDist(_spacing) << "Rectangle : pos=" << m_position << " size=" << m_size << " corner=" << m_roundedCorner);
}
void esvg::Rectangle::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans) {
void esvg::Rectangle::aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level) {
SVG_VERBOSE(spacingDist(_level) << "DRAW esvg::Rectangle");
#if 0
_myRenderer.m_renderArea->color(agg::rgba8(m_paint.fill.r, m_paint.fill.g, m_paint.fill.b, m_paint.fill.a));
// Creating a rounded rectangle
agg::rounded_rect rect_r(m_position.x(), m_position.y(), m_position.x()+m_size.x(), m_position.y()+m_size.y(), m_roundedCorner.x());
rect_r.radius(m_roundedCorner.x(), m_roundedCorner.y());
rect_r.normalize_radius();
agg::trans_affine mtx = m_transformMatrix;
mat2 mtx = m_transformMatrix;
// herited modifications ...
mtx *= _basicTrans;
if (m_paint.fill.a != 0x00) {
agg::conv_transform<agg::rounded_rect, agg::trans_affine> trans(rect_r, mtx);
agg::conv_transform<agg::rounded_rect, mat2> trans(rect_r, mtx);
// set the filling mode :
_myRenderer.m_rasterizer.filling_rule((m_paint.flagEvenOdd)?agg::fill_even_odd:agg::fill_non_zero);
_myRenderer.m_rasterizer.add_path(trans);
@ -84,10 +85,10 @@ void esvg::Rectangle::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _b
// set the filling mode :
_myRenderer.m_rasterizer.filling_rule(agg::fill_non_zero);
rect_p.width(m_paint.strokeWidth);
agg::conv_transform<agg::conv_stroke<agg::rounded_rect>, agg::trans_affine> transStroke(rect_p, mtx);
agg::conv_transform<agg::conv_stroke<agg::rounded_rect>, mat2> transStroke(rect_p, mtx);
_myRenderer.m_rasterizer.add_path(transStroke);
agg::render_scanlines(_myRenderer.m_rasterizer, _myRenderer.m_scanLine, *_myRenderer.m_renderArea);
}
#endif
}

View File

@ -14,15 +14,15 @@
namespace esvg {
class Rectangle : public esvg::Base {
private:
etk::Vector2D<float> m_position; //!< position of the rectangle
etk::Vector2D<float> m_size; //!< size of the rectangle
etk::Vector2D<float> m_roundedCorner; //!< property of the rounded corner
vec2 m_position; //!< position of the rectangle
vec2 m_size; //!< size of the rectangle
vec2 m_roundedCorner; //!< property of the rounded corner
public:
Rectangle(PaintState _parentPaintState);
~Rectangle();
virtual bool parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax);
virtual bool parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax);
virtual void display(int32_t _spacing);
virtual void aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans);
virtual void aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level);
};
};

103
esvg/RenderPath.cpp Normal file
View File

@ -0,0 +1,103 @@
/**
* @author Edouard DUPIN
*
* @copyright 2011, Edouard DUPIN, all right reserved
*
* @license APACHE v2.0 (see license file)
*/
#include <esvg/debug.h>
#include <esvg/RenderPath.h>
void esvg::RenderPath::clear() {
m_points.clear();
}
void esvg::RenderPath::stop() {
SVG_TODO("Must be implemented");
}
void esvg::RenderPath::addPoint(const vec2& _pos) {
m_points.push_back(_pos);
}
void esvg::RenderPath::moveTo(bool _relative, vec2 _pos) {
if(_relative == true) {
_pos = relativeToAbsolute(_pos);
}
if (m_points.size() > 0) {
m_points.back() = _pos;
} else {
addPoint(_pos);
}
}
vec2 esvg::RenderPath::relativeToAbsolute(const vec2& _value) {
vec2 out(_value);
if (m_points.size() > 0) {
out += m_points.back();
}
return out;
}
void esvg::RenderPath::lineTo(bool _relative, vec2 _pos) {
if(_relative == true) {
_pos = relativeToAbsolute(_pos);
}
vec2 pos;
vec2 delta;
if (m_points.size() > 0) {
vec2 oldPos = m_points.back();
vec2 delta = _pos - oldPos;
// create a basic bezier curve ...
addPoint(oldPos + delta*0.3333f);
addPoint(oldPos - delta*0.3333f);
addPoint(_pos);
} else {
SVG_ERROR("try to lineTo whith no previous point ...");
}
}
void esvg::RenderPath::curve3SmoothTo(bool _relative, vec2 _pos) {
if(_relative == true) {
_pos = relativeToAbsolute(_pos);
}
SVG_TODO("later ...");
}
void esvg::RenderPath::curve3To(bool _relative, vec2 _pos1, vec2 _pos) {
if(_relative == true) {
_pos1 = relativeToAbsolute(_pos1);
_pos = relativeToAbsolute(_pos);
}
SVG_TODO("later ...");
}
void esvg::RenderPath::curve4SmoothTo(bool _relative, vec2 _pos2, vec2 _pos) {
if(_relative == true) {
_pos2 = relativeToAbsolute(_pos2);
_pos = relativeToAbsolute(_pos);
}
SVG_TODO("later ...");
}
void esvg::RenderPath::curve4To(bool _relative, vec2 _pos1, vec2 _pos2, vec2 _pos) {
if(_relative == true) {
_pos1 = relativeToAbsolute(_pos1);
_pos2 = relativeToAbsolute(_pos2);
_pos = relativeToAbsolute(_pos);
}
addPoint(_pos1);
addPoint(_pos2);
addPoint(_pos);
}
void esvg::RenderPath::display() {
SVG_VERBOSE("PATH : ");
for (uint32_t iii=0; iii<m_points.size()-1; iii+=3) {
SVG_VERBOSE(" point: " << m_points[iii]);
SVG_VERBOSE(" v: " << m_points[iii+1]);
SVG_VERBOSE(" v: " << m_points[iii+2]);
}
SVG_VERBOSE(" point: " << m_points.back());
}

45
esvg/RenderPath.h Normal file
View File

@ -0,0 +1,45 @@
/**
* @author Edouard DUPIN
*
* @copyright 2011, Edouard DUPIN, all right reserved
*
* @license APACHE v2.0 (see license file)
*/
#ifndef __ESVG_RENDERER_PATH_H__
#define __ESVG_RENDERER_PATH_H__
#include <etk/types.h>
#include <etk/math/Vector2D.h>
namespace esvg {
class RenderPath {
public:
std::vector<vec2> m_points;
public:
RenderPath() {
}
~RenderPath() {
}
void clear();
void stop();
void addPoint(const vec2& _pos);
void moveTo(bool _relative, vec2 _pos);
void lineTo(bool _relative, vec2 _pos);
void curve4To(bool _relative, vec2 _pos1, vec2 _pos2, vec2 _pos);
void curve4SmoothTo(bool _relative, vec2 _pos2, vec2 _pos);
void curve3To(bool _relative, vec2 _pos1, vec2 _pos);
void curve3SmoothTo(bool _relative, vec2 _pos);
//void ellipticTo(bool _relative, 7 values ...);
vec2 relativeToAbsolute(const vec2& _value);
void display();
};
};
#endif

View File

@ -15,68 +15,138 @@
#undef __class__
#define __class__ "Renderer"
esvg::Renderer::Renderer(uint32_t width, uint32_t height) {
m_allocatedSize = 0;
m_size.setValue(width, height);
int32_t dataSize = ((int32_t)width * (int32_t)height * DATA_ALLOCATION_ELEMENT);
m_allocatedSize = dataSize;
// allocate Data
SVG_VERBOSE("Allocate buffer : " << dataSize);
m_buffer = new uint8_t[dataSize];
if (NULL == m_buffer) {
SVG_ERROR("Allocation of the output buffer for SVG drawing error");
m_allocatedSize = 0;
return;
}
memset(m_buffer, 0x00, dataSize * sizeof(uint8_t) );
m_renderingBuffer = new agg::rendering_buffer(m_buffer, m_size.x(), m_size.y(), m_size.x() * DATA_ALLOCATION_ELEMENT);
if (NULL == m_renderingBuffer) {
SVG_ERROR("Allocation of the m_renderingBuffer for SVG drawing error");
return;
}
m_pixFrame = new agg::pixfmt_rgba32(*m_renderingBuffer);
if (NULL == m_pixFrame) {
SVG_ERROR("Allocation of the m_pixFrame for SVG drawing error");
return;
}
m_renderBase = new rendererBase_t(*m_pixFrame);
if (NULL == m_renderBase) {
SVG_ERROR("Allocation of the m_renderBase for SVG drawing error");
return;
}
m_renderArea = new rendererSolid_t(*m_renderBase);
if (NULL == m_renderArea) {
SVG_ERROR("Allocation of the m_renderArea for SVG drawing error");
return;
}
//m_basicMatrix *= agg::trans_affine_translation(-g_base_dx2, -g_base_dy2);
//m_basicMatrix *= agg::trans_affine_scaling(g_scale*coefmult, g_scale*coefmult);
//m_basicMatrix *= agg::trans_affine_rotation(g_angle);// + agg::pi);
//m_basicMatrix *= agg::trans_affine_skewing(g_skew_x/1000.0, g_skew_y/1000.0);
//m_basicMatrix *= agg::trans_affine_translation(m_size.x*0.7, m_size.y/2);
esvg::Renderer::Renderer(const ivec2& _size) {
m_size = _size;
m_stride = DATA_ALLOCATION_ELEMENT;
m_scanline.resize(m_size.x() * m_stride, 0);
m_buffer.resize(m_size.x() * m_size.y() * m_stride, 0);
}
esvg::Renderer::~Renderer() {
if (NULL != m_buffer) {
delete[] m_buffer;
m_buffer = NULL;
}
m_buffer.clear();
m_scanline.clear();
m_stride = 0;
m_size = ivec2(0,0);
}
/*
void nsvgRasterize(NSVGrasterizer* rrr, // this
NSVGimage* image, // image definition
float tx, // move x
float ty, // move y
float scale, // scale
unsigned char* dst, //output image data
int w, // output width
int h, // output height
int stride) // pixel stride
{
NSVGshape *shape = NULL;
NSVGedge *eee = NULL;
NSVGcachedPaint cache;
int i;
rrr->bitmap = dst;
rrr->width = w;
rrr->height = h;
rrr->stride = stride;
if (w > rrr->cscanline) {
rrr->cscanline = w;
rrr->scanline = (unsigned char*)realloc(rrr->scanline, w);
if (rrr->scanline == NULL) return;
}
for (i = 0; i < h; i++)
memset(&dst[i*stride], 0, w*4);
for (shape = image->shapes;
shape != NULL;
shape = shape->next) {
if (!(shape->flags & NSVG_FLAGS_VISIBLE))
continue;
// ***********************
// *** render "fill" *****
// ***********************
if (shape->fill.type != NSVG_PAINT_NONE) {
nsvg__resetPool(rrr);
rrr->freelist = NULL;
rrr->nedges = 0;
nsvg__flattenShape(rrr, shape, scale);
// Scale and translate edges
for (i = 0; i < r->nedges; i++) {
eee = &rrr->edges[i];
eee->x0 = tx + eee->x0;
eee->y0 = (ty + eee->y0) * NSVG__SUBSAMPLES;
eee->x1 = tx + eee->x1;
eee->y1 = (ty + eee->y1) * NSVG__SUBSAMPLES;
}
// Rasterize edges
qsort(rrr->edges,
rrr->nedges,
sizeof(NSVGedge),
nsvg__cmpEdge);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
nsvg__initPaint(&cache,
&shape->fill,
shape->opacity);
nsvg__rasterizeSortedEdges(rrr,
tx,
ty,
scale,
&cache,
shape->fillRule);
}
// *************************
// *** render "stroke" *****
// *************************
if ( shape->stroke.type != NSVG_PAINT_NONE
&& (shape->strokeWidth * scale) > 0.01f) {
nsvg__resetPool(r);
rrr->freelist = NULL;
rrr->nedges = 0;
nsvg__flattenShapeStroke(rrr, shape, scale);
// dumpEdges(r, "edge.svg");
// Scale and translate edges
for (i = 0; i < rrr->nedges; i++) {
eee = &rrr->edges[i];
eee->x0 = tx + eee->x0;
eee->y0 = (ty + eee->y0) * NSVG__SUBSAMPLES;
eee->x1 = tx + eee->x1;
eee->y1 = (ty + eee->y1) * NSVG__SUBSAMPLES;
}
// Rasterize edges
qsort(rrr->edges,
rrr->nedges,
sizeof(NSVGedge),
nsvg__cmpEdge);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
nsvg__initPaint(&cache,
&shape->stroke,
shape->opacity);
nsvg__rasterizeSortedEdges(rrr,
tx,
ty,
scale,
&cache,
NSVG_FILLRULE_NONZERO);
}
}
nsvg__unpremultiplyAlpha(dst,
w,
h,
stride);
rrr->bitmap = NULL;
rrr->width = 0;
rrr->height = 0;
rrr->stride = 0;
}
*/
// Writing the buffer to a .PPM file, assuming it has
// RGB-structure, one byte per color component
//--------------------------------------------------
void esvg::Renderer::writePpm(std::string fileName) {
if (NULL == m_buffer) {
if (m_buffer.size() == 0) {
return;
}
FILE* fd = fopen(fileName.c_str(), "wb");
@ -86,10 +156,12 @@ void esvg::Renderer::writePpm(std::string fileName) {
SVG_DEBUG("Generate ppm : " << m_size);
fprintf(fd, "P6 %d %d 255 ", sizeX, sizeY);
for (int32_t iii=0 ; iii<sizeX*sizeY; iii++) {
fwrite(m_buffer+iii*DATA_ALLOCATION_ELEMENT, 1, 3, fd);
fwrite(&m_buffer[iii*DATA_ALLOCATION_ELEMENT], 1, 3, fd);
}
fclose(fd);
}
}

View File

@ -11,63 +11,26 @@
#include <etk/types.h>
#include <etk/math/Vector2D.h>
#include <draw/Color.h>
#include <agg/agg_basics.h>
#include <agg/agg_rendering_buffer.h>
#include <agg/agg_rasterizer_scanline_aa.h>
#include <agg/agg_scanline_p.h>
#include <agg/agg_renderer_scanline.h>
#include <agg/agg_path_storage.h>
#include <agg/agg_conv_transform.h>
#include <agg/agg_bounding_rect.h>
#include <agg/agg_color_rgba.h>
#include <agg/agg_pixfmt_rgba.h>
#include <etk/Color.h>
namespace esvg {
enum lineCap{
lineCapButt,
lineCapRound,
lineCapSquare
};
enum lineJoin{
lineJoinMiter,
lineJoinRound,
lineJoinBevel
};
class PaintState {
public:
draw::Color fill;
draw::Color stroke;
float strokeWidth;
bool flagEvenOdd;
enum esvg::lineCap lineCap;
enum esvg::lineJoin lineJoin;
etk::Vector2D<float> viewPort;
};
// basic definition type for the renderer
typedef agg::renderer_base<agg::pixfmt_rgba32> rendererBase_t;
typedef agg::renderer_scanline_aa_solid<rendererBase_t> rendererSolid_t;
class Renderer {
private:
uint8_t * m_buffer;
uint32_t m_allocatedSize;
public:
Renderer(uint32_t width, uint32_t height);
std::vector<uint8_t> m_scanline;
std::vector<uint8_t> m_buffer;
ivec2 m_size;
int32_t m_stride;
public:
Renderer(const ivec2& _size);
~Renderer();
void writePpm(std::string fileName);
etk::Vector2D<float> m_size;
agg::rendering_buffer * m_renderingBuffer;
agg::pixfmt_rgba32 * m_pixFrame;
rendererBase_t * m_renderBase;
rendererSolid_t * m_renderArea;
agg::rasterizer_scanline_aa<> m_rasterizer; //!< AGG renderer system
agg::scanline_p8 m_scanLine; //!<
uint8_t* getDataPointer() { return m_buffer; };
uint32_t getDataSize() { return m_allocatedSize; };
uint8_t* getDataPointer() {
return &m_buffer[0];
};
uint32_t getDataSize() {
return m_buffer.size();
};
};
};

View File

@ -20,7 +20,7 @@ esvg::Text::~Text() {
}
bool esvg::Text::parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax) {
bool esvg::Text::parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax) {
_sizeMax.setValue(0,0);
SVG_ERROR("NOT IMPLEMENTED");
return false;

View File

@ -16,7 +16,7 @@ namespace esvg {
public:
Text(PaintState _parentPaintState);
~Text();
virtual bool parse(const std::shared_ptr<exml::Element>& _element, agg::trans_affine& _parentTrans, etk::Vector2D<float>& _sizeMax);
virtual bool parse(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax);
virtual void display(int32_t _spacing);
};
};

View File

@ -16,6 +16,7 @@ namespace esvg {
};
#define SVG_BASE(info,data) TK_LOG_BASE(esvg::getLogId(),info,data)
#define SVG_PRINT(data) SVG_BASE(-1, data)
#define SVG_CRITICAL(data) SVG_BASE(1, data)
#define SVG_ERROR(data) SVG_BASE(2, data)
#define SVG_WARNING(data) SVG_BASE(3, data)

View File

@ -19,67 +19,235 @@
#include <esvg/Text.h>
#include <esvg/Group.h>
#include <agg/agg_basics.h>
#include <agg/agg_rendering_buffer.h>
#include <agg/agg_rasterizer_scanline_aa.h>
#include <agg/agg_scanline_p.h>
#include <agg/agg_renderer_scanline.h>
#include <agg/agg_path_storage.h>
#include <agg/agg_conv_transform.h>
#include <agg/agg_bounding_rect.h>
#include <agg/agg_color_rgba.h>
#include <agg/agg_pixfmt_rgba.h>
#undef __class__
#define __class__ "Document"
esvg::Document::Document(const std::string& _fileName) :
esvg::Document::Document() :
m_renderedElement(nullptr) {
m_fileName = _fileName;
m_fileName = "";
m_version = "0.0";
m_loadOK = true;
m_paint.fill = (int32_t)0xFF0000FF;
m_paint.stroke = (int32_t)0xFFFFFF00;
m_loadOK = false;
m_paint.fill = etk::color::red;
m_paint.stroke = etk::color::white;
m_paint.strokeWidth = 1.0;
m_paint.viewPort.setValue(255,255);
m_paint.flagEvenOdd = false;
m_paint.lineJoin = esvg::lineJoinMiter;
m_paint.lineCap = esvg::lineCapButt;
m_paint.lineJoin = esvg::join_miter;
m_paint.lineCap = esvg::cap_butt;
m_size.setValue(0,0);
exml::Document doc;
if (false == doc.load(m_fileName)) {
SVG_ERROR("Error occured when loading XML : " << m_fileName);
m_loadOK = false;
return;
}
esvg::Document::~Document() {
delete(m_renderedElement);
m_renderedElement = nullptr;
}
void esvg::Document::displayDebug() {
SVG_DEBUG("Main SVG node : size=" << m_size);
for (int32_t iii=0; iii<m_subElementList.size(); iii++) {
if (m_subElementList[iii] != nullptr) {
m_subElementList[iii]->display(1);
}
}
if (0 == doc.size() ) {
SVG_ERROR("(l ?) No nodes in the xml file ... \"" << m_fileName << "\"");
}
void esvg::Document::aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans)
{
for (int32_t iii=0; iii<m_subElementList.size(); iii++) {
if (m_subElementList[iii] != nullptr) {
m_subElementList[iii]->aggDraw(_myRenderer, _basicTrans);
}
}
}
void esvg::Document::generateTestFile()
{
int32_t sizeX = m_size.x();
if (sizeX == 0) {
sizeX = 64;
}
int32_t sizeY = m_size.y();
if (sizeY == 0) {
sizeY = 64;
}
delete(m_renderedElement);
m_renderedElement = nullptr;
m_renderedElement = new esvg::Renderer(ivec2(sizeX, sizeY));
// create the first element matrix modification ...
mat2 basicTrans;
//basicTrans *= etk::mat2Translate(vec2(-g_base_dx, -g_base_dy));
//basicTrans *= etk::mat2Scale(vec2(2, 2));
//basicTrans *= etk::mat2Rotate(vec2(g_angle));
//basicTrans *= etk::mat2Skew(vec2(2.0, 5.0));
//basicTrans *= etk::mat2Translate(vec2(width*0.3, height/2));
//basicTrans *= etk::mat2Translate(vec2(width/3, height/3));
aggDraw(*m_renderedElement, basicTrans);
std::string tmpFileOut = "yyy_out_";
tmpFileOut += m_fileName;
tmpFileOut += ".ppm";
m_renderedElement->writePpm(tmpFileOut);
}
void esvg::Document::generateAnImage(int32_t _sizeX, int32_t _sizeY) {
int32_t sizeX = _sizeX;
if (sizeX == 0) {
SVG_ERROR("SizeX == 0 ==> set 64");
sizeX = 64;
}
int32_t sizeY = _sizeY;
if (sizeY == 0) {
SVG_ERROR("SizeY == 0 ==> set 64");
sizeY = 64;
}
SVG_INFO("Generate size (" << sizeX << "," << sizeY << ")");
delete(m_renderedElement);
m_renderedElement = nullptr;
m_renderedElement = new esvg::Renderer(ivec2(sizeX, sizeY));
// create the first element matrix modification ...
mat2 basicTrans;
//basicTrans *= etk::mat2Translate(vec2(-g_base_dx, -g_base_dy));
basicTrans *= etk::mat2Scale(vec2(sizeX/m_size.x(), sizeY/m_size.y()));
//basicTrans *= etk::mat2Rotate(g_angle);// + agg::pi);
//basicTrans *= etk::mat2Skew(vec2(2.0, 5.0));
//basicTrans *= etk::mat2Translate(vec2(width*0.3, height/2));
//basicTrans *= etk::mat2Translate(vec2(width/3, height/3));
aggDraw(*m_renderedElement, basicTrans);
std::string tmpFileOut = "zzz_out_test.ppm";
m_renderedElement->writePpm(tmpFileOut);
}
/*
void esvg::Document::generateAnImage(draw::Image& _output) {
generateAnImage(ivec2(m_size.x(),m_size.y()), _output);
}
void esvg::Document::generateAnImage(ivec2 _size, draw::Image& _output) {
generateAnImage(_size.x(), _size.y());
_output.resize(_size);
draw::Color tmpp(0,0,0,0);
_output.setFillColor(tmpp);
_output.clear();
if(NULL != m_renderedElement) {
uint8_t* pointerOnData = m_renderedElement->getDataPointer();
int32_t sizeData = m_renderedElement->getDataSize();
uint8_t* tmpOut = (uint8_t*)_output.getTextureDataPointer();
memcpy(tmpOut, pointerOnData, sizeData);
}
}
*/
uint8_t* esvg::Document::getPointerOnData() {
if(m_renderedElement == nullptr) {
return nullptr;
}
return m_renderedElement->getDataPointer();
}
uint32_t esvg::Document::getSizeOnData() {
if(m_renderedElement == nullptr) {
return 0;
}
return m_renderedElement->getDataSize();
}
void esvg::Document::clear() {
m_fileName = "";
m_version = "0.0";
m_loadOK = true;
m_paint.fill = etk::color::red;
m_paint.stroke = etk::color::white;
m_paint.strokeWidth = 1.0;
m_paint.viewPort.setValue(255,255);
m_paint.flagEvenOdd = false;
m_paint.lineJoin = esvg::join_miter;
m_paint.lineCap = esvg::cap_butt;
m_size.setValue(0,0);
}
bool esvg::Document::parse(const std::string& _data) {
clear();
exml::Document doc;
if (doc.parse(_data) == false) {
SVG_ERROR("Error occured when loading SVG : " << m_fileName);
m_loadOK = false;
return;
return m_loadOK;
}
if (doc.size() == 0) {
SVG_ERROR("(l ?) No nodes in the SVG file ... \"" << m_fileName << "\"");
m_loadOK = false;
return m_loadOK;
}
std::shared_ptr<exml::Element> root = doc.getNamed("svg" );
if (root == nullptr) {
SVG_ERROR("(l ?) main node not find: \"svg\" in \"" << m_fileName << "\"");
m_loadOK = false;
return;
return m_loadOK;
}
m_loadOK = parseXMLData(root);
return m_loadOK;
}
bool esvg::Document::generate(std::string& _data) {
return false;
}
bool esvg::Document::load(const std::string& _file) {
clear();
m_fileName = _file;
exml::Document doc;
if (doc.load(m_fileName) == false) {
SVG_ERROR("Error occured when loading SVG : " << m_fileName);
m_loadOK = false;
return m_loadOK;
}
if (doc.size() == 0) {
SVG_ERROR("(l ?) No nodes in the SVG file ... \"" << m_fileName << "\"");
m_loadOK = false;
return m_loadOK;
}
std::shared_ptr<exml::Element> root = doc.getNamed("svg" );
if (root == nullptr) {
SVG_ERROR("(l ?) main node not find: \"svg\" in \"" << m_fileName << "\"");
m_loadOK = false;
return m_loadOK;
}
m_loadOK = parseXMLData(root);
return m_loadOK;
}
bool esvg::Document::store(const std::string& _file) {
return false;
}
bool esvg::Document::parseXMLData(const std::shared_ptr<exml::Element>& _root) {
// get the svg version :
m_version = root->getAttribute("version");
m_version = _root->getAttribute("version");
// parse ...
vec2 pos(0,0);
parseTransform(root);
parsePosition(root, pos, m_size);
parsePaintAttr(root);
SVG_VERBOSE("parsed .ROOT trans : (" << m_transformMatrix.sx << "," << m_transformMatrix.shy << "," << m_transformMatrix.shx << "," << m_transformMatrix.sy << "," << m_transformMatrix.tx << "," << m_transformMatrix.ty << ")");
parseTransform(_root);
parsePosition(_root, pos, m_size);
parsePaintAttr(_root);
SVG_VERBOSE("parsed .ROOT trans: " << m_transformMatrix);
vec2 maxSize(0,0);
vec2 size(0,0);
// parse all sub node :
for(int32_t iii=0; iii< root->size(); iii++) {
std::shared_ptr<exml::Element> child = root->getElement(iii);
for(int32_t iii=0; iii< _root->size(); iii++) {
std::shared_ptr<exml::Element> child = _root->getElement(iii);
if (child == nullptr) {
// comment trsh here...
continue;
@ -121,14 +289,14 @@ esvg::Document::Document(const std::string& _fileName) :
} else {
SVG_ERROR("(l "<<child->getPos()<<") node not suported : \""<<child->getValue()<<"\" must be [title,g,a,path,rect,circle,ellipse,line,polyline,polygon,text,metadata]");
}
if (NULL == elementParser) {
if (elementParser == nullptr) {
SVG_ERROR("(l "<<child->getPos()<<") error on node: \""<<child->getValue()<<"\" allocation error or not supported ...");
continue;
}
if (false == elementParser->parse(child, m_transformMatrix, size)) {
if (elementParser->parse(child, m_transformMatrix, size) == false) {
SVG_ERROR("(l "<<child->getPos()<<") error on node: \""<<child->getValue()<<"\" Sub Parsing ERROR");
delete(elementParser);
elementParser = NULL;
elementParser = nullptr;
continue;
}
if (maxSize.x()<size.x()) {
@ -145,143 +313,8 @@ esvg::Document::Document(const std::string& _fileName) :
} else {
m_size.setValue((int32_t)m_size.x(), (int32_t)m_size.y());
}
//DisplayDebug();
}
esvg::Document::~Document() {
if(NULL != m_renderedElement) {
delete(m_renderedElement);
m_renderedElement = NULL;
}
displayDebug();
return true;
}
void esvg::Document::displayDebug() {
SVG_DEBUG("Main SVG node : size=" << m_size);
for (int32_t iii=0; iii<m_subElementList.size(); iii++) {
if (NULL != m_subElementList[iii]) {
m_subElementList[iii]->display(1);
}
}
}
void esvg::Document::aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans)
{
for (int32_t iii=0; iii<m_subElementList.size(); iii++) {
if (NULL != m_subElementList[iii]) {
m_subElementList[iii]->aggDraw(_myRenderer, _basicTrans);
}
}
}
void esvg::Document::generateTestFile()
{
int32_t sizeX = m_size.x();
if (sizeX == 0) {
sizeX = 64;
}
int32_t sizeY = m_size.y();
if (sizeY == 0) {
sizeY = 64;
}
if(NULL != m_renderedElement) {
delete(m_renderedElement);
m_renderedElement = NULL;
}
m_renderedElement = new esvg::Renderer(sizeX, sizeY);
// create the first element matrix modification ...
agg::trans_affine basicTrans;
//basicTrans *= agg::trans_affine_translation(-g_base_dx, -g_base_dy);
//basicTrans *= agg::trans_affine_scaling(2, 2);
//basicTrans *= agg::trans_affine_rotation(g_angle);// + agg::pi);
//basicTrans *= agg::trans_affine_skewing(2.0, 5.0);
//basicTrans *= agg::trans_affine_translation(width*0.3, height/2);
//basicTrans *= agg::trans_affine_translation(width/3, height/3);
aggDraw(*m_renderedElement, basicTrans);
std::string tmpFileOut = "yyy_out_";
tmpFileOut += m_fileName;
tmpFileOut += ".ppm";
m_renderedElement->writePpm(tmpFileOut);
}
void esvg::Document::generateAnImage(int32_t _sizeX, int32_t _sizeY)
{
int32_t sizeX = _sizeX;
if (sizeX == 0) {
SVG_ERROR("SizeX == 0 ==> set 64");
sizeX = 64;
}
int32_t sizeY = _sizeY;
if (sizeY == 0) {
SVG_ERROR("SizeY == 0 ==> set 64");
sizeY = 64;
}
SVG_INFO("Generate size (" << sizeX << "," << sizeY << ")");
if(NULL != m_renderedElement) {
delete(m_renderedElement);
m_renderedElement = NULL;
}
m_renderedElement = new esvg::Renderer(sizeX, sizeY);
// create the first element matrix modification ...
agg::trans_affine basicTrans;
//basicTrans *= agg::trans_affine_translation(-g_base_dx, -g_base_dy);
basicTrans *= agg::trans_affine_scaling(sizeX/m_size.x(), sizeY/m_size.y());
//basicTrans *= agg::trans_affine_rotation(g_angle);// + agg::pi);
//basicTrans *= agg::trans_affine_skewing(2.0, 5.0);
//basicTrans *= agg::trans_affine_translation(width*0.3, height/2);
//basicTrans *= agg::trans_affine_translation(width/3, height/3);
aggDraw(*m_renderedElement, basicTrans);
/*
std::string tmpFileOut = "zzz_out_test.ppm";
m_renderedElement->WritePpm(tmpFileOut);
*/
}
void esvg::Document::generateAnImage(draw::Image& _output)
{
generateAnImage(ivec2(m_size.x(),m_size.y()), _output);
}
void esvg::Document::generateAnImage(ivec2 _size, draw::Image& _output)
{
generateAnImage(_size.x(), _size.y());
_output.resize(_size);
draw::Color tmpp(0,0,0,0);
_output.setFillColor(tmpp);
_output.clear();
if(NULL != m_renderedElement) {
uint8_t* pointerOnData = m_renderedElement->getDataPointer();
int32_t sizeData = m_renderedElement->getDataSize();
uint8_t* tmpOut = (uint8_t*)_output.getTextureDataPointer();
memcpy(tmpOut, pointerOnData, sizeData);
}
}
uint8_t* esvg::Document::getPointerOnData()
{
if(NULL == m_renderedElement) {
return NULL;
}
return m_renderedElement->getDataPointer();
}
uint32_t esvg::Document::getSizeOnData()
{
if(NULL == m_renderedElement) {
return 0;
}
return m_renderedElement->getDataSize();
}

View File

@ -15,7 +15,7 @@
#include <etk/os/FSNode.h>
#include <esvg/Base.h>
#include <draw/Image.h>
//#include <draw/Image.h>
namespace esvg {
class Document : public esvg::Base {
@ -24,22 +24,59 @@ namespace esvg {
bool m_loadOK;
std::string m_version;
std::string m_title;
std::vector<esvg::Base *> m_subElementList;
std::vector<esvg::Base*> m_subElementList;
vec2 m_size;
esvg::Renderer* m_renderedElement;
public:
Document();
Document(const std::string& _fileName);
~Document();
bool isLoadOk() { return m_loadOK; };
void clear();
/**
* @brief parse a string that contain an svg stream
* @param[in] _data Data to parse
* @return false : An error occured
* @return true : Parsing is OK
*/
bool parse(const std::string& _data);
/**
* @brief generate a string that contain the created SVG
* @param[out] _data Data where the svg is stored
* @return false : An error occured
* @return true : Parsing is OK
*/
bool generate(std::string& _data);
/**
* @brief Load the file that might contain the svg
* @param[in] _file Filename of the svg (compatible with etk FSNode naming)
* @return false : An error occured
* @return true : Parsing is OK
*/
bool load(const std::string& _file);
/**
* @brief Store the SVG in the file
* @param[in] _file Filename of the svg (compatible with etk FSNode naming)
* @return false : An error occured
* @return true : Parsing is OK
*/
bool store(const std::string& _file);
protected:
bool parseXMLData(const std::shared_ptr<exml::Element>& _root);
public:
bool isLoadOk() {
return m_loadOK;
};
void displayDebug();
void generateTestFile();
void generateAnImage(int32_t _sizeX, int32_t _sizeY);
void generateAnImage(ivec2 _size, draw::Image& _output);
void generateAnImage(draw::Image& _output);
virtual void aggDraw(esvg::Renderer& _myRenderer, agg::trans_affine& _basicTrans);
//void generateAnImage(ivec2 _size, draw::Image& _output);
//void generateAnImage(draw::Image& _output);
virtual void aggDraw(esvg::Renderer& _myRenderer, mat2& _basicTrans);
uint8_t* getPointerOnData();
uint32_t getSizeOnData();
vec2 getDefinedSize() { return m_size;};
vec2 getDefinedSize() {
return m_size;
};
};
};

36
lutin_esvg-test.py Normal file
View File

@ -0,0 +1,36 @@
#!/usr/bin/python
import lutin.module as module
import lutin.tools as tools
def get_type():
return "BINARY"
def get_sub_type():
return "TEST"
def get_desc():
return "eSVG test-unit"
def get_licence():
return "APACHE-2"
def get_compagny_type():
return "com"
def get_compagny_name():
return "atria-soft"
def get_maintainer():
return ["Mr DUPIN Edouard <yui.heero@gmail.com>"]
def create(target, module_name):
my_module = module.Module(__file__, module_name, get_type())
my_module.add_src_file([
'test/main.cpp',
'test/testParsingFile.cpp',
'test/testPath.cpp',
])
my_module.add_module_depend(['esvg', 'gtest', 'test-debug'])
return my_module

View File

@ -26,7 +26,7 @@ def get_version():
def create(target, module_name):
my_module = module.Module(__file__, module_name, get_type())
my_module.add_module_depend(['etk', 'agg', 'exml'])
my_module.add_module_depend(['etk', 'exml'])
my_module.add_src_file([
'esvg/Base.cpp',
'esvg/Circle.cpp',
@ -40,6 +40,7 @@ def create(target, module_name):
'esvg/Polyline.cpp',
'esvg/Rectangle.cpp',
'esvg/Renderer.cpp',
'esvg/RenderPath.cpp',
'esvg/Stroking.cpp',
'esvg/Text.cpp'
])

22
test/main.cpp Normal file
View File

@ -0,0 +1,22 @@
/**
* @author Edouard DUPIN
*
* @copyright 2014, Edouard DUPIN, all right reserved
*
* @license APACHE v2.0 (see license file)
*/
#include <test-debug/debug.h>
#include <vector>
#include <gtest/gtest.h>
#include <etk/etk.h>
#undef __class__
#define __class__ "esvg::test"
int main(int _argc, const char *_argv[]) {
::testing::InitGoogleTest(&_argc, const_cast<char **>(_argv));
etk::init(_argc, _argv);
//etk::initDefaultFolder("esvg-test");
return RUN_ALL_TESTS();
}

0
test/testParsingFile.cpp Normal file
View File

31
test/testPath.cpp Normal file
View File

@ -0,0 +1,31 @@
/**
* @author Edouard DUPIN
*
* @copyright 2014, Edouard DUPIN, all right reserved
*
* @license APACHE v2.0 (see license file)
*/
#include <gtest/gtest.h>
#include <esvg/esvg.h>
TEST(TestPath, basicTest) {
esvg::Document doc;
/*
doc.parse( "<?xml version='1.0' encoding='UTF-8' standalone='no'?>"
"<svg width='64' height='64'>"
" <g transform='matrix(1.2,0,0,1.2,-579.7336,-567.9832)'>"
" <path d='m 50,50 c -12.426,0 -22.5,10.072 -22.5,22.5 0,12.426 10.074,22.5 22.5,22.5 12.428,0 22.5,-10.074 22.5,-22.5 0,-12.427 -10.072,-22.5 -22.5,-22.5 z'"
" style='fill:#333333;fill-rule:evenodd' />"
" </g>"
"</svg>");
*/
doc.parse( "<?xml version='1.0' encoding='UTF-8' standalone='no'?>"
"<svg width='64' height='64'>"
" <g transform='matrix(1.2,0,0,1.2,-579.7336,-567.9832)'>"
" <path d='m 50,50 c -12.426,0 -22.5,10.072 -22.5,22.5 0,12.426 10.074,22.5 22.5,22.5 12.428,0 22.5,-10.074 22.5,-22.5 0,-12.427 -10.072,-22.5 -22.5,-22.5 z'"
" style='stroke:#00F;stroke-width:5;fill-rule:evenodd' />"
" </g>"
"</svg>");
doc.generateAnImage(128, 128);
}