Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/gdi/region.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 <memory>
21
#include <tools/vcompat.hxx>
22
#include <tools/stream.hxx>
23
#include <osl/diagnose.h>
24
#include <sal/log.hxx>
25
#include <vcl/canvastools.hxx>
26
#include <vcl/region.hxx>
27
#include <regionband.hxx>
28
29
#include <basegfx/polygon/b2dpolypolygontools.hxx>
30
#include <basegfx/polygon/b2dpolygontools.hxx>
31
#include <basegfx/polygon/b2dpolygonclipper.hxx>
32
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
33
#include <basegfx/range/b2drange.hxx>
34
#include <basegfx/matrix/b2dhommatrixtools.hxx>
35
#include <tools/poly.hxx>
36
#include <comphelper/configuration.hxx>
37
38
namespace
39
{
40
    /** Return <TRUE/> when the given polygon is rectilinear and oriented so that
41
        all sides are either horizontal or vertical.
42
    */
43
    bool ImplIsPolygonRectilinear (const tools::PolyPolygon& rPolyPoly)
44
1.59k
    {
45
        // Iterate over all polygons.
46
1.59k
        const sal_uInt16 nPolyCount = rPolyPoly.Count();
47
2.58k
        for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly)
48
2.12k
        {
49
2.12k
            const tools::Polygon&  aPoly = rPolyPoly.GetObject(nPoly);
50
51
            // Iterate over all edges of the current polygon.
52
2.12k
            const sal_uInt16 nSize = aPoly.GetSize();
53
54
2.12k
            if (nSize < 2)
55
0
                continue;
56
2.12k
            Point aPoint (aPoly.GetPoint(0));
57
2.12k
            const Point aLastPoint (aPoint);
58
14.6k
            for (sal_uInt16 nPoint = 1; nPoint < nSize; ++nPoint)
59
13.6k
            {
60
13.6k
                const Point aNextPoint (aPoly.GetPoint(nPoint));
61
                // When there is at least one edge that is neither vertical nor
62
                // horizontal then the entire polygon is not rectilinear (and
63
                // oriented along primary axes.)
64
13.6k
                if (aPoint.X() != aNextPoint.X() && aPoint.Y() != aNextPoint.Y())
65
1.13k
                    return false;
66
67
12.4k
                aPoint = aNextPoint;
68
12.4k
            }
69
            // Compare closing edge.
70
992
            if (aLastPoint.X() != aPoint.X() && aLastPoint.Y() != aPoint.Y())
71
0
                return false;
72
992
        }
73
456
        return true;
74
1.59k
    }
75
76
    /** Convert a rectilinear polygon (that is oriented along the primary axes)
77
        to a list of bands.  For this special form of polygon we can use an
78
        optimization that prevents the creation of one band per y value.
79
        However, it still is possible that some temporary bands are created that
80
        later can be optimized away.
81
        @param rPolyPolygon
82
            A set of zero, one, or more polygons, nested or not, that are
83
            converted into a list of bands.
84
        @return
85
            A new RegionBand object is returned that contains the bands that
86
            represent the given poly-polygon.
87
    */
88
    std::shared_ptr<RegionBand> ImplRectilinearPolygonToBands(const tools::PolyPolygon& rPolyPoly)
89
456
    {
90
456
        OSL_ASSERT(ImplIsPolygonRectilinear (rPolyPoly));
91
92
        // Create a new RegionBand object as container of the bands.
93
456
        std::shared_ptr<RegionBand> pRegionBand( std::make_shared<RegionBand>() );
94
456
        tools::Long nLineId = 0;
95
96
        // Iterate over all polygons.
97
456
        const sal_uInt16 nPolyCount = rPolyPoly.Count();
98
1.21k
        for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly)
99
755
        {
100
755
            const tools::Polygon&  aPoly = rPolyPoly.GetObject(nPoly);
101
102
            // Iterate over all edges of the current polygon.
103
755
            const sal_uInt16 nSize = aPoly.GetSize();
104
755
            if (nSize < 2)
105
0
                continue;
106
            // Avoid fetching every point twice (each point is the start point
107
            // of one and the end point of another edge.)
108
755
            Point aStart (aPoly.GetPoint(0));
109
755
            Point aEnd;
110
6.09k
            for (sal_uInt16 nPoint = 1; nPoint <= nSize; ++nPoint, aStart=aEnd)
111
5.33k
            {
112
                // We take the implicit closing edge into account by mapping
113
                // index nSize to 0.
114
5.33k
                aEnd = aPoly.GetPoint(nPoint%nSize);
115
5.33k
                if (aStart.Y() == aEnd.Y())
116
4.27k
                {
117
                    // Horizontal lines are ignored.
118
4.27k
                    continue;
119
4.27k
                }
120
121
                // At this point the line has to be vertical.
122
1.06k
                OSL_ASSERT(aStart.X() == aEnd.X());
123
124
                // Sort y-coordinates to simplify the algorithm and store the
125
                // direction separately.  The direction is calculated as it is
126
                // in other places (but seems to be the wrong way.)
127
1.06k
                const tools::Long nTop (::std::min(aStart.Y(), aEnd.Y()));
128
1.06k
                const tools::Long nBottom (::std::max(aStart.Y(), aEnd.Y()));
129
1.06k
                const LineType eLineType (aStart.Y() > aEnd.Y() ? LineType::Descending : LineType::Ascending);
130
131
                // Make sure that the current line is covered by bands.
132
1.06k
                pRegionBand->ImplAddMissingBands(nTop,nBottom);
133
134
                // Find top-most band that may contain nTop.
135
1.06k
                ImplRegionBand* pBand = pRegionBand->ImplGetFirstRegionBand();
136
1.06k
                while (pBand!=nullptr && pBand->mnYBottom < nTop)
137
0
                    pBand = pBand->mpNextBand;
138
1.06k
                ImplRegionBand* pTopBand = pBand;
139
                // If necessary split the band at nTop so that nTop is contained
140
                // in the lower band.
141
1.06k
                if (pBand!=nullptr
142
                       // Prevent the current band from becoming 0 pixel high
143
1.06k
                    && pBand->mnYTop<nTop
144
                       // this allows the lowest pixel of the band to be split off
145
0
                    && pBand->mnYBottom>=nTop
146
                       // do not split a band that is just one pixel high
147
0
                    && pBand->mnYTop<pBand->mnYBottom-1)
148
0
                {
149
                    // Split the top band.
150
0
                    pTopBand = pBand->SplitBand(nTop);
151
0
                }
152
153
                // Advance to band that may contain nBottom.
154
1.06k
                while (pBand!=nullptr && pBand->mnYBottom < nBottom)
155
0
                    pBand = pBand->mpNextBand;
156
                // The lowest band may have to be split at nBottom so that
157
                // nBottom itself remains in the upper band.
158
1.06k
                if (pBand!=nullptr
159
                       // allow the current band becoming 1 pixel high
160
1.06k
                    && pBand->mnYTop<=nBottom
161
                       // prevent splitting off a band that is 0 pixel high
162
1.06k
                    && pBand->mnYBottom>nBottom
163
                       // do not split a band that is just one pixel high
164
0
                    && pBand->mnYTop<pBand->mnYBottom-1)
165
0
                {
166
                    // Split the bottom band.
167
0
                    pBand->SplitBand(nBottom+1);
168
0
                }
169
170
                // Note that we remember the top band (in pTopBand) but not the
171
                // bottom band.  The later can be determined by comparing y
172
                // coordinates.
173
174
                // Add the x-value as point to all bands in the nTop->nBottom range.
175
2.12k
                for (pBand=pTopBand; pBand!=nullptr&&pBand->mnYTop<=nBottom; pBand=pBand->mpNextBand)
176
1.06k
                    pBand->InsertPoint(aStart.X(), nLineId++, true, eLineType);
177
1.06k
            }
178
755
        }
179
180
456
        return pRegionBand;
181
456
    }
182
183
    /** Convert a general polygon (one for which ImplIsPolygonRectilinear()
184
        returns <FALSE/>) to bands.
185
    */
186
    std::shared_ptr<RegionBand> ImplGeneralPolygonToBands(const tools::PolyPolygon& rPolyPoly, const tools::Rectangle& rPolygonBoundingBox)
187
1.13k
    {
188
1.13k
        tools::Long nLineID = 0;
189
190
        // initialisation and creation of Bands
191
1.13k
        std::shared_ptr<RegionBand> pRegionBand( std::make_shared<RegionBand>() );
192
1.13k
        pRegionBand->CreateBandRange(rPolygonBoundingBox.Top(), rPolygonBoundingBox.Bottom());
193
194
        // insert polygons
195
1.13k
        const sal_uInt16 nPolyCount = rPolyPoly.Count();
196
197
2.56k
        for ( sal_uInt16 nPoly = 0; nPoly < nPolyCount; nPoly++ )
198
1.42k
        {
199
            // get reference to current polygon
200
1.42k
            const tools::Polygon&  aPoly = rPolyPoly.GetObject( nPoly );
201
1.42k
            const sal_uInt16    nSize = aPoly.GetSize();
202
203
            // not enough points ( <= 2 )? -> nothing to do!
204
1.42k
            if ( nSize <= 2 )
205
0
                continue;
206
207
            // band the polygon
208
13.4k
            for ( sal_uInt16 nPoint = 1; nPoint < nSize; nPoint++ )
209
12.0k
            {
210
12.0k
                pRegionBand->InsertLine( aPoly.GetPoint(nPoint-1), aPoly.GetPoint(nPoint), nLineID++ );
211
12.0k
            }
212
213
            // close polygon with line from first point to last point, if necessary
214
1.42k
            const Point rLastPoint = aPoly.GetPoint(nSize-1);
215
1.42k
            const Point rFirstPoint = aPoly.GetPoint(0);
216
217
1.42k
            if ( rLastPoint != rFirstPoint )
218
0
            {
219
0
                pRegionBand->InsertLine( rLastPoint, rFirstPoint, nLineID++ );
220
0
            }
221
1.42k
        }
222
223
1.13k
        return pRegionBand;
224
1.13k
    }
225
} // end of anonymous namespace
226
227
namespace vcl {
228
229
bool vcl::Region::IsEmpty() const
230
27.7M
{
231
27.7M
    return !mbIsNull && !mpB2DPolyPolygon && !mpPolyPolygon && !mpRegionBand;
232
27.7M
}
233
234
235
static std::shared_ptr<RegionBand> ImplCreateRegionBandFromPolyPolygon(const tools::PolyPolygon& rPolyPolygon)
236
1.59k
{
237
1.59k
    std::shared_ptr<RegionBand> pRetval;
238
239
1.59k
    if(rPolyPolygon.Count())
240
1.59k
    {
241
        // ensure to subdivide when bezier segments are used, it's going to
242
        // be expanded to rectangles
243
1.59k
        tools::PolyPolygon aPolyPolygon;
244
245
1.59k
        rPolyPolygon.AdaptiveSubdivide(aPolyPolygon);
246
247
1.59k
        if(aPolyPolygon.Count())
248
1.59k
        {
249
1.59k
            const tools::Rectangle aRect(aPolyPolygon.GetBoundRect());
250
251
1.59k
            if(!aRect.IsEmpty())
252
1.59k
            {
253
1.59k
                if(ImplIsPolygonRectilinear(aPolyPolygon))
254
456
                {
255
                    // For rectilinear polygons there is an optimized band conversion.
256
456
                    pRetval = ImplRectilinearPolygonToBands(aPolyPolygon);
257
456
                }
258
1.13k
                else
259
1.13k
                {
260
1.13k
                    pRetval = ImplGeneralPolygonToBands(aPolyPolygon, aRect);
261
1.13k
                }
262
263
                // Convert points into seps.
264
1.59k
                if(pRetval)
265
1.59k
                {
266
1.59k
                    pRetval->processPoints();
267
268
                    // Optimize list of bands.  Adjacent bands with identical lists
269
                    // of seps are joined.
270
1.59k
                    if(!pRetval->OptimizeBandList())
271
223
                    {
272
223
                        pRetval.reset();
273
223
                    }
274
1.59k
                }
275
1.59k
            }
276
1.59k
        }
277
1.59k
    }
278
279
1.59k
    return pRetval;
280
1.59k
}
281
282
tools::PolyPolygon vcl::Region::ImplCreatePolyPolygonFromRegionBand() const
283
168k
{
284
168k
    tools::PolyPolygon aRetval;
285
286
168k
    if(getRegionBand())
287
168k
    {
288
168k
        RectangleVector aRectangles;
289
168k
        GetRegionRectangles(aRectangles);
290
291
168k
        for (auto const& rectangle : aRectangles)
292
169k
        {
293
169k
            aRetval.Insert( tools::Polygon(rectangle) );
294
169k
        }
295
168k
    }
296
0
    else
297
0
    {
298
0
        OSL_ENSURE(false, "Called with no local RegionBand (!)");
299
0
    }
300
301
168k
    return aRetval;
302
168k
}
303
304
basegfx::B2DPolyPolygon vcl::Region::ImplCreateB2DPolyPolygonFromRegionBand() const
305
168k
{
306
168k
    tools::PolyPolygon aPoly(ImplCreatePolyPolygonFromRegionBand());
307
308
168k
    return aPoly.getB2DPolyPolygon();
309
168k
}
310
311
Region::Region(bool bIsNull)
312
3.32M
:   mbIsNull(bIsNull)
313
3.32M
{
314
3.32M
}
315
316
Region::Region(const tools::Rectangle& rRect)
317
4.51M
:   mbIsNull(false)
318
4.51M
{
319
4.51M
    if (!rRect.IsEmpty())
320
80.9k
        mpRegionBand = std::make_shared<RegionBand>(rRect);
321
4.51M
}
322
323
Region::Region(const tools::Polygon& rPolygon)
324
179
:   mbIsNull(false)
325
179
{
326
327
179
    if(rPolygon.GetSize())
328
157
    {
329
157
        ImplCreatePolyPolyRegion(tools::PolyPolygon(rPolygon));
330
157
    }
331
179
}
332
333
Region::Region(const tools::PolyPolygon& rPolyPoly)
334
12.7k
:   mbIsNull(false)
335
12.7k
{
336
337
12.7k
    if(rPolyPoly.Count())
338
11.8k
    {
339
11.8k
        ImplCreatePolyPolyRegion(rPolyPoly);
340
11.8k
    }
341
12.7k
}
342
343
Region::Region(const basegfx::B2DPolyPolygon& rPolyPoly)
344
1.38k
:   mbIsNull(false)
345
1.38k
{
346
347
1.38k
    if(rPolyPoly.count())
348
1.01k
    {
349
1.01k
        ImplCreatePolyPolyRegion(rPolyPoly);
350
1.01k
    }
351
1.38k
}
352
353
348k
Region::Region(const vcl::Region&) = default;
354
355
Region::Region(vcl::Region&& rRegion) noexcept
356
276k
:   mpB2DPolyPolygon(std::move(rRegion.mpB2DPolyPolygon)),
357
276k
    mpPolyPolygon(std::move(rRegion.mpPolyPolygon)),
358
276k
    mpRegionBand(std::move(rRegion.mpRegionBand)),
359
276k
    mbIsNull(rRegion.mbIsNull)
360
276k
{
361
276k
    rRegion.mbIsNull = true;
362
276k
}
363
364
8.35M
Region::~Region() = default;
365
366
void vcl::Region::ImplCreatePolyPolyRegion( const tools::PolyPolygon& rPolyPoly )
367
12.0k
{
368
12.0k
    const sal_uInt16 nPolyCount = rPolyPoly.Count();
369
370
12.0k
    if(!nPolyCount)
371
0
        return;
372
373
    // polypolygon empty? -> empty region
374
12.0k
    const tools::Rectangle aRect(rPolyPoly.GetBoundRect());
375
376
12.0k
    if(aRect.IsEmpty())
377
262
        return;
378
379
    // width OR height == 1 ? => Rectangular region
380
11.7k
    if((1 == aRect.GetWidth()) || (1 == aRect.GetHeight()) || rPolyPoly.IsRect())
381
3.60k
    {
382
3.60k
        mpRegionBand = std::make_shared<RegionBand>(aRect);
383
3.60k
    }
384
8.13k
    else
385
8.13k
    {
386
8.13k
        mpPolyPolygon = rPolyPoly;
387
8.13k
    }
388
389
11.7k
    mbIsNull = false;
390
11.7k
}
391
392
void vcl::Region::ImplCreatePolyPolyRegion( const basegfx::B2DPolyPolygon& rPolyPoly )
393
1.01k
{
394
1.01k
    if(rPolyPoly.count() && !rPolyPoly.getB2DRange().isEmpty())
395
1.01k
    {
396
1.01k
        mpB2DPolyPolygon = rPolyPoly;
397
1.01k
        mbIsNull = false;
398
1.01k
    }
399
1.01k
}
400
401
void vcl::Region::Move( tools::Long nHorzMove, tools::Long nVertMove )
402
25.8k
{
403
25.8k
    if(IsNull() || IsEmpty())
404
16.1k
    {
405
        // empty or null need no move
406
16.1k
        return;
407
16.1k
    }
408
409
9.72k
    if(!nHorzMove && !nVertMove)
410
853
    {
411
        // no move defined
412
853
        return;
413
853
    }
414
415
8.87k
    if(getB2DPolyPolygon())
416
254
    {
417
254
        basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon());
418
419
254
        aPoly.translate(nHorzMove, nVertMove);
420
254
        if (aPoly.count())
421
253
            mpB2DPolyPolygon = aPoly;
422
1
        else
423
1
            mpB2DPolyPolygon.reset();
424
254
        mpPolyPolygon.reset();
425
254
        mpRegionBand.reset();
426
254
    }
427
8.61k
    else if(getPolyPolygon())
428
5.40k
    {
429
5.40k
        tools::PolyPolygon aPoly(*getPolyPolygon());
430
431
5.40k
        aPoly.Move(nHorzMove, nVertMove);
432
5.40k
        mpB2DPolyPolygon.reset();
433
5.40k
        if (aPoly.Count())
434
5.40k
            mpPolyPolygon = aPoly;
435
2
        else
436
2
            mpPolyPolygon.reset();
437
5.40k
        mpRegionBand.reset();
438
5.40k
    }
439
3.21k
    else if(getRegionBand())
440
3.21k
    {
441
3.21k
        std::shared_ptr<RegionBand> pNew = std::make_shared<RegionBand>(*getRegionBand());
442
443
3.21k
        pNew->Move(nHorzMove, nVertMove);
444
3.21k
        mpB2DPolyPolygon.reset();
445
3.21k
        mpPolyPolygon.reset();
446
3.21k
        mpRegionBand = std::move(pNew);
447
3.21k
    }
448
0
    else
449
0
    {
450
0
        OSL_ENSURE(false, "Region::Move error: impossible combination (!)");
451
0
    }
452
8.87k
}
453
454
void vcl::Region::Scale( double fScaleX, double fScaleY )
455
0
{
456
0
    if(IsNull() || IsEmpty())
457
0
    {
458
        // empty or null need no scale
459
0
        return;
460
0
    }
461
462
0
    if(basegfx::fTools::equalZero(fScaleX) && basegfx::fTools::equalZero(fScaleY))
463
0
    {
464
        // no scale defined
465
0
        return;
466
0
    }
467
468
0
    if(getB2DPolyPolygon())
469
0
    {
470
0
        basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon());
471
472
0
        aPoly.transform(basegfx::utils::createScaleB2DHomMatrix(fScaleX, fScaleY));
473
0
        if (aPoly.count())
474
0
            mpB2DPolyPolygon = aPoly;
475
0
        else
476
0
            mpB2DPolyPolygon.reset();
477
0
        mpPolyPolygon.reset();
478
0
        mpRegionBand.reset();
479
0
    }
480
0
    else if(getPolyPolygon())
481
0
    {
482
0
        tools::PolyPolygon aPoly(*getPolyPolygon());
483
484
0
        aPoly.Scale(fScaleX, fScaleY);
485
0
        mpB2DPolyPolygon.reset();
486
0
        if (aPoly.Count())
487
0
            mpPolyPolygon = aPoly;
488
0
        else
489
0
            mpPolyPolygon.reset();
490
0
        mpRegionBand.reset();
491
0
    }
492
0
    else if(getRegionBand())
493
0
    {
494
0
        std::shared_ptr<RegionBand> pNew = std::make_shared<RegionBand>(*getRegionBand());
495
496
0
        pNew->Scale(fScaleX, fScaleY);
497
0
        mpB2DPolyPolygon.reset();
498
0
        mpPolyPolygon.reset();
499
0
        mpRegionBand = std::move(pNew);
500
0
    }
501
0
    else
502
0
    {
503
0
        OSL_ENSURE(false, "Region::Scale error: impossible combination (!)");
504
0
    }
505
0
}
506
507
void vcl::Region::Union( const tools::Rectangle& rRect )
508
174k
{
509
174k
    if(rRect.IsEmpty())
510
0
    {
511
        // empty rectangle will not expand the existing union, nothing to do
512
0
        return;
513
0
    }
514
515
174k
    if(IsEmpty())
516
170k
    {
517
        // no local data, the union will be equal to source. Create using rectangle
518
170k
        *this = rRect;
519
170k
        return;
520
170k
    }
521
522
3.51k
    if(HasPolyPolygonOrB2DPolyPolygon())
523
0
    {
524
        // get this B2DPolyPolygon, solve on polygon base
525
0
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
526
527
0
        aThisPolyPoly = basegfx::utils::prepareForPolygonOperation(aThisPolyPoly);
528
529
0
        if(!aThisPolyPoly.count())
530
0
        {
531
            // no local polygon, use the rectangle as new region
532
0
            *this = rRect;
533
0
        }
534
0
        else
535
0
        {
536
            // get the other B2DPolyPolygon and use logical Or-Operation
537
0
            const basegfx::B2DPolygon aRectPoly(
538
0
                basegfx::utils::createPolygonFromRect(
539
0
                        vcl::unotools::b2DRectangleFromRectangle(rRect)));
540
0
            const basegfx::B2DPolyPolygon aClip(
541
0
                basegfx::utils::solvePolygonOperationOr(
542
0
                    aThisPolyPoly,
543
0
                    basegfx::B2DPolyPolygon(aRectPoly)));
544
0
            *this = vcl::Region(aClip);
545
0
        }
546
547
0
        return;
548
0
    }
549
550
    // only region band mode possibility left here or null/empty
551
3.51k
    const RegionBand* pCurrent = getRegionBand();
552
553
3.51k
    if(!pCurrent)
554
0
    {
555
        // no region band, create using the rectangle
556
0
        *this = rRect;
557
0
        return;
558
0
    }
559
560
3.51k
    std::shared_ptr<RegionBand> pNew = std::make_shared<RegionBand>(*pCurrent);
561
562
    // get justified rectangle
563
3.51k
    const tools::Long nLeft(std::min(rRect.Left(), rRect.Right()));
564
3.51k
    const tools::Long nTop(std::min(rRect.Top(), rRect.Bottom()));
565
3.51k
    const tools::Long nRight(std::max(rRect.Left(), rRect.Right()));
566
3.51k
    const tools::Long nBottom(std::max(rRect.Top(), rRect.Bottom()));
567
568
    // insert bands if the boundaries are not already in the list
569
3.51k
    pNew->InsertBands(nTop, nBottom);
570
571
    // process union
572
3.51k
    pNew->Union(nLeft, nTop, nRight, nBottom);
573
574
    // cleanup
575
3.51k
    if(!pNew->OptimizeBandList())
576
0
    {
577
0
        pNew.reset();
578
0
    }
579
580
3.51k
    mpRegionBand = std::move(pNew);
581
3.51k
}
582
583
void vcl::Region::Intersect( const tools::Rectangle& rRect )
584
86.7k
{
585
86.7k
    if ( rRect.IsEmpty() )
586
1.58k
    {
587
        // empty rectangle will create empty region
588
1.58k
        SetEmpty();
589
1.58k
        return;
590
1.58k
    }
591
592
85.1k
    if(IsNull())
593
23.2k
    {
594
        // null region (everything) intersect with rect will give rect
595
23.2k
        *this = rRect;
596
23.2k
        return;
597
23.2k
    }
598
599
61.8k
    if(IsEmpty())
600
1.91k
    {
601
        // no content, cannot get more empty
602
1.91k
        return;
603
1.91k
    }
604
605
59.9k
    if(HasPolyPolygonOrB2DPolyPolygon())
606
10.5k
    {
607
        // if polygon data prefer double precision, the other will be lost (if buffered)
608
10.5k
        if(getB2DPolyPolygon())
609
5.16k
        {
610
5.16k
            const basegfx::B2DPolyPolygon aPoly(
611
5.16k
                basegfx::utils::clipPolyPolygonOnRange(
612
5.16k
                    *getB2DPolyPolygon(),
613
5.16k
                    basegfx::B2DRange(
614
5.16k
                        rRect.Left(),
615
5.16k
                        rRect.Top(),
616
5.16k
                        rRect.Right() + 1,
617
5.16k
                        rRect.Bottom() + 1),
618
5.16k
                    true,
619
5.16k
                    false));
620
621
5.16k
            if (aPoly.count())
622
3.88k
                mpB2DPolyPolygon = aPoly;
623
1.27k
            else
624
1.27k
                mpB2DPolyPolygon.reset();
625
5.16k
            mpPolyPolygon.reset();
626
5.16k
            mpRegionBand.reset();
627
5.16k
        }
628
5.42k
        else // if(getPolyPolygon())
629
5.42k
        {
630
5.42k
            tools::PolyPolygon aPoly(*getPolyPolygon());
631
632
            // use the PolyPolygon::Clip method for rectangles, this is
633
            // fairly simple (does not even use GPC) and saves us from
634
            // unnecessary banding
635
5.42k
            aPoly.Clip(rRect);
636
637
5.42k
            mpB2DPolyPolygon.reset();
638
5.42k
            if (aPoly.Count())
639
2.91k
                mpPolyPolygon = aPoly;
640
2.51k
            else
641
2.51k
                mpPolyPolygon.reset();
642
5.42k
            mpRegionBand.reset();
643
5.42k
        }
644
645
10.5k
        return;
646
10.5k
    }
647
648
    // only region band mode possibility left here or null/empty
649
49.3k
    const RegionBand* pCurrent = getRegionBand();
650
651
49.3k
    if(!pCurrent)
652
0
    {
653
        // region is empty -> nothing to do!
654
0
        return;
655
0
    }
656
657
49.3k
    std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));
658
659
    // get justified rectangle
660
49.3k
    const tools::Long nLeft(std::min(rRect.Left(), rRect.Right()));
661
49.3k
    const tools::Long nTop(std::min(rRect.Top(), rRect.Bottom()));
662
49.3k
    const tools::Long nRight(std::max(rRect.Left(), rRect.Right()));
663
49.3k
    const tools::Long nBottom(std::max(rRect.Top(), rRect.Bottom()));
664
665
    // insert bands if the boundaries are not already in the list
666
49.3k
    pNew->InsertBands(nTop, nBottom);
667
668
    // process intersect
669
49.3k
    pNew->Intersect(nLeft, nTop, nRight, nBottom);
670
671
    // cleanup
672
49.3k
    if(!pNew->OptimizeBandList())
673
19.1k
    {
674
19.1k
        pNew.reset();
675
19.1k
    }
676
677
49.3k
    mpRegionBand = std::move(pNew);
678
49.3k
}
679
680
void vcl::Region::Exclude( const tools::Rectangle& rRect )
681
0
{
682
0
    if ( rRect.IsEmpty() )
683
0
    {
684
        // excluding nothing will do no change
685
0
        return;
686
0
    }
687
688
0
    if(IsEmpty())
689
0
    {
690
        // cannot exclude from empty, done
691
0
        return;
692
0
    }
693
694
0
    if(IsNull())
695
0
    {
696
        // error; cannot exclude from null region since this is not representable
697
        // in the data
698
0
        OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
699
0
        return;
700
0
    }
701
702
0
    if( HasPolyPolygonOrB2DPolyPolygon() )
703
0
    {
704
        // get this B2DPolyPolygon
705
0
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
706
707
0
        aThisPolyPoly = basegfx::utils::prepareForPolygonOperation(aThisPolyPoly);
708
709
0
        if(!aThisPolyPoly.count())
710
0
        {
711
            // when local polygon is empty, nothing can be excluded
712
0
            return;
713
0
        }
714
715
        // get the other B2DPolyPolygon
716
0
        const basegfx::B2DPolygon aRectPoly(
717
0
            basegfx::utils::createPolygonFromRect(
718
0
                vcl::unotools::b2DRectangleFromRectangle(rRect)));
719
0
        const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly);
720
0
        const basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationDiff(aThisPolyPoly, aOtherPolyPoly);
721
722
0
        *this = vcl::Region(aClip);
723
724
0
        return;
725
0
    }
726
727
    // only region band mode possibility left here or null/empty
728
0
    if(!mpRegionBand)
729
0
    {
730
        // empty? -> done!
731
0
        return;
732
0
    }
733
734
0
    std::shared_ptr<RegionBand>& pNew = mpRegionBand;
735
    // only make a copy if someone else is also using it
736
0
    if (pNew.use_count() > 1)
737
0
        pNew = std::make_shared<RegionBand>(*pNew);
738
739
    // get justified rectangle
740
0
    const tools::Long nLeft(std::min(rRect.Left(), rRect.Right()));
741
0
    const tools::Long nTop(std::min(rRect.Top(), rRect.Bottom()));
742
0
    const tools::Long nRight(std::max(rRect.Left(), rRect.Right()));
743
0
    const tools::Long nBottom(std::max(rRect.Top(), rRect.Bottom()));
744
745
    // insert bands if the boundaries are not already in the list
746
0
    pNew->InsertBands(nTop, nBottom);
747
748
    // process exclude
749
0
    pNew->Exclude(nLeft, nTop, nRight, nBottom);
750
751
    // cleanup
752
0
    if(!pNew->OptimizeBandList())
753
0
        pNew.reset();
754
0
}
755
756
void vcl::Region::XOr( const tools::Rectangle& rRect )
757
0
{
758
0
    if ( rRect.IsEmpty() )
759
0
    {
760
        // empty rectangle will not change local content
761
0
        return;
762
0
    }
763
764
0
    if(IsEmpty())
765
0
    {
766
        // rRect will be the xored-form (local off, rect on)
767
0
        *this = rRect;
768
0
        return;
769
0
    }
770
771
0
    if(IsNull())
772
0
    {
773
        // error; cannot exclude from null region since this is not representable
774
        // in the data
775
0
        OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
776
0
        return;
777
0
    }
778
779
0
    if( HasPolyPolygonOrB2DPolyPolygon() )
780
0
    {
781
        // get this B2DPolyPolygon
782
0
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
783
784
0
        aThisPolyPoly = basegfx::utils::prepareForPolygonOperation( aThisPolyPoly );
785
786
0
        if(!aThisPolyPoly.count())
787
0
        {
788
            // no local content, XOr will be equal to rectangle
789
0
            *this = rRect;
790
0
            return;
791
0
        }
792
793
        // get the other B2DPolyPolygon
794
0
        const basegfx::B2DPolygon aRectPoly(
795
0
            basegfx::utils::createPolygonFromRect(
796
0
                vcl::unotools::b2DRectangleFromRectangle(rRect)));
797
0
        const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly);
798
0
        const basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationXor(aThisPolyPoly, aOtherPolyPoly);
799
800
0
        *this = vcl::Region(aClip);
801
802
0
        return;
803
0
    }
804
805
    // only region band mode possibility left here or null/empty
806
0
    const RegionBand* pCurrent = getRegionBand();
807
808
0
    if(!pCurrent)
809
0
    {
810
        // rRect will be the xored-form (local off, rect on)
811
0
        *this = rRect;
812
0
        return;
813
0
    }
814
815
    // only region band mode possibility left here or null/empty
816
0
    std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*getRegionBand()));
817
818
    // get justified rectangle
819
0
    const tools::Long nLeft(std::min(rRect.Left(), rRect.Right()));
820
0
    const tools::Long nTop(std::min(rRect.Top(), rRect.Bottom()));
821
0
    const tools::Long nRight(std::max(rRect.Left(), rRect.Right()));
822
0
    const tools::Long nBottom(std::max(rRect.Top(), rRect.Bottom()));
823
824
    // insert bands if the boundaries are not already in the list
825
0
    pNew->InsertBands(nTop, nBottom);
826
827
    // process xor
828
0
    pNew->XOr(nLeft, nTop, nRight, nBottom);
829
830
    // cleanup
831
0
    if(!pNew->OptimizeBandList())
832
0
    {
833
0
        pNew.reset();
834
0
    }
835
836
0
    mpRegionBand = std::move(pNew);
837
0
}
838
839
void vcl::Region::Union( const vcl::Region& rRegion )
840
0
{
841
0
    if(rRegion.IsEmpty())
842
0
    {
843
        // no extension at all
844
0
        return;
845
0
    }
846
847
0
    if(rRegion.IsNull())
848
0
    {
849
        // extending with null region -> null region
850
0
        *this = vcl::Region(true);
851
0
        return;
852
0
    }
853
854
0
    if(IsEmpty())
855
0
    {
856
        // local is empty, union will give source region
857
0
        *this = rRegion;
858
0
        return;
859
0
    }
860
861
0
    if(IsNull())
862
0
    {
863
        // already fully expanded (is null region), cannot be extended
864
0
        return;
865
0
    }
866
867
0
    if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
868
0
    {
869
        // get this B2DPolyPolygon
870
0
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
871
872
0
        aThisPolyPoly = basegfx::utils::prepareForPolygonOperation(aThisPolyPoly);
873
874
0
        if(!aThisPolyPoly.count())
875
0
        {
876
            // when no local content, union will be equal to rRegion
877
0
            *this = rRegion;
878
0
            return;
879
0
        }
880
881
        // get the other B2DPolyPolygon
882
0
        basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
883
0
        aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation(aOtherPolyPoly);
884
885
        // use logical OR operation
886
0
        basegfx::B2DPolyPolygon aClip(basegfx::utils::solvePolygonOperationOr(aThisPolyPoly, aOtherPolyPoly));
887
888
0
        *this = vcl::Region( aClip );
889
0
        return;
890
0
    }
891
892
    // only region band mode possibility left here or null/empty
893
0
    const RegionBand* pCurrent = getRegionBand();
894
895
0
    if(!pCurrent)
896
0
    {
897
        // local is empty, union will give source region
898
0
        *this = rRegion;
899
0
        return;
900
0
    }
901
902
0
    const RegionBand* pSource = rRegion.getRegionBand();
903
904
0
    if(!pSource)
905
0
    {
906
        // no extension at all
907
0
        return;
908
0
    }
909
910
    // prepare source and target
911
0
    std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));
912
913
    // union with source
914
0
    pNew->Union(*pSource);
915
916
    // cleanup
917
0
    if(!pNew->OptimizeBandList())
918
0
    {
919
0
        pNew.reset();
920
0
    }
921
922
0
    mpRegionBand = std::move(pNew);
923
0
}
924
925
void vcl::Region::Intersect( const vcl::Region& rRegion )
926
4.63M
{
927
    // same instance data? -> nothing to do!
928
4.63M
    if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
929
0
    {
930
0
        return;
931
0
    }
932
933
4.63M
    if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
934
0
    {
935
0
        return;
936
0
    }
937
938
4.63M
    if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
939
0
    {
940
0
        return;
941
0
    }
942
943
4.63M
    if(rRegion.IsNull())
944
0
    {
945
        // source region is null-region, intersect will not change local region
946
0
        return;
947
0
    }
948
949
4.63M
    if(IsNull())
950
167k
    {
951
        // when local region is null-region, intersect will be equal to source
952
167k
        *this = rRegion;
953
167k
        return;
954
167k
    }
955
956
4.46M
    if(rRegion.IsEmpty())
957
64
    {
958
        // source region is empty, intersection will always be empty
959
64
        SetEmpty();
960
64
        return;
961
64
    }
962
963
4.46M
    if(IsEmpty())
964
4.43M
    {
965
        // local region is empty, cannot get more empty than that. Nothing to do
966
4.43M
        return;
967
4.43M
    }
968
969
28.6k
    if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
970
1.26k
    {
971
        // get this B2DPolyPolygon
972
1.26k
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
973
974
1.26k
        if(!aThisPolyPoly.count())
975
60
        {
976
            // local region is empty, cannot get more empty than that. Nothing to do
977
60
            return;
978
60
        }
979
980
        // get the other B2DPolyPolygon
981
1.20k
        basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
982
983
1.20k
        if(!aOtherPolyPoly.count())
984
11
        {
985
            // source region is empty, intersection will always be empty
986
11
            SetEmpty();
987
11
            return;
988
11
        }
989
990
1.18k
        static size_t gPointLimit = !comphelper::IsFuzzing() ? SAL_MAX_SIZE : 8192;
991
1.18k
        size_t nPointLimit(gPointLimit);
992
1.18k
        const basegfx::B2DPolyPolygon aClip(
993
1.18k
            basegfx::utils::clipPolyPolygonOnPolyPolygon(
994
1.18k
                aOtherPolyPoly,
995
1.18k
                aThisPolyPoly,
996
1.18k
                true,
997
1.18k
                false,
998
1.18k
                &nPointLimit));
999
1.18k
        *this = vcl::Region( aClip );
1000
1.18k
        return;
1001
1.20k
    }
1002
1003
    // only region band mode possibility left here or null/empty
1004
27.3k
    const RegionBand* pCurrent = getRegionBand();
1005
1006
27.3k
    if(!pCurrent)
1007
0
    {
1008
        // local region is empty, cannot get more empty than that. Nothing to do
1009
0
        return;
1010
0
    }
1011
1012
27.3k
    const RegionBand* pSource = rRegion.getRegionBand();
1013
1014
27.3k
    if(!pSource)
1015
0
    {
1016
        // source region is empty, intersection will always be empty
1017
0
        SetEmpty();
1018
0
        return;
1019
0
    }
1020
1021
    // both RegionBands exist and are not empty
1022
27.3k
    if(pCurrent->getRectangleCount() + 2 < pSource->getRectangleCount())
1023
462
    {
1024
        // when we have less rectangles, turn around the call
1025
462
        vcl::Region aTempRegion = rRegion;
1026
462
        aTempRegion.Intersect( *this );
1027
462
        *this = std::move(aTempRegion);
1028
462
    }
1029
26.9k
    else
1030
26.9k
    {
1031
        // prepare new regionBand
1032
26.9k
        std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));
1033
1034
        // intersect with source
1035
26.9k
        pNew->Intersect(*pSource);
1036
1037
        // cleanup
1038
26.9k
        if(!pNew->OptimizeBandList())
1039
24.1k
        {
1040
24.1k
            pNew.reset();
1041
24.1k
        }
1042
1043
26.9k
        mpRegionBand = std::move(pNew);
1044
26.9k
    }
1045
27.3k
}
1046
1047
void vcl::Region::Exclude( const vcl::Region& rRegion )
1048
0
{
1049
0
    if ( rRegion.IsEmpty() )
1050
0
    {
1051
        // excluding nothing will do no change
1052
0
        return;
1053
0
    }
1054
1055
0
    if ( rRegion.IsNull() )
1056
0
    {
1057
        // excluding everything will create empty region
1058
0
        SetEmpty();
1059
0
        return;
1060
0
    }
1061
1062
0
    if(IsEmpty())
1063
0
    {
1064
        // cannot exclude from empty, done
1065
0
        return;
1066
0
    }
1067
1068
0
    if(IsNull())
1069
0
    {
1070
        // error; cannot exclude from null region since this is not representable
1071
        // in the data
1072
0
        OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
1073
0
        return;
1074
0
    }
1075
1076
0
    if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1077
0
    {
1078
        // get this B2DPolyPolygon
1079
0
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
1080
1081
0
        if(!aThisPolyPoly.count())
1082
0
        {
1083
            // cannot exclude from empty, done
1084
0
            return;
1085
0
        }
1086
1087
0
        aThisPolyPoly = basegfx::utils::prepareForPolygonOperation( aThisPolyPoly );
1088
1089
        // get the other B2DPolyPolygon
1090
0
        basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
1091
0
        aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation( aOtherPolyPoly );
1092
1093
0
        basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationDiff( aThisPolyPoly, aOtherPolyPoly );
1094
0
        *this = vcl::Region( aClip );
1095
0
        return;
1096
0
    }
1097
1098
    // only region band mode possibility left here or null/empty
1099
0
    const RegionBand* pCurrent = getRegionBand();
1100
1101
0
    if(!pCurrent)
1102
0
    {
1103
        // cannot exclude from empty, done
1104
0
        return;
1105
0
    }
1106
1107
0
    const RegionBand* pSource = rRegion.getRegionBand();
1108
1109
0
    if(!pSource)
1110
0
    {
1111
        // excluding nothing will do no change
1112
0
        return;
1113
0
    }
1114
1115
    // prepare source and target
1116
0
    std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));
1117
1118
    // union with source
1119
0
    const bool bSuccess(pNew->Exclude(*pSource));
1120
1121
    // cleanup
1122
0
    if(!bSuccess)
1123
0
    {
1124
0
        pNew.reset();
1125
0
    }
1126
1127
0
    mpRegionBand = std::move(pNew);
1128
0
}
1129
1130
bool vcl::Region::XOr( const vcl::Region& rRegion )
1131
0
{
1132
0
    if ( rRegion.IsEmpty() )
1133
0
    {
1134
        // empty region will not change local content
1135
0
        return true;
1136
0
    }
1137
1138
0
    if ( rRegion.IsNull() )
1139
0
    {
1140
        // error; cannot exclude null region from local since this is not representable
1141
        // in the data
1142
0
        OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1143
0
        return true;
1144
0
    }
1145
1146
0
    if(IsEmpty())
1147
0
    {
1148
        // rRect will be the xored-form (local off, rect on)
1149
0
        *this = rRegion;
1150
0
        return true;
1151
0
    }
1152
1153
0
    if(IsNull())
1154
0
    {
1155
        // error: cannot exclude from null region since this is not representable
1156
        // in the data
1157
0
        OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1158
0
        return false;
1159
0
    }
1160
1161
0
    if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1162
0
    {
1163
        // get this B2DPolyPolygon
1164
0
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
1165
1166
0
        if(!aThisPolyPoly.count())
1167
0
        {
1168
            // rRect will be the xored-form (local off, rect on)
1169
0
            *this = rRegion;
1170
0
            return true;
1171
0
        }
1172
1173
0
        aThisPolyPoly = basegfx::utils::prepareForPolygonOperation( aThisPolyPoly );
1174
1175
        // get the other B2DPolyPolygon
1176
0
        basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
1177
0
        aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation( aOtherPolyPoly );
1178
1179
0
        basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationXor( aThisPolyPoly, aOtherPolyPoly );
1180
0
        *this = vcl::Region( aClip );
1181
0
        return true;
1182
0
    }
1183
1184
    // only region band mode possibility left here or null/empty
1185
0
    const RegionBand* pCurrent = getRegionBand();
1186
1187
0
    if(!pCurrent)
1188
0
    {
1189
        // rRect will be the xored-form (local off, rect on)
1190
0
        *this = rRegion;
1191
0
        return true;
1192
0
    }
1193
1194
0
    const RegionBand* pSource = rRegion.getRegionBand();
1195
1196
0
    if(!pSource)
1197
0
    {
1198
        // empty region will not change local content
1199
0
        return true;
1200
0
    }
1201
1202
    // prepare source and target
1203
0
    std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));
1204
1205
    // union with source
1206
0
    pNew->XOr(*pSource);
1207
1208
    // cleanup
1209
0
    if(!pNew->OptimizeBandList())
1210
0
    {
1211
0
        pNew.reset();
1212
0
    }
1213
1214
0
    mpRegionBand = std::move(pNew);
1215
1216
0
    return true;
1217
0
}
1218
1219
tools::Rectangle vcl::Region::GetBoundRect() const
1220
178k
{
1221
178k
    if(IsEmpty())
1222
43
    {
1223
        // no internal data? -> region is empty!
1224
43
        return tools::Rectangle();
1225
43
    }
1226
1227
178k
    if(IsNull())
1228
6.00k
    {
1229
        // error; null region has no BoundRect
1230
        // OSL_ENSURE(false, "Region::GetBoundRect error: null region has unlimited bound rect, not representable (!)");
1231
6.00k
        return tools::Rectangle();
1232
6.00k
    }
1233
1234
    // prefer double precision source
1235
172k
    if(getB2DPolyPolygon())
1236
0
    {
1237
0
        const basegfx::B2DRange aRange(getB2DPolyPolygon()->getB2DRange());
1238
1239
0
        if(aRange.isEmpty())
1240
0
        {
1241
            // emulate PolyPolygon::GetBoundRect() when empty polygon
1242
0
            return tools::Rectangle();
1243
0
        }
1244
0
        else
1245
0
        {
1246
            // #i122149# corrected rounding, no need for ceil() and floor() here
1247
0
            return tools::Rectangle(
1248
0
                basegfx::fround<tools::Long>(aRange.getMinX()), basegfx::fround<tools::Long>(aRange.getMinY()),
1249
0
                basegfx::fround<tools::Long>(aRange.getMaxX()), basegfx::fround<tools::Long>(aRange.getMaxY()));
1250
0
        }
1251
0
    }
1252
1253
172k
    if(getPolyPolygon())
1254
1.62k
    {
1255
1.62k
        return getPolyPolygon()->GetBoundRect();
1256
1.62k
    }
1257
1258
170k
    if(getRegionBand())
1259
170k
    {
1260
170k
        return getRegionBand()->GetBoundRect();
1261
170k
    }
1262
1263
0
    return tools::Rectangle();
1264
170k
}
1265
1266
tools::PolyPolygon vcl::Region::GetAsPolyPolygon() const
1267
0
{
1268
0
    if(getPolyPolygon())
1269
0
    {
1270
0
        return *getPolyPolygon();
1271
0
    }
1272
1273
0
    if(getB2DPolyPolygon())
1274
0
    {
1275
        // the polygon needs to be converted, buffer the down conversion
1276
0
        const tools::PolyPolygon aPolyPolgon(*getB2DPolyPolygon());
1277
0
        const_cast< vcl::Region* >(this)->mpPolyPolygon = aPolyPolgon;
1278
1279
0
        return *getPolyPolygon();
1280
0
    }
1281
1282
0
    if(getRegionBand())
1283
0
    {
1284
        // the BandRegion needs to be converted, buffer the conversion
1285
0
        const tools::PolyPolygon aPolyPolgon(ImplCreatePolyPolygonFromRegionBand());
1286
0
        const_cast< vcl::Region* >(this)->mpPolyPolygon = aPolyPolgon;
1287
1288
0
        return *getPolyPolygon();
1289
0
    }
1290
1291
0
    return tools::PolyPolygon();
1292
0
}
1293
1294
basegfx::B2DPolyPolygon vcl::Region::GetAsB2DPolyPolygon() const
1295
170k
{
1296
170k
    if(getB2DPolyPolygon())
1297
117
    {
1298
117
        return *getB2DPolyPolygon();
1299
117
    }
1300
1301
170k
    if(getPolyPolygon())
1302
1.18k
    {
1303
        // the polygon needs to be converted, buffer the up conversion. This will be preferred from now.
1304
1.18k
        const basegfx::B2DPolyPolygon aB2DPolyPolygon(getPolyPolygon()->getB2DPolyPolygon());
1305
1.18k
        const_cast< vcl::Region* >(this)->mpB2DPolyPolygon = aB2DPolyPolygon;
1306
1307
1.18k
        return *getB2DPolyPolygon();
1308
1.18k
    }
1309
1310
168k
    if(getRegionBand())
1311
168k
    {
1312
        // the BandRegion needs to be converted, buffer the conversion
1313
168k
        const basegfx::B2DPolyPolygon aB2DPolyPolygon(ImplCreateB2DPolyPolygonFromRegionBand());
1314
168k
        const_cast< vcl::Region* >(this)->mpB2DPolyPolygon = aB2DPolyPolygon;
1315
1316
168k
        return *getB2DPolyPolygon();
1317
168k
    }
1318
1319
0
    return basegfx::B2DPolyPolygon();
1320
168k
}
1321
1322
const RegionBand* vcl::Region::GetAsRegionBand() const
1323
9.04M
{
1324
9.04M
    if(!getRegionBand())
1325
6.08M
    {
1326
6.08M
        if(getB2DPolyPolygon())
1327
1.37k
        {
1328
            // convert B2DPolyPolygon to RegionBand, buffer it and return it
1329
1.37k
            const_cast< vcl::Region* >(this)->mpRegionBand = ImplCreateRegionBandFromPolyPolygon(tools::PolyPolygon(*getB2DPolyPolygon()));
1330
1.37k
        }
1331
6.08M
        else if(getPolyPolygon())
1332
216
        {
1333
            // convert B2DPolyPolygon to RegionBand, buffer it and return it
1334
216
            const_cast< vcl::Region* >(this)->mpRegionBand = ImplCreateRegionBandFromPolyPolygon(*getPolyPolygon());
1335
216
        }
1336
6.08M
    }
1337
1338
9.04M
    return getRegionBand();
1339
9.04M
}
1340
1341
bool vcl::Region::Contains( const Point& rPoint ) const
1342
0
{
1343
0
    if(IsEmpty())
1344
0
    {
1345
        // no point can be in empty region
1346
0
        return false;
1347
0
    }
1348
1349
0
    if(IsNull())
1350
0
    {
1351
        // all points are inside null-region
1352
0
        return true;
1353
0
    }
1354
1355
    // Too expensive (?)
1356
    //if(mpImplRegion->getRegionPolyPoly())
1357
    //{
1358
    //  return mpImplRegion->getRegionPolyPoly()->Contains( rPoint );
1359
    //}
1360
1361
    // ensure RegionBand existence
1362
0
    const RegionBand* pRegionBand = GetAsRegionBand();
1363
1364
0
    if(pRegionBand)
1365
0
    {
1366
0
        return pRegionBand->Contains(rPoint);
1367
0
    }
1368
1369
0
    return false;
1370
0
}
1371
1372
bool vcl::Region::Overlaps( const tools::Rectangle& rRect ) const
1373
4.46M
{
1374
4.46M
    if(IsEmpty())
1375
0
    {
1376
        // nothing can be over something empty
1377
0
        return false;
1378
0
    }
1379
1380
4.46M
    if(IsNull())
1381
0
    {
1382
        // everything is over null region
1383
0
        return true;
1384
0
    }
1385
1386
    // Can we optimize this ??? - is used in StarDraw for brushes pointers
1387
    // Why we have no IsOver for Regions ???
1388
    // create region from rectangle and intersect own region
1389
4.46M
    vcl::Region aRegion(rRect);
1390
4.46M
    aRegion.Intersect( *this );
1391
1392
    // rectangle is over if include is not empty
1393
4.46M
    return !aRegion.IsEmpty();
1394
4.46M
}
1395
1396
bool vcl::Region::IsRectangle() const
1397
0
{
1398
0
    if( IsEmpty() || IsNull() )
1399
0
        return false;
1400
1401
0
    if( getB2DPolyPolygon() )
1402
0
        return basegfx::utils::isRectangle( *getB2DPolyPolygon() );
1403
1404
0
    if( getPolyPolygon() )
1405
0
        return getPolyPolygon()->IsRect();
1406
1407
0
    if( getRegionBand() )
1408
0
        return (getRegionBand()->getRectangleCount() == 1);
1409
1410
0
    return false;
1411
0
}
1412
1413
void vcl::Region::SetNull()
1414
595k
{
1415
    // reset all content
1416
595k
    mpB2DPolyPolygon.reset();
1417
595k
    mpPolyPolygon.reset();
1418
595k
    mpRegionBand.reset();
1419
595k
    mbIsNull = true;
1420
595k
}
1421
1422
void vcl::Region::SetEmpty()
1423
22.4k
{
1424
    // reset all content
1425
22.4k
    mpB2DPolyPolygon.reset();
1426
22.4k
    mpPolyPolygon.reset();
1427
22.4k
    mpRegionBand.reset();
1428
22.4k
    mbIsNull = false;
1429
22.4k
}
1430
1431
430k
Region& vcl::Region::operator=( const vcl::Region& ) = default;
1432
1433
Region& vcl::Region::operator=( vcl::Region&& rRegion ) noexcept
1434
415k
{
1435
415k
    mpB2DPolyPolygon = std::move(rRegion.mpB2DPolyPolygon);
1436
415k
    mpPolyPolygon = std::move(rRegion.mpPolyPolygon);
1437
415k
    mpRegionBand = std::move(rRegion.mpRegionBand);
1438
415k
    mbIsNull = rRegion.mbIsNull;
1439
415k
    rRegion.mbIsNull = true;
1440
1441
415k
    return *this;
1442
415k
}
1443
1444
Region& vcl::Region::operator=( const tools::Rectangle& rRect )
1445
193k
{
1446
193k
    mpB2DPolyPolygon.reset();
1447
193k
    mpPolyPolygon.reset();
1448
193k
    if (!rRect.IsEmpty())
1449
193k
        mpRegionBand = std::make_shared<RegionBand>(rRect);
1450
0
    else
1451
0
        mpRegionBand.reset();
1452
193k
    mbIsNull = false;
1453
1454
193k
    return *this;
1455
193k
}
1456
1457
bool vcl::Region::operator==( const vcl::Region& rRegion ) const
1458
2
{
1459
2
    if(IsNull() && rRegion.IsNull())
1460
0
    {
1461
        // both are null region
1462
0
        return true;
1463
0
    }
1464
1465
2
    if(IsEmpty() && rRegion.IsEmpty())
1466
0
    {
1467
        // both are empty
1468
0
        return true;
1469
0
    }
1470
1471
2
    if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
1472
0
    {
1473
        // same instance data? -> equal
1474
0
        return true;
1475
0
    }
1476
1477
2
    if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
1478
0
    {
1479
        // same instance data? -> equal
1480
0
        return true;
1481
0
    }
1482
1483
2
    if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
1484
0
    {
1485
        // same instance data? -> equal
1486
0
        return true;
1487
0
    }
1488
1489
2
    if(IsNull() || IsEmpty())
1490
0
    {
1491
0
        return false;
1492
0
    }
1493
1494
2
    if(rRegion.IsNull() || rRegion.IsEmpty())
1495
0
    {
1496
0
        return false;
1497
0
    }
1498
1499
2
    if(rRegion.getB2DPolyPolygon() || getB2DPolyPolygon())
1500
0
    {
1501
        // one of both has a B2DPolyPolygon based region, ensure both have it
1502
        // by evtl. conversion
1503
0
        GetAsB2DPolyPolygon();
1504
0
        rRegion.GetAsB2DPolyPolygon();
1505
1506
0
        return *rRegion.getB2DPolyPolygon() == *getB2DPolyPolygon();
1507
0
    }
1508
1509
2
    if(rRegion.getPolyPolygon() || getPolyPolygon())
1510
0
    {
1511
        // one of both has a B2DPolyPolygon based region, ensure both have it
1512
        // by evtl. conversion
1513
0
        GetAsPolyPolygon();
1514
0
        rRegion.GetAsPolyPolygon();
1515
1516
0
        return *rRegion.getPolyPolygon() == *getPolyPolygon();
1517
0
    }
1518
1519
    // both are not empty or null (see above) and if content supported polygon
1520
    // data the comparison is already done. Only both on RegionBand base can be left,
1521
    // but better check
1522
2
    if(rRegion.getRegionBand() && getRegionBand())
1523
2
    {
1524
2
        return *rRegion.getRegionBand() == *getRegionBand();
1525
2
    }
1526
1527
    // should not happen, but better deny equality
1528
0
    return false;
1529
2
}
1530
1531
SvStream& ReadRegion(SvStream& rIStrm, vcl::Region& rRegion)
1532
20.7k
{
1533
20.7k
    VersionCompatRead aCompat(rIStrm);
1534
20.7k
    sal_uInt16 nVersion(0);
1535
20.7k
    sal_uInt16 nTmp16(0);
1536
1537
    // clear region to be loaded
1538
20.7k
    rRegion.SetEmpty();
1539
1540
    // get version of streamed region
1541
20.7k
    rIStrm.ReadUInt16( nVersion );
1542
1543
    // get type of region
1544
20.7k
    rIStrm.ReadUInt16( nTmp16 );
1545
1546
20.7k
    enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX };
1547
20.7k
    auto eStreamedType = nTmp16;
1548
1549
20.7k
    switch (eStreamedType)
1550
20.7k
    {
1551
10.7k
        case REGION_NULL:
1552
10.7k
        {
1553
10.7k
            rRegion.SetNull();
1554
10.7k
            break;
1555
0
        }
1556
1557
70
        case REGION_EMPTY:
1558
70
        {
1559
70
            rRegion.SetEmpty();
1560
70
            break;
1561
0
        }
1562
1563
9.94k
        default:
1564
9.94k
        {
1565
9.94k
            std::shared_ptr<RegionBand> xNewRegionBand(std::make_shared<RegionBand>());
1566
9.94k
            bool bSuccess = xNewRegionBand->load(rIStrm);
1567
9.94k
            rRegion.mpRegionBand = std::move(xNewRegionBand);
1568
1569
9.94k
            bool bHasPolyPolygon(false);
1570
9.94k
            if (aCompat.GetVersion() >= 2)
1571
2.11k
            {
1572
2.11k
                rIStrm.ReadCharAsBool( bHasPolyPolygon );
1573
1574
2.11k
                if (bHasPolyPolygon)
1575
1.73k
                {
1576
1.73k
                    tools::PolyPolygon aNewPoly;
1577
1.73k
                    ReadPolyPolygon(rIStrm, aNewPoly);
1578
1.73k
                    const auto nPolygons = aNewPoly.Count();
1579
1.73k
                    if (nPolygons > 128)
1580
0
                    {
1581
0
                        SAL_WARN("vcl.gdi", "suspiciously high no of polygons in clip:" << nPolygons);
1582
0
                        if (comphelper::IsFuzzing())
1583
0
                            aNewPoly.Clear();
1584
0
                    }
1585
1.73k
                    rRegion.mpPolyPolygon = aNewPoly;
1586
1.73k
                }
1587
2.11k
            }
1588
1589
9.94k
            if (!bSuccess && !bHasPolyPolygon)
1590
1.38k
            {
1591
1.38k
                SAL_WARN("vcl.gdi", "bad region band:" << bHasPolyPolygon);
1592
1.38k
                rRegion.SetNull();
1593
1.38k
            }
1594
1595
9.94k
            break;
1596
9.94k
        }
1597
20.7k
    }
1598
1599
20.7k
    return rIStrm;
1600
20.7k
}
1601
1602
SvStream& WriteRegion( SvStream& rOStrm, const vcl::Region& rRegion )
1603
0
{
1604
0
    const sal_uInt16 nVersion(2);
1605
0
    VersionCompatWrite aCompat(rOStrm, nVersion);
1606
1607
    // put version
1608
0
    rOStrm.WriteUInt16( nVersion );
1609
1610
    // put type
1611
0
    enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX };
1612
0
    RegionType aRegionType(REGION_COMPLEX);
1613
0
    bool bEmpty(rRegion.IsEmpty());
1614
1615
0
    if(!bEmpty && rRegion.getB2DPolyPolygon() && 0 == rRegion.getB2DPolyPolygon()->count())
1616
0
    {
1617
0
        OSL_ENSURE(false, "Region with empty B2DPolyPolygon, should not be created (!)");
1618
0
        bEmpty = true;
1619
0
    }
1620
1621
0
    if(!bEmpty && rRegion.getPolyPolygon() && 0 == rRegion.getPolyPolygon()->Count())
1622
0
    {
1623
0
        OSL_ENSURE(false, "Region with empty PolyPolygon, should not be created (!)");
1624
0
        bEmpty = true;
1625
0
    }
1626
1627
0
    if(bEmpty)
1628
0
    {
1629
0
        aRegionType = REGION_EMPTY;
1630
0
    }
1631
0
    else if(rRegion.IsNull())
1632
0
    {
1633
0
        aRegionType = REGION_NULL;
1634
0
    }
1635
0
    else if(rRegion.getRegionBand() && rRegion.getRegionBand()->isSingleRectangle())
1636
0
    {
1637
0
        aRegionType = REGION_RECTANGLE;
1638
0
    }
1639
1640
0
    rOStrm.WriteUInt16( aRegionType );
1641
1642
    // get RegionBand
1643
0
    const RegionBand* pRegionBand = rRegion.getRegionBand();
1644
1645
0
    if(pRegionBand)
1646
0
    {
1647
0
        pRegionBand->save(rOStrm);
1648
0
    }
1649
0
    else
1650
0
    {
1651
        // for compatibility, write an empty RegionBand (will only write
1652
        // the end marker STREAMENTRY_END, but this *is* needed)
1653
0
        const RegionBand aRegionBand;
1654
1655
0
        aRegionBand.save(rOStrm);
1656
0
    }
1657
1658
    // write polypolygon if available
1659
0
    const bool bHasPolyPolygon(rRegion.HasPolyPolygonOrB2DPolyPolygon());
1660
0
    rOStrm.WriteBool( bHasPolyPolygon );
1661
1662
0
    if(bHasPolyPolygon)
1663
0
    {
1664
        // #i105373#
1665
0
        tools::PolyPolygon aNoCurvePolyPolygon;
1666
0
        rRegion.GetAsPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon);
1667
1668
0
        WritePolyPolygon( rOStrm, aNoCurvePolyPolygon );
1669
0
    }
1670
1671
0
    return rOStrm;
1672
0
}
1673
1674
void vcl::Region::GetRegionRectangles(RectangleVector& rTarget) const
1675
9.04M
{
1676
    // clear returnvalues
1677
9.04M
    rTarget.clear();
1678
1679
    // ensure RegionBand existence
1680
9.04M
    const RegionBand* pRegionBand = GetAsRegionBand();
1681
1682
9.04M
    if(pRegionBand)
1683
2.95M
    {
1684
2.95M
        pRegionBand->GetRegionRectangles(rTarget);
1685
2.95M
    }
1686
9.04M
}
1687
1688
static bool ImplPolygonRectTest( const tools::Polygon& rPoly, tools::Rectangle* pRectOut = nullptr )
1689
0
{
1690
0
    bool bIsRect = false;
1691
0
    const Point* pPoints = rPoly.GetConstPointAry();
1692
0
    sal_uInt16 nPoints = rPoly.GetSize();
1693
1694
0
    if( nPoints == 4 || (nPoints == 5 && pPoints[0] == pPoints[4]) )
1695
0
    {
1696
0
        tools::Long nX1 = pPoints[0].X(), nX2 = pPoints[2].X(), nY1 = pPoints[0].Y(), nY2 = pPoints[2].Y();
1697
1698
0
        if( ( (pPoints[1].X() == nX1 && pPoints[3].X() == nX2) && (pPoints[1].Y() == nY2 && pPoints[3].Y() == nY1) )
1699
0
         || ( (pPoints[1].X() == nX2 && pPoints[3].X() == nX1) && (pPoints[1].Y() == nY1 && pPoints[3].Y() == nY2) ) )
1700
0
        {
1701
0
            bIsRect = true;
1702
1703
0
            if( pRectOut )
1704
0
            {
1705
0
                tools::Long nSwap;
1706
1707
0
                if( nX2 < nX1 )
1708
0
                {
1709
0
                    nSwap = nX2;
1710
0
                    nX2 = nX1;
1711
0
                    nX1 = nSwap;
1712
0
                }
1713
1714
0
                if( nY2 < nY1 )
1715
0
                {
1716
0
                    nSwap = nY2;
1717
0
                    nY2 = nY1;
1718
0
                    nY1 = nSwap;
1719
0
                }
1720
1721
0
                if( nX2 != nX1 )
1722
0
                {
1723
0
                    nX2--;
1724
0
                }
1725
1726
0
                if( nY2 != nY1 )
1727
0
                {
1728
0
                    nY2--;
1729
0
                }
1730
1731
0
                pRectOut->SetLeft( nX1 );
1732
0
                pRectOut->SetRight( nX2 );
1733
0
                pRectOut->SetTop( nY1 );
1734
0
                pRectOut->SetBottom( nY2 );
1735
0
            }
1736
0
        }
1737
0
    }
1738
1739
0
    return bIsRect;
1740
0
}
1741
1742
vcl::Region vcl::Region::GetRegionFromPolyPolygon( const tools::PolyPolygon& rPolyPoly )
1743
0
{
1744
    //return vcl::Region( rPolyPoly );
1745
1746
    // check if it's worth extracting the XOr'ing the Rectangles
1747
    // empiricism shows that break even between XOr'ing rectangles separately
1748
    // and ImplCreateRegionBandFromPolyPolygon is at half rectangles/half polygons
1749
0
    int nPolygonRects = 0, nPolygonPolygons = 0;
1750
0
    int nPolygons = rPolyPoly.Count();
1751
1752
0
    for( int i = 0; i < nPolygons; i++ )
1753
0
    {
1754
0
        const tools::Polygon& rPoly = rPolyPoly[i];
1755
1756
0
        if( ImplPolygonRectTest( rPoly ) )
1757
0
        {
1758
0
            nPolygonRects++;
1759
0
        }
1760
0
        else
1761
0
        {
1762
0
            nPolygonPolygons++;
1763
0
        }
1764
0
    }
1765
1766
0
    if( nPolygonPolygons > nPolygonRects )
1767
0
    {
1768
0
        return vcl::Region( rPolyPoly );
1769
0
    }
1770
1771
0
    vcl::Region aResult;
1772
0
    tools::Rectangle aRect;
1773
1774
0
    for( int i = 0; i < nPolygons; i++ )
1775
0
    {
1776
0
        const tools::Polygon& rPoly = rPolyPoly[i];
1777
1778
0
        if( ImplPolygonRectTest( rPoly, &aRect ) )
1779
0
        {
1780
0
            aResult.XOr( aRect );
1781
0
        }
1782
0
        else
1783
0
        {
1784
0
            aResult.XOr( vcl::Region(rPoly) );
1785
0
        }
1786
0
    }
1787
1788
0
    return aResult;
1789
0
}
1790
1791
} /* namespace vcl */
1792
1793
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */