summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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: */