drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx |    2 
 drawinglayer/source/primitive2d/glowprimitive2d.cxx         |    2 
 drawinglayer/source/primitive2d/shadowprimitive2d.cxx       |  398 ++++++++++--
 drawinglayer/source/primitive2d/softedgeprimitive2d.cxx     |    4 
 drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx  |  102 ---
 drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx  |    4 
 drawinglayer/source/processor2d/vclpixelprocessor2d.cxx     |   53 -
 drawinglayer/source/processor2d/vclpixelprocessor2d.hxx     |    4 
 include/drawinglayer/primitive2d/glowprimitive2d.hxx        |    1 
 include/drawinglayer/primitive2d/shadowprimitive2d.hxx      |   98 +-
 10 files changed, 414 insertions(+), 254 deletions(-)

New commits:
commit e735ad1c57cddaf17d6ffc0cf15b5e14fa63c4ad
Author:     Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de>
AuthorDate: Tue Sep 13 13:42:54 2022 +0200
Commit:     Armin Le Grand <armin.le.gr...@me.com>
CommitDate: Wed Sep 14 09:54:18 2022 +0200

    Rework of ShadowPrimitive2D
    
    This is pretty much the same for ShadowPrimitive2D as the
    change for GlowPrimitive2D and SoftEdgePrimitive2D, so for
    more comments please refer to those commits:
    
     c2d1458723c66c2fd717a112f89f773226adc841
     707b0c328a282d993fa33b618083d20b6c521de6
    
    There are some needed differences due to ShadowPrimitive2D
    having existed longer and is used for non-blurred shadow
    for a long time and is used as unchanged as possible.
    Only for active glow of shadow is a buffering and local
    decompose used.
    
    Change-Id: I55e6516f59390079356ac16f24743b474e53fb05
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/139858
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx 
b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx
index da3621aa189c..0a3249399a44 100644
--- a/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx
+++ b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx
@@ -17,7 +17,7 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
-#include <drawinglayer/primitive2d/GlowSoftEgdeShadowTools.hxx>
+#include "GlowSoftEgdeShadowTools.hxx"
 #include <vcl/bitmapex.hxx>
 #include <vcl/BitmapFilter.hxx>
 #include <vcl/BitmapBasicMorphologyFilter.hxx>
diff --git a/include/drawinglayer/primitive2d/GlowSoftEgdeShadowTools.hxx 
b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx
similarity index 100%
rename from include/drawinglayer/primitive2d/GlowSoftEgdeShadowTools.hxx
rename to drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx
diff --git a/drawinglayer/source/primitive2d/glowprimitive2d.cxx 
b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
index f8c503759e7d..44a97c536fb2 100644
--- a/drawinglayer/source/primitive2d/glowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
@@ -24,7 +24,7 @@
 #include <basegfx/matrix/b2dhommatrixtools.hxx>
 #include <toolkit/helper/vclunohelper.hxx>
 #include <drawinglayer/converters.hxx>
-#include <drawinglayer/primitive2d/GlowSoftEgdeShadowTools.hxx>
+#include "GlowSoftEgdeShadowTools.hxx"
 
 #ifdef DBG_UTIL
 #include <tools/stream.hxx>
diff --git a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx 
b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
index 15deebfb2a1e..0702c6c011f1 100644
--- a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
@@ -22,73 +22,383 @@
 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <drawinglayer/converters.hxx>
+#include "GlowSoftEgdeShadowTools.hxx"
+
+#ifdef DBG_UTIL
+#include <tools/stream.hxx>
+#include <vcl/filter/PngImageWriter.hxx>
+#endif
 
 #include <memory>
 #include <utility>
 
 using namespace com::sun::star;
 
-
 namespace drawinglayer::primitive2d
 {
-        ShadowPrimitive2D::ShadowPrimitive2D(
-            basegfx::B2DHomMatrix aShadowTransform,
-            const basegfx::BColor& rShadowColor,
-            double fShadowBlur,
-            Primitive2DContainer&& aChildren)
-        :   GroupPrimitive2D(std::move(aChildren)),
-            maShadowTransform(std::move(aShadowTransform)),
-            maShadowColor(rShadowColor),
-            mfShadowBlur(fShadowBlur)
+ShadowPrimitive2D::ShadowPrimitive2D(basegfx::B2DHomMatrix aShadowTransform,
+                                     const basegfx::BColor& rShadowColor, 
double fShadowBlur,
+                                     Primitive2DContainer&& aChildren)
+    : BufferedDecompositionGroupPrimitive2D(std::move(aChildren))
+    , maShadowTransform(std::move(aShadowTransform))
+    , maShadowColor(rShadowColor)
+    , mfShadowBlur(fShadowBlur)
+    , mfLastDiscreteBlurRadius(0.0)
+    , maLastClippedRange()
+{
+}
+
+bool ShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+    if (BufferedDecompositionGroupPrimitive2D::operator==(rPrimitive))
+    {
+        const ShadowPrimitive2D& rCompare = static_cast<const 
ShadowPrimitive2D&>(rPrimitive);
+
+        return (getShadowTransform() == rCompare.getShadowTransform()
+                && getShadowColor() == rCompare.getShadowColor()
+                && getShadowBlur() == rCompare.getShadowBlur());
+    }
+
+    return false;
+}
+
+// Helper to get the to-be-shadowed geometry completely embedded to
+// a ModifiedColorPrimitive2D (change to ShadowColor) and TransformPrimitive2D
+// (direction/offset/transformation of shadow). Since this is used pretty
+// often, pack into a helper
+void ShadowPrimitive2D::getFullyEmbeddedShadowPrimitives(Primitive2DContainer& 
rContainer) const
+{
+    if (getChildren().empty())
+        return;
+
+    // create a modifiedColorPrimitive containing the shadow color and the 
content
+    const basegfx::BColorModifierSharedPtr aBColorModifier
+        = std::make_shared<basegfx::BColorModifier_replace>(getShadowColor());
+    const Primitive2DReference xRefA(
+        new ModifiedColorPrimitive2D(Primitive2DContainer(getChildren()), 
aBColorModifier));
+    Primitive2DContainer aSequenceB{ xRefA };
+
+    // build transformed primitiveVector with shadow offset and add to target
+    rContainer.visit(new TransformPrimitive2D(getShadowTransform(), 
std::move(aSequenceB)));
+}
+
+bool ShadowPrimitive2D::prepareValuesAndcheckValidity(
+    basegfx::B2DRange& rBlurRange, basegfx::B2DRange& rClippedRange,
+    basegfx::B2DVector& rDiscreteBlurSize, double& rfDiscreteBlurRadius,
+    const geometry::ViewInformation2D& rViewInformation) const
+{
+    // no BlurRadius defined, done
+    if (getShadowBlur() <= 0.0)
+        return false;
+
+    // no geometry, done
+    if (getChildren().empty())
+        return false;
+
+    // no pixel target, done
+    if (rViewInformation.getObjectToViewTransformation().isIdentity())
+        return false;
+
+    // get fully embedded ShadowPrimitive
+    Primitive2DContainer aEmbedded;
+    getFullyEmbeddedShadowPrimitives(aEmbedded);
+
+    // get geometry range that defines area that needs to be pixelated
+    rBlurRange = aEmbedded.getB2DRange(rViewInformation);
+
+    // no range of geometry, done
+    if (rBlurRange.isEmpty())
+        return false;
+
+    // extend range by BlurRadius in all directions
+    rBlurRange.grow(getShadowBlur());
+
+    // initialize ClippedRange to full BlurRange -> all is visible
+    rClippedRange = rBlurRange;
+
+    // get Viewport and check if used. If empty, all is visible (see
+    // ViewInformation2D definition in viewinformation2d.hxx)
+    if (!rViewInformation.getViewport().isEmpty())
+    {
+        // if used, extend by BlurRadius to ensure needed parts are included
+        basegfx::B2DRange aVisibleArea(rViewInformation.getViewport());
+        aVisibleArea.grow(getShadowBlur());
+
+        // calculate ClippedRange
+        rClippedRange.intersect(aVisibleArea);
+
+        // if BlurRange is completely outside of VisibleArea, ClippedRange
+        // will be empty and we are done
+        if (rClippedRange.isEmpty())
+            return false;
+    }
+
+    // calculate discrete pixel size of BlurRange. If it's too small to 
visualize, we are done
+    rDiscreteBlurSize = rViewInformation.getObjectToViewTransformation() * 
rBlurRange.getRange();
+    if (ceil(rDiscreteBlurSize.getX()) < 2.0 || ceil(rDiscreteBlurSize.getY()) 
< 2.0)
+        return false;
+
+    // calculate discrete pixel size of BlurRadius. If it's too small to 
visualize, we are done
+    rfDiscreteBlurRadius = ceil(
+        (rViewInformation.getObjectToViewTransformation() * 
basegfx::B2DVector(getShadowBlur(), 0))
+            .getLength());
+    if (rfDiscreteBlurRadius < 1.0)
+        return false;
+
+    return true;
+}
+
+void ShadowPrimitive2D::create2DDecomposition(
+    Primitive2DContainer& rContainer, const geometry::ViewInformation2D& 
rViewInformation) const
+{
+    if (getShadowBlur() <= 0.0)
+    {
+        // Normal (non-blurred) shadow is already completely
+        // handled by get2DDecomposition and not buffered. It
+        // does not need to be since it's a simple embedding
+        // to a ModifiedColorPrimitive2D and TransformPrimitive2D
+        return;
+    }
+
+    // from here on we process a blurrred shadow
+    basegfx::B2DRange aBlurRange;
+    basegfx::B2DRange aClippedRange;
+    basegfx::B2DVector aDiscreteBlurSize;
+    double fDiscreteBlurRadius(0.0);
+
+    // Check various validity details and calculate/prepare values. If false, 
we are done
+    if (!prepareValuesAndcheckValidity(aBlurRange, aClippedRange, 
aDiscreteBlurSize,
+                                       fDiscreteBlurRadius, rViewInformation))
+        return;
+
+    // Create embedding transformation from object to top-left zero-aligned
+    // target pixel geometry (discrete form of ClippedRange)
+    // First, move to top-left of BlurRange
+    const sal_uInt32 nDiscreteBlurWidth(ceil(aDiscreteBlurSize.getX()));
+    const sal_uInt32 nDiscreteBlurHeight(ceil(aDiscreteBlurSize.getY()));
+    basegfx::B2DHomMatrix 
aEmbedding(basegfx::utils::createTranslateB2DHomMatrix(
+        -aClippedRange.getMinX(), -aClippedRange.getMinY()));
+    // Second, scale to discrete bitmap size
+    // Even when using the offset from ClippedRange, we need to use the
+    // scaling from the full representation, thus from BlurRange
+    aEmbedding.scale(nDiscreteBlurWidth / aBlurRange.getWidth(),
+                     nDiscreteBlurHeight / aBlurRange.getHeight());
+
+    // Get fully embedded ShadowPrimitives. This will also embed to
+    // ModifiedColorPrimitive2D (what is not urgently needed) to create
+    // the alpha channel, but a paint with all colors set to a single
+    // one (like shadowColor here) is often less expensive due to possible
+    // simplifications painting the primitves (e.g. gradient)
+    Primitive2DContainer aEmbedded;
+    getFullyEmbeddedShadowPrimitives(aEmbedded);
+
+    // Embed content graphics to TransformPrimitive2D
+    const primitive2d::Primitive2DReference xEmbedRef(
+        new primitive2d::TransformPrimitive2D(aEmbedding, 
std::move(aEmbedded)));
+    primitive2d::Primitive2DContainer xEmbedSeq{ xEmbedRef };
+
+    // Create BitmapEx using drawinglayer tooling, including a 
MaximumQuadraticPixel
+    // limitation to be safe and not go runtime/memory havoc. Use a pretty 
small
+    // limit due to this is Blurred Shadow functionality and will look good 
with bitmap
+    // scaling anyways. The value of 250.000 square pixels below maybe adapted 
as needed.
+    // NOTE: This may be further optimized. Only the alpha channel is needed, 
so
+    //       convertToBitmapEx may be split in tooling to have a version that 
only
+    //       creates the alpha channel. Potential win is >50% for the alpha 
pixel
+    //       creation step ('>' because alpha painting uses a ColorStack and 
thus
+    //       often can used simplified rendering)
+    const basegfx::B2DVector 
aDiscreteClippedSize(rViewInformation.getObjectToViewTransformation()
+                                                  * aClippedRange.getRange());
+    const sal_uInt32 nDiscreteClippedWidth(ceil(aDiscreteClippedSize.getX()));
+    const sal_uInt32 nDiscreteClippedHeight(ceil(aDiscreteClippedSize.getY()));
+    const geometry::ViewInformation2D aViewInformation2D;
+    const sal_uInt32 nMaximumQuadraticPixels(250000);
+    const BitmapEx aBitmapEx(::drawinglayer::convertToBitmapEx(
+        std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, 
nDiscreteClippedHeight,
+        nMaximumQuadraticPixels));
+
+    // if we have no shadow, we are done
+    if (aBitmapEx.IsEmpty())
+        return;
+
+    const Size& rBitmapExSizePixel(aBitmapEx.GetSizePixel());
+    if (!(rBitmapExSizePixel.Width() > 0 && rBitmapExSizePixel.Height() > 0))
+        return;
+
+    // We may have to take a corrective scaling into account when the
+    // MaximumQuadraticPixel limit was used/triggered
+    double fScale(1.0);
+
+    if (static_cast<sal_uInt32>(rBitmapExSizePixel.Width()) != 
nDiscreteClippedWidth
+        || static_cast<sal_uInt32>(rBitmapExSizePixel.Height()) != 
nDiscreteClippedHeight)
+    {
+        // scale in X and Y should be the same (see fReduceFactor in 
convertToBitmapEx),
+        // so adapt numerically to a single scale value, they are integer 
rounded values
+        const double fScaleX(static_cast<double>(rBitmapExSizePixel.Width())
+                             / static_cast<double>(nDiscreteClippedWidth));
+        const double fScaleY(static_cast<double>(rBitmapExSizePixel.Height())
+                             / static_cast<double>(nDiscreteClippedHeight));
+
+        fScale = (fScaleX + fScaleY) * 0.5;
+    }
+
+    // Get the Alpha and use as base to blur and apply the effect
+    const AlphaMask mask(drawinglayer::primitive2d::ProcessAndBlurAlphaMask(
+        aBitmapEx.GetAlpha(), 0, fDiscreteBlurRadius * fScale, 0, false));
+
+    // The end result is the bitmap filled with blur color and blurred 8-bit 
alpha mask
+    Bitmap bmp = aBitmapEx.GetBitmap();
+    bmp.Erase(Color(getShadowColor()));
+    BitmapEx result(bmp, mask);
+
+#ifdef DBG_UTIL
+    static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
+    if (bDoSaveForVisualControl)
+    {
+        // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
+        static const OUString sDumpPath(
+            OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
+        if (!sDumpPath.isEmpty())
         {
+            SvFileStream aNew(sDumpPath + "test_shadowblur.png",
+                              StreamMode::WRITE | StreamMode::TRUNC);
+            vcl::PngImageWriter aPNGWriter(aNew);
+            aPNGWriter.write(result);
         }
+    }
+#endif
+
+    // Independent from discrete sizes of blur alpha creation, always
+    // map and project blur result to geometry range extended by blur
+    // radius, but to the eventually clipped instance (ClippedRange)
+    const primitive2d::Primitive2DReference xEmbedRefBitmap(
+        new BitmapPrimitive2D(VCLUnoHelper::CreateVCLXBitmap(result),
+                              basegfx::utils::createScaleTranslateB2DHomMatrix(
+                                  aClippedRange.getWidth(), 
aClippedRange.getHeight(),
+                                  aClippedRange.getMinX(), 
aClippedRange.getMinY())));
+
+    rContainer = primitive2d::Primitive2DContainer{ xEmbedRefBitmap };
+}
+
+void ShadowPrimitive2D::get2DDecomposition(
+    Primitive2DDecompositionVisitor& rVisitor,
+    const geometry::ViewInformation2D& rViewInformation) const
+{
+    if (getShadowBlur() <= 0.0)
+    {
+        // normal (non-blurred) shadow
+        if (getChildren().empty())
+            return;
+
+        // get fully embedded ShadowPrimitives
+        Primitive2DContainer aEmbedded;
+        getFullyEmbeddedShadowPrimitives(aEmbedded);
+
+        rVisitor.visit(aEmbedded);
+        return;
+    }
+
+    // here we have a blurrred shadow, check conditions of last
+    // buffered decompose and decide re-use or re-create by using
+    // setBuffered2DDecomposition to reset local buffered version
+    basegfx::B2DRange aBlurRange;
+    basegfx::B2DRange aClippedRange;
+    basegfx::B2DVector aDiscreteBlurSize;
+    double fDiscreteBlurRadius(0.0);
 
-        bool ShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) 
const
+    // Check various validity details and calculate/prepare values. If false, 
we are done
+    if (!prepareValuesAndcheckValidity(aBlurRange, aClippedRange, 
aDiscreteBlurSize,
+                                       fDiscreteBlurRadius, rViewInformation))
+        return;
+
+    if (!getBuffered2DDecomposition().empty())
+    {
+        // First check is to detect if the last created decompose is capable
+        // to represent the now requested visualization (see similar
+        // implementation at GlowPrimitive2D).
+        if (!maLastClippedRange.isEmpty() && 
!maLastClippedRange.isInside(aClippedRange))
         {
-            if(BasePrimitive2D::operator==(rPrimitive))
-            {
-                const ShadowPrimitive2D& rCompare = static_cast< const 
ShadowPrimitive2D& >(rPrimitive);
+            basegfx::B2DRange aLastClippedRangeAndHairline(maLastClippedRange);
 
-                return (getShadowTransform() == rCompare.getShadowTransform()
-                    && getShadowColor() == rCompare.getShadowColor()
-                    && getShadowBlur() == rCompare.getShadowBlur());
+            if (!rViewInformation.getObjectToViewTransformation().isIdentity())
+            {
+                // Grow by view-dependent size of 1/2 pixel
+                const double 
fHalfPixel((rViewInformation.getInverseObjectToViewTransformation()
+                                         * basegfx::B2DVector(0.5, 0))
+                                            .getLength());
+                aLastClippedRangeAndHairline.grow(fHalfPixel);
             }
 
-            return false;
+            if (!aLastClippedRangeAndHairline.isInside(aClippedRange))
+            {
+                // Conditions of last local decomposition have changed, delete
+                
const_cast<ShadowPrimitive2D*>(this)->setBuffered2DDecomposition(
+                    Primitive2DContainer());
+            }
         }
+    }
 
-        basegfx::B2DRange ShadowPrimitive2D::getB2DRange(const 
geometry::ViewInformation2D& rViewInformation) const
-        {
-            basegfx::B2DRange 
aRetval(getChildren().getB2DRange(rViewInformation));
-            aRetval.grow(getShadowBlur());
-            aRetval.transform(getShadowTransform());
-            return aRetval;
-        }
+    if (!getBuffered2DDecomposition().empty())
+    {
+        // Second check is to react on changes of the DiscreteSoftRadius when
+        // zooming in/out (see similar implementation at ShadowPrimitive2D).
+        bool bFree(mfLastDiscreteBlurRadius <= 0.0 || fDiscreteBlurRadius <= 
0.0);
 
-        void 
ShadowPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& 
rVisitor, const geometry::ViewInformation2D& /*rViewInformation*/) const
+        if (!bFree)
         {
-            if(getChildren().empty())
-                return;
-
-            // create a modifiedColorPrimitive containing the shadow color and 
the content
-            const basegfx::BColorModifierSharedPtr aBColorModifier =
-                std::make_shared<basegfx::BColorModifier_replace>(
-                    getShadowColor());
-            const Primitive2DReference xRefA(
-                new ModifiedColorPrimitive2D(
-                    Primitive2DContainer(getChildren()),
-                    aBColorModifier));
-            Primitive2DContainer aSequenceB { xRefA };
-
-            // build transformed primitiveVector with shadow offset and add to 
target
-            rVisitor.visit(new TransformPrimitive2D(getShadowTransform(), 
std::move(aSequenceB)));
+            const double fDiff(fabs(mfLastDiscreteBlurRadius - 
fDiscreteBlurRadius));
+            const double fLen(fabs(mfLastDiscreteBlurRadius) + 
fabs(fDiscreteBlurRadius));
+            const double fRelativeChange(fDiff / fLen);
+
+            // Use lower fixed values here to change more often, higher to 
change less often.
+            // Value is in the range of ]0.0 .. 1.0]
+            bFree = fRelativeChange >= 0.15;
         }
 
-        // provide unique ID
-        sal_uInt32 ShadowPrimitive2D::getPrimitive2DID() const
+        if (bFree)
         {
-            return PRIMITIVE2D_ID_SHADOWPRIMITIVE2D;
+            // Conditions of last local decomposition have changed, delete
+            const_cast<ShadowPrimitive2D*>(this)->setBuffered2DDecomposition(
+                Primitive2DContainer());
         }
+    }
+
+    if (getBuffered2DDecomposition().empty())
+    {
+        // refresh last used DiscreteBlurRadius and ClippedRange to new 
remembered values
+        const_cast<ShadowPrimitive2D*>(this)->mfLastDiscreteBlurRadius = 
fDiscreteBlurRadius;
+        const_cast<ShadowPrimitive2D*>(this)->maLastClippedRange = 
aClippedRange;
+    }
+
+    // call parent, that will check for empty, call create2DDecomposition and
+    // set as decomposition
+    BufferedDecompositionGroupPrimitive2D::get2DDecomposition(rVisitor, 
rViewInformation);
+}
+
+basegfx::B2DRange
+ShadowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& 
rViewInformation) const
+{
+    // Hint: Do *not* use GroupPrimitive2D::getB2DRange, that will 
(unnecessarily)
+    // use the decompose - what works, but is not needed here.
+    // We know the to-be-visualized geometry and the radius it needs to be 
extended,
+    // so simply calculate the exact needed range.
+    basegfx::B2DRange aRetval(getChildren().getB2DRange(rViewInformation));
+
+    if (getShadowBlur() > 0.0)
+    {
+        // blurred shadow, that extends the geometry
+        aRetval.grow(getShadowBlur());
+    }
+
+    aRetval.transform(getShadowTransform());
+    return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 ShadowPrimitive2D::getPrimitive2DID() const { return 
PRIMITIVE2D_ID_SHADOWPRIMITIVE2D; }
 
 } // end of namespace
 
diff --git a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx 
b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
index 55cddb919aa6..f01b6759c7f2 100644
--- a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
@@ -24,7 +24,7 @@
 #include <basegfx/matrix/b2dhommatrixtools.hxx>
 #include <toolkit/helper/vclunohelper.hxx>
 #include <drawinglayer/converters.hxx>
-#include <drawinglayer/primitive2d/GlowSoftEgdeShadowTools.hxx>
+#include "GlowSoftEgdeShadowTools.hxx"
 
 #ifdef DBG_UTIL
 #include <tools/stream.hxx>
@@ -196,7 +196,7 @@ void SoftEdgePrimitive2D::create2DDecomposition(
         BitmapEx result(aBitmapEx.GetBitmap(), aMask);
 
 #ifdef DBG_UTIL
-        static bool bDoSaveForVisualControl(true); // loplugin:constvars:ignore
+        static bool bDoSaveForVisualControl(false); // 
loplugin:constvars:ignore
         if (bDoSaveForVisualControl)
         {
             // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index 926835788383..8e221affe978 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -928,11 +928,6 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const 
primitive2d::BasePrimi
                 static_cast<const 
primitive2d::ObjectInfoPrimitive2D&>(rCandidate));
             break;
         }
-        case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
-        {
-            processPrimitive2DOnPixelProcessor(rCandidate);
-            break;
-        }
         default:
         {
             // process recursively
@@ -2368,103 +2363,6 @@ void 
VclMetafileProcessor2D::processStructureTagPrimitive2D(
     }
 }
 
-VclPtr<VirtualDevice>
-VclMetafileProcessor2D::CreateBufferDevice(const basegfx::B2DRange& 
rCandidateRange,
-                                           geometry::ViewInformation2D& 
rViewInfo,
-                                           tools::Rectangle& rRectLogic, Size& 
rSizePixel) const
-{
-    constexpr double fMaxSquarePixels = 500000;
-    basegfx::B2DRange aViewRange(rCandidateRange);
-    aViewRange.transform(maCurrentTransformation);
-    rRectLogic = 
tools::Rectangle(static_cast<tools::Long>(std::floor(aViewRange.getMinX())),
-                                  
static_cast<tools::Long>(std::floor(aViewRange.getMinY())),
-                                  
static_cast<tools::Long>(std::ceil(aViewRange.getMaxX())),
-                                  
static_cast<tools::Long>(std::ceil(aViewRange.getMaxY())));
-    const tools::Rectangle 
aRectPixel(mpOutputDevice->LogicToPixel(rRectLogic));
-    rSizePixel = aRectPixel.GetSize();
-    const double fViewVisibleArea(rSizePixel.getWidth() * 
rSizePixel.getHeight());
-    double fReduceFactor(1.0);
-
-    if (fViewVisibleArea > fMaxSquarePixels)
-    {
-        // reduce render size
-        fReduceFactor = sqrt(fMaxSquarePixels / fViewVisibleArea);
-        rSizePixel = Size(basegfx::fround(rSizePixel.getWidth() * 
fReduceFactor),
-                          basegfx::fround(rSizePixel.getHeight() * 
fReduceFactor));
-    }
-
-    VclPtrInstance<VirtualDevice> pBufferDevice(DeviceFormat::DEFAULT, 
DeviceFormat::DEFAULT);
-    if (pBufferDevice->SetOutputSizePixel(rSizePixel))
-    {
-        // create and set MapModes for target devices
-        MapMode aNewMapMode(mpOutputDevice->GetMapMode());
-        aNewMapMode.SetOrigin(Point(-rRectLogic.Left(), -rRectLogic.Top()));
-        pBufferDevice->SetMapMode(aNewMapMode);
-
-        // prepare view transformation for target renderers
-        // ATTENTION! Need to apply another scaling because of the potential 
DPI differences
-        // between Printer and VDev (mpOutputDevice and pBufferDevice here).
-        // To get the DPI, LogicToPixel from (1,1) from MapUnit::MapInch needs 
to be used.
-        basegfx::B2DHomMatrix 
aViewTransform(pBufferDevice->GetViewTransformation());
-        const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), 
MapMode(MapUnit::MapInch)));
-        const Size aDPINew(pBufferDevice->LogicToPixel(Size(1, 1), 
MapMode(MapUnit::MapInch)));
-        const double fDPIXChange(static_cast<double>(aDPIOld.getWidth())
-                                 / static_cast<double>(aDPINew.getWidth()));
-        const double fDPIYChange(static_cast<double>(aDPIOld.getHeight())
-                                 / static_cast<double>(aDPINew.getHeight()));
-
-        if (!basegfx::fTools::equal(fDPIXChange, 1.0) || 
!basegfx::fTools::equal(fDPIYChange, 1.0))
-        {
-            aViewTransform.scale(fDPIXChange, fDPIYChange);
-        }
-
-        // also take scaling from Size reduction into account
-        if (!basegfx::fTools::equal(fReduceFactor, 1.0))
-        {
-            aViewTransform.scale(fReduceFactor, fReduceFactor);
-        }
-
-        // create view information and pixel renderer. Reuse known 
ViewInformation
-        // except new transformation and range
-        rViewInfo = geometry::ViewInformation2D(
-            getViewInformation2D().getObjectTransformation(), aViewTransform, 
aViewRange,
-            getViewInformation2D().getVisualizedPage(), 
getViewInformation2D().getViewTime());
-    }
-    else
-        pBufferDevice.disposeAndClear();
-
-#if HAVE_P1155R3
-    return pBufferDevice;
-#else
-    return std::move(pBufferDevice);
-#endif
-}
-
-void VclMetafileProcessor2D::processPrimitive2DOnPixelProcessor(
-    const primitive2d::BasePrimitive2D& rCandidate)
-{
-    basegfx::B2DRange 
aViewRange(rCandidate.getB2DRange(getViewInformation2D()));
-    geometry::ViewInformation2D aViewInfo;
-    tools::Rectangle aRectLogic;
-    Size aSizePixel;
-    auto pBufferDevice(CreateBufferDevice(aViewRange, aViewInfo, aRectLogic, 
aSizePixel));
-    if (pBufferDevice)
-    {
-        VclPixelProcessor2D aBufferProcessor(aViewInfo, *pBufferDevice, 
maBColorModifierStack);
-
-        // draw content using pixel renderer
-        primitive2d::Primitive2DReference aRef(
-            &const_cast<primitive2d::BasePrimitive2D&>(rCandidate));
-        aBufferProcessor.process({ aRef });
-        const BitmapEx aBmContent(pBufferDevice->GetBitmapEx(Point(), 
aSizePixel));
-        mpOutputDevice->DrawBitmapEx(aRectLogic.TopLeft(), 
aRectLogic.GetSize(), aBmContent);
-
-        // aBufferProcessor dtor pops state off pBufferDevice pushed on by its 
ctor, let
-        // pBufferDevice live past aBufferProcessor scope to avoid warnings
-    }
-    pBufferDevice.disposeAndClear();
-}
-
 } // end of namespace
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx 
b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx
index 06fd61e18309..0393039f4455 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx
@@ -144,10 +144,6 @@ private:
         const primitive2d::TransparencePrimitive2D& rTransparenceCandidate);
     void processStructureTagPrimitive2D(
         const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate);
-    void processPrimitive2DOnPixelProcessor(const 
primitive2d::BasePrimitive2D& rCandidate);
-    VclPtr<VirtualDevice> CreateBufferDevice(const basegfx::B2DRange& 
rCandidateRange,
-                                             geometry::ViewInformation2D& 
rViewInfo,
-                                             tools::Rectangle& rRectLogic, 
Size& rSizePixel) const;
 
     /// Convert the fWidth to the same space as its coordinates.
     double getTransformedLineWidth(double fWidth) const;
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index 12e044959b31..cf8d7dcd3ac0 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -58,7 +58,6 @@
 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
 #include <drawinglayer/primitive2d/patternfillprimitive2d.hxx>
-#include <drawinglayer/primitive2d/GlowSoftEgdeShadowTools.hxx>
 
 #include <com/sun/star/awt/XWindow2.hpp>
 #include <com/sun/star/awt/XControl.hpp>
@@ -395,12 +394,6 @@ void VclPixelProcessor2D::processBasePrimitive2D(const 
primitive2d::BasePrimitiv
                 static_cast<const 
drawinglayer::primitive2d::BorderLinePrimitive2D&>(rCandidate));
             break;
         }
-        case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
-        {
-            processShadowPrimitive2D(
-                static_cast<const 
drawinglayer::primitive2d::ShadowPrimitive2D&>(rCandidate));
-            break;
-        }
         case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D:
         {
             processFillGradientPrimitive2D(
@@ -949,52 +942,6 @@ void VclPixelProcessor2D::processMetaFilePrimitive2D(const 
primitive2d::BasePrim
     }
 }
 
-void VclPixelProcessor2D::processShadowPrimitive2D(const 
primitive2d::ShadowPrimitive2D& rCandidate)
-{
-    if (rCandidate.getShadowBlur() == 0)
-    {
-        process(rCandidate);
-        return;
-    }
-
-    basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
-    aRange.transform(maCurrentTransformation);
-    basegfx::B2DVector aBlurRadiusVector(rCandidate.getShadowBlur(), 0);
-    aBlurRadiusVector *= maCurrentTransformation;
-    const double fBlurRadius = aBlurRadiusVector.getLength();
-
-    impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
-    if (aBufferDevice.isVisible() && !aRange.isEmpty())
-    {
-        OutputDevice* pLastOutputDevice = mpOutputDevice;
-        mpOutputDevice = &aBufferDevice.getContent();
-
-        process(rCandidate);
-
-        const tools::Rectangle 
aRect(static_cast<tools::Long>(std::floor(aRange.getMinX())),
-                                     
static_cast<tools::Long>(std::floor(aRange.getMinY())),
-                                     
static_cast<tools::Long>(std::ceil(aRange.getMaxX())),
-                                     
static_cast<tools::Long>(std::ceil(aRange.getMaxY())));
-
-        BitmapEx bitmapEx = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), 
aRect.GetSize());
-
-        AlphaMask mask = 
drawinglayer::primitive2d::ProcessAndBlurAlphaMask(bitmapEx.GetAlpha(), 0,
-                                                                            
fBlurRadius, 0, false);
-
-        const basegfx::BColor aShadowColor(
-            
maBColorModifierStack.getModifiedColor(rCandidate.getShadowColor()));
-
-        Bitmap bitmap = bitmapEx.GetBitmap();
-        bitmap.Erase(Color(aShadowColor));
-        BitmapEx result(bitmap, mask);
-
-        mpOutputDevice = pLastOutputDevice;
-        mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result);
-    }
-    else
-        SAL_WARN("drawinglayer", "Temporary buffered virtual device is not 
visible");
-}
-
 void VclPixelProcessor2D::processFillGradientPrimitive2D(
     const primitive2d::FillGradientPrimitive2D& rPrimitive)
 {
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx 
b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
index 9f1e11cb9110..c144ba9647eb 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
@@ -39,9 +39,6 @@ class PolygonStrokePrimitive2D;
 class FillHatchPrimitive2D;
 class BackgroundColorPrimitive2D;
 class BorderLinePrimitive2D;
-class GlowPrimitive2D;
-class ShadowPrimitive2D;
-class SoftEdgePrimitive2D;
 class FillGradientPrimitive2D;
 class PatternFillPrimitive2D;
 }
@@ -97,7 +94,6 @@ class VclPixelProcessor2D final : public VclProcessor2D
     processBorderLinePrimitive2D(const 
drawinglayer::primitive2d::BorderLinePrimitive2D& rBorder);
     void processInvertPrimitive2D(const primitive2d::BasePrimitive2D& 
rCandidate);
     void processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& 
rCandidate);
-    void processShadowPrimitive2D(const primitive2d::ShadowPrimitive2D& 
rCandidate);
     void processFillGradientPrimitive2D(const 
primitive2d::FillGradientPrimitive2D& rPrimitive);
     void processPatternFillPrimitive2D(const 
primitive2d::PatternFillPrimitive2D& rPrimitive);
 
diff --git a/include/drawinglayer/primitive2d/glowprimitive2d.hxx 
b/include/drawinglayer/primitive2d/glowprimitive2d.hxx
index 6a60c85ad6ae..985137e23215 100644
--- a/include/drawinglayer/primitive2d/glowprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/glowprimitive2d.hxx
@@ -21,7 +21,6 @@
 
 #include <drawinglayer/drawinglayerdllapi.h>
 
-#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
 #include <drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx>
 #include <tools/color.hxx>
 
diff --git a/include/drawinglayer/primitive2d/shadowprimitive2d.hxx 
b/include/drawinglayer/primitive2d/shadowprimitive2d.hxx
index 45a97a96d476..79f2f30f700a 100644
--- a/include/drawinglayer/primitive2d/shadowprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/shadowprimitive2d.hxx
@@ -21,14 +21,12 @@
 
 #include <drawinglayer/drawinglayerdllapi.h>
 
-#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
-#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx>
 #include <basegfx/color/bcolor.hxx>
 
-
 namespace drawinglayer::primitive2d
 {
-        /** ShadowPrimitive2D class
+/** ShadowPrimitive2D class
 
             This primitive defines a generic shadow geometry construction
             for 2D objects. It decomposes to a TransformPrimitive2D embedded
@@ -42,44 +40,60 @@ namespace drawinglayer::primitive2d
             are needed for the shadow itself; all the local decompositions of 
the
             original geometry can be reused from the renderer for shadow 
visualisation.
         */
-        class DRAWINGLAYER_DLLPUBLIC ShadowPrimitive2D final : public 
GroupPrimitive2D
-        {
-        private:
-            /// the shadow transformation, normally just an offset
-            basegfx::B2DHomMatrix                   maShadowTransform;
-
-            /// the shadow color to which all geometry is to be forced
-            basegfx::BColor                         maShadowColor;
-
-            /// the blur radius of the shadow
-            double mfShadowBlur;
-
-
-    public:
-            /// constructor
-            ShadowPrimitive2D(
-                basegfx::B2DHomMatrix aShadowTransform,
-                const basegfx::BColor& rShadowColor,
-                double fShadowBlur,
-                Primitive2DContainer&& aChildren);
-
-            /// data read access
-            const basegfx::B2DHomMatrix& getShadowTransform() const { return 
maShadowTransform; }
-            const basegfx::BColor& getShadowColor() const { return 
maShadowColor; }
-            double getShadowBlur() const { return mfShadowBlur; }
-            /// compare operator
-            virtual bool operator==(const BasePrimitive2D& rPrimitive) const 
override;
-
-            /// get range
-            virtual basegfx::B2DRange getB2DRange(const 
geometry::ViewInformation2D& rViewInformation) const override;
-
-            ///  create decomposition
-            virtual void get2DDecomposition(Primitive2DDecompositionVisitor& 
rVisitor, const geometry::ViewInformation2D& rViewInformation) const override;
-
-            /// provide unique ID
-            virtual sal_uInt32 getPrimitive2DID() const override;
-        };
+class DRAWINGLAYER_DLLPUBLIC ShadowPrimitive2D final : public 
BufferedDecompositionGroupPrimitive2D
+{
+private:
+    /// the shadow transformation, normally just an offset
+    basegfx::B2DHomMatrix maShadowTransform;
+
+    /// the shadow color to which all geometry is to be forced
+    basegfx::BColor maShadowColor;
+
+    /// the blur radius of the shadow
+    double mfShadowBlur;
+
+    /// last used DiscreteBlurRadius and ClippedRange
+    double mfLastDiscreteBlurRadius;
+    basegfx::B2DRange maLastClippedRange;
+
+    /// helpers
+    void getFullyEmbeddedShadowPrimitives(Primitive2DContainer& rContainer) 
const;
+    bool prepareValuesAndcheckValidity(basegfx::B2DRange& rRange, 
basegfx::B2DRange& rClippedRange,
+                                       basegfx::B2DVector& rDiscreteSize,
+                                       double& rfDiscreteBlurRadius,
+                                       const geometry::ViewInformation2D& 
rViewInformation) const;
+
+protected:
+    /** method which is to be used to implement the local decomposition of a 
2D primitive. */
+    virtual void
+    create2DDecomposition(Primitive2DContainer& rContainer,
+                          const geometry::ViewInformation2D& rViewInformation) 
const override;
+
+public:
+    /// constructor
+    ShadowPrimitive2D(basegfx::B2DHomMatrix aShadowTransform, const 
basegfx::BColor& rShadowColor,
+                      double fShadowBlur, Primitive2DContainer&& aChildren);
+
+    /// data read access
+    const basegfx::B2DHomMatrix& getShadowTransform() const { return 
maShadowTransform; }
+    const basegfx::BColor& getShadowColor() const { return maShadowColor; }
+    double getShadowBlur() const { return mfShadowBlur; }
+
+    /// compare operator
+    virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
+
+    /// get range
+    virtual basegfx::B2DRange
+    getB2DRange(const geometry::ViewInformation2D& rViewInformation) const 
override;
+
+    ///  create decomposition
+    virtual void
+    get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor,
+                       const geometry::ViewInformation2D& rViewInformation) 
const override;
+
+    /// provide unique ID
+    virtual sal_uInt32 getPrimitive2DID() const override;
+};
 } // end of namespace drawinglayer::primitive2d
 
-
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to