diff options
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: */ |
