sd/qa/unit/tiledrendering/data/SlideRenderingTest_WithFields.odp |binary
 sd/qa/unit/tiledrendering/tiledrendering.cxx                     |   27 
 sd/source/ui/inc/SlideshowLayerRenderer.hxx                      |   73 +-
 sd/source/ui/tools/SlideshowLayerRenderer.cxx                    |  273 
+++++-----
 4 files changed, 218 insertions(+), 155 deletions(-)

New commits:
commit 3227919ea158378a6a4435410ff6c30b1be6e459
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Wed Oct 23 11:30:19 2024 +0200
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Thu Nov 7 11:08:38 2024 +0100

    slideshow: simplify rendering, render text fields each in own layer
    
    This change significantly simplifies the rendering of layers by
    introducing an analysis pass (when we render the background) that
    creates rendering pass objects, which contain which objects and
    paragraphs need to be rendered in each pass. Previously we tried
    to figure this out durign the rendering, which meant we had to
    track a lot of various states. This all is not longer needed. This
    chaneg also means that the new RenderPassObjectRedirector is fairly
    simple.
    
    In addition this changes the test and implementation to render
    each text field in its own layer as it is expected by the online
    implementation (to enable master pages caching), but it still
    doesn't first render the placeholders for text fields as MasterPage
    layer and the TextField layer as a separate bitmap layer. This
    will be done next.
    
    Change-Id: I5377f4c5e5ebff67da70b248c831837cb9f2e559
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/175568
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>

diff --git a/sd/qa/unit/tiledrendering/data/SlideRenderingTest_WithFields.odp 
b/sd/qa/unit/tiledrendering/data/SlideRenderingTest_WithFields.odp
index 960adc4c5c55..1d7cc1f15e05 100644
Binary files a/sd/qa/unit/tiledrendering/data/SlideRenderingTest_WithFields.odp 
and b/sd/qa/unit/tiledrendering/data/SlideRenderingTest_WithFields.odp differ
diff --git a/sd/qa/unit/tiledrendering/tiledrendering.cxx 
b/sd/qa/unit/tiledrendering/tiledrendering.cxx
index 8442f9332c79..2e85ab088c1b 100644
--- a/sd/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sd/qa/unit/tiledrendering/tiledrendering.cxx
@@ -3336,6 +3336,7 @@ CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, 
testSlideshowLayeredRendering_WithFie
         bool bIsBitmapLayer = false;
         OUString rJsonMsg;
         
CPPUNIT_ASSERT(!pXImpressDocument->renderNextSlideLayer(pBuffer.data(), 
bIsBitmapLayer, rJsonMsg));
+        debugWriteImageToFile(0, pBuffer, nViewWidth, nViewHeight, 
rJsonMsg.toUtf8().getStr());
     }
 
     {
@@ -3374,10 +3375,13 @@ CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, 
testSlideshowLayeredRendering_WithFie
         CPPUNIT_ASSERT_EQUAL(aTransparentColor, aBitmapEx.GetPixelColor(20, 
20));
 
         // bottom-left corner
-        CPPUNIT_ASSERT_EQUAL(Color(0x90, 0x80, 0xff), 
aBitmapEx.GetPixelColor(20, nViewHeight - 20));
+        CPPUNIT_ASSERT_EQUAL(aTransparentColor, aBitmapEx.GetPixelColor(20, 
nViewHeight - 20));
 
         // bottom-right corner
         CPPUNIT_ASSERT_EQUAL(aTransparentColor, 
aBitmapEx.GetPixelColor(nViewWidth - 20, nViewHeight - 20));
+
+        // bottom-center
+        CPPUNIT_ASSERT_EQUAL(Color(0x20, 0xaa, 0x00), 
aBitmapEx.GetPixelColor(nViewWidth / 2, nViewHeight - 20));
     }
 
     {
@@ -3391,6 +3395,27 @@ CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, 
testSlideshowLayeredRendering_WithFie
 
         BitmapEx aBitmapEx = vcl::bitmap::CreateFromData(pBuffer.data(), 
nViewWidth, nViewHeight, nViewWidth * 4, /*nBitsPerPixel*/32, true, true);
 
+        // top-left corner
+        CPPUNIT_ASSERT_EQUAL(aTransparentColor, aBitmapEx.GetPixelColor(20, 
20));
+
+        // bottom-left corner
+        CPPUNIT_ASSERT_EQUAL(Color(0x90, 0x80, 0xff), 
aBitmapEx.GetPixelColor(20, nViewHeight - 20));
+
+        // bottom-right corner
+        CPPUNIT_ASSERT_EQUAL(aTransparentColor, 
aBitmapEx.GetPixelColor(nViewWidth - 20, nViewHeight - 20));
+    }
+
+    {
+        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
+        debugWriteImageToFile(4, pBuffer, nViewWidth, nViewHeight, 
rJsonMsg.toUtf8().getStr());
+
+        BitmapEx aBitmapEx = vcl::bitmap::CreateFromData(pBuffer.data(), 
nViewWidth, nViewHeight, nViewWidth * 4, /*nBitsPerPixel*/32, true, true);
+
         // top-left corner
         CPPUNIT_ASSERT_EQUAL(Color(0x00, 0x50, 0x90), 
aBitmapEx.GetPixelColor(20, 20));
 
diff --git a/sd/source/ui/inc/SlideshowLayerRenderer.hxx 
b/sd/source/ui/inc/SlideshowLayerRenderer.hxx
index 7a48e3366c1f..1d8f427735bb 100644
--- a/sd/source/ui/inc/SlideshowLayerRenderer.hxx
+++ b/sd/source/ui/inc/SlideshowLayerRenderer.hxx
@@ -34,17 +34,21 @@ namespace com::sun::star::animations
 class XAnimate;
 }
 
+namespace sdr::contact
+{
+class ViewObjectContactRedirector;
+}
+
 namespace sd
 {
-struct RenderContext;
+class RenderContext;
 
 enum class RenderStage
 {
-    Background,
-    Master,
-    Slide,
-    TextFields,
-    Count
+    Background = 0,
+    Master = 1,
+    Slide = 2,
+    TextFields = 3,
 };
 
 struct AnimationLayerInfo
@@ -60,31 +64,36 @@ struct AnimationRenderInfo
     std::unordered_map<sal_Int32, AnimationLayerInfo> maParagraphInfos;
 };
 
-/** Holds rendering state, properties and switches through all rendering 
passes */
-struct RenderState
+// Holds information used when doing one rendering pass
+struct RenderPass
 {
     RenderStage meStage = RenderStage::Background;
+    std::unordered_map<SdrObject*, std::deque<sal_Int32>> 
maObjectsAndParagraphs;
+    bool mbRenderObjectBackground = false;
 
-    bool mbStopRenderingWhenField = true;
-
-    std::unordered_set<SdrObject*> maObjectsDone;
+    bool mbAnimation = false;
+    SdrObject* mpAnimatedObject = nullptr;
+    sal_Int32 mnAnimatedParagraph = -1;
 
-    std::unordered_map<SdrObject*, AnimationRenderInfo> 
maAnimationRenderInfoList;
+    bool isEmpty() { return maObjectsAndParagraphs.empty(); }
+};
 
-    sal_Int32 mnIndex[static_cast<unsigned>(RenderStage::Count)] = { 0, 0, 0, 
0 };
-    SdrObject* mpCurrentTarget = nullptr;
-    sal_Int32 mnCurrentTargetParagraph = -1;
+/** Holds rendering state, properties and switches through all rendering 
passes */
+struct RenderState
+{
+    std::deque<RenderPass> maRenderPasses;
 
-    sal_Int32 mnRenderedObjectsInPass = 0;
+    RenderStage meStage = RenderStage::Background;
 
-    bool mbSkipAllInThisPass = false;
+    std::unordered_map<SdrObject*, AnimationRenderInfo> 
maAnimationRenderInfoList;
 
-    sal_Int32 mnCurrentPass = 0;
+    std::array<sal_Int32, 4> maIndices = { 0, 0, 0, 0 };
 
-    std::deque<sal_Int32> maParagraphsToRender;
+    SdrObject* mpCurrentTarget = nullptr;
+    sal_Int32 mnCurrentTargetParagraph = -1;
 
     /// increments index depending on the current render stage
-    void incrementIndex() { mnIndex[static_cast<unsigned>(meStage)]++; }
+    void incrementIndex() { maIndices[size_t(meStage)]++; }
 
     /// returns the current stage as string
     OString stageString() const
@@ -99,7 +108,7 @@ struct RenderState
     }
 
     /// returns the current index depending on the current render stage
-    sal_Int32 currentIndex() const { return 
mnIndex[static_cast<unsigned>(meStage)]; }
+    sal_Int32 currentIndex() const { return maIndices[size_t(meStage)]; }
 
     /// returns the current target element for which layer is created if any
     SdrObject* currentTarget() const { return mpCurrentTarget; }
@@ -110,32 +119,13 @@ struct RenderState
     /// resets properties that are valid for one pass
     void resetPass()
     {
-        mnRenderedObjectsInPass = 0;
-        mbSkipAllInThisPass = false;
         mpCurrentTarget = nullptr;
         mnCurrentTargetParagraph = -1;
     }
 
-    bool hasPassAnyRenderedOutput() const { return mnRenderedObjectsInPass > 
0; }
-
-    /// is first rendered object in pass
-    bool isFirstObjectInPass() const { return mnRenderedObjectsInPass == 0; }
-
-    /// return if there was no rendering output in the pass
-    bool noMoreOutput() const
-    {
-        // no output and we didn't skip anything and nothing was rendered
-        return !hasPassAnyRenderedOutput() && !mbSkipAllInThisPass;
-    }
-
     /// should include background in rendering
     bool includeBackground() const { return meStage == 
RenderStage::Background; }
 
-    bool isObjectAlreadyRendered(SdrObject* pObject) const
-    {
-        return maObjectsDone.find(pObject) != maObjectsDone.end();
-    }
-
     static std::string getObjectHash(SdrObject* pObject)
     {
         css::uno::Reference<css::drawing::XShape> xShape = 
GetXShapeForSdrObject(pObject);
@@ -161,7 +151,8 @@ private:
     Size maSlideSize;
     RenderState maRenderState;
 
-    void createViewAndDraw(RenderContext& rRenderContext);
+    void createViewAndDraw(RenderContext& rRenderContext,
+                           sdr::contact::ViewObjectContactRedirector* 
pRedirector);
     void writeJSON(OString& rJsonMsg);
 
     void setupAnimations();
diff --git a/sd/source/ui/tools/SlideshowLayerRenderer.cxx 
b/sd/source/ui/tools/SlideshowLayerRenderer.cxx
index 07a9f018a6fd..0ccde3ebefb8 100644
--- a/sd/source/ui/tools/SlideshowLayerRenderer.cxx
+++ b/sd/source/ui/tools/SlideshowLayerRenderer.cxx
@@ -44,12 +44,16 @@ using namespace ::com::sun::star;
 
 namespace sd
 {
-struct RenderContext
+/// Sets up the virtual device for rendering, and cleans up afterwards
+class RenderContext
 {
+private:
     SdrModel& mrModel;
     SdrPage& mrPage;
 
     EEControlBits mnSavedControlBits;
+
+public:
     ScopedVclPtrInstance<VirtualDevice> maVirtualDevice;
 
     RenderContext(unsigned char* pBuffer, SdrModel& rModel, SdrPage& rPage, 
Size const& rSlideSize)
@@ -106,6 +110,7 @@ bool hasFields(SdrObject* pObject)
     return false;
 }
 
+/// Sets visible for all kinds of polypolys in the container
 void changePolyPolys(drawinglayer::primitive2d::Primitive2DContainer& 
rContainer,
                      bool bRenderObject)
 {
@@ -122,6 +127,7 @@ void 
changePolyPolys(drawinglayer::primitive2d::Primitive2DContainer& rContainer
     }
 }
 
+/// Searches for rectangle primitive and changes if the background should be 
rendered
 void changeBackground(drawinglayer::primitive2d::Primitive2DContainer const& 
rContainer,
                       bool bRenderObject)
 {
@@ -138,6 +144,7 @@ void 
changeBackground(drawinglayer::primitive2d::Primitive2DContainer const& rCo
     }
 }
 
+/// Find the text block in the primitive containder, decompose if necessary
 drawinglayer::primitive2d::TextHierarchyBlockPrimitive2D*
 findTextBlock(drawinglayer::primitive2d::Primitive2DContainer const& 
rContainer,
               drawinglayer::geometry::ViewInformation2D const& 
rViewInformation2D)
@@ -182,6 +189,7 @@ 
findTextBlock(drawinglayer::primitive2d::Primitive2DContainer const& rContainer,
     return nullptr;
 }
 
+/// show/hide paragraphs in the container
 void modifyParagraphs(drawinglayer::primitive2d::Primitive2DContainer& 
rContainer,
                       drawinglayer::geometry::ViewInformation2D const& 
rViewInformation2D,
                       std::deque<sal_Int32> const& rPreserveIndices, bool 
bRenderObject)
@@ -217,44 +225,51 @@ void 
modifyParagraphs(drawinglayer::primitive2d::Primitive2DContainer& rContaine
     }
 }
 
-/** VOC redirector to control which object should be rendered and which not */
-class ObjectRedirector : public sdr::contact::ViewObjectContactRedirector
+/// Analyze the renderng and create rendering passes
+class AnalyzeRenderingRedirector : public 
sdr::contact::ViewObjectContactRedirector
 {
 protected:
     RenderState& mrRenderState;
 
+    RenderPass* mpCurrentRenderPass;
+    RenderStage mePreviousStage = RenderStage::Master;
+
 public:
-    ObjectRedirector(RenderState& rRenderState)
+    AnalyzeRenderingRedirector(RenderState& rRenderState)
         : mrRenderState(rRenderState)
+        , mpCurrentRenderPass(newRenderPass())
     {
     }
 
-    virtual void createRedirectedPrimitive2DSequence(
-        const sdr::contact::ViewObjectContact& rOriginal,
-        const sdr::contact::DisplayInfo& rDisplayInfo,
-        drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) 
override
+    // Adds a new rendering pass to the list and returns it
+    RenderPass* newRenderPass()
     {
-        // Generate single pass for background layer
-        if (mrRenderState.meStage == RenderStage::Background)
-        {
-            mrRenderState.mnRenderedObjectsInPass++;
-            mrRenderState.mbSkipAllInThisPass = true;
-            return;
-        }
+        mrRenderState.maRenderPasses.emplace_back();
+        return &mrRenderState.maRenderPasses.back();
+    }
 
-        if (mrRenderState.mbSkipAllInThisPass)
+    // Closes current rendering pass, and creates a new empty current one
+    void closeRenderPass()
+    {
+        if (mpCurrentRenderPass->maObjectsAndParagraphs.empty())
             return;
 
-        SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject();
+        mpCurrentRenderPass = newRenderPass();
+    }
 
-        drawinglayer::geometry::ViewInformation2D const& rViewInformation2D
-            = rOriginal.GetObjectContact().getViewInformation2D();
+    virtual void createRedirectedPrimitive2DSequence(
+        const sdr::contact::ViewObjectContact& rOriginal,
+        const sdr::contact::DisplayInfo& rDisplayInfo,
+        drawinglayer::primitive2d::Primitive2DDecompositionVisitor& 
/*rVisitor*/) override
+    {
+        SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject();
 
         // Check if we are rendering an object that is valid to render 
(exists, and not empty)
         if (pObject == nullptr || pObject->IsEmptyPresObj())
             return;
 
         SdrPage* pPage = pObject->getSdrPageFromSdrObject();
+
         // Does the object have a page
         if (pPage == nullptr)
             return;
@@ -266,124 +281,134 @@ public:
         if (!bVisible)
             return;
 
-        // Check if we have already rendered the object
-        if (mrRenderState.isObjectAlreadyRendered(pObject))
-            return;
+        // Determine the current stage, depending on the page
+        RenderStage eCurrentStage
+            = pPage->IsMasterPage() ? RenderStage::Master : RenderStage::Slide;
 
-        // Check if we are in correct stage
-        if (mrRenderState.meStage == RenderStage::Master && 
!pPage->IsMasterPage())
-        {
-            if (mrRenderState.isFirstObjectInPass())
-            {
-                // if this is the first object - change from master to slide
-                // means we are done with rendering of master layers
-                mrRenderState.meStage = RenderStage::Slide;
-            }
-            else
-            {
-                // if not, we have to stop rendering all further objects
-                mrRenderState.mbSkipAllInThisPass = true;
-                return;
-            }
-        }
+        // We switched from master objecst to slide objects
+        if (eCurrentStage == RenderStage::Slide && mePreviousStage == 
RenderStage::Master)
+            closeRenderPass();
 
-        // Paragraph rendering switches
-        bool bRenderOtherParagraphs = false;
-        std::deque<sal_Int32> nOtherParagraphs;
-
-        // check if object is in animation
+        // check if object is in an animation
         auto aIterator = mrRenderState.maAnimationRenderInfoList.find(pObject);
         if (aIterator != mrRenderState.maAnimationRenderInfoList.end())
         {
-            // Animated object has to be only one in the render
-            mrRenderState.mbSkipAllInThisPass = true; // skip all next objects
-
-            // Force a new layer
-            if (!mrRenderState.isFirstObjectInPass())
-                return;
+            closeRenderPass();
 
             AnimationRenderInfo aInfo = aIterator->second;
 
-            if (mrRenderState.maParagraphsToRender.empty()
-                && !aInfo.maParagraphs.empty()) // we need to render paragraphs
+            if (!aInfo.maParagraphs.empty()) // we need to render paragraphs
             {
                 auto* pTextObject = dynamic_cast<SdrTextObj*>(pObject);
                 if (pTextObject)
                 {
                     sal_Int32 nNumberOfParagraphs = 
pTextObject->GetOutlinerParaObject()->Count();
 
+                    std::deque<sal_Int32> nOtherParagraphs;
                     for (sal_Int32 nParagraph = 0; nParagraph < 
nNumberOfParagraphs; ++nParagraph)
-                        nOtherParagraphs.push_back(nParagraph);
-
+                    {
+                        if (std::find(aInfo.maParagraphs.begin(), 
aInfo.maParagraphs.end(),
+                                      nParagraph)
+                            == aInfo.maParagraphs.end())
+                            nOtherParagraphs.push_back(nParagraph);
+                    }
+                    // Add the non-animated part of the object that has 
animated paragraphs
+                    
mpCurrentRenderPass->maObjectsAndParagraphs.emplace(pObject, nOtherParagraphs);
+                    mpCurrentRenderPass->meStage = eCurrentStage;
+                    mpCurrentRenderPass->mbRenderObjectBackground = true;
+                    mpCurrentRenderPass->mbAnimation = true;
+                    mpCurrentRenderPass->mpAnimatedObject = pObject;
+                    closeRenderPass();
+
+                    // Add all the animated paragraphs
                     for (sal_Int32 nParagraph : aInfo.maParagraphs)
                     {
-                        
mrRenderState.maParagraphsToRender.push_back(nParagraph);
-                        std::erase(nOtherParagraphs, nParagraph);
+                        mpCurrentRenderPass->maObjectsAndParagraphs.emplace(
+                            pObject, std::deque<sal_Int32>{ nParagraph });
+                        mpCurrentRenderPass->meStage = eCurrentStage;
+                        mpCurrentRenderPass->mbAnimation = true;
+                        mpCurrentRenderPass->mpAnimatedObject = pObject;
+                        mpCurrentRenderPass->mnAnimatedParagraph = nParagraph;
+                        closeRenderPass();
                     }
-                    bRenderOtherParagraphs = true;
                 }
             }
+            else
+            {
+                // Add the animated object - paragraphs are not animated
+                mpCurrentRenderPass->maObjectsAndParagraphs.emplace(pObject,
+                                                                    
std::deque<sal_Int32>());
+                mpCurrentRenderPass->meStage = eCurrentStage;
+                mpCurrentRenderPass->mbAnimation = true;
+                mpCurrentRenderPass->mpAnimatedObject = pObject;
+                closeRenderPass();
+            }
         }
-        else if (mrRenderState.meStage == RenderStage::Master && 
hasFields(pObject)
-                 && mrRenderState.mbStopRenderingWhenField && 
!mrRenderState.isFirstObjectInPass())
+        // Check if the object has fields (slide number)
+        else if (eCurrentStage == RenderStage::Master && hasFields(pObject))
         {
-            mrRenderState.mbStopRenderingWhenField = false;
-            mrRenderState.mbSkipAllInThisPass = true;
-            return;
+            closeRenderPass();
+
+            mpCurrentRenderPass->maObjectsAndParagraphs.emplace(pObject, 
std::deque<sal_Int32>());
+            mpCurrentRenderPass->meStage = eCurrentStage;
+            closeRenderPass();
+        }
+        // No specal handling is needed, just add the object to the current 
rendering pass
+        else
+        {
+            mpCurrentRenderPass->maObjectsAndParagraphs.emplace(pObject, 
std::deque<sal_Int32>());
+            mpCurrentRenderPass->meStage = eCurrentStage;
         }
+        mePreviousStage = eCurrentStage;
+    }
+};
 
-        mrRenderState.mpCurrentTarget = pObject;
+/// Render one render pass
+class RenderPassObjectRedirector : public 
sdr::contact::ViewObjectContactRedirector
+{
+protected:
+    RenderState& mrRenderState;
+    RenderPass const& mrRenderPass;
+
+public:
+    RenderPassObjectRedirector(RenderState& rRenderState, RenderPass const& 
rRenderPass)
+        : mrRenderState(rRenderState)
+        , mrRenderPass(rRenderPass)
+    {
+    }
+
+    virtual void createRedirectedPrimitive2DSequence(
+        const sdr::contact::ViewObjectContact& rOriginal,
+        const sdr::contact::DisplayInfo& rDisplayInfo,
+        drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) 
override
+    {
+        SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject();
+
+        if (!pObject)
+            return;
+
+        // check if object is in animation
+        auto aIterator = mrRenderPass.maObjectsAndParagraphs.find(pObject);
+        if (aIterator == mrRenderPass.maObjectsAndParagraphs.end())
+            return;
 
         // render the object
         
sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
             rOriginal, rDisplayInfo, rVisitor);
 
-        if (!mrRenderState.maParagraphsToRender.empty())
+        auto const& rParagraphs = aIterator->second;
+
+        if (!rParagraphs.empty())
         {
+            auto const& rViewInformation2D = 
rOriginal.GetObjectContact().getViewInformation2D();
             auto rContainer
                 = 
static_cast<drawinglayer::primitive2d::Primitive2DContainer&>(rVisitor);
-
-            if (bRenderOtherParagraphs)
-            {
-                modifyParagraphs(rContainer, rViewInformation2D, 
nOtherParagraphs,
-                                 true); // render the object
-                mrRenderState.mnCurrentTargetParagraph = -1;
-            }
-            else
-            {
-                sal_Int32 nParagraph = 
mrRenderState.maParagraphsToRender.front();
-                mrRenderState.maParagraphsToRender.pop_front();
-
-                std::deque<sal_Int32> aPreserveParagraphs{ nParagraph };
-                mrRenderState.mnCurrentTargetParagraph = nParagraph;
-                // render only the paragraphs
-                modifyParagraphs(rContainer, rViewInformation2D, 
aPreserveParagraphs, false);
-            }
-        }
-
-        if (mrRenderState.maParagraphsToRender.empty())
-        {
-            mrRenderState.mnRenderedObjectsInPass++;
-            mrRenderState.maObjectsDone.insert(pObject);
+            modifyParagraphs(rContainer, rViewInformation2D, rParagraphs,
+                             mrRenderPass.mbRenderObjectBackground);
         }
     }
 };
 
-bool hasEmptyMaster(SdrPage const& rPage)
-{
-    if (!rPage.TRG_HasMasterPage())
-        return true;
-
-    SdrPage& rMaster = rPage.TRG_GetMasterPage();
-    for (size_t i = 0; i < rMaster.GetObjCount(); i++)
-    {
-        auto pObject = rMaster.GetObj(i);
-        if (!pObject->IsEmptyPresObj())
-            return false;
-    }
-    return true;
-}
-
 SdrObject* getObjectForShape(uno::Reference<drawing::XShape> const& xShape)
 {
     if (!xShape.is())
@@ -507,7 +532,8 @@ Size SlideshowLayerRenderer::calculateAndSetSizePixel(Size 
const& rDesiredSizePi
     return maSlideSize;
 }
 
-void SlideshowLayerRenderer::createViewAndDraw(RenderContext& rRenderContext)
+void SlideshowLayerRenderer::createViewAndDraw(
+    RenderContext& rRenderContext, sdr::contact::ViewObjectContactRedirector* 
pRedirector)
 {
     SdrView aView(mrModel, rRenderContext.maVirtualDevice);
     aView.SetPageVisible(false);
@@ -524,8 +550,7 @@ void 
SlideshowLayerRenderer::createViewAndDraw(RenderContext& rRenderContext)
     Point aPoint;
 
     vcl::Region aRegion(::tools::Rectangle(aPoint, aPageSize));
-    ObjectRedirector aRedirector(maRenderState);
-    aView.CompleteRedraw(rRenderContext.maVirtualDevice, aRegion, 
&aRedirector);
+    aView.CompleteRedraw(rRenderContext.maVirtualDevice, aRegion, pRedirector);
 }
 
 namespace
@@ -623,21 +648,43 @@ bool SlideshowLayerRenderer::render(unsigned char* 
pBuffer, OString& rJsonMsg)
     maRenderState.resetPass();
 
     RenderContext aRenderContext(pBuffer, mrModel, mrPage, maSlideSize);
-    createViewAndDraw(aRenderContext);
 
-    // Check if we are done rendering all passes and there is no more output
-    if (maRenderState.noMoreOutput())
-        return false;
+    // Render Background and analyze other passes
+    if (maRenderState.meStage == RenderStage::Background)
+    {
+        // Render no objects, just the background, but analyze and create 
rendering passes
+        AnalyzeRenderingRedirector aRedirector(maRenderState);
+        createViewAndDraw(aRenderContext, &aRedirector);
 
-    writeJSON(rJsonMsg);
+        // Last rendering pass might be empty - delete
+        if (maRenderState.maRenderPasses.back().isEmpty())
+            maRenderState.maRenderPasses.pop_back();
 
-    maRenderState.mnCurrentPass++;
+        // Write JSON for the Background layer
+        writeJSON(rJsonMsg);
 
-    if (maRenderState.meStage == RenderStage::Background)
         maRenderState.meStage = RenderStage::Master;
+    }
+    else
+    {
+        if (maRenderState.maRenderPasses.empty())
+            return false;
 
-    if (hasEmptyMaster(mrPage))
-        maRenderState.meStage = RenderStage::Slide;
+        auto const& rRenderPass = maRenderState.maRenderPasses.front();
+        maRenderState.meStage = rRenderPass.meStage;
+        RenderPassObjectRedirector aRedirector(maRenderState, rRenderPass);
+        createViewAndDraw(aRenderContext, &aRedirector);
+
+        if (rRenderPass.mbAnimation)
+        {
+            maRenderState.mpCurrentTarget = rRenderPass.mpAnimatedObject;
+            maRenderState.mnCurrentTargetParagraph = 
rRenderPass.mnAnimatedParagraph;
+        }
+
+        writeJSON(rJsonMsg);
+
+        maRenderState.maRenderPasses.pop_front();
+    }
 
     return true;
 }

Reply via email to