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 <xiscofauli@libreoffice.org>
diff --git a/svgio/inc/svgfecompositenode.hxx b/svgio/inc/svgfecompositenode.hxx
index 44cef84..2fa321c 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 aa2d2a74..203e7f0 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 1d875e2b..dc933c9 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 0662eaa..eea28d0 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 b3418fc..95ec021 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 04b3f9f..b6e22b6 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 },