svgio/inc/svgfecompositenode.hxx              |    9 +++
 svgio/inc/svgtoken.hxx                        |    1 
 svgio/qa/cppunit/SvgImportTest.cxx            |   28 +++++-----
 svgio/qa/cppunit/data/filterFeComposite.svg   |   44 ++++++++++++++--
 svgio/source/svgreader/svgfecompositenode.cxx |   69 ++++++++++++++++++++++++--
 svgio/source/svgreader/svgtoken.cxx           |    1 
 6 files changed, 129 insertions(+), 23 deletions(-)

New commits:
commit b240f6198fae7221b7cd3e4678403ad99827b53d
Author:     Xisco Fauli <xiscofa...@libreoffice.org>
AuthorDate: Thu Mar 14 23:53:49 2024 +0100
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Fri Mar 15 01:06:22 2024 +0100

    tdf#48062: Add support for xor, in and out operators in feComposite
    
    atop and arithmetic are still missing
    
    Change-Id: I9b5bfeaa87b48071708ca4cb082916ea5f260adb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164852
    Tested-by: Jenkins
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/svgio/inc/svgfecompositenode.hxx b/svgio/inc/svgfecompositenode.hxx
index 44cef845c07f..2fa321cc6700 100644
--- a/svgio/inc/svgfecompositenode.hxx
+++ b/svgio/inc/svgfecompositenode.hxx
@@ -24,12 +24,21 @@
 
 namespace svgio::svgreader
 {
+enum class Operator
+{
+    Over,
+    In,
+    Out,
+    Xor,
+};
+
 class SvgFeCompositeNode : public SvgFilterNode
 {
 private:
     OUString maIn;
     OUString maIn2;
     OUString maResult;
+    Operator maOperator;
 
 public:
     SvgFeCompositeNode(SvgDocument& rDocument, SvgNode* pParent);
diff --git a/svgio/inc/svgtoken.hxx b/svgio/inc/svgtoken.hxx
index aa2d2a74e6c3..203e7f0996c0 100644
--- a/svgio/inc/svgtoken.hxx
+++ b/svgio/inc/svgtoken.hxx
@@ -95,6 +95,7 @@ namespace svgio::svgreader
             Filter,
             FloodColor,
             FloodOpacity,
+            Operator,
             Mask,
             ClipPathUnits,
             MaskUnits,
diff --git a/svgio/qa/cppunit/SvgImportTest.cxx 
b/svgio/qa/cppunit/SvgImportTest.cxx
index 1d875e2b74a7..dc933c9693dc 100644
--- a/svgio/qa/cppunit/SvgImportTest.cxx
+++ b/svgio/qa/cppunit/SvgImportTest.cxx
@@ -235,20 +235,20 @@ CPPUNIT_TEST_FIXTURE(Test, testFilterFeComposite)
 
     CPPUNIT_ASSERT (pDocument);
 
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]"_ostr, "color"_ostr, 
"#ff0000");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]/polypolygon"_ostr, 
"height"_ostr, "100");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]/polypolygon"_ostr, 
"width"_ostr, "100");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]/polypolygon"_ostr, 
"minx"_ostr, "70");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]/polypolygon"_ostr, 
"miny"_ostr, "70");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]/polypolygon"_ostr, 
"maxx"_ostr, "170");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]/polypolygon"_ostr, 
"maxy"_ostr, "170");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]"_ostr, "color"_ostr, 
"#6ab150");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]/polypolygon"_ostr, 
"height"_ostr, "100");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]/polypolygon"_ostr, 
"width"_ostr, "100");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]/polypolygon"_ostr, 
"minx"_ostr, "30");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]/polypolygon"_ostr, 
"miny"_ostr, "30");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]/polypolygon"_ostr, 
"maxx"_ostr, "130");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]/polypolygon"_ostr, 
"maxy"_ostr, "130");
+    assertXPath(pDocument, "/primitive2D/transform/mask"_ostr, 5);
+    // over operator
+    assertXPath(pDocument, 
"/primitive2D/transform/mask[1]/polypolygoncolor"_ostr, 3);
+    assertXPath(pDocument, 
"/primitive2D/transform/mask[1]/polypolygon/polygon/point"_ostr, 8);
+    // xor operator
+    assertXPath(pDocument, 
"/primitive2D/transform/mask[2]/polypolygoncolor"_ostr, 3);
+    assertXPath(pDocument, 
"/primitive2D/transform/mask[2]/polypolygon/polygon[1]/point"_ostr, 8);
+    assertXPath(pDocument, 
"/primitive2D/transform/mask[2]/polypolygon/polygon[2]/point"_ostr, 4);
+    // in operator
+    assertXPath(pDocument, 
"/primitive2D/transform/mask[3]/polypolygoncolor"_ostr, 3);
+    assertXPath(pDocument, 
"/primitive2D/transform/mask[3]/polypolygon/polygon/point"_ostr, 4);
+    // out operator
+    assertXPath(pDocument, 
"/primitive2D/transform/mask[4]/polypolygoncolor"_ostr, 3);
+    assertXPath(pDocument, 
"/primitive2D/transform/mask[4]/polypolygon/polygon/point"_ostr, 6);
 }
 
 CPPUNIT_TEST_FIXTURE(Test, testFilterFeGaussianBlur)
diff --git a/svgio/qa/cppunit/data/filterFeComposite.svg 
b/svgio/qa/cppunit/data/filterFeComposite.svg
index 0662eaa6f547..eea28d04647f 100644
--- a/svgio/qa/cppunit/data/filterFeComposite.svg
+++ b/svgio/qa/cppunit/data/filterFeComposite.svg
@@ -1,10 +1,44 @@
-<svg width="100" height="120" viewBox="0 0 200 240" 
xmlns="http://www.w3.org/2000/svg";>
+<svg width="100%" height="100%" viewBox="0 0 150 150" 
xmlns="http://www.w3.org/2000/svg";>
   <defs>
-  <filter id="filter" filterUnits="userSpaceOnUse" x="0" y="0" width="100%" 
height="100%">
-    <feFlood x="30" y="30" width="100" height="100" flood-color="#6ab150" 
flood-opacity="1" result="img1"></feFlood>
-    <feFlood x="70" y="70" width="100" height="100" flood-color="#ff0000" 
flood-opacity="1" result="img2"></feFlood>
+  <filter id="over" filterUnits="userSpaceOnUse">
+    <feFlood x="3" y="3" width="10" height="10" flood-color="#6ab150" 
flood-opacity="1" result="img1"></feFlood>
+    <feFlood x="7" y="7" width="10" height="10" flood-color="#ff0000" 
flood-opacity="1" result="img2"></feFlood>
     <feComposite in="img1" in2="img2" operator="over"></feComposite>
   </filter>
+  <filter id="xor" filterUnits="userSpaceOnUse">
+    <feFlood x="3" y="23" width="10" height="10" flood-color="#6ab150" 
flood-opacity="1" result="img1"></feFlood>
+    <feFlood x="7" y="27" width="10" height="10" flood-color="#ff0000" 
flood-opacity="1" result="img2"></feFlood>
+    <feComposite in="img1" in2="img2" operator="xor"></feComposite>
+  </filter>
+  <filter id="in" filterUnits="userSpaceOnUse">
+    <feFlood x="3" y="43" width="10" height="10" flood-color="#6ab150" 
flood-opacity="1" result="img1"></feFlood>
+    <feFlood x="7" y="47" width="10" height="10" flood-color="#ff0000" 
flood-opacity="1" result="img2"></feFlood>
+    <feComposite in="img1" in2="img2" operator="in"></feComposite>
+  </filter>
+  <filter id="out" filterUnits="userSpaceOnUse">
+    <feFlood x="3" y="63" width="10" height="10" flood-color="#6ab150" 
flood-opacity="1" result="img1"></feFlood>
+    <feFlood x="7" y="67" width="10" height="10" flood-color="#ff0000" 
flood-opacity="1" result="img2"></feFlood>
+    <feComposite in="img1" in2="img2" operator="out"></feComposite>
+  </filter>
+  <filter id="atop" filterUnits="userSpaceOnUse">
+    <feFlood x="3" y="83" width="10" height="10" flood-color="#6ab150" 
flood-opacity="1" result="img1"></feFlood>
+    <feFlood x="7" y="87" width="10" height="10" flood-color="#ff0000" 
flood-opacity="1" result="img2"></feFlood>
+    <feComposite in="img1" in2="img2" operator="atop"></feComposite>
+  </filter>
   </defs>
-  <use style="filter: url(#filter)"></use>
+
+  <use style="filter: url(#over)"></use>
+  <text x="20" y="10" dominant-baseline="middle" 
style="font-size:5px;">operator="over"</text>
+
+  <use style="filter: url(#xor)"></use>
+  <text x="20" y="30" dominant-baseline="middle" 
style="font-size:5px;">operator="xor"</text>
+
+  <use style="filter: url(#in)"></use>
+  <text x="20" y="50" dominant-baseline="middle" 
style="font-size:5px;">operator="in"</text>
+
+  <use style="filter: url(#out)"></use>
+  <text x="20" y="70" dominant-baseline="middle" 
style="font-size:5px;">operator="out"</text>
+
+  <use style="filter: url(#atop)"></use>
+  <text x="20" y="90" dominant-baseline="middle" 
style="font-size:5px;">operator="atop"</text>
 </svg>
diff --git a/svgio/source/svgreader/svgfecompositenode.cxx 
b/svgio/source/svgreader/svgfecompositenode.cxx
index b3418fca8709..95ec021969d8 100644
--- a/svgio/source/svgreader/svgfecompositenode.cxx
+++ b/svgio/source/svgreader/svgfecompositenode.cxx
@@ -18,11 +18,16 @@
 
 #include <svgfecompositenode.hxx>
 #include <o3tl/string_view.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
 
 namespace svgio::svgreader
 {
 SvgFeCompositeNode::SvgFeCompositeNode(SvgDocument& rDocument, SvgNode* 
pParent)
     : SvgFilterNode(SVGToken::FeComposite, rDocument, pParent)
+    , maOperator(Operator::Over)
 {
 }
 
@@ -53,6 +58,29 @@ void SvgFeCompositeNode::parseAttribute(SVGToken aSVGToken, 
const OUString& aCon
             maResult = aContent.trim();
             break;
         }
+        case SVGToken::Operator:
+        {
+            if (!aContent.isEmpty())
+            {
+                if (o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"over"))
+                {
+                    maOperator = Operator::Over;
+                }
+                else if (o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), 
u"in"))
+                {
+                    maOperator = Operator::In;
+                }
+                else if (o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), 
u"out"))
+                {
+                    maOperator = Operator::Out;
+                }
+                else if (o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), 
u"xor"))
+                {
+                    maOperator = Operator::Xor;
+                }
+            }
+            break;
+        }
         default:
         {
             break;
@@ -63,18 +91,51 @@ void SvgFeCompositeNode::parseAttribute(SVGToken aSVGToken, 
const OUString& aCon
 void 
SvgFeCompositeNode::apply(drawinglayer::primitive2d::Primitive2DContainer& 
rTarget,
                                const SvgFilterNode* pParent) const
 {
-    if (const drawinglayer::primitive2d::Primitive2DContainer* rSource2
+    basegfx::B2DPolyPolygon aPolyPolygon, aPolyPolygon2;
+
+    // Process maIn2 first
+    if (const drawinglayer::primitive2d::Primitive2DContainer* pSource2
         = pParent->findGraphicSource(maIn2))
     {
-        rTarget = *rSource2;
+        rTarget.append(*pSource2);
+        const basegfx::B2DRange aRange2(
+            
pSource2->getB2DRange(drawinglayer::geometry::ViewInformation2D()));
+
+        aPolyPolygon2 = 
basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aRange2));
     }
 
-    if (const drawinglayer::primitive2d::Primitive2DContainer* rSource
+    if (const drawinglayer::primitive2d::Primitive2DContainer* pSource
         = pParent->findGraphicSource(maIn))
     {
-        rTarget.append(*rSource);
+        rTarget.append(*pSource);
+        const basegfx::B2DRange aRange(
+            pSource->getB2DRange(drawinglayer::geometry::ViewInformation2D()));
+
+        aPolyPolygon = 
basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aRange));
+    }
+
+    basegfx::B2DPolyPolygon aResult;
+    if (maOperator == Operator::Over)
+    {
+        aResult = basegfx::utils::solvePolygonOperationOr(aPolyPolygon, 
aPolyPolygon2);
+    }
+    else if (maOperator == Operator::Out)
+    {
+        aResult = basegfx::utils::solvePolygonOperationDiff(aPolyPolygon, 
aPolyPolygon2);
+    }
+    else if (maOperator == Operator::In)
+    {
+        aResult = basegfx::utils::solvePolygonOperationAnd(aPolyPolygon, 
aPolyPolygon2);
+    }
+    else if (maOperator == Operator::Xor)
+    {
+        aResult = basegfx::utils::solvePolygonOperationXor(aPolyPolygon, 
aPolyPolygon2);
     }
 
+    rTarget = drawinglayer::primitive2d::Primitive2DContainer{
+        new drawinglayer::primitive2d::MaskPrimitive2D(std::move(aResult), 
std::move(rTarget))
+    };
+
     pParent->addGraphicSourceToMapper(maResult, rTarget);
 }
 
diff --git a/svgio/source/svgreader/svgtoken.cxx 
b/svgio/source/svgreader/svgtoken.cxx
index 04b3f9fa3ec6..b6e22b63c547 100644
--- a/svgio/source/svgreader/svgtoken.cxx
+++ b/svgio/source/svgreader/svgtoken.cxx
@@ -93,6 +93,7 @@ constexpr auto aSVGTokenMap = 
frozen::make_unordered_map<std::u16string_view, SV
     { u"filter", SVGToken::Filter },
     { u"flood-color", SVGToken::FloodColor },
     { u"flood-opacity", SVGToken::FloodOpacity },
+    { u"operator", SVGToken::Operator },
     { u"mask", SVGToken::Mask },
     { u"clipPathUnits", SVGToken::ClipPathUnits },
     { u"maskUnits", SVGToken::MaskUnits },

Reply via email to