diff options
author | Tamas Bunth <tamas.bunth@collabora.co.uk> | 2020-01-21 19:04:13 +0100 |
---|---|---|
committer | Tamás Bunth <btomi96@gmail.com> | 2020-03-03 15:52:47 +0100 |
commit | f9fc420dceb1ece2c98767da16a21aaff771f140 (patch) | |
tree | 299b9c856a3567ee85af11b7b314d2d02a03420b | |
parent | 224ab38f747dcafe711c10b54ad53c52bda9e41d (diff) |
tdf#101181 Implement glow effect on shapes
Glow effect is a color-blurred outline outside of the shape. In ooxml
document it is specified with the <a:glow> element.
The commit contains the following:
- Add support for importing and exporting <a:glow> from ooxml documents.
- Assign new properties to XShape which stores glow-related attributes.
- A new 2D primitive is introduced in module 'drawinglayer' which is
responsible for representing the glow primitive which is to be rendered.
+ A glow primitive is a clone of the original shape which has been
scaled up slightly and a new color has been assigned to it. The
radius of the glow effect and the color is defined in the <a:glow>
element being imported.
- A blur algorithm is introduced in module 'vcl', which is called during
rendering the primitive.
+ The blur algorithm works on a bitmap.
+ Since the algorithm is CPU-intensive, the result is cached in the
processor and it is recalculated only if needed.
- Add support for importing and exporting glow effect to ODF format. For
that, new attributes of element <style:graphic-properties> has been
added:
+ loext:glow, which can have the values "visible" or "hidden"
+ loext:glow-radius: which holds the radius of the glow effect in cm.
+ loext:glow-color: holds the color of the glow effect
- Tests have been added to assert properties after pptx import and
export.
Change-Id: I836aeb5e0f24e2c8d5725834c8c0f98083bc82e7
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/89125
Tested-by: Jenkins
Reviewed-by: Tamás Bunth <btomi96@gmail.com>
48 files changed, 1409 insertions, 91 deletions
diff --git a/drawinglayer/Library_drawinglayer.mk b/drawinglayer/Library_drawinglayer.mk index aba0ce9b6e08..0f93f8407b98 100644 --- a/drawinglayer/Library_drawinglayer.mk +++ b/drawinglayer/Library_drawinglayer.mk @@ -58,6 +58,7 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\ drawinglayer/source/attribute/sdrallattribute3d \ drawinglayer/source/attribute/sdrfillattribute \ drawinglayer/source/attribute/sdrfillgraphicattribute \ + drawinglayer/source/attribute/sdrglowattribute \ drawinglayer/source/attribute/sdrlightattribute3d \ drawinglayer/source/attribute/sdrlightingattribute3d \ drawinglayer/source/attribute/sdrlineattribute \ @@ -82,6 +83,7 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\ drawinglayer/source/primitive2d/fillgraphicprimitive2d \ drawinglayer/source/primitive2d/fillgradientprimitive2d \ drawinglayer/source/primitive2d/fillhatchprimitive2d \ + drawinglayer/source/primitive2d/glowprimitive2d \ drawinglayer/source/primitive2d/graphicprimitivehelper2d \ drawinglayer/source/primitive2d/graphicprimitive2d \ drawinglayer/source/primitive2d/gridprimitive2d \ diff --git a/drawinglayer/source/attribute/sdrglowattribute.cxx b/drawinglayer/source/attribute/sdrglowattribute.cxx new file mode 100644 index 000000000000..bc42b170a8e9 --- /dev/null +++ b/drawinglayer/source/attribute/sdrglowattribute.cxx @@ -0,0 +1,80 @@ +/* -*- 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 <drawinglayer/attribute/sdrglowattribute.hxx> +#include <basegfx/vector/b2dvector.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/color/bcolor.hxx> +#include <rtl/instance.hxx> + +#include <utility> + +#include <sal/log.hxx> + +namespace drawinglayer +{ +namespace attribute +{ +SdrGlowAttribute::SdrGlowAttribute(sal_Int32 nRadius, const basegfx::BColor& rColor) + : m_nRadius(nRadius) + , m_color(rColor) +{ +} + +SdrGlowAttribute::SdrGlowAttribute() + : m_nRadius(0) +{ +} + +SdrGlowAttribute::SdrGlowAttribute(const SdrGlowAttribute&) = default; + +SdrGlowAttribute::SdrGlowAttribute(SdrGlowAttribute&&) = default; + +SdrGlowAttribute::~SdrGlowAttribute() = default; + +SdrGlowAttribute& SdrGlowAttribute::operator=(const SdrGlowAttribute&) = default; + +SdrGlowAttribute& SdrGlowAttribute::operator=(SdrGlowAttribute&&) = default; + +bool SdrGlowAttribute::operator==(const SdrGlowAttribute& rCandidate) const +{ + if (rCandidate.isDefault() != isDefault()) + return false; + return m_nRadius == rCandidate.m_nRadius && m_color == rCandidate.m_color; +} + +const basegfx::B2DHomMatrix& SdrGlowAttribute::GetTransfMatrix(basegfx::B2DRange nRange) const +{ + if (!m_oTransfCache) + { + double dRadius100mm = static_cast<double>(m_nRadius) / 360.0; + // Apply a scaling with the center point of the shape as origin. + // 1) translate shape to the origin + basegfx::B2DHomMatrix matrix = basegfx::utils::createCoordinateSystemTransform( + nRange.getCenter(), basegfx::B2DVector(-1, 0), basegfx::B2DVector(0, -1)); + + basegfx::B2DHomMatrix inverse(matrix); + inverse.invert(); + + // 2) Scale up + double scale_x = (nRange.getWidth() + dRadius100mm) / nRange.getWidth(); + double scale_y = (nRange.getHeight() + dRadius100mm) / nRange.getHeight(); + matrix *= basegfx::utils::createScaleB2DHomMatrix(scale_x, scale_y); + + // 3) Translate shape back to its place + matrix *= inverse; + m_oTransfCache = std::move(matrix); + } + return *m_oTransfCache; +} + +} // end of namespace attribute +} // end of namespace drawinglayer + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/Tools.cxx b/drawinglayer/source/primitive2d/Tools.cxx index e9ff7a55394d..2a8b8239a569 100644 --- a/drawinglayer/source/primitive2d/Tools.cxx +++ b/drawinglayer/source/primitive2d/Tools.cxx @@ -228,6 +228,8 @@ OUString idToString(sal_uInt32 nId) return "POLYPOLYGONSELECTION"; case PRIMITIVE2D_ID_PAGEHIERARCHYPRIMITIVE2D: return "PAGEHIERARCHY"; + case PRIMITIVE2D_ID_GLOWPRIMITIVE2D: + return "GLOWPRIMITIVE"; default: return OUString::number((nId >> 16) & 0xFF) + "|" + OUString::number(nId & 0xFF); } diff --git a/drawinglayer/source/primitive2d/glowprimitive2d.cxx b/drawinglayer/source/primitive2d/glowprimitive2d.cxx new file mode 100644 index 000000000000..1fc035bf5279 --- /dev/null +++ b/drawinglayer/source/primitive2d/glowprimitive2d.cxx @@ -0,0 +1,87 @@ +/* -*- 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/glowprimitive2d.hxx> +#include <basegfx/color/bcolormodifier.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> + +#include <sal/log.hxx> +#include <memory> + +using namespace com::sun::star; + +namespace drawinglayer::primitive2d +{ +GlowPrimitive2D::GlowPrimitive2D(const basegfx::B2DHomMatrix& rGlowTransform, + const basegfx::BColor& rGlowColor, + const Primitive2DContainer& rChildren) + : GroupPrimitive2D(rChildren) + , maGlowTransform(rGlowTransform) + , maGlowColor(rGlowColor) +{ +} + +bool GlowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BasePrimitive2D::operator==(rPrimitive)) + { + const GlowPrimitive2D& rCompare = static_cast<const GlowPrimitive2D&>(rPrimitive); + + return (getGlowTransform() == rCompare.getGlowTransform() + && getGlowColor() == rCompare.getGlowColor()); + } + + return false; +} + +basegfx::B2DRange +GlowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const +{ + basegfx::B2DRange aRetval(getChildren().getB2DRange(rViewInformation)); + aRetval.transform(getGlowTransform()); + return aRetval; +} + +void GlowPrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + if (!getChildren().empty()) + { + // create a modifiedColorPrimitive containing the Glow color and the content + basegfx::BColorModifierSharedPtr aBColorModifier + = std::make_shared<basegfx::BColorModifier_replace>(getGlowColor()); + + const Primitive2DReference xRefA( + new ModifiedColorPrimitive2D(getChildren(), aBColorModifier)); + const Primitive2DContainer aSequenceB{ xRefA }; + + // build transformed primitiveVector with Glow offset and add to target + rVisitor.append(new TransformPrimitive2D(getGlowTransform(), aSequenceB)); + } +} + +// provide unique ID +ImplPrimitive2DIDBlock(GlowPrimitive2D, PRIMITIVE2D_ID_GLOWPRIMITIVE2D) + +} // 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 2fa5a7d47a44..4bd490dcfe78 100644 --- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx @@ -18,6 +18,8 @@ */ #include "vclpixelprocessor2d.hxx" +#include "vclhelperbufferdevice.hxx" +#include <vcl/BitmapFilterStackBlur.hxx> #include <vcl/outdev.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/Tools.hxx> @@ -48,6 +50,9 @@ #include <drawinglayer/primitive2d/epsprimitive2d.hxx> #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> +#include <vcl/dibtools.hxx> +#include <tools/stream.hxx> + using namespace com::sun::star; namespace drawinglayer::processor2d @@ -361,6 +366,47 @@ namespace drawinglayer::processor2d processBorderLinePrimitive2D(static_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D&>(rCandidate)); break; } + case PRIMITIVE2D_ID_GLOWPRIMITIVE2D: + { + basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); + aRange.transform(maCurrentTransformation); + aRange.grow(10.0); + impBufferDevice aBufferDevice(*mpOutputDevice, aRange); + if(aBufferDevice.isVisible()) + { + // remember last OutDev and set to content + OutputDevice* pLastOutputDevice = mpOutputDevice; + mpOutputDevice = &aBufferDevice.getTransparence(); + // paint content to virtual device + mpOutputDevice->Erase(); + process(rCandidate); + + // obtain result as a bitmap + auto bitmap = mpOutputDevice->GetBitmapEx(Point(aRange.getMinX(), aRange.getMinY()), Size(aRange.getWidth(), aRange.getHeight())); + constexpr sal_Int32 nRadius = 5; + bitmap.Scale(Size(aRange.getWidth()-nRadius, aRange.getHeight()-nRadius)); + // use bitmap later as mask + auto mask = bitmap.GetBitmap(); + + mpOutputDevice = &aBufferDevice.getContent(); + process(rCandidate); + bitmap = mpOutputDevice->GetBitmapEx(Point(aRange.getMinX(), aRange.getMinY()), Size(aRange.getWidth(), aRange.getHeight())); + bitmap.Scale(Size(aRange.getWidth()-nRadius, aRange.getHeight()-nRadius)); + + // calculate blurry effect + BitmapFilterStackBlur glowFilter(nRadius); + BitmapFilter::Filter(bitmap, glowFilter); + // back to old OutDev + mpOutputDevice = pLastOutputDevice; + mpOutputDevice->DrawBitmapEx(Point(aRange.getMinX()-nRadius/2, aRange.getMinY()-nRadius/2), BitmapEx(bitmap.GetBitmap(), mask)); + + // paint result + //aBufferDevice.paint(); + } + else + SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible"); + break; + } default : { SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString(rCandidate.getPrimitive2DID())); diff --git a/include/drawinglayer/attribute/sdrglowattribute.hxx b/include/drawinglayer/attribute/sdrglowattribute.hxx new file mode 100644 index 000000000000..f5120c1a2044 --- /dev/null +++ b/include/drawinglayer/attribute/sdrglowattribute.hxx @@ -0,0 +1,54 @@ +/* -*- 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_DRAWINGLAYER_ATTRIBUTE_SDRGLOWATTRIBUTE_HXX +#define INCLUDED_DRAWINGLAYER_ATTRIBUTE_SDRGLOWATTRIBUTE_HXX + +#include <drawinglayer/drawinglayerdllapi.h> +#include <o3tl/cow_wrapper.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/color/bcolor.hxx> +#include <basegfx/range/b2drange.hxx> + +#include <optional> + +namespace drawinglayer +{ +namespace attribute +{ +class DRAWINGLAYER_DLLPUBLIC SdrGlowAttribute +{ +private: + sal_Int32 m_nRadius = 0; + mutable std::optional<basegfx::B2DHomMatrix> m_oTransfCache; + basegfx::BColor m_color; + +public: + SdrGlowAttribute(sal_Int32 nRadius, const basegfx::BColor& rColor); + SdrGlowAttribute(); + SdrGlowAttribute(const SdrGlowAttribute&); + SdrGlowAttribute(SdrGlowAttribute&&); + ~SdrGlowAttribute(); + bool operator==(const SdrGlowAttribute& rCandidate) const; + + SdrGlowAttribute& operator=(const SdrGlowAttribute&); + SdrGlowAttribute& operator=(SdrGlowAttribute&&); + + // data access + const basegfx::B2DHomMatrix& GetTransfMatrix(basegfx::B2DRange nCenter) const; + const basegfx::BColor& getColor() const { return m_color; }; + sal_Int32 getRadius() const { return m_nRadius; }; + bool isDefault() const { return m_nRadius == 0; }; +}; +} // end of namespace attribute +} // end of namespace drawinglayer + +#endif //INCLUDED_DRAWINGLAYER_ATTRIBUTE_SDRGLOWATTRIBUTE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx b/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx index 1cc379d3e521..0c9aa3c0b4ba 100644 --- a/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx +++ b/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx @@ -102,6 +102,7 @@ #define PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 68) #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) // When you add a new primitive, please update the drawinglayer::primitive2d::idToString() function // in drawinglayer/source/primitive2d/baseprimitive2d.cxx. diff --git a/include/drawinglayer/primitive2d/glowprimitive2d.hxx b/include/drawinglayer/primitive2d/glowprimitive2d.hxx new file mode 100644 index 000000000000..0c77a9a94a6c --- /dev/null +++ b/include/drawinglayer/primitive2d/glowprimitive2d.hxx @@ -0,0 +1,71 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_DRAWINGLAYER_PRIMITIVE2D_GLOWPRIMITIVE2D_HXX +#define INCLUDED_DRAWINGLAYER_PRIMITIVE2D_GLOWPRIMITIVE2D_HXX + +#include <drawinglayer/drawinglayerdllapi.h> + +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/color/bcolor.hxx> + +namespace drawinglayer +{ +namespace primitive2d +{ +class DRAWINGLAYER_DLLPUBLIC GlowPrimitive2D final : public GroupPrimitive2D +{ +private: + /// the Glow transformation, normally just an offset + basegfx::B2DHomMatrix maGlowTransform; + + /// the Glow color to which all geometry is to be forced + basegfx::BColor maGlowColor; + +public: + /// constructor + GlowPrimitive2D(const basegfx::B2DHomMatrix& rGlowTransform, const basegfx::BColor& rGlowColor, + const Primitive2DContainer& rChildren); + + /// data read access + const basegfx::B2DHomMatrix& getGlowTransform() const { return maGlowTransform; } + const basegfx::BColor& getGlowColor() const { return maGlowColor; } + + /// compare operator + virtual bool operator==(const BasePrimitive2D& rPrimitive) const override; + + /// get range + virtual basegfx::B2DRange + getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override; + + /// create decomposition + virtual void + get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const override; + + /// provide unique ID + virtual sal_uInt32 getPrimitive2DID() const override; +}; +} // end of namespace primitive2d +} // end of namespace drawinglayer + +#endif //INCLUDED_DRAWINGLAYER_PRIMITIVE2D_GLOWPRIMITIVE2D_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx index a3da6a3cb442..ce5c6e9668e2 100644 --- a/include/oox/export/drawingml.hxx +++ b/include/oox/export/drawingml.hxx @@ -167,6 +167,8 @@ protected: static bool EqualGradients( css::awt::Gradient aGradient1, css::awt::Gradient aGradient2 ); + void WriteGlowEffect(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet); + public: DrawingML( ::sax_fastparser::FSHelperPtr pFS, ::oox::core::XmlFilterBase* pFB, DocumentType eDocumentType = DOCUMENT_PPTX, DMLTextExport* pTextExport = nullptr ) : meDocumentType( eDocumentType ), mpTextExport(pTextExport), mpFS( pFS ), mpFB( pFB ), mbIsBackgroundDark( false ) {} diff --git a/include/svx/sdglowmetricitem.hxx b/include/svx/sdglowmetricitem.hxx new file mode 100644 index 000000000000..4a78cdedc19c --- /dev/null +++ b/include/svx/sdglowmetricitem.hxx @@ -0,0 +1,37 @@ +/* -*- 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_SVX_SDGLOWMETRICITEM_HXX +#define INCLUDED_SVX_SDGLOWMETRICITEM_HXX + +#include <svx/svddef.hxx> +#include <svl/poolitem.hxx> +#include <svx/svxdllapi.h> + +#include <com/sun/star/table/GlowFormat.hpp> +#include <drawinglayer/attribute/sdrglowattribute.hxx> + +class SVX_DLLPUBLIC SdrGlowItem : public SfxPoolItem +{ +private: + css::uno::Reference<css::table::GlowFormat> m_xGlow; + +public: + SdrGlowItem(); + virtual bool GetPresentation(SfxItemPresentation ePres, MapUnit eCoreMetric, + MapUnit ePresMetric, OUString& rText, + const IntlWrapper&) const override; + virtual bool operator==(const SfxPoolItem&) const override; + virtual bool QueryValue(css::uno::Any& rVal, sal_uInt8 nMemberId = 0) const override; + virtual bool PutValue(const css::uno::Any& rVal, sal_uInt8 nMemberId) override; + virtual SdrGlowItem* Clone(SfxItemPool* pPool = nullptr) const override; + + drawinglayer::attribute::SdrGlowAttribute GetGlowAttr() const; +}; + +#endif diff --git a/include/svx/strings.hrc b/include/svx/strings.hrc index 0c4d78465364..b208d88605a4 100644 --- a/include/svx/strings.hrc +++ b/include/svx/strings.hrc @@ -512,6 +512,9 @@ #define SIP_SA_GRAFINVERT NC_("SIP_SA_GRAFINVERT", "Invert") #define SIP_SA_GRAFMODE NC_("SIP_SA_GRAFMODE", "Image mode") #define SIP_SA_GRAFCROP NC_("SIP_SA_GRAFCROP", "Crop") +#define SIP_SA_GLOW NC_("SIP_SA_GRAD", "Glow effect") +#define SIP_SA_GLOW_RAD NC_("SIP_SA_GLOW_RAD", "Radius of glow effect") +#define SIP_SA_GLOW_COLOR NC_("SIP_SA_GLOW_COLOR", "Color of glow effect") #define STR_ObjNameSingulMEDIA NC_("STR_ObjNameSingulMEDIA", "Media object") #define STR_ObjNamePluralMEDIA NC_("STR_ObjNamePluralMEDIA", "Media objects") // drawing layer table strings diff --git a/include/svx/svddef.hxx b/include/svx/svddef.hxx index a8696559c4fd..10713f61188b 100644 --- a/include/svx/svddef.hxx +++ b/include/svx/svddef.hxx @@ -401,10 +401,15 @@ class SdrTextHorzAdjustItem; #define SDRATTR_TABLE_BORDER_TLBR TypedWhichId<SvxLineItem>(SDRATTR_TABLE_FIRST+2) #define SDRATTR_TABLE_BORDER_BLTR TypedWhichId<SvxLineItem>(SDRATTR_TABLE_FIRST+3) #define SDRATTR_TABLE_TEXT_ROTATION TypedWhichId<SvxTextRotateItem>(SDRATTR_TABLE_FIRST+4) - #define SDRATTR_TABLE_LAST (SDRATTR_TABLE_TEXT_ROTATION) -#define SDRATTR_END SDRATTR_TABLE_LAST /* 1357 */ /* 1333 V4+++*/ /* 1243 V4+++*/ /*1213*/ /*1085*/ /*1040*/ /*Pool V2: 1123,V1: 1065 */ +#define SDRATTR_GLOW_FIRST (SDRATTR_TABLE_LAST+1) +#define SDRATTR_GLOW TypedWhichId<SdrOnOffItem>(SDRATTR_GLOW_FIRST+0) +#define SDRATTR_GLOW_RAD TypedWhichId<SdrMetricItem>(SDRATTR_GLOW_FIRST+1) +#define SDRATTR_GLOW_COLOR TypedWhichId<XColorItem>(SDRATTR_GLOW_FIRST+2) +#define SDRATTR_GLOW_LAST (SDRATTR_GLOW_COLOR) + +#define SDRATTR_END SDRATTR_GLOW_LAST /* 1357 */ /* 1333 V4+++*/ /* 1243 V4+++*/ /*1213*/ /*1085*/ /*1040*/ /*Pool V2: 1123,V1: 1065 */ #endif // INCLUDED_SVX_SVDDEF_HXX diff --git a/include/svx/unoshprp.hxx b/include/svx/unoshprp.hxx index 0861844be16d..fd70a04035fe 100644 --- a/include/svx/unoshprp.hxx +++ b/include/svx/unoshprp.hxx @@ -209,6 +209,11 @@ { OUString("FontWorkHideForm"), XATTR_FORMTXTHIDEFORM, cppu::UnoType<bool>::get(), 0, 0}, \ { OUString("FontWorkShadowTransparence"),XATTR_FORMTXTSHDWTRANSP, ::cppu::UnoType<sal_Int16>::get(), 0, 0}, +#define GLOW_PROPERTIES \ + { OUString("GlowEffect"), SDRATTR_GLOW, cppu::UnoType<bool>::get(), 0, 0}, \ + { OUString{"GlowEffectRad"}, SDRATTR_GLOW_RAD, ::cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM}, \ + { OUString{"GlowEffectColor"}, SDRATTR_GLOW_COLOR, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + #define SHADOW_PROPERTIES \ { OUString(UNO_NAME_SHADOW), SDRATTR_SHADOW, cppu::UnoType<bool>::get(), 0, 0}, \ { OUString(UNO_NAME_SHADOWCOLOR), SDRATTR_SHADOWCOLOR, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ diff --git a/include/vcl/BitmapFilterStackBlur.hxx b/include/vcl/BitmapFilterStackBlur.hxx new file mode 100644 index 000000000000..425420ded3c2 --- /dev/null +++ b/include/vcl/BitmapFilterStackBlur.hxx @@ -0,0 +1,34 @@ +/* -*- 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_VCL_BITMAPFILTERSTACKBLUR_HXX +#define INCLUDED_VCL_BITMAPFILTERSTACKBLUR_HXX + +#include <vcl/bitmap.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/BitmapFilter.hxx> + +class VCL_DLLPUBLIC BitmapFilterStackBlur : public BitmapFilter +{ + sal_Int32 mnRadius; + bool mbExtend; + +public: + BitmapFilterStackBlur(sal_Int32 nRadius, bool bExtend = true); + virtual ~BitmapFilterStackBlur(); + + virtual BitmapEx execute(BitmapEx const& rBitmap) const override; + + Bitmap filter(Bitmap const& rBitmap) const; +}; + +#endif // INCLUDED_VCL_BITMAPFILTERSTACKBLUR_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index 7289d72e5e61..ba4a588d85eb 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -925,6 +925,9 @@ namespace xmloff { namespace token { XML_GCD, XML_GENERATOR, XML_GEQ, + XML_GLOW, + XML_GLOW_RADIUS, + XML_GLOW_COLOR, XML_GOURAUD, XML_GRADIENT, XML_GRADIENT_ANGLE, diff --git a/oox/source/drawingml/effectproperties.cxx b/oox/source/drawingml/effectproperties.cxx index 2ca45bae05a8..e4e9eaddcb32 100644 --- a/oox/source/drawingml/effectproperties.cxx +++ b/oox/source/drawingml/effectproperties.cxx @@ -18,6 +18,12 @@ namespace oox::drawingml { +void EffectGlowProperties ::assignUsed(const EffectGlowProperties& rSourceProps) +{ + moGlowRad.assignIfUsed( rSourceProps.moGlowRad ); + moGlowColor.assignIfUsed( rSourceProps.moGlowColor ); +} + void EffectShadowProperties::assignUsed(const EffectShadowProperties& rSourceProps) { moShadowDist.assignIfUsed( rSourceProps.moShadowDist ); @@ -28,6 +34,7 @@ void EffectShadowProperties::assignUsed(const EffectShadowProperties& rSourcePro void EffectProperties::assignUsed( const EffectProperties& rSourceProps ) { maShadow.assignUsed(rSourceProps.maShadow); + maGlow.assignUsed(rSourceProps.maGlow); if (!rSourceProps.m_Effects.empty()) { m_Effects.clear(); diff --git a/oox/source/drawingml/effectproperties.hxx b/oox/source/drawingml/effectproperties.hxx index 146214cc9191..c4f39ac8803b 100644 --- a/oox/source/drawingml/effectproperties.hxx +++ b/oox/source/drawingml/effectproperties.hxx @@ -20,6 +20,15 @@ namespace oox { namespace drawingml { +struct EffectGlowProperties +{ + OptValue< sal_Int64 > moGlowRad; // size of glow effect + Color moGlowColor; + // TODO saturation and luminance missing + + void assignUsed( const EffectGlowProperties& rSourceProps ); +}; + struct EffectShadowProperties { OptValue< sal_Int64 > moShadowDist; @@ -42,6 +51,7 @@ struct Effect struct EffectProperties { EffectShadowProperties maShadow; + EffectGlowProperties maGlow; /** Stores all effect properties, including those not supported by core yet */ std::vector<std::unique_ptr<Effect>> m_Effects; diff --git a/oox/source/drawingml/effectpropertiescontext.cxx b/oox/source/drawingml/effectpropertiescontext.cxx index 7941920ed370..6424e99d8458 100644 --- a/oox/source/drawingml/effectpropertiescontext.cxx +++ b/oox/source/drawingml/effectpropertiescontext.cxx @@ -15,6 +15,8 @@ #include <oox/token/namespaces.hxx> #include <oox/token/tokens.hxx> +#include <sal/log.hxx> + using namespace ::oox::core; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::xml::sax; @@ -97,13 +99,18 @@ ContextHandlerRef EffectPropertiesContext::onCreateContext( sal_Int32 nElement, } break; case A_TOKEN( glow ): + { + mrEffectProperties.maGlow.moGlowRad = rAttribs.getInteger( XML_rad, 0 ); + // undo push_back to effects + mrEffectProperties.m_Effects.pop_back(); + return new ColorContext(*this, mrEffectProperties.maGlow.moGlowColor); + + } case A_TOKEN( softEdge ): case A_TOKEN( reflection ): case A_TOKEN( blur ): { - if( nElement == A_TOKEN( glow ) ) - mrEffectProperties.m_Effects[nPos]->msName = "glow"; - else if( nElement == A_TOKEN( softEdge ) ) + if( nElement == A_TOKEN( softEdge ) ) mrEffectProperties.m_Effects[nPos]->msName = "softEdge"; else if( nElement == A_TOKEN( reflection ) ) mrEffectProperties.m_Effects[nPos]->msName = "reflection"; diff --git a/oox/source/drawingml/shape.cxx b/oox/source/drawingml/shape.cxx index 0cf633369959..1c307ff38371 100644 --- a/oox/source/drawingml/shape.cxx +++ b/oox/source/drawingml/shape.cxx @@ -1215,6 +1215,7 @@ Reference< XShape > const & Shape::createAndInsert( aFormat.ShadowWidth = *oShadowDistance; aShapeProps.setProperty(PROP_ShadowFormat, aFormat); } + } else if (mbTextBox) { @@ -1464,6 +1465,15 @@ Reference< XShape > const & Shape::createAndInsert( aPropertySet.setAnyProperty(PROP_CharColor, uno::makeAny(nCharColor)); } } + + // Set glow effect properties + if ( aEffectProperties.maGlow.moGlowRad.has() ) + { + uno::Reference<beans::XPropertySet> propertySet (mxShape, uno::UNO_QUERY); + propertySet->setPropertyValue("GlowEffect", makeAny(true)); + propertySet->setPropertyValue("GlowEffectRad", makeAny(static_cast<sal_Int32>(aEffectProperties.maGlow.moGlowRad.get()))); + propertySet->setPropertyValue("GlowEffectColor", makeAny(aEffectProperties.maGlow.moGlowColor.getColor(rGraphicHelper))); + } } if( mxShape.is() ) diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index b4c37d514ece..d80b6a3bea7e 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -3719,29 +3719,38 @@ void DrawingML::WriteShapeEffects( const Reference< XPropertySet >& rXPropSet ) bool bHasShadow = false; if( GetProperty( rXPropSet, "Shadow" ) ) mAny >>= bHasShadow; - if( bHasShadow ) + bool bHasGlow = false; + if( GetProperty( rXPropSet, "GlowEffect") ) + mAny >>= bHasGlow; + //rXPropSet->getPropertyValue("GlowEffect") >>= bHasGlow; + + if( bHasShadow || bHasGlow ) { - Sequence< PropertyValue > aShadowGrabBag( 3 ); - Sequence< PropertyValue > aShadowAttribsGrabBag( 2 ); + mpFS->startElementNS(XML_a, XML_effectLst); + if( bHasShadow ) + { + Sequence< PropertyValue > aShadowGrabBag( 3 ); + Sequence< PropertyValue > aShadowAttribsGrabBag( 2 ); - double dX = +0.0, dY = +0.0; - rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX; - rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY; + double dX = +0.0, dY = +0.0; + rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX; + rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY; - aShadowAttribsGrabBag[0].Name = "dist"; - aShadowAttribsGrabBag[0].Value <<= lcl_CalculateDist(dX, dY); - aShadowAttribsGrabBag[1].Name = "dir"; - aShadowAttribsGrabBag[1].Value <<= lcl_CalculateDir(dX, dY); + aShadowAttribsGrabBag[0].Name = "dist"; + aShadowAttribsGrabBag[0].Value <<= lcl_CalculateDist(dX, dY); + aShadowAttribsGrabBag[1].Name = "dir"; + aShadowAttribsGrabBag[1].Value <<= lcl_CalculateDir(dX, dY); - aShadowGrabBag[0].Name = "Attribs"; - aShadowGrabBag[0].Value <<= aShadowAttribsGrabBag; - aShadowGrabBag[1].Name = "RgbClr"; - aShadowGrabBag[1].Value = rXPropSet->getPropertyValue( "ShadowColor" ); - aShadowGrabBag[2].Name = "RgbClrTransparency"; - aShadowGrabBag[2].Value = rXPropSet->getPropertyValue( "ShadowTransparence" ); + aShadowGrabBag[0].Name = "Attribs"; + aShadowGrabBag[0].Value <<= aShadowAttribsGrabBag; + aShadowGrabBag[1].Name = "RgbClr"; + aShadowGrabBag[1].Value = rXPropSet->getPropertyValue( "ShadowColor" ); + aShadowGrabBag[2].Name = "RgbClrTransparency"; + aShadowGrabBag[2].Value = rXPropSet->getPropertyValue( "ShadowTransparence" ); - mpFS->startElementNS(XML_a, XML_effectLst); - WriteShapeEffect( "outerShdw", aShadowGrabBag ); + WriteShapeEffect( "outerShdw", aShadowGrabBag ); + } + WriteGlowEffect(rXPropSet); mpFS->endElementNS(XML_a, XML_effectLst); } } @@ -3796,10 +3805,32 @@ void DrawingML::WriteShapeEffects( const Reference< XPropertySet >& rXPropSet ) WriteShapeEffect( rEffect.Name, aEffectProps ); } } + WriteGlowEffect(rXPropSet); + mpFS->endElementNS(XML_a, XML_effectLst); } } +void DrawingML::WriteGlowEffect(const Reference< XPropertySet >& rXPropSet) +{ + bool hasGlow = false; + rXPropSet->getPropertyValue("GlowEffect") >>= hasGlow; + if(!hasGlow) + return; + + Sequence< PropertyValue > aGlowAttribs(1); + aGlowAttribs[0].Name = "rad"; + aGlowAttribs[0].Value = rXPropSet->getPropertyValue("GlowEffectRad"); + Sequence< PropertyValue > aGlowProps(2); + aGlowProps[0].Name = "Attribs"; + aGlowProps[0].Value <<= aGlowAttribs; + aGlowProps[1].Name = "RgbClr"; + aGlowProps[1].Value = rXPropSet->getPropertyValue("GlowEffectColor"); + // TODO other stuff like saturation or luminance + + WriteShapeEffect("glow", aGlowProps); +} + void DrawingML::WriteShape3DEffects( const Reference< XPropertySet >& xPropSet ) { // check existence of the grab bag diff --git a/oox/source/token/properties.txt b/oox/source/token/properties.txt index 4fc0c5b47c57..4624573d579b 100644 --- a/oox/source/token/properties.txt +++ b/oox/source/token/properties.txt @@ -207,6 +207,8 @@ Function GapwidthSequence GenerateVbaEvents Geometry3D +GlowEffect +GlowEffectRad GradientName HatchName Graphic diff --git a/sd/qa/unit/data/pptx/shape-glow-effect.pptx b/sd/qa/unit/data/pptx/shape-glow-effect.pptx Binary files differnew file mode 100755 index 000000000000..6a33aa58ffaa --- /dev/null +++ b/sd/qa/unit/data/pptx/shape-glow-effect.pptx diff --git a/sd/qa/unit/export-tests-ooxml2.cxx b/sd/qa/unit/export-tests-ooxml2.cxx index 2a1802f138ab..2befeb024d9b 100644 --- a/sd/qa/unit/export-tests-ooxml2.cxx +++ b/sd/qa/unit/export-tests-ooxml2.cxx @@ -182,6 +182,7 @@ public: void testTdf127372(); void testTdf127379(); void testTdf98603(); + void testShapeGlowEffect(); CPPUNIT_TEST_SUITE(SdOOXMLExportTest2); @@ -282,6 +283,7 @@ public: CPPUNIT_TEST(testTdf127372); CPPUNIT_TEST(testTdf127379); CPPUNIT_TEST(testTdf98603); + CPPUNIT_TEST(testShapeGlowEffect); CPPUNIT_TEST_SUITE_END(); @@ -2633,6 +2635,22 @@ void SdOOXMLExportTest2::testTdf98603() CPPUNIT_ASSERT_EQUAL(OUString("IL"), aLocale.Country); } +void SdOOXMLExportTest2::testShapeGlowEffect() +{ + ::sd::DrawDocShellRef xDocShRef = loadURL( m_directories.getURLFromSrc("sd/qa/unit/data/pptx/shape-glow-effect.pptx"), PPTX); + xDocShRef = saveAndReload( xDocShRef.get(), PPTX ); + uno::Reference<beans::XPropertySet> xShape(getShapeFromPage(0, 0, xDocShRef)); + bool bHasGlow = false; + xShape->getPropertyValue("GlowEffect") >>= bHasGlow; + CPPUNIT_ASSERT(bHasGlow); + sal_Int64 nRadius = -1; + xShape->getPropertyValue("GlowEffectRad") >>= nRadius; + CPPUNIT_ASSERT_EQUAL(sal_Int64(139700l), nRadius); + Color nColor; + xShape->getPropertyValue("GlowEffectColor") >>= nColor; + CPPUNIT_ASSERT_EQUAL(Color(0xFFC000), nColor); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SdOOXMLExportTest2); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sd/qa/unit/import-tests.cxx b/sd/qa/unit/import-tests.cxx index 9dfdb35c4fb4..85b7f5fa0cdf 100644 --- a/sd/qa/unit/import-tests.cxx +++ b/sd/qa/unit/import-tests.cxx @@ -209,6 +209,7 @@ public: void testTdf116266(); void testTdf126324(); void testTdf128684(); + void testShapeGlowEffectPPTXImpoer(); bool checkPattern(sd::DrawDocShellRef const & rDocRef, int nShapeNumber, std::vector<sal_uInt8>& rExpected); void testPatternImport(); @@ -332,6 +333,7 @@ public: CPPUNIT_TEST(testTdf106638); CPPUNIT_TEST(testTdf128684); CPPUNIT_TEST(testTdf113198); + CPPUNIT_TEST(testShapeGlowEffectPPTXImpoer); CPPUNIT_TEST_SUITE_END(); }; @@ -3145,6 +3147,23 @@ void SdImportTest::testTdf113198() CPPUNIT_ASSERT_EQUAL(style::ParagraphAdjust_CENTER, static_cast<style::ParagraphAdjust>(nParaAdjust)); } +void SdImportTest::testShapeGlowEffectPPTXImpoer() +{ + sd::DrawDocShellRef xDocShRef + = loadURL(m_directories.getURLFromSrc("sd/qa/unit/data/pptx/shape-glow-effect.pptx"), PPTX); + + uno::Reference<beans::XPropertySet> xShape(getShapeFromPage(0, 0, xDocShRef)); + bool bHasGlow = false; + xShape->getPropertyValue("GlowEffect") >>= bHasGlow; + CPPUNIT_ASSERT(bHasGlow); + sal_Int64 nRadius = -1; + xShape->getPropertyValue("GlowEffectRad") >>= nRadius; + CPPUNIT_ASSERT_EQUAL(sal_Int64(139700l), nRadius); + Color nColor; + xShape->getPropertyValue("GlowEffectColor") >>= nColor; + CPPUNIT_ASSERT_EQUAL(Color(0xFFC000), nColor); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SdImportTest); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/svx/inc/sdr/attribute/sdrlinefillshadowtextattribute.hxx b/svx/inc/sdr/attribute/sdrlinefillshadowtextattribute.hxx index 519800e7dd02..f9d7969e519a 100644 --- a/svx/inc/sdr/attribute/sdrlinefillshadowtextattribute.hxx +++ b/svx/inc/sdr/attribute/sdrlinefillshadowtextattribute.hxx @@ -41,7 +41,8 @@ namespace drawinglayer const SdrLineStartEndAttribute& rLineStartEnd, const SdrShadowAttribute& rShadow, const FillGradientAttribute& rFillFloatTransGradient, - const SdrTextAttribute& rTextAttribute); + const SdrTextAttribute& rTextAttribute, + const SdrGlowAttribute& rGlow); SdrLineFillShadowTextAttribute(); SdrLineFillShadowTextAttribute(const SdrLineFillShadowTextAttribute& rCandidate); SdrLineFillShadowTextAttribute& operator=(const SdrLineFillShadowTextAttribute& rCandidate); diff --git a/svx/inc/sdr/attribute/sdrlineshadowtextattribute.hxx b/svx/inc/sdr/attribute/sdrlineshadowtextattribute.hxx index af7df7982422..840b20e44cc9 100644 --- a/svx/inc/sdr/attribute/sdrlineshadowtextattribute.hxx +++ b/svx/inc/sdr/attribute/sdrlineshadowtextattribute.hxx @@ -20,6 +20,7 @@ #pragma once #include <sdr/attribute/sdrshadowtextattribute.hxx> +#include <drawinglayer/attribute/sdrglowattribute.hxx> #include <drawinglayer/attribute/sdrlineattribute.hxx> #include <drawinglayer/attribute/sdrlinestartendattribute.hxx> @@ -39,7 +40,8 @@ namespace drawinglayer const SdrLineAttribute& rLine, const SdrLineStartEndAttribute& rLineStartEnd, const SdrShadowAttribute& rShadow, - const SdrTextAttribute& rTextAttribute); + const SdrTextAttribute& rTextAttribute, + const SdrGlowAttribute& rGlow); SdrLineShadowTextAttribute(); SdrLineShadowTextAttribute(const SdrLineShadowTextAttribute& rCandidate); SdrLineShadowTextAttribute& operator=(const SdrLineShadowTextAttribute& rCandidate); diff --git a/svx/inc/sdr/attribute/sdrshadowtextattribute.hxx b/svx/inc/sdr/attribute/sdrshadowtextattribute.hxx index c58437b70928..bbcd7e95a501 100644 --- a/svx/inc/sdr/attribute/sdrshadowtextattribute.hxx +++ b/svx/inc/sdr/attribute/sdrshadowtextattribute.hxx @@ -21,6 +21,7 @@ #include <drawinglayer/attribute/sdrshadowattribute.hxx> #include <sdr/attribute/sdrtextattribute.hxx> +#include <drawinglayer/attribute/sdrglowattribute.hxx> namespace drawinglayer @@ -33,10 +34,14 @@ namespace drawinglayer SdrShadowAttribute maShadow; // shadow attributes (if used) SdrTextAttribute maTextAttribute; // text and text attributes (if used) + // glow effect + SdrGlowAttribute maGlow; + public: SdrShadowTextAttribute( const SdrShadowAttribute& rShadow, - const SdrTextAttribute& rTextAttribute); + const SdrTextAttribute& rTextAttribute, + const SdrGlowAttribute& rGlow); SdrShadowTextAttribute(); SdrShadowTextAttribute(const SdrShadowTextAttribute& rCandidate); SdrShadowTextAttribute& operator=(const SdrShadowTextAttribute& rCandidate); @@ -50,6 +55,7 @@ namespace drawinglayer // data access const SdrShadowAttribute& getShadow() const { return maShadow; } const SdrTextAttribute& getText() const { return maTextAttribute; } + const SdrGlowAttribute& getGlow() const { return maGlow; } }; } // end of namespace attribute } // end of namespace drawinglayer diff --git a/svx/inc/sdr/primitive2d/sdrattributecreator.hxx b/svx/inc/sdr/primitive2d/sdrattributecreator.hxx index 1d9e17d60d2a..da0242eed3a5 100644 --- a/svx/inc/sdr/primitive2d/sdrattributecreator.hxx +++ b/svx/inc/sdr/primitive2d/sdrattributecreator.hxx @@ -31,6 +31,7 @@ namespace drawinglayer { namespace attribute { class SdrLineAttribute; class SdrLineStartEndAttribute; class SdrShadowAttribute; + class SdrGlowAttribute; class SdrFillAttribute; class SdrTextAttribute; class FillGradientAttribute; @@ -67,6 +68,9 @@ namespace drawinglayer attribute::SdrFillAttribute SVXCORE_DLLPUBLIC createNewSdrFillAttribute( const SfxItemSet& rSet); + attribute::SdrGlowAttribute createNewSdrGlowAttribute( + const SfxItemSet& rSet); + // #i101508# Support handing over given text-to-border distances attribute::SdrTextAttribute createNewSdrTextAttribute( const SfxItemSet& rSet, diff --git a/svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx b/svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx index f55edda1b7c1..6a2cf1993305 100644 --- a/svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx +++ b/svx/inc/sdr/primitive2d/sdrdecompositiontools.hxx @@ -20,6 +20,8 @@ #pragma once #include <drawinglayer/primitive2d/baseprimitive2d.hxx> +#include <drawinglayer/attribute/sdrglowattribute.hxx> + #include <svx/svxdllapi.h> // predefines @@ -71,6 +73,10 @@ namespace drawinglayer const Primitive2DContainer& rContent, const attribute::SdrShadowAttribute& rShadow); + Primitive2DContainer SVXCORE_DLLPUBLIC createEmbeddedGlowPrimitive( + const Primitive2DContainer& rContent, + const attribute::SdrGlowAttribute& rGlow); + } // end of namespace primitive2d } // end of namespace drawinglayer diff --git a/svx/source/sdr/attribute/sdrlinefillshadowtextattribute.cxx b/svx/source/sdr/attribute/sdrlinefillshadowtextattribute.cxx index 6ab08fcba99f..9ecbf331961e 100644 --- a/svx/source/sdr/attribute/sdrlinefillshadowtextattribute.cxx +++ b/svx/source/sdr/attribute/sdrlinefillshadowtextattribute.cxx @@ -29,8 +29,9 @@ namespace drawinglayer::attribute const SdrLineStartEndAttribute& rLineStartEnd, const SdrShadowAttribute& rShadow, const FillGradientAttribute& rFillFloatTransGradient, - const SdrTextAttribute& rTextAttribute) - : SdrLineShadowTextAttribute(rLine, rLineStartEnd, rShadow, rTextAttribute), + const SdrTextAttribute& rTextAttribute, + const SdrGlowAttribute& rGlow) + : SdrLineShadowTextAttribute(rLine, rLineStartEnd, rShadow, rTextAttribute, rGlow), maFill(rFill), maFillFloatTransGradient(rFillFloatTransGradient) { diff --git a/svx/source/sdr/attribute/sdrlineshadowtextattribute.cxx b/svx/source/sdr/attribute/sdrlineshadowtextattribute.cxx index bc15ad8473e4..667a996d1379 100644 --- a/svx/source/sdr/attribute/sdrlineshadowtextattribute.cxx +++ b/svx/source/sdr/attribute/sdrlineshadowtextattribute.cxx @@ -27,8 +27,9 @@ namespace drawinglayer::attribute const SdrLineAttribute& rLine, const SdrLineStartEndAttribute& rLineStartEnd, const SdrShadowAttribute& rShadow, - const SdrTextAttribute& rTextAttribute) - : SdrShadowTextAttribute(rShadow, rTextAttribute), + const SdrTextAttribute& rTextAttribute, + const SdrGlowAttribute& rGlow) + : SdrShadowTextAttribute(rShadow, rTextAttribute, rGlow), maLine(rLine), maLineStartEnd(rLineStartEnd) { diff --git a/svx/source/sdr/attribute/sdrshadowtextattribute.cxx b/svx/source/sdr/attribute/sdrshadowtextattribute.cxx index 8d08b770a7a0..705981a62c16 100644 --- a/svx/source/sdr/attribute/sdrshadowtextattribute.cxx +++ b/svx/source/sdr/attribute/sdrshadowtextattribute.cxx @@ -25,9 +25,11 @@ namespace drawinglayer::attribute { SdrShadowTextAttribute::SdrShadowTextAttribute( const SdrShadowAttribute& rShadow, - const SdrTextAttribute& rTextAttribute) + const SdrTextAttribute& rTextAttribute, + const SdrGlowAttribute& rGlow) : maShadow(rShadow), - maTextAttribute(rTextAttribute) + maTextAttribute(rTextAttribute), + maGlow(rGlow) { } @@ -39,7 +41,8 @@ namespace drawinglayer::attribute SdrShadowTextAttribute::SdrShadowTextAttribute(const SdrShadowTextAttribute& rCandidate) : maShadow(rCandidate.getShadow()), - maTextAttribute(rCandidate.getText()) + maTextAttribute(rCandidate.getText()), + maGlow(rCandidate.maGlow) { } @@ -47,6 +50,7 @@ namespace drawinglayer::attribute { maShadow = rCandidate.getShadow(); maTextAttribute = rCandidate.getText(); + maGlow = rCandidate.maGlow; return *this; } @@ -54,13 +58,14 @@ namespace drawinglayer::attribute bool SdrShadowTextAttribute::isDefault() const { return (getShadow().isDefault() - && getText().isDefault()); + && getText().isDefault() && maGlow.isDefault()); } bool SdrShadowTextAttribute::operator==(const SdrShadowTextAttribute& rCandidate) const { return (getShadow() == rCandidate.getShadow() - && getText() == rCandidate.getText()); + && getText() == rCandidate.getText() + && maGlow.isDefault()); } } // end of namespace diff --git a/svx/source/sdr/primitive2d/sdrattributecreator.cxx b/svx/source/sdr/primitive2d/sdrattributecreator.cxx index 7afbada0c1be..18f5aae3e884 100644 --- a/svx/source/sdr/primitive2d/sdrattributecreator.cxx +++ b/svx/source/sdr/primitive2d/sdrattributecreator.cxx @@ -75,12 +75,15 @@ #include <sdr/attribute/sdrlineshadowtextattribute.hxx> #include <sdr/attribute/sdrformtextattribute.hxx> #include <sdr/attribute/sdrlinefillshadowtextattribute.hxx> +#include <drawinglayer/attribute/sdrglowattribute.hxx> #include <drawinglayer/attribute/sdrsceneattribute3d.hxx> #include <drawinglayer/attribute/sdrlightingattribute3d.hxx> #include <drawinglayer/attribute/sdrlightattribute3d.hxx> #include <sdr/attribute/sdrfilltextattribute.hxx> #include <com/sun/star/drawing/LineCap.hpp> +#include <sal/log.hxx> + using namespace com::sun::star; namespace drawinglayer @@ -332,6 +335,18 @@ namespace drawinglayer::primitive2d return attribute::SdrLineStartEndAttribute(); } + attribute::SdrGlowAttribute createNewSdrGlowAttribute( const SfxItemSet& rSet) + { + const bool bGlow(rSet.Get(SDRATTR_GLOW).GetValue()); + if(!bGlow) + return attribute::SdrGlowAttribute(); + sal_Int32 nRadius = rSet.Get(SDRATTR_GLOW_RAD).GetValue(); + const Color aColor(rSet.Get(SDRATTR_GLOW_COLOR).GetColorValue()); + + attribute::SdrGlowAttribute glowAttr{ nRadius, aColor.getBColor() }; + return glowAttr; + } + attribute::SdrShadowAttribute createNewSdrShadowAttribute(const SfxItemSet& rSet) { const bool bShadow(rSet.Get(SDRATTR_SHADOW).GetValue()); @@ -724,8 +739,9 @@ namespace drawinglayer::primitive2d // try shadow const attribute::SdrShadowAttribute aShadow(createNewSdrShadowAttribute(rSet)); + const attribute::SdrGlowAttribute aGlow(createNewSdrGlowAttribute(rSet)); - return attribute::SdrShadowTextAttribute(aShadow, aText); + return attribute::SdrShadowTextAttribute(aShadow, aText, aGlow); } attribute::SdrLineShadowTextAttribute createNewSdrLineShadowTextAttribute( @@ -735,6 +751,7 @@ namespace drawinglayer::primitive2d attribute::SdrLineAttribute aLine; attribute::SdrLineStartEndAttribute aLineStartEnd; attribute::SdrTextAttribute aText; + attribute::SdrGlowAttribute aGlow; bool bFontworkHideContour(false); // look for text first @@ -768,8 +785,9 @@ namespace drawinglayer::primitive2d { // try shadow const attribute::SdrShadowAttribute aShadow(createNewSdrShadowAttribute(rSet)); + aGlow = createNewSdrGlowAttribute(rSet); - return attribute::SdrLineShadowTextAttribute(aLine, aLineStartEnd, aShadow, aText); + return attribute::SdrLineShadowTextAttribute(aLine, aLineStartEnd, aShadow, aText, aGlow); } return attribute::SdrLineShadowTextAttribute(); @@ -786,6 +804,7 @@ namespace drawinglayer::primitive2d attribute::SdrShadowAttribute aShadow; attribute::FillGradientAttribute aFillFloatTransGradient; attribute::SdrTextAttribute aText; + attribute::SdrGlowAttribute aGlow; bool bFontworkHideContour(false); // look for text first @@ -831,8 +850,11 @@ namespace drawinglayer::primitive2d // try shadow aShadow = createNewSdrShadowAttribute(rSet); + // glow + aGlow = createNewSdrGlowAttribute(rSet); + return attribute::SdrLineFillShadowTextAttribute( - aLine, aFill, aLineStartEnd, aShadow, aFillFloatTransGradient, aText); + aLine, aFill, aLineStartEnd, aShadow, aFillFloatTransGradient, aText, aGlow); } return attribute::SdrLineFillShadowTextAttribute(); diff --git a/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx index 7d32c753151f..804a653c86b2 100644 --- a/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx +++ b/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx @@ -25,6 +25,8 @@ #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> #include <drawinglayer/attribute/sdrlineattribute.hxx> +#include <sal/log.hxx> + using namespace com::sun::star; @@ -69,6 +71,12 @@ namespace drawinglayer::primitive2d } } + if(!aRetval.empty() && !getSdrSTAttribute().getGlow().isDefault()) + { + // glow + aRetval = createEmbeddedGlowPrimitive(aRetval, getSdrSTAttribute().getGlow()); + } + rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end()); } diff --git a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx index 9aaf398030f5..e89b2a537b21 100644 --- a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx +++ b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx @@ -31,6 +31,7 @@ #include <basegfx/matrix/b2dhommatrix.hxx> #include <drawinglayer/primitive2d/shadowprimitive2d.hxx> #include <sdr/attribute/sdrtextattribute.hxx> +#include <drawinglayer/primitive2d/glowprimitive2d.hxx> #include <sdr/primitive2d/sdrtextprimitive2d.hxx> #include <svx/svdotext.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> @@ -43,6 +44,7 @@ #include <drawinglayer/attribute/sdrlineattribute.hxx> #include <drawinglayer/attribute/sdrlinestartendattribute.hxx> #include <drawinglayer/attribute/sdrshadowattribute.hxx> +#include <drawinglayer/attribute/sdrglowattribute.hxx> using namespace com::sun::star; @@ -514,6 +516,25 @@ namespace drawinglayer::primitive2d return rContent; } } + + Primitive2DContainer createEmbeddedGlowPrimitive( + const Primitive2DContainer& rContent, + const attribute::SdrGlowAttribute& rGlow) + { + if(rContent.empty()) + return rContent; + Primitive2DContainer aRetval(2); + const uno::Sequence< beans::PropertyValue > xViewParameters; + geometry::ViewInformation2D aViewInformation2D(xViewParameters); + aRetval[0] = Primitive2DReference( + new GlowPrimitive2D( + rGlow.GetTransfMatrix(rContent.getB2DRange(aViewInformation2D)), + rGlow.getColor(), + rContent)); + aRetval[1] = Primitive2DReference(new GroupPrimitive2D(rContent)); + return aRetval; + } + } // end of namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/customshapeproperties.cxx b/svx/source/sdr/properties/customshapeproperties.cxx index cb9425754e39..f9da077101ee 100644 --- a/svx/source/sdr/properties/customshapeproperties.cxx +++ b/svx/source/sdr/properties/customshapeproperties.cxx @@ -28,6 +28,8 @@ #include <svl/whiter.hxx> #include <svl/hint.hxx> +#include <sal/log.hxx> + namespace sdr::properties { @@ -71,6 +73,7 @@ namespace sdr::properties // Graphic attributes, 3D properties, CustomShape // properties: SDRATTR_GRAF_FIRST, SDRATTR_CUSTOMSHAPE_LAST, + SDRATTR_GLOW_FIRST, SDRATTR_GLOW_LAST, // Range from SdrTextObj: EE_ITEMS_START, EE_ITEMS_END>{}); } diff --git a/svx/source/svdraw/svdattr.cxx b/svx/source/svdraw/svdattr.cxx index e1d56b706b25..af8d7f7d45b9 100644 --- a/svx/source/svdraw/svdattr.cxx +++ b/svx/source/svdraw/svdattr.cxx @@ -326,6 +326,10 @@ SdrItemPool::SdrItemPool( rPoolDefaults[ SDRATTR_TABLE_BORDER_BLTR - SDRATTR_START ] = new SvxLineItem( SDRATTR_TABLE_BORDER_BLTR ); rPoolDefaults[ SDRATTR_TABLE_TEXT_ROTATION - SDRATTR_START ] = new SvxTextRotateItem(0, SDRATTR_TABLE_TEXT_ROTATION); + rPoolDefaults[ SDRATTR_GLOW - SDRATTR_START ] = new SdrOnOffItem(SDRATTR_GLOW, false); + rPoolDefaults[ SDRATTR_GLOW_RAD - SDRATTR_START ] = new SdrMetricItem(SDRATTR_GLOW_RAD, 0); + rPoolDefaults[ SDRATTR_GLOW_COLOR - SDRATTR_START ] = new XColorItem(SDRATTR_GLOW_COLOR, aNullCol); + // set own ItemInfos mpLocalItemInfos[SDRATTR_SHADOW-SDRATTR_START]._nSID=SID_ATTR_FILL_SHADOW; mpLocalItemInfos[SDRATTR_SHADOWCOLOR-SDRATTR_START]._nSID=SID_ATTR_SHADOW_COLOR; @@ -446,6 +450,10 @@ OUString SdrItemPool::GetItemName(sal_uInt16 nWhich) case SDRATTR_SHADOW3D : pResId = SIP_SA_SHADOW3D;break; case SDRATTR_SHADOWPERSP : pResId = SIP_SA_SHADOWPERSP;break; + case SDRATTR_GLOW : pResId = SIP_SA_GLOW;break; + case SDRATTR_GLOW_RAD : pResId = SIP_SA_GLOW_RAD;break; + case SDRATTR_GLOW_COLOR : pResId = SIP_SA_GLOW_COLOR;break; + case SDRATTR_CAPTIONTYPE : pResId = SIP_SA_CAPTIONTYPE;break; case SDRATTR_CAPTIONFIXEDANGLE: pResId = SIP_SA_CAPTIONFIXEDANGLE;break; case SDRATTR_CAPTIONANGLE : pResId = SIP_SA_CAPTIONANGLE;break; diff --git a/svx/source/unodraw/unoprov.cxx b/svx/source/unodraw/unoprov.cxx index 639b8c798d39..67406c63ba40 100644 --- a/svx/source/unodraw/unoprov.cxx +++ b/svx/source/unodraw/unoprov.cxx @@ -68,6 +68,7 @@ static SfxItemPropertyMapEntry const * ImplGetSvxShapePropertyMap() SHAPE_DESCRIPTOR_PROPERTIES MISC_OBJ_PROPERTIES LINKTARGET_PROPERTIES + GLOW_PROPERTIES SHADOW_PROPERTIES TEXT_PROPERTIES // #FontWork# @@ -91,6 +92,7 @@ static SfxItemPropertyMapEntry const * ImplGetSvxTextShapePropertyMap() SHAPE_DESCRIPTOR_PROPERTIES MISC_OBJ_PROPERTIES_NO_SHEAR LINKTARGET_PROPERTIES + GLOW_PROPERTIES SHADOW_PROPERTIES TEXT_PROPERTIES // #FontWork# @@ -115,6 +117,7 @@ static SfxItemPropertyMapEntry const * ImplGetSvxConnectorPropertyMap() SHAPE_DESCRIPTOR_PROPERTIES MISC_OBJ_PROPERTIES LINKTARGET_PROPERTIES + GLOW_PROPERTIES SHADOW_PROPERTIES TEXT_PROPERTIES // #FontWork# @@ -139,6 +142,7 @@ static SfxItemPropertyMapEntry const * ImplGetSvxDimensioningPropertyMap() SHAPE_DESCRIPTOR_PROPERTIES MISC_OBJ_PROPERTIES LINKTARGET_PROPERTIES + GLOW_PROPERTIES SHADOW_PROPERTIES TEXT_PROPERTIES // #FontWork# @@ -163,6 +167,7 @@ static SfxItemPropertyMapEntry const * ImplGetSvxCirclePropertyMap() SHAPE_DESCRIPTOR_PROPERTIES MISC_OBJ_PROPERTIES LINKTARGET_PROPERTIES + GLOW_PROPERTIES SHADOW_PROPERTIES TEXT_PROPERTIES // #FontWork# @@ -189,6 +194,7 @@ static SfxItemPropertyMapEntry const * ImplGetSvxPolyPolygonPropertyMap() SHAPE_DESCRIPTOR_PROPERTIES MISC_OBJ_PROPERTIES LINKTARGET_PROPERTIES + GLOW_PROPERTIES SHADOW_PROPERTIES TEXT_PROPERTIES // #FontWork# @@ -218,6 +224,7 @@ static SfxItemPropertyMapEntry const * ImplGetSvxGraphicObjectPropertyMap() MISC_OBJ_PROPERTIES LINKTARGET_PROPERTIES + GLOW_PROPERTIES SHADOW_PROPERTIES TEXT_PROPERTIES // #FontWork# @@ -368,6 +375,7 @@ static SfxItemPropertyMapEntry const * ImplGetSvxAllPropertyMap() { static SfxItemPropertyMapEntry const aAllPropertyMap_Impl[] = { + GLOW_PROPERTIES SHADOW_PROPERTIES LINE_PROPERTIES LINE_PROPERTIES_START_END @@ -430,6 +438,7 @@ static SfxItemPropertyMapEntry const * ImplGetSvxOle2PropertyMap() SHAPE_DESCRIPTOR_PROPERTIES MISC_OBJ_PROPERTIES LINKTARGET_PROPERTIES + GLOW_PROPERTIES SHADOW_PROPERTIES TEXT_PROPERTIES FONTWORK_PROPERTIES @@ -681,6 +690,7 @@ static SfxItemPropertyMapEntry const * ImplGetSvxCustomShapePropertyMap() SHAPE_DESCRIPTOR_PROPERTIES MISC_OBJ_PROPERTIES LINKTARGET_PROPERTIES + GLOW_PROPERTIES SHADOW_PROPERTIES TEXT_PROPERTIES {OUString("UserDefinedAttributes"), SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0}, @@ -759,6 +769,7 @@ static comphelper::PropertyMapEntry const * ImplGetSvxDrawingDefaultsPropertyMap { static comphelper::PropertyMapEntry const aSvxDrawingDefaultsPropertyMap_Impl[] = { + GLOW_PROPERTIES SHADOW_PROPERTIES LINE_PROPERTIES_DEFAULTS FILL_PROPERTIES_BMP diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx index 344b493dae4b..721f1292e8d5 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx @@ -215,20 +215,6 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testShapeEffectPreservation, "shape-effect-p "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:softEdge/*", 0 ); // should not be present - // 5th shape with glow effect, scheme color - assertXPath(pXmlDoc, "/w:document/w:body/w:p[6]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:glow", - "rad", "101600"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[6]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:glow/a:schemeClr", - "val", "accent2"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[6]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:glow/a:schemeClr/a:satMod", - "val", "175000"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[6]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:glow/a:schemeClr/a:alpha", - "val", "40000"); - // 6th shape with reflection assertXPath(pXmlDoc, "/w:document/w:body/w:p[7]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:reflection", @@ -246,34 +232,6 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testShapeEffectPreservation, "shape-effect-p "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:reflection/*", 0 ); // should not be present - // 7th shape with several effects: glow, inner shadow and reflection - assertXPath(pXmlDoc, "/w:document/w:body/w:p[8]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:glow", - "rad", "63500"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[8]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:glow/a:schemeClr", - "val", "accent2"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[8]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:glow/a:schemeClr/*", - 2); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[8]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:innerShdw", - "blurRad", "63500"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[8]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:innerShdw", - "dir", "2700000"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[8]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:innerShdw/a:srgbClr", - "val", "000000"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[8]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:innerShdw/a:srgbClr/a:alpha", - "val", "50000"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[8]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:reflection", - "blurRad", "6350"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[8]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/wps:wsp/wps:spPr/a:effectLst/a:reflection", - "stA", "52000"); } DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testShape3DEffectPreservation, "shape-3d-effect-preservation.docx") @@ -389,17 +347,6 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testPictureEffectPreservation, "picture-effe { xmlDocPtr pXmlDoc = parseExport("word/document.xml"); - // first picture: glow effect with theme color and transformations, 3d rotation and extrusion - assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/pic:pic/pic:spPr/a:effectLst/a:glow", - "rad", "228600"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/pic:pic/pic:spPr/a:effectLst/a:glow/a:schemeClr", - "val", "accent1"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" - "wp:anchor/a:graphic/a:graphicData/pic:pic/pic:spPr/a:effectLst/a:glow/a:schemeClr/*", - 2); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:r/mc:AlternateContent/mc:Choice/w:drawing/" "wp:anchor/a:graphic/a:graphicData/pic:pic/pic:spPr/a:scene3d/a:camera", "prst", "isometricRightUp"); diff --git a/vcl/CppunitTest_vcl_bitmap_test.mk b/vcl/CppunitTest_vcl_bitmap_test.mk index 9ebef12dcfb2..dca63852a8af 100644 --- a/vcl/CppunitTest_vcl_bitmap_test.mk +++ b/vcl/CppunitTest_vcl_bitmap_test.mk @@ -15,6 +15,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,vcl_bitmap_test, \ vcl/qa/cppunit/bitmapcolor \ vcl/qa/cppunit/ScanlineToolsTest \ vcl/qa/cppunit/BitmapScaleTest \ + vcl/qa/cppunit/BitmapFilterTest \ )) $(eval $(call gb_CppunitTest_use_externals,vcl_bitmap_test,\ diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index 79f2002c8c9d..70c23d0d1a71 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -367,6 +367,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/image/ImageRepository \ vcl/source/image/ImplImage \ vcl/source/image/ImplImageTree \ + vcl/source/bitmap/BitmapFilterStackBlur \ vcl/source/helper/canvasbitmap \ vcl/source/helper/canvastools \ vcl/source/helper/commandinfoprovider \ diff --git a/vcl/qa/cppunit/BitmapFilterTest.cxx b/vcl/qa/cppunit/BitmapFilterTest.cxx new file mode 100644 index 000000000000..732c0c2dfb0b --- /dev/null +++ b/vcl/qa/cppunit/BitmapFilterTest.cxx @@ -0,0 +1,159 @@ +/* -*- 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 <cppunit/TestAssert.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> + +#include <vcl/bitmap.hxx> +#include <vcl/bitmapaccess.hxx> +#include <bitmapwriteaccess.hxx> + +#include <tools/stream.hxx> +#include <vcl/graphicfilter.hxx> + +#include <vcl/BitmapFilterStackBlur.hxx> +#include <BitmapSymmetryCheck.hxx> + +#include <chrono> + +namespace +{ +constexpr bool constWriteResultBitmap(false); +constexpr bool constEnablePerformanceTest(false); + +class BitmapFilterTest : public CppUnit::TestFixture +{ + void testBlurCorrectness(); + void testPerformance(); + + CPPUNIT_TEST_SUITE(BitmapFilterTest); + CPPUNIT_TEST(testBlurCorrectness); + CPPUNIT_TEST(testPerformance); + CPPUNIT_TEST_SUITE_END(); +}; + +void BitmapFilterTest::testBlurCorrectness() +{ + // Setup test bitmap + Size aSize(41, 31); + Bitmap aBitmap24Bit(aSize, 24); + + ScanlineFormat scanlineFormat = ScanlineFormat::NONE; + sal_uInt16 nBPP = aBitmap24Bit.GetBitCount(); + + { + long aMargin1 = 1; + long aMargin2 = 3; + BitmapScopedWriteAccess aWriteAccess(aBitmap24Bit); + scanlineFormat = aWriteAccess->GetScanlineFormat(); + aWriteAccess->Erase(COL_WHITE); + aWriteAccess->SetLineColor(COL_BLACK); + + tools::Rectangle aRectangle1(aMargin1, aMargin1, aSize.Width() - 1 - aMargin1, + aSize.Height() - 1 - aMargin1); + + tools::Rectangle aRectangle2(aMargin2, aMargin2, aSize.Width() - 1 - aMargin2, + aSize.Height() - 1 - aMargin2); + + tools::Rectangle aRectangle3(aSize.Width() / 2, aSize.Height() / 2, aSize.Width() / 2, + aSize.Height() / 2); + + aWriteAccess->DrawRect(aRectangle1); + aWriteAccess->DrawRect(aRectangle2); + aWriteAccess->DrawRect(aRectangle3); + } + + if (constWriteResultBitmap) + { + SvFileStream aStream("~/blurBefore.png", StreamMode::WRITE | StreamMode::TRUNC); + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + rFilter.compressAsPNG(aBitmap24Bit, aStream); + } + + // Perform blur + BitmapFilterStackBlur aBlurFilter(2); + aBitmap24Bit = aBlurFilter.filter(aBitmap24Bit); + + // Check the result + + if (constWriteResultBitmap) + { + SvFileStream aStream("~/blurAfter.png", StreamMode::WRITE | StreamMode::TRUNC); + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + rFilter.compressAsPNG(aBitmap24Bit, aStream); + } + + // Check blurred bitmap parameters + CPPUNIT_ASSERT_EQUAL(static_cast<long>(45), aBitmap24Bit.GetSizePixel().Width()); + CPPUNIT_ASSERT_EQUAL(static_cast<long>(35), aBitmap24Bit.GetSizePixel().Height()); + + CPPUNIT_ASSERT_EQUAL(nBPP, aBitmap24Bit.GetBitCount()); + + // Check that the bitmap is horizontally and vertically symmetrical + CPPUNIT_ASSERT(BitmapSymmetryCheck::check(aBitmap24Bit)); + + { + Bitmap::ScopedReadAccess aReadAccess(aBitmap24Bit); + CPPUNIT_ASSERT_EQUAL(scanlineFormat, aReadAccess->GetScanlineFormat()); + } +} + +void BitmapFilterTest::testPerformance() +{ + if (!constEnablePerformanceTest) + return; + + Size aSize(4000, 3000); // A rather common picture size + + // Prepare bitmap + Bitmap aBigBitmap(aSize, 24); + { + long aMargin = 500; + BitmapScopedWriteAccess aWriteAccess(aBigBitmap); + aWriteAccess->Erase(COL_WHITE); + aWriteAccess->SetLineColor(COL_BLACK); + aWriteAccess->SetFillColor(COL_BLACK); + tools::Rectangle aRectangle(aMargin, aMargin, aSize.Width() - 1 - aMargin, + aSize.Height() - 1 - aMargin); + + aWriteAccess->DrawRect(aRectangle); + } + + int nIterations = 10; + auto start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < nIterations; i++) + { + BitmapFilterStackBlur aBlurFilter(250, false); // don't extend the image + aBlurFilter.filter(aBigBitmap); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = (end - start) / nIterations; + + if (constWriteResultBitmap) + { + std::unique_ptr<SvFileStream> pStream( + new SvFileStream("~/BlurBigPerformance.png", StreamMode::WRITE | StreamMode::TRUNC)); + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + rFilter.compressAsPNG(aBigBitmap, *pStream); + + pStream.reset(new SvFileStream("~/BlurBigPerformance.txt", StreamMode::WRITE)); + pStream->WriteOString("Blur average time: "); + pStream->WriteOString(OString::number( + std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count())); + pStream->WriteOString("\n"); + } +} + +} // namespace + +CPPUNIT_TEST_SUITE_REGISTRATION(BitmapFilterTest); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/bitmap/BitmapFilterStackBlur.cxx b/vcl/source/bitmap/BitmapFilterStackBlur.cxx new file mode 100644 index 000000000000..d88480787b8b --- /dev/null +++ b/vcl/source/bitmap/BitmapFilterStackBlur.cxx @@ -0,0 +1,554 @@ +/* -*- 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/BitmapFilterStackBlur.hxx> +#include <vcl/bitmapaccess.hxx> +#include <bitmapwriteaccess.hxx> + +namespace +{ +static const sal_Int16 constMultiplyTable[255] + = { 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454, + 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512, 482, 454, + 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404, + 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, 497, 482, 468, 454, + 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291, + 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, + 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297, + 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454, + 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373, 368, 364, 359, + 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, + 287, 284, 281, 278, 274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, + 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, + 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, + 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, + 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259 }; + +static const sal_Int16 constShiftTable[255] + = { 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 }; + +class BlurSharedData +{ +public: + long mnRadius; + long mnComponentWidth; + long mnColorChannels; + long mnDiv; + std::vector<sal_uInt8> maStackBuffer; + std::vector<long> maPositionTable; + std::vector<long> maWeightTable; + + std::vector<long> mnSumVector; + std::vector<long> mnInSumVector; + std::vector<long> mnOutSumVector; + + BlurSharedData(long aRadius, long nComponentWidth, long nColorChannels) + : mnRadius(aRadius) + , mnComponentWidth(nComponentWidth) + , mnColorChannels(nColorChannels) + , mnDiv(aRadius + aRadius + 1) + , maStackBuffer(mnDiv * mnComponentWidth) + , maPositionTable(mnDiv) + , maWeightTable(mnDiv) + , mnSumVector(mnColorChannels) + , mnInSumVector(mnColorChannels) + , mnOutSumVector(mnColorChannels) + { + } + + void calculateWeightAndPositions(long nLastIndex) + { + for (long i = 0; i < mnDiv; i++) + { + maPositionTable[i] = std::min(nLastIndex, std::max(0L, i - mnRadius)); + maWeightTable[i] = mnRadius + 1 - std::abs(i - mnRadius); + } + } + + long getMultiplyValue() { return static_cast<long>(constMultiplyTable[mnRadius]); } + + long getShiftValue() { return static_cast<long>(constShiftTable[mnRadius]); } +}; + +struct SumFunction24 +{ + static inline void add(long*& pValue1, long nConstant) + { + pValue1[0] += nConstant; + pValue1[1] += nConstant; + pValue1[2] += nConstant; + } + + static inline void set(long*& pValue1, long nConstant) + { + pValue1[0] = nConstant; + pValue1[1] = nConstant; + pValue1[2] = nConstant; + } + + static inline void add(long*& pValue1, sal_uInt8*& pValue2) + { + pValue1[0] += pValue2[0]; + pValue1[1] += pValue2[1]; + pValue1[2] += pValue2[2]; + } + + static inline void add(long*& pValue1, long*& pValue2) + { + pValue1[0] += pValue2[0]; + pValue1[1] += pValue2[1]; + pValue1[2] += pValue2[2]; + } + + static inline void sub(long*& pValue1, sal_uInt8*& pValue2) + { + pValue1[0] -= pValue2[0]; + pValue1[1] -= pValue2[1]; + pValue1[2] -= pValue2[2]; + } + + static inline void sub(long*& pValue1, long*& pValue2) + { + pValue1[0] -= pValue2[0]; + pValue1[1] -= pValue2[1]; + pValue1[2] -= pValue2[2]; + } + + static inline void assignPtr(sal_uInt8*& pValue1, sal_uInt8*& pValue2) + { + pValue1[0] = pValue2[0]; + pValue1[1] = pValue2[1]; + pValue1[2] = pValue2[2]; + } + + static inline void assignMulAndShr(sal_uInt8*& result, long*& sum, long multiply, long shift) + { + result[0] = (multiply * sum[0]) >> shift; + result[1] = (multiply * sum[1]) >> shift; + result[2] = (multiply * sum[2]) >> shift; + } +}; + +struct SumFunction8 +{ + static inline void add(long*& pValue1, long nConstant) { pValue1[0] += nConstant; } + + static inline void set(long*& pValue1, long nConstant) { pValue1[0] = nConstant; } + + static inline void add(long*& pValue1, sal_uInt8*& pValue2) { pValue1[0] += pValue2[0]; } + + static inline void add(long*& pValue1, long*& pValue2) { pValue1[0] += pValue2[0]; } + + static inline void sub(long*& pValue1, sal_uInt8*& pValue2) { pValue1[0] -= pValue2[0]; } + + static inline void sub(long*& pValue1, long*& pValue2) { pValue1[0] -= pValue2[0]; } + + static inline void assignPtr(sal_uInt8*& pValue1, sal_uInt8*& pValue2) + { + pValue1[0] = pValue2[0]; + } + + static inline void assignMulAndShr(sal_uInt8*& result, long*& sum, long multiply, long shift) + { + result[0] = (multiply * sum[0]) >> shift; + } +}; + +template <typename SumFunction> +void stackBlurHorizontal(BitmapReadAccess* pReadAccess, BitmapWriteAccess* pWriteAccess, + BlurSharedData& rShared) +{ + long nWidth = pReadAccess->Width(); + long nHeight = pReadAccess->Height(); + + sal_uInt8* pStack = rShared.maStackBuffer.data(); + sal_uInt8* pStackPtr; + + long nLastIndexX = nWidth - 1; + + long nMultiplyValue = rShared.getMultiplyValue(); + long nShiftValue = rShared.getShiftValue(); + + long nRadius = rShared.mnRadius; + long nComponentWidth = rShared.mnComponentWidth; + long nDiv = rShared.mnDiv; + + Scanline pSourcePointer; + Scanline pDestinationPointer; + + long nXPosition; + long nStackIndex; + long nStackIndexStart; + long nWeight; + + long* nSum = rShared.mnSumVector.data(); + long* nInSum = rShared.mnInSumVector.data(); + long* nOutSum = rShared.mnOutSumVector.data(); + + rShared.calculateWeightAndPositions(nLastIndexX); + long* pPositionPointer = rShared.maPositionTable.data(); + long* pWeightPointer = rShared.maWeightTable.data(); + + for (long y = 0; y < nHeight; y++) + { + SumFunction::set(nSum, 0L); + SumFunction::set(nInSum, 0L); + SumFunction::set(nOutSum, 0L); + + for (long i = 0; i < nDiv; i++) + { + pSourcePointer = pReadAccess->GetScanline(pPositionPointer[i]); + + pStackPtr = &pStack[nComponentWidth * i]; + + SumFunction::assignPtr(pStackPtr, pSourcePointer); + + nWeight = pWeightPointer[i]; + + SumFunction::add(nSum, pSourcePointer[0] * nWeight); + + if (i - nRadius > 0) + { + SumFunction::add(nInSum, pSourcePointer); + } + else + { + SumFunction::add(nOutSum, pSourcePointer); + } + } + + nStackIndex = nRadius; + nXPosition = std::min(nRadius, nLastIndexX); + + pSourcePointer = pReadAccess->GetScanline(y) + nComponentWidth * nXPosition; + + for (long x = 0; x < nWidth; x++) + { + pDestinationPointer = pWriteAccess->GetScanline(y) + nComponentWidth * x; + + SumFunction::assignMulAndShr(pDestinationPointer, nSum, nMultiplyValue, nShiftValue); + + SumFunction::sub(nSum, nOutSum); + + nStackIndexStart = nStackIndex + nDiv - nRadius; + if (nStackIndexStart >= nDiv) + { + nStackIndexStart -= nDiv; + } + pStackPtr = &pStack[nComponentWidth * nStackIndexStart]; + + SumFunction::sub(nOutSum, pStackPtr); + + if (nXPosition < nLastIndexX) + { + nXPosition++; + pSourcePointer = pReadAccess->GetScanline(y) + nComponentWidth * nXPosition; + } + + SumFunction::assignPtr(pStackPtr, pSourcePointer); + + SumFunction::add(nInSum, pSourcePointer); + + SumFunction::add(nSum, nInSum); + + nStackIndex++; + if (nStackIndex >= nDiv) + { + nStackIndex = 0; + } + + pStackPtr = &pStack[nStackIndex * nComponentWidth]; + + SumFunction::add(nOutSum, pStackPtr); + SumFunction::sub(nInSum, pStackPtr); + } + } +} + +template <typename SumFunction> +void stackBlurVertical(BitmapReadAccess* pReadAccess, BitmapWriteAccess* pWriteAccess, + BlurSharedData& rShared) +{ + long nWidth = pReadAccess->Width(); + long nHeight = pReadAccess->Height(); + + sal_uInt8* pStack = rShared.maStackBuffer.data(); + sal_uInt8* pStackPtr; + + long nLastIndexY = nHeight - 1; + + long nMultiplyValue = rShared.getMultiplyValue(); + long nShiftValue = rShared.getShiftValue(); + + long nRadius = rShared.mnRadius; + long nComponentWidth = rShared.mnComponentWidth; + long nDiv = rShared.mnDiv; + + Scanline pSourcePointer; + Scanline pDestinationPointer; + + long nYPosition; + long nStackIndex; + long nStackIndexStart; + long nWeight; + + long* nSum = rShared.mnSumVector.data(); + long* nInSum = rShared.mnInSumVector.data(); + long* nOutSum = rShared.mnOutSumVector.data(); + + rShared.calculateWeightAndPositions(nLastIndexY); + long* pPositionPointer = rShared.maPositionTable.data(); + long* pWeightPointer = rShared.maWeightTable.data(); + + for (long x = 0; x < nWidth; x++) + { + SumFunction::set(nSum, 0L); + SumFunction::set(nInSum, 0L); + SumFunction::set(nOutSum, 0L); + + for (long i = 0; i < nDiv; i++) + { + pSourcePointer = pReadAccess->GetScanline(pPositionPointer[i]); + + pStackPtr = &pStack[nComponentWidth * i]; + + SumFunction::assignPtr(pStackPtr, pSourcePointer); + + nWeight = pWeightPointer[i]; + + SumFunction::add(nSum, pSourcePointer[0] * nWeight); + + if (i - nRadius > 0) + { + SumFunction::add(nInSum, pSourcePointer); + } + else + { + SumFunction::add(nOutSum, pSourcePointer); + } + } + + nStackIndex = nRadius; + nYPosition = std::min(nRadius, nLastIndexY); + + pSourcePointer = pReadAccess->GetScanline(nYPosition) + nComponentWidth * x; + + for (long y = 0; y < nHeight; y++) + { + pDestinationPointer = pWriteAccess->GetScanline(y) + nComponentWidth * x; + + SumFunction::assignMulAndShr(pDestinationPointer, nSum, nMultiplyValue, nShiftValue); + + SumFunction::sub(nSum, nOutSum); + + nStackIndexStart = nStackIndex + nDiv - nRadius; + if (nStackIndexStart >= nDiv) + { + nStackIndexStart -= nDiv; + } + pStackPtr = &pStack[nComponentWidth * nStackIndexStart]; + + SumFunction::sub(nOutSum, pStackPtr); + + if (nYPosition < nLastIndexY) + { + nYPosition++; + pSourcePointer = pReadAccess->GetScanline(nYPosition) + nComponentWidth * x; + } + + SumFunction::assignPtr(pStackPtr, pSourcePointer); + + SumFunction::add(nInSum, pSourcePointer); + + SumFunction::add(nSum, nInSum); + + nStackIndex++; + if (nStackIndex >= nDiv) + { + nStackIndex = 0; + } + + pStackPtr = &pStack[nStackIndex * nComponentWidth]; + + SumFunction::add(nOutSum, pStackPtr); + + SumFunction::sub(nInSum, pStackPtr); + } + } +} + +void stackBlur24(Bitmap& rBitmap, sal_Int32 nRadius, sal_Int32 nComponentWidth) +{ + // Limit radius + nRadius = std::clamp<sal_Int32>(nRadius, 2, 254); + const long nColorChannels = 3; // 3 color channel + BlurSharedData aData(nRadius, nComponentWidth, nColorChannels); + + { + Bitmap::ScopedReadAccess pReadAccess(rBitmap); + BitmapScopedWriteAccess pWriteAccess(rBitmap); + + stackBlurHorizontal<SumFunction24>(pReadAccess.get(), pWriteAccess.get(), aData); + } + + { + Bitmap::ScopedReadAccess pReadAccess(rBitmap); + BitmapScopedWriteAccess pWriteAccess(rBitmap); + + stackBlurVertical<SumFunction24>(pReadAccess.get(), pWriteAccess.get(), aData); + } +} + +void stackBlur8(Bitmap& rBitmap, sal_Int32 nRadius, sal_Int32 nComponentWidth) +{ + // Limit radius + nRadius = std::clamp<sal_Int32>(nRadius, 2, 254); + const long nColorChannels = 1; // 1 color channel + BlurSharedData aData(nRadius, nComponentWidth, nColorChannels); + + { + Bitmap::ScopedReadAccess pReadAccess(rBitmap); + BitmapScopedWriteAccess pWriteAccess(rBitmap); + + stackBlurHorizontal<SumFunction8>(pReadAccess.get(), pWriteAccess.get(), aData); + } + + { + Bitmap::ScopedReadAccess pReadAccess(rBitmap); + BitmapScopedWriteAccess pWriteAccess(rBitmap); + + stackBlurVertical<SumFunction8>(pReadAccess.get(), pWriteAccess.get(), aData); + } +} + +void centerExtendBitmap(Bitmap& rBitmap, sal_Int32 nExtendSize, Color aColor) +{ + const Size& rSize = rBitmap.GetSizePixel(); + const Size aNewSize(rSize.Width() + nExtendSize * 2, rSize.Height() + nExtendSize * 2); + + Bitmap aNewBitmap(aNewSize, rBitmap.GetBitCount()); + + { + Bitmap::ScopedReadAccess pReadAccess(rBitmap); + BitmapScopedWriteAccess pWriteAccess(aNewBitmap); + + long nWidthBorder = nExtendSize + rSize.Width(); + long nHeightBorder = nExtendSize + rSize.Height(); + + for (long y = 0; y < aNewSize.Height(); y++) + { + for (long x = 0; x < aNewSize.Width(); x++) + { + if (y < nExtendSize || y >= nHeightBorder || x < nExtendSize || x >= nWidthBorder) + { + pWriteAccess->SetPixel(y, x, aColor); + } + else + { + pWriteAccess->SetPixel(y, x, + pReadAccess->GetPixel(y - nExtendSize, x - nExtendSize)); + } + } + } + } + rBitmap = aNewBitmap; +} + +} // end anonymous namespace + +/** + * Implementation of stack blur - a fast Gaussian blur approximation. + * nRadius - blur radious, valid values are between 2 and 254 + * bExtend - extend the bitmap in all directions by the radius + * + * Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com> + * (http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html) + * + * Additionally eferences and implementations: + * - Blur.js by Jacob Kelley + * (http://www.blurjs.com) + * - BlurEffectForAndroidDesign by Nicolas Pomepuy + * (https://github.com/PomepuyN/BlurEffectForAndroidDesign) + * - StackBluriOS by Thomas Landspurg + * (https://github.com/tomsoft1/StackBluriOS) + * - stackblur.cpp by Benjamin Yates + * (https://gist.github.com/benjamin9999/3809142) + * - stack blur in fog 2D graphic library by Petr Kobalicek + * (https://code.google.com/p/fog/) + * + */ +BitmapFilterStackBlur::BitmapFilterStackBlur(sal_Int32 nRadius, bool bExtend) + : mnRadius(nRadius) + , mbExtend(bExtend) +{ +} + +BitmapFilterStackBlur::~BitmapFilterStackBlur() {} + +BitmapEx BitmapFilterStackBlur::execute(BitmapEx const& rBitmapEx) const +{ + Bitmap aBitmap = rBitmapEx.GetBitmap(); + Bitmap result = filter(aBitmap); + return BitmapEx(result, rBitmapEx.GetMask()); +} + +Bitmap BitmapFilterStackBlur::filter(Bitmap const& rBitmap) const +{ + Bitmap bitmapCopy(rBitmap); + ScanlineFormat nScanlineFormat; + { + Bitmap::ScopedReadAccess pReadAccess(bitmapCopy); + nScanlineFormat = pReadAccess->GetScanlineFormat(); + } + + if (nScanlineFormat == ScanlineFormat::N24BitTcRgb + || nScanlineFormat == ScanlineFormat::N24BitTcBgr + || nScanlineFormat == ScanlineFormat::N32BitTcMask + || nScanlineFormat == ScanlineFormat::N32BitTcBgra) + { + int nComponentWidth = (nScanlineFormat == ScanlineFormat::N32BitTcMask + || nScanlineFormat == ScanlineFormat::N32BitTcBgra) + ? 4 + : 3; + + if (mbExtend) + { + centerExtendBitmap(bitmapCopy, mnRadius, COL_WHITE); + } + + stackBlur24(bitmapCopy, mnRadius, nComponentWidth); + } + else if (nScanlineFormat == ScanlineFormat::N8BitPal) + { + int nComponentWidth = 1; + + if (mbExtend) + { + centerExtendBitmap(bitmapCopy, mnRadius, COL_WHITE); + } + + stackBlur8(bitmapCopy, mnRadius, nComponentWidth); + } + + return bitmapCopy; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/outdev/bitmap.cxx b/vcl/source/outdev/bitmap.cxx index 0e1f3979e5ba..6e707e953ab1 100644 --- a/vcl/source/outdev/bitmap.cxx +++ b/vcl/source/outdev/bitmap.cxx @@ -21,6 +21,7 @@ #include <vcl/bitmap.hxx> #include <vcl/bitmapex.hxx> +#include <vcl/BitmapFilterStackBlur.hxx> #include <vcl/bitmapaccess.hxx> #include <vcl/canvastools.hxx> #include <vcl/gdimtf.hxx> @@ -48,6 +49,9 @@ #include <tools/helpers.hxx> #include <tools/debug.hxx> +#include <vcl/dibtools.hxx> +#include <tools/stream.hxx> + void OutputDevice::DrawBitmap( const Point& rDestPt, const Bitmap& rBitmap ) { assert(!is_double_buffered_window()); diff --git a/xmloff/inc/xmlsdtypes.hxx b/xmloff/inc/xmlsdtypes.hxx index b49639d43a94..6b060d322f7c 100644 --- a/xmloff/inc/xmlsdtypes.hxx +++ b/xmloff/inc/xmlsdtypes.hxx @@ -68,6 +68,7 @@ #define XML_SD_TYPE_IMAGE_SCALE_MODE (XML_SD_TYPES_START + 34 ) #define XML_SD_TYPE_LINECAP (XML_SD_TYPES_START + 35 ) #define XML_SD_TYPE_FITTOSIZE_AUTOFIT (XML_SD_TYPES_START + 36 ) +#define XML_SD_TYPE_GLOW (XML_SD_TYPES_START + 37 ) ////////////////////////////////////////////////////////////////////////////// // 3D property types diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index 2005e929a684..46125e067982 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -929,6 +929,9 @@ namespace xmloff::token { TOKEN( "gcd", XML_GCD ), TOKEN( "generator", XML_GENERATOR ), TOKEN( "geq", XML_GEQ ), + TOKEN( "glow", XML_GLOW ), + TOKEN( "glow-radius", XML_GLOW_RADIUS ), + TOKEN( "glow-color", XML_GLOW_COLOR ), TOKEN( "gouraud", XML_GOURAUD ), TOKEN( "gradient", XML_GRADIENT ), TOKEN( "angle", XML_GRADIENT_ANGLE ), diff --git a/xmloff/source/draw/sdpropls.cxx b/xmloff/source/draw/sdpropls.cxx index 19b325f8e1c5..4cedc7c148c0 100644 --- a/xmloff/source/draw/sdpropls.cxx +++ b/xmloff/source/draw/sdpropls.cxx @@ -152,6 +152,11 @@ const XMLPropertyMapEntry aXMLSDProperties[] = GMAP( "ShadowColor", XML_NAMESPACE_DRAW, XML_SHADOW_COLOR, XML_TYPE_COLOR, 0 ), GMAP( "ShadowTransparence", XML_NAMESPACE_DRAW, XML_SHADOW_OPACITY, XML_TYPE_NEG_PERCENT, 0 ), + // glow attributes + GMAPV( "GlowEffect", XML_NAMESPACE_LO_EXT, XML_GLOW, XML_SD_TYPE_GLOW , 0, SvtSaveOptions::ODFVER_012_EXT_COMPAT ), + GMAPV( "GlowEffectRad", XML_NAMESPACE_LO_EXT, XML_GLOW_RADIUS, XML_TYPE_MEASURE , 0, SvtSaveOptions::ODFVER_012_EXT_COMPAT ), + GMAPV( "GlowEffectColor", XML_NAMESPACE_LO_EXT, XML_GLOW_COLOR, XML_TYPE_COLOR , 0, SvtSaveOptions::ODFVER_012_EXT_COMPAT ), + // graphic attributes GMAP( "GraphicColorMode", XML_NAMESPACE_DRAW, XML_COLOR_MODE, XML_TYPE_COLOR_MODE, 0 ), // exists in SW, too, with same property name GMAP( "AdjustLuminance", XML_NAMESPACE_DRAW, XML_LUMINANCE, XML_TYPE_PERCENT16, 0 ), // signed? exists in SW, too, with same property name @@ -1019,6 +1024,11 @@ const XMLPropertyHandler* XMLSdPropHdlFactory::GetPropertyHandler( sal_Int32 nTy pHdl = new XMLNamedBoolPropertyHdl( GetXMLToken(XML_VISIBLE), GetXMLToken(XML_HIDDEN) ); break; } + case XML_SD_TYPE_GLOW : + { + pHdl = new XMLNamedBoolPropertyHdl( GetXMLToken(XML_VISIBLE), GetXMLToken(XML_HIDDEN) ); + break; + } case XML_TYPE_SD_MIRROR: { pHdl = new XMLNamedBoolPropertyHdl( GetXMLToken(XML_HORIZONTAL), GetXMLToken(XML_NONE) ); diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index 34e145b30e46..31dae860a15e 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -847,6 +847,9 @@ gap-width gcd generator geq +glow +glow-radius +glow-color gouraud gradient angle |