summaryrefslogtreecommitdiff
path: root/drawinglayer
diff options
context:
space:
mode:
authorArmin Le Grand (allotropia) <armin.le.grand.extern@allotropia.de>2022-09-13 13:42:54 +0200
committerArmin Le Grand <Armin.Le.Grand@me.com>2022-09-14 09:54:18 +0200
commite735ad1c57cddaf17d6ffc0cf15b5e14fa63c4ad (patch)
tree194e32decaaf2aa96a12169ea3dacd749b8dffd9 /drawinglayer
parent80d3fb7ef721b5aa2de1095249557a19e3697b3b (diff)
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.Grand@me.com>
Diffstat (limited to 'drawinglayer')
-rw-r--r--drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx2
-rw-r--r--drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx42
-rw-r--r--drawinglayer/source/primitive2d/glowprimitive2d.cxx2
-rw-r--r--drawinglayer/source/primitive2d/shadowprimitive2d.cxx398
-rw-r--r--drawinglayer/source/primitive2d/softedgeprimitive2d.cxx4
-rw-r--r--drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx102
-rw-r--r--drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx4
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.cxx53
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.hxx4
9 files changed, 400 insertions, 211 deletions
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/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx
new file mode 100644
index 000000000000..61079728d841
--- /dev/null
+++ b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/alpha.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+namespace drawinglayer::primitive2d
+{
+/* Returns 8-bit alpha mask created from passed mask.
+
+ Negative fErodeDilateRadius values mean erode, positive - dilate.
+ nTransparency defines minimal transparency level.
+*/
+AlphaMask ProcessAndBlurAlphaMask(const Bitmap& rMask, double fErodeDilateRadius,
+ double fBlurRadius, sal_uInt8 nTransparency,
+ bool bConvertTo1Bit = true);
+
+drawinglayer::geometry::ViewInformation2D
+expandB2DRangeAtViewInformation2D(const drawinglayer::geometry::ViewInformation2D& rViewInfo,
+ double nAmount);
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
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);