summaryrefslogtreecommitdiffstats
path: root/sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx')
-rw-r--r--sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx492
1 files changed, 492 insertions, 0 deletions
diff --git a/sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx b/sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx
new file mode 100644
index 0000000000..4bd3808992
--- /dev/null
+++ b/sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx
@@ -0,0 +1,492 @@
+/* -*- 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 "SlsLayeredDevice.hxx"
+#include <Window.hxx>
+
+#include <utility>
+#include <vcl/virdev.hxx>
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <tools/gen.hxx>
+#include <tools/fract.hxx>
+
+#include <functional>
+
+namespace sd::slidesorter::view {
+
+namespace {
+const sal_Int32 gnMaximumLayerCount = 8;
+
+class LayerInvalidator : public ILayerInvalidator
+{
+public:
+ LayerInvalidator (
+ std::shared_ptr<LayeredDevice> pLayeredDevice,
+ sd::Window *pTargetWindow,
+ const int nLayer)
+ : mpLayeredDevice(std::move(pLayeredDevice)),
+ mpTargetWindow(pTargetWindow),
+ mnLayer(nLayer)
+ {
+ }
+
+ virtual void Invalidate (const ::tools::Rectangle& rInvalidationBox) override
+ {
+ mpLayeredDevice->Invalidate(rInvalidationBox, mnLayer);
+ mpTargetWindow->Invalidate(rInvalidationBox);
+ }
+
+private:
+ const std::shared_ptr<LayeredDevice> mpLayeredDevice;
+ VclPtr<sd::Window> mpTargetWindow;
+ const int mnLayer;
+};
+
+void DeviceCopy (
+ vcl::RenderContext& rTargetDevice,
+ vcl::RenderContext const & rSourceDevice,
+ const ::tools::Rectangle& rBox)
+{
+ rTargetDevice.DrawOutDev(
+ rBox.TopLeft(),
+ rBox.GetSize(),
+ rBox.TopLeft(),
+ rBox.GetSize(),
+ rSourceDevice);
+}
+
+void ForAllRectangles (const vcl::Region& rRegion, const std::function<void (const ::tools::Rectangle&)>& aFunction)
+{
+ OSL_ASSERT(aFunction);
+ RectangleVector aRectangles;
+ rRegion.GetRegionRectangles(aRectangles);
+
+ if(aRectangles.empty())
+ {
+ aFunction(::tools::Rectangle());
+ }
+ else
+ {
+ for(const auto& rRect : aRectangles)
+ {
+ aFunction(rRect);
+ }
+
+ //Region aMutableRegionCopy (rRegion);
+ //RegionHandle aHandle(aMutableRegionCopy.BeginEnumRects());
+ //Rectangle aBox;
+ //while (aMutableRegionCopy.GetEnumRects(aHandle, aBox))
+ // aFunction(aBox);
+ //aMutableRegionCopy.EndEnumRects(aHandle);
+ }
+}
+
+class Layer
+{
+public:
+ Layer();
+ Layer(const Layer&) = delete;
+ Layer& operator=(const Layer&) = delete;
+
+ void Initialize (sd::Window *pTargetWindow);
+ void InvalidateRectangle (const ::tools::Rectangle& rInvalidationBox);
+ void InvalidateRegion (const vcl::Region& rInvalidationRegion);
+ void Validate (const MapMode& rMapMode);
+ void Repaint (
+ OutputDevice& rTargetDevice,
+ const ::tools::Rectangle& rRepaintRectangle);
+ void Resize (const Size& rSize);
+ void AddPainter (const SharedILayerPainter& rpPainter);
+ void RemovePainter (const SharedILayerPainter& rpPainter);
+ bool HasPainter() const;
+ void Dispose();
+
+private:
+ ScopedVclPtr<VirtualDevice> mpLayerDevice;
+ ::std::vector<SharedILayerPainter> maPainters;
+ vcl::Region maInvalidationRegion;
+
+ void ValidateRectangle (const ::tools::Rectangle& rBox);
+};
+typedef std::shared_ptr<Layer> SharedLayer;
+
+} // end of anonymous namespace
+
+class LayeredDevice::LayerContainer
+{
+public:
+ LayerContainer() {}
+
+ bool empty() const { return mvLayers.empty(); }
+
+ size_t size() const { return mvLayers.size(); }
+
+ const SharedLayer& back() const { return mvLayers.back(); }
+
+ ::std::vector<SharedLayer>::const_iterator begin() const { return mvLayers.begin(); }
+ ::std::vector<SharedLayer>::const_iterator end() const { return mvLayers.end(); }
+
+ void clear() { mvLayers.clear(); }
+
+ void pop_back() { mvLayers.pop_back(); }
+
+ void resize(size_t n) { mvLayers.resize(n); }
+
+ SharedLayer& operator[](size_t i) { return mvLayers[i]; }
+
+private:
+ ::std::vector<SharedLayer> mvLayers;
+};
+
+//===== LayeredDevice =========================================================
+
+LayeredDevice::LayeredDevice (const VclPtr<sd::Window>& pTargetWindow)
+ : mpTargetWindow(pTargetWindow),
+ mpLayers(new LayerContainer()),
+ mpBackBuffer(VclPtr<VirtualDevice>::Create(*mpTargetWindow->GetOutDev())),
+ maSavedMapMode(pTargetWindow->GetMapMode())
+{
+ mpBackBuffer->SetOutputSizePixel(mpTargetWindow->GetSizePixel());
+}
+
+LayeredDevice::~LayeredDevice()
+{
+}
+
+void LayeredDevice::Invalidate (
+ const ::tools::Rectangle& rInvalidationArea,
+ const sal_Int32 nLayer)
+{
+ if (nLayer<0 || o3tl::make_unsigned(nLayer)>=mpLayers->size())
+ {
+ OSL_ASSERT(nLayer>=0 && o3tl::make_unsigned(nLayer)<mpLayers->size());
+ return;
+ }
+
+ (*mpLayers)[nLayer]->InvalidateRectangle(rInvalidationArea);
+}
+
+void LayeredDevice::InvalidateAllLayers (const ::tools::Rectangle& rInvalidationArea)
+{
+ for (size_t nLayer=0; nLayer<mpLayers->size(); ++nLayer)
+ (*mpLayers)[nLayer]->InvalidateRectangle(rInvalidationArea);
+}
+
+void LayeredDevice::InvalidateAllLayers (const vcl::Region& rInvalidationRegion)
+{
+ for (size_t nLayer=0; nLayer<mpLayers->size(); ++nLayer)
+ (*mpLayers)[nLayer]->InvalidateRegion(rInvalidationRegion);
+}
+
+void LayeredDevice::RegisterPainter (
+ const SharedILayerPainter& rpPainter,
+ const sal_Int32 nLayer)
+{
+ OSL_ASSERT(mpLayers);
+ if ( ! rpPainter)
+ {
+ OSL_ASSERT(rpPainter);
+ return;
+ }
+ if (nLayer<0 || nLayer>=gnMaximumLayerCount)
+ {
+ OSL_ASSERT(nLayer>=0 && nLayer<gnMaximumLayerCount);
+ return;
+ }
+
+ // Provide the layers.
+ if (o3tl::make_unsigned(nLayer) >= mpLayers->size())
+ {
+ const sal_Int32 nOldLayerCount (mpLayers->size());
+ mpLayers->resize(nLayer+1);
+
+ for (size_t nIndex=nOldLayerCount; nIndex<mpLayers->size(); ++nIndex)
+ (*mpLayers)[nIndex] = std::make_shared<Layer>();
+ }
+
+ (*mpLayers)[nLayer]->AddPainter(rpPainter);
+ if (nLayer == 0)
+ (*mpLayers)[nLayer]->Initialize(mpTargetWindow);
+
+ rpPainter->SetLayerInvalidator(
+ std::make_shared<LayerInvalidator>(shared_from_this(),mpTargetWindow,nLayer));
+}
+
+void LayeredDevice::RemovePainter (
+ const SharedILayerPainter& rpPainter,
+ const sal_Int32 nLayer)
+{
+ if ( ! rpPainter)
+ {
+ OSL_ASSERT(rpPainter);
+ return;
+ }
+ if (nLayer<0 || o3tl::make_unsigned(nLayer)>=mpLayers->size())
+ {
+ OSL_ASSERT(nLayer>=0 && o3tl::make_unsigned(nLayer)<mpLayers->size());
+ return;
+ }
+
+ rpPainter->SetLayerInvalidator(SharedILayerInvalidator());
+
+ (*mpLayers)[nLayer]->RemovePainter(rpPainter);
+
+ // Remove top most layers that do not contain any painters.
+ while ( ! mpLayers->empty() && ! mpLayers->back()->HasPainter())
+ mpLayers->pop_back();
+}
+
+void LayeredDevice::Repaint (const vcl::Region& rRepaintRegion)
+{
+ // Validate the contents of all layers (that have their own devices.)
+ for (auto const& it : *mpLayers)
+ {
+ it->Validate(mpTargetWindow->GetMapMode());
+ }
+
+ ForAllRectangles(rRepaintRegion,
+ [this] (::tools::Rectangle const& r) { this->RepaintRectangle(r); });
+}
+
+void LayeredDevice::RepaintRectangle (const ::tools::Rectangle& rRepaintRectangle)
+{
+ if (mpLayers->empty())
+ return;
+ else if (mpLayers->size() == 1)
+ {
+ // Just copy the main layer into the target device.
+ (*mpLayers)[0]->Repaint(*mpTargetWindow->GetOutDev(), rRepaintRectangle);
+ }
+ else
+ {
+ // Paint all layers first into the back buffer (to avoid flickering
+ // due to synchronous paints) and then copy that into the target
+ // device.
+ mpBackBuffer->SetMapMode(mpTargetWindow->GetMapMode());
+ for (auto const& it : *mpLayers)
+ {
+ it->Repaint(*mpBackBuffer, rRepaintRectangle);
+ }
+ DeviceCopy(*mpTargetWindow->GetOutDev(), *mpBackBuffer, rRepaintRectangle);
+ }
+}
+
+void LayeredDevice::Resize()
+{
+ const Size aSize (mpTargetWindow->GetSizePixel());
+ mpBackBuffer->SetOutputSizePixel(aSize);
+ for (auto const& it : *mpLayers)
+ {
+ it->Resize(aSize);
+ }
+}
+
+void LayeredDevice::Dispose()
+{
+ for (auto const& it : *mpLayers)
+ {
+ it->Dispose();
+ }
+ mpLayers->clear();
+}
+
+bool LayeredDevice::HandleMapModeChange()
+{
+ const MapMode& rMapMode (mpTargetWindow->GetMapMode());
+ if (maSavedMapMode == rMapMode)
+ return false;
+
+ const ::tools::Rectangle aLogicWindowBox (
+ mpTargetWindow->PixelToLogic(::tools::Rectangle(Point(0,0), mpTargetWindow->GetSizePixel())));
+ if (maSavedMapMode.GetScaleX() != rMapMode.GetScaleX()
+ || maSavedMapMode.GetScaleY() != rMapMode.GetScaleY()
+ || maSavedMapMode.GetMapUnit() != rMapMode.GetMapUnit())
+ {
+ // When the scale has changed then we have to paint everything.
+ InvalidateAllLayers(aLogicWindowBox);
+ }
+ else if (maSavedMapMode.GetOrigin() != rMapMode.GetOrigin())
+ {
+ // Window has been scrolled. Adapt contents of backbuffers and
+ // layer devices.
+ const Point aDelta (rMapMode.GetOrigin() - maSavedMapMode.GetOrigin());
+ mpBackBuffer->CopyArea(
+ aLogicWindowBox.TopLeft(),
+ mpTargetWindow->PixelToLogic(Point(0,0), maSavedMapMode),
+ aLogicWindowBox.GetSize());
+
+ // Invalidate the area(s) that have been exposed.
+ const ::tools::Rectangle aWindowBox (Point(0,0), mpTargetWindow->GetSizePixel());
+ if (aDelta.Y() < 0)
+ InvalidateAllLayers(mpTargetWindow->PixelToLogic(::tools::Rectangle(
+ aWindowBox.Left(),
+ aWindowBox.Bottom()+aDelta.Y(),
+ aWindowBox.Right(),
+ aWindowBox.Bottom())));
+ else if (aDelta.Y() > 0)
+ InvalidateAllLayers(mpTargetWindow->PixelToLogic(::tools::Rectangle(
+ aWindowBox.Left(),
+ aWindowBox.Top(),
+ aWindowBox.Right(),
+ aWindowBox.Top()+aDelta.Y())));
+ if (aDelta.X() < 0)
+ InvalidateAllLayers(mpTargetWindow->PixelToLogic(::tools::Rectangle(
+ aWindowBox.Right()+aDelta.X(),
+ aWindowBox.Top(),
+ aWindowBox.Right(),
+ aWindowBox.Bottom())));
+ else if (aDelta.X() > 0)
+ InvalidateAllLayers(mpTargetWindow->PixelToLogic(::tools::Rectangle(
+ aWindowBox.Left(),
+ aWindowBox.Top(),
+ aWindowBox.Left()+aDelta.X(),
+ aWindowBox.Bottom())));
+ }
+ else
+ {
+ // Can this happen? Lets trigger a warning when it does.
+ OSL_ASSERT(false);
+ }
+
+ maSavedMapMode = rMapMode;
+
+ return true;
+}
+
+//===== Layer =================================================================
+
+Layer::Layer()
+{
+}
+
+void Layer::Initialize (sd::Window *pTargetWindow)
+{
+#if 0
+ (void)pTargetWindow;
+#else
+ if ( ! mpLayerDevice)
+ {
+ mpLayerDevice.disposeAndReset(VclPtr<VirtualDevice>::Create(*pTargetWindow->GetOutDev()));
+ mpLayerDevice->SetOutputSizePixel(pTargetWindow->GetSizePixel());
+ }
+#endif
+}
+
+void Layer::InvalidateRectangle (const ::tools::Rectangle& rInvalidationBox)
+{
+ maInvalidationRegion.Union(rInvalidationBox);
+}
+
+void Layer::InvalidateRegion (const vcl::Region& rInvalidationRegion)
+{
+ maInvalidationRegion.Union(rInvalidationRegion);
+}
+
+void Layer::Validate (const MapMode& rMapMode)
+{
+ if (mpLayerDevice && ! maInvalidationRegion.IsEmpty())
+ {
+ vcl::Region aRegion (maInvalidationRegion);
+ maInvalidationRegion.SetEmpty();
+
+ mpLayerDevice->SetMapMode(rMapMode);
+ ForAllRectangles(
+ aRegion,
+ [this] (::tools::Rectangle const& r) { return this->ValidateRectangle(r); });
+ }
+}
+
+void Layer::ValidateRectangle (const ::tools::Rectangle& rBox)
+{
+ if ( ! mpLayerDevice)
+ return;
+ const vcl::Region aSavedClipRegion (mpLayerDevice->GetClipRegion());
+ mpLayerDevice->IntersectClipRegion(rBox);
+
+ for (const auto& rxPainter : maPainters)
+ {
+ rxPainter->Paint(*mpLayerDevice, rBox);
+ }
+
+ mpLayerDevice->SetClipRegion(aSavedClipRegion);
+}
+
+void Layer::Repaint (
+ OutputDevice& rTargetDevice,
+ const ::tools::Rectangle& rRepaintRectangle)
+{
+ if (mpLayerDevice)
+ {
+ DeviceCopy(rTargetDevice, *mpLayerDevice, rRepaintRectangle);
+ }
+ else
+ {
+ for (auto const& it : maPainters)
+ {
+ it->Paint(rTargetDevice, rRepaintRectangle);
+ }
+ }
+}
+
+void Layer::Resize (const Size& rSize)
+{
+ if (mpLayerDevice)
+ {
+ mpLayerDevice->SetOutputSizePixel(rSize);
+ maInvalidationRegion = ::tools::Rectangle(Point(0,0), rSize);
+ }
+}
+
+void Layer::AddPainter (const SharedILayerPainter& rpPainter)
+{
+ OSL_ASSERT(::std::find(maPainters.begin(), maPainters.end(), rpPainter) == maPainters.end());
+
+ maPainters.push_back(rpPainter);
+}
+
+void Layer::RemovePainter (const SharedILayerPainter& rpPainter)
+{
+ const ::std::vector<SharedILayerPainter>::iterator iPainter (
+ ::std::find(maPainters.begin(), maPainters.end(), rpPainter));
+ if (iPainter != maPainters.end())
+ {
+ maPainters.erase(iPainter);
+ }
+ else
+ {
+ SAL_WARN("sd", "LayeredDevice::RemovePainter called for painter that is not registered");
+ }
+}
+
+bool Layer::HasPainter() const
+{
+ return !maPainters.empty();
+}
+
+void Layer::Dispose()
+{
+ maPainters.clear();
+}
+
+} // end of namespace ::sd::slidesorter::view
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */