LibreOffice Module sd (master) 1
SlsLayeredDevice.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include "SlsLayeredDevice.hxx"
21#include <Window.hxx>
22
23#include <utility>
24#include <vcl/virdev.hxx>
25#include <sal/log.hxx>
26#include <o3tl/safeint.hxx>
27#include <osl/diagnose.h>
28
29#include <tools/gen.hxx>
30#include <tools/fract.hxx>
31
32#include <functional>
33
34namespace sd::slidesorter::view {
35
36namespace {
37const sal_Int32 gnMaximumLayerCount = 8;
38
39class LayerInvalidator : public ILayerInvalidator
40{
41public:
42 LayerInvalidator (
43 std::shared_ptr<LayeredDevice> pLayeredDevice,
44 sd::Window *pTargetWindow,
45 const int nLayer)
46 : mpLayeredDevice(std::move(pLayeredDevice)),
47 mpTargetWindow(pTargetWindow),
48 mnLayer(nLayer)
49 {
50 }
51
52 virtual void Invalidate (const ::tools::Rectangle& rInvalidationBox) override
53 {
54 mpLayeredDevice->Invalidate(rInvalidationBox, mnLayer);
55 mpTargetWindow->Invalidate(rInvalidationBox);
56 }
57
58private:
59 const std::shared_ptr<LayeredDevice> mpLayeredDevice;
61 const int mnLayer;
62};
63
64void DeviceCopy (
65 vcl::RenderContext& rTargetDevice,
66 vcl::RenderContext const & rSourceDevice,
67 const ::tools::Rectangle& rBox)
68{
69 rTargetDevice.DrawOutDev(
70 rBox.TopLeft(),
71 rBox.GetSize(),
72 rBox.TopLeft(),
73 rBox.GetSize(),
74 rSourceDevice);
75}
76
77void ForAllRectangles (const vcl::Region& rRegion, const std::function<void (const ::tools::Rectangle&)>& aFunction)
78{
79 OSL_ASSERT(aFunction);
80 RectangleVector aRectangles;
81 rRegion.GetRegionRectangles(aRectangles);
82
83 if(aRectangles.empty())
84 {
85 aFunction(::tools::Rectangle());
86 }
87 else
88 {
89 for(const auto& rRect : aRectangles)
90 {
91 aFunction(rRect);
92 }
93
94 //Region aMutableRegionCopy (rRegion);
95 //RegionHandle aHandle(aMutableRegionCopy.BeginEnumRects());
96 //Rectangle aBox;
97 //while (aMutableRegionCopy.GetEnumRects(aHandle, aBox))
98 // aFunction(aBox);
99 //aMutableRegionCopy.EndEnumRects(aHandle);
100 }
101}
102
103class Layer
104{
105public:
106 Layer();
107 Layer(const Layer&) = delete;
108 Layer& operator=(const Layer&) = delete;
109
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);
114 void Repaint (
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;
121 void Dispose();
122
123private:
125 ::std::vector<SharedILayerPainter> maPainters;
127
128 void ValidateRectangle (const ::tools::Rectangle& rBox);
129};
130typedef std::shared_ptr<Layer> SharedLayer;
131
132} // end of anonymous namespace
133
135{
136public:
138
139 bool empty() const { return mvLayers.empty(); }
140
141 size_t size() const { return mvLayers.size(); }
142
143 const SharedLayer& back() const { return mvLayers.back(); }
144
145 ::std::vector<SharedLayer>::const_iterator begin() const { return mvLayers.begin(); }
146 ::std::vector<SharedLayer>::const_iterator end() const { return mvLayers.end(); }
147
148 void clear() { mvLayers.clear(); }
149
150 void pop_back() { mvLayers.pop_back(); }
151
152 void resize(size_t n) { mvLayers.resize(n); }
153
154 SharedLayer& operator[](size_t i) { return mvLayers[i]; }
155
156private:
157 ::std::vector<SharedLayer> mvLayers;
158};
159
160//===== LayeredDevice =========================================================
161
163 : mpTargetWindow(pTargetWindow),
164 mpLayers(new LayerContainer()),
165 mpBackBuffer(VclPtr<VirtualDevice>::Create(*mpTargetWindow->GetOutDev())),
166 maSavedMapMode(pTargetWindow->GetMapMode())
167{
168 mpBackBuffer->SetOutputSizePixel(mpTargetWindow->GetSizePixel());
169}
170
172{
173}
174
176 const ::tools::Rectangle& rInvalidationArea,
177 const sal_Int32 nLayer)
178{
179 if (nLayer<0 || o3tl::make_unsigned(nLayer)>=mpLayers->size())
180 {
181 OSL_ASSERT(nLayer>=0 && o3tl::make_unsigned(nLayer)<mpLayers->size());
182 return;
183 }
184
185 (*mpLayers)[nLayer]->InvalidateRectangle(rInvalidationArea);
186}
187
188void LayeredDevice::InvalidateAllLayers (const ::tools::Rectangle& rInvalidationArea)
189{
190 for (size_t nLayer=0; nLayer<mpLayers->size(); ++nLayer)
191 (*mpLayers)[nLayer]->InvalidateRectangle(rInvalidationArea);
192}
193
194void LayeredDevice::InvalidateAllLayers (const vcl::Region& rInvalidationRegion)
195{
196 for (size_t nLayer=0; nLayer<mpLayers->size(); ++nLayer)
197 (*mpLayers)[nLayer]->InvalidateRegion(rInvalidationRegion);
198}
199
201 const SharedILayerPainter& rpPainter,
202 const sal_Int32 nLayer)
203{
204 OSL_ASSERT(mpLayers);
205 if ( ! rpPainter)
206 {
207 OSL_ASSERT(rpPainter);
208 return;
209 }
210 if (nLayer<0 || nLayer>=gnMaximumLayerCount)
211 {
212 OSL_ASSERT(nLayer>=0 && nLayer<gnMaximumLayerCount);
213 return;
214 }
215
216 // Provide the layers.
217 if (o3tl::make_unsigned(nLayer) >= mpLayers->size())
218 {
219 const sal_Int32 nOldLayerCount (mpLayers->size());
220 mpLayers->resize(nLayer+1);
221
222 for (size_t nIndex=nOldLayerCount; nIndex<mpLayers->size(); ++nIndex)
223 (*mpLayers)[nIndex] = std::make_shared<Layer>();
224 }
225
226 (*mpLayers)[nLayer]->AddPainter(rpPainter);
227 if (nLayer == 0)
228 (*mpLayers)[nLayer]->Initialize(mpTargetWindow);
229
230 rpPainter->SetLayerInvalidator(
231 std::make_shared<LayerInvalidator>(shared_from_this(),mpTargetWindow,nLayer));
232}
233
235 const SharedILayerPainter& rpPainter,
236 const sal_Int32 nLayer)
237{
238 if ( ! rpPainter)
239 {
240 OSL_ASSERT(rpPainter);
241 return;
242 }
243 if (nLayer<0 || o3tl::make_unsigned(nLayer)>=mpLayers->size())
244 {
245 OSL_ASSERT(nLayer>=0 && o3tl::make_unsigned(nLayer)<mpLayers->size());
246 return;
247 }
248
249 rpPainter->SetLayerInvalidator(SharedILayerInvalidator());
250
251 (*mpLayers)[nLayer]->RemovePainter(rpPainter);
252
253 // Remove top most layers that do not contain any painters.
254 while ( ! mpLayers->empty() && ! mpLayers->back()->HasPainter())
255 mpLayers->pop_back();
256}
257
258void LayeredDevice::Repaint (const vcl::Region& rRepaintRegion)
259{
260 // Validate the contents of all layers (that have their own devices.)
261 for (auto const& it : *mpLayers)
262 {
263 it->Validate(mpTargetWindow->GetMapMode());
264 }
265
266 ForAllRectangles(rRepaintRegion,
267 [this] (::tools::Rectangle const& r) { this->RepaintRectangle(r); });
268}
269
270void LayeredDevice::RepaintRectangle (const ::tools::Rectangle& rRepaintRectangle)
271{
272 if (mpLayers->empty())
273 return;
274 else if (mpLayers->size() == 1)
275 {
276 // Just copy the main layer into the target device.
277 (*mpLayers)[0]->Repaint(*mpTargetWindow->GetOutDev(), rRepaintRectangle);
278 }
279 else
280 {
281 // Paint all layers first into the back buffer (to avoid flickering
282 // due to synchronous paints) and then copy that into the target
283 // device.
284 mpBackBuffer->SetMapMode(mpTargetWindow->GetMapMode());
285 for (auto const& it : *mpLayers)
286 {
287 it->Repaint(*mpBackBuffer, rRepaintRectangle);
288 }
289 DeviceCopy(*mpTargetWindow->GetOutDev(), *mpBackBuffer, rRepaintRectangle);
290 }
291}
292
294{
295 const Size aSize (mpTargetWindow->GetSizePixel());
296 mpBackBuffer->SetOutputSizePixel(aSize);
297 for (auto const& it : *mpLayers)
298 {
299 it->Resize(aSize);
300 }
301}
302
304{
305 for (auto const& it : *mpLayers)
306 {
307 it->Dispose();
308 }
309 mpLayers->clear();
310}
311
313{
314 const MapMode& rMapMode (mpTargetWindow->GetMapMode());
315 if (maSavedMapMode == rMapMode)
316 return false;
317
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())
323 {
324 // When the scale has changed then we have to paint everything.
325 InvalidateAllLayers(aLogicWindowBox);
326 }
327 else if (maSavedMapMode.GetOrigin() != rMapMode.GetOrigin())
328 {
329 // Window has been scrolled. Adapt contents of backbuffers and
330 // layer devices.
331 const Point aDelta (rMapMode.GetOrigin() - maSavedMapMode.GetOrigin());
332 mpBackBuffer->CopyArea(
333 aLogicWindowBox.TopLeft(),
334 mpTargetWindow->PixelToLogic(Point(0,0), maSavedMapMode),
335 aLogicWindowBox.GetSize());
336
337 // Invalidate the area(s) that have been exposed.
338 const ::tools::Rectangle aWindowBox (Point(0,0), mpTargetWindow->GetSizePixel());
339 if (aDelta.Y() < 0)
341 aWindowBox.Left(),
342 aWindowBox.Bottom()+aDelta.Y(),
343 aWindowBox.Right(),
344 aWindowBox.Bottom())));
345 else if (aDelta.Y() > 0)
347 aWindowBox.Left(),
348 aWindowBox.Top(),
349 aWindowBox.Right(),
350 aWindowBox.Top()+aDelta.Y())));
351 if (aDelta.X() < 0)
353 aWindowBox.Right()+aDelta.X(),
354 aWindowBox.Top(),
355 aWindowBox.Right(),
356 aWindowBox.Bottom())));
357 else if (aDelta.X() > 0)
359 aWindowBox.Left(),
360 aWindowBox.Top(),
361 aWindowBox.Left()+aDelta.X(),
362 aWindowBox.Bottom())));
363 }
364 else
365 {
366 // Can this happen? Lets trigger a warning when it does.
367 OSL_ASSERT(false);
368 }
369
370 maSavedMapMode = rMapMode;
371
372 return true;
373}
374
375//===== Layer =================================================================
376
377Layer::Layer()
378{
379}
380
381void Layer::Initialize (sd::Window *pTargetWindow)
382{
383#if 0
384 (void)pTargetWindow;
385#else
386 if ( ! mpLayerDevice)
387 {
389 mpLayerDevice->SetOutputSizePixel(pTargetWindow->GetSizePixel());
390 }
391#endif
392}
393
394void Layer::InvalidateRectangle (const ::tools::Rectangle& rInvalidationBox)
395{
396 maInvalidationRegion.Union(rInvalidationBox);
397}
398
399void Layer::InvalidateRegion (const vcl::Region& rInvalidationRegion)
400{
401 maInvalidationRegion.Union(rInvalidationRegion);
402}
403
404void Layer::Validate (const MapMode& rMapMode)
405{
407 {
410
411 mpLayerDevice->SetMapMode(rMapMode);
412 ForAllRectangles(
413 aRegion,
414 [this] (::tools::Rectangle const& r) { return this->ValidateRectangle(r); });
415 }
416}
417
418void Layer::ValidateRectangle (const ::tools::Rectangle& rBox)
419{
420 if ( ! mpLayerDevice)
421 return;
422 const vcl::Region aSavedClipRegion (mpLayerDevice->GetClipRegion());
423 mpLayerDevice->IntersectClipRegion(rBox);
424
425 for (const auto& rxPainter : maPainters)
426 {
427 rxPainter->Paint(*mpLayerDevice, rBox);
428 }
429
430 mpLayerDevice->SetClipRegion(aSavedClipRegion);
431}
432
433void Layer::Repaint (
434 OutputDevice& rTargetDevice,
435 const ::tools::Rectangle& rRepaintRectangle)
436{
437 if (mpLayerDevice)
438 {
439 DeviceCopy(rTargetDevice, *mpLayerDevice, rRepaintRectangle);
440 }
441 else
442 {
443 for (auto const& it : maPainters)
444 {
445 it->Paint(rTargetDevice, rRepaintRectangle);
446 }
447 }
448}
449
450void Layer::Resize (const Size& rSize)
451{
452 if (mpLayerDevice)
453 {
454 mpLayerDevice->SetOutputSizePixel(rSize);
456 }
457}
458
459void Layer::AddPainter (const SharedILayerPainter& rpPainter)
460{
461 OSL_ASSERT(::std::find(maPainters.begin(), maPainters.end(), rpPainter) == maPainters.end());
462
463 maPainters.push_back(rpPainter);
464}
465
466void Layer::RemovePainter (const SharedILayerPainter& rpPainter)
467{
468 const ::std::vector<SharedILayerPainter>::iterator iPainter (
469 ::std::find(maPainters.begin(), maPainters.end(), rpPainter));
470 if (iPainter != maPainters.end())
471 {
472 maPainters.erase(iPainter);
473 }
474 else
475 {
476 SAL_WARN("sd", "LayeredDevice::RemovePainter called for painter that is not registered");
477 }
478}
479
480bool Layer::HasPainter() const
481{
482 return !maPainters.empty();
483}
484
485void Layer::Dispose()
486{
487 maPainters.clear();
488}
489
490} // end of namespace ::sd::slidesorter::view
491
492/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
ScopedVclPtr< VirtualDevice > mpLayerDevice
::std::vector< SharedILayerPainter > maPainters
vcl::Region maInvalidationRegion
const int mnLayer
const std::shared_ptr< LayeredDevice > mpLayeredDevice
VclPtr< sd::Window > mpTargetWindow
const Fraction & GetScaleX() const
MapUnit GetMapUnit() const
const Point & GetOrigin() const
const Fraction & GetScaleY() const
SAL_DLLPRIVATE void DrawOutDev(const Point &, const Size &, const Point &, const Size &, const Printer &)=delete
constexpr tools::Long Y() const
constexpr tools::Long X() const
void disposeAndReset(reference_type *pBody)
An SdWindow contains the actual working area of ViewShell.
Definition: Window.hxx:45
::std::vector< SharedLayer >::const_iterator end() const
::std::vector< SharedLayer >::const_iterator begin() const
void RepaintRectangle(const ::tools::Rectangle &rRepaintRectangle)
void Repaint(const vcl::Region &rRepaintRegion)
void InvalidateAllLayers(const ::tools::Rectangle &rInvalidationBox)
void Invalidate(const ::tools::Rectangle &rInvalidationBox, const sal_Int32 nLayer)
void RegisterPainter(const SharedILayerPainter &rPainter, const sal_Int32 nLayer)
ScopedVclPtr< VirtualDevice > mpBackBuffer
LayeredDevice(const VclPtr< sd::Window > &pTargetWindow)
void RemovePainter(const SharedILayerPainter &rPainter, const sal_Int32 nLayer)
std::unique_ptr< LayerContainer > mpLayers
bool IsEmpty() const
void Union(const tools::Rectangle &rRegion)
void GetRegionRectangles(RectangleVector &rTarget) const
void SetEmpty()
::OutputDevice const * GetOutDev() const
virtual Size GetSizePixel() const
sal_Int32 nIndex
sal_Int64 n
#define SAL_WARN(area, stream)
void Dispose(const T &xInterface)
int i
void Create(SwFormatVertOrient &rItem, SvStream &rStrm, sal_uInt16 nVersionAbusedAsSize)
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
std::shared_ptr< ILayerPainter > SharedILayerPainter
std::shared_ptr< ILayerInvalidator > SharedILayerInvalidator
std::vector< tools::Rectangle > RectangleVector