summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Kaganski <mike.kaganski@collabora.com>2020-05-12 20:05:54 +0300
committerMike Kaganski <mike.kaganski@collabora.com>2020-05-13 23:29:29 +0200
commitcbc13ac0624685582ebd4634812681274db803aa (patch)
treef9a5e856161f4ea5290cc31ea892843a75d8bcc0
parent4907531966880f2bb4bb14b1c159865909000842 (diff)
tdf#49247: draw soft edges
This factors out the common code for blurring used both in glow and soft edges into ProcessAndBlurAlphaMask. Also this reverts commit a98bdbae459ad7341bf7f484c402e77e4062cd16, since its use was removed from VclPixelProcessor2D. Change-Id: Icd7fdb06bef3932ff3b9ce7e283b515b15d246a5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/94087 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
-rw-r--r--drawinglayer/Library_drawinglayer.mk1
-rw-r--r--drawinglayer/source/primitive2d/Tools.cxx2
-rw-r--r--drawinglayer/source/primitive2d/softedgeprimitive2d.cxx69
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.cxx160
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.hxx2
-rw-r--r--include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx3
-rw-r--r--include/drawinglayer/primitive2d/softedgeprimitive2d.hxx51
-rw-r--r--include/vcl/BitmapBasicMorphologyFilter.hxx11
-rw-r--r--include/vcl/BitmapColorReplaceFilter.hxx44
-rw-r--r--svx/inc/sdr/attribute/sdreffectstextattribute.hxx7
-rw-r--r--svx/inc/sdr/attribute/sdrlineeffectstextattribute.hxx3
-rw-r--r--svx/inc/sdr/attribute/sdrlinefilleffectstextattribute.hxx3
-rw-r--r--svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx4
-rw-r--r--svx/source/sdr/attribute/sdreffectstextattribute.cxx15
-rw-r--r--svx/source/sdr/attribute/sdrlineeffectstextattribute.cxx5
-rw-r--r--svx/source/sdr/attribute/sdrlinefilleffectstextattribute.cxx5
-rw-r--r--svx/source/sdr/primitive2d/sdrattributecreator.cxx25
-rw-r--r--svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx7
-rw-r--r--svx/source/sdr/primitive2d/sdrdecompositiontools.cxx11
-rw-r--r--svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx7
-rw-r--r--vcl/Library_vcl.mk1
-rw-r--r--vcl/source/bitmap/BitmapBasicMorphologyFilter.cxx218
-rw-r--r--vcl/source/bitmap/BitmapColorReplaceFilter.cxx45
23 files changed, 437 insertions, 262 deletions
diff --git a/drawinglayer/Library_drawinglayer.mk b/drawinglayer/Library_drawinglayer.mk
index 2a0f1030a789..24b8055836d0 100644
--- a/drawinglayer/Library_drawinglayer.mk
+++ b/drawinglayer/Library_drawinglayer.mk
@@ -115,6 +115,7 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\
drawinglayer/source/primitive2d/sceneprimitive2d \
drawinglayer/source/primitive2d/sdrdecompositiontools2d \
drawinglayer/source/primitive2d/shadowprimitive2d \
+ drawinglayer/source/primitive2d/softedgeprimitive2d \
drawinglayer/source/primitive2d/structuretagprimitive2d \
drawinglayer/source/primitive2d/svggradientprimitive2d \
drawinglayer/source/primitive2d/textbreakuphelper \
diff --git a/drawinglayer/source/primitive2d/Tools.cxx b/drawinglayer/source/primitive2d/Tools.cxx
index 2a8b8239a569..7db3a94c8d04 100644
--- a/drawinglayer/source/primitive2d/Tools.cxx
+++ b/drawinglayer/source/primitive2d/Tools.cxx
@@ -230,6 +230,8 @@ OUString idToString(sal_uInt32 nId)
return "PAGEHIERARCHY";
case PRIMITIVE2D_ID_GLOWPRIMITIVE2D:
return "GLOWPRIMITIVE";
+ case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
+ return "SOFTEDGEPRIMITIVE";
default:
return OUString::number((nId >> 16) & 0xFF) + "|" + OUString::number(nId & 0xFF);
}
diff --git a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
new file mode 100644
index 000000000000..4c5b1b2c6102
--- /dev/null
+++ b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
@@ -0,0 +1,69 @@
+/* -*- 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 .
+ */
+
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
+
+namespace drawinglayer::primitive2d
+{
+SoftEdgePrimitive2D::SoftEdgePrimitive2D(double fRadius, const Primitive2DContainer& rChildren)
+ : GroupPrimitive2D(rChildren)
+ , mfRadius(fRadius)
+{
+}
+
+bool SoftEdgePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (GroupPrimitive2D::operator==(rPrimitive))
+ {
+ auto& rCompare = static_cast<const SoftEdgePrimitive2D&>(rPrimitive);
+ return getRadius() == rCompare.getRadius();
+ }
+
+ return false;
+}
+
+void SoftEdgePrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (getChildren().empty())
+ return;
+
+ if (!mbInMaskGeneration)
+ {
+ GroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ return;
+ }
+
+ // create a modifiedColorPrimitive containing the *black* color and the content. Using black
+ // on white allows creating useful mask in VclPixelProcessor2D::processSoftEdgePrimitive2D.
+ basegfx::BColorModifierSharedPtr aBColorModifier
+ = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor());
+
+ const Primitive2DReference xRef(new ModifiedColorPrimitive2D(getChildren(), aBColorModifier));
+ rVisitor.append(xRef);
+}
+
+ImplPrimitive2DIDBlock(SoftEdgePrimitive2D, PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index 4f7d6a22f723..0e74fe9bf2aa 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -24,9 +24,7 @@
#include <sal/log.hxx>
#include <tools/stream.hxx>
#include <vcl/BitmapBasicMorphologyFilter.hxx>
-#include <vcl/BitmapColorReplaceFilter.hxx>
#include <vcl/BitmapFilterStackBlur.hxx>
-#include <vcl/BitmapMonochromeFilter.hxx>
#include <vcl/outdev.hxx>
#include <vcl/dibtools.hxx>
#include <vcl/hatch.hxx>
@@ -62,6 +60,7 @@
#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
#include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
+#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
#include <com/sun/star/awt/XWindow2.hpp>
#include <com/sun/star/awt/XControl.hpp>
@@ -387,6 +386,12 @@ void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitiv
static_cast<const drawinglayer::primitive2d::GlowPrimitive2D&>(rCandidate));
break;
}
+ case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
+ {
+ processSoftEdgePrimitive2D(
+ static_cast<const drawinglayer::primitive2d::SoftEdgePrimitive2D&>(rCandidate));
+ break;
+ }
default:
{
SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString(
@@ -911,6 +916,58 @@ void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrim
}
}
+namespace
+{
+/* Returns 8-bit alpha mask created from passed mask. The result may be scaled down; it's
+ expected that it will be automatically scaled up back when applied to the bitmap.
+
+ Negative fErodeDilateRadius values mean erode, positive - dilate.
+ nTransparency defines minimal transparency level.
+*/
+AlphaMask ProcessAndBlurAlphaMask(const Bitmap& rBWMask, double fErodeDilateRadius,
+ double fBlurRadius, sal_uInt8 nTransparency)
+{
+ // Only completely white pixels on the initial mask must be considered for transparency. Any
+ // other color must be treated as black. This creates 1-bit B&W bitmap.
+ BitmapEx mask(rBWMask.CreateMask(COL_WHITE));
+
+ // Scaling down increases performance without noticeable quality loss. Additionally,
+ // current blur implementation can only handle blur radius between 2 and 254.
+ Size aSize = mask.GetSizePixel();
+ double fScale = 1.0;
+ while (fBlurRadius > 254 || aSize.Height() > 1000 || aSize.Width() > 1000)
+ {
+ fScale /= 2;
+ fBlurRadius /= 2;
+ fErodeDilateRadius /= 2;
+ aSize.setHeight(aSize.Height() / 2);
+ aSize.setWidth(aSize.Width() / 2);
+ }
+
+ // BmpScaleFlag::Fast is important for following color replacement
+ mask.Scale(fScale, fScale, BmpScaleFlag::Fast);
+
+ if (fErodeDilateRadius > 0)
+ BitmapFilter::Filter(mask, BitmapDilateFilter(fErodeDilateRadius));
+ else if (fErodeDilateRadius < 0)
+ BitmapFilter::Filter(mask, BitmapErodeFilter(-fErodeDilateRadius, 0xFF));
+
+ if (nTransparency)
+ {
+ const Color aTransparency(nTransparency, nTransparency, nTransparency);
+ mask.Replace(COL_BLACK, aTransparency);
+ }
+
+ // We need 8-bit grey mask for blurring
+ mask.Convert(BmpConversion::N8BitGreys);
+
+ // calculate blurry effect
+ BitmapFilter::Filter(mask, BitmapFilterStackBlur(fBlurRadius));
+
+ return AlphaMask(mask.GetBitmap());
+}
+}
+
void VclPixelProcessor2D::processGlowPrimitive2D(const primitive2d::GlowPrimitive2D& rCandidate)
{
basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
@@ -918,7 +975,14 @@ void VclPixelProcessor2D::processGlowPrimitive2D(const primitive2d::GlowPrimitiv
basegfx::B2DVector aGlowRadiusVector(rCandidate.getGlowRadius(), 0);
// Calculate the pixel size of glow radius in current transformation
aGlowRadiusVector *= maCurrentTransformation;
- const double fGlowRadius = aGlowRadiusVector.getLength();
+ // Glow radius is the size of the halo from each side of the object. The halo is the
+ // border of glow color that fades from glow transparency level to fully transparent
+ // When blurring a sharp boundary (our case), it gets 50% of original intensity, and
+ // fades to both sides by the blur radius; thus blur radius is half of glow radius.
+ const double fBlurRadius = aGlowRadiusVector.getLength() / 2;
+ // Consider glow transparency (initial transparency near the object edge)
+ const sal_uInt8 nTransparency = rCandidate.getGlowColor().GetTransparency();
+
impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
if (aBufferDevice.isVisible())
{
@@ -933,53 +997,60 @@ void VclPixelProcessor2D::processGlowPrimitive2D(const primitive2d::GlowPrimitiv
Bitmap bitmap = mpOutputDevice->GetBitmap(Point(aRange.getMinX(), aRange.getMinY()),
Size(aRange.getWidth(), aRange.getHeight()));
- BitmapEx mask(bitmap); // copy the bitmap to mask
- // Only completely transparent parts will be completely white; only those must be
- // considered white on the initial B&W alpha mask. Any other color must be treated
- // as black.
- BitmapFilter::Filter(mask, BitmapMonochromeFilter(255));
-
- // Scaling down increases performance without noticeable quality loss. Additionally,
- // current blur implementation can only handle blur radius between 2 and 254.
- Size aSize = mask.GetSizePixel();
- double fScale = 1.0;
- // Glow radius is the size of the halo from each side of the object. The halo is the
- // border of glow color that fades from glow transparency level to fully transparent
- // When blurring a sharp boundary (our case), it gets 50% of original intensity, and
- // fades to both sides by the blur radius; thus blur radius is half of glow radius.
- double fBlurRadius = fGlowRadius / 2;
- while (fBlurRadius > 254 || aSize.Height() > 1000 || aSize.Width() > 1000)
- {
- fScale /= 2;
- fBlurRadius /= 2;
- aSize.setHeight(aSize.Height() / 2);
- aSize.setWidth(aSize.Width() / 2);
- }
-
- // BmpScaleFlag::Fast is important for following color replacement
- mask.Scale(fScale, fScale, BmpScaleFlag::Fast);
-
- // Dilate the black pixels using blur radius, to make blur start at actual object margins.
- // This differentiates glow from blurry shadow; so potentially extend this function to also
- // handle blurry shadow, and conditionally skip this step
- BitmapFilter::Filter(mask, BitmapDilateFilter(fBlurRadius));
-
- // We need 8-bit grey mask for blurring
- mask.Convert(BmpConversion::N8BitGreys);
- // Consider glow transparency (initial transparency near the object edge)
- const sal_uInt8 nTransparency = rCandidate.getGlowColor().GetTransparency();
- const Color aTransparency(nTransparency, nTransparency, nTransparency);
- BitmapFilter::Filter(mask, BitmapColorReplaceFilter(COL_BLACK, aTransparency));
-
- // calculate blurry effect
- BitmapFilter::Filter(mask, BitmapFilterStackBlur(fBlurRadius));
+ AlphaMask mask = ProcessAndBlurAlphaMask(bitmap, fBlurRadius, fBlurRadius, nTransparency);
// The end result is the bitmap filled with glow color and blurred 8-bit alpha mask
const basegfx::BColor aGlowColor(
maBColorModifierStack.getModifiedColor(rCandidate.getGlowColor().getBColor()));
bitmap.Erase(Color(aGlowColor));
// alpha mask will be scaled up automatically to match bitmap
- BitmapEx result(bitmap, AlphaMask(mask.GetBitmap()));
+ BitmapEx result(bitmap, mask);
+
+ // back to old OutDev
+ mpOutputDevice = pLastOutputDevice;
+ mpOutputDevice->DrawBitmapEx(Point(aRange.getMinX(), aRange.getMinY()), result);
+ }
+ else
+ SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible");
+}
+
+void VclPixelProcessor2D::processSoftEdgePrimitive2D(
+ const primitive2d::SoftEdgePrimitive2D& rCandidate)
+{
+ basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
+ aRange.transform(maCurrentTransformation);
+ basegfx::B2DVector aRadiusVector(rCandidate.getRadius(), 0);
+ // Calculate the pixel size of soft edge radius in current transformation
+ aRadiusVector *= maCurrentTransformation;
+ // Blur radius is equal to soft edge radius
+ const double fBlurRadius = aRadiusVector.getLength();
+
+ impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
+ if (aBufferDevice.isVisible())
+ {
+ // remember last OutDev and set to content
+ OutputDevice* pLastOutputDevice = mpOutputDevice;
+ mpOutputDevice = &aBufferDevice.getContent();
+ // Processing will draw whatever geometry on white background, applying *black*
+ // replacement color
+ mpOutputDevice->Erase();
+ rCandidate.setMaskGeneration();
+ process(rCandidate);
+ rCandidate.setMaskGeneration(false);
+ Bitmap bitmap = mpOutputDevice->GetBitmap(Point(aRange.getMinX(), aRange.getMinY()),
+ Size(aRange.getWidth(), aRange.getHeight()));
+
+ AlphaMask mask = ProcessAndBlurAlphaMask(bitmap, -fBlurRadius, fBlurRadius, 0);
+
+ // The end result is the original bitmap with blurred 8-bit alpha mask
+
+ mpOutputDevice->Erase();
+ process(rCandidate);
+ bitmap = mpOutputDevice->GetBitmap(Point(aRange.getMinX(), aRange.getMinY()),
+ Size(aRange.getWidth(), aRange.getHeight()));
+
+ // alpha mask will be scaled up automatically to match bitmap
+ BitmapEx result(bitmap, mask);
// back to old OutDev
mpOutputDevice = pLastOutputDevice;
@@ -988,6 +1059,7 @@ void VclPixelProcessor2D::processGlowPrimitive2D(const primitive2d::GlowPrimitiv
else
SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible");
}
+
} // end of namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
index fd72efe41d16..e78d1f08225a 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
@@ -40,6 +40,7 @@ class FillHatchPrimitive2D;
class BackgroundColorPrimitive2D;
class BorderLinePrimitive2D;
class GlowPrimitive2D;
+class SoftEdgePrimitive2D;
}
namespace drawinglayer::processor2d
@@ -95,6 +96,7 @@ class VclPixelProcessor2D final : public VclProcessor2D
void processInvertPrimitive2D(const primitive2d::BasePrimitive2D& rCandidate);
void processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate);
void processGlowPrimitive2D(const primitive2d::GlowPrimitive2D& rCandidate);
+ void processSoftEdgePrimitive2D(const primitive2d::SoftEdgePrimitive2D& rCandidate);
public:
/// constructor/destructor
diff --git a/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx b/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx
index 0c9aa3c0b4ba..687efb1d85ff 100644
--- a/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx
+++ b/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx
@@ -103,7 +103,8 @@
#define PRIMITIVE2D_ID_POLYPOLYGONSELECTIONPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 69)
#define PRIMITIVE2D_ID_PAGEHIERARCHYPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 70)
#define PRIMITIVE2D_ID_GLOWPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 71)
+#define PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 72)
// When you add a new primitive, please update the drawinglayer::primitive2d::idToString() function
-// in drawinglayer/source/primitive2d/baseprimitive2d.cxx.
+// in drawinglayer/source/primitive2d/Tools.cxx.
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/drawinglayer/primitive2d/softedgeprimitive2d.hxx b/include/drawinglayer/primitive2d/softedgeprimitive2d.hxx
new file mode 100644
index 000000000000..f16e50287b0e
--- /dev/null
+++ b/include/drawinglayer/primitive2d/softedgeprimitive2d.hxx
@@ -0,0 +1,51 @@
+/* -*- 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 <drawinglayer/drawinglayerdllapi.h>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
+
+namespace drawinglayer::primitive2d
+{
+class DRAWINGLAYER_DLLPUBLIC SoftEdgePrimitive2D final : public GroupPrimitive2D
+{
+private:
+ /// Soft edge size, in logical units (100ths of mm)
+ double mfRadius;
+ mutable bool mbInMaskGeneration = false;
+
+public:
+ SoftEdgePrimitive2D(double fRadius, const Primitive2DContainer& rChildren);
+
+ double getRadius() const { return mfRadius; }
+
+ void setMaskGeneration(bool bVal = true) const { mbInMaskGeneration = bVal; }
+
+ virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
+
+ virtual void
+ get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const override;
+
+ virtual sal_uInt32 getPrimitive2DID() const override;
+};
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/BitmapBasicMorphologyFilter.hxx b/include/vcl/BitmapBasicMorphologyFilter.hxx
index ade82adaa957..c7999dc28819 100644
--- a/include/vcl/BitmapBasicMorphologyFilter.hxx
+++ b/include/vcl/BitmapBasicMorphologyFilter.hxx
@@ -26,6 +26,7 @@ class VCL_DLLPUBLIC BitmapBasicMorphologyFilter : public BitmapFilter
{
public:
BitmapBasicMorphologyFilter(BasicMorphologyOp op, sal_Int32 nRadius);
+ BitmapBasicMorphologyFilter(BasicMorphologyOp op, sal_Int32 nRadius, sal_uInt8 nValueOutside);
virtual ~BitmapBasicMorphologyFilter();
virtual BitmapEx execute(BitmapEx const& rBitmap) const override;
@@ -35,6 +36,8 @@ private:
BasicMorphologyOp m_eOp;
sal_Int32 m_nRadius;
+ sal_uInt8 m_nValueOutside = 0;
+ bool m_bUseValueOutside = false;
};
class BitmapErodeFilter : public BitmapBasicMorphologyFilter
@@ -44,6 +47,10 @@ public:
: BitmapBasicMorphologyFilter(BasicMorphologyOp::erode, nRadius)
{
}
+ BitmapErodeFilter(sal_Int32 nRadius, sal_uInt8 nValueOutside)
+ : BitmapBasicMorphologyFilter(BasicMorphologyOp::erode, nRadius, nValueOutside)
+ {
+ }
};
class BitmapDilateFilter : public BitmapBasicMorphologyFilter
@@ -53,6 +60,10 @@ public:
: BitmapBasicMorphologyFilter(BasicMorphologyOp::dilate, nRadius)
{
}
+ BitmapDilateFilter(sal_Int32 nRadius, sal_uInt8 nValueOutside)
+ : BitmapBasicMorphologyFilter(BasicMorphologyOp::dilate, nRadius, nValueOutside)
+ {
+ }
};
#endif // INCLUDED_VCL_BITMAPBASICMORPHOLOGYFILTER_HXX
diff --git a/include/vcl/BitmapColorReplaceFilter.hxx b/include/vcl/BitmapColorReplaceFilter.hxx
deleted file mode 100644
index caeac83d0fda..000000000000
--- a/include/vcl/BitmapColorReplaceFilter.hxx
+++ /dev/null
@@ -1,44 +0,0 @@
-/* -*- 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/.
- *
- */
-
-#ifndef INCLUDED_INCLUDE_VCL_BITMAPCOLORREPLACEFILTER_HXX
-#define INCLUDED_INCLUDE_VCL_BITMAPCOLORREPLACEFILTER_HXX
-
-#include <vcl/BitmapFilter.hxx>
-
-class VCL_DLLPUBLIC BitmapColorReplaceFilter final : public BitmapFilter
-{
-public:
- /** Replaces a color with another by changing pixels, without shortcuts like modifying palette
- (that is how it's different from using Bitmap::Replace).
-
- @param cReplaceWhat
- Color that will be replaced.
-
- @param cReplaceTo
- New color that will replace cReplaceWhat.
-
- */
- BitmapColorReplaceFilter(const Color& cReplaceWhat, const Color& cReplaceTo)
- : m_aReplaceWhat(cReplaceWhat)
- , m_aReplaceTo(cReplaceTo)
- {
- }
-
- virtual BitmapEx execute(BitmapEx const& rBitmapEx) const override;
-
-private:
- Color m_aReplaceWhat;
- Color m_aReplaceTo;
-};
-
-#endif
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/inc/sdr/attribute/sdreffectstextattribute.hxx b/svx/inc/sdr/attribute/sdreffectstextattribute.hxx
index 1c02eda63dd2..7a209c739199 100644
--- a/svx/inc/sdr/attribute/sdreffectstextattribute.hxx
+++ b/svx/inc/sdr/attribute/sdreffectstextattribute.hxx
@@ -37,11 +37,15 @@ namespace drawinglayer
// glow effect
SdrGlowAttribute maGlow;
+ // soft edge
+ sal_Int32 mnSoftEdgeRadius = 0;
+
public:
SdrEffectsTextAttribute(
const SdrShadowAttribute& rShadow,
const SdrTextAttribute& rTextAttribute,
- const SdrGlowAttribute& rGlow);
+ const SdrGlowAttribute& rGlow,
+ sal_Int32 nSoftEdgeRadius);
SdrEffectsTextAttribute();
SdrEffectsTextAttribute(const SdrEffectsTextAttribute& rCandidate);
SdrEffectsTextAttribute& operator=(const SdrEffectsTextAttribute& rCandidate);
@@ -56,6 +60,7 @@ namespace drawinglayer
const SdrShadowAttribute& getShadow() const { return maShadow; }
const SdrTextAttribute& getText() const { return maTextAttribute; }
const SdrGlowAttribute& getGlow() const { return maGlow; }
+ sal_Int32 getSoftEdgeRadius() const { return mnSoftEdgeRadius; }
};
} // end of namespace attribute
} // end of namespace drawinglayer
diff --git a/svx/inc/sdr/attribute/sdrlineeffectstextattribute.hxx b/svx/inc/sdr/attribute/sdrlineeffectstextattribute.hxx
index ce9e0223c201..c53650bfcff2 100644
--- a/svx/inc/sdr/attribute/sdrlineeffectstextattribute.hxx
+++ b/svx/inc/sdr/attribute/sdrlineeffectstextattribute.hxx
@@ -41,7 +41,8 @@ namespace drawinglayer
const SdrLineStartEndAttribute& rLineStartEnd,
const SdrShadowAttribute& rShadow,
const SdrTextAttribute& rTextAttribute,
- const SdrGlowAttribute& rGlow);
+ const SdrGlowAttribute& rGlow,
+ sal_Int32 nSoftEdgeRadius);
SdrLineEffectsTextAttribute();
SdrLineEffectsTextAttribute(const SdrLineEffectsTextAttribute& rCandidate);
SdrLineEffectsTextAttribute& operator=(const SdrLineEffectsTextAttribute& rCandidate);
diff --git a/svx/inc/sdr/attribute/sdrlinefilleffectstextattribute.hxx b/svx/inc/sdr/attribute/sdrlinefilleffectstextattribute.hxx
index d590378bf4c5..7fb25a4f5936 100644
--- a/svx/inc/sdr/attribute/sdrlinefilleffectstextattribute.hxx
+++ b/svx/inc/sdr/attribute/sdrlinefilleffectstextattribute.hxx
@@ -42,7 +42,8 @@ namespace drawinglayer
const SdrShadowAttribute& rShadow,
const FillGradientAttribute& rFillFloatTransGradient,
const SdrTextAttribute& rTextAttribute,
- const SdrGlowAttribute& rGlow);
+ const SdrGlowAttribute& rGlow,
+ sal_Int32 nSoftEdgeRadius);
SdrLineFillEffectsTextAttribute();
SdrLineFillEffectsTextAttribute(const SdrLineFillEffectsTextAttribute& rCandidate);
SdrLineFillEffectsTextAttribute& operator=(const SdrLineFillEffectsTextAttribute& rCandidate);
diff --git a/svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx b/svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx
index d991eb4ed2f3..e619206303ca 100644
--- a/svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx
+++ b/svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx
@@ -78,6 +78,10 @@ namespace drawinglayer
const Primitive2DContainer& rContent,
const attribute::SdrGlowAttribute& rGlow);
+ Primitive2DContainer SVXCORE_DLLPUBLIC createEmbeddedSoftEdgePrimitive(
+ const Primitive2DContainer& rContent,
+ sal_Int32 nRadius);
+
} // end of namespace primitive2d
} // end of namespace drawinglayer
diff --git a/svx/source/sdr/attribute/sdreffectstextattribute.cxx b/svx/source/sdr/attribute/sdreffectstextattribute.cxx
index 05775f0beffa..acf9a2ee5daf 100644
--- a/svx/source/sdr/attribute/sdreffectstextattribute.cxx
+++ b/svx/source/sdr/attribute/sdreffectstextattribute.cxx
@@ -26,10 +26,12 @@ namespace drawinglayer::attribute
SdrEffectsTextAttribute::SdrEffectsTextAttribute(
const SdrShadowAttribute& rShadow,
const SdrTextAttribute& rTextAttribute,
- const SdrGlowAttribute& rGlow)
+ const SdrGlowAttribute& rGlow,
+ sal_Int32 nSoftEdgeRadius)
: maShadow(rShadow),
maTextAttribute(rTextAttribute),
- maGlow(rGlow)
+ maGlow(rGlow),
+ mnSoftEdgeRadius(nSoftEdgeRadius)
{
}
@@ -42,7 +44,8 @@ namespace drawinglayer::attribute
SdrEffectsTextAttribute::SdrEffectsTextAttribute(const SdrEffectsTextAttribute& rCandidate)
: maShadow(rCandidate.getShadow()),
maTextAttribute(rCandidate.getText()),
- maGlow(rCandidate.maGlow)
+ maGlow(rCandidate.maGlow),
+ mnSoftEdgeRadius(rCandidate.mnSoftEdgeRadius)
{
}
@@ -51,6 +54,7 @@ namespace drawinglayer::attribute
maShadow = rCandidate.getShadow();
maTextAttribute = rCandidate.getText();
maGlow = rCandidate.maGlow;
+ mnSoftEdgeRadius = rCandidate.mnSoftEdgeRadius;
return *this;
}
@@ -58,14 +62,15 @@ namespace drawinglayer::attribute
bool SdrEffectsTextAttribute::isDefault() const
{
return (getShadow().isDefault()
- && getText().isDefault() && maGlow.isDefault());
+ && getText().isDefault() && maGlow.isDefault() && getSoftEdgeRadius() == 0);
}
bool SdrEffectsTextAttribute::operator==(const SdrEffectsTextAttribute& rCandidate) const
{
return (getShadow() == rCandidate.getShadow()
&& getText() == rCandidate.getText()
- && getGlow() == rCandidate.getGlow());
+ && getGlow() == rCandidate.getGlow()
+ && getSoftEdgeRadius() == rCandidate.getSoftEdgeRadius());
}
} // end of namespace
diff --git a/svx/source/sdr/attribute/sdrlineeffectstextattribute.cxx b/svx/source/sdr/attribute/sdrlineeffectstextattribute.cxx
index a7a5e265427c..d2add96388f2 100644
--- a/svx/source/sdr/attribute/sdrlineeffectstextattribute.cxx
+++ b/svx/source/sdr/attribute/sdrlineeffectstextattribute.cxx
@@ -28,8 +28,9 @@ namespace drawinglayer::attribute
const SdrLineStartEndAttribute& rLineStartEnd,
const SdrShadowAttribute& rShadow,
const SdrTextAttribute& rTextAttribute,
- const SdrGlowAttribute& rGlow)
- : SdrEffectsTextAttribute(rShadow, rTextAttribute, rGlow),
+ const SdrGlowAttribute& rGlow,
+ sal_Int32 nSoftEdgeRadius)
+ : SdrEffectsTextAttribute(rShadow, rTextAttribute, rGlow, nSoftEdgeRadius),
maLine(rLine),
maLineStartEnd(rLineStartEnd)
{
diff --git a/svx/source/sdr/attribute/sdrlinefilleffectstextattribute.cxx b/svx/source/sdr/attribute/sdrlinefilleffectstextattribute.cxx
index 6200b7b51308..c9199435fed4 100644
--- a/svx/source/sdr/attribute/sdrlinefilleffectstextattribute.cxx
+++ b/svx/source/sdr/attribute/sdrlinefilleffectstextattribute.cxx
@@ -30,8 +30,9 @@ namespace drawinglayer::attribute
const SdrShadowAttribute& rShadow,
const FillGradientAttribute& rFillFloatTransGradient,
const SdrTextAttribute& rTextAttribute,
- const SdrGlowAttribute& rGlow)
- : SdrLineEffectsTextAttribute(rLine, rLineStartEnd, rShadow, rTextAttribute, rGlow),
+ const SdrGlowAttribute& rGlow,
+ sal_Int32 nSoftEdgeRadius)
+ : SdrLineEffectsTextAttribute(rLine, rLineStartEnd, rShadow, rTextAttribute, rGlow, nSoftEdgeRadius),
maFill(rFill),
maFillFloatTransGradient(rFillFloatTransGradient)
{
diff --git a/svx/source/sdr/primitive2d/sdrattributecreator.cxx b/svx/source/sdr/primitive2d/sdrattributecreator.cxx
index 5f036976d90a..41452ecf4826 100644
--- a/svx/source/sdr/primitive2d/sdrattributecreator.cxx
+++ b/svx/source/sdr/primitive2d/sdrattributecreator.cxx
@@ -227,6 +227,11 @@ namespace drawinglayer
attribute::SdrGlowAttribute glowAttr{ nRadius, aColor };
return glowAttr;
}
+
+ sal_Int32 getSoftEdgeRadius(const SfxItemSet& rSet)
+ {
+ return rSet.Get(SDRATTR_SOFTEDGE_RAD).GetValue();
+ }
} // end of anonymous namespace
} // end of namespace drawinglayer
@@ -748,8 +753,9 @@ namespace drawinglayer::primitive2d
// try shadow
const attribute::SdrShadowAttribute aShadow(createNewSdrShadowAttribute(rSet));
const attribute::SdrGlowAttribute aGlow(createNewSdrGlowAttribute(rSet));
+ const sal_Int32 nSoftEdgeRadius(getSoftEdgeRadius(rSet));
- return attribute::SdrEffectsTextAttribute(aShadow, aText, aGlow);
+ return attribute::SdrEffectsTextAttribute(aShadow, aText, aGlow, nSoftEdgeRadius);
}
attribute::SdrLineEffectsTextAttribute createNewSdrLineEffectsTextAttribute(
@@ -792,9 +798,11 @@ namespace drawinglayer::primitive2d
{
// try shadow
const attribute::SdrShadowAttribute aShadow(createNewSdrShadowAttribute(rSet));
- attribute::SdrGlowAttribute aGlow = createNewSdrGlowAttribute(rSet);
+ const attribute::SdrGlowAttribute aGlow = createNewSdrGlowAttribute(rSet);
+ const sal_Int32 nSoftEdgeRadius(getSoftEdgeRadius(rSet));
- return attribute::SdrLineEffectsTextAttribute(aLine, aLineStartEnd, aShadow, aText, aGlow);
+ return attribute::SdrLineEffectsTextAttribute(aLine, aLineStartEnd, aShadow, aText,
+ aGlow, nSoftEdgeRadius);
}
return attribute::SdrLineEffectsTextAttribute();
@@ -853,13 +861,16 @@ namespace drawinglayer::primitive2d
if(bHasContent || !aLine.isDefault() || !aFill.isDefault() || !aText.isDefault())
{
// try shadow
- attribute::SdrShadowAttribute aShadow = createNewSdrShadowAttribute(rSet);
+ const attribute::SdrShadowAttribute aShadow = createNewSdrShadowAttribute(rSet);
// glow
- attribute::SdrGlowAttribute aGlow = createNewSdrGlowAttribute(rSet);
+ const attribute::SdrGlowAttribute aGlow = createNewSdrGlowAttribute(rSet);
+
+ const sal_Int32 nSoftEdgeRadius(getSoftEdgeRadius(rSet));
- return attribute::SdrLineFillEffectsTextAttribute(
- aLine, aFill, aLineStartEnd, aShadow, aFillFloatTransGradient, aText, aGlow);
+ return attribute::SdrLineFillEffectsTextAttribute(aLine, aFill, aLineStartEnd,
+ aShadow, aFillFloatTransGradient,
+ aText, aGlow, nSoftEdgeRadius);
}
return attribute::SdrLineFillEffectsTextAttribute();
diff --git a/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx
index 5de10ebe523a..f237369e3562 100644
--- a/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx
+++ b/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx
@@ -37,6 +37,13 @@ namespace drawinglayer::primitive2d
{
Primitive2DContainer aRetval(getSubPrimitives());
+ // Soft edges should be before text, since text is not affected by soft edges
+ if (!aRetval.empty() && getSdrSTAttribute().getSoftEdgeRadius())
+ {
+ aRetval = createEmbeddedSoftEdgePrimitive(aRetval,
+ getSdrSTAttribute().getSoftEdgeRadius());
+ }
+
// add text
if(!getSdrSTAttribute().getText().isDefault())
{
diff --git a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
index c5d0d6c0bdc4..59b38300d375 100644
--- a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
+++ b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
@@ -23,6 +23,7 @@
#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
@@ -558,6 +559,16 @@ namespace drawinglayer::primitive2d
return aRetval;
}
+ Primitive2DContainer createEmbeddedSoftEdgePrimitive(const Primitive2DContainer& rContent,
+ sal_Int32 nRadius)
+ {
+ if (rContent.empty() || !nRadius)
+ return rContent;
+ Primitive2DContainer aRetval(1);
+ aRetval[0] = Primitive2DReference(new SoftEdgePrimitive2D(nRadius, rContent));
+ return aRetval;
+ }
+
} // end of namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx
index f4b1848eb31e..8f8925201e26 100644
--- a/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx
+++ b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx
@@ -97,6 +97,13 @@ namespace drawinglayer::primitive2d
}
}
+ // Soft edges should be before text, since text is not affected by soft edges
+ if (!aRetval.empty() && getSdrLFSTAttribute().getSoftEdgeRadius())
+ {
+ aRetval = createEmbeddedSoftEdgePrimitive(
+ aRetval, getSdrLFSTAttribute().getSoftEdgeRadius());
+ }
+
// add text
if(!getSdrLFSTAttribute().getText().isDefault())
{
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 64fe3ecb5edf..65f65b525f4c 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -333,7 +333,6 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
vcl/source/bitmap/bitmapfilter \
vcl/source/bitmap/BitmapAlphaClampFilter \
vcl/source/bitmap/BitmapBasicMorphologyFilter \
- vcl/source/bitmap/BitmapColorReplaceFilter \
vcl/source/bitmap/BitmapMonochromeFilter \
vcl/source/bitmap/BitmapSmoothenFilter \
vcl/source/bitmap/BitmapLightenFilter \
diff --git a/vcl/source/bitmap/BitmapBasicMorphologyFilter.cxx b/vcl/source/bitmap/BitmapBasicMorphologyFilter.cxx
index b570b33e6495..fe4f63f90d16 100644
--- a/vcl/source/bitmap/BitmapBasicMorphologyFilter.cxx
+++ b/vcl/source/bitmap/BitmapBasicMorphologyFilter.cxx
@@ -26,11 +26,16 @@ struct FilterSharedData
BitmapReadAccess* mpReadAccess;
BitmapWriteAccess* mpWriteAccess;
long mnRadius;
+ sal_uInt8 mnOutsideVal;
+ Color maOutsideColor;
- FilterSharedData(BitmapReadAccess* pReadAccess, BitmapWriteAccess* pWriteAccess, long nRadius)
- : mpReadAccess(pReadAccess)
- , mpWriteAccess(pWriteAccess)
+ FilterSharedData(Bitmap::ScopedReadAccess& rReadAccess, BitmapScopedWriteAccess& rWriteAccess,
+ long nRadius, sal_uInt8 nOutsideVal)
+ : mpReadAccess(rReadAccess.get())
+ , mpWriteAccess(rWriteAccess.get())
, mnRadius(nRadius)
+ , mnOutsideVal(nOutsideVal)
+ , maOutsideColor(nOutsideVal, nOutsideVal, nOutsideVal, nOutsideVal)
{
}
};
@@ -41,130 +46,116 @@ struct ErodeOp
{
static sal_uInt8 apply(sal_uInt8 v1, sal_uInt8 v2) { return std::max(v1, v2); }
static constexpr sal_uInt8 initVal = 0;
- static constexpr Color initColor = COL_BLACK;
};
struct DilateOp
{
static sal_uInt8 apply(sal_uInt8 v1, sal_uInt8 v2) { return std::min(v1, v2); }
- static constexpr sal_uInt8 initVal{ SAL_MAX_UINT8 };
- static constexpr Color initColor = COL_TRANSPARENT;
+ static constexpr sal_uInt8 initVal = SAL_MAX_UINT8;
};
-template <typename MorphologyOp> struct OpHelper
+// 8 bit per channel case
+
+template <typename MorphologyOp, int nComponentWidth> struct Value
{
- template <int n> static void apply(sal_uInt8 (&rResult)[n], Scanline pSource)
+ static constexpr int nWidthBytes = nComponentWidth / 8;
+ static_assert(nWidthBytes * 8 == nComponentWidth);
+
+ sal_uInt8 aResult[nWidthBytes];
+
+ // If we are at the start or at the end of the line, consider outside value
+ Value(FilterSharedData const& rShared, bool bLookOutside)
{
- std::transform(pSource, pSource + n, rResult, rResult, MorphologyOp::apply);
+ std::fill_n(aResult, nWidthBytes,
+ bLookOutside ? rShared.mnOutsideVal : MorphologyOp::initVal);
}
- static void apply(Color& rResult, const Color& rSource)
+ void apply(BitmapReadAccess* pReadAccess, long x, long y, sal_uInt8* pHint = nullptr)
{
- rResult = Color(MorphologyOp::apply(rSource.GetTransparency(), rResult.GetTransparency()),
- MorphologyOp::apply(rSource.GetRed(), rResult.GetRed()),
- MorphologyOp::apply(rSource.GetGreen(), rResult.GetGreen()),
- MorphologyOp::apply(rSource.GetBlue(), rResult.GetBlue()));
+ sal_uInt8* pSource = (pHint ? pHint : pReadAccess->GetScanline(y)) + nWidthBytes * x;
+ std::transform(pSource, pSource + nWidthBytes, aResult, aResult, MorphologyOp::apply);
}
- template <int n> static void init(sal_uInt8 (&rResult)[n])
+ void copy(BitmapWriteAccess* pWriteAccess, long x, long y, sal_uInt8* pHint = nullptr)
{
- std::fill_n(rResult, n, MorphologyOp::initVal);
+ sal_uInt8* pDest = (pHint ? pHint : pWriteAccess->GetScanline(y)) + nWidthBytes * x;
+ std::copy_n(aResult, nWidthBytes, pDest);
}
};
-// 8 bit per channel case
+// Partial specializations for nComponentWidth == 0, using access' GetColor/SetPixel
-template <typename MorphologyOp, int nComponentWidth> struct pass
+template <typename MorphologyOp> struct Value<MorphologyOp, 0>
{
- static constexpr int nWidthBytes = nComponentWidth / 8;
- static_assert(nWidthBytes * 8 == nComponentWidth);
- static void Horizontal(FilterSharedData const& rShared, const long nStart, const long nEnd)
- {
- BitmapReadAccess* pReadAccess = rShared.mpReadAccess;
- BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess;
-
- const long nWidth = pReadAccess->Width();
- const long nLastIndex = nWidth - 1;
+ static constexpr Color initColor{ MorphologyOp::initVal, MorphologyOp::initVal,
+ MorphologyOp::initVal, MorphologyOp::initVal };
- const long nRadius = rShared.mnRadius;
+ Color aResult;
- for (long y = nStart; y <= nEnd; y++)
- {
- const Scanline pScanline = pReadAccess->GetScanline(y);
- for (long x = 0; x < nWidth; x++)
- {
- // This processes [nRadius * 2 + 1] pixels of source per resulting pixel
- // TODO: try to optimize this to not process same pixels repeatedly
- sal_uInt8 aResult[nWidthBytes];
- OpHelper<MorphologyOp>::init(aResult);
- const long iMax = std::min(x + nRadius, nLastIndex);
- for (long i = std::max(x - nRadius, 0L); i <= iMax; ++i)
- OpHelper<MorphologyOp>::apply(aResult, pScanline + nWidthBytes * i);
-
- Scanline pDestinationPointer = pWriteAccess->GetScanline(y) + nWidthBytes * x;
- for (const auto& val : aResult)
- *pDestinationPointer++ = val;
- }
- }
+ // If we are at the start or at the end of the line, consider outside value
+ Value(FilterSharedData const& rShared, bool bLookOutside)
+ : aResult(bLookOutside ? rShared.maOutsideColor : initColor)
+ {
}
- static void Vertical(FilterSharedData const& rShared, const long nStart, const long nEnd)
+ void apply(BitmapReadAccess* pReadAccess, long x, long y, sal_uInt8* /*pHint*/ = nullptr)
{
- BitmapReadAccess* pReadAccess = rShared.mpReadAccess;
- BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess;
-
- const long nHeight = pReadAccess->Height();
- const long nLastIndex = nHeight - 1;
-
- const long nRadius = rShared.mnRadius;
+ const auto& rSource = pReadAccess->GetColor(y, x);
+ aResult = Color(MorphologyOp::apply(rSource.GetTransparency(), aResult.GetTransparency()),
+ MorphologyOp::apply(rSource.GetRed(), aResult.GetRed()),
+ MorphologyOp::apply(rSource.GetGreen(), aResult.GetGreen()),
+ MorphologyOp::apply(rSource.GetBlue(), aResult.GetBlue()));
+ }
- for (long x = nStart; x <= nEnd; x++)
- {
- for (long y = 0; y < nHeight; y++)
- {
- // This processes [nRadius * 2 + 1] pixels of source per resulting pixel
- // TODO: try to optimize this to not process same pixels repeatedly
- sal_uInt8 aResult[nWidthBytes];
- OpHelper<MorphologyOp>::init(aResult);
- const long iMax = std::min(y + nRadius, nLastIndex);
- for (long i = std::max(y - nRadius, 0L); i <= iMax; ++i)
- OpHelper<MorphologyOp>::apply(aResult,
- pReadAccess->GetScanline(i) + nWidthBytes * x);
-
- Scanline pDestinationPointer = pWriteAccess->GetScanline(y) + nWidthBytes * x;
- for (auto& val : aResult)
- *pDestinationPointer++ = val;
- }
- }
+ void copy(BitmapWriteAccess* pWriteAccess, long x, long y, sal_uInt8* /*pHint*/ = nullptr)
+ {
+ pWriteAccess->SetPixel(y, x, aResult);
}
};
-// Partial specializations for nComponentWidth == 0, using access' GetColor/SetPixel
+bool GetMinMax(long nCenter, long nRadius, long nMaxLimit, long& nMin, long& nMax)
+{
+ nMin = nCenter - nRadius;
+ nMax = nCenter + nRadius;
+ bool bLookOutside = false;
+ if (nMin < 0)
+ {
+ bLookOutside = true;
+ nMin = 0;
+ }
+ if (nMax > nMaxLimit)
+ {
+ bLookOutside = true;
+ nMax = nMaxLimit;
+ }
+ return bLookOutside;
+}
-template <typename MorphologyOp> struct pass<MorphologyOp, 0>
+template <typename MorphologyOp, int nComponentWidth> struct pass
{
static void Horizontal(FilterSharedData const& rShared, const long nStart, const long nEnd)
{
BitmapReadAccess* pReadAccess = rShared.mpReadAccess;
BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess;
- const long nWidth = pReadAccess->Width();
- const long nLastIndex = nWidth - 1;
-
- const long nRadius = rShared.mnRadius;
+ const long nLastIndex = pReadAccess->Width() - 1;
for (long y = nStart; y <= nEnd; y++)
{
- for (long x = 0; x < nWidth; x++)
+ // Optimization
+ sal_uInt8* const pSourceHint = pReadAccess->GetScanline(y);
+ sal_uInt8* const pDestHint = pWriteAccess->GetScanline(y);
+ for (long x = 0; x <= nLastIndex; x++)
{
// This processes [nRadius * 2 + 1] pixels of source per resulting pixel
// TODO: try to optimize this to not process same pixels repeatedly
- Color aResult = MorphologyOp::initColor;
- const long iMax = std::min(x + nRadius, nLastIndex);
- for (long i = std::max(x - nRadius, 0L); i <= iMax; ++i)
- OpHelper<MorphologyOp>::apply(aResult, pReadAccess->GetColor(y, i));
+ long iMin, iMax;
+ const bool bLookOutside = GetMinMax(x, rShared.mnRadius, nLastIndex, iMin, iMax);
+ Value<MorphologyOp, nComponentWidth> aResult(rShared, bLookOutside);
+ for (long i = iMin; i <= iMax; ++i)
+ aResult.apply(pReadAccess, i, y, pSourceHint);
- pWriteAccess->SetPixel(y, x, aResult);
+ aResult.copy(pWriteAccess, x, y, pDestHint);
}
}
}
@@ -174,23 +165,21 @@ template <typename MorphologyOp> struct pass<MorphologyOp, 0>
BitmapReadAccess* pReadAccess = rShared.mpReadAccess;
BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess;
- const long nHeight = pReadAccess->Height();
- const long nLastIndex = nHeight - 1;
-
- const long nRadius = rShared.mnRadius;
+ const long nLastIndex = pReadAccess->Height() - 1;
for (long x = nStart; x <= nEnd; x++)
{
- for (long y = 0; y < nHeight; y++)
+ for (long y = 0; y <= nLastIndex; y++)
{
// This processes [nRadius * 2 + 1] pixels of source per resulting pixel
// TODO: try to optimize this to not process same pixels repeatedly
- Color aResult = MorphologyOp::initColor;
- const long iMax = std::min(y + nRadius, nLastIndex);
- for (long i = std::max(y - nRadius, 0L); i <= iMax; ++i)
- OpHelper<MorphologyOp>::apply(aResult, pReadAccess->GetColor(i, x));
+ long iMin, iMax;
+ const bool bLookOutside = GetMinMax(y, rShared.mnRadius, nLastIndex, iMin, iMax);
+ Value<MorphologyOp, nComponentWidth> aResult(rShared, bLookOutside);
+ for (long i = iMin; i <= iMax; ++i)
+ aResult.apply(pReadAccess, x, i);
- pWriteAccess->SetPixel(y, x, aResult);
+ aResult.copy(pWriteAccess, x, y);
}
}
}
@@ -222,9 +211,11 @@ public:
constexpr long nThreadStrip = 16;
template <typename MorphologyOp, int nComponentWidth>
-void runFilter(Bitmap& rBitmap, const long nRadius, const bool bParallel)
+void runFilter(Bitmap& rBitmap, const long nRadius, const bool bParallel, bool bUseValueOutside,
+ sal_uInt8 nValueOutside)
{
using myPass = pass<MorphologyOp, nComponentWidth>;
+ const sal_uInt8 nOutsideVal = bUseValueOutside ? nValueOutside : MorphologyOp::initVal;
if (bParallel)
{
try
@@ -235,7 +226,7 @@ void runFilter(Bitmap& rBitmap, const long nRadius, const bool bParallel)
{
Bitmap::ScopedReadAccess pReadAccess(rBitmap);
BitmapScopedWriteAccess pWriteAccess(rBitmap);
- FilterSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius);
+ FilterSharedData aSharedData(pReadAccess, pWriteAccess, nRadius, nOutsideVal);
const long nLastIndex = pReadAccess->Height() - 1;
long nStripStart = 0;
@@ -253,7 +244,7 @@ void runFilter(Bitmap& rBitmap, const long nRadius, const bool bParallel)
{
Bitmap::ScopedReadAccess pReadAccess(rBitmap);
BitmapScopedWriteAccess pWriteAccess(rBitmap);
- FilterSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius);
+ FilterSharedData aSharedData(pReadAccess, pWriteAccess, nRadius, nOutsideVal);
const long nLastIndex = pReadAccess->Width() - 1;
long nStripStart = 0;
@@ -279,7 +270,7 @@ void runFilter(Bitmap& rBitmap, const long nRadius, const bool bParallel)
{
Bitmap::ScopedReadAccess pReadAccess(rBitmap);
BitmapScopedWriteAccess pWriteAccess(rBitmap);
- FilterSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius);
+ FilterSharedData aSharedData(pReadAccess, pWriteAccess, nRadius, nOutsideVal);
long nFirstIndex = 0;
long nLastIndex = pReadAccess->Height() - 1;
myPass::Horizontal(aSharedData, nFirstIndex, nLastIndex);
@@ -287,7 +278,7 @@ void runFilter(Bitmap& rBitmap, const long nRadius, const bool bParallel)
{
Bitmap::ScopedReadAccess pReadAccess(rBitmap);
BitmapScopedWriteAccess pWriteAccess(rBitmap);
- FilterSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius);
+ FilterSharedData aSharedData(pReadAccess, pWriteAccess, nRadius, nOutsideVal);
long nFirstIndex = 0;
long nLastIndex = pReadAccess->Width() - 1;
myPass::Vertical(aSharedData, nFirstIndex, nLastIndex);
@@ -296,14 +287,17 @@ void runFilter(Bitmap& rBitmap, const long nRadius, const bool bParallel)
}
template <int nComponentWidth>
-void runFilter(Bitmap& rBitmap, BasicMorphologyOp op, sal_Int32 nRadius)
+void runFilter(Bitmap& rBitmap, BasicMorphologyOp op, sal_Int32 nRadius, bool bUseValueOutside,
+ sal_uInt8 nValueOutside)
{
const bool bParallel = true;
if (op == BasicMorphologyOp::erode)
- runFilter<ErodeOp, nComponentWidth>(rBitmap, nRadius, bParallel);
+ runFilter<ErodeOp, nComponentWidth>(rBitmap, nRadius, bParallel, bUseValueOutside,
+ nValueOutside);
else if (op == BasicMorphologyOp::dilate)
- runFilter<DilateOp, nComponentWidth>(rBitmap, nRadius, bParallel);
+ runFilter<DilateOp, nComponentWidth>(rBitmap, nRadius, bParallel, bUseValueOutside,
+ nValueOutside);
}
} // end anonymous namespace
@@ -314,12 +308,20 @@ BitmapBasicMorphologyFilter::BitmapBasicMorphologyFilter(BasicMorphologyOp op, s
{
}
+BitmapBasicMorphologyFilter::BitmapBasicMorphologyFilter(BasicMorphologyOp op, sal_Int32 nRadius,
+ sal_uInt8 nValueOutside)
+ : m_eOp(op)
+ , m_nRadius(nRadius)
+ , m_nValueOutside(nValueOutside)
+ , m_bUseValueOutside(true)
+{
+}
+
BitmapBasicMorphologyFilter::~BitmapBasicMorphologyFilter() = default;
BitmapEx BitmapBasicMorphologyFilter::execute(BitmapEx const& rBitmapEx) const
{
- Bitmap aBitmap = rBitmapEx.GetBitmap();
- Bitmap result = filter(aBitmap);
+ Bitmap result = filter(rBitmapEx.GetBitmap());
return BitmapEx(result, rBitmapEx.GetMask());
}
@@ -336,19 +338,19 @@ Bitmap BitmapBasicMorphologyFilter::filter(Bitmap const& rBitmap) const
{
case ScanlineFormat::N24BitTcRgb:
case ScanlineFormat::N24BitTcBgr:
- runFilter<24>(bitmapCopy, m_eOp, m_nRadius);
+ runFilter<24>(bitmapCopy, m_eOp, m_nRadius, m_bUseValueOutside, m_nValueOutside);
break;
case ScanlineFormat::N32BitTcMask:
case ScanlineFormat::N32BitTcBgra:
- runFilter<32>(bitmapCopy, m_eOp, m_nRadius);
+ runFilter<32>(bitmapCopy, m_eOp, m_nRadius, m_bUseValueOutside, m_nValueOutside);
break;
case ScanlineFormat::N8BitPal:
- runFilter<8>(bitmapCopy, m_eOp, m_nRadius);
+ runFilter<8>(bitmapCopy, m_eOp, m_nRadius, m_bUseValueOutside, m_nValueOutside);
break;
// TODO: handle 1-bit images
default:
// Use access' GetColor/SetPixel fallback
- runFilter<0>(bitmapCopy, m_eOp, m_nRadius);
+ runFilter<0>(bitmapCopy, m_eOp, m_nRadius, m_bUseValueOutside, m_nValueOutside);
break;
}
diff --git a/vcl/source/bitmap/BitmapColorReplaceFilter.cxx b/vcl/source/bitmap/BitmapColorReplaceFilter.cxx
deleted file mode 100644
index 151cfbfbb48b..000000000000
--- a/vcl/source/bitmap/BitmapColorReplaceFilter.cxx
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -*- 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/.
- *
- */
-
-#include <vcl/bitmap.hxx>
-#include <vcl/bitmapaccess.hxx>
-#include <vcl/BitmapColorReplaceFilter.hxx>
-#include <vcl/bitmapex.hxx>
-
-#include <bitmapwriteaccess.hxx>
-
-BitmapEx BitmapColorReplaceFilter::execute(BitmapEx const& aBitmapEx) const
-{
- Bitmap aBitmap = aBitmapEx.GetBitmap();
-
- if (BitmapScopedWriteAccess pWriteAcc{ aBitmap })
- {
- const BitmapColor aReplaceWhat(pWriteAcc->GetBestMatchingColor(m_aReplaceWhat));
- const BitmapColor aReplaceTo(pWriteAcc->GetBestMatchingColor(m_aReplaceTo));
- const long nWidth = pWriteAcc->Width();
- const long nHeight = pWriteAcc->Height();
-
- for (long nY = 0; nY < nHeight; nY++)
- {
- Scanline pScanline = pWriteAcc->GetScanline(nY);
- for (long nX = 0; nX < nWidth; nX++)
- {
- if (pWriteAcc->GetPixelFromData(pScanline, nX) == aReplaceWhat)
- {
- pWriteAcc->SetPixelOnData(pScanline, nX, aReplaceTo);
- }
- }
- }
- }
-
- return BitmapEx(aBitmap);
-}
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */