1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "SlsLayeredDevice.hxx"
24 #include <vcl/virdev.hxx>
25 #include <sal/log.hxx>
26 #include <o3tl/safeint.hxx>
27 #include <osl/diagnose.h>
29 #include <tools/gen.hxx>
30 #include <tools/fract.hxx>
34 namespace sd::slidesorter::view
{
37 const sal_Int32 gnMaximumLayerCount
= 8;
39 class LayerInvalidator
: public ILayerInvalidator
43 std::shared_ptr
<LayeredDevice
> pLayeredDevice
,
44 sd::Window
*pTargetWindow
,
46 : mpLayeredDevice(std::move(pLayeredDevice
)),
47 mpTargetWindow(pTargetWindow
),
52 virtual void Invalidate (const ::tools::Rectangle
& rInvalidationBox
) override
54 mpLayeredDevice
->Invalidate(rInvalidationBox
, mnLayer
);
55 mpTargetWindow
->Invalidate(rInvalidationBox
);
59 const std::shared_ptr
<LayeredDevice
> mpLayeredDevice
;
60 VclPtr
<sd::Window
> mpTargetWindow
;
65 vcl::RenderContext
& rTargetDevice
,
66 vcl::RenderContext
const & rSourceDevice
,
67 const ::tools::Rectangle
& rBox
)
69 rTargetDevice
.DrawOutDev(
77 void ForAllRectangles (const vcl::Region
& rRegion
, const std::function
<void (const ::tools::Rectangle
&)>& aFunction
)
79 OSL_ASSERT(aFunction
);
80 RectangleVector aRectangles
;
81 rRegion
.GetRegionRectangles(aRectangles
);
83 if(aRectangles
.empty())
85 aFunction(::tools::Rectangle());
89 for(const auto& rRect
: aRectangles
)
94 //Region aMutableRegionCopy (rRegion);
95 //RegionHandle aHandle(aMutableRegionCopy.BeginEnumRects());
97 //while (aMutableRegionCopy.GetEnumRects(aHandle, aBox))
99 //aMutableRegionCopy.EndEnumRects(aHandle);
107 Layer(const Layer
&) = delete;
108 Layer
& operator=(const Layer
&) = delete;
110 void Initialize (sd::Window
*pTargetWindow
);
111 void InvalidateRectangle (const ::tools::Rectangle
& rInvalidationBox
);
112 void InvalidateRegion (const vcl::Region
& rInvalidationRegion
);
113 void Validate (const MapMode
& rMapMode
);
115 OutputDevice
& rTargetDevice
,
116 const ::tools::Rectangle
& rRepaintRectangle
);
117 void Resize (const Size
& rSize
);
118 void AddPainter (const SharedILayerPainter
& rpPainter
);
119 void RemovePainter (const SharedILayerPainter
& rpPainter
);
120 bool HasPainter() const;
124 ScopedVclPtr
<VirtualDevice
> mpLayerDevice
;
125 ::std::vector
<SharedILayerPainter
> maPainters
;
126 vcl::Region maInvalidationRegion
;
128 void ValidateRectangle (const ::tools::Rectangle
& rBox
);
130 typedef std::shared_ptr
<Layer
> SharedLayer
;
132 } // end of anonymous namespace
134 class LayeredDevice::LayerContainer
139 bool empty() const { return mvLayers
.empty(); }
141 size_t size() const { return mvLayers
.size(); }
143 const SharedLayer
& back() const { return mvLayers
.back(); }
145 ::std::vector
<SharedLayer
>::const_iterator
begin() const { return mvLayers
.begin(); }
146 ::std::vector
<SharedLayer
>::const_iterator
end() const { return mvLayers
.end(); }
148 void clear() { mvLayers
.clear(); }
150 void pop_back() { mvLayers
.pop_back(); }
152 void resize(size_t n
) { mvLayers
.resize(n
); }
154 SharedLayer
& operator[](size_t i
) { return mvLayers
[i
]; }
157 ::std::vector
<SharedLayer
> mvLayers
;
160 //===== LayeredDevice =========================================================
162 LayeredDevice::LayeredDevice (const VclPtr
<sd::Window
>& pTargetWindow
)
163 : mpTargetWindow(pTargetWindow
),
164 mpLayers(new LayerContainer()),
165 mpBackBuffer(VclPtr
<VirtualDevice
>::Create(*mpTargetWindow
->GetOutDev())),
166 maSavedMapMode(pTargetWindow
->GetMapMode())
168 mpBackBuffer
->SetOutputSizePixel(mpTargetWindow
->GetSizePixel());
171 LayeredDevice::~LayeredDevice()
175 void LayeredDevice::Invalidate (
176 const ::tools::Rectangle
& rInvalidationArea
,
177 const sal_Int32 nLayer
)
179 if (nLayer
<0 || o3tl::make_unsigned(nLayer
)>=mpLayers
->size())
181 OSL_ASSERT(nLayer
>=0 && o3tl::make_unsigned(nLayer
)<mpLayers
->size());
185 (*mpLayers
)[nLayer
]->InvalidateRectangle(rInvalidationArea
);
188 void LayeredDevice::InvalidateAllLayers (const ::tools::Rectangle
& rInvalidationArea
)
190 for (size_t nLayer
=0; nLayer
<mpLayers
->size(); ++nLayer
)
191 (*mpLayers
)[nLayer
]->InvalidateRectangle(rInvalidationArea
);
194 void LayeredDevice::InvalidateAllLayers (const vcl::Region
& rInvalidationRegion
)
196 for (size_t nLayer
=0; nLayer
<mpLayers
->size(); ++nLayer
)
197 (*mpLayers
)[nLayer
]->InvalidateRegion(rInvalidationRegion
);
200 void LayeredDevice::RegisterPainter (
201 const SharedILayerPainter
& rpPainter
,
202 const sal_Int32 nLayer
)
204 OSL_ASSERT(mpLayers
);
207 OSL_ASSERT(rpPainter
);
210 if (nLayer
<0 || nLayer
>=gnMaximumLayerCount
)
212 OSL_ASSERT(nLayer
>=0 && nLayer
<gnMaximumLayerCount
);
216 // Provide the layers.
217 if (o3tl::make_unsigned(nLayer
) >= mpLayers
->size())
219 const sal_Int32
nOldLayerCount (mpLayers
->size());
220 mpLayers
->resize(nLayer
+1);
222 for (size_t nIndex
=nOldLayerCount
; nIndex
<mpLayers
->size(); ++nIndex
)
223 (*mpLayers
)[nIndex
] = std::make_shared
<Layer
>();
226 (*mpLayers
)[nLayer
]->AddPainter(rpPainter
);
228 (*mpLayers
)[nLayer
]->Initialize(mpTargetWindow
);
230 rpPainter
->SetLayerInvalidator(
231 std::make_shared
<LayerInvalidator
>(shared_from_this(),mpTargetWindow
,nLayer
));
234 void LayeredDevice::RemovePainter (
235 const SharedILayerPainter
& rpPainter
,
236 const sal_Int32 nLayer
)
240 OSL_ASSERT(rpPainter
);
243 if (nLayer
<0 || o3tl::make_unsigned(nLayer
)>=mpLayers
->size())
245 OSL_ASSERT(nLayer
>=0 && o3tl::make_unsigned(nLayer
)<mpLayers
->size());
249 rpPainter
->SetLayerInvalidator(SharedILayerInvalidator());
251 (*mpLayers
)[nLayer
]->RemovePainter(rpPainter
);
253 // Remove top most layers that do not contain any painters.
254 while ( ! mpLayers
->empty() && ! mpLayers
->back()->HasPainter())
255 mpLayers
->pop_back();
258 void LayeredDevice::Repaint (const vcl::Region
& rRepaintRegion
)
260 // Validate the contents of all layers (that have their own devices.)
261 for (auto const& it
: *mpLayers
)
263 it
->Validate(mpTargetWindow
->GetMapMode());
266 ForAllRectangles(rRepaintRegion
,
267 [this] (::tools::Rectangle
const& r
) { this->RepaintRectangle(r
); });
270 void LayeredDevice::RepaintRectangle (const ::tools::Rectangle
& rRepaintRectangle
)
272 if (mpLayers
->empty())
274 else if (mpLayers
->size() == 1)
276 // Just copy the main layer into the target device.
277 (*mpLayers
)[0]->Repaint(*mpTargetWindow
->GetOutDev(), rRepaintRectangle
);
281 // Paint all layers first into the back buffer (to avoid flickering
282 // due to synchronous paints) and then copy that into the target
284 mpBackBuffer
->SetMapMode(mpTargetWindow
->GetMapMode());
285 for (auto const& it
: *mpLayers
)
287 it
->Repaint(*mpBackBuffer
, rRepaintRectangle
);
289 DeviceCopy(*mpTargetWindow
->GetOutDev(), *mpBackBuffer
, rRepaintRectangle
);
293 void LayeredDevice::Resize()
295 const Size
aSize (mpTargetWindow
->GetSizePixel());
296 mpBackBuffer
->SetOutputSizePixel(aSize
);
297 for (auto const& it
: *mpLayers
)
303 void LayeredDevice::Dispose()
305 for (auto const& it
: *mpLayers
)
312 bool LayeredDevice::HandleMapModeChange()
314 const MapMode
& rMapMode (mpTargetWindow
->GetMapMode());
315 if (maSavedMapMode
== rMapMode
)
318 const ::tools::Rectangle
aLogicWindowBox (
319 mpTargetWindow
->PixelToLogic(::tools::Rectangle(Point(0,0), mpTargetWindow
->GetSizePixel())));
320 if (maSavedMapMode
.GetScaleX() != rMapMode
.GetScaleX()
321 || maSavedMapMode
.GetScaleY() != rMapMode
.GetScaleY()
322 || maSavedMapMode
.GetMapUnit() != rMapMode
.GetMapUnit())
324 // When the scale has changed then we have to paint everything.
325 InvalidateAllLayers(aLogicWindowBox
);
327 else if (maSavedMapMode
.GetOrigin() != rMapMode
.GetOrigin())
329 // Window has been scrolled. Adapt contents of backbuffers and
331 const Point
aDelta (rMapMode
.GetOrigin() - maSavedMapMode
.GetOrigin());
332 mpBackBuffer
->CopyArea(
333 aLogicWindowBox
.TopLeft(),
334 mpTargetWindow
->PixelToLogic(Point(0,0), maSavedMapMode
),
335 aLogicWindowBox
.GetSize());
337 // Invalidate the area(s) that have been exposed.
338 const ::tools::Rectangle
aWindowBox (Point(0,0), mpTargetWindow
->GetSizePixel());
340 InvalidateAllLayers(mpTargetWindow
->PixelToLogic(::tools::Rectangle(
342 aWindowBox
.Bottom()+aDelta
.Y(),
344 aWindowBox
.Bottom())));
345 else if (aDelta
.Y() > 0)
346 InvalidateAllLayers(mpTargetWindow
->PixelToLogic(::tools::Rectangle(
350 aWindowBox
.Top()+aDelta
.Y())));
352 InvalidateAllLayers(mpTargetWindow
->PixelToLogic(::tools::Rectangle(
353 aWindowBox
.Right()+aDelta
.X(),
356 aWindowBox
.Bottom())));
357 else if (aDelta
.X() > 0)
358 InvalidateAllLayers(mpTargetWindow
->PixelToLogic(::tools::Rectangle(
361 aWindowBox
.Left()+aDelta
.X(),
362 aWindowBox
.Bottom())));
366 // Can this happen? Lets trigger a warning when it does.
370 maSavedMapMode
= rMapMode
;
375 //===== Layer =================================================================
381 void Layer::Initialize (sd::Window
*pTargetWindow
)
386 if ( ! mpLayerDevice
)
388 mpLayerDevice
.disposeAndReset(VclPtr
<VirtualDevice
>::Create(*pTargetWindow
->GetOutDev()));
389 mpLayerDevice
->SetOutputSizePixel(pTargetWindow
->GetSizePixel());
394 void Layer::InvalidateRectangle (const ::tools::Rectangle
& rInvalidationBox
)
396 maInvalidationRegion
.Union(rInvalidationBox
);
399 void Layer::InvalidateRegion (const vcl::Region
& rInvalidationRegion
)
401 maInvalidationRegion
.Union(rInvalidationRegion
);
404 void Layer::Validate (const MapMode
& rMapMode
)
406 if (mpLayerDevice
&& ! maInvalidationRegion
.IsEmpty())
408 vcl::Region
aRegion (maInvalidationRegion
);
409 maInvalidationRegion
.SetEmpty();
411 mpLayerDevice
->SetMapMode(rMapMode
);
414 [this] (::tools::Rectangle
const& r
) { return this->ValidateRectangle(r
); });
418 void Layer::ValidateRectangle (const ::tools::Rectangle
& rBox
)
420 if ( ! mpLayerDevice
)
422 const vcl::Region
aSavedClipRegion (mpLayerDevice
->GetClipRegion());
423 mpLayerDevice
->IntersectClipRegion(rBox
);
425 for (const auto& rxPainter
: maPainters
)
427 rxPainter
->Paint(*mpLayerDevice
, rBox
);
430 mpLayerDevice
->SetClipRegion(aSavedClipRegion
);
433 void Layer::Repaint (
434 OutputDevice
& rTargetDevice
,
435 const ::tools::Rectangle
& rRepaintRectangle
)
439 DeviceCopy(rTargetDevice
, *mpLayerDevice
, rRepaintRectangle
);
443 for (auto const& it
: maPainters
)
445 it
->Paint(rTargetDevice
, rRepaintRectangle
);
450 void Layer::Resize (const Size
& rSize
)
454 mpLayerDevice
->SetOutputSizePixel(rSize
);
455 maInvalidationRegion
= ::tools::Rectangle(Point(0,0), rSize
);
459 void Layer::AddPainter (const SharedILayerPainter
& rpPainter
)
461 OSL_ASSERT(::std::find(maPainters
.begin(), maPainters
.end(), rpPainter
) == maPainters
.end());
463 maPainters
.push_back(rpPainter
);
466 void Layer::RemovePainter (const SharedILayerPainter
& rpPainter
)
468 const ::std::vector
<SharedILayerPainter
>::iterator
iPainter (
469 ::std::find(maPainters
.begin(), maPainters
.end(), rpPainter
));
470 if (iPainter
!= maPainters
.end())
472 maPainters
.erase(iPainter
);
476 SAL_WARN("sd", "LayeredDevice::RemovePainter called for painter that is not registered");
480 bool Layer::HasPainter() const
482 return !maPainters
.empty();
485 void Layer::Dispose()
490 } // end of namespace ::sd::slidesorter::view
492 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */