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 /drawinglayer/source | |
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>
Diffstat (limited to 'drawinglayer/source')
-rw-r--r-- | drawinglayer/source/attribute/sdrglowattribute.cxx | 80 | ||||
-rw-r--r-- | drawinglayer/source/primitive2d/Tools.cxx | 2 | ||||
-rw-r--r-- | drawinglayer/source/primitive2d/glowprimitive2d.cxx | 87 | ||||
-rw-r--r-- | drawinglayer/source/processor2d/vclpixelprocessor2d.cxx | 46 |
4 files changed, 215 insertions, 0 deletions
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())); |