2024-08-08 15:44:47 +02:00
|
|
|
/* -*- 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/sdr/contact/viewobjectcontact.hxx>
|
|
|
|
#include <svx/sdr/contact/viewcontact.hxx>
|
2024-11-15 13:20:06 +01:00
|
|
|
#include <svx/sdr/contact/objectcontact.hxx>
|
2024-08-08 15:44:47 +02:00
|
|
|
#include <svx/svdoutl.hxx>
|
2024-11-15 13:20:06 +01:00
|
|
|
#include <svx/svdpagv.hxx>
|
2024-09-25 15:10:05 +02:00
|
|
|
#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
|
|
|
|
#include <svx/unoshape.hxx>
|
|
|
|
|
2024-08-08 15:44:47 +02:00
|
|
|
#include <vcl/virdev.hxx>
|
|
|
|
#include <tools/json_writer.hxx>
|
2024-08-19 20:51:18 +02:00
|
|
|
#include <editeng/editeng.hxx>
|
2024-09-25 15:10:05 +02:00
|
|
|
#include <animations/animationnodehelper.hxx>
|
|
|
|
#include <sdpage.hxx>
|
|
|
|
#include <comphelper/servicehelper.hxx>
|
2024-08-08 15:44:47 +02:00
|
|
|
|
2024-08-30 16:56:59 +02:00
|
|
|
#include <com/sun/star/animations/XAnimate.hpp>
|
|
|
|
#include <com/sun/star/animations/XAnimationNode.hpp>
|
|
|
|
#include <com/sun/star/animations/AnimationNodeType.hpp>
|
2024-09-04 21:38:29 +02:00
|
|
|
#include <com/sun/star/drawing/XShape.hpp>
|
2024-08-30 16:56:59 +02:00
|
|
|
#include <com/sun/star/presentation/ParagraphTarget.hpp>
|
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
#include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
|
|
|
|
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
|
|
|
|
#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx>
|
|
|
|
#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
|
|
|
|
#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
|
|
|
|
|
|
|
|
#include <drawinglayer/tools/primitive2dxmldump.hxx>
|
2024-08-30 16:56:59 +02:00
|
|
|
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
|
2024-08-08 15:44:47 +02:00
|
|
|
namespace sd
|
|
|
|
{
|
2024-08-19 20:51:18 +02:00
|
|
|
struct RenderContext
|
|
|
|
{
|
|
|
|
SdrModel& mrModel;
|
|
|
|
|
|
|
|
EEControlBits mnSavedControlBits;
|
|
|
|
ScopedVclPtrInstance<VirtualDevice> maVirtualDevice;
|
|
|
|
|
2024-12-16 07:42:05 -04:00
|
|
|
RenderContext(unsigned char* pBuffer, SdrModel& rModel, SdrPage& rPage, Size const& rSlideSize,
|
|
|
|
const Fraction& rScale)
|
2024-08-19 20:51:18 +02:00
|
|
|
: mrModel(rModel)
|
|
|
|
, maVirtualDevice(DeviceFormat::WITHOUT_ALPHA)
|
|
|
|
{
|
2024-08-21 11:25:05 +02:00
|
|
|
// Turn off spelling
|
2024-08-19 20:51:18 +02:00
|
|
|
SdrOutliner& rOutliner = mrModel.GetDrawOutliner();
|
|
|
|
mnSavedControlBits = rOutliner.GetControlWord();
|
|
|
|
rOutliner.SetControlWord(mnSavedControlBits & ~EEControlBits::ONLINESPELLING);
|
|
|
|
|
|
|
|
maVirtualDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
|
|
|
|
|
2025-02-05 14:56:23 +02:00
|
|
|
maVirtualDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(rSlideSize, rScale, Point(),
|
2024-12-16 07:42:05 -04:00
|
|
|
pBuffer);
|
2024-11-22 12:17:55 +02:00
|
|
|
Size aPageSize(rPage.GetSize());
|
2024-08-19 20:51:18 +02:00
|
|
|
|
|
|
|
MapMode aMapMode(MapUnit::Map100thMM);
|
|
|
|
const Fraction aFracX(rSlideSize.Width(),
|
|
|
|
maVirtualDevice->LogicToPixel(aPageSize, aMapMode).Width());
|
|
|
|
aMapMode.SetScaleX(aFracX);
|
|
|
|
|
|
|
|
const Fraction aFracY(rSlideSize.Height(),
|
|
|
|
maVirtualDevice->LogicToPixel(aPageSize, aMapMode).Height());
|
|
|
|
aMapMode.SetScaleY(aFracY);
|
|
|
|
|
|
|
|
maVirtualDevice->SetMapMode(aMapMode);
|
|
|
|
}
|
|
|
|
|
|
|
|
~RenderContext()
|
|
|
|
{
|
|
|
|
// Restore spelling
|
|
|
|
SdrOutliner& rOutliner = mrModel.GetDrawOutliner();
|
|
|
|
rOutliner.SetControlWord(mnSavedControlBits);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
2024-08-21 11:25:05 +02:00
|
|
|
bool hasFields(SdrObject* pObject)
|
|
|
|
{
|
|
|
|
auto* pTextObject = dynamic_cast<SdrTextObj*>(pObject);
|
|
|
|
if (!pTextObject)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
OutlinerParaObject* pOutlinerParagraphObject = pTextObject->GetOutlinerParaObject();
|
|
|
|
if (pOutlinerParagraphObject)
|
|
|
|
{
|
|
|
|
const EditTextObject& rEditText = pOutlinerParagraphObject->GetTextObject();
|
|
|
|
if (rEditText.IsFieldObject())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
void changePolyPolys(drawinglayer::primitive2d::Primitive2DContainer& rContainer,
|
|
|
|
bool bRenderObject)
|
|
|
|
{
|
|
|
|
for (auto& pBasePrimitive : rContainer)
|
|
|
|
{
|
|
|
|
if (pBasePrimitive->getPrimitive2DID() == PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D
|
|
|
|
|| pBasePrimitive->getPrimitive2DID() == PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D
|
|
|
|
|| pBasePrimitive->getPrimitive2DID() == PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D
|
|
|
|
|| pBasePrimitive->getPrimitive2DID() == PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D
|
|
|
|
|| pBasePrimitive->getPrimitive2DID() == PRIMITIVE2D_ID_POLYPOLYGONHAIRLINEPRIMITIVE2D)
|
|
|
|
{
|
|
|
|
pBasePrimitive->setVisible(bRenderObject);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void changeBackground(drawinglayer::primitive2d::Primitive2DContainer const& rContainer,
|
|
|
|
bool bRenderObject)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < rContainer.size(); i++)
|
|
|
|
{
|
|
|
|
drawinglayer::primitive2d::BasePrimitive2D* pBasePrimitive = rContainer[i].get();
|
|
|
|
if (pBasePrimitive->getPrimitive2DID() == PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D)
|
|
|
|
{
|
|
|
|
drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
|
|
|
|
pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
|
|
|
|
drawinglayer::geometry::ViewInformation2D());
|
|
|
|
changePolyPolys(aPrimitiveContainer, bRenderObject);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
drawinglayer::primitive2d::TextHierarchyBlockPrimitive2D*
|
|
|
|
findTextBlock(drawinglayer::primitive2d::Primitive2DContainer const& rContainer)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < rContainer.size(); i++)
|
|
|
|
{
|
|
|
|
drawinglayer::primitive2d::BasePrimitive2D* pBasePrimitive = rContainer[i].get();
|
|
|
|
if (pBasePrimitive->getPrimitive2DID() == PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D)
|
|
|
|
{
|
|
|
|
auto* pPrimitive
|
|
|
|
= dynamic_cast<drawinglayer::primitive2d::TextHierarchyBlockPrimitive2D*>(
|
|
|
|
pBasePrimitive);
|
|
|
|
if (pPrimitive)
|
|
|
|
return pPrimitive;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto* pGroupPrimitive
|
|
|
|
= dynamic_cast<drawinglayer::primitive2d::GroupPrimitive2D*>(pBasePrimitive);
|
|
|
|
if (pGroupPrimitive)
|
|
|
|
{
|
|
|
|
auto* pTextBlock = findTextBlock(pGroupPrimitive->getChildren());
|
|
|
|
if (pTextBlock)
|
|
|
|
return pTextBlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto* pBufferedPrimitive
|
|
|
|
= dynamic_cast<drawinglayer::primitive2d::BufferedDecompositionPrimitive2D*>(
|
|
|
|
pBasePrimitive);
|
|
|
|
if (pBufferedPrimitive)
|
|
|
|
{
|
|
|
|
// try to decompose
|
|
|
|
drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
|
|
|
|
pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
|
|
|
|
drawinglayer::geometry::ViewInformation2D());
|
|
|
|
auto* pTextBlock = findTextBlock(aPrimitiveContainer);
|
|
|
|
if (pTextBlock)
|
|
|
|
return pTextBlock;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void modifyParagraphs(drawinglayer::primitive2d::Primitive2DContainer& rContainer,
|
|
|
|
std::deque<sal_Int32> const& rPreserveIndices, bool bRenderObject)
|
|
|
|
{
|
|
|
|
auto* pTextBlock = findTextBlock(rContainer);
|
|
|
|
|
|
|
|
if (pTextBlock)
|
|
|
|
{
|
|
|
|
auto& rPrimitives = const_cast<drawinglayer::primitive2d::Primitive2DContainer&>(
|
|
|
|
pTextBlock->getChildren());
|
|
|
|
sal_Int32 nIndex = 0;
|
|
|
|
for (auto& pPrimitive : rPrimitives)
|
|
|
|
{
|
|
|
|
if (pPrimitive->getPrimitive2DID() == PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D)
|
|
|
|
{
|
|
|
|
auto& pParagraphPrimitive2d
|
|
|
|
= static_cast<drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D&>(
|
|
|
|
*pPrimitive);
|
|
|
|
|
|
|
|
// find the index
|
|
|
|
auto aIterator
|
|
|
|
= std::find(rPreserveIndices.begin(), rPreserveIndices.end(), nIndex);
|
|
|
|
|
|
|
|
// is index in preserve list - if false, hide the primitive
|
|
|
|
bool bHideIndex = aIterator == rPreserveIndices.end();
|
|
|
|
|
|
|
|
pParagraphPrimitive2d.setVisible(!bHideIndex);
|
|
|
|
}
|
|
|
|
nIndex++;
|
|
|
|
}
|
|
|
|
|
|
|
|
changeBackground(rContainer, bRenderObject);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-21 11:25:05 +02:00
|
|
|
/** VOC redirector to control which object should be rendered and which not */
|
2024-11-15 13:20:06 +01:00
|
|
|
class ObjectRedirector : public sdr::contact::ViewObjectContactRedirector
|
|
|
|
{
|
|
|
|
protected:
|
2024-08-21 11:25:05 +02:00
|
|
|
RenderState& mrRenderState;
|
2024-11-15 13:20:06 +01:00
|
|
|
|
2024-08-08 15:44:47 +02:00
|
|
|
public:
|
2024-08-21 11:25:05 +02:00
|
|
|
ObjectRedirector(RenderState& rRenderState)
|
|
|
|
: mrRenderState(rRenderState)
|
2024-11-15 13:20:06 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-08-08 15:44:47 +02:00
|
|
|
virtual void createRedirectedPrimitive2DSequence(
|
|
|
|
const sdr::contact::ViewObjectContact& rOriginal,
|
|
|
|
const sdr::contact::DisplayInfo& rDisplayInfo,
|
|
|
|
drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override
|
|
|
|
{
|
2024-09-09 23:40:05 +02:00
|
|
|
// Generate single pass for background layer
|
2024-09-09 17:34:03 +02:00
|
|
|
if (mrRenderState.meStage == RenderStage::Background)
|
|
|
|
{
|
2024-09-25 15:10:05 +02:00
|
|
|
mrRenderState.mnRenderedObjectsInPass++;
|
2024-09-09 17:34:03 +02:00
|
|
|
mrRenderState.mbSkipAllInThisPass = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-08-21 11:25:05 +02:00
|
|
|
if (mrRenderState.mbSkipAllInThisPass)
|
|
|
|
return;
|
2024-08-08 15:44:47 +02:00
|
|
|
|
2024-08-21 11:25:05 +02:00
|
|
|
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())
|
2024-08-08 15:44:47 +02:00
|
|
|
return;
|
|
|
|
|
2024-08-21 11:25:05 +02:00
|
|
|
SdrPage* pPage = pObject->getSdrPageFromSdrObject();
|
|
|
|
// Does the object have a page
|
|
|
|
if (pPage == nullptr)
|
2024-11-15 13:20:06 +01:00
|
|
|
return;
|
|
|
|
|
2024-08-21 11:25:05 +02:00
|
|
|
// is the object visible and not hidden by any option
|
|
|
|
const bool bVisible
|
|
|
|
= pObject->getSdrPageFromSdrObject()->checkVisibility(rOriginal, rDisplayInfo, true);
|
|
|
|
|
|
|
|
if (!bVisible)
|
2024-11-15 13:20:06 +01:00
|
|
|
return;
|
|
|
|
|
2024-08-21 11:25:05 +02:00
|
|
|
// Check if we have already rendered the object
|
|
|
|
if (mrRenderState.isObjectAlreadyRendered(pObject))
|
|
|
|
return;
|
2024-08-08 15:44:47 +02:00
|
|
|
|
2024-08-21 11:25:05 +02:00
|
|
|
// Check if we are in correct stage
|
|
|
|
if (mrRenderState.meStage == RenderStage::Master && !pPage->IsMasterPage())
|
2024-08-08 15:44:47 +02:00
|
|
|
{
|
2024-09-25 15:10:05 +02:00
|
|
|
if (mrRenderState.isFirstObjectInPass())
|
2024-08-21 11:25:05 +02:00
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
}
|
2024-08-08 15:44:47 +02:00
|
|
|
}
|
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
// Paragraph rendering switches
|
|
|
|
bool bRenderOtherParagraphs = false;
|
|
|
|
std::deque<sal_Int32> nOtherParagraphs;
|
|
|
|
|
|
|
|
// check if object is in animation
|
|
|
|
auto aIterator = mrRenderState.maAnimationRenderInfoList.find(pObject);
|
|
|
|
if (aIterator != mrRenderState.maAnimationRenderInfoList.end())
|
2024-08-30 16:56:59 +02:00
|
|
|
{
|
2024-09-10 06:29:39 +02:00
|
|
|
// Animated object has to be only one in the render
|
2024-09-25 15:10:05 +02:00
|
|
|
mrRenderState.mbSkipAllInThisPass = true; // skip all next objects
|
2024-09-10 06:29:39 +02:00
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
// Force a new layer
|
|
|
|
if (!mrRenderState.isFirstObjectInPass())
|
2024-09-09 18:14:16 +02:00
|
|
|
return;
|
2024-08-30 16:56:59 +02:00
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
AnimationRenderInfo aInfo = aIterator->second;
|
|
|
|
|
|
|
|
if (mrRenderState.maParagraphsToRender.empty()
|
|
|
|
&& !aInfo.maParagraphs.empty()) // we need to render paragraphs
|
|
|
|
{
|
|
|
|
auto* pTextObject = dynamic_cast<SdrTextObj*>(pObject);
|
|
|
|
if (pTextObject)
|
|
|
|
{
|
|
|
|
sal_Int32 nNumberOfParagraphs = pTextObject->GetOutlinerParaObject()->Count();
|
|
|
|
|
|
|
|
for (sal_Int32 nParagraph = 0; nParagraph < nNumberOfParagraphs; ++nParagraph)
|
|
|
|
nOtherParagraphs.push_back(nParagraph);
|
|
|
|
|
|
|
|
for (sal_Int32 nParagraph : aInfo.maParagraphs)
|
|
|
|
{
|
|
|
|
mrRenderState.maParagraphsToRender.push_back(nParagraph);
|
|
|
|
std::erase(nOtherParagraphs, nParagraph);
|
|
|
|
}
|
|
|
|
bRenderOtherParagraphs = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (mrRenderState.meStage == RenderStage::Master && hasFields(pObject)
|
|
|
|
&& mrRenderState.mbStopRenderingWhenField && !mrRenderState.isFirstObjectInPass())
|
2024-08-21 11:25:05 +02:00
|
|
|
{
|
|
|
|
mrRenderState.mbStopRenderingWhenField = false;
|
|
|
|
mrRenderState.mbSkipAllInThisPass = true;
|
2024-08-08 15:44:47 +02:00
|
|
|
return;
|
2024-08-21 11:25:05 +02:00
|
|
|
}
|
2024-08-08 15:44:47 +02:00
|
|
|
|
2024-09-09 18:14:16 +02:00
|
|
|
mrRenderState.mpCurrentTarget = pObject;
|
|
|
|
|
2024-08-21 11:25:05 +02:00
|
|
|
// render the object
|
2024-08-08 15:44:47 +02:00
|
|
|
sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
|
|
|
|
rOriginal, rDisplayInfo, rVisitor);
|
2024-08-21 11:25:05 +02:00
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
if (!mrRenderState.maParagraphsToRender.empty())
|
|
|
|
{
|
|
|
|
auto rContainer
|
|
|
|
= static_cast<drawinglayer::primitive2d::Primitive2DContainer&>(rVisitor);
|
|
|
|
|
|
|
|
if (bRenderOtherParagraphs)
|
|
|
|
{
|
|
|
|
modifyParagraphs(rContainer, 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;
|
|
|
|
modifyParagraphs(rContainer, aPreserveParagraphs,
|
|
|
|
false); // render only the paragraphs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mrRenderState.maParagraphsToRender.empty())
|
|
|
|
{
|
|
|
|
mrRenderState.mnRenderedObjectsInPass++;
|
|
|
|
mrRenderState.maObjectsDone.insert(pObject);
|
|
|
|
}
|
2024-08-08 15:44:47 +02:00
|
|
|
}
|
|
|
|
};
|
2024-11-15 13:20:06 +01:00
|
|
|
|
|
|
|
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;
|
2024-08-08 15:44:47 +02:00
|
|
|
}
|
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
SdrObject* getObjectForShape(uno::Reference<drawing::XShape> const& xShape)
|
|
|
|
{
|
|
|
|
if (!xShape.is())
|
|
|
|
return nullptr;
|
|
|
|
SvxShape* pShape = comphelper::getFromUnoTunnel<SvxShape>(xShape);
|
|
|
|
if (pShape)
|
|
|
|
return pShape->GetSdrObject();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2024-11-15 13:20:06 +01:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
SlideshowLayerRenderer::SlideshowLayerRenderer(SdrPage& rPage)
|
|
|
|
: mrPage(rPage)
|
|
|
|
, mrModel(rPage.getSdrModelFromSdrPage())
|
2024-08-08 15:44:47 +02:00
|
|
|
{
|
2024-09-09 17:34:03 +02:00
|
|
|
maRenderState.meStage = RenderStage::Background;
|
2024-08-30 16:56:59 +02:00
|
|
|
setupAnimations();
|
|
|
|
}
|
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
void SlideshowLayerRenderer::resolveEffect(CustomAnimationEffectPtr const& rEffect)
|
2024-08-30 16:56:59 +02:00
|
|
|
{
|
2024-09-25 15:10:05 +02:00
|
|
|
SdrObject* pObject = nullptr;
|
|
|
|
sal_Int32 nParagraph = -1;
|
|
|
|
uno::Reference<drawing::XShape> xShape;
|
2024-08-30 16:56:59 +02:00
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
uno::Any aTargetAny(rEffect->getTarget());
|
|
|
|
|
|
|
|
if ((aTargetAny >>= xShape) && xShape.is())
|
|
|
|
{
|
|
|
|
pObject = getObjectForShape(xShape);
|
|
|
|
}
|
|
|
|
else // if target is not a shape - could be paragraph target containing a shape
|
|
|
|
{
|
|
|
|
presentation::ParagraphTarget aParagraphTarget;
|
|
|
|
if ((aTargetAny >>= aParagraphTarget) && aParagraphTarget.Shape.is())
|
|
|
|
{
|
|
|
|
nParagraph = aParagraphTarget.Paragraph;
|
|
|
|
pObject = getObjectForShape(aParagraphTarget.Shape);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pObject)
|
2024-08-30 16:56:59 +02:00
|
|
|
return;
|
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
AnimationRenderInfo aAnimationInfo;
|
|
|
|
auto aIterator = maRenderState.maAnimationRenderInfoList.find(pObject);
|
|
|
|
if (aIterator != maRenderState.maAnimationRenderInfoList.end())
|
|
|
|
aAnimationInfo = aIterator->second;
|
|
|
|
|
|
|
|
AnimationLayerInfo aLayerInfo;
|
2024-08-30 16:56:59 +02:00
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
std::optional<bool> bVisible;
|
|
|
|
uno::Any aAny
|
|
|
|
= rEffect->getProperty(animations::AnimationNodeType::SET, u"Visibility", EValue::To);
|
|
|
|
if (aAny.hasValue())
|
2024-08-30 16:56:59 +02:00
|
|
|
{
|
2024-09-25 15:10:05 +02:00
|
|
|
// if initial anim sets shape visible, set it
|
|
|
|
// to invisible. If we're asked for the final
|
|
|
|
// state, don't do anything obviously
|
|
|
|
bVisible = !anim::getVisibilityPropertyForAny(aAny);
|
|
|
|
}
|
|
|
|
aLayerInfo.moInitiallyVisible = bVisible;
|
2024-08-30 16:56:59 +02:00
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
OStringBuffer aStringBuffer;
|
|
|
|
anim::convertTarget(aStringBuffer, aTargetAny);
|
|
|
|
aLayerInfo.msHash = aStringBuffer.makeStringAndClear();
|
2024-09-08 20:56:06 +02:00
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
// We have paragraphs
|
|
|
|
if (nParagraph >= 0)
|
|
|
|
{
|
|
|
|
auto aParagraphIterator = std::find(aAnimationInfo.maParagraphs.begin(),
|
|
|
|
aAnimationInfo.maParagraphs.end(), nParagraph);
|
2024-09-08 20:56:06 +02:00
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
// Check if paragraph already exists
|
|
|
|
if (aParagraphIterator == aAnimationInfo.maParagraphs.end())
|
|
|
|
{
|
|
|
|
// We have a paragraph, so write the hash for the paragraph
|
|
|
|
aAnimationInfo.maParagraphs.push_back(nParagraph);
|
|
|
|
aAnimationInfo.maParagraphInfos.emplace(nParagraph, aLayerInfo);
|
2024-08-30 16:56:59 +02:00
|
|
|
}
|
|
|
|
}
|
2024-09-25 15:10:05 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!aAnimationInfo.moObjectInfo)
|
|
|
|
aAnimationInfo.moObjectInfo = aLayerInfo;
|
|
|
|
}
|
|
|
|
|
2025-01-03 15:10:42 +00:00
|
|
|
maRenderState.maAnimationRenderInfoList[pObject] = std::move(aAnimationInfo);
|
2024-09-25 15:10:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void SlideshowLayerRenderer::setupAnimations()
|
|
|
|
{
|
|
|
|
auto* pSdPage = dynamic_cast<SdPage*>(&mrPage);
|
|
|
|
|
|
|
|
if (!pSdPage)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto const& rMain = pSdPage->getMainSequence();
|
|
|
|
|
|
|
|
for (auto const& rEffect : rMain->getSequence())
|
|
|
|
{
|
|
|
|
resolveEffect(rEffect);
|
|
|
|
}
|
2024-08-08 15:44:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Size SlideshowLayerRenderer::calculateAndSetSizePixel(Size const& rDesiredSizePixel)
|
|
|
|
{
|
2024-11-15 13:20:06 +01:00
|
|
|
double fRatio = double(mrPage.GetHeight()) / mrPage.GetWidth();
|
2024-08-08 15:44:47 +02:00
|
|
|
Size aSize(rDesiredSizePixel.Width(), ::tools::Long(rDesiredSizePixel.Width() * fRatio));
|
|
|
|
maSlideSize = aSize;
|
|
|
|
|
|
|
|
return maSlideSize;
|
|
|
|
}
|
|
|
|
|
2024-08-21 11:25:05 +02:00
|
|
|
void SlideshowLayerRenderer::createViewAndDraw(RenderContext& rRenderContext)
|
2024-08-08 15:44:47 +02:00
|
|
|
{
|
2024-08-19 20:51:18 +02:00
|
|
|
SdrView aView(mrModel, rRenderContext.maVirtualDevice);
|
2024-11-15 13:20:06 +01:00
|
|
|
aView.SetPageVisible(false);
|
|
|
|
aView.SetPageShadowVisible(false);
|
|
|
|
aView.SetPageBorderVisible(false);
|
|
|
|
aView.SetBordVisible(false);
|
|
|
|
aView.SetGridVisible(false);
|
|
|
|
aView.SetHlplVisible(false);
|
|
|
|
aView.SetGlueVisible(false);
|
2024-08-21 11:25:05 +02:00
|
|
|
aView.setHideBackground(!maRenderState.includeBackground());
|
2024-11-15 13:20:06 +01:00
|
|
|
aView.ShowSdrPage(&mrPage);
|
|
|
|
|
2024-08-19 20:51:18 +02:00
|
|
|
Size aPageSize(mrPage.GetSize());
|
|
|
|
Point aPoint;
|
|
|
|
|
2024-11-15 13:20:06 +01:00
|
|
|
vcl::Region aRegion(::tools::Rectangle(aPoint, aPageSize));
|
2024-08-21 11:25:05 +02:00
|
|
|
ObjectRedirector aRedirector(maRenderState);
|
2024-08-19 20:51:18 +02:00
|
|
|
aView.CompleteRedraw(rRenderContext.maVirtualDevice, aRegion, &aRedirector);
|
|
|
|
}
|
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
void writeContentNode(::tools::JsonWriter& aJsonWriter)
|
2024-09-04 21:38:29 +02:00
|
|
|
{
|
|
|
|
auto aContentNode = aJsonWriter.startNode("content");
|
|
|
|
aJsonWriter.put("type", "%IMAGETYPE%");
|
|
|
|
aJsonWriter.put("checksum", "%IMAGECHECKSUM%");
|
|
|
|
}
|
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
void writeBoundingBox(::tools::JsonWriter& aJsonWriter, SdrObject* pObject)
|
2024-09-10 09:56:54 +02:00
|
|
|
{
|
|
|
|
auto aContentNode = aJsonWriter.startNode("bounds");
|
2024-09-10 11:31:37 +02:00
|
|
|
::tools::Rectangle aRectmm100 = pObject->GetCurrentBoundRect();
|
|
|
|
::tools::Rectangle aRect = o3tl::convert(aRectmm100, o3tl::Length::mm100, o3tl::Length::twip);
|
2024-09-10 09:56:54 +02:00
|
|
|
aJsonWriter.put("x", aRect.getX());
|
|
|
|
aJsonWriter.put("y", aRect.getY());
|
|
|
|
aJsonWriter.put("width", aRect.GetWidth());
|
|
|
|
aJsonWriter.put("height", aRect.GetHeight());
|
|
|
|
}
|
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
void writeAnimated(::tools::JsonWriter& aJsonWriter, AnimationLayerInfo const& rLayerInfo,
|
|
|
|
SdrObject* pObject)
|
|
|
|
{
|
|
|
|
aJsonWriter.put("type", "animated");
|
|
|
|
{
|
|
|
|
bool bInitiallyVisible = true;
|
|
|
|
if (rLayerInfo.moInitiallyVisible.has_value())
|
|
|
|
bInitiallyVisible = *rLayerInfo.moInitiallyVisible;
|
|
|
|
|
|
|
|
auto aContentNode = aJsonWriter.startNode("content");
|
|
|
|
aJsonWriter.put("hash", rLayerInfo.msHash);
|
|
|
|
aJsonWriter.put("initVisible", bInitiallyVisible);
|
|
|
|
aJsonWriter.put("type", "bitmap");
|
|
|
|
writeContentNode(aJsonWriter);
|
|
|
|
writeBoundingBox(aJsonWriter, pObject);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2024-08-21 11:25:05 +02:00
|
|
|
void SlideshowLayerRenderer::writeJSON(OString& rJsonMsg)
|
2024-08-19 20:51:18 +02:00
|
|
|
{
|
2024-11-15 13:20:06 +01:00
|
|
|
::tools::JsonWriter aJsonWriter;
|
2024-08-21 11:25:05 +02:00
|
|
|
aJsonWriter.put("group", maRenderState.stageString());
|
|
|
|
aJsonWriter.put("index", maRenderState.currentIndex());
|
2024-11-15 13:20:06 +01:00
|
|
|
aJsonWriter.put("slideHash", GetInterfaceHash(GetXDrawPageForSdrPage(&mrPage)));
|
2024-08-21 11:25:05 +02:00
|
|
|
|
2024-09-04 21:38:29 +02:00
|
|
|
SdrObject* pObject = maRenderState.currentTarget();
|
2024-09-25 15:10:05 +02:00
|
|
|
sal_Int32 nParagraph = maRenderState.currentTargetParagraph();
|
2024-09-04 21:38:29 +02:00
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
auto aIterator = maRenderState.maAnimationRenderInfoList.find(pObject);
|
|
|
|
if (aIterator != maRenderState.maAnimationRenderInfoList.end())
|
2024-11-15 13:20:06 +01:00
|
|
|
{
|
2024-09-25 15:10:05 +02:00
|
|
|
AnimationRenderInfo& rInfo = aIterator->second;
|
2024-09-09 23:40:05 +02:00
|
|
|
assert(pObject);
|
2024-09-25 15:10:05 +02:00
|
|
|
|
|
|
|
if (nParagraph >= 0)
|
|
|
|
{
|
|
|
|
auto aParagraphInfoIterator = rInfo.maParagraphInfos.find(nParagraph);
|
|
|
|
if (aParagraphInfoIterator != rInfo.maParagraphInfos.end())
|
|
|
|
{
|
|
|
|
writeAnimated(aJsonWriter, aParagraphInfoIterator->second, pObject);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (rInfo.moObjectInfo)
|
|
|
|
{
|
|
|
|
writeAnimated(aJsonWriter, *rInfo.moObjectInfo, pObject);
|
|
|
|
}
|
|
|
|
else // No object hash and paragraph hash -> Non-animated part of the object (non-animated paragraphs)
|
2024-09-04 21:38:29 +02:00
|
|
|
{
|
|
|
|
aJsonWriter.put("type", "bitmap");
|
|
|
|
writeContentNode(aJsonWriter);
|
|
|
|
}
|
2024-11-15 13:20:06 +01:00
|
|
|
}
|
2024-09-04 21:38:29 +02:00
|
|
|
else
|
|
|
|
{
|
2024-09-11 09:13:59 +02:00
|
|
|
if (pObject && hasFields(pObject))
|
|
|
|
aJsonWriter.put("isField", true); // TODO: to be removed, implement properly
|
2024-09-04 21:38:29 +02:00
|
|
|
aJsonWriter.put("type", "bitmap");
|
|
|
|
writeContentNode(aJsonWriter);
|
|
|
|
}
|
|
|
|
|
2024-11-15 13:20:06 +01:00
|
|
|
rJsonMsg = aJsonWriter.finishAndGetAsOString();
|
|
|
|
|
2024-08-21 11:25:05 +02:00
|
|
|
maRenderState.incrementIndex();
|
2024-11-15 13:20:06 +01:00
|
|
|
}
|
|
|
|
|
2024-12-16 07:42:05 -04:00
|
|
|
bool SlideshowLayerRenderer::render(unsigned char* pBuffer, double& rScale, OString& rJsonMsg)
|
2024-11-15 13:20:06 +01:00
|
|
|
{
|
2024-09-25 15:10:05 +02:00
|
|
|
// We want to render one pass (one iteration through objects)
|
|
|
|
|
|
|
|
// Reset state for this pass
|
2024-08-21 11:25:05 +02:00
|
|
|
maRenderState.resetPass();
|
2024-08-08 15:44:47 +02:00
|
|
|
|
2024-12-16 07:42:05 -04:00
|
|
|
RenderContext aRenderContext(pBuffer, mrModel, mrPage, maSlideSize, Fraction(rScale));
|
2024-08-21 11:25:05 +02:00
|
|
|
createViewAndDraw(aRenderContext);
|
2024-08-08 15:44:47 +02:00
|
|
|
|
2024-09-25 15:10:05 +02:00
|
|
|
// Check if we are done rendering all passes and there is no more output
|
2024-08-21 11:25:05 +02:00
|
|
|
if (maRenderState.noMoreOutput())
|
2024-11-15 13:20:06 +01:00
|
|
|
return false;
|
|
|
|
|
2024-08-21 11:25:05 +02:00
|
|
|
writeJSON(rJsonMsg);
|
2024-11-15 13:20:06 +01:00
|
|
|
|
2024-08-21 11:25:05 +02:00
|
|
|
maRenderState.mnCurrentPass++;
|
2024-08-08 15:44:47 +02:00
|
|
|
|
2024-09-09 17:34:03 +02:00
|
|
|
if (maRenderState.meStage == RenderStage::Background)
|
|
|
|
maRenderState.meStage = RenderStage::Master;
|
|
|
|
|
|
|
|
if (hasEmptyMaster(mrPage))
|
|
|
|
maRenderState.meStage = RenderStage::Slide;
|
|
|
|
|
2024-08-08 15:44:47 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // end of namespace sd
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|