sd/Library_sd.mk                                      |    1 
 sd/qa/unit/tiledrendering/data/SlideRenderingTest.odp |binary
 sd/qa/unit/tiledrendering/tiledrendering.cxx          |   53 ++++++
 sd/source/ui/inc/SlideshowLayerRenderer.hxx           |   36 ++++
 sd/source/ui/inc/unomodel.hxx                         |    3 
 sd/source/ui/tools/SlideshowLayerRenderer.cxx         |  146 ++++++++++++++++++
 sd/source/ui/unoidl/unomodel.cxx                      |  124 ++++++++++-----
 7 files changed, 327 insertions(+), 36 deletions(-)

New commits:
commit 51d99782ddbddd5f5492c27a7150839a80b93dc2
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Thu Aug 8 15:44:47 2024 +0200
Commit:     Szymon Kłos <szymon.k...@collabora.com>
CommitDate: Wed Sep 4 14:33:37 2024 +0200

    render slide layers with VCL / drawinglayer primitives
    
    Instead of using slideshow module to render the slide layers, use
    the VCL / drawinglayer instead, which makes it possible to render
    transparency correctly and fast.
    
    Change-Id: Idfc9fcc8bc0e2bc139df93d1805f7a19fb1bce1f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171640
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Szymon Kłos <szymon.k...@collabora.com>

diff --git a/sd/Library_sd.mk b/sd/Library_sd.mk
index 7f526c33a061..099a8b49ce65 100644
--- a/sd/Library_sd.mk
+++ b/sd/Library_sd.mk
@@ -429,6 +429,7 @@ $(eval $(call gb_Library_add_exception_objects,sd,\
        sd/source/ui/tools/IdleDetection \
        sd/source/ui/tools/PreviewRenderer \
        sd/source/ui/tools/SdGlobalResourceContainer \
+       sd/source/ui/tools/SlideshowLayerRenderer \
        sd/source/ui/tools/SlotStateListener \
        sd/source/ui/tools/TimerBasedTaskExecution \
        sd/source/ui/uitest/uiobject \
diff --git a/sd/qa/unit/tiledrendering/data/SlideRenderingTest.odp 
b/sd/qa/unit/tiledrendering/data/SlideRenderingTest.odp
new file mode 100644
index 000000000000..4c7d4f101e32
Binary files /dev/null and 
b/sd/qa/unit/tiledrendering/data/SlideRenderingTest.odp differ
diff --git a/sd/qa/unit/tiledrendering/tiledrendering.cxx 
b/sd/qa/unit/tiledrendering/tiledrendering.cxx
index 3f91ad463f49..bb57cddf4bbc 100644
--- a/sd/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sd/qa/unit/tiledrendering/tiledrendering.cxx
@@ -40,6 +40,7 @@
 #include <DrawDocShell.hxx>
 #include <ViewShellBase.hxx>
 #include <ViewShell.hxx>
+#include <SlideshowLayerRenderer.hxx>
 #include <sdpage.hxx>
 #include <unomodel.hxx>
 #include <drawdoc.hxx>
@@ -55,6 +56,8 @@
 #include <vcl/virdev.hxx>
 #include <o3tl/string_view.hxx>
 #include <sfx2/sidebar/Sidebar.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <vcl/filter/PngImageWriter.hxx>
 
 #include <chrono>
 #include <cstdlib>
@@ -3165,6 +3168,56 @@ CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, 
testPresentationInfo)
     }
 }
 
+CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testSlideshowLayeredRendering)
+{
+    // Check rendering of slideshow layers (as in the document):
+    // - background layer
+    // - master slide layer
+    // - main slide layer
+    // The doucment has nothing set for the background, so it should be 
application color = white
+    // On the master slide there is a (blue) rectangle on the right side - 
top-left should be transparent
+    // On the main slide there is a (green) rectanlge on the top-left size - 
right side should be transparent
+    // enable layer output to PNG files
+    const bool bOutputPNG = false;
+    SdXImpressDocument* pXImpressDocument = 
createDoc("SlideRenderingTest.odp");
+    
pXImpressDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+    sd::ViewShell* pViewShell = 
pXImpressDocument->GetDocShell()->GetViewShell();
+    CPPUNIT_ASSERT(pViewShell);
+    SdPage* pPage = pViewShell->GetActualPage();
+    CPPUNIT_ASSERT(pPage);
+    sal_Int32 nViewWidth = 2000;
+    sal_Int32 nViewHeight = 2000;
+    CPPUNIT_ASSERT(pXImpressDocument->createSlideRenderer(0, nViewWidth, 
nViewHeight, true, true));
+    CPPUNIT_ASSERT_EQUAL(2000, nViewWidth);
+    CPPUNIT_ASSERT_EQUAL(1125, nViewHeight); // adjusted to the slide aspect 
ratio
+    std::vector<sal_uInt8> pBuffer(nViewWidth * nViewHeight * 4);
+    bool bIsBitmapLayer = false;
+    OUString rJsonMsg;
+
+    CPPUNIT_ASSERT(!pXImpressDocument->renderNextSlideLayer(pBuffer.data(), 
bIsBitmapLayer, rJsonMsg));
+    CPPUNIT_ASSERT(bIsBitmapLayer);
+    // TODO - check JSON content
+    // printf ("1 %s

", rJsonMsg.toUtf8().getStr());
+    {
+        BitmapEx aBitmapEx = vcl::bitmap::CreateFromData(pBuffer.data(), 
nViewWidth, nViewHeight, nViewWidth * 4, /*nBitsPerPixel*/32, true, true);
+        if (bOutputPNG)
+        {
+            SvFileStream aStream("/home/quikee/XXX_01.png", StreamMode::WRITE 
| StreamMode::TRUNC);
+            vcl::PngImageWriter aPNGWriter(aStream);
+            aPNGWriter.write(aBitmapEx);
+        }
+
+        // bottom-left corner
+        CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0x00000000), 
aBitmapEx.GetPixelColor(20, nViewHeight - 20));
+
+        // bottom-right corner
+        CPPUNIT_ASSERT_EQUAL(Color(0xff, 0xd5, 0x46), 
aBitmapEx.GetPixelColor(nViewWidth - 20, nViewHeight - 20));
+    }
+    // should return true - no more content
+    CPPUNIT_ASSERT(pXImpressDocument->renderNextSlideLayer(pBuffer.data(), 
bIsBitmapLayer, rJsonMsg));
+    pXImpressDocument->postSlideshowCleanup();
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/inc/SlideshowLayerRenderer.hxx 
b/sd/source/ui/inc/SlideshowLayerRenderer.hxx
new file mode 100644
index 000000000000..28525839d579
--- /dev/null
+++ b/sd/source/ui/inc/SlideshowLayerRenderer.hxx
@@ -0,0 +1,36 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sddllapi.h>
+#include <tools/gen.hxx>
+#include <rtl/string.hxx>
+
+class SdrPage;
+class BitmapEx;
+class Size;
+
+namespace sd
+{
+class SD_DLLPUBLIC SlideshowLayerRenderer
+{
+    SdrPage* mpPage;
+    Size maSlideSize;
+    bool bRenderDone = false;
+
+public:
+    SlideshowLayerRenderer(SdrPage* pPage);
+    Size calculateAndSetSizePixel(Size const& rDesiredSizePixel);
+    bool render(unsigned char* pBuffer, OString& rJsonMsg);
+};
+
+} // end of namespace sd
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/inc/unomodel.hxx b/sd/source/ui/inc/unomodel.hxx
index 155d50e9ecce..f65eb0865a76 100644
--- a/sd/source/ui/inc/unomodel.hxx
+++ b/sd/source/ui/inc/unomodel.hxx
@@ -56,6 +56,7 @@ class SvxItemPropertySet;
 namespace sd {
 class DrawDocShell;
 class DrawViewShell;
+class SlideshowLayerRenderer;
 }
 
 extern OUString getPageApiName( SdPage const * pPage );
@@ -87,6 +88,8 @@ private:
     SdDrawDocument* mpDoc;
     bool mbDisposed;
 
+    std::unique_ptr<sd::SlideshowLayerRenderer> mpSlideshowLayerRenderer;
+
     css::uno::Reference<css::uno::XInterface> create(
         OUString const & aServiceSpecifier, OUString const & referer);
 
diff --git a/sd/source/ui/tools/SlideshowLayerRenderer.cxx 
b/sd/source/ui/tools/SlideshowLayerRenderer.cxx
new file mode 100644
index 000000000000..e618dd2fac33
--- /dev/null
+++ b/sd/source/ui/tools/SlideshowLayerRenderer.cxx
@@ -0,0 +1,146 @@
+/* -*- 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 <SlideshowLayerRenderer.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdview.hxx>
+#include <svx/unoapi.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/svdoutl.hxx>
+#include <vcl/virdev.hxx>
+#include <tools/helpers.hxx>
+#include <tools/json_writer.hxx>
+
+namespace sd
+{
+namespace
+{
+class SelectObjectRedirector : public sdr::contact::ViewObjectContactRedirector
+{
+public:
+    virtual void createRedirectedPrimitive2DSequence(
+        const sdr::contact::ViewObjectContact& rOriginal,
+        const sdr::contact::DisplayInfo& rDisplayInfo,
+        drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) 
override
+    {
+        SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject();
+        SdrPage* pPage = pObject ? pObject->getSdrPageFromSdrObject() : 
nullptr;
+
+        if (pObject == nullptr || pPage == nullptr)
+        {
+            // Not a SdrObject or a object not connected to a page (object 
with no page)
+
+            
//sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
+            //    rOriginal, rDisplayInfo, rVisitor);
+            return;
+        }
+
+        const bool bDoCreateGeometry(
+            pObject->getSdrPageFromSdrObject()->checkVisibility(rOriginal, 
rDisplayInfo, true));
+
+        if (!bDoCreateGeometry
+            && (pObject->GetObjInventor() != SdrInventor::Default
+                || pObject->GetObjIdentifier() != SdrObjKind::Page))
+        {
+            return;
+        }
+
+        if (pObject->IsEmptyPresObj())
+            return;
+
+        
sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
+            rOriginal, rDisplayInfo, rVisitor);
+    }
+};
+}
+
+SlideshowLayerRenderer::SlideshowLayerRenderer(SdrPage* pPage)
+    : mpPage(pPage)
+{
+}
+
+Size SlideshowLayerRenderer::calculateAndSetSizePixel(Size const& 
rDesiredSizePixel)
+{
+    if (!mpPage)
+        return Size();
+
+    double fRatio = double(mpPage->GetHeight()) / mpPage->GetWidth();
+    Size aSize(rDesiredSizePixel.Width(), 
::tools::Long(rDesiredSizePixel.Width() * fRatio));
+    maSlideSize = aSize;
+
+    return maSlideSize;
+}
+
+bool SlideshowLayerRenderer::render(unsigned char* pBuffer, OString& rJsonMsg)
+{
+    if (bRenderDone)
+        return false;
+
+    if (!mpPage)
+        return false;
+
+    SdrModel& rModel = mpPage->getSdrModelFromSdrPage();
+
+    ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
+    pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+
+    pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(maSlideSize, 
Fraction(2.0), Point(),
+                                                       pBuffer);
+
+    Point aPoint;
+    Size aPageSize(mpPage->GetSize());
+
+    MapMode aMapMode(MapUnit::Map100thMM);
+    const Fraction aFracX(maSlideSize.Width(), 
pDevice->LogicToPixel(aPageSize, aMapMode).Width());
+    aMapMode.SetScaleX(aFracX);
+
+    const Fraction aFracY(maSlideSize.Height(),
+                          pDevice->LogicToPixel(aPageSize, aMapMode).Height());
+    aMapMode.SetScaleY(aFracY);
+
+    pDevice->SetMapMode(aMapMode);
+
+    SdrView aView(rModel, pDevice);
+
+    aView.SetPageVisible(false);
+    aView.SetPageShadowVisible(false);
+    aView.SetPageBorderVisible(false);
+    aView.SetBordVisible(false);
+    aView.SetGridVisible(false);
+    aView.SetHlplVisible(false);
+    aView.SetGlueVisible(false);
+
+    aView.ShowSdrPage(mpPage);
+
+    vcl::Region aRegion(::tools::Rectangle(aPoint, aPageSize));
+    SelectObjectRedirector aRedirector;
+    aView.CompleteRedraw(pDevice, aRegion, &aRedirector);
+
+    ::tools::JsonWriter aJsonWriter;
+    aJsonWriter.put("group", "DrawPage");
+    aJsonWriter.put("slideHash", 
GetInterfaceHash(GetXDrawPageForSdrPage(mpPage)));
+    aJsonWriter.put("index", 0);
+    aJsonWriter.put("type", "bitmap");
+    {
+        ::tools::ScopedJsonWriterNode aContentNode = 
aJsonWriter.startNode("content");
+        aJsonWriter.put("type", "%IMAGETYPE%");
+        aJsonWriter.put("checksum", "%IMAGECHECKSUM%");
+    }
+    rJsonMsg = aJsonWriter.finishAndGetAsOString();
+
+    bRenderDone = true;
+
+    return true;
+}
+
+} // end of namespace sd
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/unoidl/unomodel.cxx b/sd/source/ui/unoidl/unomodel.cxx
index 99d5d9718fec..d388e6f80ee6 100644
--- a/sd/source/ui/unoidl/unomodel.cxx
+++ b/sd/source/ui/unoidl/unomodel.cxx
@@ -146,6 +146,7 @@
 #include <ViewShell.hxx>
 #include <Window.hxx>
 #include <optsitem.hxx>
+#include <SlideshowLayerRenderer.hxx>
 
 #include <vcl/pdfextoutdevdata.hxx>
 #include <vcl/pdf/PDFNote.hxx>
@@ -4402,63 +4403,114 @@ OString SdXImpressDocument::getPresentationInfo() const
     return aJsonWriter.finishAndGetAsOString();
 }
 
+namespace
+{
+// use VCL slideshow renderer or not - leave the old one in for now, so it is 
possible to compare output
+constexpr const bool bVCLSlideShowRenderer = true;
+}
+
 bool SdXImpressDocument::createSlideRenderer(
     sal_Int32 nSlideNumber, sal_Int32& nViewWidth, sal_Int32& nViewHeight,
     bool bRenderBackground, bool bRenderMasterPage)
 {
-    DrawViewShell* pViewSh = GetViewShell();
-    if (!pViewSh)
-        return false;
-
-    uno::Reference<presentation::XSlideShow> xSlideShow = 
pViewSh->getXSlideShowInstance();
-    if (!xSlideShow.is())
-        return false;
-
-    bool bSuccess = false;
-    try
+    if (bVCLSlideShowRenderer)
     {
-        uno::Reference<drawing::XDrawPagesSupplier> 
xDrawPages(mpDoc->getUnoModel(), uno::UNO_QUERY_THROW);
-        uno::Reference<container::XIndexAccess> 
xSlides(xDrawPages->getDrawPages(), uno::UNO_QUERY_THROW);
-        uno::Reference<drawing::XDrawPage> 
xSlide(xSlides->getByIndex(nSlideNumber), uno::UNO_QUERY_THROW);
-        uno::Reference<animations::XAnimationNodeSupplier> 
xAnimNodeSupplier(xSlide, uno::UNO_QUERY_THROW);
-        uno::Reference<animations::XAnimationNode> xAnimNode = 
xAnimNodeSupplier->getAnimationNode();
+        SdPage* pPage = mpDoc->GetSdPage(sal_uInt16(nSlideNumber), 
PageKind::Standard);
+        if (!pPage)
+            return false;
 
-        bSuccess = xSlideShow->createLOKSlideRenderer(nViewWidth, nViewHeight,
-                                                 bRenderMasterPage, 
bRenderBackground,
-                                                 xSlide, xDrawPages, 
xAnimNode);
+        mpSlideshowLayerRenderer.reset(new SlideshowLayerRenderer(pPage));
+        Size aDesiredSize(nViewWidth, nViewHeight);
+        Size aCalculatedSize = 
mpSlideshowLayerRenderer->calculateAndSetSizePixel(aDesiredSize);
+        nViewWidth = aCalculatedSize.Width();
+        nViewHeight = aCalculatedSize.Height();
+        return true;
     }
-    catch (uno::Exception&)
+    else
     {
-        TOOLS_WARN_EXCEPTION( "sd", 
"SdXImpressDocument::createLOKSlideRenderer: failed" );
+        DrawViewShell* pViewSh = GetViewShell();
+        if (!pViewSh)
+            return false;
+
+        uno::Reference<presentation::XSlideShow> xSlideShow = 
pViewSh->getXSlideShowInstance();
+        if (!xSlideShow.is())
+            return false;
+
+        bool bSuccess = false;
+        try
+        {
+            uno::Reference<drawing::XDrawPagesSupplier> 
xDrawPages(mpDoc->getUnoModel(), uno::UNO_QUERY_THROW);
+            uno::Reference<container::XIndexAccess> 
xSlides(xDrawPages->getDrawPages(), uno::UNO_QUERY_THROW);
+            uno::Reference<drawing::XDrawPage> 
xSlide(xSlides->getByIndex(nSlideNumber), uno::UNO_QUERY_THROW);
+            uno::Reference<animations::XAnimationNodeSupplier> 
xAnimNodeSupplier(xSlide, uno::UNO_QUERY_THROW);
+            uno::Reference<animations::XAnimationNode> xAnimNode = 
xAnimNodeSupplier->getAnimationNode();
+
+            bSuccess = xSlideShow->createLOKSlideRenderer(nViewWidth, 
nViewHeight,
+                                                     bRenderMasterPage, 
bRenderBackground,
+                                                     xSlide, xDrawPages, 
xAnimNode);
+        }
+        catch (uno::Exception&)
+        {
+            TOOLS_WARN_EXCEPTION( "sd", 
"SdXImpressDocument::createLOKSlideRenderer: failed" );
+        }
+        return bSuccess;
     }
-    return bSuccess;
 }
 
 void SdXImpressDocument::postSlideshowCleanup()
 {
-    DrawViewShell* pViewSh = GetViewShell();
-    if (!pViewSh)
-        return;
+    if (bVCLSlideShowRenderer)
+    {
+        mpSlideshowLayerRenderer.reset();
+    }
+    else
+    {
+        DrawViewShell* pViewSh = GetViewShell();
+        if (!pViewSh)
+            return;
 
-    pViewSh->destroyXSlideShowInstance();
+        pViewSh->destroyXSlideShowInstance();
+}
 }
 
 bool SdXImpressDocument::renderNextSlideLayer(unsigned char* pBuffer, bool& 
bIsBitmapLayer, OUString& rJsonMsg)
 {
-    DrawViewShell* pViewSh = GetViewShell();
-    if (!pViewSh)
-        return true;
+    if (bVCLSlideShowRenderer)
+    {
+        bool bDone = true;
 
-    uno::Reference<presentation::XSlideShow> xSlideShow = 
pViewSh->getXSlideShowInstance();
-    if (!xSlideShow.is())
-        return true;
+        if (!mpSlideshowLayerRenderer)
+            return bDone;
 
-    auto nBufferPointer = 
sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(pBuffer));
-    sal_Bool bBitmapLayer = false;
-    bool bDone = xSlideShow->renderNextLOKSlideLayer(nBufferPointer, 
bBitmapLayer, rJsonMsg);
-    bIsBitmapLayer = bBitmapLayer;
+        OString sMsg;
+        bool bOK = mpSlideshowLayerRenderer->render(pBuffer, sMsg);
 
-    return bDone;
+        if (bOK)
+        {
+            rJsonMsg = OUString::fromUtf8(sMsg);
+            bIsBitmapLayer = true;
+            bDone = false;
+        }
+
+        return bDone;
+    }
+    else
+    {
+        DrawViewShell* pViewSh = GetViewShell();
+        if (!pViewSh)
+            return true;
+
+        uno::Reference<presentation::XSlideShow> xSlideShow = 
pViewSh->getXSlideShowInstance();
+        if (!xSlideShow.is())
+            return true;
+
+        auto nBufferPointer = 
sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(pBuffer));
+        sal_Bool bBitmapLayer = false;
+        bool bDone = xSlideShow->renderNextLOKSlideLayer(nBufferPointer, 
bBitmapLayer, rJsonMsg);
+        bIsBitmapLayer = bBitmapLayer;
+
+        return bDone;
+    }
 }
 
 SdrModel& SdXImpressDocument::getSdrModelFromUnoModel() const

Reply via email to