drawinglayer/source/primitive2d/shadowprimitive2d.cxx   |   71 ++++++++---
 drawinglayer/source/processor2d/vclpixelprocessor2d.cxx |   10 +
 drawinglayer/source/tools/primitive2dxmldump.cxx        |   33 +++++
 include/drawinglayer/primitive2d/baseprimitive2d.hxx    |    7 +
 include/drawinglayer/primitive2d/shadowprimitive2d.hxx  |    8 +
 svx/CppunitTest_svx_unit.mk                             |    1 
 svx/qa/unit/data/table-shadow-blur.pptx                 |binary
 svx/qa/unit/table.cxx                                   |  100 ++++++++++++++++
 svx/source/table/viewcontactoftableobj.cxx              |   13 +-
 9 files changed, 221 insertions(+), 22 deletions(-)

New commits:
commit 52524e52e8f061aa3f0c63219d954d2d5aefd059
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Mon Sep 20 11:26:53 2021 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Tue Sep 28 19:19:19 2021 +0200

    tdf#144091 svx: fix unwanted blur of shadow from table cell fill
    
    Initial render support for shadows of table shapes were added in commit
    a75bf43a8d6c5dec6dcc86908c142ceec541aa8c (tdf#129961 svx: add rendering
    for table shadow as direct format, 2020-12-02).
    
    That already noticed a trick with the shadow of table shapes: the shadow
    is generate from the cell fill and the border, but not from the text.
    
    An additional trick is that when blur is enabled for the table shape's
    shadow, then only the border should be blurred, not the cell fill.
    
    In the bug document's case, the effective cell background was gray, with
    a semi-transparent red shadow. We used to render cc0000 with blur and
    cccccc without blur, now we correctly render cca3a3, matching
    PowerPoint.
    
    (cherry picked from commit 37a52d30bbfcf1d073779b50139c4dafa507be4b)
    
    Conflicts:
            drawinglayer/source/primitive2d/shadowprimitive2d.cxx
            drawinglayer/source/tools/primitive2dxmldump.cxx
            
include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx
    
    Change-Id: I7326a5f6254cf19b2d05181084c78e734ff7a7b4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/122383
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/122760
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx 
b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
index f0efd64c7bdc..516cc0c0bd4a 100644
--- a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
@@ -30,6 +30,27 @@ using namespace com::sun::star;
 
 namespace drawinglayer::primitive2d
 {
+namespace
+{
+void get2DDecompositionOfChildren(const ShadowPrimitive2D& rPrimitive,
+                                  Primitive2DDecompositionVisitor& rVisitor,
+                                  const Primitive2DContainer& rChildren)
+{
+    if (rChildren.empty())
+        return;
+
+    // create a modifiedColorPrimitive containing the shadow color and the 
content
+    const basegfx::BColorModifierSharedPtr aBColorModifier
+        = 
std::make_shared<basegfx::BColorModifier_replace>(rPrimitive.getShadowColor());
+    const Primitive2DReference xRefA(new ModifiedColorPrimitive2D(rChildren, 
aBColorModifier));
+    Primitive2DContainer aSequenceB{ xRefA };
+
+    // build transformed primitiveVector with shadow offset and add to target
+    rVisitor.append(
+        new TransformPrimitive2D(rPrimitive.getShadowTransform(), 
std::move(aSequenceB)));
+}
+}
+
         ShadowPrimitive2D::ShadowPrimitive2D(
             const basegfx::B2DHomMatrix& rShadowTransform,
             const basegfx::BColor& rShadowColor,
@@ -66,21 +87,41 @@ namespace drawinglayer::primitive2d
 
         void 
ShadowPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& 
rVisitor, const geometry::ViewInformation2D& /*rViewInformation*/) const
         {
-            if(getChildren().empty())
-                return;
-
-            // create a modifiedColorPrimitive containing the shadow color and 
the content
-            const basegfx::BColorModifierSharedPtr aBColorModifier =
-                std::make_shared<basegfx::BColorModifier_replace>(
-                    getShadowColor());
-            const Primitive2DReference xRefA(
-                new ModifiedColorPrimitive2D(
-                    getChildren(),
-                    aBColorModifier));
-            const Primitive2DContainer aSequenceB { xRefA };
-
-            // build transformed primitiveVector with shadow offset and add to 
target
-            rVisitor.append(new TransformPrimitive2D(getShadowTransform(), 
aSequenceB));
+            get2DDecompositionOfChildren(*this, rVisitor, getChildren());
+        }
+
+        void ShadowPrimitive2D::get2DDecompositionWithoutBlur(
+            Primitive2DDecompositionVisitor& rVisitor,
+            const geometry::ViewInformation2D& /*rViewInformation*/) const
+        {
+            Primitive2DContainer aChildren;
+            // Only decompose children which are not blurred (they opted in 
for this).
+            std::copy_if(getChildren().begin(), getChildren().end(), 
std::back_inserter(aChildren),
+                         [](const Primitive2DReference& xChild) {
+                             auto pChild
+                                 = 
dynamic_cast<primitive2d::BufferedDecompositionPrimitive2D*>(
+                                     xChild.get());
+                             return pChild && pChild->getExcludeFromBlur();
+                         });
+
+            get2DDecompositionOfChildren(*this, rVisitor, aChildren);
+        }
+
+        void ShadowPrimitive2D::get2DDecompositionWithBlur(
+            Primitive2DDecompositionVisitor& rVisitor,
+            const geometry::ViewInformation2D& /*rViewInformation*/) const
+        {
+            // Only decompose children which are blurred (which is the 
default).
+            Primitive2DContainer aChildren;
+            std::copy_if(getChildren().begin(), getChildren().end(), 
std::back_inserter(aChildren),
+                         [](const Primitive2DReference& xChild) {
+                             auto pChild
+                                 = 
dynamic_cast<primitive2d::BufferedDecompositionPrimitive2D*>(
+                                     xChild.get());
+                             return !pChild || !pChild->getExcludeFromBlur();
+                         });
+
+            get2DDecompositionOfChildren(*this, rVisitor, aChildren);
         }
 
         // provide unique ID
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index 47f39e87bf8b..393c40c90842 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -1160,10 +1160,18 @@ void 
VclPixelProcessor2D::processShadowPrimitive2D(const primitive2d::ShadowPrim
     impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
     if (aBufferDevice.isVisible())
     {
+        // Process children which don't want blur.
+        primitive2d::Primitive2DContainer aContainer;
+        rCandidate.get2DDecompositionWithoutBlur(aContainer, 
getViewInformation2D());
+        process(aContainer);
+
+        // Process children which want blur.
         OutputDevice* pLastOutputDevice = mpOutputDevice;
         mpOutputDevice = &aBufferDevice.getContent();
 
-        process(rCandidate);
+        aContainer.clear();
+        rCandidate.get2DDecompositionWithBlur(aContainer, 
getViewInformation2D());
+        process(aContainer);
 
         const tools::Rectangle 
aRect(static_cast<tools::Long>(std::floor(aRange.getMinX())),
                                      
static_cast<tools::Long>(std::floor(aRange.getMinY())),
diff --git a/drawinglayer/source/tools/primitive2dxmldump.cxx 
b/drawinglayer/source/tools/primitive2dxmldump.cxx
index 203f6d008d15..1342fd39354f 100644
--- a/drawinglayer/source/tools/primitive2dxmldump.cxx
+++ b/drawinglayer/source/tools/primitive2dxmldump.cxx
@@ -517,11 +517,42 @@ void Primitive2dXmlDump::decomposeAndWrite(
                 break;
             }
 
+            case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D:
+            {
+                // ModifiedColorPrimitive2D.
+                rWriter.startElement("modifiedColor");
+                drawinglayer::primitive2d::Primitive2DContainer 
aPrimitiveContainer;
+                pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
+                                                   
drawinglayer::geometry::ViewInformation2D());
+                decomposeAndWrite(aPrimitiveContainer, rWriter);
+                rWriter.endElement();
+                break;
+            }
+
             default:
             {
-                rWriter.startElement("unhandled");
+                OString aName("unhandled");
+                switch (nId)
+                {
+                    case PRIMITIVE2D_ID_RANGE_SVX | 14: // 
PRIMITIVE2D_ID_SDRCELLPRIMITIVE2D
+                    {
+                        aName = "sdrCell";
+                        break;
+                    }
+                }
+                rWriter.startElement(aName);
                 rWriter.attribute("id", OUStringToOString(sCurrentElementTag, 
RTL_TEXTENCODING_UTF8));
                 rWriter.attribute("idNumber", nId);
+
+                auto pBufferedDecomposition
+                    = dynamic_cast<const 
BufferedDecompositionPrimitive2D*>(pBasePrimitive);
+                if (pBufferedDecomposition)
+                {
+                    rWriter.attribute(
+                        "excludeFromBlur",
+                        
OString::boolean(pBufferedDecomposition->getExcludeFromBlur()));
+                }
+
                 drawinglayer::primitive2d::Primitive2DContainer 
aPrimitiveContainer;
                 pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
                                                    
drawinglayer::geometry::ViewInformation2D());
diff --git a/include/drawinglayer/primitive2d/baseprimitive2d.hxx 
b/include/drawinglayer/primitive2d/baseprimitive2d.hxx
index 45d0e3519c40..b94218b07111 100644
--- a/include/drawinglayer/primitive2d/baseprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/baseprimitive2d.hxx
@@ -204,6 +204,9 @@ private:
     /// a sequence used for buffering the last create2DDecomposition() result
     Primitive2DContainer maBuffered2DDecomposition;
 
+    /// When blur is invoked on a list of primitives, exclude this primitive 
from the blur effect.
+    bool mbExcludeFromBlur = false;
+
 protected:
     /** access methods to maBuffered2DDecomposition. The usage of this methods 
may allow
         later thread-safe stuff to be added if needed. Only to be used by 
getDecomposition()
@@ -237,6 +240,10 @@ public:
     virtual void
     get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor,
                        const geometry::ViewInformation2D& rViewInformation) 
const override;
+
+    void setExcludeFromBlur(bool bExcludeFromBlur) { mbExcludeFromBlur = 
bExcludeFromBlur; }
+
+    bool getExcludeFromBlur() const { return mbExcludeFromBlur; }
 };
 
 } // end of namespace drawinglayer::primitive2d
diff --git a/include/drawinglayer/primitive2d/shadowprimitive2d.hxx 
b/include/drawinglayer/primitive2d/shadowprimitive2d.hxx
index 37258d8f34ed..7bd223847f8d 100644
--- a/include/drawinglayer/primitive2d/shadowprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/shadowprimitive2d.hxx
@@ -76,6 +76,14 @@ namespace drawinglayer::primitive2d
             ///  create decomposition
             virtual void get2DDecomposition(Primitive2DDecompositionVisitor& 
rVisitor, const geometry::ViewInformation2D& rViewInformation) const override;
 
+            void get2DDecompositionWithoutBlur(
+                Primitive2DDecompositionVisitor& rVisitor,
+                const geometry::ViewInformation2D& rViewInformation) const;
+
+            void
+            get2DDecompositionWithBlur(Primitive2DDecompositionVisitor& 
rVisitor,
+                                       const geometry::ViewInformation2D& 
rViewInformation) const;
+
             /// provide unique ID
             virtual sal_uInt32 getPrimitive2DID() const override;
         };
diff --git a/svx/CppunitTest_svx_unit.mk b/svx/CppunitTest_svx_unit.mk
index 598d788392b4..7401d37808b9 100644
--- a/svx/CppunitTest_svx_unit.mk
+++ b/svx/CppunitTest_svx_unit.mk
@@ -28,6 +28,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,svx_unit, \
     svx/qa/unit/classicshapes \
        svx/qa/unit/sdr \
        svx/qa/unit/svdraw \
+       svx/qa/unit/table \
        svx/qa/unit/unodraw \
        svx/qa/unit/xoutdev \
        svx/qa/unit/XTableImportExportTest \
diff --git a/svx/qa/unit/data/table-shadow-blur.pptx 
b/svx/qa/unit/data/table-shadow-blur.pptx
new file mode 100644
index 000000000000..959940e09296
Binary files /dev/null and b/svx/qa/unit/data/table-shadow-blur.pptx differ
diff --git a/svx/qa/unit/table.cxx b/svx/qa/unit/table.cxx
new file mode 100644
index 000000000000..4419892a42ba
--- /dev/null
+++ b/svx/qa/unit/table.cxx
@@ -0,0 +1,100 @@
+/* -*- 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 <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+#include <test/xmltesttools.hxx>
+
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+
+#include <drawinglayer/tools/primitive2dxmldump.hxx>
+#include <rtl/ustring.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/unopage.hxx>
+#include <vcl/virdev.hxx>
+#include <sdr/contact/objectcontactofobjlistpainter.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Tests for svx/source/table/ code.
+class Test : public test::BootstrapFixture, public unotest::MacrosTest, public 
XmlTestTools
+{
+protected:
+    uno::Reference<lang::XComponent> mxComponent;
+
+public:
+    virtual void setUp() override
+    {
+        test::BootstrapFixture::setUp();
+        mxDesktop.set(frame::Desktop::create(m_xContext));
+    }
+
+    virtual void tearDown() override
+    {
+        if (mxComponent.is())
+        {
+            mxComponent->dispose();
+        }
+        test::BootstrapFixture::tearDown();
+    }
+    uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
+
+    drawinglayer::primitive2d::Primitive2DContainer
+    renderPageToPrimitives(const uno::Reference<drawing::XDrawPage>& 
xDrawPage);
+};
+
+drawinglayer::primitive2d::Primitive2DContainer
+Test::renderPageToPrimitives(const uno::Reference<drawing::XDrawPage>& 
xDrawPage)
+{
+    auto pDrawPage = dynamic_cast<SvxDrawPage*>(xDrawPage.get());
+    CPPUNIT_ASSERT(pDrawPage);
+    SdrPage* pSdrPage = pDrawPage->GetSdrPage();
+    ScopedVclPtrInstance<VirtualDevice> aVirtualDevice;
+    sdr::contact::ObjectContactOfObjListPainter aObjectContact(*aVirtualDevice,
+                                                               { 
pSdrPage->GetObj(0) }, nullptr);
+    const sdr::contact::ViewObjectContact& rDrawPageVOContact
+        = pSdrPage->GetViewContact().GetViewObjectContact(aObjectContact);
+    sdr::contact::DisplayInfo aDisplayInfo;
+    return rDrawPageVOContact.getPrimitive2DSequenceHierarchy(aDisplayInfo);
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTableShadowBlur)
+{
+    // Given a document containing a table with a blurry shadow:
+    test::Directories aDirectories;
+    OUString aURL = 
aDirectories.getURLFromSrc(u"svx/qa/unit/data/table-shadow-blur.pptx");
+    getComponent() = loadFromDesktop(aURL);
+
+    // When rendering the table shadow to primitives:
+    uno::Reference<drawing::XDrawPagesSupplier> 
xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+    uno::Reference<drawing::XDrawPage> 
xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+                                                 uno::UNO_QUERY);
+    drawinglayer::primitive2d::Primitive2DContainer xPrimitiveSequence
+        = renderPageToPrimitives(xDrawPage);
+
+    // Then make sure that the cell fill part of the shadow is excluded from 
blurring:
+    drawinglayer::Primitive2dXmlDump aDumper;
+    xmlDocUniquePtr pDocument = aDumper.dumpAndParse(xPrimitiveSequence);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: true
+    // - Actual  : false
+    // i.e. blur was applied on both the cell fill and the cell border.
+    assertXPath(pDocument, "//shadow/transform/modifiedColor/sdrCell[1]", 
"excludeFromBlur",
+                "true");
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/viewcontactoftableobj.cxx 
b/svx/source/table/viewcontactoftableobj.cxx
index bd950a02f7fb..409e099ae3e5 100644
--- a/svx/source/table/viewcontactoftableobj.cxx
+++ b/svx/source/table/viewcontactoftableobj.cxx
@@ -320,14 +320,16 @@ namespace sdr::contact
                                         aRetval.append(xCellReference);
                                     }
 
-                                    // Create cell primitive without text.
+                                    // Create cell primitive without text and 
blur.
                                     aAttribute
                                         = 
drawinglayer::primitive2d::createNewSdrFillTextAttribute(
                                             rCellItemSet, nullptr);
+                                    rtl::Reference pCellReference
+                                        = new 
drawinglayer::primitive2d::SdrCellPrimitive2D(
+                                            aCellMatrix, aAttribute);
+                                    pCellReference->setExcludeFromBlur(true);
                                     const 
drawinglayer::primitive2d::Primitive2DReference
-                                        xCellReference(
-                                            new 
drawinglayer::primitive2d::SdrCellPrimitive2D(
-                                                aCellMatrix, aAttribute));
+                                        xCellReference(pCellReference.get());
                                     aRetvalForShadow.append(xCellReference);
                                 }
                             }
@@ -377,7 +379,8 @@ namespace sdr::contact
                                 aTransform,
                                 aCellBorderPrimitives));
 
-                        // Borders are always the same for shadow as well.
+                        // Borders are always the same for shadow as well, and 
implicitly included
+                        // in blur.
                         aRetvalForShadow.append(new 
drawinglayer::primitive2d::TransformPrimitive2D(
                             aTransform, aCellBorderPrimitives));
                     }

Reply via email to