summaryrefslogtreecommitdiff
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
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>
-rw-r--r--drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx2
-rw-r--r--drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx (renamed from include/drawinglayer/primitive2d/GlowSoftEgdeShadowTools.hxx)0
-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
-rw-r--r--include/drawinglayer/primitive2d/glowprimitive2d.hxx1
-rw-r--r--include/drawinglayer/primitive2d/shadowprimitive2d.hxx98
11 files changed, 414 insertions, 254 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/include/drawinglayer/primitive2d/GlowSoftEgdeShadowTools.hxx b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx
index 61079728d841..61079728d841 100644
--- a/include/drawinglayer/primitive2d/GlowSoftEgdeShadowTools.hxx
+++ b/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: */