summaryrefslogtreecommitdiff
path: root/drawinglayer/source
diff options
context:
space:
mode:
authorTamas Bunth <tamas.bunth@collabora.co.uk>2020-01-21 19:04:13 +0100
committerTamás Bunth <btomi96@gmail.com>2020-03-03 15:52:47 +0100
commitf9fc420dceb1ece2c98767da16a21aaff771f140 (patch)
tree299b9c856a3567ee85af11b7b314d2d02a03420b /drawinglayer/source
parent224ab38f747dcafe711c10b54ad53c52bda9e41d (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.cxx80
-rw-r--r--drawinglayer/source/primitive2d/Tools.cxx2
-rw-r--r--drawinglayer/source/primitive2d/glowprimitive2d.cxx87
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.cxx46
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()));