Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/sdr/overlay/overlaymanager.cxx
Line
Count
Source
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 <svx/sdr/overlay/overlaymanager.hxx>
21
#include <basegfx/range/b2drange.hxx>
22
#include <comphelper/propertyvalue.hxx>
23
#include <tools/gen.hxx>
24
#include <vcl/canvastools.hxx>
25
#include <vcl/outdev.hxx>
26
#include <vcl/window.hxx>
27
#include <vcl/gdimtf.hxx>
28
#include <svx/sdr/overlay/overlayobject.hxx>
29
#include <basegfx/matrix/b2dhommatrix.hxx>
30
#include <drawinglayer/processor2d/baseprocessor2d.hxx>
31
#include <drawinglayer/processor2d/processor2dtools.hxx>
32
#include <osl/diagnose.h>
33
#include <memory>
34
35
36
using namespace com::sun::star;
37
38
39
namespace sdr::overlay
40
{
41
        void OverlayManager::ImpDrawMembers(const basegfx::B2DRange& rRange, OutputDevice& rDestinationDevice) const
42
0
        {
43
0
            const sal_uInt32 nSize(maOverlayObjects.size());
44
45
0
            if(!nSize)
46
0
                return;
47
48
            // prepare ViewInformation2D
49
0
            drawinglayer::geometry::ViewInformation2D aViewInformation2D(getCurrentViewInformation2D());
50
51
            // create processor
52
0
            std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createProcessor2DFromOutputDevice(
53
0
                rDestinationDevice,
54
0
                aViewInformation2D));
55
56
0
            for(const auto& rpOverlayObject : maOverlayObjects)
57
0
            {
58
0
                OSL_ENSURE(rpOverlayObject, "Corrupted OverlayObject List (!)");
59
0
                const OverlayObject& rCandidate = *rpOverlayObject;
60
61
0
                if(rCandidate.isVisible())
62
0
                {
63
0
                    const drawinglayer::primitive2d::Primitive2DContainer aSequence = rCandidate.getOverlayObjectPrimitive2DSequence();
64
65
0
                    if(!aSequence.empty())
66
0
                    {
67
0
                        if(rRange.overlaps(rCandidate.getBaseRange()))
68
0
                        {
69
                            // update AA flag at local ViewInformation2D
70
0
                            aViewInformation2D.setUseAntiAliasing(rCandidate.allowsAntiAliase());
71
72
                            // update DrawModeFlags at local ViewInformation2D
73
0
                            aViewInformation2D.setDrawModeFlags(rCandidate.isHighContrastSelection() ?
74
0
                                aViewInformation2D.getDrawModeFlags() | DrawModeFlags::SettingsForSelection :
75
0
                                aViewInformation2D.getDrawModeFlags() & ~DrawModeFlags::SettingsForSelection);
76
77
                            // set if changed - this will update the renderer and/or render
78
                            // target, including changed settings, renderer-independent
79
0
                            if (aViewInformation2D != pProcessor->getViewInformation2D())
80
0
                            {
81
0
                                pProcessor->setViewInformation2D(aViewInformation2D);
82
0
                            }
83
84
                            // render primitive
85
0
                            pProcessor->process(aSequence);
86
0
                        }
87
0
                    }
88
0
                }
89
0
            }
90
0
        }
91
92
        void OverlayManager::ImpStripeDefinitionChanged()
93
8
        {
94
8
            const sal_uInt32 nSize(maOverlayObjects.size());
95
96
8
            if(nSize)
97
0
            {
98
0
                for(const auto& rpOverlayObject : maOverlayObjects)
99
0
                {
100
0
                    OSL_ENSURE(rpOverlayObject, "Corrupted OverlayObject List (!)");
101
0
                    OverlayObject& rCandidate = *rpOverlayObject;
102
0
                    rCandidate.stripeDefinitionHasChanged();
103
0
                }
104
0
            }
105
8
        }
106
107
        double OverlayManager::getDiscreteOne() const
108
0
        {
109
0
            if(basegfx::fTools::equalZero(mfDiscreteOne))
110
0
            {
111
0
                const basegfx::B2DVector aDiscreteInLogic(getOutputDevice().GetInverseViewTransformation() * basegfx::B2DVector(1.0, 0.0));
112
0
                const_cast< OverlayManager* >(this)->mfDiscreteOne = aDiscreteInLogic.getLength();
113
0
            }
114
115
0
            return mfDiscreteOne;
116
0
        }
117
118
        OverlayManager::OverlayManager(OutputDevice& rOutputDevice)
119
4
        :   mrOutputDevice(rOutputDevice),
120
4
            maStripeColorA(COL_BLACK),
121
4
            maStripeColorB(COL_WHITE),
122
4
            mnStripeLengthPixel(5),
123
4
            mfDiscreteOne(0.0)
124
4
        {
125
            // Set Property 'ReducedDisplayQuality' to true to allow simpler interaction
126
            // visualisations. Note: Currently will use reduced quality for 3d scene soft renderer
127
4
            uno::Sequence< beans::PropertyValue > xProperties{
128
4
                comphelper::makePropertyValue(u"ReducedDisplayQuality"_ustr, true)
129
4
            };
130
4
            maViewInformation2D = drawinglayer::geometry::createViewInformation2D(xProperties);
131
4
        }
132
133
        rtl::Reference<OverlayManager> OverlayManager::create(OutputDevice& rOutputDevice)
134
4
        {
135
4
            return rtl::Reference<OverlayManager>(new OverlayManager(rOutputDevice));
136
4
        }
137
138
        bool OverlayManager::isOutputToRecordingMetaFile() const
139
0
        {
140
0
            GDIMetaFile* pMetaFile(getOutputDevice().GetConnectMetaFile());
141
0
            return (pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause());
142
0
        }
143
144
        drawinglayer::geometry::ViewInformation2D const & OverlayManager::getCurrentViewInformation2D() const
145
0
        {
146
0
            drawinglayer::geometry::ViewInformation2D aViewInformation(maViewInformation2D);
147
148
0
            if(getOutputDevice().GetViewTransformation() != maViewTransformation)
149
0
            {
150
0
                basegfx::B2DRange aViewRange(maViewInformation2D.getViewport());
151
152
0
                if(OUTDEV_WINDOW == getOutputDevice().GetOutDevType())
153
0
                {
154
0
                    const Size aOutputSizePixel(getOutputDevice().GetOutputSizePixel());
155
156
                    // only set when we *have* an output size, else let aViewRange
157
                    // stay on empty
158
0
                    if(aOutputSizePixel.Width() && aOutputSizePixel.Height())
159
0
                    {
160
0
                        aViewRange = basegfx::B2DRange(0.0, 0.0, aOutputSizePixel.getWidth(), aOutputSizePixel.getHeight());
161
0
                        aViewRange.transform(getOutputDevice().GetInverseViewTransformation());
162
0
                    }
163
0
                }
164
165
0
                maViewTransformation = getOutputDevice().GetViewTransformation();
166
0
                aViewInformation.setViewTransformation(maViewTransformation);
167
0
                aViewInformation.setViewport(aViewRange);
168
0
                mfDiscreteOne = 0.0;
169
0
            }
170
171
0
            if (!isOutputToRecordingMetaFile())
172
0
            {
173
                // this is the EditView repaint, provide that information,
174
                // but only if we do not export to metafile
175
0
                aViewInformation.setEditViewActive(true);
176
177
                // also copy the current DrawModeFlags
178
0
                aViewInformation.setDrawModeFlags(getOutputDevice().GetDrawMode());
179
0
            }
180
181
0
            if (aViewInformation != maViewInformation2D)
182
0
            {
183
0
                maViewInformation2D = aViewInformation;
184
0
            }
185
186
0
            return maViewInformation2D;
187
0
        }
188
189
        void OverlayManager::impApplyRemoveActions(OverlayObject& rTarget)
190
0
        {
191
            // handle evtl. animation
192
0
            if(rTarget.allowsAnimation())
193
0
            {
194
                // remove from event chain
195
0
                RemoveEvent(&rTarget);
196
0
            }
197
198
            // make invisible
199
0
            invalidateRange(rTarget.getBaseRange());
200
201
            // clear manager
202
0
            rTarget.mpOverlayManager = nullptr;
203
0
        }
204
205
        void OverlayManager::impApplyAddActions(OverlayObject& rTarget)
206
0
        {
207
            // set manager
208
0
            rTarget.mpOverlayManager = this;
209
210
            // make visible
211
0
            invalidateRange(rTarget.getBaseRange());
212
213
            // handle evtl. animation
214
0
            if(rTarget.allowsAnimation())
215
0
            {
216
                // Trigger at current time to get alive. This will do the
217
                // object-specific next time calculation and hand over adding
218
                // again to the scheduler to the animated object, too. This works for
219
                // a paused or non-paused animator.
220
0
                rTarget.Trigger(GetTime());
221
0
            }
222
0
        }
223
224
        OverlayManager::~OverlayManager()
225
4
        {
226
            // The OverlayManager is not the owner of the OverlayObjects
227
            // and thus will not delete them, but remove them. Profit here
228
            // from knowing that all will be removed
229
4
            const sal_uInt32 nSize(maOverlayObjects.size());
230
231
4
            if(nSize)
232
0
            {
233
0
                for(const auto& rpOverlayObject : maOverlayObjects)
234
0
                {
235
0
                    OSL_ENSURE(rpOverlayObject, "Corrupted OverlayObject List (!)");
236
0
                    OverlayObject& rCandidate = *rpOverlayObject;
237
0
                    impApplyRemoveActions(rCandidate);
238
0
                }
239
240
                // erase vector
241
0
                maOverlayObjects.clear();
242
0
            }
243
4
        }
244
245
        void OverlayManager::completeRedraw(const vcl::Region& rRegion, OutputDevice* pPreRenderDevice) const
246
4
        {
247
4
            if(rRegion.IsEmpty() || maOverlayObjects.empty())
248
4
                return;
249
250
            // check for changed MapModes. That may influence the
251
            // logical size of pixel based OverlayObjects (like BitmapHandles)
252
            //ImpCheckMapModeChange();
253
254
            // paint members
255
0
            const tools::Rectangle aRegionBoundRect(rRegion.GetBoundRect());
256
0
            const basegfx::B2DRange aRegionRange = vcl::unotools::b2DRectangleFromRectangle(aRegionBoundRect);
257
258
0
            OutputDevice& rTarget = pPreRenderDevice ? *pPreRenderDevice : getOutputDevice();
259
0
            ImpDrawMembers(aRegionRange, rTarget);
260
0
        }
261
262
        void OverlayManager::add(OverlayObject& rOverlayObject)
263
0
        {
264
0
            OSL_ENSURE(nullptr == rOverlayObject.mpOverlayManager, "OverlayObject is added twice to an OverlayManager (!)");
265
266
            // add to the end of chain to preserve display order in paint
267
0
            maOverlayObjects.push_back(&rOverlayObject);
268
269
            // execute add actions
270
0
            impApplyAddActions(rOverlayObject);
271
0
        }
272
273
        void OverlayManager::remove(OverlayObject& rOverlayObject)
274
0
        {
275
0
            OSL_ENSURE(rOverlayObject.mpOverlayManager == this, "OverlayObject is removed from wrong OverlayManager (!)");
276
277
            // execute remove actions
278
0
            impApplyRemoveActions(rOverlayObject);
279
280
            // remove from vector
281
0
            const OverlayObjectVector::iterator aFindResult = ::std::find(maOverlayObjects.begin(), maOverlayObjects.end(), &rOverlayObject);
282
0
            const bool bFound(aFindResult != maOverlayObjects.end());
283
0
            OSL_ENSURE(bFound, "OverlayObject NOT found at OverlayManager (!)");
284
285
0
            if(bFound)
286
0
            {
287
0
                maOverlayObjects.erase(aFindResult);
288
0
            }
289
0
        }
290
291
        tools::Rectangle OverlayManager::RangeToInvalidateRectangle(const basegfx::B2DRange& rRange) const
292
0
        {
293
0
            if (rRange.isEmpty()) {
294
0
                return {};
295
0
            }
296
0
            if (getCurrentViewInformation2D().getUseAntiAliasing())
297
0
            {
298
                // assume AA needs one pixel more and invalidate one pixel more
299
0
                const double fDiscreteOne(getDiscreteOne());
300
0
                const tools::Rectangle aInvalidateRectangle(
301
0
                    static_cast<tools::Long>(floor(rRange.getMinX() - fDiscreteOne)),
302
0
                    static_cast<tools::Long>(floor(rRange.getMinY() - fDiscreteOne)),
303
0
                    static_cast<tools::Long>(ceil(rRange.getMaxX() + fDiscreteOne)),
304
0
                    static_cast<tools::Long>(ceil(rRange.getMaxY() + fDiscreteOne)));
305
0
                return aInvalidateRectangle;
306
0
            }
307
0
            else
308
0
            {
309
                // #i77674# transform to rectangle. Use floor/ceil to get all covered
310
                // discrete pixels, see #i75163# and OverlayManagerBuffered::invalidateRange
311
0
                const tools::Rectangle aInvalidateRectangle(
312
0
                    static_cast<sal_Int32>(floor(rRange.getMinX())), static_cast<sal_Int32>(floor(rRange.getMinY())),
313
0
                    static_cast<sal_Int32>(ceil(rRange.getMaxX())), static_cast<sal_Int32>(ceil(rRange.getMaxY())));
314
0
                return aInvalidateRectangle;
315
0
            }
316
0
        }
317
318
        void OverlayManager::invalidateRange(const basegfx::B2DRange& rRange)
319
0
        {
320
0
            if (OUTDEV_WINDOW == getOutputDevice().GetOutDevType())
321
0
            {
322
0
                tools::Rectangle aInvalidateRectangle(RangeToInvalidateRectangle(rRange));
323
                // simply invalidate
324
0
                getOutputDevice().GetOwnerWindow()->Invalidate(aInvalidateRectangle, InvalidateFlags::NoErase);
325
0
            }
326
0
        }
327
328
        // stripe support ColA
329
        void OverlayManager::setStripeColorA(Color aNew)
330
4
        {
331
4
            if(aNew != maStripeColorA)
332
0
            {
333
0
                maStripeColorA = aNew;
334
0
                ImpStripeDefinitionChanged();
335
0
            }
336
4
        }
337
338
        // stripe support ColB
339
        void OverlayManager::setStripeColorB(Color aNew)
340
4
        {
341
4
            if(aNew != maStripeColorB)
342
4
            {
343
4
                maStripeColorB = aNew;
344
4
                ImpStripeDefinitionChanged();
345
4
            }
346
4
        }
347
348
        // stripe support StripeLengthPixel
349
        void OverlayManager::setStripeLengthPixel(sal_uInt32 nNew)
350
4
        {
351
4
            if(nNew != mnStripeLengthPixel)
352
4
            {
353
4
                mnStripeLengthPixel = nNew;
354
4
                ImpStripeDefinitionChanged();
355
4
            }
356
4
        }
357
358
} // end of namespace
359
360
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */