sd/Library_sd.mk | 1 sd/qa/unit/tiledrendering/data/SlideRenderingTest.odp |binary sd/qa/unit/tiledrendering/tiledrendering.cxx | 53 ++++++ sd/source/ui/inc/SlideshowLayerRenderer.hxx | 36 ++++ sd/source/ui/inc/unomodel.hxx | 3 sd/source/ui/tools/SlideshowLayerRenderer.cxx | 146 ++++++++++++++++++ sd/source/ui/unoidl/unomodel.cxx | 124 ++++++++++----- 7 files changed, 327 insertions(+), 36 deletions(-)
New commits: commit 51d99782ddbddd5f5492c27a7150839a80b93dc2 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Thu Aug 8 15:44:47 2024 +0200 Commit: Szymon Kłos <szymon.k...@collabora.com> CommitDate: Wed Sep 4 14:33:37 2024 +0200 render slide layers with VCL / drawinglayer primitives Instead of using slideshow module to render the slide layers, use the VCL / drawinglayer instead, which makes it possible to render transparency correctly and fast. Change-Id: Idfc9fcc8bc0e2bc139df93d1805f7a19fb1bce1f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171640 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Szymon Kłos <szymon.k...@collabora.com> diff --git a/sd/Library_sd.mk b/sd/Library_sd.mk index 7f526c33a061..099a8b49ce65 100644 --- a/sd/Library_sd.mk +++ b/sd/Library_sd.mk @@ -429,6 +429,7 @@ $(eval $(call gb_Library_add_exception_objects,sd,\ sd/source/ui/tools/IdleDetection \ sd/source/ui/tools/PreviewRenderer \ sd/source/ui/tools/SdGlobalResourceContainer \ + sd/source/ui/tools/SlideshowLayerRenderer \ sd/source/ui/tools/SlotStateListener \ sd/source/ui/tools/TimerBasedTaskExecution \ sd/source/ui/uitest/uiobject \ diff --git a/sd/qa/unit/tiledrendering/data/SlideRenderingTest.odp b/sd/qa/unit/tiledrendering/data/SlideRenderingTest.odp new file mode 100644 index 000000000000..4c7d4f101e32 Binary files /dev/null and b/sd/qa/unit/tiledrendering/data/SlideRenderingTest.odp differ diff --git a/sd/qa/unit/tiledrendering/tiledrendering.cxx b/sd/qa/unit/tiledrendering/tiledrendering.cxx index 3f91ad463f49..bb57cddf4bbc 100644 --- a/sd/qa/unit/tiledrendering/tiledrendering.cxx +++ b/sd/qa/unit/tiledrendering/tiledrendering.cxx @@ -40,6 +40,7 @@ #include <DrawDocShell.hxx> #include <ViewShellBase.hxx> #include <ViewShell.hxx> +#include <SlideshowLayerRenderer.hxx> #include <sdpage.hxx> #include <unomodel.hxx> #include <drawdoc.hxx> @@ -55,6 +56,8 @@ #include <vcl/virdev.hxx> #include <o3tl/string_view.hxx> #include <sfx2/sidebar/Sidebar.hxx> +#include <vcl/BitmapTools.hxx> +#include <vcl/filter/PngImageWriter.hxx> #include <chrono> #include <cstdlib> @@ -3165,6 +3168,56 @@ CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testPresentationInfo) } } +CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testSlideshowLayeredRendering) +{ + // Check rendering of slideshow layers (as in the document): + // - background layer + // - master slide layer + // - main slide layer + // The doucment has nothing set for the background, so it should be application color = white + // On the master slide there is a (blue) rectangle on the right side - top-left should be transparent + // On the main slide there is a (green) rectanlge on the top-left size - right side should be transparent + // enable layer output to PNG files + const bool bOutputPNG = false; + SdXImpressDocument* pXImpressDocument = createDoc("SlideRenderingTest.odp"); + pXImpressDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); + sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell(); + CPPUNIT_ASSERT(pViewShell); + SdPage* pPage = pViewShell->GetActualPage(); + CPPUNIT_ASSERT(pPage); + sal_Int32 nViewWidth = 2000; + sal_Int32 nViewHeight = 2000; + CPPUNIT_ASSERT(pXImpressDocument->createSlideRenderer(0, nViewWidth, nViewHeight, true, true)); + CPPUNIT_ASSERT_EQUAL(2000, nViewWidth); + CPPUNIT_ASSERT_EQUAL(1125, nViewHeight); // adjusted to the slide aspect ratio + std::vector<sal_uInt8> pBuffer(nViewWidth * nViewHeight * 4); + bool bIsBitmapLayer = false; + OUString rJsonMsg; + + CPPUNIT_ASSERT(!pXImpressDocument->renderNextSlideLayer(pBuffer.data(), bIsBitmapLayer, rJsonMsg)); + CPPUNIT_ASSERT(bIsBitmapLayer); + // TODO - check JSON content + // printf ("1 %s ", rJsonMsg.toUtf8().getStr()); + { + BitmapEx aBitmapEx = vcl::bitmap::CreateFromData(pBuffer.data(), nViewWidth, nViewHeight, nViewWidth * 4, /*nBitsPerPixel*/32, true, true); + if (bOutputPNG) + { + SvFileStream aStream("/home/quikee/XXX_01.png", StreamMode::WRITE | StreamMode::TRUNC); + vcl::PngImageWriter aPNGWriter(aStream); + aPNGWriter.write(aBitmapEx); + } + + // bottom-left corner + CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0x00000000), aBitmapEx.GetPixelColor(20, nViewHeight - 20)); + + // bottom-right corner + CPPUNIT_ASSERT_EQUAL(Color(0xff, 0xd5, 0x46), aBitmapEx.GetPixelColor(nViewWidth - 20, nViewHeight - 20)); + } + // should return true - no more content + CPPUNIT_ASSERT(pXImpressDocument->renderNextSlideLayer(pBuffer.data(), bIsBitmapLayer, rJsonMsg)); + pXImpressDocument->postSlideshowCleanup(); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SlideshowLayerRenderer.hxx b/sd/source/ui/inc/SlideshowLayerRenderer.hxx new file mode 100644 index 000000000000..28525839d579 --- /dev/null +++ b/sd/source/ui/inc/SlideshowLayerRenderer.hxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <sddllapi.h> +#include <tools/gen.hxx> +#include <rtl/string.hxx> + +class SdrPage; +class BitmapEx; +class Size; + +namespace sd +{ +class SD_DLLPUBLIC SlideshowLayerRenderer +{ + SdrPage* mpPage; + Size maSlideSize; + bool bRenderDone = false; + +public: + SlideshowLayerRenderer(SdrPage* pPage); + Size calculateAndSetSizePixel(Size const& rDesiredSizePixel); + bool render(unsigned char* pBuffer, OString& rJsonMsg); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unomodel.hxx b/sd/source/ui/inc/unomodel.hxx index 155d50e9ecce..f65eb0865a76 100644 --- a/sd/source/ui/inc/unomodel.hxx +++ b/sd/source/ui/inc/unomodel.hxx @@ -56,6 +56,7 @@ class SvxItemPropertySet; namespace sd { class DrawDocShell; class DrawViewShell; +class SlideshowLayerRenderer; } extern OUString getPageApiName( SdPage const * pPage ); @@ -87,6 +88,8 @@ private: SdDrawDocument* mpDoc; bool mbDisposed; + std::unique_ptr<sd::SlideshowLayerRenderer> mpSlideshowLayerRenderer; + css::uno::Reference<css::uno::XInterface> create( OUString const & aServiceSpecifier, OUString const & referer); diff --git a/sd/source/ui/tools/SlideshowLayerRenderer.cxx b/sd/source/ui/tools/SlideshowLayerRenderer.cxx new file mode 100644 index 000000000000..e618dd2fac33 --- /dev/null +++ b/sd/source/ui/tools/SlideshowLayerRenderer.cxx @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <SlideshowLayerRenderer.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svdview.hxx> +#include <svx/unoapi.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/svdoutl.hxx> +#include <vcl/virdev.hxx> +#include <tools/helpers.hxx> +#include <tools/json_writer.hxx> + +namespace sd +{ +namespace +{ +class SelectObjectRedirector : public sdr::contact::ViewObjectContactRedirector +{ +public: + virtual void createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override + { + SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject(); + SdrPage* pPage = pObject ? pObject->getSdrPageFromSdrObject() : nullptr; + + if (pObject == nullptr || pPage == nullptr) + { + // Not a SdrObject or a object not connected to a page (object with no page) + + //sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence( + // rOriginal, rDisplayInfo, rVisitor); + return; + } + + const bool bDoCreateGeometry( + pObject->getSdrPageFromSdrObject()->checkVisibility(rOriginal, rDisplayInfo, true)); + + if (!bDoCreateGeometry + && (pObject->GetObjInventor() != SdrInventor::Default + || pObject->GetObjIdentifier() != SdrObjKind::Page)) + { + return; + } + + if (pObject->IsEmptyPresObj()) + return; + + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence( + rOriginal, rDisplayInfo, rVisitor); + } +}; +} + +SlideshowLayerRenderer::SlideshowLayerRenderer(SdrPage* pPage) + : mpPage(pPage) +{ +} + +Size SlideshowLayerRenderer::calculateAndSetSizePixel(Size const& rDesiredSizePixel) +{ + if (!mpPage) + return Size(); + + double fRatio = double(mpPage->GetHeight()) / mpPage->GetWidth(); + Size aSize(rDesiredSizePixel.Width(), ::tools::Long(rDesiredSizePixel.Width() * fRatio)); + maSlideSize = aSize; + + return maSlideSize; +} + +bool SlideshowLayerRenderer::render(unsigned char* pBuffer, OString& rJsonMsg) +{ + if (bRenderDone) + return false; + + if (!mpPage) + return false; + + SdrModel& rModel = mpPage->getSdrModelFromSdrPage(); + + ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA); + pDevice->SetBackground(Wallpaper(COL_TRANSPARENT)); + + pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(maSlideSize, Fraction(2.0), Point(), + pBuffer); + + Point aPoint; + Size aPageSize(mpPage->GetSize()); + + MapMode aMapMode(MapUnit::Map100thMM); + const Fraction aFracX(maSlideSize.Width(), pDevice->LogicToPixel(aPageSize, aMapMode).Width()); + aMapMode.SetScaleX(aFracX); + + const Fraction aFracY(maSlideSize.Height(), + pDevice->LogicToPixel(aPageSize, aMapMode).Height()); + aMapMode.SetScaleY(aFracY); + + pDevice->SetMapMode(aMapMode); + + SdrView aView(rModel, pDevice); + + aView.SetPageVisible(false); + aView.SetPageShadowVisible(false); + aView.SetPageBorderVisible(false); + aView.SetBordVisible(false); + aView.SetGridVisible(false); + aView.SetHlplVisible(false); + aView.SetGlueVisible(false); + + aView.ShowSdrPage(mpPage); + + vcl::Region aRegion(::tools::Rectangle(aPoint, aPageSize)); + SelectObjectRedirector aRedirector; + aView.CompleteRedraw(pDevice, aRegion, &aRedirector); + + ::tools::JsonWriter aJsonWriter; + aJsonWriter.put("group", "DrawPage"); + aJsonWriter.put("slideHash", GetInterfaceHash(GetXDrawPageForSdrPage(mpPage))); + aJsonWriter.put("index", 0); + aJsonWriter.put("type", "bitmap"); + { + ::tools::ScopedJsonWriterNode aContentNode = aJsonWriter.startNode("content"); + aJsonWriter.put("type", "%IMAGETYPE%"); + aJsonWriter.put("checksum", "%IMAGECHECKSUM%"); + } + rJsonMsg = aJsonWriter.finishAndGetAsOString(); + + bRenderDone = true; + + return true; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unomodel.cxx b/sd/source/ui/unoidl/unomodel.cxx index 99d5d9718fec..d388e6f80ee6 100644 --- a/sd/source/ui/unoidl/unomodel.cxx +++ b/sd/source/ui/unoidl/unomodel.cxx @@ -146,6 +146,7 @@ #include <ViewShell.hxx> #include <Window.hxx> #include <optsitem.hxx> +#include <SlideshowLayerRenderer.hxx> #include <vcl/pdfextoutdevdata.hxx> #include <vcl/pdf/PDFNote.hxx> @@ -4402,63 +4403,114 @@ OString SdXImpressDocument::getPresentationInfo() const return aJsonWriter.finishAndGetAsOString(); } +namespace +{ +// use VCL slideshow renderer or not - leave the old one in for now, so it is possible to compare output +constexpr const bool bVCLSlideShowRenderer = true; +} + bool SdXImpressDocument::createSlideRenderer( sal_Int32 nSlideNumber, sal_Int32& nViewWidth, sal_Int32& nViewHeight, bool bRenderBackground, bool bRenderMasterPage) { - DrawViewShell* pViewSh = GetViewShell(); - if (!pViewSh) - return false; - - uno::Reference<presentation::XSlideShow> xSlideShow = pViewSh->getXSlideShowInstance(); - if (!xSlideShow.is()) - return false; - - bool bSuccess = false; - try + if (bVCLSlideShowRenderer) { - uno::Reference<drawing::XDrawPagesSupplier> xDrawPages(mpDoc->getUnoModel(), uno::UNO_QUERY_THROW); - uno::Reference<container::XIndexAccess> xSlides(xDrawPages->getDrawPages(), uno::UNO_QUERY_THROW); - uno::Reference<drawing::XDrawPage> xSlide(xSlides->getByIndex(nSlideNumber), uno::UNO_QUERY_THROW); - uno::Reference<animations::XAnimationNodeSupplier> xAnimNodeSupplier(xSlide, uno::UNO_QUERY_THROW); - uno::Reference<animations::XAnimationNode> xAnimNode = xAnimNodeSupplier->getAnimationNode(); + SdPage* pPage = mpDoc->GetSdPage(sal_uInt16(nSlideNumber), PageKind::Standard); + if (!pPage) + return false; - bSuccess = xSlideShow->createLOKSlideRenderer(nViewWidth, nViewHeight, - bRenderMasterPage, bRenderBackground, - xSlide, xDrawPages, xAnimNode); + mpSlideshowLayerRenderer.reset(new SlideshowLayerRenderer(pPage)); + Size aDesiredSize(nViewWidth, nViewHeight); + Size aCalculatedSize = mpSlideshowLayerRenderer->calculateAndSetSizePixel(aDesiredSize); + nViewWidth = aCalculatedSize.Width(); + nViewHeight = aCalculatedSize.Height(); + return true; } - catch (uno::Exception&) + else { - TOOLS_WARN_EXCEPTION( "sd", "SdXImpressDocument::createLOKSlideRenderer: failed" ); + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return false; + + uno::Reference<presentation::XSlideShow> xSlideShow = pViewSh->getXSlideShowInstance(); + if (!xSlideShow.is()) + return false; + + bool bSuccess = false; + try + { + uno::Reference<drawing::XDrawPagesSupplier> xDrawPages(mpDoc->getUnoModel(), uno::UNO_QUERY_THROW); + uno::Reference<container::XIndexAccess> xSlides(xDrawPages->getDrawPages(), uno::UNO_QUERY_THROW); + uno::Reference<drawing::XDrawPage> xSlide(xSlides->getByIndex(nSlideNumber), uno::UNO_QUERY_THROW); + uno::Reference<animations::XAnimationNodeSupplier> xAnimNodeSupplier(xSlide, uno::UNO_QUERY_THROW); + uno::Reference<animations::XAnimationNode> xAnimNode = xAnimNodeSupplier->getAnimationNode(); + + bSuccess = xSlideShow->createLOKSlideRenderer(nViewWidth, nViewHeight, + bRenderMasterPage, bRenderBackground, + xSlide, xDrawPages, xAnimNode); + } + catch (uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "SdXImpressDocument::createLOKSlideRenderer: failed" ); + } + return bSuccess; } - return bSuccess; } void SdXImpressDocument::postSlideshowCleanup() { - DrawViewShell* pViewSh = GetViewShell(); - if (!pViewSh) - return; + if (bVCLSlideShowRenderer) + { + mpSlideshowLayerRenderer.reset(); + } + else + { + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; - pViewSh->destroyXSlideShowInstance(); + pViewSh->destroyXSlideShowInstance(); +} } bool SdXImpressDocument::renderNextSlideLayer(unsigned char* pBuffer, bool& bIsBitmapLayer, OUString& rJsonMsg) { - DrawViewShell* pViewSh = GetViewShell(); - if (!pViewSh) - return true; + if (bVCLSlideShowRenderer) + { + bool bDone = true; - uno::Reference<presentation::XSlideShow> xSlideShow = pViewSh->getXSlideShowInstance(); - if (!xSlideShow.is()) - return true; + if (!mpSlideshowLayerRenderer) + return bDone; - auto nBufferPointer = sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(pBuffer)); - sal_Bool bBitmapLayer = false; - bool bDone = xSlideShow->renderNextLOKSlideLayer(nBufferPointer, bBitmapLayer, rJsonMsg); - bIsBitmapLayer = bBitmapLayer; + OString sMsg; + bool bOK = mpSlideshowLayerRenderer->render(pBuffer, sMsg); - return bDone; + if (bOK) + { + rJsonMsg = OUString::fromUtf8(sMsg); + bIsBitmapLayer = true; + bDone = false; + } + + return bDone; + } + else + { + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return true; + + uno::Reference<presentation::XSlideShow> xSlideShow = pViewSh->getXSlideShowInstance(); + if (!xSlideShow.is()) + return true; + + auto nBufferPointer = sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(pBuffer)); + sal_Bool bBitmapLayer = false; + bool bDone = xSlideShow->renderNextLOKSlideLayer(nBufferPointer, bBitmapLayer, rJsonMsg); + bIsBitmapLayer = bBitmapLayer; + + return bDone; + } } SdrModel& SdXImpressDocument::getSdrModelFromUnoModel() const