1917 lines
69 KiB
C++
1917 lines
69 KiB
C++
/* -*- 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 <osl/diagnose.hxx>
|
|
#include <canvas/canvastools.hxx>
|
|
#include <comphelper/diagnose_ex.hxx>
|
|
#include <cppcanvas/basegfxfactory.hxx>
|
|
#include <cppcanvas/vclfactory.hxx>
|
|
|
|
#include <basegfx/matrix/b2dhommatrix.hxx>
|
|
#include <basegfx/matrix/b2dhommatrixtools.hxx>
|
|
#include <basegfx/point/b2dpoint.hxx>
|
|
|
|
#include <com/sun/star/awt/SystemPointer.hpp>
|
|
#include <com/sun/star/drawing/XMasterPageTarget.hpp>
|
|
#include <com/sun/star/frame/XModel.hpp>
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
#include <com/sun/star/presentation/ParagraphTarget.hpp>
|
|
#include <com/sun/star/presentation/EffectNodeType.hpp>
|
|
|
|
#include <slide.hxx>
|
|
#include <slideshowcontext.hxx>
|
|
#include "slideanimations.hxx"
|
|
#include <doctreenode.hxx>
|
|
#include <screenupdater.hxx>
|
|
#include <cursormanager.hxx>
|
|
#include <shapeimporter.hxx>
|
|
#include <slideshowexceptions.hxx>
|
|
#include <eventqueue.hxx>
|
|
#include <activitiesqueue.hxx>
|
|
#include "layermanager.hxx"
|
|
#include "shapemanagerimpl.hxx"
|
|
#include <usereventqueue.hxx>
|
|
#include "userpaintoverlay.hxx"
|
|
#include "targetpropertiescreator.hxx"
|
|
#include <tools.hxx>
|
|
#include <tools/helpers.hxx>
|
|
#include <tools/json_writer.hxx>
|
|
#include <box2dtools.hxx>
|
|
#include <utility>
|
|
#include <vcl/graphicfilter.hxx>
|
|
#include <vcl/virdev.hxx>
|
|
#include <svx/svdograf.hxx>
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
|
|
namespace slideshow::internal
|
|
{
|
|
|
|
namespace
|
|
{
|
|
basegfx::B2IVector getSlideSizePixel(const basegfx::B2DVector& rSlideSize,
|
|
const basegfx::B2DHomMatrix& rTransformation)
|
|
{
|
|
const basegfx::B2DRange aRect(0, 0, rSlideSize.getX(), rSlideSize.getY());
|
|
|
|
basegfx::B2DRange aTmpRect = canvas::tools::calcTransformedRectBounds(aRect, rTransformation);
|
|
|
|
// #i42440# Returned slide size is one pixel too small, as
|
|
// rendering happens one pixel to the right and below the
|
|
// actual bound rect.
|
|
return basegfx::B2IVector(basegfx::fround(aTmpRect.getRange().getX()) + 1,
|
|
basegfx::fround(aTmpRect.getRange().getY()) + 1);
|
|
}
|
|
|
|
basegfx::B2DHomMatrix createTransformation(Size& rDeviceSize, const Size& rSlideSize )
|
|
{
|
|
basegfx::B2DHomMatrix aViewTransform(1, 0, 0, 0, 1, 0);
|
|
|
|
const Size aWindowSize( rDeviceSize );
|
|
Size aOutputSize( aWindowSize );
|
|
Size aPageSize( rSlideSize );
|
|
|
|
const double page_ratio = static_cast<double>(aPageSize.Width()) / static_cast<double>(aPageSize.Height());
|
|
const double output_ratio = static_cast<double>(aOutputSize.Width()) / static_cast<double>(aOutputSize.Height());
|
|
|
|
if( page_ratio > output_ratio )
|
|
{
|
|
aOutputSize.setHeight( ( aOutputSize.Width() * aPageSize.Height() ) / aPageSize.Width() );
|
|
}
|
|
else if( page_ratio < output_ratio )
|
|
{
|
|
aOutputSize.setWidth( ( aOutputSize.Height() * aPageSize.Width() ) / aPageSize.Height() );
|
|
}
|
|
|
|
// Reduce available width by one, as the slides might actually
|
|
// render one pixel wider and higher as aPageSize below specifies
|
|
// (when shapes of page size have visible border lines)
|
|
aOutputSize.AdjustWidth( -1 );
|
|
aOutputSize.AdjustHeight( -1 );
|
|
|
|
rDeviceSize = aOutputSize;
|
|
|
|
// scale presentation into available window rect (minus 10%); center in the window
|
|
aViewTransform = basegfx::utils::createScaleB2DHomMatrix(aOutputSize.Width(), aOutputSize.Height());
|
|
|
|
if (basegfx::fTools::equalZero(aViewTransform.get(0,0)) ||
|
|
basegfx::fTools::equalZero(aViewTransform.get(1,1)))
|
|
{
|
|
OSL_FAIL( "SlideView::SlideView(): Singular matrix!" );
|
|
|
|
aViewTransform = basegfx::B2DHomMatrix::abcdef(1, 0, 0, 0, 1, 0);
|
|
}
|
|
|
|
basegfx::B2DHomMatrix aScaleMatrix;
|
|
aScaleMatrix.scale( 1.0 / rSlideSize.getWidth(), 1.0 / rSlideSize.getHeight() );
|
|
aViewTransform = aViewTransform * aScaleMatrix;
|
|
|
|
return aViewTransform;
|
|
}
|
|
|
|
OUString getPlaceholderType(std::u16string_view sShapeType)
|
|
{
|
|
OUString aType;
|
|
if (sShapeType == u"com.sun.star.presentation.SlideNumberShape")
|
|
aType = u"SlideNumber"_ustr;
|
|
if (sShapeType == u"com.sun.star.presentation.FooterShape")
|
|
aType = u"Footer"_ustr;
|
|
if (sShapeType == u"com.sun.star.presentation.DateTimeShape")
|
|
aType = u"DateTime"_ustr;
|
|
|
|
return aType;
|
|
}
|
|
|
|
void appendImageInfoPlaceholder(tools::JsonWriter& rJsonWriter)
|
|
{
|
|
auto aContentNode = rJsonWriter.startNode("content");
|
|
rJsonWriter.put("type", "%IMAGETYPE%");
|
|
rJsonWriter.put("checksum", "%IMAGECHECKSUM%");
|
|
}
|
|
|
|
class LOKSlideRenderer
|
|
{
|
|
public:
|
|
enum LayerGroupType
|
|
{
|
|
BACKGROUND,
|
|
MASTER_PAGE,
|
|
DRAW_PAGE,
|
|
TEXT_FIELDS
|
|
};
|
|
public:
|
|
LOKSlideRenderer(const Size& rViewSize, const Size& rSlideSize,
|
|
bool bRenderBackground, bool bRenderMasterPageObjects,
|
|
const uno::Reference<drawing::XDrawPage>& rxDrawPage,
|
|
const uno::Reference<drawing::XDrawPagesSupplier>& rxDrawPagesSupplier,
|
|
const uno::Reference<animations::XAnimationNode>& rxRootNode,
|
|
const SlideShowContext& rContext,
|
|
const std::shared_ptr<LayerManager>& pLayerManager,
|
|
bool bSkipAnimations);
|
|
|
|
void renderNextLayer(unsigned char* pBuffer);
|
|
|
|
const Size& getDeviceSize() const { return maDeviceSize; }
|
|
bool isSlideRenderingDone() const { return mbSlideRenderingDone; }
|
|
bool isBitmapLayer() const { return mbIsBitmapLayer; }
|
|
|
|
const OString& getJsonMessage() const { return msLastJsonMessage; }
|
|
|
|
private:
|
|
void collectAnimatedShapes();
|
|
|
|
void renderImpl(LayerGroupType eLayersSet, unsigned char* pBuffer);
|
|
void renderBackgroundImpl(VirtualDevice& rDevice);
|
|
void renderTextFieldsImpl(VirtualDevice& rDevice);
|
|
void renderMasterPageImpl(VirtualDevice& rDevice);
|
|
void renderDrawPageImpl(VirtualDevice& rDevice);
|
|
|
|
void renderLayerImpl(VirtualDevice& rDevice, tools::JsonWriter& rJsonWriter);
|
|
void renderAnimatedShapeImpl(VirtualDevice& rDevice, const std::shared_ptr<Shape>& pShape,
|
|
tools::JsonWriter& rJsonWriter);
|
|
|
|
SlideBitmapSharedPtr createLayerBitmap(const ::cppcanvas::CanvasSharedPtr& pCanvas,
|
|
const ::basegfx::B2ISize& rBmpSize ) const;
|
|
void renderLayerBitmapImpl(VirtualDevice& rDevice);
|
|
|
|
private:
|
|
Size maDeviceSize;
|
|
Size maSlideSize;
|
|
//bool mbRenderBackground;
|
|
//bool mbRenderMasterPageObjects;
|
|
basegfx::B2DHomMatrix maTransformation;
|
|
uno::Reference<drawing::XDrawPage> mxDrawPage;
|
|
uno::Reference<drawing::XDrawPagesSupplier> mxDrawPagesSupplier;
|
|
uno::Reference<animations::XAnimationNode> mxRootNode;
|
|
const SlideShowContext& mrContext;
|
|
std::shared_ptr<LayerManager> mpLayerManager;
|
|
uno::Reference<drawing::XDrawPage> mxMasterPage;
|
|
std::shared_ptr<ShapeImporter> mpTFShapesFunctor;
|
|
std::shared_ptr<ShapeImporter> mpMPShapesFunctor;
|
|
std::shared_ptr<ShapeImporter> mpShapesFunctor;
|
|
std::unordered_map< BitmapChecksum, BitmapEx > maBitmapMap;
|
|
std::unordered_map<std::string, bool> maAnimatedShapeVisibilityMap;
|
|
|
|
sal_uInt32 mnMPLayerIndex;
|
|
sal_uInt32 mnDPLayerIndex;
|
|
bool mbBackgroundRenderingDone;
|
|
bool mbTextFieldsRenderingDone;
|
|
bool mbMasterPageRenderingDone;
|
|
bool mbDrawPageRenderingDone;
|
|
bool mbSlideRenderingDone;
|
|
bool mbIsPageNumberVisible;
|
|
bool mbIsDateTimeVisible;
|
|
bool mbIsFooterVisible;
|
|
ShapeSharedPtr mpDPLastAnimatedShape;
|
|
OUString msLastPlaceholder;
|
|
|
|
bool mbIsBitmapLayer;
|
|
bool mbSkipAnimations = false;
|
|
OString msLastJsonMessage;
|
|
};
|
|
|
|
LOKSlideRenderer::LOKSlideRenderer(const Size& rViewSize, const Size& rSlideSize,
|
|
bool /*bRenderBackground*/, bool /*bRenderMasterPageObjects*/,
|
|
const uno::Reference<drawing::XDrawPage>& rxDrawPage,
|
|
const uno::Reference<drawing::XDrawPagesSupplier>& rxDrawPagesSupplier,
|
|
const uno::Reference<animations::XAnimationNode>& rxRootNode,
|
|
const SlideShowContext& rContext,
|
|
const std::shared_ptr<LayerManager>& pLayerManager,
|
|
bool bSkipAnimations)
|
|
|
|
: maDeviceSize(rViewSize),
|
|
maSlideSize(rSlideSize),
|
|
//mbRenderBackground(bRenderBackground),
|
|
//mbRenderMasterPageObjects(bRenderMasterPageObjects),
|
|
maTransformation(createTransformation(maDeviceSize, maSlideSize)),
|
|
mxDrawPage(rxDrawPage),
|
|
mxDrawPagesSupplier(rxDrawPagesSupplier),
|
|
mxRootNode(rxRootNode),
|
|
mrContext(rContext),
|
|
mpLayerManager(pLayerManager),
|
|
mnMPLayerIndex(0),
|
|
mnDPLayerIndex(0),
|
|
mbBackgroundRenderingDone(false),
|
|
mbTextFieldsRenderingDone(false),
|
|
mbMasterPageRenderingDone(false),
|
|
mbDrawPageRenderingDone(false),
|
|
mbSlideRenderingDone(false),
|
|
mbIsPageNumberVisible(true),
|
|
mbIsDateTimeVisible(true),
|
|
mbIsFooterVisible(true),
|
|
mbIsBitmapLayer(false),
|
|
mbSkipAnimations(bSkipAnimations)
|
|
{
|
|
uno::Reference< drawing::XMasterPageTarget > xMasterPageTarget( mxDrawPage, uno::UNO_QUERY );
|
|
if( xMasterPageTarget.is() )
|
|
{
|
|
mxMasterPage = xMasterPageTarget->getMasterPage();
|
|
uno::Reference< drawing::XShapes > xMasterPageShapes = mxMasterPage;
|
|
OSL_ASSERT(mxDrawPage.is() && mxMasterPage.is() && xMasterPageShapes.is());
|
|
|
|
uno::Reference<beans::XPropertySet> xPropSet(mxDrawPage, uno::UNO_QUERY);
|
|
OSL_ASSERT(xPropSet.is());
|
|
|
|
bool bBackgroundVisibility = true; // default visible
|
|
xPropSet->getPropertyValue("IsBackgroundVisible") >>= bBackgroundVisibility;
|
|
mbBackgroundRenderingDone = !bBackgroundVisibility;
|
|
|
|
bool bBackgroundObjectsVisibility = true; // default visible
|
|
xPropSet->getPropertyValue("IsBackgroundObjectsVisible") >>= bBackgroundObjectsVisibility;
|
|
mbTextFieldsRenderingDone = mbMasterPageRenderingDone = !bBackgroundObjectsVisibility;
|
|
|
|
// try to skip empty layer
|
|
if (bBackgroundObjectsVisibility)
|
|
{
|
|
xPropSet->getPropertyValue("IsPageNumberVisible") >>= mbIsPageNumberVisible;
|
|
xPropSet->getPropertyValue("IsDateTimeVisible") >>= mbIsDateTimeVisible;
|
|
xPropSet->getPropertyValue("IsFooterVisible") >>= mbIsFooterVisible;
|
|
if (mbIsDateTimeVisible)
|
|
{
|
|
bool bDateTimeFixed = true; // default: fixed
|
|
xPropSet->getPropertyValue("IsDateTimeFixed") >>= bDateTimeFixed;
|
|
if (bDateTimeFixed)
|
|
{
|
|
OUString sDateTimeText;
|
|
xPropSet->getPropertyValue("DateTimeText") >>= sDateTimeText;
|
|
mbIsDateTimeVisible = !sDateTimeText.isEmpty();
|
|
}
|
|
}
|
|
if (mbIsFooterVisible)
|
|
{
|
|
OUString sFooterText;
|
|
xPropSet->getPropertyValue("FooterText") >>= sFooterText;
|
|
mbIsFooterVisible = !sFooterText.isEmpty();
|
|
}
|
|
|
|
mbTextFieldsRenderingDone =
|
|
!mbIsPageNumberVisible && !mbIsDateTimeVisible && !mbIsFooterVisible;
|
|
}
|
|
|
|
if (!mbTextFieldsRenderingDone)
|
|
{
|
|
mpTFShapesFunctor
|
|
= std::make_shared<ShapeImporter>(mxMasterPage, mxDrawPage, mxDrawPagesSupplier,
|
|
mrContext, 0, /* shape num starts at 0 */
|
|
true);
|
|
mpTFShapesFunctor->setTextFieldsOnly(true);
|
|
}
|
|
if (!(mbBackgroundRenderingDone && mbMasterPageRenderingDone))
|
|
{
|
|
mpMPShapesFunctor
|
|
= std::make_shared<ShapeImporter>(mxMasterPage, mxDrawPage, mxDrawPagesSupplier,
|
|
mrContext, 0, /* shape num starts at 0 */
|
|
true);
|
|
mpMPShapesFunctor->setMasterPageObjectsOnly(true);
|
|
}
|
|
|
|
uno::Reference<drawing::XShapes> const xShapes(mxDrawPage, uno::UNO_QUERY_THROW);
|
|
if (xShapes.is())
|
|
{
|
|
mbDrawPageRenderingDone = xShapes->getCount() == 0;
|
|
}
|
|
|
|
if (!mbDrawPageRenderingDone)
|
|
{
|
|
mpShapesFunctor
|
|
= std::make_shared<ShapeImporter>(mxDrawPage, mxDrawPage, mxDrawPagesSupplier,
|
|
mrContext, 0, /* shape num starts at 0 */
|
|
false);
|
|
}
|
|
}
|
|
if (!mbDrawPageRenderingDone)
|
|
collectAnimatedShapes();
|
|
}
|
|
|
|
void LOKSlideRenderer::renderNextLayer(unsigned char* pBuffer)
|
|
{
|
|
OSL_ASSERT(pBuffer);
|
|
|
|
msLastJsonMessage = ""_ostr;
|
|
mbIsBitmapLayer = false;
|
|
|
|
if (!mbDrawPageRenderingDone)
|
|
{
|
|
renderImpl(LayerGroupType::DRAW_PAGE, pBuffer);
|
|
if (!msLastJsonMessage.isEmpty())
|
|
return;
|
|
}
|
|
|
|
mbSlideRenderingDone = true;
|
|
}
|
|
|
|
void LOKSlideRenderer::renderBackgroundImpl(VirtualDevice& rDevice)
|
|
{
|
|
if (mbBackgroundRenderingDone)
|
|
return;
|
|
|
|
tools::JsonWriter aJsonWriter;
|
|
aJsonWriter.put("group", "Background");
|
|
std::string sSlideHash = GetInterfaceHash(mxDrawPage);
|
|
aJsonWriter.put("slideHash", sSlideHash);
|
|
|
|
ShapeSharedPtr const xBGShape(mpMPShapesFunctor->importBackgroundShape());
|
|
mpLayerManager->addShape(xBGShape);
|
|
|
|
// render and collect bitmap
|
|
renderLayerBitmapImpl(rDevice);
|
|
BitmapEx aBitmapEx(
|
|
rDevice.GetBitmapEx(Point(0, 0), rDevice.GetOutputSizePixel()));
|
|
BitmapChecksum nChecksum = aBitmapEx.GetChecksum();
|
|
maBitmapMap[nChecksum] = aBitmapEx;
|
|
|
|
// json
|
|
mbIsBitmapLayer = true;
|
|
aJsonWriter.put("type", "bitmap");
|
|
appendImageInfoPlaceholder(aJsonWriter);
|
|
|
|
msLastJsonMessage = aJsonWriter.finishAndGetAsOString();
|
|
|
|
// clean up
|
|
rDevice.Erase();
|
|
mpLayerManager->removeShape(xBGShape);
|
|
|
|
mbBackgroundRenderingDone = true;
|
|
}
|
|
|
|
void LOKSlideRenderer::renderMasterPageImpl(VirtualDevice& rDevice)
|
|
{
|
|
if (!msLastPlaceholder.isEmpty())
|
|
{
|
|
tools::JsonWriter aJsonWriter;
|
|
aJsonWriter.put("group", "MasterPage");
|
|
aJsonWriter.put("slideHash", GetInterfaceHash(mxDrawPage));
|
|
aJsonWriter.put("index", mnMPLayerIndex);
|
|
|
|
aJsonWriter.put("type", "placeholder");
|
|
{
|
|
auto aContentNode = aJsonWriter.startNode("content");
|
|
aJsonWriter.put("type", msLastPlaceholder);
|
|
}
|
|
|
|
msLastPlaceholder = "";
|
|
msLastJsonMessage = aJsonWriter.finishAndGetAsOString();
|
|
++mnMPLayerIndex;
|
|
return;
|
|
}
|
|
|
|
if (mpMPShapesFunctor->isImportDone())
|
|
mbMasterPageRenderingDone = true;
|
|
|
|
if (mbMasterPageRenderingDone)
|
|
return;
|
|
|
|
tools::JsonWriter aJsonWriter;
|
|
aJsonWriter.put("group", "MasterPage");
|
|
aJsonWriter.put("slideHash", GetInterfaceHash(mxDrawPage));
|
|
aJsonWriter.put("index", mnMPLayerIndex);
|
|
|
|
bool bDoRendering = false;
|
|
while (!mpMPShapesFunctor->isImportDone())
|
|
{
|
|
ShapeSharedPtr const pShape(mpMPShapesFunctor->importShape());
|
|
if (!pShape)
|
|
continue;
|
|
|
|
uno::Reference<drawing::XShape> xShape = pShape->getXShape();
|
|
if (xShape.is())
|
|
{
|
|
OUString sShapeType = xShape->getShapeType();
|
|
OUString sPlaceholderType = getPlaceholderType(sShapeType);
|
|
if (sPlaceholderType.isEmpty())
|
|
{
|
|
mpLayerManager->addShape(pShape);
|
|
bDoRendering = true;
|
|
}
|
|
else
|
|
{
|
|
if (bDoRendering)
|
|
{
|
|
msLastPlaceholder = sPlaceholderType;
|
|
renderLayerImpl(rDevice, aJsonWriter);
|
|
}
|
|
else
|
|
{
|
|
aJsonWriter.put("type", "placeholder");
|
|
{
|
|
auto aContentNode = aJsonWriter.startNode("content");
|
|
aJsonWriter.put("type", sPlaceholderType);
|
|
}
|
|
}
|
|
bDoRendering = false;
|
|
msLastJsonMessage = aJsonWriter.finishAndGetAsOString();
|
|
++mnMPLayerIndex;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (bDoRendering)
|
|
{
|
|
renderLayerImpl(rDevice, aJsonWriter);
|
|
}
|
|
msLastJsonMessage = aJsonWriter.finishAndGetAsOString();
|
|
|
|
mbMasterPageRenderingDone = true;
|
|
}
|
|
|
|
void LOKSlideRenderer::renderTextFieldsImpl(VirtualDevice& rDevice)
|
|
{
|
|
while( !mpTFShapesFunctor->isImportDone() )
|
|
{
|
|
ShapeSharedPtr const pShape(mpTFShapesFunctor->importShape());
|
|
if (!pShape)
|
|
continue;
|
|
|
|
uno::Reference<drawing::XShape> xShape = pShape->getXShape();
|
|
if (xShape.is())
|
|
{
|
|
OUString sShapeType = xShape->getShapeType();
|
|
OUString sPlaceholderType = getPlaceholderType(sShapeType);
|
|
if (!sPlaceholderType.isEmpty())
|
|
{
|
|
if ((!mbIsPageNumberVisible && sPlaceholderType == "SlideNumber") ||
|
|
(!mbIsDateTimeVisible && sPlaceholderType == "DateTime") ||
|
|
(!mbIsFooterVisible && sPlaceholderType == "Footer"))
|
|
continue;
|
|
|
|
mpLayerManager->addShape(pShape);
|
|
|
|
// render and collect bitmap
|
|
renderLayerBitmapImpl(rDevice);
|
|
BitmapEx aBitmapEx(rDevice.GetBitmapEx(Point(0, 0), rDevice.GetOutputSizePixel()));
|
|
BitmapChecksum nChecksum = aBitmapEx.GetChecksum();
|
|
maBitmapMap[nChecksum] = aBitmapEx;
|
|
mbIsBitmapLayer = true;
|
|
|
|
// json
|
|
tools::JsonWriter aJsonWriter;
|
|
aJsonWriter.put("group", "TextFields");
|
|
aJsonWriter.put("slideHash", GetInterfaceHash(mxDrawPage));
|
|
{
|
|
auto aContentNode = aJsonWriter.startNode("content");
|
|
aJsonWriter.put("type", sPlaceholderType);
|
|
appendImageInfoPlaceholder(aJsonWriter);
|
|
}
|
|
msLastJsonMessage = aJsonWriter.finishAndGetAsOString();
|
|
|
|
// clean up
|
|
rDevice.Erase();
|
|
mpLayerManager->removeShape(pShape);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
mbTextFieldsRenderingDone = true;
|
|
}
|
|
|
|
void LOKSlideRenderer::renderLayerImpl(VirtualDevice& rDevice, tools::JsonWriter& rJsonWriter)
|
|
{
|
|
// render and collect bitmap
|
|
renderLayerBitmapImpl(rDevice);
|
|
BitmapEx aBitmapEx(rDevice.GetBitmapEx(Point(0, 0), rDevice.GetOutputSizePixel()));
|
|
BitmapChecksum nChecksum = aBitmapEx.GetChecksum();
|
|
maBitmapMap[nChecksum] = aBitmapEx;
|
|
|
|
// json
|
|
mbIsBitmapLayer = true;
|
|
rJsonWriter.put("type", "bitmap");
|
|
appendImageInfoPlaceholder(rJsonWriter);
|
|
|
|
// clean up
|
|
rDevice.Erase();
|
|
mpLayerManager->removeAllShapes();
|
|
}
|
|
|
|
void LOKSlideRenderer::renderDrawPageImpl(VirtualDevice& rDevice)
|
|
{
|
|
if (mpDPLastAnimatedShape)
|
|
{
|
|
tools::JsonWriter aJsonWriter;
|
|
aJsonWriter.put("group", "DrawPage");
|
|
aJsonWriter.put("slideHash", GetInterfaceHash(mxDrawPage));
|
|
aJsonWriter.put("index", mnDPLayerIndex);
|
|
|
|
renderAnimatedShapeImpl(rDevice, mpDPLastAnimatedShape, aJsonWriter);
|
|
mpDPLastAnimatedShape.reset();
|
|
msLastJsonMessage = aJsonWriter.finishAndGetAsOString();
|
|
++mnDPLayerIndex;
|
|
return;
|
|
}
|
|
|
|
if (mpShapesFunctor->isImportDone())
|
|
mbDrawPageRenderingDone = true;
|
|
|
|
if (mbDrawPageRenderingDone)
|
|
return;
|
|
|
|
tools::JsonWriter aJsonWriter;
|
|
aJsonWriter.put("group", "DrawPage");
|
|
aJsonWriter.put("slideHash", GetInterfaceHash(mxDrawPage));
|
|
aJsonWriter.put("index", mnDPLayerIndex);
|
|
|
|
bool bDoRendering = false;
|
|
|
|
std::shared_ptr<ShapeImporter> pMasterShapeImporter = std::make_shared<ShapeImporter>(mxMasterPage, mxDrawPage, mxDrawPagesSupplier, mrContext, 0, true);
|
|
|
|
ShapeSharedPtr const pBGShape(pMasterShapeImporter->importBackgroundShape());
|
|
if (pBGShape)
|
|
{
|
|
bDoRendering = true;
|
|
mpLayerManager->addShape(pBGShape);
|
|
}
|
|
|
|
while (!pMasterShapeImporter->isImportDone())
|
|
{
|
|
ShapeSharedPtr const pShape(pMasterShapeImporter->importShape());
|
|
if (!pShape)
|
|
continue;
|
|
|
|
pShape->setIsForeground(false);
|
|
|
|
uno::Reference<drawing::XShape> xShape = pShape->getXShape();
|
|
if (xShape.is())
|
|
{
|
|
|
|
mpLayerManager->addShape(pShape);
|
|
bDoRendering = true;
|
|
}
|
|
}
|
|
|
|
auto nCurrCount = static_cast<sal_Int32>(pMasterShapeImporter->getImportedShapesCount());
|
|
mpShapesFunctor = std::make_shared<ShapeImporter>(mxDrawPage, mxDrawPage, mxDrawPagesSupplier, mrContext, nCurrCount, false);
|
|
|
|
while (!mpShapesFunctor->isImportDone())
|
|
{
|
|
ShapeSharedPtr const pShape(mpShapesFunctor->importShape());
|
|
if (pShape)
|
|
{
|
|
std::string sShapeId = GetInterfaceHash(pShape->getXShape());
|
|
const auto aIter = maAnimatedShapeVisibilityMap.find(sShapeId);
|
|
bool bIsAnimated = aIter != maAnimatedShapeVisibilityMap.end();
|
|
if (!bIsAnimated)
|
|
{
|
|
mpLayerManager->addShape(pShape);
|
|
bDoRendering = true;
|
|
}
|
|
else
|
|
{
|
|
if (bDoRendering)
|
|
{
|
|
mpDPLastAnimatedShape = pShape;
|
|
renderLayerImpl(rDevice, aJsonWriter);
|
|
}
|
|
else
|
|
{
|
|
renderAnimatedShapeImpl(rDevice, pShape, aJsonWriter);
|
|
}
|
|
msLastJsonMessage = aJsonWriter.finishAndGetAsOString();
|
|
++mnDPLayerIndex;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (bDoRendering)
|
|
{
|
|
renderLayerImpl(rDevice, aJsonWriter);
|
|
}
|
|
msLastJsonMessage = aJsonWriter.finishAndGetAsOString();
|
|
|
|
mbDrawPageRenderingDone = true;
|
|
}
|
|
|
|
void LOKSlideRenderer::renderAnimatedShapeImpl(VirtualDevice& rDevice,
|
|
const std::shared_ptr<Shape>& pShape,
|
|
tools::JsonWriter& rJsonWriter)
|
|
{
|
|
rJsonWriter.put("type", "animated");
|
|
|
|
auto aContentNode = rJsonWriter.startNode("content");
|
|
std::string sShapeId = GetInterfaceHash(pShape->getXShape());
|
|
rJsonWriter.put("hash", sShapeId);
|
|
|
|
bool bIsInitVisible = maAnimatedShapeVisibilityMap.at(sShapeId);
|
|
rJsonWriter.put("initVisible", bIsInitVisible);
|
|
mpLayerManager->addShape(pShape);
|
|
renderLayerImpl(rDevice, rJsonWriter);
|
|
}
|
|
|
|
void LOKSlideRenderer::renderImpl(LayerGroupType eLayersSet, unsigned char* pBuffer)
|
|
{
|
|
VclPtr<VirtualDevice> pDevice = VclPtr<VirtualDevice>::Create(DeviceFormat::WITH_ALPHA);
|
|
pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
|
|
pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(
|
|
maDeviceSize, Fraction(1.0),
|
|
Point(), pBuffer);
|
|
|
|
pDevice->Erase();
|
|
OSL_ASSERT(pDevice->GetCanvas().is());
|
|
mbIsBitmapLayer = false;
|
|
msLastJsonMessage = ""_ostr;
|
|
try
|
|
{
|
|
switch (eLayersSet)
|
|
{
|
|
case LayerGroupType::BACKGROUND: return renderBackgroundImpl(*pDevice);
|
|
case LayerGroupType::MASTER_PAGE: return renderMasterPageImpl(*pDevice);
|
|
case LayerGroupType::DRAW_PAGE: return renderDrawPageImpl(*pDevice);
|
|
case LayerGroupType::TEXT_FIELDS: return renderTextFieldsImpl(*pDevice);
|
|
}
|
|
}
|
|
catch (uno::RuntimeException&)
|
|
{
|
|
throw;
|
|
}
|
|
catch (ShapeLoadFailedException&)
|
|
{
|
|
// TODO(E2): Error handling. For now, bail out
|
|
TOOLS_WARN_EXCEPTION( "slideshow", "SlideImpl::loadShapes(): caught ShapeLoadFailedException" );
|
|
return;
|
|
}
|
|
catch (uno::Exception&)
|
|
{
|
|
TOOLS_WARN_EXCEPTION( "slideshow", "General Exception");
|
|
return;
|
|
}
|
|
}
|
|
|
|
SlideBitmapSharedPtr LOKSlideRenderer::createLayerBitmap(const ::cppcanvas::CanvasSharedPtr& pCanvas,
|
|
const ::basegfx::B2ISize& rBmpSize ) const
|
|
{
|
|
::cppcanvas::BitmapSharedPtr pBitmap(
|
|
::cppcanvas::BaseGfxFactory::createBitmap(
|
|
pCanvas,
|
|
rBmpSize ) );
|
|
|
|
ENSURE_OR_THROW(pBitmap,
|
|
"LOKSlideRenderer::createCurrentSlideBitmap(): Cannot create page bitmap");
|
|
|
|
::cppcanvas::BitmapCanvasSharedPtr pBitmapCanvas(pBitmap->getBitmapCanvas());
|
|
|
|
ENSURE_OR_THROW( pBitmapCanvas,
|
|
"LOKSlideRenderer::createCurrentSlideBitmap(): Cannot create page bitmap canvas" );
|
|
|
|
// apply linear part of destination canvas transformation (linear means in this context:
|
|
// transformation without any translational components)
|
|
::basegfx::B2DHomMatrix aLinearTransform(maTransformation);
|
|
aLinearTransform.set( 0, 2, 0.0 );
|
|
aLinearTransform.set( 1, 2, 0.0 );
|
|
pBitmapCanvas->setTransformation( aLinearTransform );
|
|
|
|
initSlideBackground( pBitmapCanvas, rBmpSize );
|
|
mpLayerManager->renderTo( pBitmapCanvas );
|
|
|
|
return std::make_shared<SlideBitmap>( pBitmap );
|
|
}
|
|
|
|
void LOKSlideRenderer::renderLayerBitmapImpl(VirtualDevice& rDevice)
|
|
{
|
|
auto aSize = getSlideSizePixel(basegfx::B2DVector(maSlideSize.getWidth(), maSlideSize.getHeight()),
|
|
maTransformation);
|
|
const basegfx::B2ISize rSlideSize(aSize.getX(), aSize.getY());
|
|
|
|
::cppcanvas::CanvasSharedPtr pCanvas = cppcanvas::VCLFactory::createCanvas(rDevice.GetCanvas());
|
|
|
|
SlideBitmapSharedPtr pBitmap = createLayerBitmap(pCanvas, rSlideSize);
|
|
|
|
// setup a canvas with device coordinate space, the slide
|
|
// bitmap already has the correct dimension.
|
|
// const ::basegfx::B2DPoint aOutPosPixel( rTransformation * ::basegfx::B2DPoint() );
|
|
::cppcanvas::CanvasSharedPtr pDevicePixelCanvas( pCanvas->clone() );
|
|
|
|
// render at given output position
|
|
// pBitmap->move( aOutPosPixel );
|
|
|
|
// clear clip (might have been changed, e.g. from comb
|
|
// transition)
|
|
pBitmap->clip( ::basegfx::B2DPolyPolygon() );
|
|
pBitmap->draw( pDevicePixelCanvas );
|
|
}
|
|
|
|
void LOKSlideRenderer::collectAnimatedShapes()
|
|
{
|
|
if (mbSkipAnimations)
|
|
return;
|
|
|
|
if (!mxRootNode.is())
|
|
return;
|
|
|
|
const uno::Sequence< animations::TargetProperties > aProps(
|
|
TargetPropertiesCreator::createTargetProperties( mxRootNode, true /* Initial */ ) );
|
|
|
|
for (const auto& rProp : aProps)
|
|
{
|
|
uno::Reference<drawing::XShape> xShape(rProp.Target, uno::UNO_QUERY);
|
|
|
|
if (!xShape.is())
|
|
{
|
|
// not a shape target. Maybe a ParagraphTarget?
|
|
presentation::ParagraphTarget aParaTarget;
|
|
|
|
if (rProp.Target >>= aParaTarget)
|
|
{
|
|
// yep, ParagraphTarget found - extract shape
|
|
// and index
|
|
xShape = aParaTarget.Shape;
|
|
}
|
|
}
|
|
|
|
if( xShape.is() )
|
|
{
|
|
const uno::Sequence< beans::NamedValue >& rShapeProps( rProp.Properties );
|
|
for (const auto& rShapeProp : rShapeProps)
|
|
{
|
|
bool bVisible = false;
|
|
if (rShapeProp.Name.equalsIgnoreAsciiCase("visibility") &&
|
|
extractValue( bVisible,
|
|
rShapeProp.Value,
|
|
nullptr,
|
|
basegfx::B2DVector() ))
|
|
{
|
|
maAnimatedShapeVisibilityMap[GetInterfaceHash(xShape)] = bVisible;
|
|
}
|
|
else
|
|
{
|
|
OSL_FAIL( "LOKSlideRenderer::collectAnimatedShapes:(): Unexpected "
|
|
"(and unimplemented) property encountered" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class SlideImpl : public Slide,
|
|
public CursorManager,
|
|
public ViewEventHandler,
|
|
public ::osl::DebugBase<SlideImpl>
|
|
{
|
|
public:
|
|
SlideImpl( const uno::Reference<drawing::XDrawPage>& xDrawPage,
|
|
uno::Reference<drawing::XDrawPagesSupplier> xDrawPages,
|
|
uno::Reference<animations::XAnimationNode> xRootNode,
|
|
EventQueue& rEventQueue,
|
|
EventMultiplexer& rEventMultiplexer,
|
|
ScreenUpdater& rScreenUpdater,
|
|
ActivitiesQueue& rActivitiesQueue,
|
|
UserEventQueue& rUserEventQueue,
|
|
CursorManager& rCursorManager,
|
|
MediaFileManager& rMediaFileManager,
|
|
const UnoViewContainer& rViewContainer,
|
|
const uno::Reference<uno::XComponentContext>& xContext,
|
|
const ShapeEventListenerMap& rShapeListenerMap,
|
|
const ShapeCursorMap& rShapeCursorMap,
|
|
PolyPolygonVector&& rPolyPolygonVector,
|
|
RGBColor const& rUserPaintColor,
|
|
double dUserPaintStrokeWidth,
|
|
bool bUserPaintEnabled,
|
|
bool bIntrinsicAnimationsAllowed,
|
|
bool bDisableAnimationZOrder);
|
|
|
|
virtual ~SlideImpl() override;
|
|
|
|
|
|
// Slide interface
|
|
|
|
|
|
virtual void prefetch() override;
|
|
virtual void show( bool ) override;
|
|
virtual void hide() override;
|
|
|
|
virtual basegfx::B2ISize getSlideSize() const override;
|
|
virtual uno::Reference<drawing::XDrawPage > getXDrawPage() const override;
|
|
virtual uno::Reference<animations::XAnimationNode> getXAnimationNode() const override;
|
|
virtual PolyPolygonVector getPolygons() override;
|
|
virtual void drawPolygons() const override;
|
|
virtual bool isPaintOverlayActive() const override;
|
|
virtual void enablePaintOverlay() override;
|
|
virtual void update_settings( bool bUserPaintEnabled, RGBColor const& aUserPaintColor, double dUserPaintStrokeWidth ) override;
|
|
|
|
|
|
// TODO(F2): Rework SlideBitmap to no longer be based on XBitmap,
|
|
// but on canvas-independent basegfx bitmaps
|
|
virtual SlideBitmapSharedPtr getCurrentSlideBitmap( const UnoViewSharedPtr& rView ) const override;
|
|
|
|
virtual Size createLOKSlideRenderer(int nViewWidth, int nViewHeight,
|
|
bool bRenderBackground,
|
|
bool bRenderMasterPageObjects) override;
|
|
|
|
virtual bool renderNextLOKSlideLayer(unsigned char* buffer,
|
|
bool& bIsBitmapLayer,
|
|
OString& rJsonMsg) override;
|
|
|
|
private:
|
|
// ViewEventHandler
|
|
virtual void viewAdded( const UnoViewSharedPtr& rView ) override;
|
|
virtual void viewRemoved( const UnoViewSharedPtr& rView ) override;
|
|
virtual void viewChanged( const UnoViewSharedPtr& rView ) override;
|
|
virtual void viewsChanged() override;
|
|
|
|
// CursorManager
|
|
virtual bool requestCursor( sal_Int16 nCursorShape ) override;
|
|
virtual void resetCursor() override;
|
|
|
|
void activatePaintOverlay();
|
|
void deactivatePaintOverlay();
|
|
|
|
/** Query whether the slide has animations at all
|
|
|
|
If the slide doesn't have animations, show() displays
|
|
only static content. If an event is registered with
|
|
registerSlideEndEvent(), this event will be
|
|
immediately activated at the end of the show() method.
|
|
|
|
@return true, if this slide has animations, false
|
|
otherwise
|
|
*/
|
|
bool isAnimated();
|
|
|
|
/// Set all Shapes to their initial attributes for slideshow
|
|
bool applyInitialShapeAttributes( const css::uno::Reference< css::animations::XAnimationNode >& xRootAnimationNode );
|
|
|
|
/// Set shapes to attributes corresponding to initial or final state of slide
|
|
void applyShapeAttributes(
|
|
const css::uno::Reference< css::animations::XAnimationNode >& xRootAnimationNode,
|
|
bool bInitial) const;
|
|
|
|
/// Renders current slide content to bitmap
|
|
SlideBitmapSharedPtr createCurrentSlideBitmap(
|
|
const UnoViewSharedPtr& rView,
|
|
::basegfx::B2ISize const & rSlideSize ) const;
|
|
|
|
/// Prefetch all shapes (not the animations)
|
|
bool loadShapes();
|
|
|
|
/// Retrieve slide size from XDrawPage
|
|
basegfx::B2ISize getSlideSizeImpl() const;
|
|
|
|
/// Prefetch show, but don't call applyInitialShapeAttributes()
|
|
bool implPrefetchShow();
|
|
|
|
/// Add Polygons to the member maPolygons
|
|
void addPolygons(const PolyPolygonVector& rPolygons);
|
|
|
|
// Types
|
|
// =====
|
|
|
|
enum SlideAnimationState
|
|
{
|
|
CONSTRUCTING_STATE=0,
|
|
INITIAL_STATE=1,
|
|
SHOWING_STATE=2,
|
|
FINAL_STATE=3,
|
|
SlideAnimationState_NUM_ENTRIES=4
|
|
};
|
|
|
|
typedef std::vector< SlideBitmapSharedPtr > VectorOfSlideBitmaps;
|
|
/** Vector of slide bitmaps.
|
|
|
|
Since the bitmap content is sensitive to animation
|
|
effects, we have an inner vector containing a distinct
|
|
bitmap for each of the SlideAnimationStates.
|
|
*/
|
|
typedef ::std::vector< std::pair< UnoViewSharedPtr,
|
|
VectorOfSlideBitmaps > > VectorOfVectorOfSlideBitmaps;
|
|
|
|
|
|
// Member variables
|
|
// ================
|
|
|
|
/// The page model object
|
|
uno::Reference< drawing::XDrawPage > mxDrawPage;
|
|
uno::Reference< drawing::XDrawPagesSupplier > mxDrawPagesSupplier;
|
|
uno::Reference< animations::XAnimationNode > mxRootNode;
|
|
|
|
LayerManagerSharedPtr mpLayerManager;
|
|
std::shared_ptr<ShapeManagerImpl> mpShapeManager;
|
|
std::shared_ptr<SubsettableShapeManager> mpSubsettableShapeManager;
|
|
box2d::utils::Box2DWorldSharedPtr mpBox2DWorld;
|
|
|
|
/// Contains common objects needed throughout the slideshow
|
|
SlideShowContext maContext;
|
|
|
|
/// parent cursor manager
|
|
CursorManager& mrCursorManager;
|
|
|
|
/// Handles the animation and event generation for us
|
|
SlideAnimations maAnimations;
|
|
PolyPolygonVector maPolygons;
|
|
|
|
std::shared_ptr<LOKSlideRenderer> mpLOKRenderer;
|
|
|
|
RGBColor maUserPaintColor;
|
|
double mdUserPaintStrokeWidth;
|
|
UserPaintOverlaySharedPtr mpPaintOverlay;
|
|
|
|
/// Bitmaps with slide content at various states
|
|
mutable VectorOfVectorOfSlideBitmaps maSlideBitmaps;
|
|
|
|
SlideAnimationState meAnimationState;
|
|
|
|
const basegfx::B2ISize maSlideSize;
|
|
|
|
sal_Int16 mnCurrentCursor;
|
|
|
|
/// True, when intrinsic shape animations are allowed
|
|
bool mbIntrinsicAnimationsAllowed;
|
|
|
|
/// True, when user paint overlay is enabled
|
|
bool mbUserPaintOverlayEnabled;
|
|
|
|
/// True, if initial load of all page shapes succeeded
|
|
bool mbShapesLoaded;
|
|
|
|
/// True, if initial load of all animation info succeeded
|
|
bool mbShowLoaded;
|
|
|
|
/** True, if this slide is not static.
|
|
|
|
If this slide has animated content, this variable will
|
|
be true, and false otherwise.
|
|
*/
|
|
bool mbHaveAnimations;
|
|
|
|
/** True, if this slide has a main animation sequence.
|
|
|
|
If this slide has animation content, which in turn has
|
|
a main animation sequence (which must be fully run
|
|
before EventMultiplexer::notifySlideAnimationsEnd() is
|
|
called), this member is true.
|
|
*/
|
|
bool mbMainSequenceFound;
|
|
|
|
/// When true, show() was called. Slide hidden otherwise.
|
|
bool mbActive;
|
|
|
|
/// When true, enablePaintOverlay was called and mbUserPaintOverlay = true
|
|
bool mbPaintOverlayActive;
|
|
|
|
/// When true, final state attributes are already applied to shapes
|
|
bool mbFinalStateApplied;
|
|
};
|
|
|
|
|
|
void slideRenderer( SlideImpl const * pSlide, const UnoViewSharedPtr& rView )
|
|
{
|
|
// fully clear view content to background color
|
|
rView->clearAll();
|
|
|
|
SlideBitmapSharedPtr pBitmap( pSlide->getCurrentSlideBitmap( rView ) );
|
|
::cppcanvas::CanvasSharedPtr pCanvas( rView->getCanvas() );
|
|
|
|
const ::basegfx::B2DHomMatrix aViewTransform( rView->getTransformation() );
|
|
const ::basegfx::B2DPoint aOutPosPixel( aViewTransform * ::basegfx::B2DPoint() );
|
|
|
|
// setup a canvas with device coordinate space, the slide
|
|
// bitmap already has the correct dimension.
|
|
::cppcanvas::CanvasSharedPtr pDevicePixelCanvas( pCanvas->clone() );
|
|
pDevicePixelCanvas->setTransformation( ::basegfx::B2DHomMatrix() );
|
|
|
|
// render at given output position
|
|
pBitmap->move( aOutPosPixel );
|
|
|
|
// clear clip (might have been changed, e.g. from comb
|
|
// transition)
|
|
pBitmap->clip( ::basegfx::B2DPolyPolygon() );
|
|
pBitmap->draw( pDevicePixelCanvas );
|
|
}
|
|
|
|
|
|
SlideImpl::SlideImpl( const uno::Reference< drawing::XDrawPage >& xDrawPage,
|
|
uno::Reference<drawing::XDrawPagesSupplier> xDrawPages,
|
|
uno::Reference< animations::XAnimationNode > xRootNode,
|
|
EventQueue& rEventQueue,
|
|
EventMultiplexer& rEventMultiplexer,
|
|
ScreenUpdater& rScreenUpdater,
|
|
ActivitiesQueue& rActivitiesQueue,
|
|
UserEventQueue& rUserEventQueue,
|
|
CursorManager& rCursorManager,
|
|
MediaFileManager& rMediaFileManager,
|
|
const UnoViewContainer& rViewContainer,
|
|
const uno::Reference< uno::XComponentContext >& xComponentContext,
|
|
const ShapeEventListenerMap& rShapeListenerMap,
|
|
const ShapeCursorMap& rShapeCursorMap,
|
|
PolyPolygonVector&& rPolyPolygonVector,
|
|
RGBColor const& aUserPaintColor,
|
|
double dUserPaintStrokeWidth,
|
|
bool bUserPaintEnabled,
|
|
bool bIntrinsicAnimationsAllowed,
|
|
bool bDisableAnimationZOrder) :
|
|
mxDrawPage( xDrawPage ),
|
|
mxDrawPagesSupplier(std::move( xDrawPages )),
|
|
mxRootNode(std::move( xRootNode )),
|
|
mpLayerManager( std::make_shared<LayerManager>(
|
|
rViewContainer,
|
|
bDisableAnimationZOrder) ),
|
|
mpShapeManager( std::make_shared<ShapeManagerImpl>(
|
|
rEventMultiplexer,
|
|
mpLayerManager,
|
|
rCursorManager,
|
|
rShapeListenerMap,
|
|
rShapeCursorMap,
|
|
xDrawPage)),
|
|
mpSubsettableShapeManager( mpShapeManager ),
|
|
mpBox2DWorld( std::make_shared<box2d::utils::box2DWorld>(
|
|
basegfx::B2DVector(getSlideSizeImpl().getWidth(), getSlideSizeImpl().getHeight()) ) ),
|
|
maContext( mpSubsettableShapeManager,
|
|
rEventQueue,
|
|
rEventMultiplexer,
|
|
rScreenUpdater,
|
|
rActivitiesQueue,
|
|
rUserEventQueue,
|
|
*this,
|
|
rMediaFileManager,
|
|
rViewContainer,
|
|
xComponentContext,
|
|
mpBox2DWorld ),
|
|
mrCursorManager( rCursorManager ),
|
|
maAnimations( maContext,
|
|
basegfx::B2DVector(getSlideSizeImpl().getWidth(), getSlideSizeImpl().getHeight()) ),
|
|
maPolygons(std::move(rPolyPolygonVector)),
|
|
maUserPaintColor(aUserPaintColor),
|
|
mdUserPaintStrokeWidth(dUserPaintStrokeWidth),
|
|
mpPaintOverlay(),
|
|
maSlideBitmaps(),
|
|
meAnimationState( CONSTRUCTING_STATE ),
|
|
maSlideSize(getSlideSizeImpl()),
|
|
mnCurrentCursor( awt::SystemPointer::ARROW ),
|
|
mbIntrinsicAnimationsAllowed( bIntrinsicAnimationsAllowed ),
|
|
mbUserPaintOverlayEnabled(bUserPaintEnabled),
|
|
mbShapesLoaded( false ),
|
|
mbShowLoaded( false ),
|
|
mbHaveAnimations( false ),
|
|
mbMainSequenceFound( false ),
|
|
mbActive( false ),
|
|
mbPaintOverlayActive( false ),
|
|
mbFinalStateApplied( false )
|
|
{
|
|
if (uno::Reference<frame::XModel> xModel{ mxDrawPagesSupplier, uno::UNO_QUERY })
|
|
{
|
|
OUString presentationURL = xModel->getURL();
|
|
auto fileDirectoryEndIdx = presentationURL.lastIndexOf("/");
|
|
if (presentationURL.startsWith("file:///") && fileDirectoryEndIdx != -1)
|
|
maContext.maFallbackDir = OUString::Concat(presentationURL.subView(0, fileDirectoryEndIdx + 1));
|
|
}
|
|
|
|
// clone already existing views for slide bitmaps
|
|
for( const auto& rView : rViewContainer )
|
|
viewAdded( rView );
|
|
|
|
// register screen update (LayerManager needs to signal pending
|
|
// updates)
|
|
maContext.mrScreenUpdater.addViewUpdate(mpShapeManager);
|
|
}
|
|
|
|
void SlideImpl::update_settings( bool bUserPaintEnabled, RGBColor const& aUserPaintColor, double dUserPaintStrokeWidth )
|
|
{
|
|
maUserPaintColor = aUserPaintColor;
|
|
mdUserPaintStrokeWidth = dUserPaintStrokeWidth;
|
|
mbUserPaintOverlayEnabled = bUserPaintEnabled;
|
|
}
|
|
|
|
SlideImpl::~SlideImpl()
|
|
{
|
|
if( mpShapeManager )
|
|
{
|
|
maContext.mrScreenUpdater.removeViewUpdate(mpShapeManager);
|
|
mpShapeManager->dispose();
|
|
|
|
// TODO(Q3): Make sure LayerManager (and thus Shapes) dies
|
|
// first, because SlideShowContext has SubsettableShapeManager
|
|
// as reference member.
|
|
mpLayerManager.reset();
|
|
}
|
|
}
|
|
|
|
void SlideImpl::prefetch()
|
|
{
|
|
if( !mxRootNode.is() )
|
|
return;
|
|
|
|
// Try to prefetch all graphics from the page. This will be done
|
|
// in threads to be more efficient than loading them on-demand one by one.
|
|
std::vector<Graphic*> graphics;
|
|
for (sal_Int32 i = 0; i < mxDrawPage->getCount(); i++)
|
|
{
|
|
css::uno::Reference<css::drawing::XShape> xShape(mxDrawPage->getByIndex(i), css::uno::UNO_QUERY_THROW);
|
|
SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape);
|
|
if (!pObj)
|
|
continue;
|
|
if( SdrGrafObj* grafObj = dynamic_cast<SdrGrafObj*>(pObj))
|
|
if( !grafObj->GetGraphic().isAvailable())
|
|
graphics.push_back( const_cast<Graphic*>(&grafObj->GetGraphic()));
|
|
}
|
|
if(graphics.size() > 1) // threading does not help with loading just one
|
|
GraphicFilter::GetGraphicFilter().MakeGraphicsAvailableThreaded( graphics );
|
|
|
|
applyInitialShapeAttributes(mxRootNode);
|
|
}
|
|
|
|
void SlideImpl::show( bool bSlideBackgroundPainted )
|
|
{
|
|
if( mbActive )
|
|
return; // already active
|
|
|
|
if( !mpShapeManager || !mpLayerManager )
|
|
return; // disposed
|
|
|
|
// set initial shape attributes (e.g. hide shapes that have
|
|
// 'appear' effect set)
|
|
if( !applyInitialShapeAttributes(mxRootNode) )
|
|
return;
|
|
|
|
// activate and take over view - clears view, if necessary
|
|
mbActive = true;
|
|
requestCursor( mnCurrentCursor );
|
|
|
|
// enable shape management & event broadcasting for shapes of this
|
|
// slide. Also enables LayerManager to record updates. Currently,
|
|
// never let LayerManager render initial slide content, use
|
|
// buffered slide bitmaps instead.
|
|
mpShapeManager->activate();
|
|
|
|
|
|
// render slide to screen, if requested
|
|
if( !bSlideBackgroundPainted )
|
|
{
|
|
for( const auto& rContext : maContext.mrViewContainer )
|
|
slideRenderer( this, rContext );
|
|
|
|
maContext.mrScreenUpdater.notifyUpdate();
|
|
}
|
|
|
|
|
|
// fire up animations
|
|
const bool bIsAnimated( isAnimated() );
|
|
if( bIsAnimated )
|
|
maAnimations.start(); // feeds initial events into queue
|
|
|
|
// NOTE: this looks slightly weird, but is indeed correct:
|
|
// as isAnimated() might return false, _although_ there is
|
|
// a main sequence (because the animation nodes don't
|
|
// contain any executable effects), we gotta check both
|
|
// conditions here.
|
|
if( !bIsAnimated || !mbMainSequenceFound )
|
|
{
|
|
// manually trigger a slide animation end event (we don't have
|
|
// animations at all, or we don't have a main animation
|
|
// sequence, but if we had, it'd end now). Note that having
|
|
// animations alone does not matter here, as only main
|
|
// sequence animations prevents showing the next slide on
|
|
// nextEvent().
|
|
maContext.mrEventMultiplexer.notifySlideAnimationsEnd();
|
|
}
|
|
|
|
// enable shape-intrinsic animations (drawing layer animations or
|
|
// GIF animations)
|
|
if( mbIntrinsicAnimationsAllowed )
|
|
mpSubsettableShapeManager->notifyIntrinsicAnimationsEnabled();
|
|
|
|
// enable paint overlay, if maUserPaintColor is valid
|
|
activatePaintOverlay();
|
|
|
|
|
|
// from now on, animations might be showing
|
|
meAnimationState = SHOWING_STATE;
|
|
}
|
|
|
|
void SlideImpl::hide()
|
|
{
|
|
if( !mbActive || !mpShapeManager )
|
|
return; // already hidden/disposed
|
|
|
|
|
|
// from now on, all animations are stopped
|
|
meAnimationState = FINAL_STATE;
|
|
|
|
|
|
// disable user paint overlay under all circumstances,
|
|
// this slide now ceases to be active.
|
|
deactivatePaintOverlay();
|
|
|
|
|
|
// switch off all shape-intrinsic animations.
|
|
mpSubsettableShapeManager->notifyIntrinsicAnimationsDisabled();
|
|
|
|
// force-end all SMIL animations, too
|
|
maAnimations.end();
|
|
|
|
|
|
// disable shape management & event broadcasting for shapes of this
|
|
// slide. Also disables LayerManager.
|
|
mpShapeManager->deactivate();
|
|
|
|
// vanish from view
|
|
resetCursor();
|
|
mbActive = false;
|
|
}
|
|
|
|
basegfx::B2ISize SlideImpl::getSlideSize() const
|
|
{
|
|
return maSlideSize;
|
|
}
|
|
|
|
uno::Reference<drawing::XDrawPage > SlideImpl::getXDrawPage() const
|
|
{
|
|
return mxDrawPage;
|
|
}
|
|
|
|
uno::Reference<animations::XAnimationNode> SlideImpl::getXAnimationNode() const
|
|
{
|
|
return mxRootNode;
|
|
}
|
|
|
|
PolyPolygonVector SlideImpl::getPolygons()
|
|
{
|
|
if(mbPaintOverlayActive)
|
|
maPolygons = mpPaintOverlay->getPolygons();
|
|
return maPolygons;
|
|
}
|
|
|
|
SlideBitmapSharedPtr SlideImpl::getCurrentSlideBitmap( const UnoViewSharedPtr& rView ) const
|
|
{
|
|
// search corresponding entry in maSlideBitmaps (which
|
|
// contains the views as the key)
|
|
VectorOfVectorOfSlideBitmaps::iterator aIter;
|
|
const VectorOfVectorOfSlideBitmaps::iterator aEnd( maSlideBitmaps.end() );
|
|
if( (aIter=std::find_if( maSlideBitmaps.begin(),
|
|
aEnd,
|
|
[&rView]
|
|
( const VectorOfVectorOfSlideBitmaps::value_type& cp )
|
|
{ return rView == cp.first; } ) ) == aEnd )
|
|
{
|
|
// corresponding view not found - maybe view was not
|
|
// added to Slide?
|
|
ENSURE_OR_THROW( false,
|
|
"SlideImpl::getInitialSlideBitmap(): view does not "
|
|
"match any of the added ones" );
|
|
}
|
|
|
|
// ensure that the show is loaded
|
|
if( !mbShowLoaded )
|
|
{
|
|
// only prefetch and init shapes when not done already
|
|
// (otherwise, at least applyInitialShapeAttributes() will be
|
|
// called twice for initial slide rendering). Furthermore,
|
|
// applyInitialShapeAttributes() _always_ performs
|
|
// initializations, which would be highly unwanted during a
|
|
// running show. OTOH, a slide whose mbShowLoaded is false is
|
|
// guaranteed not be running a show.
|
|
|
|
// set initial shape attributes (e.g. hide 'appear' effect
|
|
// shapes)
|
|
if( !const_cast<SlideImpl*>(this)->applyInitialShapeAttributes( mxRootNode ) )
|
|
ENSURE_OR_THROW(false,
|
|
"SlideImpl::getCurrentSlideBitmap(): Cannot "
|
|
"apply initial attributes");
|
|
}
|
|
|
|
SlideBitmapSharedPtr& rBitmap( aIter->second.at( meAnimationState ));
|
|
auto aSize = getSlideSizePixel(basegfx::B2DVector(getSlideSize().getWidth(), getSlideSize().getHeight()), rView);
|
|
const basegfx::B2ISize rSlideSize(aSize.getX(), aSize.getY());
|
|
|
|
// is the bitmap valid (actually existent, and of correct
|
|
// size)?
|
|
if( !rBitmap || rBitmap->getSize() != rSlideSize )
|
|
{
|
|
// no bitmap there yet, or wrong size - create one
|
|
rBitmap = createCurrentSlideBitmap(rView, rSlideSize);
|
|
}
|
|
|
|
return rBitmap;
|
|
}
|
|
|
|
|
|
// private methods
|
|
|
|
|
|
void SlideImpl::viewAdded( const UnoViewSharedPtr& rView )
|
|
{
|
|
maSlideBitmaps.emplace_back( rView,
|
|
VectorOfSlideBitmaps(SlideAnimationState_NUM_ENTRIES) );
|
|
|
|
if( mpLayerManager )
|
|
mpLayerManager->viewAdded( rView );
|
|
}
|
|
|
|
void SlideImpl::viewRemoved( const UnoViewSharedPtr& rView )
|
|
{
|
|
if( mpLayerManager )
|
|
mpLayerManager->viewRemoved( rView );
|
|
|
|
std::erase_if(maSlideBitmaps,
|
|
[&rView]
|
|
( const VectorOfVectorOfSlideBitmaps::value_type& cp )
|
|
{ return rView == cp.first; } );
|
|
}
|
|
|
|
void SlideImpl::viewChanged( const UnoViewSharedPtr& rView )
|
|
{
|
|
// nothing to do for the Slide - getCurrentSlideBitmap() lazily
|
|
// handles bitmap resizes
|
|
if( mbActive && mpLayerManager )
|
|
mpLayerManager->viewChanged(rView);
|
|
}
|
|
|
|
void SlideImpl::viewsChanged()
|
|
{
|
|
// nothing to do for the Slide - getCurrentSlideBitmap() lazily
|
|
// handles bitmap resizes
|
|
if( mbActive && mpLayerManager )
|
|
mpLayerManager->viewsChanged();
|
|
}
|
|
|
|
bool SlideImpl::requestCursor( sal_Int16 nCursorShape )
|
|
{
|
|
mnCurrentCursor = nCursorShape;
|
|
return mrCursorManager.requestCursor(mnCurrentCursor);
|
|
}
|
|
|
|
void SlideImpl::resetCursor()
|
|
{
|
|
mnCurrentCursor = awt::SystemPointer::ARROW;
|
|
mrCursorManager.resetCursor();
|
|
}
|
|
|
|
bool SlideImpl::isAnimated()
|
|
{
|
|
// prefetch, but don't apply initial shape attributes
|
|
if( !implPrefetchShow() )
|
|
return false;
|
|
|
|
return mbHaveAnimations && maAnimations.isAnimated();
|
|
}
|
|
|
|
Size SlideImpl::createLOKSlideRenderer(int nViewWidth, int nViewHeight,
|
|
bool bRenderBackground, bool bRenderMasterPageObjects)
|
|
{
|
|
if (!mpLOKRenderer)
|
|
{
|
|
Size aViewSize(nViewWidth, nViewHeight);
|
|
Size aSlideSize(getSlideSize().getWidth(), getSlideSize().getHeight());
|
|
mpLOKRenderer = std::make_shared<LOKSlideRenderer>(aViewSize, aSlideSize,
|
|
bRenderBackground,
|
|
bRenderMasterPageObjects,
|
|
mxDrawPage, mxDrawPagesSupplier,
|
|
mxRootNode, maContext, mpLayerManager,
|
|
true);
|
|
if (mpLOKRenderer)
|
|
{
|
|
return mpLOKRenderer->getDeviceSize();
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
bool SlideImpl::renderNextLOKSlideLayer(unsigned char* buffer, bool& bIsBitmapLayer, OString& rJsonMsg)
|
|
{
|
|
if (mpLOKRenderer)
|
|
{
|
|
if (!mpLOKRenderer->isSlideRenderingDone())
|
|
{
|
|
mpLOKRenderer->renderNextLayer(buffer);
|
|
bIsBitmapLayer = mpLOKRenderer->isBitmapLayer();
|
|
rJsonMsg = mpLOKRenderer->getJsonMessage();
|
|
}
|
|
|
|
return mpLOKRenderer->isSlideRenderingDone();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
SlideBitmapSharedPtr SlideImpl::createCurrentSlideBitmap( const UnoViewSharedPtr& rView,
|
|
const ::basegfx::B2ISize& rBmpSize ) const
|
|
{
|
|
ENSURE_OR_THROW( rView && rView->getCanvas(),
|
|
"SlideImpl::createCurrentSlideBitmap(): Invalid view" );
|
|
ENSURE_OR_THROW( mpLayerManager,
|
|
"SlideImpl::createCurrentSlideBitmap(): Invalid layer manager" );
|
|
ENSURE_OR_THROW( mbShowLoaded,
|
|
"SlideImpl::createCurrentSlideBitmap(): No show loaded" );
|
|
|
|
// tdf#96083 ensure end state settings are applied to shapes once when bitmap gets re-rendered
|
|
// in that state
|
|
if(!mbFinalStateApplied && FINAL_STATE == meAnimationState && mxRootNode.is())
|
|
{
|
|
const_cast< SlideImpl* >(this)->mbFinalStateApplied = true;
|
|
applyShapeAttributes(mxRootNode, false);
|
|
}
|
|
|
|
::cppcanvas::CanvasSharedPtr pCanvas( rView->getCanvas() );
|
|
|
|
// create a bitmap of appropriate size
|
|
::cppcanvas::BitmapSharedPtr pBitmap(
|
|
::cppcanvas::BaseGfxFactory::createBitmap(
|
|
pCanvas,
|
|
rBmpSize ) );
|
|
|
|
ENSURE_OR_THROW( pBitmap,
|
|
"SlideImpl::createCurrentSlideBitmap(): Cannot create page bitmap" );
|
|
|
|
::cppcanvas::BitmapCanvasSharedPtr pBitmapCanvas( pBitmap->getBitmapCanvas() );
|
|
|
|
ENSURE_OR_THROW( pBitmapCanvas,
|
|
"SlideImpl::createCurrentSlideBitmap(): Cannot create page bitmap canvas" );
|
|
|
|
// apply linear part of destination canvas transformation (linear means in this context:
|
|
// transformation without any translational components)
|
|
::basegfx::B2DHomMatrix aLinearTransform( rView->getTransformation() );
|
|
aLinearTransform.set( 0, 2, 0.0 );
|
|
aLinearTransform.set( 1, 2, 0.0 );
|
|
pBitmapCanvas->setTransformation( aLinearTransform );
|
|
|
|
// output all shapes to bitmap
|
|
initSlideBackground( pBitmapCanvas, rBmpSize );
|
|
mpLayerManager->renderTo( pBitmapCanvas );
|
|
|
|
return std::make_shared<SlideBitmap>( pBitmap );
|
|
}
|
|
|
|
class MainSequenceSearcher
|
|
{
|
|
public:
|
|
MainSequenceSearcher()
|
|
{
|
|
maSearchKey.Name = "node-type";
|
|
maSearchKey.Value <<= presentation::EffectNodeType::MAIN_SEQUENCE;
|
|
}
|
|
|
|
void operator()( const uno::Reference< animations::XAnimationNode >& xChildNode )
|
|
{
|
|
uno::Sequence< beans::NamedValue > aUserData( xChildNode->getUserData() );
|
|
|
|
if( findNamedValue( aUserData, maSearchKey ) )
|
|
{
|
|
maMainSequence = xChildNode;
|
|
}
|
|
}
|
|
|
|
const uno::Reference< animations::XAnimationNode >& getMainSequence() const
|
|
{
|
|
return maMainSequence;
|
|
}
|
|
|
|
private:
|
|
beans::NamedValue maSearchKey;
|
|
uno::Reference< animations::XAnimationNode > maMainSequence;
|
|
};
|
|
|
|
bool SlideImpl::implPrefetchShow()
|
|
{
|
|
if( mbShowLoaded )
|
|
return true;
|
|
|
|
ENSURE_OR_RETURN_FALSE( mxDrawPage.is(),
|
|
"SlideImpl::implPrefetchShow(): Invalid draw page" );
|
|
ENSURE_OR_RETURN_FALSE( mpLayerManager,
|
|
"SlideImpl::implPrefetchShow(): Invalid layer manager" );
|
|
|
|
// fetch desired page content
|
|
// ==========================
|
|
|
|
if( !loadShapes() )
|
|
return false;
|
|
|
|
// New animations framework: import the shape effect info
|
|
// ======================================================
|
|
|
|
try
|
|
{
|
|
if( mxRootNode.is() )
|
|
{
|
|
if( !maAnimations.importAnimations( mxRootNode ) )
|
|
{
|
|
OSL_FAIL( "SlideImpl::implPrefetchShow(): have animation nodes, "
|
|
"but import animations failed." );
|
|
|
|
// could not import animation framework,
|
|
// _although_ some animation nodes are there -
|
|
// this is an error (not finding animations at
|
|
// all is okay - might be a static slide)
|
|
return false;
|
|
}
|
|
|
|
// now check whether we've got a main sequence (if
|
|
// not, we must manually call
|
|
// EventMultiplexer::notifySlideAnimationsEnd()
|
|
// above, as e.g. interactive sequences alone
|
|
// don't block nextEvent() from issuing the next
|
|
// slide)
|
|
MainSequenceSearcher aSearcher;
|
|
if( for_each_childNode( mxRootNode, aSearcher ) )
|
|
mbMainSequenceFound = aSearcher.getMainSequence().is();
|
|
|
|
// import successfully done
|
|
mbHaveAnimations = true;
|
|
}
|
|
}
|
|
catch( uno::RuntimeException& )
|
|
{
|
|
throw;
|
|
}
|
|
catch( uno::Exception& )
|
|
{
|
|
TOOLS_WARN_EXCEPTION( "slideshow", "" );
|
|
// TODO(E2): Error handling. For now, bail out
|
|
}
|
|
|
|
mbShowLoaded = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void SlideImpl::enablePaintOverlay()
|
|
{
|
|
if( !mbUserPaintOverlayEnabled || !mbPaintOverlayActive )
|
|
{
|
|
mbUserPaintOverlayEnabled = true;
|
|
activatePaintOverlay();
|
|
}
|
|
}
|
|
|
|
void SlideImpl::activatePaintOverlay()
|
|
{
|
|
if( mbUserPaintOverlayEnabled || !maPolygons.empty() )
|
|
{
|
|
mpPaintOverlay = UserPaintOverlay::create( maUserPaintColor,
|
|
mdUserPaintStrokeWidth,
|
|
maContext,
|
|
std::vector(maPolygons),
|
|
mbUserPaintOverlayEnabled );
|
|
mbPaintOverlayActive = true;
|
|
}
|
|
}
|
|
|
|
void SlideImpl::drawPolygons() const
|
|
{
|
|
if( mpPaintOverlay )
|
|
mpPaintOverlay->drawPolygons();
|
|
}
|
|
|
|
void SlideImpl::addPolygons(const PolyPolygonVector& rPolygons)
|
|
{
|
|
maPolygons.insert(maPolygons.end(), rPolygons.begin(), rPolygons.end());
|
|
}
|
|
|
|
bool SlideImpl::isPaintOverlayActive() const
|
|
{
|
|
return mbPaintOverlayActive;
|
|
}
|
|
|
|
void SlideImpl::deactivatePaintOverlay()
|
|
{
|
|
if(mbPaintOverlayActive)
|
|
maPolygons = mpPaintOverlay->getPolygons();
|
|
|
|
mpPaintOverlay.reset();
|
|
mbPaintOverlayActive = false;
|
|
}
|
|
|
|
void SlideImpl::applyShapeAttributes(
|
|
const css::uno::Reference< css::animations::XAnimationNode >& xRootAnimationNode,
|
|
bool bInitial) const
|
|
{
|
|
const uno::Sequence< animations::TargetProperties > aProps(
|
|
TargetPropertiesCreator::createTargetProperties( xRootAnimationNode, bInitial ) );
|
|
|
|
// apply extracted values to our shapes
|
|
for( const auto& rProp : aProps )
|
|
{
|
|
sal_Int16 nParaIndex( -1 );
|
|
uno::Reference< drawing::XShape > xShape( rProp.Target,
|
|
uno::UNO_QUERY );
|
|
|
|
if( !xShape.is() )
|
|
{
|
|
// not a shape target. Maybe a ParagraphTarget?
|
|
presentation::ParagraphTarget aParaTarget;
|
|
|
|
if( rProp.Target >>= aParaTarget )
|
|
{
|
|
// yep, ParagraphTarget found - extract shape
|
|
// and index
|
|
xShape = aParaTarget.Shape;
|
|
nParaIndex = aParaTarget.Paragraph;
|
|
}
|
|
}
|
|
|
|
if( xShape.is() )
|
|
{
|
|
ShapeSharedPtr pShape( mpLayerManager->lookupShape( xShape ) );
|
|
|
|
if( !pShape )
|
|
{
|
|
OSL_FAIL( "SlideImpl::applyInitialShapeAttributes(): no shape found for given target" );
|
|
continue;
|
|
}
|
|
|
|
AttributableShapeSharedPtr pAttrShape(
|
|
::std::dynamic_pointer_cast< AttributableShape >( pShape ) );
|
|
|
|
if( !pAttrShape )
|
|
{
|
|
OSL_FAIL( "SlideImpl::applyInitialShapeAttributes(): shape found does not "
|
|
"implement AttributableShape interface" );
|
|
continue;
|
|
}
|
|
|
|
if( nParaIndex != -1 )
|
|
{
|
|
// our target is a paragraph subset, thus look
|
|
// this up first.
|
|
const DocTreeNodeSupplier& rNodeSupplier( pAttrShape->getTreeNodeSupplier() );
|
|
|
|
if( rNodeSupplier.getNumberOfTreeNodes(
|
|
DocTreeNode::NodeType::LogicalParagraph ) <= nParaIndex )
|
|
{
|
|
OSL_FAIL( "SlideImpl::applyInitialShapeAttributes(): shape found does not "
|
|
"provide a subset for requested paragraph index" );
|
|
continue;
|
|
}
|
|
|
|
pAttrShape = pAttrShape->getSubset(
|
|
rNodeSupplier.getTreeNode(
|
|
nParaIndex,
|
|
DocTreeNode::NodeType::LogicalParagraph ) );
|
|
|
|
if( !pAttrShape )
|
|
{
|
|
OSL_FAIL( "SlideImpl::applyInitialShapeAttributes(): shape found does not "
|
|
"provide a subset for requested paragraph index" );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const uno::Sequence< beans::NamedValue >& rShapeProps( rProp.Properties );
|
|
for( const auto& rShapeProp : rShapeProps )
|
|
{
|
|
bool bVisible=false;
|
|
if( rShapeProp.Name.equalsIgnoreAsciiCase("visibility") &&
|
|
extractValue( bVisible,
|
|
rShapeProp.Value,
|
|
pShape,
|
|
basegfx::B2DVector(getSlideSize().getWidth(), getSlideSize().getHeight()) ))
|
|
{
|
|
pAttrShape->setVisibility( bVisible );
|
|
}
|
|
else
|
|
{
|
|
OSL_FAIL( "SlideImpl::applyInitialShapeAttributes(): Unexpected "
|
|
"(and unimplemented) property encountered" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SlideImpl::applyInitialShapeAttributes(
|
|
const uno::Reference< animations::XAnimationNode >& xRootAnimationNode )
|
|
{
|
|
if( !implPrefetchShow() )
|
|
return false;
|
|
|
|
if( !xRootAnimationNode.is() )
|
|
{
|
|
meAnimationState = INITIAL_STATE;
|
|
|
|
return true; // no animations - no attributes to apply -
|
|
// succeeded
|
|
}
|
|
|
|
applyShapeAttributes(xRootAnimationNode, true);
|
|
|
|
meAnimationState = INITIAL_STATE;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SlideImpl::loadShapes()
|
|
{
|
|
if( mbShapesLoaded )
|
|
return true;
|
|
|
|
ENSURE_OR_RETURN_FALSE( mxDrawPage.is(),
|
|
"SlideImpl::loadShapes(): Invalid draw page" );
|
|
ENSURE_OR_RETURN_FALSE( mpLayerManager,
|
|
"SlideImpl::loadShapes(): Invalid layer manager" );
|
|
|
|
// fetch desired page content
|
|
// ==========================
|
|
|
|
// also take master page content
|
|
uno::Reference< drawing::XDrawPage > xMasterPage;
|
|
uno::Reference< drawing::XShapes > xMasterPageShapes;
|
|
sal_Int32 nCurrCount(0);
|
|
|
|
uno::Reference< drawing::XMasterPageTarget > xMasterPageTarget( mxDrawPage,
|
|
uno::UNO_QUERY );
|
|
if( xMasterPageTarget.is() )
|
|
{
|
|
xMasterPage = xMasterPageTarget->getMasterPage();
|
|
xMasterPageShapes = xMasterPage;
|
|
|
|
if( xMasterPage.is() && xMasterPageShapes.is() )
|
|
{
|
|
// TODO(P2): maybe cache master pages here (or treat the
|
|
// masterpage as a single metafile. At least currently,
|
|
// masterpages do not contain animation effects)
|
|
try
|
|
{
|
|
// load the masterpage shapes
|
|
|
|
ShapeImporter aMPShapesFunctor( xMasterPage,
|
|
mxDrawPage,
|
|
mxDrawPagesSupplier,
|
|
maContext,
|
|
0, /* shape num starts at 0 */
|
|
true );
|
|
|
|
mpLayerManager->addShape(
|
|
aMPShapesFunctor.importBackgroundShape() );
|
|
|
|
while( !aMPShapesFunctor.isImportDone() )
|
|
{
|
|
ShapeSharedPtr const pShape(
|
|
aMPShapesFunctor.importShape() );
|
|
if( pShape )
|
|
{
|
|
pShape->setIsForeground(false);
|
|
mpLayerManager->addShape( pShape );
|
|
}
|
|
}
|
|
addPolygons(aMPShapesFunctor.getPolygons());
|
|
|
|
nCurrCount = static_cast<sal_Int32>(aMPShapesFunctor.getImportedShapesCount());
|
|
}
|
|
catch( uno::RuntimeException& )
|
|
{
|
|
throw;
|
|
}
|
|
catch( ShapeLoadFailedException& )
|
|
{
|
|
// TODO(E2): Error handling. For now, bail out
|
|
TOOLS_WARN_EXCEPTION( "slideshow", "SlideImpl::loadShapes(): caught ShapeLoadFailedException" );
|
|
return false;
|
|
|
|
}
|
|
catch( uno::Exception& )
|
|
{
|
|
TOOLS_WARN_EXCEPTION( "slideshow", "" );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
// load the normal page shapes
|
|
|
|
|
|
ShapeImporter aShapesFunctor( mxDrawPage,
|
|
mxDrawPage,
|
|
mxDrawPagesSupplier,
|
|
maContext,
|
|
nCurrCount,
|
|
false );
|
|
|
|
while( !aShapesFunctor.isImportDone() )
|
|
{
|
|
ShapeSharedPtr const pShape(
|
|
aShapesFunctor.importShape() );
|
|
if( pShape )
|
|
mpLayerManager->addShape( pShape );
|
|
}
|
|
addPolygons(aShapesFunctor.getPolygons());
|
|
}
|
|
catch( uno::RuntimeException& )
|
|
{
|
|
throw;
|
|
}
|
|
catch( ShapeLoadFailedException& )
|
|
{
|
|
// TODO(E2): Error handling. For now, bail out
|
|
TOOLS_WARN_EXCEPTION( "slideshow", "SlideImpl::loadShapes(): caught ShapeLoadFailedException" );
|
|
return false;
|
|
}
|
|
catch( uno::Exception& )
|
|
{
|
|
TOOLS_WARN_EXCEPTION( "slideshow", "" );
|
|
return false;
|
|
}
|
|
|
|
mbShapesLoaded = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
basegfx::B2ISize SlideImpl::getSlideSizeImpl() const
|
|
{
|
|
uno::Reference< beans::XPropertySet > xPropSet(
|
|
mxDrawPage, uno::UNO_QUERY_THROW );
|
|
|
|
sal_Int32 nDocWidth = 0;
|
|
sal_Int32 nDocHeight = 0;
|
|
xPropSet->getPropertyValue(u"Width"_ustr) >>= nDocWidth;
|
|
xPropSet->getPropertyValue(u"Height"_ustr) >>= nDocHeight;
|
|
|
|
return basegfx::B2ISize( nDocWidth, nDocHeight );
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
SlideSharedPtr createSlide( const uno::Reference< drawing::XDrawPage >& xDrawPage,
|
|
const uno::Reference<drawing::XDrawPagesSupplier>& xDrawPages,
|
|
const uno::Reference< animations::XAnimationNode >& xRootNode,
|
|
EventQueue& rEventQueue,
|
|
EventMultiplexer& rEventMultiplexer,
|
|
ScreenUpdater& rScreenUpdater,
|
|
ActivitiesQueue& rActivitiesQueue,
|
|
UserEventQueue& rUserEventQueue,
|
|
CursorManager& rCursorManager,
|
|
MediaFileManager& rMediaFileManager,
|
|
const UnoViewContainer& rViewContainer,
|
|
const uno::Reference< uno::XComponentContext >& xComponentContext,
|
|
const ShapeEventListenerMap& rShapeListenerMap,
|
|
const ShapeCursorMap& rShapeCursorMap,
|
|
PolyPolygonVector&& rPolyPolygonVector,
|
|
RGBColor const& rUserPaintColor,
|
|
double dUserPaintStrokeWidth,
|
|
bool bUserPaintEnabled,
|
|
bool bIntrinsicAnimationsAllowed,
|
|
bool bDisableAnimationZOrder )
|
|
{
|
|
auto pRet = std::make_shared<SlideImpl>( xDrawPage, xDrawPages, xRootNode, rEventQueue,
|
|
rEventMultiplexer, rScreenUpdater,
|
|
rActivitiesQueue, rUserEventQueue,
|
|
rCursorManager, rMediaFileManager, rViewContainer,
|
|
xComponentContext, rShapeListenerMap,
|
|
rShapeCursorMap, std::move(rPolyPolygonVector), rUserPaintColor,
|
|
dUserPaintStrokeWidth, bUserPaintEnabled,
|
|
bIntrinsicAnimationsAllowed,
|
|
bDisableAnimationZOrder );
|
|
|
|
rEventMultiplexer.addViewHandler( pRet );
|
|
|
|
return pRet;
|
|
}
|
|
|
|
} // namespace slideshow
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|