[DEBUG] correct the display error of linear gradient

This commit is contained in:
Edouard DUPIN 2015-12-20 21:23:40 +01:00
parent c613a23825
commit 8610d6491b
7 changed files with 545 additions and 65 deletions

View File

@ -280,3 +280,174 @@ namespace etk {
return from_string(_variableRet, etk::to_string(_value));
}
};
#undef __class__
#define __class__ "Dimension1D"
esvg::Dimension1D::Dimension1D() :
m_data(0.0f),
m_type(esvg::distance_pixel) {
// notinh to do ...
}
esvg::Dimension1D::Dimension1D(float _size, enum esvg::distance _type) :
m_data(0.0f),
m_type(esvg::distance_pixel) {
set(_size, _type);
}
void esvg::Dimension1D::set(std::string _config) {
m_data = 0;
m_type = esvg::distance_pixel;
enum distance type = esvg::distance_pixel;
if (etk::end_with(_config, "%", false) == true) {
type = esvg::distance_pourcent;
_config.erase(_config.size()-1, 1);
} else if (etk::end_with(_config, "px",false) == true) {
type = esvg::distance_pixel;
_config.erase(_config.size()-2, 2);
} else if (etk::end_with(_config, "ft",false) == true) {
type = esvg::distance_foot;
_config.erase(_config.size()-2, 2);
} else if (etk::end_with(_config, "in",false) == true) {
type = esvg::distance_inch;
_config.erase(_config.size()-2, 2);
} else if (etk::end_with(_config, "km",false) == true) {
type = esvg::distance_kilometer;
_config.erase(_config.size()-2, 2);
} else if (etk::end_with(_config, "mm",false) == true) {
type = esvg::distance_millimeter;
_config.erase(_config.size()-2, 2);
} else if (etk::end_with(_config, "cm",false) == true) {
type = esvg::distance_centimeter;
_config.erase(_config.size()-2, 2);
} else if (etk::end_with(_config, "m",false) == true) {
type = esvg::distance_meter;
_config.erase(_config.size()-1, 1);
} else {
ESVG_CRITICAL("Can not parse dimention : \"" << _config << "\"");
return;
}
float tmp = etk::string_to_float(_config);
set(tmp, type);
ESVG_VERBOSE(" config dimention : \"" << _config << "\" == > " << *this );
}
esvg::Dimension1D::~Dimension1D() {
// nothing to do ...
}
esvg::Dimension1D::operator std::string() const {
std::string str;
str = getValue();
switch(getType()) {
case esvg::distance_pourcent:
str += "%";
break;
case esvg::distance_pixel:
str += "px";
break;
case esvg::distance_meter:
str += "m";
break;
case esvg::distance_centimeter:
str += "cm";
break;
case esvg::distance_millimeter:
str += "mm";
break;
case esvg::distance_kilometer:
str += "km";
break;
case esvg::distance_inch:
str += "in";
break;
case esvg::distance_foot:
str += "ft";
break;
case esvg::distance_element:
str += "em";
break;
case esvg::distance_ex:
str += "ex";
break;
case esvg::distance_point:
str += "pt";
break;
case esvg::distance_pc:
str += "pc";
break;
}
return str;
}
void esvg::Dimension1D::set(float _size, enum esvg::distance _type) {
m_data = _size;
m_type = _type;
switch(_type) {
case esvg::distance_pourcent:
case esvg::distance_pixel:
// nothing to do: Supported ...
break;
case esvg::distance_meter:
case esvg::distance_centimeter:
case esvg::distance_millimeter:
case esvg::distance_kilometer:
case esvg::distance_inch:
case esvg::distance_foot:
case esvg::distance_element:
case esvg::distance_ex:
case esvg::distance_point:
case esvg::distance_pc:
ESVG_ERROR("Does not support other than Px and % type of dimention1D : " << _type << " automaticly convert with {72,72} pixel/inch");
break;
}
}
float esvg::Dimension1D::getPixel(float _upperSize) const {
switch(m_type) {
case esvg::distance_pourcent:
return _upperSize*m_data*0.01f;
case esvg::distance_pixel:
return m_data;
case esvg::distance_meter:
return m_data*meterToMillimeter*basicRatio;
case esvg::distance_centimeter:
return m_data*centimeterToMillimeter*basicRatio;
case esvg::distance_millimeter:
return m_data*basicRatio;
case esvg::distance_kilometer:
return m_data*kilometerToMillimeter*basicRatio;
case esvg::distance_inch:
return m_data*inchToMillimeter*basicRatio;
case esvg::distance_foot:
return m_data*footToMillimeter*basicRatio;
}
return 128.0f;
}
std::ostream& esvg::operator <<(std::ostream& _os, const esvg::Dimension1D& _obj) {
_os << _obj.getValue() << _obj.getType();
return _os;
}
namespace etk {
template<> std::string to_string<esvg::Dimension1D>(const esvg::Dimension1D& _obj) {
return _obj;
}
template<> std::u32string to_u32string<esvg::Dimension1D>(const esvg::Dimension1D& _obj) {
return etk::to_u32string(etk::to_string(_obj));
}
template<> bool from_string<esvg::Dimension1D>(esvg::Dimension1D& _variableRet, const std::string& _value) {
_variableRet = esvg::Dimension1D(_value);
return true;
}
template<> bool from_string<esvg::Dimension1D>(esvg::Dimension1D& _variableRet, const std::u32string& _value) {
return from_string(_variableRet, etk::to_string(_value));
}
};

View File

@ -149,6 +149,110 @@ namespace esvg {
};
std::ostream& operator <<(std::ostream& _os, enum esvg::distance _obj);
std::ostream& operator <<(std::ostream& _os, const esvg::Dimension& _obj);
/**
* @brief in the dimention class we store the data as the more usefull unit (pixel)
* but one case need to be dynamic the %, then when requested in % the register the % value
*/
class Dimension1D {
private:
float m_data;
enum distance m_type;
public:
/**
* @brief Constructor (default :0,0 mode pixel)
*/
Dimension1D();
/**
* @brief Constructor
* @param[in] _size Requested dimention
* @param[in] _type Unit of the Dimention
*/
Dimension1D(float _size, enum esvg::distance _type=esvg::distance_pixel);
/**
* @brief Constructor
* @param[in] _config dimension configuration.
*/
Dimension1D(const std::string& _config) :
m_data(0.0f),
m_type(esvg::distance_pixel) {
set(_config);
};
/**
* @brief Destructor
*/
~Dimension1D();
/**
* @brief string cast :
*/
operator std::string() const;
/**
* @brief get the current dimention.
* @return dimention requested.
*/
const float& getValue() const {
return m_data;
}
/**
* @breif get the dimension type
* @return the type
*/
enum distance getType() const {
return m_type;
};
/**
* @brief set the current dimention in requested type
* @param[in] _size Dimention to set
* @param[in] _type Type of unit requested.
*/
void set(float _size, enum distance _type);
public:
/**
* @brief set the current dimention in requested type
* @param[in] _config dimension configuration.
*/
void set(std::string _config);
public:
/**
* @brief get the current dimention in pixel
* @param[in] _upperSize Size in pixel of the upper value
* @return dimention in Pixel
*/
float getPixel(float _upperSize) const;
/*****************************************************
* = assigment
*****************************************************/
const Dimension1D& operator= (const Dimension1D& _obj ) {
if (this!=&_obj) {
m_data = _obj.m_data;
m_type = _obj.m_type;
}
return *this;
}
/*****************************************************
* == operator
*****************************************************/
bool operator == (const Dimension1D& _obj) const {
if( m_data == _obj.m_data
&& m_type == _obj.m_type) {
return true;
}
return false;
}
/*****************************************************
* != operator
*****************************************************/
bool operator!= (const Dimension1D& _obj) const {
if( m_data != _obj.m_data
|| m_type != _obj.m_type) {
return true;
}
return false;
}
};
std::ostream& operator <<(std::ostream& _os, const esvg::Dimension1D& _obj);
};
#endif

View File

@ -8,6 +8,7 @@
#include <esvg/debug.h>
#include <esvg/LinearGradient.h>
#include <esvg/RadialGradient.h>
#include <esvg/render/Path.h>
#include <esvg/render/Weight.h>
#include <esvg/esvg.h>
@ -15,9 +16,13 @@
#undef __class__
#define __class__ "LinearGradient"
esvg::LinearGradient::LinearGradient(PaintState _parentPaintState) : esvg::Base(_parentPaintState) {
m_pos1.set(vec2(0,0), esvg::distance_pixel);
m_pos2.set(vec2(0,0), esvg::distance_pixel);
esvg::LinearGradient::LinearGradient(PaintState _parentPaintState) :
esvg::Base(_parentPaintState),
m_pos1(vec2(50,50), esvg::distance_pourcent),
m_pos2(vec2(50,50), esvg::distance_pourcent),
m_unit(gradientUnits_objectBoundingBox),
m_spread(spreadMethod_pad) {
}
esvg::LinearGradient::~LinearGradient() {
@ -160,12 +165,16 @@ const std::vector<std::pair<float, etk::Color<float,4>>>& esvg::LinearGradient::
ESVG_ERROR("Can not get base : '" << m_href << "'");
return m_data;
}
std::shared_ptr<esvg::LinearGradient> gradient = std::dynamic_pointer_cast<esvg::LinearGradient>(base);
if (gradient == nullptr) {
ESVG_ERROR("Can not cast in a linear gradient: '" << m_href << "' ==> wrong type");
return m_data;
std::shared_ptr<esvg::RadialGradient> gradientR = std::dynamic_pointer_cast<esvg::RadialGradient>(base);
if (gradientR == nullptr) {
std::shared_ptr<esvg::LinearGradient> gradientL = std::dynamic_pointer_cast<esvg::LinearGradient>(base);
if (gradientL == nullptr) {
ESVG_ERROR("Can not cast in a linear/radial gradient: '" << m_href << "' ==> wrong type");
return m_data;
}
return gradientL->getColors(_document);
}
return gradient->getColors(_document);
return gradientR->getColors(_document);
}

View File

@ -0,0 +1,190 @@
/**
* @author Edouard DUPIN
*
* @copyright 2011, Edouard DUPIN, all right reserved
*
* @license APACHE v2.0 (see license file)
*/
#include <esvg/debug.h>
#include <esvg/LinearGradient.h>
#include <esvg/RadialGradient.h>
#include <esvg/render/Path.h>
#include <esvg/render/Weight.h>
#include <esvg/esvg.h>
#undef __class__
#define __class__ "RadialGradient"
esvg::RadialGradient::RadialGradient(PaintState _parentPaintState) :
esvg::Base(_parentPaintState),
m_center(vec2(50,50), esvg::distance_pourcent),
m_radius(50, esvg::distance_pourcent),
m_focal(vec2(50,50), esvg::distance_pourcent),
m_unit(gradientUnits_objectBoundingBox),
m_spread(spreadMethod_pad) {
}
esvg::RadialGradient::~RadialGradient() {
}
bool esvg::RadialGradient::parseXML(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax) {
// line must have a minimum size...
//m_paint.strokeWidth = 1;
if (_element == nullptr) {
return false;
}
// ---------------- get unique ID ----------------
m_id = _element->getAttribute("id");
//parseTransform(_element);
//parsePaintAttr(_element);
// add the property of the parrent modifications ...
m_transformMatrix *= _parentTrans;
std::string contentX = _element->getAttribute("cx");
std::string contentY = _element->getAttribute("cy");
if ( contentX != ""
&& contentY != "") {
m_center.set(contentX, contentY);
}
contentX = _element->getAttribute("r");
if (contentX != "") {
m_radius.set(contentX);
}
contentX = _element->getAttribute("fx");
contentY = _element->getAttribute("fy");
if ( contentX != ""
&& contentY != "") {
m_focal.set(contentX, contentY);
}
contentX = _element->getAttribute("gradientUnits");
if (contentX == "userSpaceOnUse") {
m_unit = gradientUnits_userSpaceOnUse;
} else {
m_unit = gradientUnits_objectBoundingBox;
if ( contentX.size() != 0
&& contentX != "objectBoundingBox") {
ESVG_ERROR("Parsing error of 'gradientUnits' ==> not suported value: '" << contentX << "' not in : {userSpaceOnUse/objectBoundingBox} use objectBoundingBox");
}
}
contentX = _element->getAttribute("spreadMethod");
if (contentX == "reflect") {
m_spread = spreadMethod_reflect;
} else if (contentX == "repeat") {
m_spread = spreadMethod_repeat;
} else {
m_spread = spreadMethod_pad;
if ( contentX.size() != 0
&& contentX != "pad") {
ESVG_ERROR("Parsing error of 'spreadMethod' ==> not suported value: '" << contentX << "' not in : {reflect/repeate/pad} use pad");
}
}
// note: xlink:href is incompatible with subNode "stop"
m_href = _element->getAttribute("xlink:href");
if (m_href.size() != 0) {
m_href = std::string(m_href.begin()+1, m_href.end());
}
// parse all sub node :
for(int32_t iii=0; iii<_element->size() ; iii++) {
std::shared_ptr<exml::Element> child = _element->getElement(iii);
if (child == nullptr) {
// can be a comment ...
continue;
}
if (child->getValue() == "stop") {
float offset = 100;
etk::Color<float,4> stopColor = etk::color::none;
std::string content = child->getAttribute("offset");
if (content.size()!=0) {
std::pair<float, enum esvg::distance> tmp = parseLength2(content);
if (tmp.second == esvg::distance_pixel) {
// special case ==> all time % then no type define ==> % in [0.0 .. 1.0]
offset = tmp.first*100.0f;
} else if (tmp.second != esvg::distance_pourcent) {
ESVG_ERROR("offset : " << content << " res=" << tmp.first << "," << tmp.second << " Not support other than pourcent %");
} else {
offset = tmp.first;
}
}
content = child->getAttribute("stop-color");
if (content.size()!=0) {
stopColor = parseColor(content).first;
ESVG_VERBOSE(" color : \"" << content << "\" == > " << stopColor);
}
content = child->getAttribute("stop-opacity");
if (content.size()!=0) {
float opacity = parseLength(content);
opacity = std::avg(0.0f, opacity, 1.0f);
stopColor.setA(opacity);
ESVG_VERBOSE(" opacity : \"" << content << "\" == > " << stopColor);
}
m_data.push_back(std::pair<float, etk::Color<float,4>>(offset, stopColor));
} else {
ESVG_ERROR("(l " << child->getPos() << ") node not suported : \"" << child->getValue() << "\" must be [stop]");
}
}
if (m_data.size() != 0) {
if (m_href != "") {
ESVG_ERROR("(l " << _element->getPos() << ") node can not have an xlink:href element with sub node named: stop ==> removing href");
m_href = "";
}
}
return true;
}
void esvg::RadialGradient::display(int32_t _spacing) {
ESVG_DEBUG(spacingDist(_spacing) << "RadialGradient center=" << m_center << " focal=" << m_focal << " radius=" << m_radius);
for (auto &it : m_data) {
ESVG_DEBUG(spacingDist(_spacing+1) << "STOP: offset=" << it.first << " color=" << it.second);
}
}
void esvg::RadialGradient::draw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level) {
ESVG_VERBOSE(spacingDist(_level) << "DRAW esvg::RadialGradient");
}
const esvg::Dimension& esvg::RadialGradient::getCenter() {
return m_center;
}
const esvg::Dimension& esvg::RadialGradient::getFocal() {
return m_focal;
}
const esvg::Dimension1D& esvg::RadialGradient::getRadius() {
return m_radius;
}
const std::vector<std::pair<float, etk::Color<float,4>>>& esvg::RadialGradient::getColors(esvg::Document* _document) {
if (m_href == "") {
return m_data;
}
if (_document == nullptr) {
ESVG_ERROR("Get nullptr input for document");
return m_data;
}
std::shared_ptr<esvg::Base> base = _document->getReference(m_href);
if (base == nullptr) {
ESVG_ERROR("Can not get base : '" << m_href << "'");
return m_data;
}
std::shared_ptr<esvg::RadialGradient> gradientR = std::dynamic_pointer_cast<esvg::RadialGradient>(base);
if (gradientR == nullptr) {
std::shared_ptr<esvg::LinearGradient> gradientL = std::dynamic_pointer_cast<esvg::LinearGradient>(base);
if (gradientL == nullptr) {
ESVG_ERROR("Can not cast in a linear/radial gradient: '" << m_href << "' ==> wrong type");
return m_data;
}
return gradientL->getColors(_document);
}
return gradientR->getColors(_document);
}

View File

@ -0,0 +1,43 @@
/**
* @author Edouard DUPIN
*
* @copyright 2011, Edouard DUPIN, all right reserved
*
* @license APACHE v2.0 (see license file)
*/
#ifndef __ESVG_RADIAL_GRANDIENT_H__
#define __ESVG_RADIAL_GRANDIENT_H__
#include <esvg/Base.h>
#include <esvg/gradientUnits.h>
#include <esvg/spreadMethod.h>
namespace esvg {
class Document;
class RadialGradient : public esvg::Base {
private:
esvg::Dimension m_center; //!< gradient position cx cy
esvg::Dimension1D m_radius; //!< Radius of the gradient
esvg::Dimension m_focal; //!< gradient Focal fx fy
public:
enum gradientUnits m_unit;
enum spreadMethod m_spread;
private:
std::string m_href; //!< in case of using a single gradient in multiple gradient, the gradient is store in an other element...
std::vector<std::pair<float, etk::Color<float,4>>> m_data; //!< incompatible with href
public:
RadialGradient(PaintState _parentPaintState);
~RadialGradient();
virtual bool parseXML(const std::shared_ptr<exml::Element>& _element, mat2& _parentTrans, vec2& _sizeMax);
virtual void display(int32_t _spacing);
virtual void draw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level);
public:
const esvg::Dimension& getCenter();
const esvg::Dimension& getFocal();
const esvg::Dimension1D& getRadius();
const std::vector<std::pair<float, etk::Color<float,4>>>& getColors(esvg::Document* _document);
};
};
#endif

View File

@ -43,7 +43,7 @@ static vec2 getIntersect(const vec2& _point1,
return _point2;
}
// TODO : This can optimize ... really slow ...
// TODO : This can optimize ... really slow ... and not linear
etk::Color<float,4> esvg::render::DynamicColorLinear::getColor(const ivec2& _pos) {
if (m_data.size() < 2) {
return etk::color::purple;
@ -90,43 +90,24 @@ etk::Color<float,4> esvg::render::DynamicColorLinear::getColor(const ivec2& _pos
vec2 vectorBaseDrawY = intersecY - m_pos1;
float baseDrawX = vectorBaseDrawX.length();
float baseDrawY = vectorBaseDrawY.length();
#if 1
if (m_axeX.dot(vectorBaseDrawX) < 0) {
baseDrawX *= -1.0f;
}
if (m_axeY.dot(vectorBaseDrawY) < 0) {
baseDrawY *= -1.0f;
}
if (m_baseSize.x()+m_baseSize.y() != 0.0f) {
if ( m_baseSize.x() != 0.0f
&& m_baseSize.y() != 0.0f) {
ratio = (baseDrawX*m_baseSize.y() + baseDrawY*m_baseSize.x())/(m_baseSize.x()*m_baseSize.y()*2.0f);
} else if (m_baseSize.x() != 0.0f) {
ratio = baseDrawX/m_baseSize.x();
} else {
ratio = baseDrawY/m_baseSize.y();
}
if (m_axeX.dot(vectorBaseDrawX) < 0) {
baseDrawX *= -1.0f;
}
if (m_axeY.dot(vectorBaseDrawY) < 0) {
baseDrawY *= -1.0f;
}
if (m_baseSize.x()+m_baseSize.y() != 0.0f) {
if ( m_baseSize.x() != 0.0f
&& m_baseSize.y() != 0.0f) {
ratio = (baseDrawX*m_baseSize.y() + baseDrawY*m_baseSize.x())/(m_baseSize.x()*m_baseSize.y()*2.0f);
} else if (m_baseSize.x() != 0.0f) {
ratio = baseDrawX/m_baseSize.x();
} else {
ratio = 1.0f;
ratio = baseDrawY/m_baseSize.y();
}
#else
bool dotX = m_axeX.dot(vectorBaseDrawX) < 0;
bool dotY = m_axeY.dot(vectorBaseDrawY) < 0;
if (m_baseSize.x()+m_baseSize.y() != 0.0f) {
if (dotX != dotY) {
if (dotX == false) {
ratio = (-baseDrawX*m_baseSize.y() + baseDrawY*m_baseSize.x())/(m_baseSize.x()*m_baseSize.y()*2.0f);
} else {
ratio = (baseDrawX*m_baseSize.y() - baseDrawY*m_baseSize.x())/(m_baseSize.x()*m_baseSize.y()*2.0f);
}
if (ratio < 0.0f) {
ratio *= -1;
}
} else {
ratio = (baseDrawX*m_baseSize.y() + baseDrawY*m_baseSize.x())/(m_baseSize.x()*m_baseSize.y()*2.0f);
}
}
#endif
} else {
ratio = 1.0f;
}
switch(m_spread) {
case spreadMethod_pad:
// nothing to do ...
@ -197,9 +178,6 @@ void esvg::render::DynamicColorLinear::generate(esvg::Document* _document) {
if (dimPos2.getType() == esvg::distance_pourcent) {
m_pos2 += m_viewPort.first;
}
// 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..
vec2 delta = m_pos2 - m_pos1;
if (delta.x() < 0.0f) {
@ -212,6 +190,9 @@ void esvg::render::DynamicColorLinear::generate(esvg::Document* _document) {
} else {
m_axeY = vec2(0.0f, 1.0f);
}
// Move the positions ...
m_pos1 = m_matrix * m_pos1;
m_pos2 = m_matrix * m_pos2;
m_axeX = m_matrix.applyScaleRotation(m_axeX);
m_axeY = m_matrix.applyScaleRotation(m_axeY);
// 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..

View File

@ -205,24 +205,6 @@ TEST(TestGradientLinear, internalHref) {
doc.generateAnImage(ivec2(100, 100), "TestGradientLinear_internalHref.bmp", g_visualDebug);
}
TEST(TestGradientLinear, inkscape) {
std::string data("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"
"<svg height='100' width='100'>\n"
" <defs>\n"
" <linearGradient id='grad2Values'>\n"
" <stop offset='0' style='stop-color:#ba4f4f;stop-opacity:1' />\n"
" <stop offset='1' style='stop-color:#57ba4f;stop-opacity:1' />\n"
" </linearGradient>\n"
" <linearGradient id='grad2' x1='40.653418' y1='63.601116' x2='71.911972' y2='47.372902' gradientUnits='userSpaceOnUse' xlink:href='#grad2Values' />\n"
" </defs>\n"
" <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n"
"</svg>\n");
esvg::Document doc;
doc.parse(data);
etk::FSNodeWriteAllData("TestGradientLinear_inkscape.svg", data);
doc.generateAnImage(ivec2(100, 100), "TestGradientLinear_inkscape.bmp", g_visualDebug);
}
TEST(TestGradientLinear, unitBox_spreadNone) {
std::string data("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"