[DEV] first linear gradient OK with the spec

This commit is contained in:
Edouard DUPIN 2015-12-17 21:30:55 +01:00
parent 85a35ae71b
commit 6b976bb9ea
7 changed files with 280 additions and 33 deletions

View File

@ -52,6 +52,40 @@ esvg::Base::Base(PaintState _parentPaintState) {
m_paint = _parentPaintState;
}
std::string extractTransformData(const std::string& _value, const std::string& _base) {
size_t posStart = _value.find(_base);
if (posStart == std::string::npos) {
// Not find element is a normal case ...
return "";
}
posStart += _base.size();
if (_value.size() < posStart+2) {
ESVG_ERROR("Not enought spece in the String to have transform value for ' (' or '()' in '" << _value << "'");
return "";
}
if (_value[posStart] == '(') {
// normal SVG does not support this case ...
posStart++;
} else if ( _value[posStart] == ' '
&& _value[posStart+1] == '(') {
posStart+=2;
} else {
ESVG_ERROR("Can not find ' (' or '(' in '" << &_value[posStart] << "' for '" << _value << "'");
return "";
}
if (_value.size() < posStart+1) {
ESVG_ERROR("Not enought spece in the String to have transform value for ')' in '" << _value << "'");
return "";
}
size_t posEnd = _value.find(')', posStart);
if (posEnd == std::string::npos) {
ESVG_ERROR("Missing element ')' in '" << _value << "' for " << _base);
return "";
}
ESVG_VERBOSE("Find : '" << std::string(_value.begin()+posStart, _value.begin()+posEnd) << "' for " << _base);
return std::string(_value.begin()+posStart, _value.begin()+posEnd);
}
void esvg::Base::parseTransform(const std::shared_ptr<exml::Element>& _element) {
if (_element == nullptr) {
return;
@ -67,46 +101,81 @@ void esvg::Base::parseTransform(const std::shared_ptr<exml::Element>& _element)
}
}
ESVG_VERBOSE("find transform : \"" << inputString << "\"");
double matrix[6];
float angle, xxx, yyy;
int32_t n;
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) {
// need to find elements in order ...
std::string data = extractTransformData(inputString, "matrix");
if (data.size() != 0) {
double matrix[6];
if (sscanf(data.c_str(), "%lf %lf %lf %lf %lf %lf", &matrix[0], &matrix[1], &matrix[2], &matrix[3], &matrix[4], &matrix[5]) == 6) {
m_transformMatrix = mat2(matrix);
} else if (sscanf(pointerOnData, "translate (%f %f) %n", &xxx, &yyy, &n) == 2) {
// find a matrix : simply exit ...
return;
} else {
ESVG_ERROR("Parsing matrix() with wrong data ... '" << data << "'");
}
}
data = extractTransformData(inputString, "translate");
if (data.size() != 0) {
float xxx, yyy;
if (sscanf(data.c_str(), "%f %f", &xxx, &yyy) == 2) {
m_transformMatrix *= etk::mat2Translate(vec2(xxx, yyy));
ESVG_VERBOSE("Translate : " << xxx << ", " << yyy);
} else if (sscanf(pointerOnData, "translate (%f) %n", &xxx, &n) == 1) {
} else if (sscanf(data.c_str(), "%f", &xxx) == 1) {
m_transformMatrix *= etk::mat2Translate(vec2(xxx, 0));
ESVG_VERBOSE("Translate : " << xxx << ", " << 0);
} else if (sscanf(pointerOnData, "scale (%f %f) %n", &xxx, &yyy, &n) == 2) {
} else {
ESVG_ERROR("Parsing translate() with wrong data ... '" << data << "'");
}
}
data = extractTransformData(inputString, "scale");
if (data.size() != 0) {
float xxx, yyy;
if (sscanf(data.c_str(), "%f %f", &xxx, &yyy) == 2) {
m_transformMatrix *= etk::mat2Scale(vec2(xxx, yyy));
ESVG_VERBOSE("Scale : " << xxx << ", " << yyy);
} else if (sscanf(pointerOnData, "scale (%f) %n", &xxx, &n) == 1) {
} else if (sscanf(data.c_str(), "%f", &xxx) == 1) {
m_transformMatrix *= etk::mat2Scale(xxx);
ESVG_VERBOSE("Scale : " << xxx << ", " << xxx);
} else if (sscanf(pointerOnData, "rotate (%f %f %f) %n", &angle, &xxx, &yyy, &n) == 3) {
} else {
ESVG_ERROR("Parsing scale() with wrong data ... '" << data << "'");
}
}
data = extractTransformData(inputString, "rotate");
if (data.size() != 0) {
float angle, xxx, yyy;
if (sscanf(data.c_str(), "%f %f %f", &angle, &xxx, &yyy) == 3) {
angle = angle / 180 * M_PI;
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) {
} else if (sscanf(data.c_str(), "%f", &angle) == 1) {
angle = angle / 180 * M_PI;
ESVG_VERBOSE("rotate : " << angle << "rad, " << (angle/M_PI*180) << "°");
m_transformMatrix *= etk::mat2Rotate(angle);
} else if (sscanf(pointerOnData, "skewX (%f) %n", &angle, &n) == 1) {
} else {
ESVG_ERROR("Parsing rotate() with wrong data ... '" << data << "'");
}
}
data = extractTransformData(inputString, "skewX");
if (data.size() != 0) {
float angle;
if (sscanf(data.c_str(), "%f", &angle) == 1) {
angle = angle / 180 * M_PI;
ESVG_VERBOSE("skewX : " << angle << "rad, " << (angle/M_PI*180) << "°");
m_transformMatrix *= etk::mat2Skew(vec2(angle, 0.0f));
} else if (sscanf(pointerOnData, "skewY (%f) %n", &angle, &n) == 1) {
} else {
ESVG_ERROR("Parsing skewX() with wrong data ... '" << data << "'");
}
}
data = extractTransformData(inputString, "skewY");
if (data.size() != 0) {
float angle;
if (sscanf(data.c_str(), "%f", &angle) == 1) {
angle = angle / 180 * M_PI;
ESVG_VERBOSE("skewY : " << angle << "rad, " << (angle/M_PI*180) << "°");
m_transformMatrix *= etk::mat2Skew(vec2(0.0f, angle));
} else {
break;
ESVG_ERROR("Parsing skewY() with wrong data ... '" << data << "'");
}
pointerOnData += n;
}
}

View File

@ -65,9 +65,11 @@ void esvg::Renderer::print(const esvg::render::Weight& _weightFill,
int32_t sizeX = m_size.x();
int32_t sizeY = m_size.y();
if (_colorFill != nullptr) {
//_colorFill->setViewPort(std::pair<vec2, vec2>(vec2(0,0), vec2(sizeX, sizeY)));
_colorFill->generate(m_document);
}
if (_colorStroke != nullptr) {
//_colorStroke->setViewPort(std::pair<vec2, vec2>(vec2(0,0), vec2(sizeX, sizeY)));
_colorStroke->generate(m_document);
}
#if DEBUG
@ -88,11 +90,13 @@ void esvg::Renderer::print(const esvg::render::Weight& _weightFill,
// calculate merge of stroke and fill value:
etk::Color<float,4> intermediateColorFill(etk::color::none);
etk::Color<float,4> intermediateColorStroke(etk::color::none);
if (_colorFill != nullptr) {
if ( _colorFill != nullptr
&& valueFill != 0.0f) {
intermediateColorFill = _colorFill->getColor(pos);
intermediateColorFill.setA(intermediateColorFill.a()*valueFill);
}
if (_colorStroke != nullptr) {
if ( _colorStroke != nullptr
&& valueStroke != 0.0f) {
intermediateColorStroke = _colorStroke->getColor(pos);
intermediateColorStroke.setA(intermediateColorStroke.a()*valueStroke);
}
@ -101,6 +105,38 @@ void esvg::Renderer::print(const esvg::render::Weight& _weightFill,
m_buffer[sizeX*yyy + xxx] = mergeColor(m_buffer[sizeX*yyy + xxx], intermediateColor);
}
}
#ifdef DEBUG
// display the gradient position:
std::shared_ptr<esvg::render::DynamicColorLinear> tmpColor = std::dynamic_pointer_cast<esvg::render::DynamicColorLinear>(_colorFill);
if (tmpColor != nullptr) {
esvg::render::SegmentList listSegment;
// Display bounding box
listSegment.addSegment(esvg::render::Point(tmpColor->m_viewPort.first),
esvg::render::Point(vec2(tmpColor->m_viewPort.first.x(), tmpColor->m_viewPort.second.y()) ),
false);
listSegment.addSegment(esvg::render::Point(vec2(tmpColor->m_viewPort.first.x(), tmpColor->m_viewPort.second.y()) ),
esvg::render::Point(tmpColor->m_viewPort.second),
false);
listSegment.addSegment(esvg::render::Point(tmpColor->m_viewPort.second),
esvg::render::Point(vec2(tmpColor->m_viewPort.second.x(), tmpColor->m_viewPort.first.y()) ),
false);
listSegment.addSegment(esvg::render::Point(vec2(tmpColor->m_viewPort.second.x(), tmpColor->m_viewPort.first.y()) ),
esvg::render::Point(tmpColor->m_viewPort.first),
false);
listSegment.applyMatrix(tmpColor->m_matrix);
// display the gradient axis
listSegment.addSegment(esvg::render::Point(tmpColor->m_pos1),
esvg::render::Point(tmpColor->m_pos2),
false);
/*
mat2 m_matrix;
std::pair<vec2, vec2> m_viewPort;
vec2 m_pos1;
vec2 m_pos2;
*/
addDebugSegment(listSegment);
}
#endif
}
#ifdef DEBUG

View File

@ -11,6 +11,9 @@
#include <esvg/LinearGradient.h>
#include <esvg/esvg.h>
#undef __class__
#define __class__ "render:DynamicColorLinear"
esvg::render::DynamicColorLinear::DynamicColorLinear(const std::string& _link, const mat2& _mtx) :
m_colorName(_link),
m_matrix(_mtx),
@ -45,19 +48,46 @@ etk::Color<float,4> esvg::render::DynamicColorLinear::getColor(const ivec2& _pos
if (m_data.size() < 2) {
return etk::color::purple;
}
vec2 vectorBase = m_pos2 - m_pos1;
vec2 vectorOrtho(vectorBase.y(), -vectorBase.x());
vec2 intersec = getIntersect(m_pos1, vectorBase,
vec2(_pos.x(), _pos.y()), vectorOrtho);
float baseSize = vectorBase.length();
float baseDraw = (m_pos1 - intersec).length();
float ratio = baseDraw / baseSize;
#if 0
vec2 vectorBase = m_pos2 - m_pos1;
vec2 vectorOrtho(vectorBase.y(), -vectorBase.x());
vec2 intersec = getIntersect(m_pos1, vectorBase,
vec2(_pos.x(), _pos.y()), vectorOrtho);
float baseSize = vectorBase.length();
float baseDraw = (m_pos1 - intersec).length();
float ratio = baseDraw / baseSize;
#else
// in the basic vertion of the gradient the color is calculated with the ration in X and Y in the bonding box associated (it is rotate with the object..
vec2 intersecX = getIntersect(m_pos1, m_axeX,
vec2(_pos.x(), _pos.y()), m_axeY);
vec2 intersecY = getIntersect(m_pos1, m_axeY,
vec2(_pos.x(), _pos.y()), m_axeX);
float baseDrawX = (m_pos1 - intersecX).length();
float baseDrawY = (m_pos1 - intersecY).length();
float ratio = 0.0f;
if (m_baseSize.x() != 0.0f) {
if (m_baseSize.y() != 0.0f) {
ratio += baseDrawX/m_baseSize.x() * 0.5f;
} else {
ratio += baseDrawX/m_baseSize.x();
}
}
if (m_baseSize.y() != 0.0f) {
if (m_baseSize.x() != 0.0f) {
ratio += baseDrawY/m_baseSize.y() * 0.5f;
} else {
ratio += baseDrawY/m_baseSize.y();
}
}
#endif
//ESVG_DEBUG("plop " << ratio);
if (ratio <= m_data[0].first*0.01f) {
return m_data[0].second;
}
if (ratio >= m_data.back().first*0.01f) {
return m_data.back().second;
}
for (size_t iii=1; iii<m_data.size(); ++iii) {
if (ratio <= m_data[iii].first*0.01f) {
float localRatio = ratio - m_data[iii-1].first*0.01f;
@ -104,10 +134,24 @@ void esvg::render::DynamicColorLinear::generate(esvg::Document* _document) {
// Move the positions ...
m_pos1 = m_matrix * m_pos1;
m_pos2 = m_matrix * m_pos2;
// in the basic vertion of the gradient the color is calculated with the ration in X and Y in the bonding box associated (it is rotate with the object..
m_axeX = m_matrix.applyScaleRotation(vec2(1.0f, 0.0f));
m_axeY = m_matrix.applyScaleRotation(vec2(0.0f, 1.0f));
// in the basic vertion of the gradient the color is calculated with the ration in X and Y in the bonding box associated (it is rotate with the object..
vec2 intersecX = getIntersect(m_pos1, m_axeX,
m_pos2, m_axeY);
vec2 intersecY = getIntersect(m_pos1, m_axeY,
m_pos2, m_axeX);
m_baseSize = vec2((m_pos1 - intersecX).length(),
(m_pos1 - intersecY).length());
// get all the colors
m_data = gradient->getColors();
}
#undef __class__
#define __class__ "render:DynamicColor"
std::shared_ptr<esvg::render::DynamicColor> esvg::render::createColor(std::pair<etk::Color<float,4>, std::string> _color, const mat2& _mtx) {
// Check if need to create a color:
if ( _color.first.a() == 0x00

View File

@ -53,6 +53,9 @@ namespace esvg {
std::pair<vec2, vec2> m_viewPort;
vec2 m_pos1;
vec2 m_pos2;
vec2 m_axeX;
vec2 m_axeY;
vec2 m_baseSize;
std::vector<std::pair<float, etk::Color<float,4>>> m_data;
public:
DynamicColorLinear(const std::string& _link, const mat2& _mtx);

View File

@ -28,6 +28,16 @@ void esvg::render::SegmentList::addSegment(const esvg::render::Point& _pos0, con
m_data.push_back(Segment(_pos0.m_pos, _pos1.m_pos));
}
void esvg::render::SegmentList::addSegment(const esvg::render::Point& _pos0, const esvg::render::Point& _pos1, bool _disableHorizontal) {
// Skip horizontal Segments
if ( _disableHorizontal == true
&& _pos0.m_pos.y() == _pos1.m_pos.y()) {
// remove /0 operation
return;
}
m_data.push_back(Segment(_pos0.m_pos, _pos1.m_pos));
}
std::pair<vec2, vec2> esvg::render::SegmentList::getViewPort() {
std::pair<vec2, vec2> out(vec2(9999999999.0,9999999999.0),vec2(-9999999999.0,-9999999999.0));
for (auto &it : m_data) {

View File

@ -24,6 +24,7 @@ namespace esvg {
public:
SegmentList();
void addSegment(const esvg::render::Point& _pos0, const esvg::render::Point& _pos1);
void addSegment(const esvg::render::Point& _pos0, const esvg::render::Point& _pos1, bool _disableHorizontal);
void createSegmentList(const esvg::render::PointList& _listPoint);
void createSegmentListStroke(esvg::render::PointList& _listPoint,
float _width,

View File

@ -18,8 +18,10 @@ TEST(TestGradientLinear, horizontal) {
"<svg height='100' width='100'>\n"
" <defs>\n"
" <linearGradient id='grad1' x1='0%' y1='0%' x2='100%' y2='0%'>\n"
" <stop offset='0%' style='stop-color:rgb(255,255,0);stop-opacity:1' />\n"
" <stop offset='100%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
" <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
" <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
" <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
" <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n"
" </linearGradient>\n"
" </defs>\n"
" <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n"
@ -36,8 +38,10 @@ TEST(TestGradientLinear, vertical) {
"<svg height='100' width='100'>\n"
" <defs>\n"
" <linearGradient id='grad2' x1='0%' y1='0%' x2='0%' y2='100%'>\n"
" <stop offset='0%' style='stop-color:rgb(255,255,0);stop-opacity:1' />\n"
" <stop offset='100%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
" <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
" <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
" <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
" <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n"
" </linearGradient>\n"
" </defs>\n"
" <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n"
@ -53,8 +57,10 @@ TEST(TestGradientLinear, diag1) {
"<svg height='100' width='100'>\n"
" <defs>\n"
" <linearGradient id='grad2' x1='0%' y1='0%' x2='100%' y2='100%'>\n"
" <stop offset='0%' style='stop-color:rgb(255,255,0);stop-opacity:1' />\n"
" <stop offset='100%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
" <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
" <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
" <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
" <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n"
" </linearGradient>\n"
" </defs>\n"
" <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n"
@ -70,8 +76,10 @@ TEST(TestGradientLinear, diag2) {
"<svg height='100' width='100'>\n"
" <defs>\n"
" <linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%'>\n"
" <stop offset='0%' style='stop-color:rgb(255,255,0);stop-opacity:1' />\n"
" <stop offset='100%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
" <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
" <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
" <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
" <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n"
" </linearGradient>\n"
" </defs>\n"
" <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n"
@ -83,3 +91,79 @@ TEST(TestGradientLinear, diag2) {
}
TEST(TestGradientLinear, diag2Rotate0) {
std::string data("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"
"<svg height='100' width='100'>\n"
" <defs>\n"
" <linearGradient id='grad2' x1='0%' y1='50%' x2='100%' y2='50%'>\n"
" <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
" <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
" <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
" <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n"
" </linearGradient>\n"
" </defs>\n"
" <ellipse transform='rotate (30 50 50)' cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n"
"</svg>\n");
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestGradientLinear_diag2Rotate0.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestGradientLinear_diag2Rotate0.bmp", g_visualDebug);
}
TEST(TestGradientLinear, diag2Rotate1) {
std::string data("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"
"<svg height='100' width='100'>\n"
" <defs>\n"
" <linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%'>\n"
" <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
" <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
" <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
" <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n"
" </linearGradient>\n"
" </defs>\n"
" <ellipse transform='rotate (45 50 50)' cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n"
"</svg>\n");
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestGradientLinear_diag2Rotate1.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestGradientLinear_diag2Rotate1.bmp", g_visualDebug);
}
TEST(TestGradientLinear, diag2Rotate2) {
std::string data("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"
"<svg height='100' width='100'>\n"
" <defs>\n"
" <linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%'>\n"
" <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
" <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
" <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
" <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n"
" </linearGradient>\n"
" </defs>\n"
" <ellipse transform='rotate (-45 50 50)' cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n"
"</svg>\n");
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestGradientLinear_diag2Rotate2.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestGradientLinear_diag2Rotate2.bmp", g_visualDebug);
}
TEST(TestGradientLinear, diag2scale) {
std::string data("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"
"<svg height='100' width='100'>\n"
" <defs>\n"
" <linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%'>\n"
" <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
" <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
" <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
" <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n"
" </linearGradient>\n"
" </defs>\n"
" <ellipse transform='scale (0.5 2.0) translate (10,-25)' cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n"
"</svg>\n");
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestGradientLinear_diag2scale.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestGradientLinear_diag2scale.bmp", g_visualDebug);
}