diff --git a/.checkstyle b/.checkstyle new file mode 100644 index 0000000..34ed486 --- /dev/null +++ b/.checkstyle @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..d43dd0c --- /dev/null +++ b/.classpath @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d075be7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +/bin/ +/Operator/ +/DrawerProperties/ +*.pdfd +*.dbc +SchedulerConfig.txt +scenicView.properties +ScenariumConfig.txt +*.class +*~ +*.bck +build.number +/extern/ +/out/ +/.settings/ +/junit/ +/target/ diff --git a/.project b/.project new file mode 100644 index 0000000..e14ad1b --- /dev/null +++ b/.project @@ -0,0 +1,24 @@ + + + atriasoft-esvg + + + atriasoft-esvg + + + + org.eclipse.jdt.core.javabuilder + + + + + net.sf.eclipsecs.core.CheckstyleBuilder + + + + + + org.eclipse.jdt.core.javanature + net.sf.eclipsecs.core.CheckstyleNature + + diff --git a/CheckStyle.xml b/CheckStyle.xml new file mode 100755 index 0000000..d68aedd --- /dev/null +++ b/CheckStyle.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CleanUp.xml b/CleanUp.xml new file mode 100644 index 0000000..6cf4cba --- /dev/null +++ b/CleanUp.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Formatter.xml b/Formatter.xml new file mode 100644 index 0000000..14a5d6c --- /dev/null +++ b/Formatter.xml @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a612ad9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/src/module-info.java b/src/module-info.java new file mode 100644 index 0000000..11c18b8 --- /dev/null +++ b/src/module-info.java @@ -0,0 +1,10 @@ +/** Basic module interface. + * + * @author Edouard DUPIN */ + +open module org.atriasoft.esvg { + exports org.atriasoft.esvg; + exports org.atriasoft.esvg.render; + + requires transitive io.scenarium.logger; +} diff --git a/src/org/atriasoft/esvg/Base.cpp b/src/org/atriasoft/esvg/Base.cpp new file mode 100644 index 0000000..1932bee --- /dev/null +++ b/src/org/atriasoft/esvg/Base.cpp @@ -0,0 +1,439 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + + +#include +#include +#include + +const float esvg::kappa90(0.5522847493f); + +esvg::PaintState::PaintState() : + fill(etk::Pair, etk::String>(etk::color::black, "")), + stroke(etk::Pair, etk::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 = etk::Pair, etk::String>(etk::color::black, ""); + stroke = etk::Pair, etk::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; +} + +etk::String extractTransformData(const etk::String& _value, const etk::String& _base) { + size_t posStart = _value.find(_base); + if (posStart == etk::String::npos) { + // Not find element is a normal case ... + return ""; + } + posStart += _base.size(); + if (_value.size() < posStart+2) { + Log.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 { + Log.error("Can not find ' (' or '(' in '" << &_value[posStart] << "' for '" << _value << "'"); + return ""; + } + if (_value.size() < posStart+1) { + Log.error("Not enought spece in the String to have transform value for ')' in '" << _value << "'"); + return ""; + } + size_t posEnd = _value.find(')', posStart); + if (posEnd == etk::String::npos) { + Log.error("Missing element ')' in '" << _value << "' for " << _base); + return ""; + } + Log.verbose("Find : '" << etk::String(_value.begin()+posStart, _value.begin()+posEnd) << "' for " << _base); + return etk::String(_value.begin()+posStart, _value.begin()+posEnd); +} + +void esvg::Base::parseTransform(const exml::Element& _element) { + if (_element.exist() == false) { + return; + } + etk::String inputString = _element.attributes["transform"]; + if (inputString.size() == 0) { + return; + } + Log.verbose("find transform : \"" << inputString << "\""); + for (size_t iii=0; iii esvg::Base::parseLength2(const etk::String& _dataInput) { + Log.verbose(" lenght : '" << _dataInput << "'"); + float n = _dataInput.to(); + etk::String unit; + for (size_t iii=0; iii<_dataInput.size(); ++iii) { + if( (_dataInput[iii]>='0' && _dataInput[iii]<='9') + || _dataInput[iii]=='+' + || _dataInput[iii]=='-' + || _dataInput[iii]=='.') { + continue; + } + unit = etk::String(_dataInput, iii); + break; + } + Log.verbose(" lenght : '" << n << "' => unit=" << unit); + // note : ";" is for the parsing of the style elements ... + if(unit.size() == 0) { + return etk::makePair(n, esvg::distance_pixel); + } else if (unit[0] == '%') { // xxx % + return etk::makePair(n, esvg::distance_pourcent); + } else if ( unit[0] == 'e' + && unit[1] == 'm') { // xxx em + return etk::makePair(n, esvg::distance_element); + } else if ( unit[0] == 'e' + && unit[1] == 'x') { // xxx ex + return etk::makePair(n, esvg::distance_ex); + } else if ( unit[0] == 'p' + && unit[1] == 'x') { // xxx px + return etk::makePair(n, esvg::distance_pixel); + } else if ( unit[0] == 'p' + && unit[1] == 't') { // xxx pt + return etk::makePair(n, esvg::distance_point); + } else if ( unit[0] == 'p' + && unit[1] == 'c') { // xxx pc + return etk::makePair(n, esvg::distance_pc); + } else if ( unit[0] == 'm' + && unit[1] == 'm') { // xxx mm + return etk::makePair(n, esvg::distance_millimeter); + } else if ( unit[0] == 'c' + && unit[1] == 'm') { // xxx cm + return etk::makePair(n, esvg::distance_centimeter); + } else if ( unit[0] == 'i' + && unit[1] == 'n') { // xxx in + return etk::makePair(n, esvg::distance_inch); + } + return etk::makePair(0.0f, esvg::distance_pixel); +} + + +float esvg::Base::parseLength(const etk::String& _dataInput) { + etk::Pair value = parseLength2(_dataInput); + Log.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; + */ + etk::String content; + // ---------------- get unique ID ---------------- + m_id = _element.attributes["id"]; + // ---------------- stroke ---------------- + content = _element.attributes["stroke"]; + if (content == "none") { + m_paint.stroke = etk::Pair, etk::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 = etk::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; + Log.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; + Log.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 = etk::max(0.0f, tmp); + } + } + // ---------------- FILL ---------------- + content = _element.attributes["fill"]; + if (content == "none") { + m_paint.fill = etk::Pair, etk::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 = etk::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 { + Log.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 = etk::avg(0.0f, m_paint.opacity, 1.0f); + } + } +} + +etk::Pair, etk::String> esvg::Base::parseColor(const etk::String& _inputData) { + etk::Pair, etk::String> localColor(etk::color::white, ""); + + if( _inputData.size() > 4 + && _inputData[0] == 'u' + && _inputData[1] == 'r' + && _inputData[2] == 'l' + && _inputData[3] == '(') { + if (_inputData[4] == '#') { + etk::String color(_inputData.begin() + 5, _inputData.end()-1); + localColor = etk::Pair, etk::String>(etk::color::none, color); + } else { + Log.error("Problem in parsing the color : \"" << _inputData << "\" == > url(XXX) is not supported now ..."); + } + } else { + localColor = etk::Pair, etk::String>(_inputData, ""); + } + Log.verbose("Parse color : \"" << _inputData << "\" == > " << localColor.first << " " << localColor.second); + return localColor; +} + +bool esvg::Base::parseXML(const exml::Element& _element, mat2x3& _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, mat2x3& _basicTrans, int32_t _level) { + Log.warning(spacingDist(_level) << "DRAW esvg::Base ... ==> No drawing availlable"); +} + + + +const etk::String& esvg::Base::getId() const { + return m_id; +} + +void esvg::Base::setId(const etk::String& _newId) { + // TODO : Check if it is UNIQUE ... + m_id = _newId; +} + + +void esvg::Base::drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level) { + +} + diff --git a/src/org/atriasoft/esvg/Base.java b/src/org/atriasoft/esvg/Base.java new file mode 100644 index 0000000..986c12b --- /dev/null +++ b/src/org/atriasoft/esvg/Base.java @@ -0,0 +1,129 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + + +#include +#include + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace esvg { + extern const float kappa90; //!< proportional lenght to the radius of a bezier handle for 90° arcs. + /** + * @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. + }; + + class PaintState { + public: + PaintState(); + void clear(); + public: + etk::Pair, etk::String> fill; + etk::Pair, etk::String> stroke; + float strokeWidth; + bool flagEvenOdd; //!< Fill rules + enum esvg::cap lineCap; + enum esvg::join lineJoin; + float miterLimit; + etk::Pair viewPort; //!< min pos, max pos + float opacity; + }; + + class Base { + protected: + PaintState m_paint; + mat2x3 m_transformMatrix; //!< specific render of the curent element + const char * spacingDist(int32_t _spacing); + public: + Base() {}; + Base(PaintState _parentPaintState); + virtual ~Base() { }; + /** + * @brief parse all the element needed in the basic node + * @param[in] _element standart XML node + * @return true if no problem arrived + */ + virtual bool parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax); + /** + * @brief Draw the form in the renderer + * @param[in] _myRenderer Renderer engine + * @param[in] _basicTrans Parant transformation of the environement + * @param[in] _level Level of the tree + */ + virtual void draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level=1); + /** + * @brief Draw rhe shape with all points + * @param[in] _out where the lines are added + * @param[in] _recurtionMax interpolation recurtion max + * @param[in] _threshold threshold to stop recurtion + * @param[in] _basicTrans Parant transformation of the environement + * @param[in] _level Level of the tree + */ + virtual void drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level=1); + + virtual void display(int32_t _spacing) { }; + void parseTransform(const exml::Element& _element); + /** + * @brief parse x, y, width, height attribute of the xml node + * @param[in] _element XML node + * @param[out] _pos parsed position + * @param[out] _size parsed dimention + */ + void parsePosition(const exml::Element& _element, vec2 &_pos, vec2 &_size); + /** + * @brief parse a lenght of the xml element + * @param[in] _dataInput Data C String with the printed lenght + * @return standard number of pixels + */ + float parseLength(const etk::String& _dataInput); + etk::Pair parseLength2(const etk::String& _dataInput); + /** + * @brief parse a Painting attribute of a specific node + * @param[in] _element Basic node of the XML that might be parsed + */ + void parsePaintAttr(const exml::Element& _element); + /** + * @brief parse a color specification from the svg file + * @param[in] _inputData Data C String with the xml definition + * @return The parsed color (color used and the link if needed) + */ + etk::Pair, etk::String> parseColor(const etk::String& _inputData); + protected: + etk::String m_id; //!< unique ID of the element. + public: + /** + * @brief Get the ID of the Element + * @return UniqueId in the svg file + */ + const etk::String& getId() const; + /** + * @brief Set the ID of the Element + * @param[in] _newId New Id of the element + */ + void setId(const etk::String& _newId); + }; +}; diff --git a/src/org/atriasoft/esvg/Circle.cpp b/src/org/atriasoft/esvg/Circle.cpp new file mode 100644 index 0000000..b92ab65 --- /dev/null +++ b/src/org/atriasoft/esvg/Circle.cpp @@ -0,0 +1,166 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include + +esvg::Circle::Circle(PaintState _parentPaintState) : esvg::Base(_parentPaintState) { + +} + +esvg::Circle::~Circle() { + +} + +bool esvg::Circle::parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) { + m_radius = 0.0; + m_position.setValue(0,0); + if (_element.exist() == false) { + return false; + } + parseTransform(_element); + parsePaintAttr(_element); + + // add the property of the parrent modifications ... + m_transformMatrix *= _parentTrans; + + etk::String content = _element.attributes["cx"]; + if (content.size()!=0) { + m_position.setX(parseLength(content)); + } + content = _element.attributes["cy"]; + if (content.size()!=0) { + m_position.setY(parseLength(content)); + } + content = _element.attributes["r"]; + if (content.size()!=0) { + m_radius = parseLength(content); + } else { + Log.error("(l "<<_element.getPos()<<") Circle \"r\" is not present"); + return false; + } + if (0 > m_radius) { + m_radius = 0; + Log.error("(l "<<_element.getPos()<<") Circle \"r\" is negative"); + return false; + } + _sizeMax.setValue(m_position.x() + m_radius, m_position.y() + m_radius); + return true; +} + +void esvg::Circle::display(int32_t _spacing) { + Log.debug(spacingDist(_spacing) << "Circle " << m_position << " radius=" << m_radius); +} + +esvg::render::Path esvg::Circle::createPath() { + esvg::render::Path out; + + out.clear(); + out.moveTo(false, m_position + vec2(m_radius, 0.0f)); + out.curveTo(false, + m_position + vec2(m_radius, m_radius*esvg::kappa90), + m_position + vec2(m_radius*esvg::kappa90, m_radius), + m_position + vec2(0.0f, m_radius)); + out.curveTo(false, + m_position + vec2(-m_radius*esvg::kappa90, m_radius), + m_position + vec2(-m_radius, m_radius*esvg::kappa90), + m_position + vec2(-m_radius, 0.0f)); + out.curveTo(false, + m_position + vec2(-m_radius, -m_radius*esvg::kappa90), + m_position + vec2(-m_radius*esvg::kappa90, -m_radius), + m_position + vec2(0.0f, -m_radius)); + out.curveTo(false, + m_position + vec2(m_radius*esvg::kappa90, -m_radius), + m_position + vec2(m_radius, -m_radius*esvg::kappa90), + m_position + vec2(m_radius, 0.0f)); + out.close(); + return out; +} + +void esvg::Circle::draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW esvg::Circle"); + if (m_radius <= 0.0f) { + Log.verbose(spacingDist(_level+1) << "Too small radius" << m_radius); + return; + } + esvg::render::Path listElement = createPath(); + + mat2x3 mtx = m_transformMatrix; + mtx *= _basicTrans; + + esvg::render::PointList listPoints; + listPoints = listElement.generateListPoints(_level, + _myRenderer.getInterpolationRecurtionMax(), + _myRenderer.getInterpolationThreshold()); + //listPoints.applyMatrix(mtx); + esvg::render::SegmentList listSegmentFill; + esvg::render::SegmentList listSegmentStroke; + esvg::render::Weight tmpFill; + esvg::render::Weight tmpStroke; + ememory::SharedPtr colorFill = esvg::render::createColor(m_paint.fill, mtx); + ememory::SharedPtr colorStroke; + if (m_paint.strokeWidth > 0.0f) { + colorStroke = esvg::render::createColor(m_paint.stroke, mtx); + } + // Check if we need to display background + if (colorFill != null) { + listSegmentFill.createSegmentList(listPoints); + colorFill->setViewPort(listSegmentFill.getViewPort()); + listSegmentFill.applyMatrix(mtx); + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + tmpFill.generate(_myRenderer.getSize(), + _myRenderer.getNumberSubScanLine(), + listSegmentFill); + } + // check if we need to display stroke: + if (colorStroke != null) { + listSegmentStroke.createSegmentListStroke(listPoints, + m_paint.strokeWidth, + m_paint.lineCap, + m_paint.lineJoin, + m_paint.miterLimit); + colorStroke->setViewPort(listSegmentStroke.getViewPort()); + listSegmentStroke.applyMatrix(mtx); + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + tmpStroke.generate(_myRenderer.getSize(), + _myRenderer.getNumberSubScanLine(), + listSegmentStroke); + } + // add on images: + _myRenderer.print(tmpFill, + colorFill, + tmpStroke, + colorStroke, + m_paint.opacity); + #ifdef DEBUG + _myRenderer.addDebugSegment(listSegmentFill); + _myRenderer.addDebugSegment(listSegmentStroke); + #endif +} + +void esvg::Circle::drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW Shape esvg::Circle"); + esvg::render::Path listElement = createPath(); + mat2x3 mtx = m_transformMatrix; + mtx *= _basicTrans; + esvg::render::PointList listPoints; + listPoints = listElement.generateListPoints(_level, _recurtionMax, _threshold); + listPoints.applyMatrix(mtx); + for (auto &it : listPoints.m_data) { + etk::Vector listPoint; + for (auto &itDot : it) { + listPoint.pushBack(itDot.m_pos); + } + _out.pushBack(listPoint); + } +} + diff --git a/src/org/atriasoft/esvg/Circle.java b/src/org/atriasoft/esvg/Circle.java new file mode 100644 index 0000000..ad01971 --- /dev/null +++ b/src/org/atriasoft/esvg/Circle.java @@ -0,0 +1,31 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include + +namespace esvg { + class Circle : public esvg::Base { + private: + vec2 m_position; //!< Position of the Circle + float m_radius; //!< Radius of the Circle + public: + Circle(PaintState _parentPaintState); + ~Circle(); + bool parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) override; + void display(int32_t _spacing) override; + void draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) override; + void drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level=1) override; + private: + esvg::render::Path createPath(); + }; +} + + diff --git a/src/org/atriasoft/esvg/Dimension.cpp b/src/org/atriasoft/esvg/Dimension.cpp new file mode 100644 index 0000000..f11770f --- /dev/null +++ b/src/org/atriasoft/esvg/Dimension.cpp @@ -0,0 +1,445 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +static const float inchToMillimeter = 1.0f/25.4f; +static const float footToMillimeter = 1.0f/304.8f; +static const float meterToMillimeter = 1.0f/1000.0f; +static const float centimeterToMillimeter = 1.0f/10.0f; +static const float kilometerToMillimeter = 1.0f/1000000.0f; +static const float millimeterToInch = 25.4f; +static const float millimeterToFoot = 304.8f; +static const float millimeterToMeter =1000.0f; +static const float millimeterToCentimeter = 10.0f; +static const float millimeterToKilometer = 1000000.0f; + +// 72 px /inch(2.54cm) +static const float basicRatio = 72.0f / 25.4f; + +esvg::Dimension::Dimension() : + m_data(0,0), + m_type(esvg::distance_pixel) { + // notinh to do ... +} + +esvg::Dimension::Dimension(const vec2& _size, enum esvg::distance _type) : + m_data(0,0), + m_type(esvg::distance_pixel) { + set(_size, _type); +} + +void esvg::Dimension::set(etk::String _config) { + m_data.setValue(0,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 { + type = esvg::distance_pixel; + Log.verbose("default dimention type for: '" << _config << "' ==> pixel"); + return; + } + vec2 tmp = _config; + set(tmp, type); + Log.verbose(" config dimention : \"" << _config << "\" == > " << *this ); +} + +static enum esvg::distance parseType(etk::String& _config) { + enum esvg::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 { + type = esvg::distance_pixel; + Log.verbose("default dimention type for: '" << _config << "' ==> pixel"); + } + return type; +} + + +void esvg::Dimension::set(etk::String _configX, etk::String _configY) { + m_data.setValue(0,0); + m_type = esvg::distance_pixel; + enum distance type = esvg::distance_pixel; + // First Parse X + enum distance typeX = parseType(_configX); + float valueX = etk::string_to_float(_configX); + // Second Parse Y + enum distance typeY = parseType(_configY); + float valueY = etk::string_to_float(_configY); + // TODO : Check difference ... + set(vec2(valueX, valueY), typeX); + Log.verbose(" config dimention : '" << _configX << "' '" << _configY << "' == > " << *this ); +} + + +esvg::Dimension::~Dimension() { + // nothing to do ... +} + +esvg::Dimension::operator etk::String() const { + etk::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::Dimension::set(const vec2& _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: + Log.error("Does not support other than Px and % type of dimention : " << _type << " automaticly convert with {72,72} pixel/inch"); + break; + } +} + +vec2 esvg::Dimension::getPixel(const vec2& _upperSize) const { + switch(m_type) { + case esvg::distance_pourcent: + return vec2(_upperSize.x()*m_data.x()*0.01f, _upperSize.y()*m_data.y()*0.01f); + case esvg::distance_pixel: + return m_data; + case esvg::distance_meter: + return vec2(m_data.x()*meterToMillimeter*basicRatio, m_data.y()*meterToMillimeter*basicRatio); + case esvg::distance_centimeter: + return vec2(m_data.x()*centimeterToMillimeter*basicRatio, m_data.y()*centimeterToMillimeter*basicRatio); + case esvg::distance_millimeter: + return vec2(m_data.x()*basicRatio, m_data.y()*basicRatio); + case esvg::distance_kilometer: + return vec2(m_data.x()*kilometerToMillimeter*basicRatio, m_data.y()*kilometerToMillimeter*basicRatio); + case esvg::distance_inch: + return vec2(m_data.x()*inchToMillimeter*basicRatio, m_data.y()*inchToMillimeter*basicRatio); + case esvg::distance_foot: + return vec2(m_data.x()*footToMillimeter*basicRatio, m_data.y()*footToMillimeter*basicRatio); + } + return vec2(128.0f, 128.0f); +} + +etk::Stream& esvg::operator <<(etk::Stream& _os, enum esvg::distance _obj) { + switch(_obj) { + case esvg::distance_pourcent: + _os << "%"; + break; + case esvg::distance_pixel: + _os << "px"; + break; + case esvg::distance_meter: + _os << "m"; + break; + case esvg::distance_centimeter: + _os << "cm"; + break; + case esvg::distance_millimeter: + _os << "mm"; + break; + case esvg::distance_kilometer: + _os << "km"; + break; + case esvg::distance_inch: + _os << "in"; + break; + case esvg::distance_foot: + _os << "ft"; + break; + case esvg::distance_element: + _os << "em"; + break; + case esvg::distance_ex: + _os << "ex"; + break; + case esvg::distance_point: + _os << "pt"; + break; + case esvg::distance_pc: + _os << "pc"; + break; + } + return _os; +} + +etk::Stream& esvg::operator <<(etk::Stream& _os, const esvg::Dimension& _obj) { + _os << _obj.getValue() << _obj.getType(); + return _os; +} + +namespace etk { + template<> etk::String toString(const esvg::Dimension& _obj) { + return _obj; + } + template<> etk::UString toUString(const esvg::Dimension& _obj) { + return etk::toUString(etk::toString(_obj)); + } + template<> bool from_string(esvg::Dimension& _variableRet, const etk::String& _value) { + _variableRet = esvg::Dimension(_value); + return true; + } + template<> bool from_string(esvg::Dimension& _variableRet, const etk::UString& _value) { + return from_string(_variableRet, etk::toString(_value)); + } +}; + +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(etk::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 { + type = esvg::distance_pixel; + Log.verbose("default dimention type for: '" << _config << "' ==> pixel"); + } + float tmp = etk::string_to_float(_config); + set(tmp, type); + Log.verbose(" config dimention : \"" << _config << "\" == > " << *this ); +} + +esvg::Dimension1D::~Dimension1D() { + // nothing to do ... +} + +esvg::Dimension1D::operator etk::String() const { + etk::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: + Log.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; +} + +etk::Stream& esvg::operator <<(etk::Stream& _os, const esvg::Dimension1D& _obj) { + _os << _obj.getValue() << _obj.getType(); + return _os; +} + +namespace etk { + template<> etk::String toString(const esvg::Dimension1D& _obj) { + return _obj; + } + template<> etk::UString toUString(const esvg::Dimension1D& _obj) { + return etk::toUString(etk::toString(_obj)); + } + template<> bool from_string(esvg::Dimension1D& _variableRet, const etk::String& _value) { + _variableRet = esvg::Dimension1D(_value); + return true; + } + template<> bool from_string(esvg::Dimension1D& _variableRet, const etk::UString& _value) { + return from_string(_variableRet, etk::toString(_value)); + } +}; + + + + diff --git a/src/org/atriasoft/esvg/Dimension.java b/src/org/atriasoft/esvg/Dimension.java new file mode 100644 index 0000000..73a7eec --- /dev/null +++ b/src/org/atriasoft/esvg/Dimension.java @@ -0,0 +1,252 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include + +namespace esvg { + enum distance { + distance_pourcent=0, //!< "%" + distance_pixel, //!< "px" + distance_meter, //!< "m" + distance_centimeter, //!< "cm" + distance_millimeter, //!< "mm" + distance_kilometer, //!< "km" + distance_inch, //!< "in" + distance_foot, //!< "ft" + distance_element, //!< "em" + distance_ex, //!< "ex" + distance_point, //!< "pt" + distance_pc //!< "pc" + }; + /** + * @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 Dimension { + private: + vec2 m_data; + enum distance m_type; + public: + /** + * @brief Constructor (default :0,0 mode pixel) + */ + Dimension(); + /** + * @brief Constructor + * @param[in] _size Requested dimention + * @param[in] _type Unit of the Dimention + */ + Dimension(const vec2& _size, enum esvg::distance _type=esvg::distance_pixel); + /** + * @brief Constructor + * @param[in] _config dimension configuration. + */ + Dimension(const etk::String& _config) : + m_data(0,0), + m_type(esvg::distance_pixel) { + set(_config); + }; + /** + * @brief Constructor + * @param[in] _configX dimension X configuration. + * @param[in] _configY dimension Y configuration. + */ + Dimension(const etk::String& _configX, const etk::String& _configY) : + m_data(0,0), + m_type(esvg::distance_pixel) { + set(_configX, _configY); + }; + /** + * @brief Destructor + */ + ~Dimension(); + + /** + * @brief string cast : + */ + operator etk::String() const; + + /** + * @brief get the current dimention. + * @return dimention requested. + */ + const vec2& 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(const vec2& _size, enum distance _type); + + public: + /** + * @brief set the current dimention in requested type + * @param[in] _config dimension configuration. + */ + void set(etk::String _config); + /** + * @brief set the current dimention in requested type + * @param[in] _configX dimension X configuration. + * @param[in] _configY dimension Y configuration. + */ + void set(etk::String _configX, etk::String _configY); + public: + /** + * @brief get the current dimention in pixel + * @param[in] _upperSize Size in pixel of the upper value + * @return dimention in Pixel + */ + vec2 getPixel(const vec2& _upperSize) const; + /***************************************************** + * = assigment + *****************************************************/ + const Dimension& operator= (const Dimension& _obj ) { + if (this!=&_obj) { + m_data = _obj.m_data; + m_type = _obj.m_type; + } + return *this; + } + /***************************************************** + * == operator + *****************************************************/ + bool operator == (const Dimension& _obj) const { + if( m_data == _obj.m_data + && m_type == _obj.m_type) { + return true; + } + return false; + } + /***************************************************** + * != operator + *****************************************************/ + bool operator!= (const Dimension& _obj) const { + if( m_data != _obj.m_data + || m_type != _obj.m_type) { + return true; + } + return false; + } + }; + etk::Stream& operator <<(etk::Stream& _os, enum esvg::distance _obj); + etk::Stream& operator <<(etk::Stream& _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 etk::String& _config) : + m_data(0.0f), + m_type(esvg::distance_pixel) { + set(_config); + }; + /** + * @brief Destructor + */ + ~Dimension1D(); + + /** + * @brief string cast : + */ + operator etk::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(etk::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; + } + }; + etk::Stream& operator <<(etk::Stream& _os, const esvg::Dimension1D& _obj); +} + diff --git a/src/org/atriasoft/esvg/Ellipse.cpp b/src/org/atriasoft/esvg/Ellipse.cpp new file mode 100644 index 0000000..206a9f5 --- /dev/null +++ b/src/org/atriasoft/esvg/Ellipse.cpp @@ -0,0 +1,172 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include + +esvg::Ellipse::Ellipse(PaintState _parentPaintState) : esvg::Base(_parentPaintState) { + +} + +esvg::Ellipse::~Ellipse() { + +} + +bool esvg::Ellipse::parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) { + if (_element.exist() == false) { + return false; + } + parseTransform(_element); + parsePaintAttr(_element); + + // add the property of the parrent modifications ... + m_transformMatrix *= _parentTrans; + + m_c.setValue(0,0); + m_r.setValue(0,0); + + etk::String content = _element.attributes["cx"]; + if (content.size()!=0) { + m_c.setX(parseLength(content)); + } + content = _element.attributes["cy"]; + if (content.size()!=0) { + m_c.setY(parseLength(content)); + } + content = _element.attributes["rx"]; + if (content.size()!=0) { + m_r.setX(parseLength(content)); + } else { + Log.error("(l "<<_element.getPos()<<") Ellipse \"rx\" is not present"); + return false; + } + content = _element.attributes["ry"]; + if (content.size()!=0) { + m_r.setY(parseLength(content)); + } else { + Log.error("(l "<<_element.getPos()<<") Ellipse \"ry\" is not present"); + return false; + } + _sizeMax.setValue(m_c.x() + m_r.x(), m_c.y() + m_r.y()); + + return true; +} + +void esvg::Ellipse::display(int32_t _spacing) { + Log.debug(spacingDist(_spacing) << "Ellipse c=" << m_c << " r=" << m_r); +} + + +esvg::render::Path esvg::Ellipse::createPath() { + esvg::render::Path out; + out.clear(); + out.moveTo(false, m_c + vec2(m_r.x(), 0.0f)); + out.curveTo(false, + m_c + vec2(m_r.x(), m_r.y()*esvg::kappa90), + m_c + vec2(m_r.x()*esvg::kappa90, m_r.y()), + m_c + vec2(0.0f, m_r.y())); + out.curveTo(false, + m_c + vec2(-m_r.x()*esvg::kappa90, m_r.y()), + m_c + vec2(-m_r.x(), m_r.y()*esvg::kappa90), + m_c + vec2(-m_r.x(), 0.0f)); + out.curveTo(false, + m_c + vec2(-m_r.x(), -m_r.y()*esvg::kappa90), + m_c + vec2(-m_r.x()*esvg::kappa90, -m_r.y()), + m_c + vec2(0.0f, -m_r.y())); + out.curveTo(false, + m_c + vec2(m_r.x()*esvg::kappa90, -m_r.y()), + m_c + vec2(m_r.x(), -m_r.y()*esvg::kappa90), + m_c + vec2(m_r.x(), 0.0f)); + out.close(); + return out; +} + +void esvg::Ellipse::draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW esvg::Ellipse"); + if ( m_r.x()<=0.0f + || m_r.y()<=0.0f) { + Log.verbose(spacingDist(_level+1) << "Too small radius" << m_r); + return; + } + esvg::render::Path listElement = createPath(); + + mat2x3 mtx = m_transformMatrix; + mtx *= _basicTrans; + + esvg::render::PointList listPoints; + listPoints = listElement.generateListPoints(_level, + _myRenderer.getInterpolationRecurtionMax(), + _myRenderer.getInterpolationThreshold()); + //listPoints.applyMatrix(mtx); + esvg::render::SegmentList listSegmentFill; + esvg::render::SegmentList listSegmentStroke; + esvg::render::Weight tmpFill; + esvg::render::Weight tmpStroke; + ememory::SharedPtr colorFill = esvg::render::createColor(m_paint.fill, mtx); + ememory::SharedPtr colorStroke; + if (m_paint.strokeWidth > 0.0f) { + colorStroke = esvg::render::createColor(m_paint.stroke, mtx); + } + // Check if we need to display background + if (colorFill != null) { + listSegmentFill.createSegmentList(listPoints); + colorFill->setViewPort(listSegmentFill.getViewPort()); + listSegmentFill.applyMatrix(mtx); + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + tmpFill.generate(_myRenderer.getSize(), + _myRenderer.getNumberSubScanLine(), + listSegmentFill); + } + // check if we need to display stroke: + if (colorStroke != null) { + listSegmentStroke.createSegmentListStroke(listPoints, + m_paint.strokeWidth, + m_paint.lineCap, + m_paint.lineJoin, + m_paint.miterLimit); + colorStroke->setViewPort(listSegmentStroke.getViewPort()); + listSegmentStroke.applyMatrix(mtx); + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + tmpStroke.generate(_myRenderer.getSize(), + _myRenderer.getNumberSubScanLine(), + listSegmentStroke); + } + // add on images: + _myRenderer.print(tmpFill, + colorFill, + tmpStroke, + colorStroke, + m_paint.opacity); + #ifdef DEBUG + _myRenderer.addDebugSegment(listSegmentFill); + _myRenderer.addDebugSegment(listSegmentStroke); + #endif +} + + +void esvg::Ellipse::drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW Shape esvg::Ellipse"); + esvg::render::Path listElement = createPath(); + mat2x3 mtx = m_transformMatrix; + mtx *= _basicTrans; + esvg::render::PointList listPoints; + listPoints = listElement.generateListPoints(_level, _recurtionMax, _threshold); + listPoints.applyMatrix(mtx); + for (auto &it : listPoints.m_data) { + etk::Vector listPoint; + for (auto &itDot : it) { + listPoint.pushBack(itDot.m_pos); + } + _out.pushBack(listPoint); + } +} + diff --git a/src/org/atriasoft/esvg/Ellipse.java b/src/org/atriasoft/esvg/Ellipse.java new file mode 100644 index 0000000..02ac432 --- /dev/null +++ b/src/org/atriasoft/esvg/Ellipse.java @@ -0,0 +1,30 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include + +namespace esvg { + class Ellipse : public esvg::Base { + private: + vec2 m_c; //!< Center property of the ellipse + vec2 m_r; //!< Radius property of the ellipse + public: + Ellipse(PaintState _parentPaintState); + ~Ellipse(); + bool parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) override; + void display(int32_t _spacing) override; + void draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) override; + void drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level=1) override; + private: + esvg::render::Path createPath(); + }; +} + diff --git a/src/org/atriasoft/esvg/Group.cpp b/src/org/atriasoft/esvg/Group.cpp new file mode 100644 index 0000000..98fac79 --- /dev/null +++ b/src/org/atriasoft/esvg/Group.cpp @@ -0,0 +1,129 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +esvg::Group::Group(PaintState _parentPaintState) : esvg::Base(_parentPaintState) { + +} + +esvg::Group::~Group() { + +} + +bool esvg::Group::parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) { + if (_element.exist() == false) { + return false; + } + // parse ... + vec2 pos(0,0); + vec2 size(0,0); + parseTransform(_element); + parsePosition(_element, pos, size); + parsePaintAttr(_element); + Log.verbose("parsed G1. trans : " << m_transformMatrix); + + // add the property of the parrent modifications ... + m_transformMatrix *= _parentTrans; + + Log.verbose("parsed G2. trans : " << m_transformMatrix); + + _sizeMax.setValue(0,0); + vec2 tmpPos(0,0); + // parse all sub node : + for(const auto it : _element.nodes) { + exml::Element child = it.toElement(); + if (child.exist() == false) { + // can be a comment ... + continue; + } + ememory::SharedPtr elementParser; + if (child.getValue() == "g") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "a") { + // TODO ... + } else if (child.getValue() == "path") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "rect") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "circle") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "ellipse") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "line") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "polyline") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "polygon") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "text") { + elementParser = ememory::makeShared(m_paint); + } else { + Log.error("(l " << child.getPos() << ") node not suported : '" << child.getValue() << "' must be [g,a,path,rect,circle,ellipse,line,polyline,polygon,text]"); + } + if (elementParser == null) { + Log.error("(l " << child.getPos() << ") error on node: '" << child.getValue() << "' allocation error or not supported ..."); + continue; + } + if (elementParser->parseXML(child, m_transformMatrix, tmpPos) == false) { + Log.error("(l " << child.getPos() << ") error on node: '" << child.getValue() << "' Sub Parsing ERROR"); + elementParser.reset(); + continue; + } + _sizeMax.setValue(etk::max(_sizeMax.x(), tmpPos.x()), + etk::max(_sizeMax.y(), tmpPos.y())); + // add element in the system + m_subElementList.pushBack(elementParser); + } + return true; +} + +void esvg::Group::display(int32_t _spacing) { + Log.debug(spacingDist(_spacing) << "Group (START) fill=" << m_paint.fill.first << "/" << m_paint.fill.second + << " stroke=" << m_paint.stroke.first << "/" << m_paint.stroke.second + << " stroke-width=" << m_paint.strokeWidth ); + for (auto &it : m_subElementList) { + if (it != null) { + it->display(_spacing+1); + } + } + Log.debug(spacingDist(_spacing) << "Group (STOP)"); +} + +void esvg::Group::draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW esvg::group"); + for (auto &it : m_subElementList) { + if (it != null) { + it->draw(_myRenderer, _basicTrans, _level+1); + } + } +} + +void esvg::Group::drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW shape esvg::group"); + for (auto &it : m_subElementList) { + if (it != null) { + it->drawShapePoints(_out, _recurtionMax, _threshold, _basicTrans, _level+1); + } + } +} + diff --git a/src/org/atriasoft/esvg/Group.java b/src/org/atriasoft/esvg/Group.java new file mode 100644 index 0000000..9f8bedb --- /dev/null +++ b/src/org/atriasoft/esvg/Group.java @@ -0,0 +1,28 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include + +namespace esvg { + class Group : public esvg::Base { + private: + etk::Vector> m_subElementList; //!< sub elements ... + public: + Group(PaintState _parentPaintState); + ~Group(); + bool parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) override; + void display(int32_t spacing) override; + void draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) override; + void drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level=1) override; + }; +} + diff --git a/src/org/atriasoft/esvg/Line.cpp b/src/org/atriasoft/esvg/Line.cpp new file mode 100644 index 0000000..11e1160 --- /dev/null +++ b/src/org/atriasoft/esvg/Line.cpp @@ -0,0 +1,139 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include + +esvg::Line::Line(PaintState _parentPaintState) : esvg::Base(_parentPaintState) { + m_startPos.setValue(0,0); + m_stopPos.setValue(0,0); +} + +esvg::Line::~Line() { + +} + +bool esvg::Line::parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) { + // line must have a minimum size... + m_paint.strokeWidth = 1; + if (_element.exist() == false) { + return false; + } + parseTransform(_element); + parsePaintAttr(_element); + + // add the property of the parrent modifications ... + m_transformMatrix *= _parentTrans; + + etk::String content = _element.attributes["x1"]; + if (content.size() != 0) { + m_startPos.setX(parseLength(content)); + } + content = _element.attributes["y1"]; + if (content.size() != 0) { + m_startPos.setY(parseLength(content)); + } + content = _element.attributes["x2"]; + if (content.size() != 0) { + m_stopPos.setX(parseLength(content)); + } + content = _element.attributes["y2"]; + if (content.size() != 0) { + m_stopPos.setY(parseLength(content)); + } + _sizeMax.setValue(etk::max(m_startPos.x(), m_stopPos.x()), + etk::max(m_startPos.y(), m_stopPos.y())); + return true; +} + +void esvg::Line::display(int32_t _spacing) { + Log.debug(spacingDist(_spacing) << "Line " << m_startPos << " to " << m_stopPos); +} + +esvg::render::Path esvg::Line::createPath() { + esvg::render::Path out; + out.clear(); + out.moveTo(false, m_startPos); + out.lineTo(false, m_stopPos); + out.stop(); + return out; +} + +void esvg::Line::draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW esvg::Line"); + + esvg::render::Path listElement = createPath(); + + mat2x3 mtx = m_transformMatrix; + mtx *= _basicTrans; + + esvg::render::PointList listPoints; + listPoints = listElement.generateListPoints(_level, + _myRenderer.getInterpolationRecurtionMax(), + _myRenderer.getInterpolationThreshold()); + //listPoints.applyMatrix(mtx); + esvg::render::SegmentList listSegmentFill; + esvg::render::SegmentList listSegmentStroke; + esvg::render::Weight tmpFill; + esvg::render::Weight tmpStroke; + ememory::SharedPtr colorFill = esvg::render::createColor(m_paint.fill, mtx); + ememory::SharedPtr colorStroke; + if (m_paint.strokeWidth > 0.0f) { + colorStroke = esvg::render::createColor(m_paint.stroke, mtx); + } + // Check if we need to display background + // No background ... + // check if we need to display stroke: + if (colorStroke != null) { + listSegmentStroke.createSegmentListStroke(listPoints, + m_paint.strokeWidth, + m_paint.lineCap, + m_paint.lineJoin, + m_paint.miterLimit); + colorStroke->setViewPort(listSegmentStroke.getViewPort()); + listSegmentStroke.applyMatrix(mtx); + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + tmpStroke.generate(_myRenderer.getSize(), + _myRenderer.getNumberSubScanLine(), + listSegmentStroke); + } + // add on images: + _myRenderer.print(tmpFill, + colorFill, + tmpStroke, + colorStroke, + m_paint.opacity); + #ifdef DEBUG + _myRenderer.addDebugSegment(listSegmentFill); + _myRenderer.addDebugSegment(listSegmentStroke); + _myRenderer.addDebugSegment(listElement.m_debugInformation); + #endif +} + + +void esvg::Line::drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW Shape esvg::Line"); + esvg::render::Path listElement = createPath(); + mat2x3 mtx = m_transformMatrix; + mtx *= _basicTrans; + esvg::render::PointList listPoints; + listPoints = listElement.generateListPoints(_level, _recurtionMax, _threshold); + listPoints.applyMatrix(mtx); + for (auto &it : listPoints.m_data) { + etk::Vector listPoint; + for (auto &itDot : it) { + listPoint.pushBack(itDot.m_pos); + } + _out.pushBack(listPoint); + } +} + diff --git a/src/org/atriasoft/esvg/Line.java b/src/org/atriasoft/esvg/Line.java new file mode 100644 index 0000000..bff9baa --- /dev/null +++ b/src/org/atriasoft/esvg/Line.java @@ -0,0 +1,30 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include + +namespace esvg { + class Line : public esvg::Base { + private: + vec2 m_startPos; //!< Start line position + vec2 m_stopPos; //!< Stop line position + public: + Line(PaintState _parentPaintState); + ~Line(); + bool parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) override; + void display(int32_t _spacing) override; + void draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) override; + void drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level=1) override; + private: + esvg::render::Path createPath(); + }; +} + diff --git a/src/org/atriasoft/esvg/LinearGradient.cpp b/src/org/atriasoft/esvg/LinearGradient.cpp new file mode 100644 index 0000000..96ea594 --- /dev/null +++ b/src/org/atriasoft/esvg/LinearGradient.cpp @@ -0,0 +1,176 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include +#include +#include + +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() { + +} + + +bool esvg::LinearGradient::parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) { + // line must have a minimum size... + //m_paint.strokeWidth = 1; + if (_element.exist() == false) { + return false; + } + + // ---------------- get unique ID ---------------- + m_id = _element.attributes["id"]; + + //parseTransform(_element); + //parsePaintAttr(_element); + + // add the property of the parrent modifications ... + m_transformMatrix *= _parentTrans; + + etk::String contentX = _element.attributes["x1"]; + etk::String contentY = _element.attributes["y1"]; + if ( contentX != "" + && contentY != "") { + m_pos1.set(contentX, contentY); + } + contentX = _element.attributes["x2"]; + contentY = _element.attributes["y2"]; + if ( contentX != "" + && contentY != "") { + m_pos2.set(contentX, contentY); + } + contentX = _element.attributes["gradientUnits"]; + if (contentX == "userSpaceOnUse") { + m_unit = gradientUnits_userSpaceOnUse; + } else { + m_unit = gradientUnits_objectBoundingBox; + if ( contentX.size() != 0 + && contentX != "objectBoundingBox") { + Log.error("Parsing error of 'gradientUnits' ==> not suported value: '" << contentX << "' not in : {userSpaceOnUse/objectBoundingBox} use objectBoundingBox"); + } + } + contentX = _element.attributes["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") { + Log.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.attributes["xlink:href"]; + if (m_href.size() != 0) { + m_href = etk::String(m_href.begin()+1, m_href.end()); + } + // parse all sub node : + for(const auto it : _element.nodes) { + exml::Element child = it.toElement(); + if (child.exist() == false) { + // can be a comment ... + continue; + } + if (child.getValue() == "stop") { + float offset = 100; + etk::Color stopColor = etk::color::none; + etk::String content = child.attributes["offset"]; + if (content.size()!=0) { + etk::Pair 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) { + Log.error("offset : " << content << " res=" << tmp.first << "," << tmp.second << " Not support other than pourcent %"); + } else { + offset = tmp.first; + } + } + content = child.attributes["stop-color"]; + if (content.size()!=0) { + stopColor = parseColor(content).first; + Log.verbose(" color : '" << content << "' == > " << stopColor); + } + content = child.attributes["stop-opacity"]; + if (content.size()!=0) { + float opacity = parseLength(content); + opacity = etk::avg(0.0f, opacity, 1.0f); + stopColor.setA(opacity); + Log.verbose(" opacity : '" << content << "' == > " << stopColor); + } + m_data.pushBack(etk::Pair>(offset, stopColor)); + } else { + Log.error("(l " << child.getPos() << ") node not suported : '" << child.getValue() << "' must be [stop]"); + } + } + if (m_data.size() != 0) { + if (m_href != "") { + Log.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::LinearGradient::display(int32_t _spacing) { + Log.debug(spacingDist(_spacing) << "LinearGradient " << m_pos1 << " to " << m_pos2); + for (auto &it : m_data) { + Log.debug(spacingDist(_spacing+1) << "STOP: offset=" << it.first << " color=" << it.second); + } +} + +void esvg::LinearGradient::draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW esvg::LinearGradient"); +} + +const esvg::Dimension& esvg::LinearGradient::getPosition1() { + return m_pos1; +} + +const esvg::Dimension& esvg::LinearGradient::getPosition2() { + return m_pos2; +} + +const etk::Vector>>& esvg::LinearGradient::getColors(esvg::Document* _document) { + if (m_href == "") { + return m_data; + } + if (_document == null) { + Log.error("Get null input for document"); + return m_data; + } + ememory::SharedPtr base = _document->getReference(m_href); + if (base == null) { + Log.error("Can not get base : '" << m_href << "'"); + return m_data; + } + ememory::SharedPtr gradientR = ememory::dynamicPointerCast(base); + if (gradientR == null) { + ememory::SharedPtr gradientL = ememory::dynamicPointerCast(base); + if (gradientL == null) { + Log.error("Can not cast in a linear/radial gradient: '" << m_href << "' ==> wrong type"); + return m_data; + } + return gradientL->getColors(_document); + } + return gradientR->getColors(_document); +} + + + diff --git a/src/org/atriasoft/esvg/LinearGradient.java b/src/org/atriasoft/esvg/LinearGradient.java new file mode 100644 index 0000000..84d4630 --- /dev/null +++ b/src/org/atriasoft/esvg/LinearGradient.java @@ -0,0 +1,36 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esvg { + class Document; + class LinearGradient : public esvg::Base { + private: + esvg::Dimension m_pos1; //!< gradient position x1 y1 + esvg::Dimension m_pos2; //!< gradient position x2 y2 + public: + enum gradientUnits m_unit; + enum spreadMethod m_spread; + private: + etk::String m_href; //!< in case of using a single gradient in multiple gradient, the gradient is store in an other element... + etk::Vector>> m_data; //!< incompatible with href + public: + LinearGradient(PaintState _parentPaintState); + ~LinearGradient(); + virtual bool parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax); + virtual void display(int32_t _spacing); + virtual void draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level); + public: + const esvg::Dimension& getPosition1(); + const esvg::Dimension& getPosition2(); + const etk::Vector>>& getColors(esvg::Document* _document); + }; +} + diff --git a/src/org/atriasoft/esvg/Path.cpp b/src/org/atriasoft/esvg/Path.cpp new file mode 100644 index 0000000..b826503 --- /dev/null +++ b/src/org/atriasoft/esvg/Path.cpp @@ -0,0 +1,349 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include + +esvg::Path::Path(PaintState _parentPaintState) : esvg::Base(_parentPaintState) { + +} + +esvg::Path::~Path() { + +} + + +// return the next char position ... (after 'X' or NULL) +const char * extractCmd(const char* _input, char& _cmd, etk::Vector& _outputList) { + if (*_input == '\0') { + return null; + } + _outputList.clear(); + _cmd = '\0'; + const char * outputPointer = null; + if (!( ( _input[0] <= 'Z' + && _input[0] >= 'A') + || ( _input[0] <= 'z' + && _input[0] >= 'a') ) ) { + Log.error("Error in the SVG Path : \"" << _input << "\""); + return null; + } + _cmd = _input[0]; + Log.verbose("Find command : " << _cmd); + if (_input[1] == '\0') { + return &_input[1]; + } + int32_t iii=1; + // extract every float separated by a ' ' or a ',' + float element; + char spacer[10]; + int32_t nbElementRead; + while( sscanf(&_input[iii], "%1[, ]%f%n", spacer, &element, &nbElementRead) == 2 + || sscanf(&_input[iii], "%f%n", &element, &nbElementRead) == 1) { + Log.verbose("Find element : " << element); + _outputList.pushBack(element); + iii += nbElementRead; + } + outputPointer = &_input[iii]; + while(*outputPointer!= '\0' && *outputPointer == ' ') { + outputPointer++; + } + //outputPointer++; + return outputPointer; +} +etk::String cleanBadSpaces(const etk::String& _input) { + etk::String out; + bool haveSpace = false; + for (auto &it : _input) { + if ( it == ' ' + || it == '\t' + || it == '\t' + || it == '\r') { + haveSpace = true; + } else { + if (haveSpace == true) { + haveSpace = false; + out += ' '; + } + out += it; + } + } + return out; +} + +bool esvg::Path::parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) { + if (_element.exist() == false) { + return false; + } + parseTransform(_element); + parsePaintAttr(_element); + + // add the property of the parrent modifications ... + m_transformMatrix *= _parentTrans; + + + etk::String elementXML1 = _element.attributes["d"]; + if (elementXML1.size() == 0) { + Log.warning("(l "<<_element.getPos()<<") path: missing 'd' attribute or empty"); + return false; + } + Log.verbose("Parse Path : \"" << elementXML1 << "\""); + + char command; + etk::Vector listDot; + elementXML1 = cleanBadSpaces(elementXML1); + const char* elementXML = elementXML1.c_str(); + + for( const char *sss=extractCmd(elementXML, command, listDot); + sss != null; + sss=extractCmd(sss, command, listDot) ) { + bool relative = false; + switch(command) { + case 'm': // Move to (relative) + relative = true; + case 'M': // Move to (absolute) + // 2 Elements ... + if(listDot.size()%2 != 0) { + Log.warning("the PATH command "<< command << " has not the good number of element = " << listDot.size() ); + break; + } + if (listDot.size() >= 2) { + m_listElement.moveTo(relative, + vec2(listDot[0], listDot[1])); + } + for (size_t iii=2; iii colorFill = esvg::render::createColor(m_paint.fill, mtx); + ememory::SharedPtr colorStroke; + if (m_paint.strokeWidth > 0.0f) { + colorStroke = esvg::render::createColor(m_paint.stroke, mtx); + } + // Check if we need to display background + if (colorFill != null) { + listSegmentFill.createSegmentList(listPoints); + colorFill->setViewPort(listSegmentFill.getViewPort()); + listSegmentFill.applyMatrix(mtx); + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + tmpFill.generate(_myRenderer.getSize(), _myRenderer.getNumberSubScanLine(), listSegmentFill); + } + // check if we need to display stroke: + if (colorStroke != null) { + listSegmentStroke.createSegmentListStroke(listPoints, + m_paint.strokeWidth, + m_paint.lineCap, + m_paint.lineJoin, + m_paint.miterLimit); + colorStroke->setViewPort(listSegmentStroke.getViewPort()); + listSegmentStroke.applyMatrix(mtx); + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + tmpStroke.generate(_myRenderer.getSize(), _myRenderer.getNumberSubScanLine(), listSegmentStroke); + } + // add on images: + _myRenderer.print(tmpFill, + colorFill, + tmpStroke, + colorStroke, + m_paint.opacity); + #ifdef DEBUG + _myRenderer.addDebugSegment(listSegmentFill); + _myRenderer.addDebugSegment(listSegmentStroke); + m_listElement.m_debugInformation.applyMatrix(mtx); + _myRenderer.addDebugSegment(m_listElement.m_debugInformation); + #endif +} + + +void esvg::Path::drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW Shape esvg::Path"); + + mat2x3 mtx = m_transformMatrix; + mtx *= _basicTrans; + + esvg::render::PointList listPoints; + listPoints = m_listElement.generateListPoints(_level, _recurtionMax, _threshold); + listPoints.applyMatrix(mtx); + for (auto &it : listPoints.m_data) { + etk::Vector listPoint; + for (auto &itDot : it) { + listPoint.pushBack(itDot.m_pos); + } + _out.pushBack(listPoint); + } +} + diff --git a/src/org/atriasoft/esvg/Path.java b/src/org/atriasoft/esvg/Path.java new file mode 100644 index 0000000..2427089 --- /dev/null +++ b/src/org/atriasoft/esvg/Path.java @@ -0,0 +1,28 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include + +namespace esvg { + class Path : public esvg::Base { + public: + esvg::render::Path m_listElement; + public: + Path(PaintState _parentPaintState); + ~Path(); + bool parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) override; + void display(int32_t _spacing) override; + void draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) override; + void drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level=1) override; + }; +} + diff --git a/src/org/atriasoft/esvg/Polygon.cpp b/src/org/atriasoft/esvg/Polygon.cpp new file mode 100644 index 0000000..e1bc214 --- /dev/null +++ b/src/org/atriasoft/esvg/Polygon.cpp @@ -0,0 +1,153 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include + +esvg::Polygon::Polygon(PaintState parentPaintState) : esvg::Base(parentPaintState) { + +} + +esvg::Polygon::~Polygon() { + +} + +bool esvg::Polygon::parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) { + if (_element.exist() == false) { + return false; + } + parseTransform(_element); + parsePaintAttr(_element); + + Log.verbose("parsed P1. trans: " << m_transformMatrix); + + // add the property of the parrent modifications ... + m_transformMatrix *= _parentTrans; + + Log.verbose("parsed P2. trans: " << m_transformMatrix); + + const etk::String sss1 = _element.attributes["points"]; + if (sss1.size() == 0) { + Log.error("(l "/*<<_element->Pos()*/<<") polygon: missing points attribute"); + return false; + } + const char * sss = sss1.c_str(); + _sizeMax.setValue(0,0); + Log.verbose("Parse polygon : \"" << sss << "\""); + while ('\0' != sss[0]) { + vec2 pos(0,0); + int32_t n; + if (sscanf(sss, "%f,%f%n", &pos.m_floats[0], &pos.m_floats[1], &n) == 2) { + m_listPoint.pushBack(pos); + sss += n; + _sizeMax.setValue(etk::max(_sizeMax.x(), pos.x()), + etk::max(_sizeMax.y(), pos.y())); + if(sss[0] == ' ' || sss[0] == ',') { + sss++; + } + } else { + break; + } + } + return true; +} + +void esvg::Polygon::display(int32_t _spacing) { + Log.debug(spacingDist(_spacing) << "Polygon nbPoint=" << m_listPoint.size()); +} + +esvg::render::Path esvg::Polygon::createPath() { + esvg::render::Path out; + out.moveTo(false, m_listPoint[0]); + for(size_t iii=1; iii< m_listPoint.size(); iii++) { + out.lineTo(false, m_listPoint[iii]); + } + out.close(); + return out; +} + +void esvg::Polygon::draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW esvg::Polygon"); + + esvg::render::Path listElement = createPath(); + + mat2x3 mtx = m_transformMatrix; + mtx *= _basicTrans; + + esvg::render::PointList listPoints; + listPoints = listElement.generateListPoints(_level, + _myRenderer.getInterpolationRecurtionMax(), + _myRenderer.getInterpolationThreshold()); + //listPoints.applyMatrix(mtx); + esvg::render::SegmentList listSegmentFill; + esvg::render::SegmentList listSegmentStroke; + esvg::render::Weight tmpFill; + esvg::render::Weight tmpStroke; + ememory::SharedPtr colorFill = esvg::render::createColor(m_paint.fill, mtx); + ememory::SharedPtr colorStroke; + if (m_paint.strokeWidth > 0.0f) { + colorStroke = esvg::render::createColor(m_paint.stroke, mtx); + } + // Check if we need to display background + if (colorFill != null) { + listSegmentFill.createSegmentList(listPoints); + colorFill->setViewPort(listSegmentFill.getViewPort()); + listSegmentFill.applyMatrix(mtx); + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + tmpFill.generate(_myRenderer.getSize(), + _myRenderer.getNumberSubScanLine(), + listSegmentFill); + } + // check if we need to display stroke: + if (colorStroke != null) { + listSegmentStroke.createSegmentListStroke(listPoints, + m_paint.strokeWidth, + m_paint.lineCap, + m_paint.lineJoin, + m_paint.miterLimit); + colorStroke->setViewPort(listSegmentStroke.getViewPort()); + listSegmentStroke.applyMatrix(mtx); + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + tmpStroke.generate(_myRenderer.getSize(), + _myRenderer.getNumberSubScanLine(), + listSegmentStroke); + } + // add on images: + _myRenderer.print(tmpFill, + colorFill, + tmpStroke, + colorStroke, + m_paint.opacity); + #ifdef DEBUG + _myRenderer.addDebugSegment(listSegmentFill); + _myRenderer.addDebugSegment(listSegmentStroke); + #endif +} + + +void esvg::Polygon::drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW Shape esvg::Polygon"); + esvg::render::Path listElement = createPath(); + mat2x3 mtx = m_transformMatrix; + mtx *= _basicTrans; + esvg::render::PointList listPoints; + listPoints = listElement.generateListPoints(_level, _recurtionMax, _threshold); + listPoints.applyMatrix(mtx); + for (auto &it : listPoints.m_data) { + etk::Vector listPoint; + for (auto &itDot : it) { + listPoint.pushBack(itDot.m_pos); + } + _out.pushBack(listPoint); + } +} + diff --git a/src/org/atriasoft/esvg/Polygon.java b/src/org/atriasoft/esvg/Polygon.java new file mode 100644 index 0000000..e6223a6 --- /dev/null +++ b/src/org/atriasoft/esvg/Polygon.java @@ -0,0 +1,37 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include + +namespace esvg { + /* + enum polygonMode { + polygoneModeNonZero, + polygoneModeEvenOdd + }; + */ + class Polygon : public esvg::Base { + private: + etk::Vector m_listPoint; //!< list of all point of the polygone + //enum esvg::polygonMode m_diplayMode; //!< polygone specific display mode + public: + Polygon(PaintState parentPaintState); + ~Polygon(); + bool parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) override; + void display(int32_t _spacing) override; + void draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) override; + void drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level=1) override; + private: + esvg::render::Path createPath(); + }; +} + diff --git a/src/org/atriasoft/esvg/Polyline.cpp b/src/org/atriasoft/esvg/Polyline.cpp new file mode 100644 index 0000000..1f4c6f4 --- /dev/null +++ b/src/org/atriasoft/esvg/Polyline.cpp @@ -0,0 +1,149 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include + +esvg::Polyline::Polyline(PaintState _parentPaintState) : esvg::Base(_parentPaintState) { + +} + +esvg::Polyline::~Polyline() { + +} + +bool esvg::Polyline::parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) { + // line must have a minimum size... + m_paint.strokeWidth = 1; + if (_element.exist() == false) { + return false; + } + parseTransform(_element); + parsePaintAttr(_element); + + // add the property of the parrent modifications ... + m_transformMatrix *= _parentTrans; + + etk::String sss1 = _element.attributes["points"]; + if (sss1.size() == 0) { + Log.error("(l "<<_element.getPos()<<") polyline: missing points attribute"); + return false; + } + _sizeMax.setValue(0,0); + Log.verbose("Parse polyline : \"" << sss1 << "\""); + const char* sss = sss1.c_str(); + while ('\0' != sss[0]) { + vec2 pos; + int32_t n; + if (sscanf(sss, "%f,%f %n", &pos.m_floats[0], &pos.m_floats[1], &n) == 2) { + m_listPoint.pushBack(pos); + _sizeMax.setValue(etk::max(_sizeMax.x(), pos.x()), + etk::max(_sizeMax.y(), pos.y())); + sss += n; + } else { + break; + } + } + return true; +} + +void esvg::Polyline::display(int32_t _spacing) { + Log.debug(spacingDist(_spacing) << "Polyline nbPoint=" << m_listPoint.size()); +} + + +esvg::render::Path esvg::Polyline::createPath() { + esvg::render::Path out; + out.clear(); + out.moveTo(false, m_listPoint[0]); + for(size_t iii=1; iii< m_listPoint.size(); iii++) { + out.lineTo(false, m_listPoint[iii]); + } + out.stop(); + return out; +} + +void esvg::Polyline::draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW esvg::Polyline"); + + esvg::render::Path listElement = createPath(); + + mat2x3 mtx = m_transformMatrix; + mtx *= _basicTrans; + + esvg::render::PointList listPoints; + listPoints = listElement.generateListPoints(_level, + _myRenderer.getInterpolationRecurtionMax(), + _myRenderer.getInterpolationThreshold()); + //listPoints.applyMatrix(mtx); + esvg::render::SegmentList listSegmentFill; + esvg::render::SegmentList listSegmentStroke; + esvg::render::Weight tmpFill; + esvg::render::Weight tmpStroke; + ememory::SharedPtr colorFill = esvg::render::createColor(m_paint.fill, mtx); + ememory::SharedPtr colorStroke; + if (m_paint.strokeWidth > 0.0f) { + colorStroke = esvg::render::createColor(m_paint.stroke, mtx); + } + // Check if we need to display background + if (colorFill != null) { + listSegmentFill.createSegmentList(listPoints); + colorFill->setViewPort(listSegmentFill.getViewPort()); + listSegmentFill.applyMatrix(mtx); + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + tmpFill.generate(_myRenderer.getSize(), + _myRenderer.getNumberSubScanLine(), + listSegmentFill); + } + // check if we need to display stroke: + if (colorStroke != null) { + listSegmentStroke.createSegmentListStroke(listPoints, + m_paint.strokeWidth, + m_paint.lineCap, + m_paint.lineJoin, + m_paint.miterLimit); + colorStroke->setViewPort(listSegmentStroke.getViewPort()); + listSegmentStroke.applyMatrix(mtx); + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + tmpStroke.generate(_myRenderer.getSize(), + _myRenderer.getNumberSubScanLine(), + listSegmentStroke); + } + // add on images: + _myRenderer.print(tmpFill, + colorFill, + tmpStroke, + colorStroke, + m_paint.opacity); + #ifdef DEBUG + _myRenderer.addDebugSegment(listSegmentFill); + _myRenderer.addDebugSegment(listSegmentStroke); + #endif +} + + +void esvg::Polyline::drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW Shape esvg::Polyline"); + esvg::render::Path listElement = createPath(); + mat2x3 mtx = m_transformMatrix; + mtx *= _basicTrans; + esvg::render::PointList listPoints; + listPoints = listElement.generateListPoints(_level, _recurtionMax, _threshold); + listPoints.applyMatrix(mtx); + for (auto &it : listPoints.m_data) { + etk::Vector listPoint; + for (auto &itDot : it) { + listPoint.pushBack(itDot.m_pos); + } + _out.pushBack(listPoint); + } +} diff --git a/src/org/atriasoft/esvg/Polyline.java b/src/org/atriasoft/esvg/Polyline.java new file mode 100644 index 0000000..844202f --- /dev/null +++ b/src/org/atriasoft/esvg/Polyline.java @@ -0,0 +1,30 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include + +namespace esvg { + class Polyline : public esvg::Base { + private: + etk::Vector m_listPoint; //!< list of all point of the polyline + public: + Polyline(PaintState _parentPaintState); + ~Polyline(); + bool parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) override; + void display(int32_t _spacing) override; + void draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) override; + void drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level=1) override; + private: + esvg::render::Path createPath(); + }; +} + diff --git a/src/org/atriasoft/esvg/RadialGradient.cpp b/src/org/atriasoft/esvg/RadialGradient.cpp new file mode 100644 index 0000000..17858bd --- /dev/null +++ b/src/org/atriasoft/esvg/RadialGradient.cpp @@ -0,0 +1,185 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include +#include +#include + +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 exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) { + // line must have a minimum size... + //m_paint.strokeWidth = 1; + if (_element.exist() == false) { + return false; + } + + // ---------------- get unique ID ---------------- + m_id = _element.attributes["id"]; + + //parseTransform(_element); + //parsePaintAttr(_element); + + // add the property of the parrent modifications ... + m_transformMatrix *= _parentTrans; + + etk::String contentX = _element.attributes["cx"]; + etk::String contentY = _element.attributes["cy"]; + if ( contentX != "" + && contentY != "") { + m_center.set(contentX, contentY); + } + contentX = _element.attributes["r"]; + if (contentX != "") { + m_radius.set(contentX); + } + contentX = _element.attributes["fx"]; + contentY = _element.attributes["fy"]; + if ( contentX != "" + && contentY != "") { + m_focal.set(contentX, contentY); + } + contentX = _element.attributes["gradientUnits"]; + if (contentX == "userSpaceOnUse") { + m_unit = gradientUnits_userSpaceOnUse; + } else { + m_unit = gradientUnits_objectBoundingBox; + if ( contentX.size() != 0 + && contentX != "objectBoundingBox") { + Log.error("Parsing error of 'gradientUnits' ==> not suported value: '" << contentX << "' not in : {userSpaceOnUse/objectBoundingBox} use objectBoundingBox"); + } + } + contentX = _element.attributes["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") { + Log.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.attributes["xlink:href"]; + if (m_href.size() != 0) { + m_href = etk::String(m_href.begin()+1, m_href.end()); + } + // parse all sub node : + for(auto it : _element.nodes) { + exml::Element child = it.toElement(); + if (child.exist() == false) { + // can be a comment ... + continue; + } + if (child.getValue() == "stop") { + float offset = 100; + etk::Color stopColor = etk::color::none; + etk::String content = child.attributes["offset"]; + if (content.size()!=0) { + etk::Pair 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) { + Log.error("offset : " << content << " res=" << tmp.first << "," << tmp.second << " Not support other than pourcent %"); + } else { + offset = tmp.first; + } + } + content = child.attributes["stop-color"]; + if (content.size()!=0) { + stopColor = parseColor(content).first; + Log.verbose(" color : \"" << content << "\" == > " << stopColor); + } + content = child.attributes["stop-opacity"]; + if (content.size()!=0) { + float opacity = parseLength(content); + opacity = etk::avg(0.0f, opacity, 1.0f); + stopColor.setA(opacity); + Log.verbose(" opacity : '" << content << "' == > " << stopColor); + } + m_data.pushBack(etk::Pair>(offset, stopColor)); + } else { + Log.error("(l " << child.getPos() << ") node not suported : '" << child.getValue() << "' must be [stop]"); + } + } + if (m_data.size() != 0) { + if (m_href != "") { + Log.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) { + Log.debug(spacingDist(_spacing) << "RadialGradient center=" << m_center << " focal=" << m_focal << " radius=" << m_radius); + for (auto &it : m_data) { + Log.debug(spacingDist(_spacing+1) << "STOP: offset=" << it.first << " color=" << it.second); + } +} + +void esvg::RadialGradient::draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) { + Log.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 etk::Vector>>& esvg::RadialGradient::getColors(esvg::Document* _document) { + if (m_href == "") { + return m_data; + } + if (_document == null) { + Log.error("Get null input for document"); + return m_data; + } + ememory::SharedPtr base = _document->getReference(m_href); + if (base == null) { + Log.error("Can not get base : '" << m_href << "'"); + return m_data; + } + ememory::SharedPtr gradientR = ememory::dynamicPointerCast(base); + if (gradientR == null) { + ememory::SharedPtr gradientL = ememory::dynamicPointerCast(base); + if (gradientL == null) { + Log.error("Can not cast in a linear/radial gradient: '" << m_href << "' ==> wrong type"); + return m_data; + } + return gradientL->getColors(_document); + } + return gradientR->getColors(_document); +} + + + diff --git a/src/org/atriasoft/esvg/RadialGradient.java b/src/org/atriasoft/esvg/RadialGradient.java new file mode 100644 index 0000000..05f1caf --- /dev/null +++ b/src/org/atriasoft/esvg/RadialGradient.java @@ -0,0 +1,38 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +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: + etk::String m_href; //!< in case of using a single gradient in multiple gradient, the gradient is store in an other element... + etk::Vector>> m_data; //!< incompatible with href + public: + RadialGradient(PaintState _parentPaintState); + ~RadialGradient(); + virtual bool parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax); + virtual void display(int32_t _spacing); + virtual void draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level); + public: + const esvg::Dimension& getCenter(); + const esvg::Dimension& getFocal(); + const esvg::Dimension1D& getRadius(); + const etk::Vector>>& getColors(esvg::Document* _document); + }; +} + diff --git a/src/org/atriasoft/esvg/Rectangle.cpp b/src/org/atriasoft/esvg/Rectangle.cpp new file mode 100644 index 0000000..de695d6 --- /dev/null +++ b/src/org/atriasoft/esvg/Rectangle.cpp @@ -0,0 +1,166 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include + +esvg::Rectangle::Rectangle(PaintState _parentPaintState) : esvg::Base(_parentPaintState) { + m_position.setValue(0,0); + m_size.setValue(0,0); + m_roundedCorner.setValue(0,0); +} + +esvg::Rectangle::~Rectangle() { + +} + +bool esvg::Rectangle::parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) { + if (_element.exist() == false) { + return false; + } + m_position.setValue(0.0f, 0.0f); + m_size.setValue(0.0f, 0.0f); + m_roundedCorner.setValue(0.0f, 0.0f); + + parseTransform(_element); + parsePaintAttr(_element); + + // add the property of the parrent modifications ... + m_transformMatrix *= _parentTrans; + + parsePosition(_element, m_position, m_size); + + etk::String content = _element.attributes["rx"]; + if (content.size()!=0) { + m_roundedCorner.setX(parseLength(content)); + } + content = _element.attributes["ry"]; + if (content.size()!=0) { + m_roundedCorner.setY(parseLength(content)); + } + _sizeMax.setValue(m_position.x() + m_size.x() + m_paint.strokeWidth, + m_position.y() + m_size.y() + m_paint.strokeWidth); + return true; +} + +void esvg::Rectangle::display(int32_t _spacing) { + Log.debug(spacingDist(_spacing) << "Rectangle : pos=" << m_position << " size=" << m_size << " corner=" << m_roundedCorner); +} + +esvg::render::Path esvg::Rectangle::createPath() { + esvg::render::Path out; + out.clear(); + if ( m_roundedCorner.x() == 0.0f + || m_roundedCorner.y() == 0.0f) { + out.moveTo(false, m_position); + out.lineToH(true, m_size.x()); + out.lineToV(true, m_size.y()); + out.lineToH(true, -m_size.x()); + } else { + // Rounded rectangle + out.moveTo(false, m_position + vec2(m_roundedCorner.x(), 0.0f)); + out.lineToH(true, m_size.x()-m_roundedCorner.x()*2.0f); + out.curveTo(true, vec2(m_roundedCorner.x()*esvg::kappa90, 0.0f), + vec2(m_roundedCorner.x(), m_roundedCorner.y() * (1.0f - esvg::kappa90)), + vec2(m_roundedCorner.x(), m_roundedCorner.y()) ); + out.lineToV(true, m_size.y()-m_roundedCorner.y()*2.0f); + out.curveTo(true, vec2(0.0f, m_roundedCorner.y() * esvg::kappa90), + vec2(-m_roundedCorner.x()* (1.0f - esvg::kappa90), m_roundedCorner.y()), + vec2(-m_roundedCorner.x(), m_roundedCorner.y()) ); + out.lineToH(true, -(m_size.x()-m_roundedCorner.x()*2.0f)); + out.curveTo(true, vec2(-m_roundedCorner.x()*esvg::kappa90, 0.0f), + vec2(-m_roundedCorner.x(), -m_roundedCorner.y() * (1.0f - esvg::kappa90)), + vec2(-m_roundedCorner.x(), -m_roundedCorner.y()) ); + out.lineToV(true, -(m_size.y()-m_roundedCorner.y()*2.0f)); + out.curveTo(true, vec2(0.0f, -m_roundedCorner.y() * esvg::kappa90), + vec2(m_roundedCorner.x()* (1.0f - esvg::kappa90), -m_roundedCorner.y()), + vec2(m_roundedCorner.x(), -m_roundedCorner.y()) ); + } + out.close(); + return out; +} + +void esvg::Rectangle::draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW esvg::Rectangle: fill=" << m_paint.fill.first << "/" << m_paint.fill.second + << " stroke=" << m_paint.stroke.first << "/" << m_paint.stroke.second); + esvg::render::Path listElement = createPath(); + + mat2x3 mtx = m_transformMatrix; + mtx *= _basicTrans; + + esvg::render::PointList listPoints; + listPoints = listElement.generateListPoints(_level, + _myRenderer.getInterpolationRecurtionMax(), + _myRenderer.getInterpolationThreshold()); + //listPoints.applyMatrix(mtx); + esvg::render::SegmentList listSegmentFill; + esvg::render::SegmentList listSegmentStroke; + esvg::render::Weight tmpFill; + esvg::render::Weight tmpStroke; + ememory::SharedPtr colorFill = esvg::render::createColor(m_paint.fill, mtx); + ememory::SharedPtr colorStroke; + if (m_paint.strokeWidth > 0.0f) { + colorStroke = esvg::render::createColor(m_paint.stroke, mtx); + } + // Check if we need to display background + if (colorFill != null) { + listSegmentFill.createSegmentList(listPoints); + colorFill->setViewPort(listSegmentFill.getViewPort()); + listSegmentFill.applyMatrix(mtx); + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + tmpFill.generate(_myRenderer.getSize(), + _myRenderer.getNumberSubScanLine(), + listSegmentFill); + } + // check if we need to display stroke: + if (colorStroke != null) { + listSegmentStroke.createSegmentListStroke(listPoints, + m_paint.strokeWidth, + m_paint.lineCap, + m_paint.lineJoin, + m_paint.miterLimit); + colorStroke->setViewPort(listSegmentStroke.getViewPort()); + listSegmentStroke.applyMatrix(mtx); + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + tmpStroke.generate(_myRenderer.getSize(), + _myRenderer.getNumberSubScanLine(), + listSegmentStroke); + } + // add on images: + _myRenderer.print(tmpFill, + colorFill, + tmpStroke, + colorStroke, + m_paint.opacity); + #ifdef DEBUG + _myRenderer.addDebugSegment(listSegmentFill); + _myRenderer.addDebugSegment(listSegmentStroke); + #endif +} + + +void esvg::Rectangle::drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW Shape esvg::Rectangle"); + esvg::render::Path listElement = createPath(); + mat2x3 mtx = m_transformMatrix; + mtx *= _basicTrans; + esvg::render::PointList listPoints; + listPoints = listElement.generateListPoints(_level, _recurtionMax, _threshold); + listPoints.applyMatrix(mtx); + for (auto &it : listPoints.m_data) { + etk::Vector listPoint; + for (auto &itDot : it) { + listPoint.pushBack(itDot.m_pos); + } + _out.pushBack(listPoint); + } +} diff --git a/src/org/atriasoft/esvg/Rectangle.java b/src/org/atriasoft/esvg/Rectangle.java new file mode 100644 index 0000000..1234bb4 --- /dev/null +++ b/src/org/atriasoft/esvg/Rectangle.java @@ -0,0 +1,31 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include + +namespace esvg { + class Rectangle : public esvg::Base { + private: + 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(); + bool parseXML(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) override; + void display(int32_t _spacing) override; + void draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) override; + void drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level=1) override; + private: + esvg::render::Path createPath(); + }; +} + diff --git a/src/org/atriasoft/esvg/Renderer.cpp b/src/org/atriasoft/esvg/Renderer.cpp new file mode 100644 index 0000000..4f1acde --- /dev/null +++ b/src/org/atriasoft/esvg/Renderer.cpp @@ -0,0 +1,427 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include + +esvg::Renderer::Renderer(const ivec2& _size, esvg::Document* _document, bool _visualDebug) : +#ifdef DEBUG + m_visualDebug(_visualDebug), + m_factor(1), +#endif + m_interpolationRecurtionMax(10), + m_interpolationThreshold(0.25f), + m_nbSubScanLine(8), + m_document(_document) { + #ifdef DEBUG + if (m_visualDebug == true) { + m_factor = 20; + } + #endif + setSize(_size); +} + +esvg::Renderer::~Renderer() { + m_buffer.clear(); + m_size = ivec2(0,0); +} + +etk::Color esvg::Renderer::mergeColor(etk::Color _base, etk::Color _integration) { + etk::Color result; + /* + if (_integration.a() < _base.a()) { + result = _integration; + _integration = _base; + _base = result; + } + */ + result.setR(_integration.a() * _integration.r() + _base.a() * (1.0f - _integration.a()) * _base.r()); + result.setG(_integration.a() * _integration.g() + _base.a() * (1.0f - _integration.a()) * _base.g()); + result.setB(_integration.a() * _integration.b() + _base.a() * (1.0f - _integration.a()) * _base.b()); + result.setA(_integration.a() + _base.a() * (1.0f - _integration.a())); + if (result.a() != 0.0f) { + float reverse = 1.0f/result.a(); + result.setR(result.r()*reverse); + result.setG(result.g()*reverse); + result.setB(result.b()*reverse); + } + return result; +} + +void esvg::Renderer::print(const esvg::render::Weight& _weightFill, + ememory::SharedPtr& _colorFill, + const esvg::render::Weight& _weightStroke, + ememory::SharedPtr& _colorStroke, + float _opacity) { + if (_colorFill != null) { + //_colorFill->setViewPort(etk::Pair(vec2(0,0), vec2(sizeX, sizeY))); + _colorFill->generate(m_document); + } + if (_colorStroke != null) { + //_colorStroke->setViewPort(etk::Pair(vec2(0,0), vec2(sizeX, sizeY))); + _colorStroke->generate(m_document); + } + // all together + for (int32_t yyy=0; yyy intermediateColorFill(etk::color::none); + etk::Color intermediateColorStroke(etk::color::none); + if ( _colorFill != null + && valueFill != 0.0f) { + intermediateColorFill = _colorFill->getColor(pos); + intermediateColorFill.setA(intermediateColorFill.a()*valueFill); + } + if ( _colorStroke != null + && valueStroke != 0.0f) { + intermediateColorStroke = _colorStroke->getColor(pos); + intermediateColorStroke.setA(intermediateColorStroke.a()*valueStroke); + } + etk::Color intermediateColor = mergeColor(intermediateColorFill, intermediateColorStroke); + intermediateColor.setA(intermediateColor.a() * _opacity); + #if DEBUG + for (int32_t deltaY=0; deltaY tmpColor = ememory::dynamicPointerCast(_colorFill); + if (tmpColor != null) { + 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); + /* + mat2x3 m_matrix; + etk::Pair m_viewPort; + vec2 m_pos1; + vec2 m_pos2; + */ + addDebugSegment(listSegment); + } + #endif +} + +#ifdef DEBUG + void esvg::Renderer::addDebugSegment(const esvg::render::SegmentList& _listSegment) { + if (m_visualDebug == false) { + return; + } + ivec2 dynamicSize = m_size * m_factor; + // for each lines: + for (int32_t yyy=0; yyy availlableSegmentPixel; + for (auto &it : _listSegment.m_data) { + if ( it.p0.y() * m_factor <= float(yyy+1) + && it.p1.y() * m_factor >= float(yyy)) { + availlableSegmentPixel.pushBack(it); + } + } + //find all the segment that cross the middle of the line of the center of the pixel line: + float subSamplingCenterPos = yyy + 0.5f; + etk::Vector availlableSegment; + // find in the subList ... + for (auto &it : availlableSegmentPixel) { + if ( it.p0.y() * m_factor <= subSamplingCenterPos + && it.p1.y() * m_factor >= subSamplingCenterPos ) { + availlableSegment.pushBack(it); + } + } + // x position, angle + etk::Vector> listPosition; + for (auto &it : availlableSegment) { + vec2 delta = it.p0 * m_factor - it.p1 * m_factor; + // x = coefficent*y+bbb; + float coefficient = delta.x()/delta.y(); + float bbb = it.p0.x() * m_factor - coefficient*it.p0.y() * m_factor; + float xpos = coefficient * subSamplingCenterPos + bbb; + if ( xpos >= 0 + && xpos < dynamicSize.x() + && yyy >= 0 + && yyy < dynamicSize.y() ) { + if (it.direction == 1.0f) { + m_buffer[(dynamicSize.x()*yyy + int32_t(xpos))] = etk::color::blue; + } else { + m_buffer[(dynamicSize.x()*yyy + int32_t(xpos))] = etk::color::darkRed; + } + } + } + } + // for each colomn: + for (int32_t xxx=0; xxx availlableSegmentPixel; + for (auto &it : _listSegment.m_data) { + if ( ( it.p0.x() * m_factor <= float(xxx+1) + && it.p1.x() * m_factor >= float(xxx) ) + || ( it.p0.x() * m_factor >= float(xxx+1) + && it.p1.x() * m_factor <= float(xxx) ) ) { + availlableSegmentPixel.pushBack(it); + } + } + //find all the segment that cross the middle of the line of the center of the pixel line: + float subSamplingCenterPos = xxx + 0.5f; + etk::Vector availlableSegment; + // find in the subList ... + for (auto &it : availlableSegmentPixel) { + if ( ( it.p0.x() * m_factor <= subSamplingCenterPos + && it.p1.x() * m_factor >= subSamplingCenterPos) + || ( it.p0.x() * m_factor >= subSamplingCenterPos + && it.p1.x() * m_factor <= subSamplingCenterPos) ) { + availlableSegment.pushBack(it); + } + } + // x position, angle + etk::Vector> listPosition; + for (auto &it : availlableSegment) { + vec2 delta = it.p0 * m_factor - it.p1 * m_factor; + // x = coefficent*y+bbb; + if (delta.x() == 0) { + continue; + } + float coefficient = delta.y()/delta.x(); + float bbb = it.p0.y() * m_factor - coefficient*it.p0.x() * m_factor; + float ypos = coefficient * subSamplingCenterPos + bbb; + if ( ypos >= 0 + && ypos < dynamicSize.y() + && xxx >= 0 + && xxx < dynamicSize.y() ) { + if (it.direction == 1.0f) { + m_buffer[(dynamicSize.x()*int32_t(ypos) + xxx)] = etk::color::blue; + } else { + m_buffer[(dynamicSize.x()*int32_t(ypos) + xxx)] = etk::color::darkRed; + } + } + } + } + } +#endif + + +void esvg::Renderer::writePPM(const etk::Uri& _uri) { + if (m_buffer.size() == 0) { + return; + } + auto fileIo = etk::uri::get(_uri); + if (fileIo == null) { + Log.error("Can not create the uri: " << _uri); + return; + } + if (fileIo->open(etk::io::OpenMode::Write) == false) { + Log.error("Can not open (r) the file : " << _uri); + return; + } + int32_t sizeX = m_size.x(); + int32_t sizeY = m_size.y(); + #if DEBUG + sizeX *= m_factor; + sizeY *= m_factor; + #endif + Log.debug("Generate ppm : " << m_size << " debug size=" << ivec2(sizeX,sizeY)); + char tmpValue[1024]; + sprintf(tmpValue, "P6 %d %d 255 ", sizeX, sizeY); + fileIo->write(tmpValue,1,sizeof(tmpValue)); + for (int32_t iii=0 ; iii tmp = m_buffer[iii]; + fileIo->write(&tmp, 1, 3); + } + fileIo->close(); +} +#define PLOPPP +extern "C" { + #pragma pack(push,1) + struct bitmapFileHeader { + int16_t bfType; + int32_t bfSize; + int32_t bfReserved; + int32_t bfOffBits; + }; + struct bitmapInfoHeader { + int32_t biSize; + int32_t biWidth; + int32_t biHeight; + int16_t biPlanes; + int16_t biBitCount; + int32_t biCompression; + int32_t biSizeImage; + int32_t biXPelsPerMeter; + int32_t biYPelsPerMeter; + #ifndef PLOPPP + int32_t biClrUsed; + int32_t biClrImportant; + #else + // https://en.wikipedia.org/wiki/BMP_file_format / example 2 + int32_t biPaletteNumber; + int32_t biImportantColor; + int32_t biBitMaskRed; + int32_t biBitMaskGreen; + int32_t biBitMaskBlue; + int32_t biBitMaskAlpha; + int32_t biLCSColorSpace; + int32_t biUnused[16]; + #endif + }; + #pragma pack(pop) +} +void esvg::Renderer::writeBMP(const etk::Uri& _uri) { + if (m_buffer.size() == 0) { + return; + } + auto fileIo = etk::uri::get(_uri); + if (fileIo == null) { + Log.error("Can not create the uri: " << _uri); + return; + } + if (fileIo->open(etk::io::OpenMode::Write) == false) { + Log.error("Can not open (r) the file : " << _uri); + return; + } + struct bitmapFileHeader fileHeader; + struct bitmapInfoHeader infoHeader; + + int32_t sizeX = m_size.x(); + int32_t sizeY = m_size.y(); + #if DEBUG + sizeX *= m_factor; + sizeY *= m_factor; + #endif + + fileHeader.bfType = 0x4D42; + fileHeader.bfSize = sizeof(struct bitmapFileHeader) + sizeof(struct bitmapInfoHeader) + sizeX*sizeY*4; + fileHeader.bfReserved = 0; + fileHeader.bfOffBits = sizeof(struct bitmapFileHeader) + sizeof(struct bitmapInfoHeader); + + + infoHeader.biSize = sizeof(struct bitmapInfoHeader); + infoHeader.biWidth = sizeX; + infoHeader.biHeight = sizeY; + infoHeader.biPlanes = 1; + infoHeader.biBitCount = 32; + #ifndef PLOPPP + infoHeader.biCompression = 0; + #else + infoHeader.biCompression = 3; + #endif + infoHeader.biSizeImage = sizeX*sizeY*4; + infoHeader.biXPelsPerMeter = 75; + infoHeader.biYPelsPerMeter = 75; + #ifndef PLOPPP + infoHeader.biClrUsed = 0; + infoHeader.biClrImportant = 0; + #else + infoHeader.biPaletteNumber = 0; + infoHeader.biImportantColor = 0; + infoHeader.biBitMaskRed = 0xFF000000; + infoHeader.biBitMaskGreen = 0x00FF0000; + infoHeader.biBitMaskBlue =0x0000FF00; + infoHeader.biBitMaskAlpha = 0x000000FF; + infoHeader.biLCSColorSpace = 0x73524742; // "Win " + for (int32_t jjj=0; jjj<16; ++jjj) { + infoHeader.biUnused[jjj] = 0; + } + infoHeader.biUnused[12] = 0x00000002; + #endif + // get the data : + fileIo->write(&fileHeader, sizeof(struct bitmapFileHeader), 1); + fileIo->write(&infoHeader, sizeof(struct bitmapInfoHeader), 1); + + uint8_t data[16]; + for(int32_t yyy=sizeY-1; yyy>=0; --yyy) { + for(int32_t xxx=0; xxx& tmpColor = m_buffer[sizeX*yyy + xxx]; + uint8_t* pointer = data; + #ifndef PLOPPP + *pointer++ = tmpColor.a(); + *pointer++ = tmpColor.r(); + *pointer++ = tmpColor.g(); + *pointer++ = tmpColor.b(); + #else + *pointer++ = tmpColor.a(); + *pointer++ = tmpColor.b(); + *pointer++ = tmpColor.g(); + *pointer++ = tmpColor.r(); + #endif + fileIo->write(data,1,4); + } + } + fileIo->close(); +} + + +void esvg::Renderer::setSize(const ivec2& _size) { + m_size = _size; + m_buffer.resize(m_size.x() * m_size.y() + #if DEBUG + * m_factor * m_factor + #endif + , etk::color::none); +} + +const ivec2& esvg::Renderer::getSize() const { + return m_size; +} + +etk::Vector> esvg::Renderer::getData() { + return m_buffer; +} + + + + +void esvg::Renderer::setInterpolationRecurtionMax(int32_t _value) { + m_interpolationRecurtionMax = etk::avg(1, _value, 200); +} + +int32_t esvg::Renderer::getInterpolationRecurtionMax() const { + return m_interpolationRecurtionMax; +} + +void esvg::Renderer::setInterpolationThreshold(float _value) { + m_interpolationThreshold = etk::avg(0.0f, _value, 20000.0f); +} + +float esvg::Renderer::getInterpolationThreshold() const { + return m_interpolationThreshold; +} + +void esvg::Renderer::setNumberSubScanLine(int32_t _value) { + m_nbSubScanLine = etk::avg(1, _value, 200); +} + +int32_t esvg::Renderer::getNumberSubScanLine() const { + return m_nbSubScanLine; +} + + diff --git a/src/org/atriasoft/esvg/Renderer.java b/src/org/atriasoft/esvg/Renderer.java new file mode 100644 index 0000000..420627a --- /dev/null +++ b/src/org/atriasoft/esvg/Renderer.java @@ -0,0 +1,73 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace esvg { + class Document; + class Renderer { + #ifdef DEBUG + private: + bool m_visualDebug; + int32_t m_factor; + #endif + public: + Renderer(const ivec2& _size, esvg::Document* _document, bool _visualDebug=false); + ~Renderer(); + protected: + ivec2 m_size; + public: + void setSize(const ivec2& _size); + const ivec2& getSize() const; + protected: + etk::Vector> m_buffer; + public: + etk::Vector> getData(); + protected: + int32_t m_interpolationRecurtionMax; + public: + void setInterpolationRecurtionMax(int32_t _value); + int32_t getInterpolationRecurtionMax() const; + protected: + float m_interpolationThreshold; + public: + void setInterpolationThreshold(float _value); + float getInterpolationThreshold() const; + protected: + int32_t m_nbSubScanLine; + public: + void setNumberSubScanLine(int32_t _value); + int32_t getNumberSubScanLine() const; + public: + void writePPM(const etk::Uri& _uri); + void writeBMP(const etk::Uri& _uri); + protected: + etk::Color mergeColor(etk::Color _base, etk::Color _integration); + public: + void print(const esvg::render::Weight& _weightFill, + ememory::SharedPtr& _colorFill, + const esvg::render::Weight& _weightStroke, + ememory::SharedPtr& _colorStroke, + float _opacity); + #ifdef DEBUG + void addDebugSegment(const esvg::render::SegmentList& _listSegment); + void addDebug(const etk::Vector>& _info); + #endif + protected: + esvg::Document* m_document; + public: + esvg::Document* getMainDocument() { + return m_document; + } + }; +} + diff --git a/src/org/atriasoft/esvg/Text.cpp b/src/org/atriasoft/esvg/Text.cpp new file mode 100644 index 0000000..99ea620 --- /dev/null +++ b/src/org/atriasoft/esvg/Text.cpp @@ -0,0 +1,28 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::Text::Text(PaintState _parentPaintState) : esvg::Base(_parentPaintState) { + +} + +esvg::Text::~Text() { + +} + +bool esvg::Text::parse(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax) { + _sizeMax.setValue(0,0); + Log.error("NOT IMPLEMENTED"); + return false; +} + +void esvg::Text::display(int32_t _spacing) { + Log.debug(spacingDist(_spacing) << "Text"); +} + + diff --git a/src/org/atriasoft/esvg/Text.java b/src/org/atriasoft/esvg/Text.java new file mode 100644 index 0000000..ba00400 --- /dev/null +++ b/src/org/atriasoft/esvg/Text.java @@ -0,0 +1,19 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include + +namespace esvg { + class Text : public esvg::Base { + public: + Text(PaintState _parentPaintState); + ~Text(); + bool parse(const exml::Element& _element, mat2x3& _parentTrans, vec2& _sizeMax); + void display(int32_t _spacing) override; + }; +} + diff --git a/src/org/atriasoft/esvg/cap.cpp b/src/org/atriasoft/esvg/cap.cpp new file mode 100644 index 0000000..c77719a --- /dev/null +++ b/src/org/atriasoft/esvg/cap.cpp @@ -0,0 +1,20 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +static const char* values[] = { + "butt", + "round", + "square" +}; + +etk::Stream& esvg::operator <<(etk::Stream& _os, enum esvg::cap _obj) { + _os << values[_obj]; + return _os; +} + diff --git a/src/org/atriasoft/esvg/cap.java b/src/org/atriasoft/esvg/cap.java new file mode 100644 index 0000000..a7eed9a --- /dev/null +++ b/src/org/atriasoft/esvg/cap.java @@ -0,0 +1,22 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include + +namespace esvg { + enum cap { + cap_butt, + cap_round, + cap_square + }; + /** + * @brief Debug operator To display the curent element in a Human redeable information + */ + etk::Stream& operator <<(etk::Stream& _os, enum esvg::cap _obj); +} + diff --git a/src/org/atriasoft/esvg/debug.cpp b/src/org/atriasoft/esvg/debug.cpp new file mode 100644 index 0000000..dcb1404 --- /dev/null +++ b/src/org/atriasoft/esvg/debug.cpp @@ -0,0 +1,13 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include + +int32_t esvg::getLogId() { + static int32_t g_val = elog::registerInstance("esvg"); + return g_val; +} + diff --git a/src/org/atriasoft/esvg/esvg.cpp b/src/org/atriasoft/esvg/esvg.cpp new file mode 100644 index 0000000..03bad9c --- /dev/null +++ b/src/org/atriasoft/esvg/esvg.cpp @@ -0,0 +1,400 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +esvg::Document::Document() { + m_uri = ""; + m_version = "0.0"; + m_loadOK = false; + m_size.setValue(0,0); +} + +esvg::Document::~Document() { + +} + + + +void esvg::Document::displayDebug() { + Log.debug("Main SVG: size=" << m_size); + Log.debug(" refs:"); + for (size_t iii=0; iiidisplay(2); + } + } + Log.debug(" Nodes:"); + for (size_t iii=0; iiidisplay(2); + } + } +} + + +void esvg::Document::draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level) { + for (size_t iii=0; iiidraw(_myRenderer, _basicTrans); + } + } +} + +// FOR TEST only ... +void esvg::Document::generateAnImage(const etk::Uri& _uri, bool _visualDebug) { + generateAnImage(m_size, _uri, _visualDebug); +} +void esvg::Document::generateAnImage(const ivec2& _size, const etk::Uri& _uri, bool _visualDebug) { + ivec2 sizeRender = _size; + if (sizeRender.x() <= 0) { + sizeRender.setX(m_size.x()); + } + if (sizeRender.y() <= 0) { + sizeRender.setY(m_size.y()); + } + Log.debug("Generate size " << sizeRender); + + ememory::SharedPtr renderedElement = ememory::makeShared(sizeRender, this, _visualDebug); + // create the first element matrix modification ... + mat2x3 basicTrans; + basicTrans *= etk::mat2x3Scale(vec2(sizeRender.x()/m_size.x(), sizeRender.y()/m_size.y())); + + draw(*renderedElement, basicTrans); + + if (_uri.getPath().getExtention() == "ppm") { + renderedElement->writePPM(_uri); + } else if (_uri.getPath().getExtention() == "bmp") { + renderedElement->writeBMP(_uri); + } else { + Log.error("Can not store with this extention : " << _uri << " not in .bmp/.ppm"); + } +} + + +etk::Vector> esvg::Document::renderImageFloatRGBA(ivec2& _size) { + if (_size.x() <= 0) { + _size.setX(m_size.x()); + } + if (_size.y() <= 0) { + _size.setY(m_size.y()); + } + Log.debug("Generate size " << _size); + ememory::SharedPtr renderedElement = ememory::makeShared(_size, this); + // create the first element matrix modification ... + mat2x3 basicTrans; + basicTrans *= etk::mat2x3Scale(vec2(_size.x()/m_size.x(), _size.y()/m_size.y())); + draw(*renderedElement, basicTrans); + + // direct return the generated data ... + return renderedElement->getData(); +} + +etk::Vector> esvg::Document::renderImageFloatRGB(ivec2& _size) { + etk::Vector> data = renderImageFloatRGBA(_size); + // Reduce scope: + etk::Vector> out; + out.resize(data.size()); + for (size_t iii=0; iii> esvg::Document::renderImageU8RGBA(ivec2& _size) { + etk::Vector> data = renderImageFloatRGBA(_size); + // Reduce scope: + etk::Vector> out; + out.resize(data.size()); + for (size_t iii=0; iii> esvg::Document::renderImageU8RGB(ivec2& _size) { + etk::Vector> data = renderImageFloatRGBA(_size); + // Reduce scope: + etk::Vector> out; + out.resize(data.size()); + for (size_t iii=0; iii listStyle = etk::split(content, ';'); + for (auto &it : listStyle) { + etk::Vector value = etk::split(it, ':'); + if (value.size() != 2) { + Log.error("parsing style with a wrong patern : " << it << " missing ':'"); + continue; + } + // TODO : Check if the attibute already exist ... + child.attributes.set(value[0], value[1]); + } + } + // remove attribute style: + child.attributes.remove("style"); + } + // sub-parsing ... + cleanStyleProperty(child); + } + return true; +} + +bool esvg::Document::parseXMLData(const exml::Element& _root, bool _isReference) { + // get the svg version : + m_version = _root.attributes["version"]; + // parse ... + vec2 pos(0,0); + if (_isReference == false) { + parseTransform(_root); + parsePosition(_root, pos, m_size); + parsePaintAttr(_root); + Log.verbose("parsed .ROOT trans: " << m_transformMatrix); + } else { + Log.verbose("Parse Reference section ... (no attibute)"); + } + vec2 maxSize(0,0); + vec2 size(0,0); + // parse all sub node: + for(auto it : _root.nodes) { + exml::Element child = it.toElement(); + if (child.exist() == false) { + // comment can be here... + continue; + } + ememory::SharedPtr elementParser; + if (child.getValue() == "g") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "a") { + Log.info("Note : 'a' balise is parsed like a g balise ..."); + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "title") { + m_title = "TODO : set the title here ..."; + continue; + } else if (child.getValue() == "path") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "rect") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "circle") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "ellipse") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "line") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "polyline") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "polygon") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "text") { + elementParser = ememory::makeShared(m_paint); + } else if (child.getValue() == "radialGradient") { + if (_isReference == false) { + Log.error("'" << child.getValue() << "' node must not be defined outside a defs Section"); + continue; + } else { + elementParser = ememory::makeShared(m_paint); + } + } else if (child.getValue() == "linearGradient") { + if (_isReference == false) { + Log.error("'" << child.getValue() << "' node must not be defined outside a defs Section"); + continue; + } else { + elementParser = ememory::makeShared(m_paint); + } + } else if (child.getValue() == "defs") { + if (_isReference == true) { + Log.error("'" << child.getValue() << "' node must not be defined in a defs Section"); + continue; + } else { + bool retRefs = parseXMLData(child, true); + // TODO : Use retRefs ... + continue; + } + } else if (child.getValue() == "sodipodi:namedview") { + // Node ignore : generaly inkscape data + continue; + } else if (child.getValue() == "metadata") { + // Node ignore : generaly inkscape data + continue; + } else { + Log.error("(l " << child.getPos() << ") node not suported : '" << child.getValue() << "' must be [title,g,a,path,rect,circle,ellipse,line,polyline,polygon,text,metadata]"); + } + if (elementParser == null) { + Log.error("(l " << child.getPos() << ") error on node: '" << child.getValue() << "' allocation error or not supported ..."); + continue; + } + if (elementParser->parseXML(child, m_transformMatrix, size) == false) { + Log.error("(l " << child.getPos() << ") error on node: '" << child.getValue() << "' Sub Parsing ERROR"); + elementParser.reset(); + continue; + } + if (maxSize.x() esvg::Document::getReference(const etk::String& _name) { + if (_name == "") { + Log.error("request a reference with no name ... "); + return null; + } + for (auto &it : m_refList) { + if (it == null) { + continue; + } + if (it->getId() == _name) { + return it; + } + } + Log.error("Can not find reference name : '" << _name << "'"); + return null; +} + +etk::Vector> esvg::Document::getLines(vec2 _size) { + etk::Vector> out; + if (_size.x() <= 0) { + _size.setX(m_size.x()); + } + if (_size.y() <= 0) { + _size.setY(m_size.y()); + } + Log.debug("lineification size " << _size); + // create the first element matrix modification ... + mat2x3 basicTrans; + basicTrans *= etk::mat2x3Scale(vec2(_size.x()/m_size.x(), _size.y()/m_size.y())); + drawShapePoints(out, 10, 0.25f, basicTrans); + return out; +} + + +void esvg::Document::drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level) { + Log.verbose(spacingDist(_level) << "DRAW shape esvg::Document"); + for (auto &it : m_subElementList) { + if (it != null) { + it->drawShapePoints(_out, _recurtionMax, _threshold, _basicTrans, _level+1); + } + } +} diff --git a/src/org/atriasoft/esvg/esvg.java b/src/org/atriasoft/esvg/esvg.java new file mode 100644 index 0000000..d8682c8 --- /dev/null +++ b/src/org/atriasoft/esvg/esvg.java @@ -0,0 +1,105 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include +#include + +#include + +/** + * @brief Main esvg namespace + */ +namespace esvg { + class Document : public esvg::Base { + private: + etk::Uri m_uri; + bool m_loadOK; + etk::String m_version; + etk::String m_title; + etk::Vector> m_subElementList; //!< sub-element list + etk::Vector> m_refList; //!< reference elements ... + vec2 m_size; + public: + Document(); + ~Document(); + 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 etk::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(etk::String& _data); + /** + * @brief Load the file that might contain the svg + * @param[in] _uri File of the svg + * @return false : An error occured + * @return true : Parsing is OK + */ + bool load(const etk::Uri& _uri); + /** + * @brief Store the SVG in the file + * @param[in] _uri File of the svg + * @return false : An error occured + * @return true : Parsing is OK + */ + bool store(const etk::Uri& _uri); + protected: + /** + * @brief change all style in a xml atribute + */ + virtual bool cleanStyleProperty(const exml::Element& _root); + virtual bool parseXMLData(const exml::Element& _root, bool _isReference = false); + public: + bool isLoadOk() { + return m_loadOK; + }; + /** + * @brief Display all the node in the svg file. + */ + void displayDebug(); + // TODO: remove this fucntion : use generic function ... + void generateAnImage(const etk::Uri& _uri, bool _visualDebug=false); + void generateAnImage(const ivec2& _size, const etk::Uri& _uri, bool _visualDebug=false); + /** + * @brief Generate Image in a specific format. + * @param[in,out] _size Size expected of the rendered image (value <=0 if it need to be automatic.) return the size generate + * @return Vector of the data used to display (simple vector: generic to transmit) + */ + etk::Vector> renderImageFloatRGBA(ivec2& _size); + //! @previous + etk::Vector> renderImageFloatRGB(ivec2& _size); + //! @previous + etk::Vector> renderImageU8RGBA(ivec2& _size); + //! @previous + etk::Vector> renderImageU8RGB(ivec2& _size); + etk::Vector> getLines(vec2 _size=vec2(256,256)); + protected: + void draw(esvg::Renderer& _myRenderer, mat2x3& _basicTrans, int32_t _level=0) override; + public: + vec2 getDefinedSize() { + return m_size; + }; + ememory::SharedPtr getReference(const etk::String& _name); + protected: + void drawShapePoints(etk::Vector>& _out, + int32_t _recurtionMax, + float _threshold, + mat2x3& _basicTrans, + int32_t _level=1) override; + }; +} + diff --git a/src/org/atriasoft/esvg/gradientUnits.cpp b/src/org/atriasoft/esvg/gradientUnits.cpp new file mode 100644 index 0000000..a01ebe2 --- /dev/null +++ b/src/org/atriasoft/esvg/gradientUnits.cpp @@ -0,0 +1,19 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +static const char* values[] = { + "userSpaceOnUse", + "objectBoundingBox" +}; + +etk::Stream& esvg::operator <<(etk::Stream& _os, enum esvg::gradientUnits _obj) { + _os << values[_obj]; + return _os; +} + diff --git a/src/org/atriasoft/esvg/gradientUnits.java b/src/org/atriasoft/esvg/gradientUnits.java new file mode 100644 index 0000000..1ffac4c --- /dev/null +++ b/src/org/atriasoft/esvg/gradientUnits.java @@ -0,0 +1,20 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include + +namespace esvg { + enum gradientUnits { + gradientUnits_userSpaceOnUse, + gradientUnits_objectBoundingBox + }; + /** + * @brief Debug operator To display the curent element in a Human redeable information + */ + etk::Stream& operator <<(etk::Stream& _os, enum esvg::gradientUnits _obj); +} diff --git a/src/org/atriasoft/esvg/internal/Log.java b/src/org/atriasoft/esvg/internal/Log.java new file mode 100644 index 0000000..245f93d --- /dev/null +++ b/src/org/atriasoft/esvg/internal/Log.java @@ -0,0 +1,68 @@ +package org.atriasoft.esvg.internal; + +import io.scenarium.logger.LogLevel; +import io.scenarium.logger.Logger; + +class Log { + private static final String LIB_NAME = "esvg"; + private static final String LIB_NAME_DRAW = Logger.getDrawableName(LIB_NAME); + private static final boolean PRINT_CRITICAL = Logger.getNeedPrint(LIB_NAME, LogLevel.CRITICAL); + private static final boolean PRINT_ERROR = Logger.getNeedPrint(LIB_NAME, LogLevel.ERROR); + private static final boolean PRINT_WARNING = Logger.getNeedPrint(LIB_NAME, LogLevel.WARNING); + private static final boolean PRINT_INFO = Logger.getNeedPrint(LIB_NAME, LogLevel.INFO); + private static final boolean PRINT_DEBUG = Logger.getNeedPrint(LIB_NAME, LogLevel.DEBUG); + private static final boolean PRINT_VERBOSE = Logger.getNeedPrint(LIB_NAME, LogLevel.VERBOSE); + private static final boolean PRINT_TODO = Logger.getNeedPrint(LIB_NAME, LogLevel.TODO); + private static final boolean PRINT_PRINT = Logger.getNeedPrint(LIB_NAME, LogLevel.PRINT); + + public static void critical(final String data) { + if (PRINT_CRITICAL) { + Logger.critical(LIB_NAME_DRAW, data); + } + } + + public static void debug(final String data) { + if (PRINT_DEBUG) { + Logger.debug(LIB_NAME_DRAW, data); + } + } + + public static void error(final String data) { + if (PRINT_ERROR) { + Logger.error(LIB_NAME_DRAW, data); + } + } + + public static void info(final String data) { + if (PRINT_INFO) { + Logger.info(LIB_NAME_DRAW, data); + } + } + + public static void print(final String data) { + if (PRINT_PRINT) { + Logger.print(LIB_NAME_DRAW, data); + } + } + + public static void todo(final String data) { + if (PRINT_TODO) { + Logger.todo(LIB_NAME_DRAW, data); + } + } + + public static void verbose(final String data) { + if (PRINT_VERBOSE) { + Logger.verbose(LIB_NAME_DRAW, data); + } + } + + public static void warning(final String data) { + if (PRINT_WARNING) { + Logger.warning(LIB_NAME_DRAW, data); + } + } + + private Log() {} + +} diff --git a/src/org/atriasoft/esvg/join.cpp b/src/org/atriasoft/esvg/join.cpp new file mode 100644 index 0000000..db89cf3 --- /dev/null +++ b/src/org/atriasoft/esvg/join.cpp @@ -0,0 +1,20 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +static const char* values[] = { + "miter", + "round", + "bevel" +}; + +etk::Stream& esvg::operator <<(etk::Stream& _os, enum esvg::join _obj) { + _os << values[_obj]; + return _os; +} + diff --git a/src/org/atriasoft/esvg/join.java b/src/org/atriasoft/esvg/join.java new file mode 100644 index 0000000..5c8ba6e --- /dev/null +++ b/src/org/atriasoft/esvg/join.java @@ -0,0 +1,23 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include + +namespace esvg { + enum join { + join_miter, + join_round, + join_bevel + }; + /** + * @brief Debug operator To display the curent element in a Human redeable information + */ + etk::Stream& operator <<(etk::Stream& _os, enum esvg::join _obj); +} + + diff --git a/src/org/atriasoft/esvg/render/DynamicColor.cpp b/src/org/atriasoft/esvg/render/DynamicColor.cpp new file mode 100644 index 0000000..92e03c1 --- /dev/null +++ b/src/org/atriasoft/esvg/render/DynamicColor.cpp @@ -0,0 +1,452 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include +#include + +esvg::render::DynamicColorSpecial::DynamicColorSpecial(const etk::String& _link, const mat2x3& _mtx) : + m_linear(true), + m_colorName(_link), + m_matrix(_mtx), + m_viewPort(vec2(9999999999.0,9999999999.0),vec2(-9999999999.0,-9999999999.0)) { + +} + +void esvg::render::DynamicColorSpecial::setViewPort(const etk::Pair& _viewPort) { + m_viewPort = _viewPort; +} + + +static vec2 getIntersect(const vec2& _point1, + const vec2& _vect1, + const vec2& _point2, + const vec2& _vect2) { + float diviseur = _vect1.x() * _vect2.y() - _vect1.y() * _vect2.x(); + if(diviseur != 0.0f) { + float mmm = ( _vect1.x() * _point1.y() + - _vect1.x() * _point2.y() + - _vect1.y() * _point1.x() + + _vect1.y() * _point2.x() + ) / diviseur; + return vec2(_point2 + _vect2 * mmm); + } + Log.error("Get divider / 0.0f"); + return _point2; +} + +etk::Color esvg::render::DynamicColorSpecial::getColor(const ivec2& _pos) const { + if (m_data.size() < 2) { + return etk::color::purple; + } + if (m_linear == true) { + return getColorLinear(_pos); + } else { + return getColorRadial(_pos); + } + return etk::color::purple; +} + +etk::Color esvg::render::DynamicColorSpecial::getColorLinear(const ivec2& _pos) const { + float ratio = 0.0f; + if (m_unit == gradientUnits_userSpaceOnUse) { + 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(); + vec2 vectorBaseDraw = intersec - m_pos1; + float baseDraw = vectorBaseDraw.length(); + ratio = baseDraw / baseSize; + switch(m_spread) { + case spreadMethod_pad: + if (vectorBase.dot(vectorBaseDraw) < 0) { + ratio *= -1.0; + } + break; + case spreadMethod_reflect: + ratio -= float((int32_t(ratio)>>1)<<1); + if (ratio > 1.0f) { + ratio = 2.0f-ratio; + } + break; + case spreadMethod_repeat: + if (vectorBase.dot(vectorBaseDraw) < 0) { + ratio *= -1.0; + } + ratio -= float(int32_t(ratio)); + if (ratio <0.0f) { + #ifndef __STDCPP_LLVM__ + ratio = 1.0f-etk::abs(ratio); + #else + ratio = 1.0f-abs(ratio); + #endif + } + break; + } + } 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); + vec2 vectorBaseDrawX = intersecX - m_pos1; + vec2 vectorBaseDrawY = intersecY - m_pos1; + float baseDrawX = vectorBaseDrawX.length(); + float baseDrawY = vectorBaseDrawY.length(); + 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(); + } + } else { + ratio = 1.0f; + } + switch(m_spread) { + case spreadMethod_pad: + // nothing to do ... + break; + case spreadMethod_reflect: + #ifndef __STDCPP_LLVM__ + ratio = etk::abs(ratio); + #else + ratio = abs(ratio); + #endif + ratio -= float((int32_t(ratio)>>1)<<1); + if (ratio > 1.0f) { + ratio = 2.0f-ratio; + } + break; + case spreadMethod_repeat: + ratio -= float(int32_t(ratio)); + if (ratio <0.0f) { + #ifndef __STDCPP_LLVM__ + ratio = 1.0f-etk::abs(ratio); + #else + ratio = 1.0f-abs(ratio); + #endif + } + break; + } + } + 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[iii-1].second.r() * (1.0-localRatio) + m_data[iii].second.r() * localRatio, + m_data[iii-1].second.g() * (1.0-localRatio) + m_data[iii].second.g() * localRatio, + m_data[iii-1].second.b() * (1.0-localRatio) + m_data[iii].second.b() * localRatio, + m_data[iii-1].second.a() * (1.0-localRatio) + m_data[iii].second.a() * localRatio); + } + } + return etk::color::green; +} +static etk::Pair intersectLineToCircle(const vec2& _pos1, + const vec2& _pos2, + const vec2& _center = vec2(0.0f, 0.0f), + float _radius = 1.0f) { + vec2 v1; + vec2 v2; + //vector2D from point 1 to point 2 + v1 = _pos2 - _pos1; + //vector2D from point 1 to the circle's center + v2 = _center - _pos1; + + float dot = v1.dot(v2); + vec2 proj1 = vec2(((dot / (v1.length2())) * v1.x()), + ((dot / (v1.length2())) * v1.y())); + vec2 midpt = _pos1 + proj1; + + float distToCenter = (midpt - _center).length2(); + if (distToCenter > _radius * _radius) { + return etk::Pair(vec2(0.0,0.0), vec2(0.0,0.0)); + } + if (distToCenter == _radius * _radius) { + return etk::Pair(midpt, midpt); + } + float distToIntersection; + if (distToCenter == 0.0f) { + distToIntersection = _radius; + } else { + #ifndef __STDCPP_LLVM__ + distToCenter = etk::sqrt(distToCenter); + distToIntersection = etk::sqrt(_radius * _radius - distToCenter * distToCenter); + #else + distToCenter = sqrtf(distToCenter); + distToIntersection = sqrtf(_radius * _radius - distToCenter * distToCenter); + #endif + } + // normalize... + v1.safeNormalize(); + v1 *= distToIntersection; + return etk::Pair(midpt + v1, midpt - v1); +} + +etk::Color esvg::render::DynamicColorSpecial::getColorRadial(const ivec2& _pos) const { + float ratio = 0.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, + vec2(_pos.x(), _pos.y()), m_axeY); + vec2 intersecY = getIntersect(m_pos1, m_axeY, + vec2(_pos.x(), _pos.y()), m_axeX); + vec2 vectorBaseDrawX = intersecX - m_pos1; + vec2 vectorBaseDrawY = intersecY - m_pos1; + float baseDrawX = vectorBaseDrawX.length(); + float baseDrawY = vectorBaseDrawY.length(); + // specal case when focal == center (this is faster ...) + if (m_centerIsFocal == true) { + ratio = vec2(baseDrawX, baseDrawY).length(); + if (m_baseSize.x()+m_baseSize.y() != 0.0f) { + if ( m_baseSize.x() != 0.0f + && m_baseSize.y() != 0.0f) { + ratio = vec2(baseDrawX/m_baseSize.x(), baseDrawY/m_baseSize.y()).length(); + } else if (m_baseSize.x() != 0.0f) { + ratio = baseDrawX/m_baseSize.x(); + } else { + ratio = baseDrawY/m_baseSize.y(); + } + } else { + ratio = 1.0f; + } + } else { + // set the sense of the elements: + if (m_axeX.dot(vectorBaseDrawX) < 0) { + baseDrawX *= -1.0f; + } + if (m_axeY.dot(vectorBaseDrawY) < 0) { + baseDrawY *= -1.0f; + } + if (m_baseSize.y() != 0.0f) { + baseDrawY /= m_baseSize.y(); + } + // normalize to 1.0f + baseDrawX /= m_baseSize.x(); + if ( m_clipOut == true + && baseDrawX <= -1.0f) { + ratio = 1.0f; + } else { + float tmpLength = -m_focalLength/m_baseSize.x(); + vec2 focalCenter = vec2(tmpLength, 0.0f); + vec2 currentPoint = vec2(baseDrawX, baseDrawY); + if (focalCenter == currentPoint) { + ratio = 0.0f; + } else { + etk::Pair positions = intersectLineToCircle(focalCenter, currentPoint); + float lenghtBase = (currentPoint - focalCenter).length(); + float lenghtBorder1 = (positions.first - focalCenter).length(); + float lenghtBorder2 = (positions.second - focalCenter).length(); + ratio = lenghtBase/lenghtBorder1; + } + } + } + switch(m_spread) { + case spreadMethod_pad: + // nothing to do ... + break; + case spreadMethod_reflect: + ratio -= float((int32_t(ratio)>>1)<<1); + if (ratio > 1.0f) { + ratio = 2.0f-ratio; + } + break; + case spreadMethod_repeat: + ratio -= float(int32_t(ratio)); + if (ratio <0.0f) { + #ifndef __STDCPP_LLVM__ + ratio = 1.0f-etk::abs(ratio); + #else + ratio = 1.0f-abs(ratio); + #endif + } + break; + } + 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[iii-1].second.r() * (1.0-localRatio) + m_data[iii].second.r() * localRatio, + m_data[iii-1].second.g() * (1.0-localRatio) + m_data[iii].second.g() * localRatio, + m_data[iii-1].second.b() * (1.0-localRatio) + m_data[iii].second.b() * localRatio, + m_data[iii-1].second.a() * (1.0-localRatio) + m_data[iii].second.a() * localRatio); + } + } + return etk::color::green; +} + + +void esvg::render::DynamicColorSpecial::generate(esvg::Document* _document) { + if (_document == null) { + Log.error("Get null input for document"); + return; + } + ememory::SharedPtr base = _document->getReference(m_colorName); + if (base == null) { + Log.error("Can not get base : '" << m_colorName << "'"); + return; + } + // Now we can know if we use linear or radial gradient ... + ememory::SharedPtr gradient = ememory::dynamicPointerCast(base); + if (gradient != null) { + m_linear = true; + Log.verbose("get for color linear:"); + gradient->display(2); + m_unit = gradient->m_unit; + m_spread = gradient->m_spread; + Log.verbose(" viewport = {" << m_viewPort.first << "," << m_viewPort.second << "}"); + vec2 size = m_viewPort.second - m_viewPort.first; + + esvg::Dimension dimPos1 = gradient->getPosition1(); + m_pos1 = dimPos1.getPixel(size); + if (dimPos1.getType() == esvg::distance_pourcent) { + m_pos1 += m_viewPort.first; + } + esvg::Dimension dimPos2 = gradient->getPosition2(); + m_pos2 = dimPos2.getPixel(size); + if (dimPos2.getType() == esvg::distance_pourcent) { + m_pos2 += m_viewPort.first; + } + // 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) { + m_axeX = vec2(-1.0f, 0.0f); + } else { + m_axeX = vec2(1.0f, 0.0f); + } + if (delta.y() < 0.0f) { + m_axeY = vec2(0.0f, -1.0f); + } 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.. + 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(_document); + } else { + m_linear = false; + ememory::SharedPtr gradient = ememory::dynamicPointerCast(base); + if (gradient == null) { + Log.error("Can not cast in a linear gradient: '" << m_colorName << "' ==> wrong type"); + return; + } + Log.verbose("get for color Radial:"); + gradient->display(2); + m_unit = gradient->m_unit; + m_spread = gradient->m_spread; + Log.verbose(" viewport = {" << m_viewPort.first << "," << m_viewPort.second << "}"); + vec2 size = m_viewPort.second - m_viewPort.first; + + esvg::Dimension dimCenter = gradient->getCenter(); + vec2 center = dimCenter.getPixel(size); + if (dimCenter.getType() == esvg::distance_pourcent) { + center += m_viewPort.first; + } + esvg::Dimension dimFocal = gradient->getFocal(); + vec2 focal = dimFocal.getPixel(size); + if (dimFocal.getType() == esvg::distance_pourcent) { + focal += m_viewPort.first; + } + esvg::Dimension1D dimRadius = gradient->getRadius(); + // 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).. + if (center == focal) { + m_centerIsFocal = true; + m_pos2.setX(dimRadius.getPixel(size.x())); + m_pos2.setY(dimRadius.getPixel(size.y())); + m_pos2 += center; + vec2 delta = center - m_pos2; + if (delta.x() < 0.0f) { + m_axeX = vec2(-1.0f, 0.0f); + } else { + m_axeX = vec2(1.0f, 0.0f); + } + if (delta.y() < 0.0f) { + m_axeY = vec2(0.0f, -1.0f); + } else { + m_axeY = vec2(0.0f, 1.0f); + } + m_pos1 = center; + } else { + m_centerIsFocal = false; + m_axeX = (center - focal).safeNormalize(); + m_axeY = vec2(m_axeX.y(), -m_axeX.x()); + + m_pos2 = m_axeX * dimRadius.getPixel(size.x()) + m_axeY * dimRadius.getPixel(size.y()); + m_pos2 += center; + m_pos1 = center; + } + // Move the positions ... + m_pos1 = m_matrix * m_pos1; + center = m_matrix * center; + 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.. + 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((intersecX - m_pos1).length(), + (intersecY - m_pos1).length()); + if (m_centerIsFocal == false) { + m_focalLength = (center - m_matrix * focal).length(); + if (m_focalLength >= m_baseSize.x()) { + Log.debug("Change position of the Focal ... ==> set it inside the circle"); + m_focalLength = m_baseSize.x()*0.999998f; + m_clipOut = true; + } else { + m_clipOut = false; + } + } + Log.verbose("baseSize=" << m_baseSize << " m_pos1=" << m_pos1 << " dim=" << dimCenter << " m_focal=" << m_focal << " m_pos2=" << m_pos2 << " dim=" << dimRadius); + // get all the colors + m_data = gradient->getColors(_document); + } +} + +ememory::SharedPtr esvg::render::createColor(etk::Pair, etk::String> _color, const mat2x3& _mtx) { + // Check if need to create a color: + if ( _color.first.a() == 0x00 + && _color.second == "") { + return null; + } + if (_color.second != "") { + return ememory::makeShared(_color.second, _mtx); + } + return ememory::makeShared(_color.first); +} diff --git a/src/org/atriasoft/esvg/render/DynamicColor.java b/src/org/atriasoft/esvg/render/DynamicColor.java new file mode 100644 index 0000000..73f70c0 --- /dev/null +++ b/src/org/atriasoft/esvg/render/DynamicColor.java @@ -0,0 +1,80 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace esvg { + class Document; + namespace render { + class DynamicColor { + public: + DynamicColor() { + // nothing to do ... + } + virtual ~DynamicColor() {}; + virtual etk::Color getColor(const ivec2& _pos) const = 0; + virtual void generate(esvg::Document* _document) = 0; + virtual void setViewPort(const etk::Pair& _viewPort) = 0; + }; + class DynamicColorUni : public esvg::render::DynamicColor { + public: + etk::Color m_color; + public: + DynamicColorUni(const etk::Color& _color) : + m_color(_color) { + + } + virtual etk::Color getColor(const ivec2& _pos) const { + return m_color; + } + virtual void generate(esvg::Document* _document) { + // nothing to do ... + } + virtual void setViewPort(const etk::Pair& _viewPort) { + // nothing to do ... + }; + }; + class DynamicColorSpecial : public esvg::render::DynamicColor { + public: + bool m_linear; + esvg::spreadMethod m_spread; + esvg::gradientUnits m_unit; + etk::String m_colorName; + mat2x3 m_matrix; + etk::Pair m_viewPort; + vec2 m_pos1; // in radius ==> center + vec2 m_pos2; // in radius ==> radius end position + vec2 m_focal; // Specific radius + vec2 m_axeX; + vec2 m_axeY; + vec2 m_baseSize; + float m_focalLength; + bool m_clipOut; + bool m_centerIsFocal; + etk::Vector>> m_data; + public: + DynamicColorSpecial(const etk::String& _link, const mat2x3& _mtx); + virtual etk::Color getColor(const ivec2& _pos) const; + private: + etk::Color getColorLinear(const ivec2& _pos) const; + etk::Color getColorRadial(const ivec2& _pos) const; + public: + virtual void generate(esvg::Document* _document); + virtual void setViewPort(const etk::Pair& _viewPort); + }; + + ememory::SharedPtr createColor(etk::Pair, etk::String> _color, const mat2x3& _mtx); + } +} + diff --git a/src/org/atriasoft/esvg/render/Element.cpp b/src/org/atriasoft/esvg/render/Element.cpp new file mode 100644 index 0000000..c46c695 --- /dev/null +++ b/src/org/atriasoft/esvg/render/Element.cpp @@ -0,0 +1,57 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +etk::Stream& esvg::operator <<(etk::Stream& _os, enum esvg::render::path _obj) { + switch (_obj) { + case esvg::render::path_stop: + _os << "path_stop"; + break; + case esvg::render::path_close: + _os << "path_close"; + break; + case esvg::render::path_moveTo: + _os << "path_moveTo"; + break; + case esvg::render::path_lineTo: + _os << "path_lineTo"; + break; + case esvg::render::path_lineToH: + _os << "path_lineToH"; + break; + case esvg::render::path_lineToV: + _os << "path_lineToV"; + break; + case esvg::render::path_curveTo: + _os << "path_curveTo"; + break; + case esvg::render::path_smoothCurveTo: + _os << "path_smoothCurveTo"; + break; + case esvg::render::path_bezierCurveTo: + _os << "path_bezierCurveTo"; + break; + case esvg::render::path_bezierSmoothCurveTo: + _os << "path_bezierSmoothCurveTo"; + break; + case esvg::render::path_elliptic: + _os << "path_elliptic"; + break; + default: + _os << "????"; + break; + }; + return _os; +} +etk::Stream& esvg::operator <<(etk::Stream& _os, const esvg::render::Element& _obj) { + _os << _obj.getType(); + _os << ": rel=" << etk::toString(_obj.getRelative()) << " "; + _os << _obj.display(); + return _os; +} + diff --git a/src/org/atriasoft/esvg/render/Element.java b/src/org/atriasoft/esvg/render/Element.java new file mode 100644 index 0000000..8bfed54 --- /dev/null +++ b/src/org/atriasoft/esvg/render/Element.java @@ -0,0 +1,101 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include + +namespace esvg { + namespace render { + enum path { + path_stop, + path_close, + path_moveTo, + path_lineTo, + path_lineToH, + path_lineToV, + path_curveTo, + path_smoothCurveTo, + path_bezierCurveTo, + path_bezierSmoothCurveTo, + path_elliptic + }; + class Element { + public: + Element(enum path _type, bool _relative=false) : + m_cmd(_type), + m_relative(_relative) { + + } + virtual ~Element() { } + private: + enum path m_cmd; + public: + enum path getType() const { + return m_cmd; + } + protected: + bool m_relative; + public: + bool getRelative() const { + return m_relative; + } + void setRelative(bool _relative) { + m_relative = _relative; + } + protected: + vec2 m_pos; + public: + const vec2& getPos() const { + return m_pos; + } + void setPos(const vec2& _val) { + m_pos = _val; + } + protected: + vec2 m_pos1; + public: + const vec2& getPos1() const { + return m_pos1; + } + void setPos1(const vec2& _val) { + m_pos1 = _val; + } + protected: + vec2 m_pos2; + public: + const vec2& getPos2() const { + return m_pos2; + } + void setPos2(const vec2& _val) { + m_pos2 = _val; + } + public: + virtual etk::String display() const = 0; + }; + } + /** + * @brief Debug operator To display the curent element in a Human redeable information + */ + etk::Stream& operator <<(etk::Stream& _os, const esvg::render::Element& _obj); + /** + * @brief Debug operator To display the curent element in a Human redeable information + */ + etk::Stream& operator <<(etk::Stream& _os, enum esvg::render::path _obj); +} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + diff --git a/src/org/atriasoft/esvg/render/ElementBezierCurveTo.cpp b/src/org/atriasoft/esvg/render/ElementBezierCurveTo.cpp new file mode 100644 index 0000000..7a4a386 --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementBezierCurveTo.cpp @@ -0,0 +1,18 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::ElementBezierCurveTo::ElementBezierCurveTo(bool _relative, const vec2& _pos1, const vec2& _pos): + Element(esvg::render::path_bezierCurveTo, _relative) { + m_pos = _pos; + m_pos1 = _pos1; +} + +etk::String esvg::render::ElementBezierCurveTo::display() const { + return etk::String("pos=") + etk::toString(m_pos) + " pos1=" + etk::toString(m_pos1); +} \ No newline at end of file diff --git a/src/org/atriasoft/esvg/render/ElementBezierCurveTo.java b/src/org/atriasoft/esvg/render/ElementBezierCurveTo.java new file mode 100644 index 0000000..42cda1f --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementBezierCurveTo.java @@ -0,0 +1,22 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esvg { + namespace render { + class ElementBezierCurveTo : public esvg::render::Element { + public: + ElementBezierCurveTo(bool _relative, const vec2& _pos1, const vec2& _pos); + public: + virtual etk::String display() const; + }; + } +} + diff --git a/src/org/atriasoft/esvg/render/ElementBezierSmoothCurveTo.cpp b/src/org/atriasoft/esvg/render/ElementBezierSmoothCurveTo.cpp new file mode 100644 index 0000000..a57adce --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementBezierSmoothCurveTo.cpp @@ -0,0 +1,18 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::ElementBezierSmoothCurveTo::ElementBezierSmoothCurveTo(bool _relative, const vec2& _pos): + Element(esvg::render::path_bezierSmoothCurveTo, _relative) { + m_pos = _pos; +} + + +etk::String esvg::render::ElementBezierSmoothCurveTo::display() const { + return etk::String("pos=") + etk::toString(m_pos); +} \ No newline at end of file diff --git a/src/org/atriasoft/esvg/render/ElementBezierSmoothCurveTo.java b/src/org/atriasoft/esvg/render/ElementBezierSmoothCurveTo.java new file mode 100644 index 0000000..eb779f3 --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementBezierSmoothCurveTo.java @@ -0,0 +1,23 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esvg { + namespace render { + class ElementBezierSmoothCurveTo : public esvg::render::Element { + public: + ElementBezierSmoothCurveTo(bool _relative, const vec2& _pos); + public: + virtual etk::String display() const; + }; + } +} + + diff --git a/src/org/atriasoft/esvg/render/ElementClose.cpp b/src/org/atriasoft/esvg/render/ElementClose.cpp new file mode 100644 index 0000000..73eec7f --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementClose.cpp @@ -0,0 +1,19 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::ElementClose::ElementClose(bool _relative): + Element(esvg::render::path_close, _relative) { + +} + +etk::String esvg::render::ElementClose::display() const { + return ""; +} + + diff --git a/src/org/atriasoft/esvg/render/ElementClose.java b/src/org/atriasoft/esvg/render/ElementClose.java new file mode 100644 index 0000000..5fd2304 --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementClose.java @@ -0,0 +1,22 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esvg { + namespace render { + class ElementClose : public esvg::render::Element { + public: + ElementClose(bool _relative=false); + public: + virtual etk::String display() const; + }; + } +} + diff --git a/src/org/atriasoft/esvg/render/ElementCurveTo.cpp b/src/org/atriasoft/esvg/render/ElementCurveTo.cpp new file mode 100644 index 0000000..638895f --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementCurveTo.cpp @@ -0,0 +1,21 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::ElementCurveTo::ElementCurveTo(bool _relative, const vec2& _pos1, const vec2& _pos2, const vec2& _pos): + Element(esvg::render::path_curveTo, _relative) { + m_pos = _pos; + m_pos1 = _pos1; + m_pos2 = _pos2; +} + + + +etk::String esvg::render::ElementCurveTo::display() const { + return etk::String("pos=") + etk::toString(m_pos) + " pos1=" + etk::toString(m_pos1) + " pos2=" + etk::toString(m_pos2); +} \ No newline at end of file diff --git a/src/org/atriasoft/esvg/render/ElementCurveTo.java b/src/org/atriasoft/esvg/render/ElementCurveTo.java new file mode 100644 index 0000000..8d79a98 --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementCurveTo.java @@ -0,0 +1,21 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include + +namespace esvg { + namespace render { + class ElementCurveTo : public esvg::render::Element { + public: + ElementCurveTo(bool _relative, const vec2& _pos1, const vec2& _pos2, const vec2& _pos); + public: + virtual etk::String display() const; + }; + } +} + diff --git a/src/org/atriasoft/esvg/render/ElementElliptic.cpp b/src/org/atriasoft/esvg/render/ElementElliptic.cpp new file mode 100644 index 0000000..2d14496 --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementElliptic.cpp @@ -0,0 +1,31 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::ElementElliptic::ElementElliptic(bool _relative, + const vec2& _radius, // in m_vec1 + float _angle, + bool _largeArcFlag, + bool _sweepFlag, + const vec2& _pos): + Element(esvg::render::path_elliptic, _relative) { + m_pos1 = _radius; + m_pos = _pos; + m_angle = _angle; + m_largeArcFlag = _largeArcFlag; + m_sweepFlag = _sweepFlag; +} + + +etk::String esvg::render::ElementElliptic::display() const { + return etk::String("pos=") + etk::toString(m_pos) + + " radius=" + etk::toString(m_pos1) + + " angle=" + etk::toString(m_angle) + + " largeArcFlag=" + etk::toString(m_largeArcFlag) + + " sweepFlag=" + etk::toString(m_sweepFlag); +} \ No newline at end of file diff --git a/src/org/atriasoft/esvg/render/ElementElliptic.java b/src/org/atriasoft/esvg/render/ElementElliptic.java new file mode 100644 index 0000000..c00474a --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementElliptic.java @@ -0,0 +1,31 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esvg { + namespace render { + class ElementElliptic : public esvg::render::Element { + public: + float m_angle; + bool m_largeArcFlag; + bool m_sweepFlag; + public: + ElementElliptic(bool _relative, + const vec2& _radius, // in m_pos1 + float _angle, + bool _largeArcFlag, + bool _sweepFlag, + const vec2& _pos); + public: + virtual etk::String display() const; + }; + } +} + diff --git a/src/org/atriasoft/esvg/render/ElementLineTo.cpp b/src/org/atriasoft/esvg/render/ElementLineTo.cpp new file mode 100644 index 0000000..e54cf0e --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementLineTo.cpp @@ -0,0 +1,18 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::ElementLineTo::ElementLineTo(bool _relative, const vec2& _pos): + Element(esvg::render::path_lineTo, _relative) { + m_pos = _pos; +} + + +etk::String esvg::render::ElementLineTo::display() const { + return etk::String("pos=") + etk::toString(m_pos); +} \ No newline at end of file diff --git a/src/org/atriasoft/esvg/render/ElementLineTo.java b/src/org/atriasoft/esvg/render/ElementLineTo.java new file mode 100644 index 0000000..9688bbd --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementLineTo.java @@ -0,0 +1,22 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esvg { + namespace render { + class ElementLineTo : public esvg::render::Element { + public: + ElementLineTo(bool _relative, const vec2& _pos); + public: + virtual etk::String display() const; + }; + } +} + diff --git a/src/org/atriasoft/esvg/render/ElementLineToH.cpp b/src/org/atriasoft/esvg/render/ElementLineToH.cpp new file mode 100644 index 0000000..ab73437 --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementLineToH.cpp @@ -0,0 +1,18 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::ElementLineToH::ElementLineToH(bool _relative, float _posX): + Element(esvg::render::path_lineToH, _relative) { + m_pos = vec2(_posX, 0.0f); +} + + +etk::String esvg::render::ElementLineToH::display() const { + return etk::String("posX=") + etk::toString(m_pos.x()); +} \ No newline at end of file diff --git a/src/org/atriasoft/esvg/render/ElementLineToH.java b/src/org/atriasoft/esvg/render/ElementLineToH.java new file mode 100644 index 0000000..3a0edee --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementLineToH.java @@ -0,0 +1,22 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esvg { + namespace render { + class ElementLineToH : public esvg::render::Element { + public: + ElementLineToH(bool _relative, float _posX); + public: + virtual etk::String display() const; + }; + } +} + diff --git a/src/org/atriasoft/esvg/render/ElementLineToV.cpp b/src/org/atriasoft/esvg/render/ElementLineToV.cpp new file mode 100644 index 0000000..17b10f9 --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementLineToV.cpp @@ -0,0 +1,18 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::ElementLineToV::ElementLineToV(bool _relative, float _posY): + Element(esvg::render::path_lineToV, _relative) { + m_pos = vec2(0.0f, _posY); +} + + +etk::String esvg::render::ElementLineToV::display() const { + return etk::String("posY=") + etk::toString(m_pos.y()); +} \ No newline at end of file diff --git a/src/org/atriasoft/esvg/render/ElementLineToV.java b/src/org/atriasoft/esvg/render/ElementLineToV.java new file mode 100644 index 0000000..9b01f86 --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementLineToV.java @@ -0,0 +1,23 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esvg { + namespace render { + class ElementLineToV : public esvg::render::Element { + public: + ElementLineToV(bool _relative, float _posY); + public: + virtual etk::String display() const; + }; + } +} + + diff --git a/src/org/atriasoft/esvg/render/ElementMoveTo.cpp b/src/org/atriasoft/esvg/render/ElementMoveTo.cpp new file mode 100644 index 0000000..c833e7b --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementMoveTo.cpp @@ -0,0 +1,18 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::ElementMoveTo::ElementMoveTo(bool _relative, const vec2& _pos): + Element(esvg::render::path_moveTo, _relative) { + m_pos = _pos; +} + + +etk::String esvg::render::ElementMoveTo::display() const { + return etk::String("pos=") + etk::toString(m_pos); +} \ No newline at end of file diff --git a/src/org/atriasoft/esvg/render/ElementMoveTo.java b/src/org/atriasoft/esvg/render/ElementMoveTo.java new file mode 100644 index 0000000..eb52b72 --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementMoveTo.java @@ -0,0 +1,22 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esvg { + namespace render { + class ElementMoveTo : public esvg::render::Element { + public: + ElementMoveTo(bool _relative, const vec2& _pos); + public: + virtual etk::String display() const; + }; + } +} + diff --git a/src/org/atriasoft/esvg/render/ElementSmoothCurveTo.cpp b/src/org/atriasoft/esvg/render/ElementSmoothCurveTo.cpp new file mode 100644 index 0000000..03428d0 --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementSmoothCurveTo.cpp @@ -0,0 +1,19 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::ElementSmoothCurveTo::ElementSmoothCurveTo(bool _relative, const vec2& _pos2, const vec2& _pos): + Element(esvg::render::path_smoothCurveTo, _relative) { + m_pos = _pos; + m_pos2 = _pos2; +} + + +etk::String esvg::render::ElementSmoothCurveTo::display() const { + return etk::String("pos=") + etk::toString(m_pos) + " pos2=" + etk::toString(m_pos2); +} \ No newline at end of file diff --git a/src/org/atriasoft/esvg/render/ElementSmoothCurveTo.java b/src/org/atriasoft/esvg/render/ElementSmoothCurveTo.java new file mode 100644 index 0000000..0119c6c --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementSmoothCurveTo.java @@ -0,0 +1,22 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esvg { + namespace render { + class ElementSmoothCurveTo : public esvg::render::Element { + public: + ElementSmoothCurveTo(bool _relative, const vec2& _pos2, const vec2& _pos); + public: + virtual etk::String display() const; + }; + } +} + diff --git a/src/org/atriasoft/esvg/render/ElementStop.cpp b/src/org/atriasoft/esvg/render/ElementStop.cpp new file mode 100644 index 0000000..47af044 --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementStop.cpp @@ -0,0 +1,19 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::ElementStop::ElementStop(): + Element(esvg::render::path_stop) { + +} + +etk::String esvg::render::ElementStop::display() const { + return ""; +} + + diff --git a/src/org/atriasoft/esvg/render/ElementStop.java b/src/org/atriasoft/esvg/render/ElementStop.java new file mode 100644 index 0000000..680077c --- /dev/null +++ b/src/org/atriasoft/esvg/render/ElementStop.java @@ -0,0 +1,22 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esvg { + namespace render { + class ElementStop : public esvg::render::Element { + public: + ElementStop(); + public: + virtual etk::String display() const; + }; + } +} + diff --git a/src/org/atriasoft/esvg/render/Path.cpp b/src/org/atriasoft/esvg/render/Path.cpp new file mode 100644 index 0000000..4da1bab --- /dev/null +++ b/src/org/atriasoft/esvg/render/Path.cpp @@ -0,0 +1,513 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#include +#include +#include + +void esvg::render::Path::clear() { + m_listElement.clear(); +} + +void esvg::render::Path::stop() { + m_listElement.pushBack(ememory::makeShared()); +} + +void esvg::render::Path::close(bool _relative) { + m_listElement.pushBack(ememory::makeShared(_relative)); +} + +void esvg::render::Path::moveTo(bool _relative, const vec2& _pos) { + m_listElement.pushBack(ememory::makeShared(_relative, _pos)); +} + +void esvg::render::Path::lineTo(bool _relative, const vec2& _pos) { + m_listElement.pushBack(ememory::makeShared(_relative, _pos)); +} + +void esvg::render::Path::lineToH(bool _relative, float _posX) { + m_listElement.pushBack(ememory::makeShared(_relative, _posX)); +} + +void esvg::render::Path::lineToV(bool _relative, float _posY) { + m_listElement.pushBack(ememory::makeShared(_relative, _posY)); +} + +void esvg::render::Path::curveTo(bool _relative, const vec2& _pos1, const vec2& _pos2, const vec2& _pos) { + m_listElement.pushBack(ememory::makeShared(_relative, _pos1, _pos2, _pos)); +} + +void esvg::render::Path::smoothCurveTo(bool _relative, const vec2& _pos2, const vec2& _pos) { + m_listElement.pushBack(ememory::makeShared(_relative, _pos2, _pos)); +} + +void esvg::render::Path::bezierCurveTo(bool _relative, const vec2& _pos1, const vec2& _pos) { + m_listElement.pushBack(ememory::makeShared(_relative, _pos1, _pos)); +} + +void esvg::render::Path::bezierSmoothCurveTo(bool _relative, const vec2& _pos) { + m_listElement.pushBack(ememory::makeShared(_relative, _pos)); +} + +void esvg::render::Path::ellipticTo(bool _relative, + const vec2& _radius, + float _angle, + bool _largeArcFlag, + bool _sweepFlag, + const vec2& _pos) { + m_listElement.pushBack(ememory::makeShared(_relative, _radius, _angle, _largeArcFlag, _sweepFlag, _pos)); +} + +static const char* spacingDist(int32_t _spacing) { + static const char *tmpValue = " "; + if (_spacing>20) { + _spacing = 20; + } + return tmpValue + 20*4 - _spacing*4; +} + +void esvg::render::Path::display(int32_t _spacing) { + Log.debug(spacingDist(_spacing) << "Path"); + for(auto &it : m_listElement) { + if (it == null) { + continue; + } + Log.debug(spacingDist(_spacing+1) << *it); + } +} + + +void interpolateCubicBezier(etk::Vector& _listPoint, + int32_t _recurtionMax, + float _threshold, + vec2 _pos1, + vec2 _pos2, + vec2 _pos3, + vec2 _pos4, + int32_t _level, + enum esvg::render::Point::type _type) { + if (_level > _recurtionMax) { + return; + } + vec2 pos12 = (_pos1+_pos2)*0.5f; + vec2 pos23 = (_pos2+_pos3)*0.5f; + vec2 pos34 = (_pos3+_pos4)*0.5f; + + vec2 delta = _pos4 - _pos1; + #ifndef __STDCPP_LLVM__ + float distance2 = etk::abs(((_pos2.x() - _pos4.x()) * delta.y() - (_pos2.y() - _pos4.y()) * delta.x() )); + float distance3 = etk::abs(((_pos3.x() - _pos4.x()) * delta.y() - (_pos3.y() - _pos4.y()) * delta.x() )); + #else + float distance2 = fabs(((_pos2.x() - _pos4.x()) * delta.y() - (_pos2.y() - _pos4.y()) * delta.x() )); + float distance3 = fabs(((_pos3.x() - _pos4.x()) * delta.y() - (_pos3.y() - _pos4.y()) * delta.x() )); + #endif + + if ((distance2 + distance3)*(distance2 + distance3) < _threshold * delta.length2()) { + _listPoint.pushBack(esvg::render::Point(_pos4, _type) ); + return; + } + vec2 pos123 = (pos12+pos23)*0.5f; + vec2 pos234 = (pos23+pos34)*0.5f; + vec2 pos1234 = (pos123+pos234)*0.5f; + + interpolateCubicBezier(_listPoint, _recurtionMax, _threshold, _pos1, pos12, pos123, pos1234, _level+1, esvg::render::Point::type::interpolation); + interpolateCubicBezier(_listPoint, _recurtionMax, _threshold, pos1234, pos234, pos34, _pos4, _level+1, _type); +} + +static float vectorAngle(vec2 _uuu, vec2 _vvv) { + _uuu.safeNormalize(); + _vvv.safeNormalize(); + return atan2(_uuu.cross(_vvv), _uuu.dot(_vvv)); +} + +esvg::render::PointList esvg::render::Path::generateListPoints(int32_t _level, int32_t _recurtionMax, float _threshold) { + Log.verbose(spacingDist(_level) << "Generate List Points ... from a path"); + esvg::render::PointList out; + etk::Vector tmpListPoint; + vec2 lastPosition(0.0f, 0.0f); + vec2 lastAngle(0.0f, 0.0f); + int32_t lastPointId = -1; + bool PathStart = false; + // Foreach element, we move in the path: + for(auto &it : m_listElement) { + if (it == null) { + continue; + } + Log.verbose(spacingDist(_level+1) << " Draw : " << *it); + switch (it->getType()) { + case esvg::render::path_stop: + if (tmpListPoint.size() != 0) { + if (tmpListPoint.size() == 0) { + Log.warning(spacingDist(_level+1) << " Request path stop of not starting path ..."); + } else { + tmpListPoint.back().setEndPath(); + out.addList(tmpListPoint); + tmpListPoint.clear(); + } + } + lastAngle = vec2(0.0f, 0.0f); + // nothing alse to do ... + break; + case esvg::render::path_close: + if (tmpListPoint.size() != 0) { + if (tmpListPoint.size() == 0) { + Log.warning(spacingDist(_level+1) << " Request path close of not starting path ..."); + } else { + // find the previous tart of the path ... + tmpListPoint.front().m_type = esvg::render::Point::type::join; + // Remove the last point if it is the same position... + vec2 delta = (tmpListPoint.front().m_pos - tmpListPoint.back().m_pos).absolute(); + if ( delta.x() <= 0.00001 + && delta.y() <= 0.00001) { + tmpListPoint.popBack(); + Log.verbose(" Remove point Z property : " << tmpListPoint.back().m_pos << " with delta=" << delta); + } + out.addList(tmpListPoint); + tmpListPoint.clear(); + } + } + lastAngle = vec2(0.0f, 0.0f); + // nothing alse to do ... + break; + case esvg::render::path_moveTo: + // stop last path + if (tmpListPoint.size() != 0) { + tmpListPoint.back().setEndPath(); + out.addList(tmpListPoint); + tmpListPoint.clear(); + } + // create a new one + if (it->getRelative() == false) { + lastPosition = vec2(0.0f, 0.0f); + } + lastPosition += it->getPos(); + tmpListPoint.pushBack(esvg::render::Point(lastPosition, esvg::render::Point::type::start)); + lastAngle = lastPosition; + break; + case esvg::render::path_lineTo: + // If no previous point, we need to create the last point has start ... + if (tmpListPoint.size() == 0) { + tmpListPoint.pushBack(esvg::render::Point(lastPosition, esvg::render::Point::type::start)); + } + if (it->getRelative() == false) { + lastPosition = vec2(0.0f, 0.0f); + } + lastPosition += it->getPos(); + tmpListPoint.pushBack(esvg::render::Point(lastPosition, esvg::render::Point::type::join)); + lastAngle = lastPosition; + break; + case esvg::render::path_lineToH: + // If no previous point, we need to create the last point has start ... + if (tmpListPoint.size() == 0) { + tmpListPoint.pushBack(esvg::render::Point(lastPosition, esvg::render::Point::type::start)); + } + if (it->getRelative() == false) { + lastPosition = vec2(0.0f, 0.0f); + } + lastPosition += it->getPos(); + tmpListPoint.pushBack(esvg::render::Point(lastPosition, esvg::render::Point::type::join)); + lastAngle = lastPosition; + break; + case esvg::render::path_lineToV: + // If no previous point, we need to create the last point has start ... + if (tmpListPoint.size() == 0) { + tmpListPoint.pushBack(esvg::render::Point(lastPosition, esvg::render::Point::type::start)); + } + if (it->getRelative() == false) { + lastPosition = vec2(0.0f, 0.0f); + } + lastPosition += it->getPos(); + tmpListPoint.pushBack(esvg::render::Point(lastPosition, esvg::render::Point::type::join)); + lastAngle = lastPosition; + break; + case esvg::render::path_curveTo: + // If no previous point, we need to create the last point has start ... + if (tmpListPoint.size() == 0) { + tmpListPoint.pushBack(esvg::render::Point(lastPosition, esvg::render::Point::type::join)); + } + { + vec2 lastPosStore(lastPosition); + if (it->getRelative() == false) { + lastPosition = vec2(0.0f, 0.0f); + } + vec2 pos1 = lastPosition + it->getPos1(); + vec2 pos2 = lastPosition + it->getPos2(); + vec2 pos = lastPosition + it->getPos(); + interpolateCubicBezier(tmpListPoint, + _recurtionMax, + _threshold, + lastPosStore, + pos1, + pos2, + pos, + 0, + esvg::render::Point::type::join); + lastPosition = pos; + lastAngle = pos2; + } + break; + case esvg::render::path_smoothCurveTo: + // If no previous point, we need to create the last point has start ... + if (tmpListPoint.size() == 0) { + tmpListPoint.pushBack(esvg::render::Point(lastPosition, esvg::render::Point::type::join)); + } + { + vec2 lastPosStore(lastPosition); + if (it->getRelative() == false) { + lastPosition = vec2(0.0f, 0.0f); + } + vec2 pos2 = lastPosition + it->getPos2(); + vec2 pos = lastPosition + it->getPos(); + // generate Pos 1 + vec2 pos1 = lastPosStore*2.0f - lastAngle; + interpolateCubicBezier(tmpListPoint, + _recurtionMax, + _threshold, + lastPosStore, + pos1, + pos2, + pos, + 0, + esvg::render::Point::type::join); + lastPosition = pos; + lastAngle = pos2; + } + break; + case esvg::render::path_bezierCurveTo: + // If no previous point, we need to create the last point has start ... + if (tmpListPoint.size() == 0) { + tmpListPoint.pushBack(esvg::render::Point(lastPosition, esvg::render::Point::type::join)); + } + { + vec2 lastPosStore(lastPosition); + if (it->getRelative() == false) { + lastPosition = vec2(0.0f, 0.0f); + } + vec2 pos = lastPosition + it->getPos(); + vec2 tmp1 = lastPosition + it->getPos1(); + // generate pos1 and pos2 + vec2 pos1 = lastPosStore + (tmp1 - lastPosStore)*0.666666666f; + vec2 pos2 = pos + (tmp1 - pos)*0.666666666f; + interpolateCubicBezier(tmpListPoint, + _recurtionMax, + _threshold, + lastPosStore, + pos1, + pos2, + pos, + 0, + esvg::render::Point::type::join); + lastPosition = pos; + lastAngle = tmp1; + } + break; + case esvg::render::path_bezierSmoothCurveTo: + // If no previous point, we need to create the last point has start ... + if (tmpListPoint.size() == 0) { + tmpListPoint.pushBack(esvg::render::Point(lastPosition, esvg::render::Point::type::join)); + } + { + vec2 lastPosStore(lastPosition); + if (it->getRelative() == false) { + lastPosition = vec2(0.0f, 0.0f); + } + vec2 pos = lastPosition + it->getPos(); + vec2 tmp1 = lastPosStore*2.0f - lastAngle; + // generate pos1 and pos2 + vec2 pos1 = lastPosStore + (tmp1 - lastPosStore)*0.666666666f; + vec2 pos2 = pos + (tmp1 - pos)*0.66666666f; + interpolateCubicBezier(tmpListPoint, + _recurtionMax, + _threshold, + lastPosStore, + pos1, + pos2, + pos, + 0, + esvg::render::Point::type::join); + lastPosition = pos; + lastAngle = tmp1; + } + break; + case esvg::render::path_elliptic: + // If no previous point, we need to create the last point has start ... + if (tmpListPoint.size() == 0) { + tmpListPoint.pushBack(esvg::render::Point(lastPosition, esvg::render::Point::type::join)); + } + { + ememory::SharedPtr tmpIt(ememory::dynamicPointerCast(it)); + float angle = tmpIt->m_angle * (M_PI / 180.0); + ESVG_TODO(spacingDist(_level+1) << " Elliptic arc: radius=" << tmpIt->getPos1()); + ESVG_TODO(spacingDist(_level+1) << " angle=" << tmpIt->m_angle); + ESVG_TODO(spacingDist(_level+1) << " m_largeArcFlag=" << tmpIt->m_largeArcFlag); + ESVG_TODO(spacingDist(_level+1) << " m_sweepFlag=" << tmpIt->m_sweepFlag); + + + vec2 lastPosStore(lastPosition); + if (it->getRelative() == false) { + lastPosition = vec2(0.0f, 0.0f); + } + vec2 pos = lastPosition + it->getPos(); + float rotationX = tmpIt->m_angle * (M_PI / 180.0); + vec2 radius = tmpIt->getPos1(); + + #ifdef DEBUG + m_debugInformation.addSegment(lastPosStore, pos); + #endif + vec2 delta = lastPosStore - pos; + float ddd = delta.length(); + if ( ddd < 1e-6f + || radius.x() < 1e-6f + || radius.y() < 1e-6f) { + Log.warning("Degenerate arc in Line"); + if (tmpListPoint.size() == 0) { + tmpListPoint.pushBack(esvg::render::Point(lastPosition, esvg::render::Point::type::join)); + } + tmpListPoint.pushBack(esvg::render::Point(pos, esvg::render::Point::type::join)); + } else { + // Convert to center point parameterization. + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + // procedure describe here : http://www.w3.org/TR/SVG11/implnote.html#ArcConversionCenterToEndpoint + // Compute delta' + mat2x3 matrixRotationCenter = etk::mat2x3Rotate(-rotationX); + vec2 deltaPrim = matrixRotationCenter * (delta*0.5f); + ddd = (deltaPrim.x()*deltaPrim.x())/(radius.x()*radius.x()) + + (deltaPrim.y()*deltaPrim.y())/(radius.y()*radius.y()); + if (ddd > 1.0f) { + #ifndef __STDCPP_LLVM__ + ddd = etk::sqrt(ddd); + #else + ddd = sqrtf(ddd); + #endif + radius *= ddd; + } + // Compute center' + float sss = 0.0f; + float ssa = radius.x()*radius.x()*radius.y()*radius.y() + - radius.x()*radius.x()*deltaPrim.y()*deltaPrim.y() + - radius.y()*radius.y()*deltaPrim.x()*deltaPrim.x(); + float ssb = radius.x()*radius.x()*deltaPrim.y()*deltaPrim.y() + + radius.y()*radius.y()*deltaPrim.x()*deltaPrim.x(); + if (ssa < 0.0f) { + ssa = 0.0f; + } + if (ssb > 0.0f) { + #ifndef __STDCPP_LLVM__ + sss = etk::sqrt(ssa / ssb); + #else + sss = sqrtf(ssa / ssb); + #endif + } + if (tmpIt->m_largeArcFlag == tmpIt->m_sweepFlag) { + sss *= -1.0f; + } + vec2 centerPrime(sss * radius.x() * deltaPrim.y() / radius.y(), + sss * -radius.y() * deltaPrim.x() / radius.x()); + // Compute center from center' + mat2x3 matrix = etk::mat2x3Rotate(rotationX); + vec2 center = (lastPosStore + pos)*0.5f + matrix*centerPrime; + #ifdef DEBUG + m_debugInformation.addSegment(center-vec2(3.0,3.0), center+vec2(3.0,3.0)); + m_debugInformation.addSegment(center-vec2(3.0,-3.0), center+vec2(3.0,-3.0)); + #endif + // Calculate theta1, and delta theta. + vec2 vectorA = (deltaPrim - centerPrime) / radius; + vec2 vectorB = (deltaPrim + centerPrime) / radius * -1.0f; + #ifdef DEBUG + m_debugInformation.addSegment(center, center+vectorA*radius.x()); + m_debugInformation.addSegment(center, center+vectorB*radius.y()); + #endif + // Initial angle + float theta1 = vectorAngle(vec2(1.0f,0.0f), vectorA); + // Delta angle + float deltaTheta = vectorAngle(vectorA, vectorB); + // special case of invert angle... + if ( ( deltaTheta == float(M_PI) + || deltaTheta == -float(M_PI)) + && tmpIt->m_sweepFlag == false) { + deltaTheta *= -1.0f; + } + if (tmpIt->m_largeArcFlag == true) { + // Choose large arc + if (deltaTheta > 0.0f) { + deltaTheta -= 2.0f*M_PI; + } else { + deltaTheta += 2.0f*M_PI; + } + } + // Approximate the arc using cubic spline segments. + matrix.translate(center); + // Split arc into max 90 degree segments. + // The loop assumes an iteration per end point (including start and end), this +1. + #ifndef __STDCPP_LLVM__ + int32_t ndivs = int32_t(etk::abs(deltaTheta) / (M_PI*0.5f)) + 1; + #else + int32_t ndivs = int32_t(fabs(deltaTheta) / (M_PI*0.5f)) + 1; + #endif + float hda = (deltaTheta / float(ndivs)) * 0.5f; + #ifndef __STDCPP_LLVM__ + float kappa = etk::abs(4.0f / 3.0f * (1.0f - etk::cos(hda)) / etk::sin(hda)); + #else + float kappa = fabs(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda)); + #endif + if (deltaTheta < 0.0f) { + kappa = -kappa; + } + vec2 pointPosPrevious(0.0,0.0); + vec2 tangentPrevious(0.0,0.0); + for (int32_t iii=0; iii<=ndivs; ++iii) { + float a = theta1 + deltaTheta * (float(iii)/(float)ndivs); + #ifndef __STDCPP_LLVM__ + delta = vec2(etk::cos(a), etk::sin(a)); + #else + delta = vec2(cosf(a), sinf(a)); + #endif + // position + vec2 pointPos = matrix * vec2(delta.x()*radius.x(), delta.y()*radius.y()); + // tangent + vec2 tangent = matrix.applyScaleRotation(vec2(-delta.y()*radius.x() * kappa, delta.x()*radius.y() * kappa)); + if (iii > 0) { + vec2 zlastPosStore(lastPosition); + if (it->getRelative() == false) { + lastPosition = vec2(0.0f, 0.0f); + } + vec2 zpos1 = pointPosPrevious + tangentPrevious; + vec2 zpos2 = pointPos - tangent; + vec2 zpos = pointPos; + interpolateCubicBezier(tmpListPoint, + _recurtionMax, + _threshold, + zlastPosStore, + zpos1, + zpos2, + zpos, + 0, + esvg::render::Point::type::join); + lastPosition = zpos; + lastAngle = zpos2; + } + pointPosPrevious = pointPos; + tangentPrevious = tangent; + } + } + lastPosition = pos; + } + break; + default: + Log.error(spacingDist(_level+1) << " Unknow PATH commant (internal error)"); + break; + } + } + // special case : No request end of path ==> open path: + if (tmpListPoint.size() != 0) { + Log.verbose("Auto-end PATH"); + tmpListPoint.back().setEndPath(); + out.addList(tmpListPoint); + tmpListPoint.clear(); + } + out.display(); + return out; +} + diff --git a/src/org/atriasoft/esvg/render/Path.java b/src/org/atriasoft/esvg/render/Path.java new file mode 100644 index 0000000..d1e27c1 --- /dev/null +++ b/src/org/atriasoft/esvg/render/Path.java @@ -0,0 +1,55 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include +#include +#include +#ifdef DEBUG + #include +#endif + +namespace esvg { + namespace render { + class Path { + public: + etk::Vector> m_listElement; + #ifdef DEBUG + esvg::render::SegmentList m_debugInformation; + #endif + public: + Path() { + + } + + ~Path() { + + } + void clear(); + void stop(); + void close(bool _relative=false); + void moveTo(bool _relative, const vec2& _pos); + void lineTo(bool _relative, const vec2& _pos); + void lineToH(bool _relative, float _posX); + void lineToV(bool _relative, float _posY); + void curveTo(bool _relative, const vec2& _pos1, const vec2& _pos2, const vec2& _pos); + void smoothCurveTo(bool _relative, const vec2& _pos2, const vec2& _pos); + void bezierCurveTo(bool _relative, const vec2& _pos1, const vec2& _pos); + void bezierSmoothCurveTo(bool _relative, const vec2& _pos); + void ellipticTo(bool _relative, + const vec2& _radius, + float _angle, + bool _largeArcFlag, + bool _sweepFlag, + const vec2& _pos); + void display(int32_t _spacing); + esvg::render::PointList generateListPoints(int32_t _level, int32_t _recurtionMax = 10, float _threshold = 0.25f); + }; + } +} + diff --git a/src/org/atriasoft/esvg/render/Point.cpp b/src/org/atriasoft/esvg/render/Point.cpp new file mode 100644 index 0000000..d387f15 --- /dev/null +++ b/src/org/atriasoft/esvg/render/Point.cpp @@ -0,0 +1,31 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +void esvg::render::Point::setEndPath() { + if (m_type == esvg::render::Point::type::interpolation) { + Log.warning("Request stop path of an interpolate Point"); + m_type = esvg::render::Point::type::stop; + return; + } + if (m_type == esvg::render::Point::type::stop) { + Log.warning("Request stop path of an STOP Point"); + return; + } + if (m_type == esvg::render::Point::type::start) { + m_type = esvg::render::Point::type::single; + return; + } + m_type = esvg::render::Point::type::stop; +} + +void esvg::render::Point::normalize(const vec2& _nextPoint) { + m_delta = _nextPoint - m_pos; + m_len = m_delta.length(); +} + diff --git a/src/org/atriasoft/esvg/render/Point.java b/src/org/atriasoft/esvg/render/Point.java new file mode 100644 index 0000000..1517c86 --- /dev/null +++ b/src/org/atriasoft/esvg/render/Point.java @@ -0,0 +1,50 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esvg { + namespace render { + class Point { + public: + enum class type { + single, //!< Point type is single, this mean that it start and stop of a path + start, //!< Point type is starting of a path + stop, //!< Point type is stoping of a path + join, //!< Point type in an user point provided inside a path + interpolation, //!< This point is dynamicly calculated to create an interpolation + }; + public: + // TODO : Clean all element here ... + vec2 m_pos; //!< position of the point + enum esvg::render::Point::type m_type; + vec2 m_miterAxe; + vec2 m_orthoAxePrevious; + vec2 m_orthoAxeNext; + vec2 m_posPrevious; + vec2 m_posNext; + vec2 m_delta; + float m_len; + // TODO: Update etk::Vector to support not having it ... + Point() : + m_pos(0,0), + m_type(esvg::render::Point::type::join) { + // nothing to do ... + } + Point(const vec2& _pos, enum esvg::render::Point::type _type = esvg::render::Point::type::join) : + m_pos(_pos), + m_type(_type) { + // nothing to do ... + } + void setEndPath(); + void normalize(const vec2& _nextPoint); + }; + } +} + diff --git a/src/org/atriasoft/esvg/render/PointList.cpp b/src/org/atriasoft/esvg/render/PointList.cpp new file mode 100644 index 0000000..14e1d43 --- /dev/null +++ b/src/org/atriasoft/esvg/render/PointList.cpp @@ -0,0 +1,64 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::PointList::PointList() { + // nothing to do ... +} + +void esvg::render::PointList::addList(etk::Vector& _list) { + m_data.pushBack(_list); + // TODO : Add a checker of correct list ... +} + +void esvg::render::PointList::applyMatrix(const mat2x3& _transformationMatrix) { + for (auto &it : m_data) { + for (auto &val : it) { + val.m_pos = _transformationMatrix * val.m_pos; + } + } +} + +etk::Pair esvg::render::PointList::getViewPort() { + etk::Pair out(vec2(9999999999.0,9999999999.0),vec2(-9999999999.0,-9999999999.0)); + for (auto &it : m_data) { + for (auto &it2 : it) { + out.first.setMin(it2.m_pos); + out.second.setMax(it2.m_pos); + } + } + return out; +} + +void esvg::render::PointList::display() { + Log.verbose(" Display list of points : size=" << m_data.size()); + for (auto &it : m_data) { + Log.verbose(" Find List " << it.size() << " members"); + for (size_t iii=0; + iii < it.size(); + ++iii) { + switch (it[iii].m_type) { + case esvg::render::Point::type::single: + Log.verbose(" [" << iii << "] Find Single " << it[iii].m_pos); + break; + case esvg::render::Point::type::start: + Log.verbose(" [" << iii << "] Find Start " << it[iii].m_pos); + break; + case esvg::render::Point::type::stop: + Log.verbose(" [" << iii << "] Find Stop " << it[iii].m_pos); + break; + case esvg::render::Point::type::interpolation: + Log.verbose(" [" << iii << "] Find interpolation " << it[iii].m_pos); + break; + case esvg::render::Point::type::join: + Log.verbose(" [" << iii << "] Find Join " << it[iii].m_pos); + break; + } + } + } +} diff --git a/src/org/atriasoft/esvg/render/PointList.java b/src/org/atriasoft/esvg/render/PointList.java new file mode 100644 index 0000000..2b6d1ce --- /dev/null +++ b/src/org/atriasoft/esvg/render/PointList.java @@ -0,0 +1,28 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace esvg { + namespace render { + class PointList { + public: + etk::Vector> m_data; + public: + PointList(); + void addList(etk::Vector& _list); + void display(); + void applyMatrix(const mat2x3& _transformationMatrix); + etk::Pair getViewPort(); + }; + } +} diff --git a/src/org/atriasoft/esvg/render/Scanline.cpp b/src/org/atriasoft/esvg/render/Scanline.cpp new file mode 100644 index 0000000..9b9bf55 --- /dev/null +++ b/src/org/atriasoft/esvg/render/Scanline.cpp @@ -0,0 +1,38 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::Scanline::Scanline(size_t _size) { + m_data.resize(_size, 0.0f); +} + +size_t esvg::render::Scanline::size() const { + return m_data.size(); +} + +void esvg::render::Scanline::clear(float _fill) { + for (auto &it : m_data) { + it = _fill; + } +} + +float esvg::render::Scanline::get(int32_t _pos) const { + if( _pos >= 0 + && size_t(_pos) < m_data.size()) { + return m_data[_pos]; + } + return 0; +} + +void esvg::render::Scanline::set(int32_t _pos, float _newColor) { + if( _pos >= 0 + && size_t(_pos) < m_data.size()) { + m_data[_pos] = _newColor; + } +} + diff --git a/src/org/atriasoft/esvg/render/Scanline.java b/src/org/atriasoft/esvg/render/Scanline.java new file mode 100644 index 0000000..3f8f9a6 --- /dev/null +++ b/src/org/atriasoft/esvg/render/Scanline.java @@ -0,0 +1,29 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include + +namespace esvg { + namespace render { + class Scanline { + private: + etk::Vector m_data; + public: + // constructor : + Scanline(size_t _size=32); + // destructor + ~Scanline() { }; + public: + size_t size() const; + void clear(float _fill); + float get(int32_t _pos) const; + void set(int32_t _pos, float _newColor); + }; + } +} + diff --git a/src/org/atriasoft/esvg/render/Segment.cpp b/src/org/atriasoft/esvg/render/Segment.cpp new file mode 100644 index 0000000..a47c210 --- /dev/null +++ b/src/org/atriasoft/esvg/render/Segment.cpp @@ -0,0 +1,39 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +esvg::render::Segment::Segment() { + p0 = vec2(0,0); + p1 = vec2(0,0); + direction = 0; +} + +esvg::render::Segment::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 + p0 = _p0; + p1 = _p1; + direction = 0; +} + +void esvg::render::Segment::applyMatrix(const mat2x3& _transformationMatrix) { + p0 = _transformationMatrix * p0; + p1 = _transformationMatrix * p1; + createDirection(); +} + +void esvg::render::Segment::createDirection() { + if (p0.y() < p1.y()) { + direction = 1; // direction like clock + } else { + vec2 tmp(p0); + p0 = p1; + p1 = tmp; + direction = -1; // direction like anti-clock + } +} + diff --git a/src/org/atriasoft/esvg/render/Segment.java b/src/org/atriasoft/esvg/render/Segment.java new file mode 100644 index 0000000..46b6615 --- /dev/null +++ b/src/org/atriasoft/esvg/render/Segment.java @@ -0,0 +1,27 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esvg { + namespace render { + class Segment { + public: + // TODO: Update etk::Vector to support not having it ... + Segment(); + Segment(const vec2& _p0, const vec2& _p1); + vec2 p0; + vec2 p1; + int32_t direction; + void applyMatrix(const mat2x3& _transformationMatrix); + void createDirection(); + }; + } +} + diff --git a/src/org/atriasoft/esvg/render/SegmentList.cpp b/src/org/atriasoft/esvg/render/SegmentList.cpp new file mode 100644 index 0000000..303e9cf --- /dev/null +++ b/src/org/atriasoft/esvg/render/SegmentList.cpp @@ -0,0 +1,508 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include + +esvg::render::SegmentList::SegmentList() { + +} +#ifdef DEBUG + void esvg::render::SegmentList::addSegment(const vec2& _pos0, const vec2& _pos1) { + m_data.pushBack(Segment(_pos0, _pos1)); + } +#endif + +void esvg::render::SegmentList::addSegment(const esvg::render::Point& _pos0, const esvg::render::Point& _pos1) { + // Skip horizontal Segments + if (_pos0.m_pos.y() == _pos1.m_pos.y()) { + // remove /0 operation + return; + } + m_data.pushBack(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.pushBack(Segment(_pos0.m_pos, _pos1.m_pos)); +} + +etk::Pair esvg::render::SegmentList::getViewPort() { + etk::Pair out(vec2(9999999999.0,9999999999.0),vec2(-9999999999.0,-9999999999.0)); + for (auto &it : m_data) { + out.first.setMin(it.p0); + out.second.setMax(it.p0); + out.first.setMin(it.p1); + out.second.setMax(it.p1); + } + return out; +} +void esvg::render::SegmentList::createSegmentList(const esvg::render::PointList& _listPoint) { + for (auto &it : _listPoint.m_data) { + // Build Segments + for (size_t iii=0, jjj=it.size()-1; + iii < it.size(); + jjj = iii++) { + addSegment(it[jjj], it[iii]); + } + } +} + +static vec2 getIntersect(const vec2& _point1, + const vec2& _vect1, + const vec2& _point2, + const vec2& _vect2) { + float diviseur = _vect1.x() * _vect2.y() - _vect1.y() * _vect2.x(); + if(diviseur != 0.0f) { + float mmm = ( _vect1.x() * _point1.y() + - _vect1.x() * _point2.y() + - _vect1.y() * _point1.x() + + _vect1.y() * _point2.x() + ) / diviseur; + return vec2(_point2 + _vect2 * mmm); + } + Log.error("Get divider / 0.0f"); + return _point2; +} + +void esvg::render::SegmentList::createSegmentListStroke(const vec2& _point1, + const vec2& _point2, + const vec2& _center, + float _width, + bool _isStart) { + int32_t nbDot = int32_t(_width); + if (nbDot <= 2) { + nbDot = 2; + } + float angleToDraw = acos((_point1 - _center).safeNormalize().dot((_point2 - _center).safeNormalize())); + float baseAngle = angleToDraw/float(nbDot); + float iii; + vec2 axe = (_point1 - _center).safeNormalize(); + vec2 ppp1(_point1); + vec2 ppp2(_point2); + for (iii=baseAngle; iii " << ppp1); + } else { + addSegment(ppp1, ppp2); + Log.verbose(" segment :" << ppp1 << " -> " << ppp2); + } + ppp1 = ppp2; + } + if (_isStart == true) { + addSegment(_point2, ppp1); + Log.verbose(" segment :" << _point2 << " -> " << ppp1); + } else { + addSegment(ppp1, _point2); + Log.verbose(" segment :" << ppp1 << " -> " << _point2); + } +} + +void esvg::render::SegmentList::createSegmentListStroke(esvg::render::PointList& _listPoint, + float _width, + enum esvg::cap _cap, + enum esvg::join _join, + float _miterLimit) { + for (auto &itListPoint : _listPoint.m_data) { + // generate for every point all the orthogonal elements + // + // normal edge * end path + // (mitter) * | * * * * * * * * * * * * * * + // * |<--*----this | * + // * | * this -->| * + // * * * | * + // * . | . * . . . . . . . . * * + // * . | . * | * + // * A . | . B * | * + // . * . | * + // . * * . * * * * * * * * * * * * * + // * * + // * * + for (int64_t idPevious=itListPoint.size()-1, idCurrent=0, idNext=1; + idCurrent < int64_t(itListPoint.size()); + idPevious = idCurrent++, idNext++) { + if (idNext == int64_t(itListPoint.size())) { + idNext = 0; + } + if ( itListPoint[idCurrent].m_type == esvg::render::Point::type::join + || itListPoint[idCurrent].m_type == esvg::render::Point::type::interpolation) { + if (idPevious < 0 ) { + Log.error("an error occure a previous ID is < 0.... "); + continue; + } + if (idNext >= int64_t(itListPoint.size())) { + Log.error("an error occure a next ID is >= nbPoint len .... "); + continue; + } + //Log.debug("JOIN : id : prev/curr/next : " << idPevious << "/" << idCurrent << "/" << idNext); + //Log.debug("JOIN : val : prev/curr/next : " << itListPoint[idPevious].m_pos << "/" << itListPoint[idCurrent].m_pos << "/" << itListPoint[idNext].m_pos); + vec2 vecA = itListPoint[idCurrent].m_pos - itListPoint[idPevious].m_pos; + //Log.debug("JOIN : vecA : " << vecA); + vecA.safeNormalize(); + vec2 vecB = itListPoint[idNext].m_pos - itListPoint[idCurrent].m_pos; + //Log.debug("JOIN : vecB : " << vecB); + vecB.safeNormalize(); + vec2 vecC = vecA - vecB; + //Log.debug("JOIN : vecC : " << vecC); + if (vecC == vec2(0.0f, 0.0f)) { + // special case: 1 line ... + itListPoint[idCurrent].m_miterAxe = vec2(vecA.y(), vecA.x()); + } else { + vecC.safeNormalize(); + itListPoint[idCurrent].m_miterAxe = vecC; + } + itListPoint[idCurrent].m_posPrevious = itListPoint[idPevious].m_pos; + itListPoint[idCurrent].m_posNext = itListPoint[idNext].m_pos; + vecB = itListPoint[idNext].m_pos - itListPoint[idCurrent].m_pos; + vecB.safeNormalize(); + itListPoint[idCurrent].m_orthoAxeNext = vec2(vecB.y(), -vecB.x()); + vecB = itListPoint[idCurrent].m_pos - itListPoint[idPevious].m_pos; + vecB.safeNormalize(); + itListPoint[idCurrent].m_orthoAxePrevious = vec2(vecB.y(), -vecB.x()); + //Log.debug("JOIN : miterAxe " << itListPoint[idCurrent].m_miterAxe); + } else if (itListPoint[idCurrent].m_type == esvg::render::Point::type::start) { + itListPoint[idCurrent].m_posNext = itListPoint[idNext].m_pos; + vec2 vecB = itListPoint[idNext].m_pos - itListPoint[idCurrent].m_pos; + vecB.safeNormalize(); + itListPoint[idCurrent].m_miterAxe = vec2(vecB.y(), -vecB.x()); + itListPoint[idCurrent].m_orthoAxePrevious = itListPoint[idCurrent].m_miterAxe; + itListPoint[idCurrent].m_orthoAxeNext = itListPoint[idCurrent].m_miterAxe; + } else if (itListPoint[idCurrent].m_type == esvg::render::Point::type::stop) { + if (idPevious < 0 ) { + Log.error("an error occure a previous ID is < 0.... "); + continue; + } + itListPoint[idCurrent].m_posPrevious = itListPoint[idPevious].m_pos; + vec2 vecA = itListPoint[idCurrent].m_pos - itListPoint[idPevious].m_pos; + vecA.safeNormalize(); + itListPoint[idCurrent].m_miterAxe = vec2(vecA.y(), -vecA.x()); + itListPoint[idCurrent].m_orthoAxePrevious = itListPoint[idCurrent].m_miterAxe; + itListPoint[idCurrent].m_orthoAxeNext = itListPoint[idCurrent].m_miterAxe; + } else { + ESVG_TODO("Unsupported type of point ...."); + } + } + // create segment list: + bool haveStartLine = false; + vec2 leftPoint(0,0); + vec2 rightPoint(0,0); + if (itListPoint.size() > 0) { + if (itListPoint.front().m_type == esvg::render::Point::type::join) { + const esvg::render::Point& it = itListPoint.back(); + // Calculate the perpendiculary axis ... + leftPoint = itListPoint.back().m_pos + + itListPoint.back().m_orthoAxePrevious*_width*0.5f; + rightPoint = itListPoint.back().m_pos + - itListPoint.back().m_orthoAxePrevious*_width*0.5f; + // cyclic path... + if (it.m_type == esvg::render::Point::type::interpolation) { + leftPoint = getIntersect(leftPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe); + rightPoint = getIntersect(rightPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe); + } else if (it.m_type == esvg::render::Point::type::join) { + // Calculate the perpendiculary axis ... + leftPoint = it.m_pos + + it.m_orthoAxePrevious*_width*0.5f; + rightPoint = it.m_pos + - it.m_orthoAxePrevious*_width*0.5f; + // project on the miter Axis ... + switch (_join) { + case esvg::join_miter: + { + vec2 left = getIntersect(leftPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe); + vec2 right = getIntersect(rightPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe); + // Check the miter limit: + float limitRight = (left - it.m_pos).length() / _width * 2.0f; + float limitLeft = (right - it.m_pos).length() / _width * 2.0f; + Log.verbose(" miter Limit: " << limitRight << " " << limitLeft << " <= " << _miterLimit); + if ( limitRight <= _miterLimit + && limitLeft <= _miterLimit) { + leftPoint = left; + rightPoint = right; + break; + } else { + // BEVEL the miter point ... + } + } + case esvg::join_round: + case esvg::join_bevel: + { + vec2 axePrevious = (it.m_pos-it.m_posPrevious).safeNormalize(); + vec2 axeNext = (it.m_posNext - it.m_pos).safeNormalize(); + float cross = axePrevious.cross(axeNext); + if (cross > 0.0f) { + rightPoint = getIntersect(rightPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe); + leftPoint = it.m_pos + + it.m_orthoAxeNext*_width*0.5f; + } else { + leftPoint = getIntersect(leftPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe); + rightPoint = it.m_pos + - it.m_orthoAxeNext*_width*0.5f; + } + break; + } + } + } else { + Log.error("Start list point with a join, but last lement is not a join"); + } + } + } + for (auto &it : itListPoint) { + switch (it.m_type) { + case esvg::render::Point::type::single: + // just do nothing .... + Log.verbose("Find Single " << it.m_pos); + break; + case esvg::render::Point::type::start: + Log.verbose("Find Start " << it.m_pos); + if (haveStartLine == true) { + // close previous : + Log.warning(" find a non close path ..."); + addSegment(leftPoint, rightPoint); + } + haveStartLine = true; + startStopPoint(leftPoint, rightPoint, it, _cap, _width, true); + break; + case esvg::render::Point::type::stop: + Log.verbose("Find Stop " << it.m_pos); + if (haveStartLine == false) { + Log.warning("find close path without start part ..."); + break; + } + haveStartLine = false; + startStopPoint(leftPoint, rightPoint, it, _cap, _width, false); + break; + case esvg::render::Point::type::interpolation: + { + Log.verbose("Find interpolation " << it.m_pos); + vec2 left = getIntersect(leftPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe); + vec2 right = getIntersect(rightPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe); + //Draw from previous point: + addSegment(leftPoint, left); + Log.verbose(" segment :" << leftPoint << " -> " << left); + addSegment(right, rightPoint); + Log.verbose(" segment :" << right << " -> " << rightPoint); + leftPoint = left; + rightPoint = right; + } + break; + case esvg::render::Point::type::join: + Log.verbose("Find join " << it.m_pos); + switch (_join) { + case esvg::join_miter: + { + vec2 left = getIntersect(leftPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe); + vec2 right = getIntersect(rightPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe); + // Check the miter limit: + float limitRight = (left - it.m_pos).length() / _width * 2.0f; + float limitLeft = (right - it.m_pos).length() / _width * 2.0f; + Log.verbose(" miter Limit: " << limitRight << " " << limitLeft << " <= " << _miterLimit); + if ( limitRight <= _miterLimit + && limitLeft <= _miterLimit) { + //Draw from previous point: + addSegment(leftPoint, left); + Log.verbose(" segment :" << leftPoint << " -> " << left); + addSegment(right, rightPoint); + Log.verbose(" segment :" << right << " -> " << rightPoint); + leftPoint = left; + rightPoint = right; + break; + } else { + // We do a bevel join ... + Log.verbose(" Find miter Limit ... ==> create BEVEL"); + } + } + case esvg::join_round: + case esvg::join_bevel: + { + vec2 axePrevious = (it.m_pos-it.m_posPrevious).safeNormalize(); + vec2 axeNext = (it.m_posNext - it.m_pos).safeNormalize(); + float cross = axePrevious.cross(axeNext); + if (cross > 0.0f) { + vec2 right = getIntersect(rightPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe); + vec2 left1 = it.m_pos + + it.m_orthoAxePrevious*_width*0.5f; + vec2 left2 = it.m_pos + + it.m_orthoAxeNext*_width*0.5f; + //Draw from previous point: + addSegment(leftPoint, left1); + Log.verbose(" segment :" << leftPoint << " -> " << left1); + if (_join != esvg::join_round) { + // Miter and bevel: + addSegment(left1, left2); + Log.verbose(" segment :" << left1 << " -> " << left2); + }else { + createSegmentListStroke(left1, + left2, + it.m_pos, + _width, + false); + } + addSegment(right, rightPoint); + Log.verbose(" segment :" << right << " -> " << rightPoint); + leftPoint = left2; + rightPoint = right; + } else { + vec2 left = getIntersect(leftPoint, it.m_pos-it.m_posPrevious, it.m_pos, it.m_miterAxe); + vec2 right1 = it.m_pos + - it.m_orthoAxePrevious*_width*0.5f; + vec2 right2 = it.m_pos + - it.m_orthoAxeNext*_width*0.5f;//Draw from previous point: + addSegment(leftPoint, left); + Log.verbose(" segment :" << leftPoint << " -> " << left); + addSegment(right1, rightPoint); + Log.verbose(" segment :" << right1 << " -> " << rightPoint); + if (_join != esvg::join_round) { + // Miter and bevel: + addSegment(right2, right1); + Log.verbose(" segment :" << right2 << " -> " << right1); + } else { + createSegmentListStroke(right1, + right2, + it.m_pos, + _width, + true); + } + leftPoint = left; + rightPoint = right2; + } + } + break; + } + break; + } + } + } +} + +void esvg::render::SegmentList::startStopPoint(vec2& _leftPoint, + vec2& _rightPoint, + const esvg::render::Point& _point, + enum esvg::cap _cap, + float _width, + bool _isStart) { + switch (_cap) { + case esvg::cap_butt: + { + vec2 left = _point.m_pos + + _point.m_miterAxe*_width*0.5f; + vec2 right = _point.m_pos + - _point.m_miterAxe*_width*0.5f; + if (_isStart == false) { + //Draw from previous point: + addSegment(_leftPoint, left); + Log.verbose(" segment :" << _leftPoint << " -> " << left); + addSegment(right, _rightPoint); + Log.verbose(" segment :" << right << " -> " << _rightPoint); + } + _leftPoint = left; + _rightPoint = right; + } + if (_isStart == false) { + addSegment(_leftPoint, _rightPoint); + Log.verbose(" segment :" << _leftPoint << " -> " << _rightPoint); + } else { + addSegment(_rightPoint, _leftPoint); + Log.verbose(" segment :" << _rightPoint << " -> " << _leftPoint); + } + break; + case esvg::cap_round: + { + if (_isStart == false) { + vec2 left = _point.m_pos + + _point.m_miterAxe*_width*0.5f; + vec2 right = _point.m_pos + - _point.m_miterAxe*_width*0.5f; + if (_isStart == false) { + //Draw from previous point: + addSegment(_leftPoint, left); + Log.verbose(" segment :" << _leftPoint << " -> " << left); + addSegment(right, _rightPoint); + Log.verbose(" segment :" << right << " -> " << _rightPoint); + } + _leftPoint = left; + _rightPoint = right; + } + int32_t nbDot = int32_t(_width); + if (nbDot <= 2) { + nbDot = 2; + } + float baseAngle = M_PI/float(nbDot); + float iii; + _leftPoint = _point.m_pos + + _point.m_miterAxe*_width*0.5f; + _rightPoint = _point.m_pos + - _point.m_miterAxe*_width*0.5f; + createSegmentListStroke(_leftPoint, + _rightPoint, + _point.m_pos, + _width, + _isStart); + } + break; + case esvg::cap_square: + { + vec2 nextAxe; + if (_isStart == true) { + nextAxe = _point.m_posNext - _point.m_pos; + } else { + nextAxe = _point.m_posPrevious - _point.m_pos; + } + vec2 left = _point.m_pos + + _point.m_miterAxe*_width*0.5f; + vec2 right = _point.m_pos + - _point.m_miterAxe*_width*0.5f; + mat2x3 tmpMat = etk::mat2x3Translate(nextAxe.safeNormalize()*_width*-0.5f); + left = tmpMat*left; + right = tmpMat*right; + if (_isStart == false) { + if (_isStart == false) { + //Draw from previous point: + addSegment(_leftPoint, left); + Log.verbose(" segment :" << _leftPoint << " -> " << left); + addSegment(right, _rightPoint); + Log.verbose(" segment :" << right << " -> " << _rightPoint); + } + } + _leftPoint = left; + _rightPoint = right; + if (_isStart == false) { + addSegment(_leftPoint, _rightPoint); + Log.verbose(" segment :" << _leftPoint << " -> " << _rightPoint); + } else { + addSegment(_rightPoint, _leftPoint); + Log.verbose(" segment :" << _rightPoint << " -> " << _leftPoint); + } + Log.verbose(" segment :" << _leftPoint << " -> " << _rightPoint); + } + break; + default: + Log.error(" Undefined CAP TYPE"); + break; + } +} + +void esvg::render::SegmentList::applyMatrix(const mat2x3& _transformationMatrix) { + for (auto &it : m_data) { + it.applyMatrix(_transformationMatrix); + } +} + diff --git a/src/org/atriasoft/esvg/render/SegmentList.java b/src/org/atriasoft/esvg/render/SegmentList.java new file mode 100644 index 0000000..e7e5d5d --- /dev/null +++ b/src/org/atriasoft/esvg/render/SegmentList.java @@ -0,0 +1,50 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace esvg { + namespace render { + class SegmentList { + public: + etk::Vector m_data; + public: + SegmentList(); + #ifdef DEBUG + void addSegment(const vec2& _pos0, const vec2& _pos1); + #endif + 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, + enum esvg::cap _cap, + enum esvg::join _join, + float _miterLimit); + private: + void startStopPoint(vec2& _leftPoint, + vec2& _rightPoint, + const esvg::render::Point& _point, + enum esvg::cap _cap, + float _width, + bool _isStart); + void createSegmentListStroke(const vec2& _point1, + const vec2& _point2, + const vec2& _center, + float _width, + bool _isStart); + public: + etk::Pair getViewPort(); + void applyMatrix(const mat2x3& _transformationMatrix); + }; + } +} diff --git a/src/org/atriasoft/esvg/render/Weight.cpp b/src/org/atriasoft/esvg/render/Weight.cpp new file mode 100644 index 0000000..12f3045 --- /dev/null +++ b/src/org/atriasoft/esvg/render/Weight.cpp @@ -0,0 +1,203 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include + +esvg::render::Weight::Weight() : + m_size(0,0) { + +} + +esvg::render::Weight::Weight(const ivec2& _size) : + m_size(_size) { + resize(_size); +} + +esvg::render::Weight::~Weight() { + +} + +void esvg::render::Weight::resize(const ivec2& _size) { + 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()) { + Log.warning("Wrong weigth buffer size ..."); + return; + } +} + +const ivec2& esvg::render::Weight::getSize() const { + return m_size; +} + +int32_t esvg::render::Weight::getWidth() const { + return m_size.x(); +} + +int32_t esvg::render::Weight::getHeight() const { + return m_size.y(); +} + +void esvg::render::Weight::clear(float _fill) { + for (int32_t iii=0; iii=0 && _pos.x()=0 && _pos.y()=0 && _pos.x()=0 && _pos.y()=0 + && _posY=0 + && _posY& _e1, const etk::Pair& _e2) { + return _e1.first < _e2.first; +} + + +void esvg::render::Weight::generate(ivec2 _size, int32_t _subSamplingCount, const esvg::render::SegmentList& _listSegment) { + resize(_size); + // for each lines: + for (int32_t yyy=0; yyy<_size.y(); ++yyy) { + Log.verbose("Weighting ... " << yyy << " / " << _size.y()); + // Reduce the number of lines in the subsampling parsing: + etk::Vector availlableSegmentPixel; + for (auto &it : _listSegment.m_data) { + if ( it.p0.y() < float(yyy+1) + && it.p1.y() > float(yyy)) { + availlableSegmentPixel.pushBack(it); + } + } + if (availlableSegmentPixel.size() == 0) { + continue; + } + Log.verbose(" Find Basic segments " << availlableSegmentPixel.size()); + // This represent the pondaration on the subSampling + float deltaSize = 1.0f/_subSamplingCount; + for (int32_t kkk=0; kkk<_subSamplingCount ; ++kkk) { + Log.verbose(" Scanline ... " << kkk << " / " << _subSamplingCount); + 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; + etk::Vector availlableSegment; + // find in the subList ... + for (auto &it : availlableSegmentPixel) { + if ( it.p0.y() <= subSamplingCenterPos + && it.p1.y() > subSamplingCenterPos) { + // check if we not get 2 identical lines: + if ( availlableSegment.size() > 0 + && availlableSegment.back().p1 == it.p0 + && availlableSegment.back().direction == it.direction) { + // we not add this point in this case to prevent double count of the same point. + } else { + availlableSegment.pushBack(it); + } + } + } + Log.verbose(" Availlable Segment " << availlableSegment.size()); + if (availlableSegment.size() == 0) { + continue; + } + for (auto &it : availlableSegment) { + Log.verbose(" Availlable Segment " << it.p0 << " -> " << it.p1 << " dir=" << it.direction); + } + // x position, angle + etk::Vector> 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.pushBack(etk::Pair(xpos, it.direction)); + } + Log.verbose(" List position " << listPosition.size()); + // now we order position of the xPosition: + etk::algorithm::quickSort(listPosition, sortXPosFunction); + // move through all element in the point: + int32_t lastState = 0; + 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: + #if __CPP_VERSION__ >= 2011 && !defined(__TARGET_OS__MacOs) && !defined(__TARGET_OS__IOs) + float endValue = float(etk::min(1,etk::abs(lastState))) * deltaSize; + #else + float endValue = float(etk::min(1,abs(lastState))) * deltaSize; + #endif + for (int32_t iii=currentPos+1; iii fill if to the end with full value ... 2.0 + if (lastState != 0) { + // just past the last state to the end of the image ... + Log.error("end of Path whith no end ... " << currentPos << " -> " << _size.x()); + for (int32_t xxx=currentPos; xxx<_size.x(); ++xxx) { + scanline.set(xxx, 100.0); + } + } + append(yyy, scanline); + } + } +} diff --git a/src/org/atriasoft/esvg/render/Weight.java b/src/org/atriasoft/esvg/render/Weight.java new file mode 100644 index 0000000..47472d1 --- /dev/null +++ b/src/org/atriasoft/esvg/render/Weight.java @@ -0,0 +1,42 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include +#include + +namespace esvg { + namespace render { + class Weight { + private: + ivec2 m_size; + etk::Vector m_data; + public: + // constructor : + Weight(); + Weight(const ivec2& _size); + // destructor + ~Weight(); + // ----------------------------------------------- + // -- basic tools : + // ----------------------------------------------- + public: + void resize(const ivec2& _size); + const ivec2& getSize() const; + int32_t getWidth() const; + int32_t getHeight() const; + void clear(float _fill); + float get(const ivec2& _pos) const; + void set(const ivec2& _pos, float _newColor); + void set(int32_t _posY, const esvg::render::Scanline& _data); + void append(int32_t _posY, const esvg::render::Scanline& _data); + void generate(ivec2 _size, int32_t _subSamplingCount, const esvg::render::SegmentList& _listSegment); + }; + } +} + diff --git a/src/org/atriasoft/esvg/spreadMethod.cpp b/src/org/atriasoft/esvg/spreadMethod.cpp new file mode 100644 index 0000000..bd97c1a --- /dev/null +++ b/src/org/atriasoft/esvg/spreadMethod.cpp @@ -0,0 +1,20 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include + +static const char* values[] = { + "pad", + "reflect", + "repeat" +}; + +etk::Stream& esvg::operator <<(etk::Stream& _os, enum esvg::spreadMethod _obj) { + _os << values[_obj]; + return _os; +} + diff --git a/src/org/atriasoft/esvg/spreadMethod.java b/src/org/atriasoft/esvg/spreadMethod.java new file mode 100644 index 0000000..45e61c8 --- /dev/null +++ b/src/org/atriasoft/esvg/spreadMethod.java @@ -0,0 +1,23 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include + +namespace esvg { + enum spreadMethod { + spreadMethod_pad, + spreadMethod_reflect, + spreadMethod_repeat + }; + /** + * @brief Debug operator To display the curent element in a Human redeable information + */ + etk::Stream& operator <<(etk::Stream& _os, enum esvg::spreadMethod _obj); +} + + diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/test/src/test/atriasoft/esvg/main.hpp b/test/src/test/atriasoft/esvg/main.hpp new file mode 100644 index 0000000..3621c93 --- /dev/null +++ b/test/src/test/atriasoft/esvg/main.hpp @@ -0,0 +1,15 @@ +/** @file + * @author Edouard DUPIN + * + * @copyright 2014, Edouard DUPIN, all right reserved + * + * @license MPL v2.0 (see license file) + */ + +#include +#include + + +#pragma once + +extern bool g_visualDebug; diff --git a/test/src/test/atriasoft/esvg/main.java b/test/src/test/atriasoft/esvg/main.java new file mode 100644 index 0000000..1af352c --- /dev/null +++ b/test/src/test/atriasoft/esvg/main.java @@ -0,0 +1,40 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2014, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include + +bool g_visualDebug = false; + + +int main(int _argc, const char *_argv[]) { + etest::init(_argc, _argv); + etk::init(_argc, _argv); + for (int32_t iii=0; iii<_argc ; ++iii) { + etk::String data = _argv[iii]; + #ifdef DEBUG + if (data == "--visual-test") { + TEST_PRINT("visual-test=enable"); + g_visualDebug = true; + } else + #endif + if ( data == "-h" + || data == "--help") { + TEST_PRINT("esvg-test - help : "); + TEST_PRINT(" " << _argv[0] << " [options]"); + #ifdef DEBUG + TEST_PRINT(" --visual-test Enable decoration in logged file in debug mode only"); + #else + TEST_PRINT(" No optiions ..."); + #endif + return -1; + } + } + //etk::initDefaultFolder("esvg-test"); + return RUN_ALL_TESTS(); +} diff --git a/test/src/test/atriasoft/esvg/testCap.java b/test/src/test/atriasoft/esvg/testCap.java new file mode 100644 index 0000000..01f9a50 --- /dev/null +++ b/test/src/test/atriasoft/esvg/testCap.java @@ -0,0 +1,158 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2014, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include "main.hpp" + +TEST(TestCap, butt) { + etk::String data("" + "" + " " + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCap_butt.svg"), data); + doc.generateAnImage(etk::Path("TestCap_butt.bmp"), g_visualDebug); +} + +TEST(TestCap, round) { + etk::String data("" + "" + " " + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCap_round.svg"), data); + doc.generateAnImage(etk::Path("TestCap_round.bmp"), g_visualDebug); +} + +TEST(TestCap, square) { + etk::String data("" + "" + " " + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCap_square.svg"), data); + doc.generateAnImage(etk::Path("TestCap_square.bmp"), g_visualDebug); +} + + +TEST(TestCap, buttVert) { + etk::String data("" + "" + " " + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCap_buttVert.svg"), data); + doc.generateAnImage(etk::Path("TestCap_buttVert.bmp"), g_visualDebug); +} + +TEST(TestCap, roundVert) { + etk::String data("" + "" + " " + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCap_roundVert.svg"), data); + doc.generateAnImage(etk::Path("TestCap_roundVert.bmp"), g_visualDebug); +} + +TEST(TestCap, squareVert) { + etk::String data("" + "" + " " + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCap_squareVert.svg"), data); + doc.generateAnImage(etk::Path("TestCap_squareVert.bmp"), g_visualDebug); +} + + + +TEST(TestCap, buttDiag1) { + etk::String data("" + "" + " " + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCap_buttDiag1.svg"), data); + doc.generateAnImage(etk::Path("TestCap_buttDiag1.bmp"), g_visualDebug); +} + +TEST(TestCap, roundDiag1) { + etk::String data("" + "" + " " + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCap_roundDiag1.svg"), data); + doc.generateAnImage(etk::Path("TestCap_roundDiag1.bmp"), g_visualDebug); +} + +TEST(TestCap, squareDiag1) { + etk::String data("" + "" + " " + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCap_squareDiag1.svg"), data); + doc.generateAnImage(etk::Path("TestCap_squareDiag1.bmp"), g_visualDebug); +} + + +TEST(TestCap, buttDiag2) { + etk::String data("" + "" + " " + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCap_buttDiag2.svg"), data); + doc.generateAnImage(etk::Path("TestCap_buttDiag2.bmp"), g_visualDebug); +} + +TEST(TestCap, roundDiag2) { + etk::String data("" + "" + " " + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCap_roundDiag2.svg"), data); + doc.generateAnImage(etk::Path("TestCap_roundDiag2.bmp"), g_visualDebug); +} + +TEST(TestCap, squareDiag2) { + etk::String data("" + "" + " " + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCap_squareDiag2.svg"), data); + doc.generateAnImage(etk::Path("TestCap_squareDiag2.bmp"), g_visualDebug); +} + diff --git a/test/src/test/atriasoft/esvg/testCircle.java b/test/src/test/atriasoft/esvg/testCircle.java new file mode 100644 index 0000000..ec0cb8d --- /dev/null +++ b/test/src/test/atriasoft/esvg/testCircle.java @@ -0,0 +1,42 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2014, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include "main.hpp" + +TEST(TestCircle, fill) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCircle_fill.svg"), data); + doc.generateAnImage(etk::Path("TestCircle_fill.bmp"), g_visualDebug); +} + +TEST(TestCircle, stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCircle_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestCircle_stroke.bmp"), g_visualDebug); +} + +TEST(TestCircle, fill_and_stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestCircle_fill_and_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestCircle_fill_and_stroke.bmp"), g_visualDebug); +} diff --git a/test/src/test/atriasoft/esvg/testColor.java b/test/src/test/atriasoft/esvg/testColor.java new file mode 100644 index 0000000..3074197 --- /dev/null +++ b/test/src/test/atriasoft/esvg/testColor.java @@ -0,0 +1,55 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2014, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include "main.hpp" + +TEST(TestColor, blending) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestColor_blending.svg"), data); + doc.generateAnImage(etk::Path("TestColor_blending.bmp"), g_visualDebug); +} + +TEST(TestColor, opacity) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestColor_opacity.svg"), data); + doc.generateAnImage(etk::Path("TestColor_opacity.bmp"), g_visualDebug); +} + +TEST(TestColor, blending_and_opacity) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestColor_blending_and_opacity.svg"), data); + doc.generateAnImage(etk::Path("TestColor_blending_and_opacity.bmp"), g_visualDebug); +} + +TEST(TestColor, multiple_layer) { + etk::String data("" + "" + " " + " " + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestColor_multiple_layer.svg"), data); + doc.generateAnImage(etk::Path("TestColor_multiple_layer.bmp"), g_visualDebug); +} diff --git a/test/src/test/atriasoft/esvg/testEllipse.java b/test/src/test/atriasoft/esvg/testEllipse.java new file mode 100644 index 0000000..8397455 --- /dev/null +++ b/test/src/test/atriasoft/esvg/testEllipse.java @@ -0,0 +1,43 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2014, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include "main.hpp" + +TEST(TestEllipse, fill) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestEllipse_fill.svg"), data); + doc.generateAnImage(etk::Path("TestEllipse_fill.bmp"), g_visualDebug); +} + +TEST(TestEllipse, stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestEllipse_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestEllipse_stroke.bmp"), g_visualDebug); +} + +TEST(TestEllipse, fill_and_stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestEllipse_fill_and_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestEllipse_fill_and_stroke.bmp"), g_visualDebug); +} + diff --git a/test/src/test/atriasoft/esvg/testGradientLinear.java b/test/src/test/atriasoft/esvg/testGradientLinear.java new file mode 100644 index 0000000..fe8f5fa --- /dev/null +++ b/test/src/test/atriasoft/esvg/testGradientLinear.java @@ -0,0 +1,343 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2014, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include "main.hpp" + +TEST(TestGradientLinear, horizontal) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_horizontal.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_horizontal.bmp"), g_visualDebug); +} + + +TEST(TestGradientLinear, vertical) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_vertical.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_vertical.bmp"), g_visualDebug); +} + +TEST(TestGradientLinear, diag1) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_diag1.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_diag1.bmp"), g_visualDebug); +} + +TEST(TestGradientLinear, diag1Partiel) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_diag1Partiel.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_diag1Partiel.bmp"), g_visualDebug); +} + +TEST(TestGradientLinear, diag2) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_diag2.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_diag2.bmp"), g_visualDebug); +} + + + +TEST(TestGradientLinear, diag2Rotate0) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_diag2Rotate0.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_diag2Rotate0.bmp"), g_visualDebug); +} + +TEST(TestGradientLinear, diag2Rotate1) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_diag2Rotate1.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_diag2Rotate1.bmp"), g_visualDebug); +} + +TEST(TestGradientLinear, diag2Rotate2) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_diag2Rotate2.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_diag2Rotate2.bmp"), g_visualDebug); +} + +TEST(TestGradientLinear, diag2scale) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_diag2scale.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_diag2scale.bmp"), g_visualDebug); +} + +TEST(TestGradientLinear, internalHref) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_internalHref.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_internalHref.bmp"), g_visualDebug); +} + + +TEST(TestGradientLinear, unitBox_spreadNone) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_unitBox_spreadNone.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_unitBox_spreadNone.bmp"), g_visualDebug); +} + +TEST(TestGradientLinear, unitBox_spreadPad) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_unitBox_spreadPad.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_unitBox_spreadPad.bmp"), g_visualDebug); +} + + +TEST(TestGradientLinear, unitBox_spreadReflect) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_unitBox_spreadReflect.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_unitBox_spreadReflect.bmp"), g_visualDebug); +} + + +TEST(TestGradientLinear, unitBox_spreadRepeat) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_unitBox_spreadRepeat.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_unitBox_spreadRepeat.bmp"), g_visualDebug); +} + +TEST(TestGradientLinear, unitUser_spreadNone) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_unitUser_spreadNone.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_unitUser_spreadNone.bmp"), g_visualDebug); +} + +TEST(TestGradientLinear, unitUser_spreadPad) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_unitUser_spreadPad.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_unitUser_spreadPad.bmp"), g_visualDebug); +} + + +TEST(TestGradientLinear, unitUser_spreadReflect) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_unitUser_spreadReflect.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_unitUser_spreadReflect.bmp"), g_visualDebug); +} + + +TEST(TestGradientLinear, unitUser_spreadRepeate) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientLinear_unitUser_spreadRepeate.svg"), data); + doc.generateAnImage(etk::Path("TestGradientLinear_unitUser_spreadRepeate.bmp"), g_visualDebug); +} + diff --git a/test/src/test/atriasoft/esvg/testGradientRadial.java b/test/src/test/atriasoft/esvg/testGradientRadial.java new file mode 100644 index 0000000..ae8693b --- /dev/null +++ b/test/src/test/atriasoft/esvg/testGradientRadial.java @@ -0,0 +1,319 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2014, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include "main.hpp" + +TEST(TestGradientRadial, circle) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_circle.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_circle.bmp"), g_visualDebug); +} + + +TEST(TestGradientRadial, full) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_full.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_full.bmp"), g_visualDebug); +} + + +TEST(TestGradientRadial, partial) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_partial.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_partial.bmp"), g_visualDebug); +} + +TEST(TestGradientRadial, unitBox_spreadNone) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_unitBox_spreadNone.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_unitBox_spreadNone.bmp"), g_visualDebug); +} + +TEST(TestGradientRadial, unitBox_spreadPad) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_unitBox_spreadPad.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_unitBox_spreadPad.bmp"), g_visualDebug); +} + +TEST(TestGradientRadial, unitBox_spreadReflect) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_unitBox_spreadReflect.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_unitBox_spreadReflect.bmp"), g_visualDebug); +} + + +TEST(TestGradientRadial, unitBox_spreadRepeat) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_unitBox_spreadRepeat.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_unitBox_spreadRepeat.bmp"), g_visualDebug); +} + + +TEST(TestGradientRadial, unitUser_spreadNone) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_unitUser_spreadNone.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_unitUser_spreadNone.bmp"), g_visualDebug); +} + +TEST(TestGradientRadial, unitUser_spreadPad) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_unitUser_spreadPad.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_unitUser_spreadPad.bmp"), g_visualDebug); +} + +TEST(TestGradientRadial, unitUser_spreadReflect) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_unitUser_spreadReflect.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_unitUser_spreadReflect.bmp"), g_visualDebug); +} + + +TEST(TestGradientRadial, unitUser_spreadRepeat) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_unitUser_spreadRepeat.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_unitUser_spreadRepeat.bmp"), g_visualDebug); +} + +TEST(TestGradientRadial, unitUser_spreadPad_unCenter) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_unitUser_spreadPad_unCenter.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_unitUser_spreadPad_unCenter.bmp"), g_visualDebug); +} + +TEST(TestGradientRadial, unitUser_spreadReflect_unCenter) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_unitUser_spreadReflect_unCenter.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_unitUser_spreadReflect_unCenter.bmp"), g_visualDebug); +} + +TEST(TestGradientRadial, unitUser_spreadRepeat_unCenter) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_unitUser_spreadRepeat_unCenter.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_unitUser_spreadRepeat_unCenter.bmp"), g_visualDebug); +} + +TEST(TestGradientRadial, unitUser_spreadRepeat_unCenter2) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_unitUser_spreadRepeat_unCenter2.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_unitUser_spreadRepeat_unCenter2.bmp"), g_visualDebug); +} + + +TEST(TestGradientRadial, unitUser_spreadRepeat_out) { + etk::String data("\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestGradientRadial_unitUser_spreadRepeat_out.svg"), data); + doc.generateAnImage(etk::Path("TestGradientRadial_unitUser_spreadRepeat_out.bmp"), g_visualDebug); +} \ No newline at end of file diff --git a/test/src/test/atriasoft/esvg/testGroup.java b/test/src/test/atriasoft/esvg/testGroup.java new file mode 100644 index 0000000..e69de29 diff --git a/test/src/test/atriasoft/esvg/testJoin.java b/test/src/test/atriasoft/esvg/testJoin.java new file mode 100644 index 0000000..71fc632 --- /dev/null +++ b/test/src/test/atriasoft/esvg/testJoin.java @@ -0,0 +1,376 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2014, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include "main.hpp" + +// ------------------------------------------------------ Miter test ----------------------------------------------------- + +TEST(TestJoin, miterRight1) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterRight1.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterRight1.bmp"), g_visualDebug); +} + +TEST(TestJoin, miterRight2) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterRight2.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterRight2.bmp"), g_visualDebug); +} + +TEST(TestJoin, miterRight3) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterRight3.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterRight3.bmp"), g_visualDebug); +} + +TEST(TestJoin, miterRight4) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterRight4.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterRight4.bmp"), g_visualDebug); +} + +TEST(TestJoin, miterLeft1) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterLeft1.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterLeft1.bmp"), g_visualDebug); +} + +TEST(TestJoin, miterLeft2) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterLeft2.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterLeft2.bmp"), g_visualDebug); +} + +TEST(TestJoin, miterLeft3) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterLeft3.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterLeft3.bmp"), g_visualDebug); +} + +TEST(TestJoin, miterLeft4) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterLeft4.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterLeft4.bmp"), g_visualDebug); +} + +TEST(TestJoin, miterLimit1) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterLimit1.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterLimit1.bmp"), g_visualDebug); +} + +TEST(TestJoin, miterLimit2) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterLimit2.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterLimit2.bmp"), g_visualDebug); +} + +TEST(TestJoin, miterLimit3) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterLimit3.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterLimit3.bmp"), g_visualDebug); +} + +TEST(TestJoin, miterLimit4) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterLimit4.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterLimit4.bmp"), g_visualDebug); +} + +TEST(TestJoin, miterCornerCasePath) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterCornerCasePath.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterCornerCasePath.bmp"), g_visualDebug); +} + +TEST(TestJoin, miterCornerCasePathLimit) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_miterCornerCasePathLimit.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_miterCornerCasePathLimit.bmp"), g_visualDebug); +} + +// ------------------------------------------------------ Round test ----------------------------------------------------- + +TEST(TestJoin, roundRight1) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_roundRight1.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_roundRight1.bmp"), g_visualDebug); +} + +TEST(TestJoin, roundRight2) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_roundRight2.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_roundRight2.bmp"), g_visualDebug); +} + +TEST(TestJoin, roundRight3) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_roundRight3.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_roundRight3.bmp"), g_visualDebug); +} + +TEST(TestJoin, roundRight4) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_roundRight4.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_roundRight4.bmp"), g_visualDebug); +} + +TEST(TestJoin, roundLeft1) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_roundLeft1.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_roundLeft1.bmp"), g_visualDebug); +} + +TEST(TestJoin, roundLeft2) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_roundLeft2.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_roundLeft2.bmp"), g_visualDebug); +} + +TEST(TestJoin, roundLeft3) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_roundLeft3.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_roundLeft3.bmp"), g_visualDebug); +} + +TEST(TestJoin, roundLeft4) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_roundLeft4.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_roundLeft4.bmp"), g_visualDebug); +} + +TEST(TestJoin, roundCornerCasePath) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_roundCornerCasePath.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_roundCornerCasePath.bmp"), g_visualDebug); +} + + +// ------------------------------------------------------ Bevel test ----------------------------------------------------- + +TEST(TestJoin, bevelRight1) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_bevelRight1.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_bevelRight1.bmp"), g_visualDebug); +} + +TEST(TestJoin, bevelRight2) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_bevelRight2.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_bevelRight2.bmp"), g_visualDebug); +} + +TEST(TestJoin, bevelRight3) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_bevelRight3.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_bevelRight3.bmp"), g_visualDebug); +} + +TEST(TestJoin, bevelRight4) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_bevelRight4.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_bevelRight4.bmp"), g_visualDebug); +} + +TEST(TestJoin, bevelLeft1) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_bevelLeft1.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_bevelLeft1.bmp"), g_visualDebug); +} + +TEST(TestJoin, bevelLeft2) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_bevelLeft2.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_bevelLeft2.bmp"), g_visualDebug); +} + +TEST(TestJoin, bevelLeft3) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_bevelLeft3.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_bevelLeft3.bmp"), g_visualDebug); +} + +TEST(TestJoin, bevelLeft4) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_bevelLeft4.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_bevelLeft4.bmp"), g_visualDebug); +} + +TEST(TestJoin, bevelCornerCasePath) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestJoin_bevelCornerCasePath.svg"), data); + doc.generateAnImage(etk::Path("TestJoin_bevelCornerCasePath.bmp"), g_visualDebug); +} diff --git a/test/src/test/atriasoft/esvg/testLine.java b/test/src/test/atriasoft/esvg/testLine.java new file mode 100644 index 0000000..b305df0 --- /dev/null +++ b/test/src/test/atriasoft/esvg/testLine.java @@ -0,0 +1,22 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2014, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include "main.hpp" + +TEST(TestLine, stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestLine_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestLine_stroke.bmp"), g_visualDebug); +} + + diff --git a/test/src/test/atriasoft/esvg/testParsingFile.java b/test/src/test/atriasoft/esvg/testParsingFile.java new file mode 100644 index 0000000..e69de29 diff --git a/test/src/test/atriasoft/esvg/testPath.java b/test/src/test/atriasoft/esvg/testPath.java new file mode 100644 index 0000000..91e3c49 --- /dev/null +++ b/test/src/test/atriasoft/esvg/testPath.java @@ -0,0 +1,137 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2014, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include "main.hpp" + +TEST(TestPath, fill) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPath_fill.svg"), data); + doc.generateAnImage(etk::Path("TestPath_fill.bmp"), g_visualDebug); +} + +TEST(TestPath, stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPath_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestPath_stroke.bmp"), g_visualDebug); +} + +TEST(TestPath, fill_and_stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPath_fill_and_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestPath_fill_and_stroke.bmp"), g_visualDebug); +} + +TEST(TestPath, curveTo) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPath_curveTo.svg"), data); + doc.generateAnImage(etk::Path("TestPath_curveTo.bmp"), g_visualDebug); +} + + + +TEST(TestPath, smoothCurveTo) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPath_smoothCurveTo.svg"), data); + doc.generateAnImage(etk::Path("TestPath_smoothCurveTo.bmp"), g_visualDebug); +} + + + +TEST(TestPath, bezierCurveTo) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPath_bezierCurveTo.svg"), data); + doc.generateAnImage(etk::Path("TestPath_bezierCurveTo.bmp"), g_visualDebug); +} + + + +TEST(TestPath, bezierSmoothCurveTo) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPath_bezierSmoothCurveTo.svg"), data); + doc.generateAnImage(etk::Path("TestPath_bezierSmoothCurveTo.bmp"), g_visualDebug); +} + +TEST(TestPath, arc) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPath_arc.svg"), data); + doc.generateAnImage(etk::Path("TestPath_arc.bmp"), g_visualDebug); +} + + + + +TEST(TestPath, end_path_border_case) { + etk::String data("" + "" + " \n" + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPath_end_path_border_case.svg"), data); + doc.generateAnImage(etk::Path("TestPath_end_path_border_case.bmp"), g_visualDebug); +} \ No newline at end of file diff --git a/test/src/test/atriasoft/esvg/testPolygon.java b/test/src/test/atriasoft/esvg/testPolygon.java new file mode 100644 index 0000000..a5f5045 --- /dev/null +++ b/test/src/test/atriasoft/esvg/testPolygon.java @@ -0,0 +1,42 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2014, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include "main.hpp" + +TEST(TestPolygon, fill) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPolygon_fill.svg"), data); + doc.generateAnImage(etk::Path("TestPolygon_fill.bmp"), g_visualDebug); +} + +TEST(TestPolygon, stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPolygon_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestPolygon_stroke.bmp"), g_visualDebug); +} + +TEST(TestPolygon, fill_and_stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPolygon_fill_and_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestPolygon_fill_and_stroke.bmp"), g_visualDebug); +} \ No newline at end of file diff --git a/test/src/test/atriasoft/esvg/testPolyline.java b/test/src/test/atriasoft/esvg/testPolyline.java new file mode 100644 index 0000000..15783f9 --- /dev/null +++ b/test/src/test/atriasoft/esvg/testPolyline.java @@ -0,0 +1,46 @@ +/** @file + * @author Edouard DUPIN + * + * @copyright 2014, Edouard DUPIN, all right reserved + * + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include "main.hpp" + +TEST(TestPolyLine, fill) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPolyLine_fill.svg"), data); + doc.generateAnImage(etk::Path("TestPolyLine_fill.bmp"), g_visualDebug); +} + +TEST(TestPolyLine, stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPolyLine_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestPolyLine_stroke.bmp"), g_visualDebug); +} + +TEST(TestPolyLine, fill_and_stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestPolyLine_fill_and_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestPolyLine_fill_and_stroke.bmp"), g_visualDebug); +} + + diff --git a/test/src/test/atriasoft/esvg/testRectangle.java b/test/src/test/atriasoft/esvg/testRectangle.java new file mode 100644 index 0000000..2761005 --- /dev/null +++ b/test/src/test/atriasoft/esvg/testRectangle.java @@ -0,0 +1,75 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2014, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include "main.hpp" + +TEST(TestRectangle, fill) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestRectangle_fill.svg"), data); + doc.generateAnImage(etk::Path("TestRectangle_fill.bmp"), g_visualDebug); +} + +TEST(TestRectangle, stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestRectangle_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestRectangle_stroke.bmp"), g_visualDebug); +} + +TEST(TestRectangle, fill_and_stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestRectangle_fill_and_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestRectangle_fill_and_stroke.bmp"), g_visualDebug); +} + +TEST(TestRectangle, corned_fill) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestRectangle_corned_fill.svg"), data); + doc.generateAnImage(etk::Path("TestRectangle_corned_fill.bmp"), g_visualDebug); +} + +TEST(TestRectangle, corned_stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestRectangle_corned_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestRectangle_corned_stroke.bmp"), g_visualDebug); +} + +TEST(TestRectangle, corned_fill_and_stroke) { + etk::String data("" + "" + " " + ""); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestRectangle_corned_fill_and_stroke.svg"), data); + doc.generateAnImage(etk::Path("TestRectangle_corned_fill_and_stroke.bmp"), g_visualDebug); +} diff --git a/test/src/test/atriasoft/esvg/testStyle.java b/test/src/test/atriasoft/esvg/testStyle.java new file mode 100644 index 0000000..6167e89 --- /dev/null +++ b/test/src/test/atriasoft/esvg/testStyle.java @@ -0,0 +1,175 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2014, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include "main.hpp" + +TEST(TestExtern, worddown) { + etk::String data("\n" +"\n" +"\n" +"\n" +" \n" +" \n" +" \n" +" image/svg+xml\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +/* +" \n" +*/ +" \n" +/* +" \n" +" \n" +*/ +" \n" +" \n" +/* +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +*/ +"\n"); + esvg::Document doc; + doc.parse(data); + etk::uri::writeAll(etk::Path("TestExtern_worddown.svg"), data); + doc.generateAnImage(etk::Path("TestExtern_worddown.bmp"), g_visualDebug); +} + + diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..6c6aa7c --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +0.1.0 \ No newline at end of file