tdf#128150 Implement/add SlideBackgroundFill visualization

React/interpret new SlideBackgroundFill mode for SdrObjects/
XShapes, defined by drawing::FillStyle_NONE and usage of
XFillUseSlideBackgroundItem as (true).

Do so by adding a detection, a mode to SdrFillAttribute,
reacting on it by creating a new implemented  B2DPrimitive:

SlideBackgroundFillPrimitive2D provides a Primitive2D for
the SlideBackgroundFill mode. It is capable of expressing
that state of fill and it's decomposition implements all
needed preparation/creation/collecting of the geometry
in an isolated and controllable space and way - the
B2DPrimitive way. It does so based on the given
ViewInformation2D, reacing out for all needed data/
geometry from this.
It clips & transforms/combines as needed, providing the
necessary visualization of the MasterPage content *over*
other objects on the Page/Slide. It is currently simple
buffered (due to being derived from
BufferedDecompositionPrimitive2D) and detects if
MasterPage changes. Still, more may be needed.

Change-Id: Ie11403c0b705fc2ecdfd7eb2fa5ae1a93a21b460
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135971
Tested-by: Jenkins
Reviewed-by: Samuel Mehrbrodt <samuel.mehrbrodt@allotropia.de>
diff --git a/drawinglayer/source/attribute/sdrfillattribute.cxx b/drawinglayer/source/attribute/sdrfillattribute.cxx
index 1a4388e..8cee8f9 100644
--- a/drawinglayer/source/attribute/sdrfillattribute.cxx
+++ b/drawinglayer/source/attribute/sdrfillattribute.cxx
@@ -82,6 +82,11 @@ namespace drawinglayer::attribute
                static SdrFillAttribute::ImplType SINGLETON;
                return SINGLETON;
            }
            SdrFillAttribute::ImplType& slideBackgroundFillGlobalDefault()
            {
                static SdrFillAttribute::ImplType SINGLETON2;
                return SINGLETON2;
            }
        }

        SdrFillAttribute::SdrFillAttribute(
@@ -95,8 +100,10 @@ namespace drawinglayer::attribute
        {
        }

        SdrFillAttribute::SdrFillAttribute()
        :   mpSdrFillAttribute(theGlobalDefault())
        SdrFillAttribute::SdrFillAttribute(bool bSlideBackgroundFill)
        :   mpSdrFillAttribute(bSlideBackgroundFill
                ? slideBackgroundFillGlobalDefault()
                : theGlobalDefault())
        {
        }

@@ -111,6 +118,11 @@ namespace drawinglayer::attribute
            return mpSdrFillAttribute.same_object(theGlobalDefault());
        }

        bool SdrFillAttribute::isSlideBackgroundFill() const
        {
            return mpSdrFillAttribute.same_object(slideBackgroundFillGlobalDefault());
        }

        SdrFillAttribute& SdrFillAttribute::operator=(const SdrFillAttribute&) = default;

        SdrFillAttribute& SdrFillAttribute::operator=(SdrFillAttribute&&) = default;
diff --git a/include/drawinglayer/attribute/sdrfillattribute.hxx b/include/drawinglayer/attribute/sdrfillattribute.hxx
index 93fc7ca..6ba1400 100644
--- a/include/drawinglayer/attribute/sdrfillattribute.hxx
+++ b/include/drawinglayer/attribute/sdrfillattribute.hxx
@@ -56,7 +56,7 @@ namespace drawinglayer::attribute
                const FillGradientAttribute& rGradient,
                const FillHatchAttribute& rHatch,
                const SdrFillGraphicAttribute& rFillGraphic);
            SdrFillAttribute();
            SdrFillAttribute(bool bSlideBackgroundFill = false);
            SdrFillAttribute(const SdrFillAttribute&);
            SdrFillAttribute(SdrFillAttribute&&);
            SdrFillAttribute& operator=(const SdrFillAttribute&);
@@ -66,6 +66,9 @@ namespace drawinglayer::attribute
            // checks if the incarnation is default constructed
            bool isDefault() const;

            // checks if the incarnation is slideBackgroundFill
            bool isSlideBackgroundFill() const;

            // compare operator
            bool operator==(const SdrFillAttribute& rCandidate) const;

diff --git a/include/svx/sdr/primitive2d/svx_primitivetypes2d.hxx b/include/svx/sdr/primitive2d/svx_primitivetypes2d.hxx
index 0af20f0..917d22f 100644
--- a/include/svx/sdr/primitive2d/svx_primitivetypes2d.hxx
+++ b/include/svx/sdr/primitive2d/svx_primitivetypes2d.hxx
@@ -49,6 +49,7 @@
#define PRIMITIVE2D_ID_SDRCHAINEDTEXTPRIMITIVE2D        (PRIMITIVE2D_ID_RANGE_SVX| 23)
#define PRIMITIVE2D_ID_SDRFRAMEBORDERTPRIMITIVE2D       (PRIMITIVE2D_ID_RANGE_SVX| 24)
#define PRIMITIVE2D_ID_OVERLAYDIAGRAMPRIMITIVE2D        (PRIMITIVE2D_ID_RANGE_SVX| 25)
#define PRIMITIVE2D_ID_SLIDEBACKGROUNDFILLPRIMITIVE2D   (PRIMITIVE2D_ID_RANGE_SVX| 26)


#endif // INCLUDED_SVX_SDR_PRIMITIVE2D_SVX_PRIMITIVETYPES2D_HXX
diff --git a/svx/source/sdr/primitive2d/sdrattributecreator.cxx b/svx/source/sdr/primitive2d/sdrattributecreator.cxx
index 6524550..bafa96f 100644
--- a/svx/source/sdr/primitive2d/sdrattributecreator.cxx
+++ b/svx/source/sdr/primitive2d/sdrattributecreator.cxx
@@ -40,6 +40,7 @@
#include <svx/xlnedcit.hxx>
#include <svx/xdash.hxx>
#include <svx/xlndsit.hxx>
#include <svx/xfilluseslidebackgrounditem.hxx>
#include <svx/xfltrit.hxx>
#include <svx/xflftrit.hxx>
#include <svx/xflclit.hxx>
@@ -418,6 +419,19 @@ namespace drawinglayer::primitive2d
                nTransparence = 100;
            }

            if(drawing::FillStyle_NONE == eStyle)
            {
                XFillUseSlideBackgroundItem aBckItem(rSet.Get(XATTR_FILLUSESLIDEBACKGROUND));
                const bool bSlideBackgroundFill(aBckItem.GetValue());

                if(bSlideBackgroundFill)
                {
                    // we have SlideBackgroundFill mode, create a
                    // SdrFillAttribute accordingly
                    return attribute::SdrFillAttribute(true);
                }
            }

            if(drawing::FillStyle_NONE != eStyle)
            {
                if(100 != nTransparence)
diff --git a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
index bffc183c..5b20497 100644
--- a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
+++ b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
@@ -51,6 +51,15 @@
#include <drawinglayer/attribute/sdrglowattribute.hxx>
#include <osl/diagnose.h>

// for SlideBackgroundFillPrimitive2D
#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
#include <svx/unoapi.hxx>
#include <svx/svdpage.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <svx/sdr/contact/objectcontact.hxx>
#include <svx/sdr/contact/viewobjectcontact.hxx>
#include <svx/sdr/contact/displayinfo.hxx>
#include <basegfx/polygon/b2dpolygonclipper.hxx>

using namespace com::sun::star;

@@ -122,8 +131,238 @@ basegfx::B2DRange getTextAnchorRange(const attribute::SdrTextAttribute& rText,
    }
    return aAnchorRange;
}

sdr::contact::ViewContact* getMasterPageViewContact(
    const geometry::ViewInformation2D& rViewInformation,
    basegfx::B2DVector& rPageSize)
{
    // get SdrPage
    const SdrPage* pVisualizedPage(GetSdrPageFromXDrawPage(rViewInformation.getVisualizedPage()));
    if(nullptr == pVisualizedPage)
        return nullptr;

    // do not use in MasterPage mode, so initial SdrPage shall *not* be a
    // MasterPage
    if(pVisualizedPage->IsMasterPage())
        return nullptr;

    // we need that SdrPage to have a MasterPage
    if(!pVisualizedPage->TRG_HasMasterPage())
        return nullptr;

    // copy needed values for processing
    rPageSize.setX(pVisualizedPage->GetWidth());
    rPageSize.setY(pVisualizedPage->GetHeight());

    // return it's ViewContact
    return &pVisualizedPage->TRG_GetMasterPageDescriptorViewContact();
}

// provide a Primitive2D for the SlideBackgroundFill-mode. It is capable
// of expressing that state of fill and it's decomposition implements all
// needed preparation of the geometry in an isolated and controllable
// space and way.
// It is currently simple buffered (due to being derived from
// BufferedDecompositionPrimitive2D) and detects if MasterPage changes
class SlideBackgroundFillPrimitive2D final : public BufferedDecompositionPrimitive2D
{
private:
    /// the basegfx::B2DPolyPolygon geometry
    basegfx::B2DPolyPolygon maPolyPolygon;

    /// the polygon fill color - to allow simple fallback if needed
    basegfx::BColor maBColor;

    /// the last VC the geometry was created for
    sdr::contact::ViewContact* mpLastVC;

protected:
    // create decomposition data
    virtual void create2DDecomposition(
        Primitive2DContainer& rContainer,
        const geometry::ViewInformation2D& rViewInformation) const override;

public:
    /// constructor
    SlideBackgroundFillPrimitive2D(
        const basegfx::B2DPolyPolygon& rPolyPolygon,
        const basegfx::BColor& rBColor);

    /// check existing decomposition data, call parent
    virtual void get2DDecomposition(
        Primitive2DDecompositionVisitor& rVisitor,
        const geometry::ViewInformation2D& rViewInformation) const override;

    /// data read access
    const basegfx::B2DPolyPolygon& getB2DPolyPolygon() const { return maPolyPolygon; }
    const basegfx::BColor& getBColor() const { return maBColor; }

    /// compare operator
    virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;

    /// get range
    virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;

    /// provide unique ID
    virtual sal_uInt32 getPrimitive2DID() const override;
};

SlideBackgroundFillPrimitive2D::SlideBackgroundFillPrimitive2D(
    const basegfx::B2DPolyPolygon& rPolyPolygon,
    const basegfx::BColor& rBColor)
: BufferedDecompositionPrimitive2D()
, maPolyPolygon(rPolyPolygon)
, maBColor(rBColor)
, mpLastVC(nullptr)
{
}

void SlideBackgroundFillPrimitive2D::create2DDecomposition(
    Primitive2DContainer& rContainer,
    const geometry::ViewInformation2D& rViewInformation) const
{
    basegfx::B2DVector aPageSize;
    sdr::contact::ViewContact* pViewContact(getMasterPageViewContact(rViewInformation, aPageSize));

    // Check that we have a referenced SdrPage that is no MasterPage itself and has a MasterPage,
    // we got it's ViewContact in pViewContact
    if(nullptr != pViewContact)
    {
        // Get PolygonRange of own local geometry
        const basegfx::B2DRange aPolygonRange(getB2DPolyPolygon().getB2DRange());

        // if local geometry is empty, nothing will be shown, we are done
        if(aPolygonRange.isEmpty())
            return;

        // Get PageRange
        const basegfx::B2DRange aPageRange(0.0, 0.0, aPageSize.getX(), aPageSize.getY());

        // if local geometry does not overlap with PageRange, nothing will be shown, we are done
        if(!aPageRange.overlaps(aPolygonRange))
            return;

        // Get the geometry

        // Add the MasterPage BG fill (if used, e.g. Picture/gradient, ...)
        pViewContact->getViewIndependentPrimitive2DContainer(rContainer);

        // To create the MasterPage's ObjectHierarchy we need an ObjectContact (AKA View-side of things).
        // We do not have one, but can use a temporary one anytime for temporary work. That whole VC/VOC/OC
        // is designed to work with on-demand temporary objects if needed
        sdr::contact::ObjectContact aObjectContact;

        // get the VOC from it (gets created)
        sdr::contact::ViewObjectContact& rViewObjectContact(pViewContact->GetViewObjectContact(aObjectContact));
        sdr::contact::DisplayInfo aDisplayInfo;

        // we need this DisplayInfo-flag here - exceptionally - to get the same output as
        // if the MasterPage is used as sub-content when creating geometry content for
        // the non-MasterPage-view
        aDisplayInfo.SetSubContentActive(true);

        // get the full MasterPage ObjectHierarchy
        rViewObjectContact.getPrimitive2DSequenceSubHierarchy(aDisplayInfo, rContainer);

        if(!rContainer.empty())
        {
            // We got the geometry, but it may overlap the PageBounds of the
            // Page/MasterPage, thus showing more beyond the PageBorders than
            // the regular PageView does and is intended.
            // This is independent from the geometry we collected in rContainer
            // since the defining geometry is the getB2DPolyPolygon() one.
            // We have already checked above that it's no empty and overlaps
            // somehow.
            // It also might be completely insinde the PageRange. If not, we
            // additionally would need to mask the content against PageBounds,
            // so using potentially two different MaskPrimitive2D's.
            // Since in this case we have a PolyPolygon and a B2DRange it is cheaper
            // to geometrically clip that PolyPolygon geometry and use it
            basegfx::B2DPolyPolygon aPolyPolygon(getB2DPolyPolygon());

            if(!aPageRange.isInside(aPolygonRange))
            {
                // we need to clip local geometry against PageBounds
                aPolyPolygon = basegfx::utils::clipPolyPolygonOnRange(
                    aPolyPolygon,
                    aPageRange,
                    true /* bInside, use inside geometry */,
                    false /* bStroke, handle as filled PolyPolygon */);
            }

            // create MaskPrimitive2D to limit display to PolygonGeometry
            const Primitive2DReference aMasked(
                new MaskPrimitive2D(
                    aPolyPolygon,
                    std::move(rContainer)));

            rContainer = Primitive2DContainer { aMasked };
            return;
        }
    }

    // fallback: create as if drawing::FillStyle_SOLID was used
    rContainer.push_back(
        new PolyPolygonColorPrimitive2D(
            getB2DPolyPolygon(),
            getBColor()));
}

void SlideBackgroundFillPrimitive2D::get2DDecomposition(
    Primitive2DDecompositionVisitor& rVisitor,
    const geometry::ViewInformation2D& rViewInformation) const
{
    basegfx::B2DVector aPageSize;
    sdr::contact::ViewContact* pViewContact(getMasterPageViewContact(rViewInformation, aPageSize));

    if(!getBuffered2DDecomposition().empty())
    {
        if(nullptr != pViewContact && pViewContact != mpLastVC)
        {
            // conditions of last local decomposition have changed, delete
            const_cast< SlideBackgroundFillPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
        }
    }

    if(getBuffered2DDecomposition().empty())
    {
        // remember last MasterPageViewContact
        const_cast< SlideBackgroundFillPrimitive2D* >(this)->mpLastVC = pViewContact;
    }

    // use parent implementation
    BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}

bool SlideBackgroundFillPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
{
    if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
    {
        const SlideBackgroundFillPrimitive2D& rCompare
            = static_cast<const SlideBackgroundFillPrimitive2D&>(rPrimitive);

        return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
            && getBColor() == rCompare.getBColor());
    }

    return false;
}

basegfx::B2DRange SlideBackgroundFillPrimitive2D::getB2DRange(
    const geometry::ViewInformation2D& /*rViewInformation*/) const
{
    // return range
    return basegfx::utils::getRange(getB2DPolyPolygon());
}

// provide unique ID
sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const
{
    return PRIMITIVE2D_ID_SLIDEBACKGROUNDFILLPRIMITIVE2D;
}

}; // end of anonymous namespace

        class TransparencePrimitive2D;

        Primitive2DReference createPolyPolygonFillPrimitive(
@@ -178,6 +417,14 @@ basegfx::B2DRange getTextAnchorRange(const attribute::SdrTextAttribute& rText,
                    rDefinitionRange,
                    rFill.getFillGraphic().createFillGraphicAttribute(rDefinitionRange));
            }
            else if(rFill.isSlideBackgroundFill())
            {
                // create needed Primitive2D representation for
                // SlideBackgroundFill-mode
                pNewFillPrimitive = new SlideBackgroundFillPrimitive2D(
                    rPolyPolygon,
                    rFill.getColor());
            }
            else
            {
                pNewFillPrimitive = new PolyPolygonColorPrimitive2D(