esvg/esvg/Base.cpp

440 lines
14 KiB
C++

/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file)
*/
#include <esvg/debug.hpp>
#include <esvg/Base.hpp>
#include <cmath>
const float esvg::kappa90(0.5522847493f);
esvg::PaintState::PaintState() :
fill(std::pair<etk::Color<float,4>, std::string>(etk::color::black, "")),
stroke(std::pair<etk::Color<float,4>, std::string>(etk::color::none, "")),
strokeWidth(1.0f),
flagEvenOdd(false),
lineCap(esvg::cap_butt),
lineJoin(esvg::join_miter),
miterLimit(4.0f),
viewPort(vec2(0.0f,0.0f), vec2(0.0f,0.0f)),
opacity(1.0) {
}
void esvg::PaintState::clear() {
fill = std::pair<etk::Color<float,4>, std::string>(etk::color::black, "");
stroke = std::pair<etk::Color<float,4>, std::string>(etk::color::none, "");
strokeWidth = 1.0;
viewPort.first.setValue(0.0f,0.0f);
viewPort.first.setValue(0.0f,0.0f);
flagEvenOdd = false;
lineJoin = esvg::join_miter;
lineCap = esvg::cap_butt;
miterLimit = 4.0f;
opacity = 1.0;
}
esvg::Base::Base(PaintState _parentPaintState) {
// copy the parent painting properties ...
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 exml::Element& _element) {
if (_element.exist() == false) {
return;
}
std::string inputString = _element.attributes["transform"];
if (inputString.size() == 0) {
return;
}
ESVG_VERBOSE("find transform : \"" << inputString << "\"");
for (int32_t iii=0; iii<inputString.size(); iii++) {
if (inputString[iii] == ',') {
inputString[iii] = ' ';
}
}
ESVG_VERBOSE("find transform : \"" << inputString << "\"");
// 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);
// 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(data.c_str(), "%f", &xxx) == 1) {
m_transformMatrix *= etk::mat2Translate(vec2(xxx, 0));
ESVG_VERBOSE("Translate : " << xxx << ", " << 0);
} 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(data.c_str(), "%f", &xxx) == 1) {
m_transformMatrix *= etk::mat2Scale(xxx);
ESVG_VERBOSE("Scale : " << xxx << ", " << xxx);
} 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(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 {
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 {
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 {
ESVG_ERROR("Parsing skewY() with wrong data ... '" << data << "'");
}
}
}
void esvg::Base::parsePosition(const exml::Element& _element, vec2 &_pos, vec2 &_size) {
_pos.setValue(0,0);
_size.setValue(0,0);
if (_element.exist() == false) {
return;
}
std::string content = _element.attributes["x"];
if (content.size()!=0) {
_pos.setX(parseLength(content));
}
content = _element.attributes["y"];
if (content.size()!=0) {
_pos.setY(parseLength(content));
}
content = _element.attributes["width"];
if (content.size()!=0) {
_size.setX(parseLength(content));
}
content = _element.attributes["height"];
if (content.size()!=0) {
_size.setY(parseLength(content));
}
}
std::pair<float, enum esvg::distance> esvg::Base::parseLength2(const std::string& _dataInput) {
ESVG_VERBOSE(" lenght : '" << _dataInput << "'");
float n = stof(_dataInput);
std::string unit;
for (int32_t iii=0; iii<_dataInput.size(); ++iii) {
if( (_dataInput[iii]>='0' && _dataInput[iii]<='9')
|| _dataInput[iii]=='+'
|| _dataInput[iii]=='-'
|| _dataInput[iii]=='.') {
continue;
}
unit = std::string(_dataInput, iii);
break;
}
ESVG_VERBOSE(" lenght : '" << n << "' => unit=" << unit);
// note : ";" is for the parsing of the style elements ...
if(unit.size() == 0) {
return std::make_pair(n, esvg::distance_pixel);
} else if (unit[0] == '%') { // xxx %
return std::make_pair(n, esvg::distance_pourcent);
} else if ( unit[0] == 'e'
&& unit[1] == 'm') { // xxx em
return std::make_pair(n, esvg::distance_element);
} else if ( unit[0] == 'e'
&& unit[1] == 'x') { // xxx ex
return std::make_pair(n, esvg::distance_ex);
} else if ( unit[0] == 'p'
&& unit[1] == 'x') { // xxx px
return std::make_pair(n, esvg::distance_pixel);
} else if ( unit[0] == 'p'
&& unit[1] == 't') { // xxx pt
return std::make_pair(n, esvg::distance_point);
} else if ( unit[0] == 'p'
&& unit[1] == 'c') { // xxx pc
return std::make_pair(n, esvg::distance_pc);
} else if ( unit[0] == 'm'
&& unit[1] == 'm') { // xxx mm
return std::make_pair(n, esvg::distance_millimeter);
} else if ( unit[0] == 'c'
&& unit[1] == 'm') { // xxx cm
return std::make_pair(n, esvg::distance_centimeter);
} else if ( unit[0] == 'i'
&& unit[1] == 'n') { // xxx in
return std::make_pair(n, esvg::distance_inch);
}
return std::make_pair(0.0f, esvg::distance_pixel);
}
float esvg::Base::parseLength(const std::string& _dataInput) {
std::pair<float, enum esvg::distance> value = parseLength2(_dataInput);
ESVG_VERBOSE(" lenght : '" << value.first << "' => unit=" << value.second);
float font_size = 20.0f;
switch (value.second) {
case esvg::distance_pourcent:
return value.first;// / 100.0 * m_paint.viewPort.x();
case esvg::distance_element:
return value.first * font_size;
case esvg::distance_ex:
return value.first / 2.0f * font_size;
case esvg::distance_pixel:
return value.first;
case esvg::distance_point:
return value.first * 1.25f;
case esvg::distance_pc:
return value.first * 15.0f;
case esvg::distance_millimeter:
return value.first * 3.543307f;
case esvg::distance_centimeter:
return value.first * 35.43307f;
case esvg::distance_inch:
return value.first * 90.0f;
}
return 0.0f;
}
void esvg::Base::parsePaintAttr(const exml::Element& _element) {
if (_element.exist() == false) {
return;
}
/*
bool fillNone = false;
bool strokeNone = false;
*/
std::string content;
// ---------------- get unique ID ----------------
m_id = _element.attributes["id"];
// ---------------- stroke ----------------
content = _element.attributes["stroke"];
if (content == "none") {
m_paint.stroke = std::pair<etk::Color<float,4>, std::string>(etk::color::none, "");
} else {
if (content.size()!=0) {
m_paint.stroke = parseColor(content);
}
content = _element.attributes["stroke-width"];
if (content.size()!=0) {
m_paint.strokeWidth = parseLength(content);
}
content = _element.attributes["stroke-opacity"];
if (content.size()!=0) {
float opacity = parseLength(content);
opacity = std::avg(0.0f, opacity, 1.0f);
m_paint.stroke.first.setA(opacity);
}
content = _element.attributes["stroke-dasharray"];
if (content.size()!=0) {
if (content == "none" ) {
// OK, Nothing to do ...
} else {
ESVG_TODO(" 'stroke-dasharray' not implemented ...");
}
}
content = _element.attributes["stroke-linecap"];
if (content.size()!=0) {
if (content == "butt" ) {
m_paint.lineCap = esvg::cap_butt;
} else if (content == "round" ) {
m_paint.lineCap = esvg::cap_round;
} else if (content == "square" ) {
m_paint.lineCap = esvg::cap_square;
} else {
m_paint.lineCap = esvg::cap_butt;
ESVG_ERROR("not know stroke-linecap value : \"" << content << "\", not in [butt,round,square]");
}
}
content = _element.attributes["stroke-linejoin"];
if (content.size()!=0) {
if (content == "miter" ) {
m_paint.lineJoin = esvg::join_miter;
} else if (content == "round" ) {
m_paint.lineJoin = esvg::join_round;
} else if (content == "bevel" ) {
m_paint.lineJoin = esvg::join_bevel;
} else {
m_paint.lineJoin = esvg::join_miter;
ESVG_ERROR("not know stroke-linejoin value : \"" << content << "\", not in [miter,round,bevel]");
}
}
content = _element.attributes["stroke-miterlimit"];
if (content.size()!=0) {
float tmp = parseLength(content);
m_paint.miterLimit = std::max(0.0f, tmp);
}
}
// ---------------- FILL ----------------
content = _element.attributes["fill"];
if (content == "none") {
m_paint.fill = std::pair<etk::Color<float,4>, std::string>(etk::color::none, "");
} else {
if (content.size()!=0) {
m_paint.fill = parseColor(content);
}
content = _element.attributes["fill-opacity"];
if (content.size()!=0) {
float opacity = parseLength(content);
opacity = std::avg(0.0f, opacity, 1.0f);
m_paint.fill.first.setA(opacity);
}
content = _element.attributes["fill-rule"];
if (content.size()!=0) {
if (content == "nonzero") {
m_paint.flagEvenOdd = false;
} else if (content == "evenodd" ) {
m_paint.flagEvenOdd = true;
} else {
ESVG_ERROR("not know fill-rule value : \"" << content << "\", not in [nonzero,evenodd]");
}
}
// ---------------- opacity ----------------
content = _element.attributes["opacity"];
if (content.size()!=0) {
m_paint.opacity = parseLength(content);
m_paint.opacity = std::avg(0.0f, m_paint.opacity, 1.0f);
}
}
}
std::pair<etk::Color<float,4>, std::string> esvg::Base::parseColor(const std::string& _inputData) {
std::pair<etk::Color<float,4>, std::string> localColor(etk::color::white, "");
if( _inputData.size() > 4
&& _inputData[0] == 'u'
&& _inputData[1] == 'r'
&& _inputData[2] == 'l'
&& _inputData[3] == '(') {
if (_inputData[4] == '#') {
std::string color(_inputData.begin() + 5, _inputData.end()-1);
localColor = std::pair<etk::Color<float,4>, std::string>(etk::color::none, color);
} else {
ESVG_ERROR("Problem in parsing the color : \"" << _inputData << "\" == > url(XXX) is not supported now ...");
}
} else {
localColor = std::pair<etk::Color<float,4>, std::string>(_inputData, "");
}
ESVG_VERBOSE("Parse color : \"" << _inputData << "\" == > " << localColor.first << " " << localColor.second);
return localColor;
}
bool esvg::Base::parseXML(const exml::Element& _element, mat2& _parentTrans, vec2& _sizeMax) {
// TODO : UNDERSTAND why nothing is done here ...
// Parse basic elements (ID...):
m_id = _element.attributes["id"];
_sizeMax = vec2(0.0f, 0.0f);
return false;
}
const char * esvg::Base::spacingDist(int32_t _spacing) {
static const char *tmpValue = " ";
if (_spacing>20) {
_spacing = 20;
}
return tmpValue + 20*4 - _spacing*4;
}
void esvg::Base::draw(esvg::Renderer& _myRenderer, mat2& _basicTrans, int32_t _level) {
ESVG_WARNING(spacingDist(_level) << "DRAW esvg::Base ... ==> No drawing availlable");
}
const std::string& esvg::Base::getId() const {
return m_id;
}
void esvg::Base::setId(const std::string& _newId) {
// TODO : Check if it is UNIQUE ...
m_id = _newId;
}
void esvg::Base::drawShapePoints(std::vector<std::vector<vec2>>& _out,
int32_t _recurtionMax,
float _threshold,
mat2& _basicTrans,
int32_t _level) {
}