Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/gdi/regband.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 <sal/config.h>
21
22
#include <basegfx/numeric/ftools.hxx>
23
#include <tools/helpers.hxx>
24
#include <osl/diagnose.h>
25
#include <sal/log.hxx>
26
27
#include <regband.hxx>
28
29
// ImplRegionBand
30
31
// Each band contains all rectangles between upper and lower border.
32
// For Union, Intersect, Xor and Exclude operations rectangles of
33
// equal height are evaluated. The borders of the bands should always
34
// be chosen such that this is possible.
35
36
// If possible, rectangles within the bands are condensed.
37
38
// When converting polygons all points of the polygon are registered
39
// in the individual bands (for each band they are stored as
40
// points in a list). After registration of these points they are
41
// converted to rectangles and the points in the list are deleted.
42
43
ImplRegionBand::ImplRegionBand( tools::Long nTop, tools::Long nBottom )
44
480k
{
45
    // save boundaries
46
480k
    mnYTop              = nTop;
47
480k
    mnYBottom           = nBottom;
48
49
    // initialize lists
50
480k
    mpNextBand          = nullptr;
51
480k
    mpPrevBand          = nullptr;
52
480k
    mpFirstSep          = nullptr;
53
480k
    mpFirstBandPoint    = nullptr;
54
480k
    mbTouched           = false;
55
480k
}
56
57
ImplRegionBand::ImplRegionBand(
58
    const ImplRegionBand& rRegionBand,
59
    const bool bIgnorePoints)
60
192k
{
61
    // copy boundaries
62
192k
    mnYTop              = rRegionBand.mnYTop;
63
192k
    mnYBottom           = rRegionBand.mnYBottom;
64
192k
    mbTouched           = rRegionBand.mbTouched;
65
66
    // initialisation
67
192k
    mpNextBand          = nullptr;
68
192k
    mpPrevBand          = nullptr;
69
192k
    mpFirstSep          = nullptr;
70
192k
    mpFirstBandPoint    = nullptr;
71
72
    // copy all elements of the list with separations
73
192k
    ImplRegionBandSep* pNewSep;
74
192k
    ImplRegionBandSep* pPrevSep = nullptr;
75
192k
    ImplRegionBandSep* pSep = rRegionBand.mpFirstSep;
76
347k
    while ( pSep )
77
155k
    {
78
        // create new and copy data
79
155k
        pNewSep             = new ImplRegionBandSep;
80
155k
        pNewSep->mnXLeft    = pSep->mnXLeft;
81
155k
        pNewSep->mnXRight   = pSep->mnXRight;
82
155k
        pNewSep->mbRemoved  = pSep->mbRemoved;
83
155k
        pNewSep->mpNextSep  = nullptr;
84
155k
        if ( pSep == rRegionBand.mpFirstSep )
85
152k
            mpFirstSep = pNewSep;
86
3.12k
        else
87
3.12k
            pPrevSep->mpNextSep = pNewSep;
88
89
155k
        pPrevSep = pNewSep;
90
155k
        pSep = pSep->mpNextSep;
91
155k
    }
92
93
192k
    if (  bIgnorePoints)
94
192k
        return;
95
96
    // Copy points.
97
0
    ImplRegionBandPoint* pPoint = rRegionBand.mpFirstBandPoint;
98
0
    ImplRegionBandPoint* pPrevPointCopy = nullptr;
99
0
    while (pPoint != nullptr)
100
0
    {
101
0
        ImplRegionBandPoint* pPointCopy = new ImplRegionBandPoint;
102
0
        pPointCopy->mpNextBandPoint = nullptr;
103
0
        pPointCopy->mnX = pPoint->mnX;
104
0
        pPointCopy->mnLineId = pPoint->mnLineId;
105
0
        pPointCopy->mbEndPoint = pPoint->mbEndPoint;
106
0
        pPointCopy->meLineType = pPoint->meLineType;
107
108
0
        if (pPrevPointCopy != nullptr)
109
0
            pPrevPointCopy->mpNextBandPoint = pPointCopy;
110
0
        else
111
0
            mpFirstBandPoint = pPointCopy;
112
113
0
        pPrevPointCopy = pPointCopy;
114
0
        pPoint = pPoint->mpNextBandPoint;
115
0
    }
116
0
}
117
118
ImplRegionBand::~ImplRegionBand()
119
672k
{
120
672k
    SAL_WARN_IF( mpFirstBandPoint != nullptr, "vcl", "ImplRegionBand::~ImplRegionBand -> pointlist not empty" );
121
122
    // delete elements of the list
123
672k
    ImplRegionBandSep* pSep = mpFirstSep;
124
1.15M
    while ( pSep )
125
480k
    {
126
480k
        ImplRegionBandSep* pTempSep = pSep->mpNextSep;
127
480k
        delete pSep;
128
480k
        pSep = pTempSep;
129
480k
    }
130
131
    // delete elements of the list
132
672k
    ImplRegionBandPoint* pPoint = mpFirstBandPoint;
133
672k
    while ( pPoint )
134
0
    {
135
0
        ImplRegionBandPoint* pTempPoint = pPoint->mpNextBandPoint;
136
0
        delete pPoint;
137
0
        pPoint = pTempPoint;
138
0
    }
139
672k
}
140
141
// generate separations from lines and process union with existing
142
// separations
143
144
void ImplRegionBand::ProcessPoints()
145
3.37k
{
146
    // check Pointlist
147
3.37k
    ImplRegionBandPoint* pRegionBandPoint = mpFirstBandPoint;
148
10.5k
    while ( pRegionBandPoint )
149
7.18k
    {
150
        // within list?
151
7.18k
        if ( pRegionBandPoint->mpNextBandPoint )
152
5.47k
        {
153
            // start/stop?
154
5.47k
            if ( pRegionBandPoint->mbEndPoint && pRegionBandPoint->mpNextBandPoint->mbEndPoint )
155
5.47k
            {
156
                // same direction? -> remove next point!
157
5.47k
                if ( pRegionBandPoint->meLineType == pRegionBandPoint->mpNextBandPoint->meLineType )
158
528
                {
159
528
                    ImplRegionBandPoint* pSaveRegionBandPoint = pRegionBandPoint->mpNextBandPoint;
160
528
                    pRegionBandPoint->mpNextBandPoint = pRegionBandPoint->mpNextBandPoint->mpNextBandPoint;
161
528
                    delete pSaveRegionBandPoint;
162
528
                }
163
5.47k
            }
164
5.47k
        }
165
166
        // continue with next element in the list
167
7.18k
        pRegionBandPoint = pRegionBandPoint->mpNextBandPoint;
168
7.18k
    }
169
170
3.37k
    pRegionBandPoint = mpFirstBandPoint;
171
6.89k
    while ( pRegionBandPoint && pRegionBandPoint->mpNextBandPoint )
172
3.51k
    {
173
3.51k
        Union( pRegionBandPoint->mnX, pRegionBandPoint->mpNextBandPoint->mnX );
174
175
3.51k
        ImplRegionBandPoint* pNextBandPoint = pRegionBandPoint->mpNextBandPoint->mpNextBandPoint;
176
177
        // remove already processed points
178
3.51k
        delete pRegionBandPoint->mpNextBandPoint;
179
3.51k
        delete pRegionBandPoint;
180
181
        // continue with next element in the list
182
3.51k
        pRegionBandPoint = pNextBandPoint;
183
3.51k
    }
184
185
    // remove last element if necessary
186
3.37k
    delete pRegionBandPoint;
187
188
    // list is now empty
189
3.37k
    mpFirstBandPoint = nullptr;
190
3.37k
}
191
192
// generate separations from lines and process union with existing
193
// separations
194
195
bool ImplRegionBand::InsertPoint( tools::Long nX, tools::Long nLineId,
196
                                  bool bEndPoint, LineType eLineType )
197
7.71k
{
198
7.71k
    if ( !mpFirstBandPoint )
199
1.82k
    {
200
1.82k
        mpFirstBandPoint                  = new ImplRegionBandPoint;
201
1.82k
        mpFirstBandPoint->mnX             = nX;
202
1.82k
        mpFirstBandPoint->mnLineId        = nLineId;
203
1.82k
        mpFirstBandPoint->mbEndPoint      = bEndPoint;
204
1.82k
        mpFirstBandPoint->meLineType      = eLineType;
205
1.82k
        mpFirstBandPoint->mpNextBandPoint = nullptr;
206
1.82k
        return true;
207
1.82k
    }
208
209
    // look if line already touched the band
210
5.88k
    ImplRegionBandPoint* pRegionBandPoint = mpFirstBandPoint;
211
5.88k
    ImplRegionBandPoint* pLastTestedRegionBandPoint = nullptr;
212
19.1k
    while( pRegionBandPoint )
213
13.2k
    {
214
13.2k
        if ( pRegionBandPoint->mnLineId == nLineId )
215
0
        {
216
0
            if ( bEndPoint )
217
0
            {
218
0
                if( !pRegionBandPoint->mbEndPoint )
219
0
                {
220
                    // remove old band point
221
0
                    if( !mpFirstBandPoint->mpNextBandPoint )
222
0
                    {
223
                        // if we've only got one point => replace first point
224
0
                        pRegionBandPoint->mnX = nX;
225
0
                        pRegionBandPoint->mbEndPoint = true;
226
0
                        return true;
227
0
                    }
228
0
                    else
229
0
                    {
230
                        // remove current point
231
0
                        if( !pLastTestedRegionBandPoint )
232
0
                        {
233
                            // remove and delete old first point
234
0
                            ImplRegionBandPoint* pSaveBandPoint = mpFirstBandPoint;
235
0
                            mpFirstBandPoint = mpFirstBandPoint->mpNextBandPoint;
236
0
                            delete pSaveBandPoint;
237
0
                        }
238
0
                        else
239
0
                        {
240
                            // remove and delete current band point
241
0
                            pLastTestedRegionBandPoint->mpNextBandPoint = pRegionBandPoint->mpNextBandPoint;
242
0
                            delete pRegionBandPoint;
243
0
                        }
244
245
0
                        break;
246
0
                    }
247
0
                }
248
0
            }
249
0
            else
250
0
                return false;
251
0
        }
252
253
        // use next element
254
13.2k
        pLastTestedRegionBandPoint = pRegionBandPoint;
255
13.2k
        pRegionBandPoint = pRegionBandPoint->mpNextBandPoint;
256
13.2k
    }
257
258
    // search appropriate position and insert point into the list
259
5.88k
    ImplRegionBandPoint* pNewRegionBandPoint;
260
261
5.88k
    pRegionBandPoint = mpFirstBandPoint;
262
5.88k
    pLastTestedRegionBandPoint = nullptr;
263
6.81k
    while ( pRegionBandPoint )
264
6.45k
    {
265
        // new point completely left? -> insert as first point
266
6.45k
        if ( nX <= pRegionBandPoint->mnX )
267
5.53k
        {
268
5.53k
            pNewRegionBandPoint                     = new ImplRegionBandPoint;
269
5.53k
            pNewRegionBandPoint->mnX                = nX;
270
5.53k
            pNewRegionBandPoint->mnLineId           = nLineId;
271
5.53k
            pNewRegionBandPoint->mbEndPoint         = bEndPoint;
272
5.53k
            pNewRegionBandPoint->meLineType         = eLineType;
273
5.53k
            pNewRegionBandPoint->mpNextBandPoint    = pRegionBandPoint;
274
275
            // connections to the new point
276
5.53k
            if ( !pLastTestedRegionBandPoint )
277
5.43k
                mpFirstBandPoint = pNewRegionBandPoint;
278
102
            else
279
102
                pLastTestedRegionBandPoint->mpNextBandPoint = pNewRegionBandPoint;
280
281
5.53k
            return true;
282
5.53k
        }
283
284
        // use next element
285
922
        pLastTestedRegionBandPoint = pRegionBandPoint;
286
922
        pRegionBandPoint = pRegionBandPoint->mpNextBandPoint;
287
922
    }
288
289
    // not inserted -> add to the end of the list
290
357
    pNewRegionBandPoint                     = new ImplRegionBandPoint;
291
357
    pNewRegionBandPoint->mnX                = nX;
292
357
    pNewRegionBandPoint->mnLineId           = nLineId;
293
357
    pNewRegionBandPoint->mbEndPoint         = bEndPoint;
294
357
    pNewRegionBandPoint->meLineType         = eLineType;
295
357
    pNewRegionBandPoint->mpNextBandPoint    = nullptr;
296
297
    // connections to the new point
298
357
    pLastTestedRegionBandPoint->mpNextBandPoint = pNewRegionBandPoint;
299
300
357
    return true;
301
5.88k
}
302
303
void ImplRegionBand::MoveX( tools::Long nHorzMove )
304
3.30k
{
305
    // move all x-separations
306
3.30k
    ImplRegionBandSep* pSep = mpFirstSep;
307
6.47k
    while ( pSep )
308
3.16k
    {
309
3.16k
        pSep->mnXLeft  += nHorzMove;
310
3.16k
        pSep->mnXRight += nHorzMove;
311
3.16k
        pSep = pSep->mpNextSep;
312
3.16k
    }
313
3.30k
}
314
315
void ImplRegionBand::ScaleX( double fHorzScale )
316
0
{
317
0
    ImplRegionBandSep* pSep = mpFirstSep;
318
0
    while ( pSep )
319
0
    {
320
0
        pSep->mnXLeft = basegfx::fround<tools::Long>(pSep->mnXLeft * fHorzScale);
321
0
        pSep->mnXRight = basegfx::fround<tools::Long>(pSep->mnXRight * fHorzScale);
322
0
        pSep = pSep->mpNextSep;
323
0
    }
324
0
}
325
326
// combine overlapping separations
327
328
void ImplRegionBand::OptimizeBand()
329
89.7k
{
330
89.7k
    ImplRegionBandSep* pPrevSep = nullptr;
331
89.7k
    ImplRegionBandSep* pSep = mpFirstSep;
332
202k
    while ( pSep )
333
113k
    {
334
        // remove?
335
113k
        if ( pSep->mbRemoved || (pSep->mnXRight < pSep->mnXLeft) )
336
12.7k
        {
337
12.7k
            ImplRegionBandSep* pOldSep = pSep;
338
12.7k
            if ( pSep == mpFirstSep )
339
8.72k
                mpFirstSep = pSep->mpNextSep;
340
4.05k
            else
341
4.05k
                pPrevSep->mpNextSep = pSep->mpNextSep;
342
12.7k
            pSep = pSep->mpNextSep;
343
12.7k
            delete pOldSep;
344
12.7k
            continue;
345
12.7k
        }
346
347
        // overlapping separations? -> combine!
348
100k
        if ( pSep->mpNextSep )
349
19.6k
        {
350
19.6k
            if ( (pSep->mnXRight+1) >= pSep->mpNextSep->mnXLeft )
351
1.47k
            {
352
1.47k
                if ( pSep->mpNextSep->mnXRight > pSep->mnXRight )
353
382
                    pSep->mnXRight = pSep->mpNextSep->mnXRight;
354
355
1.47k
                ImplRegionBandSep* pOldSep = pSep->mpNextSep;
356
1.47k
                pSep->mpNextSep = pOldSep->mpNextSep;
357
1.47k
                delete pOldSep;
358
1.47k
                continue;
359
1.47k
            }
360
19.6k
        }
361
362
98.8k
        pPrevSep = pSep;
363
98.8k
        pSep = pSep->mpNextSep;
364
98.8k
    }
365
89.7k
}
366
367
void ImplRegionBand::Union( tools::Long nXLeft, tools::Long nXRight )
368
373k
{
369
373k
    SAL_WARN_IF( nXLeft > nXRight, "vcl", "ImplRegionBand::Union(): nxLeft > nXRight" );
370
371
    // band empty? -> add element
372
373k
    if ( !mpFirstSep )
373
324k
    {
374
324k
        mpFirstSep              = new ImplRegionBandSep;
375
324k
        mpFirstSep->mnXLeft     = nXLeft;
376
324k
        mpFirstSep->mnXRight    = nXRight;
377
324k
        mpFirstSep->mbRemoved   = false;
378
324k
        mpFirstSep->mpNextSep   = nullptr;
379
324k
        return;
380
324k
    }
381
382
    // process real union
383
49.4k
    ImplRegionBandSep* pNewSep;
384
49.4k
    ImplRegionBandSep* pPrevSep = nullptr;
385
49.4k
    ImplRegionBandSep* pSep = mpFirstSep;
386
56.0k
    while ( pSep )
387
53.9k
    {
388
        // new separation completely inside? nothing to do!
389
53.9k
        if ( (nXLeft >= pSep->mnXLeft) && (nXRight <= pSep->mnXRight) )
390
28.7k
            return;
391
392
        // new separation completely left? -> new separation!
393
25.2k
        if ( nXRight < pSep->mnXLeft )
394
6.44k
        {
395
6.44k
            pNewSep             = new ImplRegionBandSep;
396
6.44k
            pNewSep->mnXLeft    = nXLeft;
397
6.44k
            pNewSep->mnXRight   = nXRight;
398
6.44k
            pNewSep->mbRemoved  = false;
399
400
6.44k
            pNewSep->mpNextSep = pSep;
401
6.44k
            if ( pSep == mpFirstSep )
402
5.50k
                mpFirstSep = pNewSep;
403
937
            else
404
937
                pPrevSep->mpNextSep = pNewSep;
405
6.44k
            break;
406
6.44k
        }
407
408
        // new separation overlapping from left? -> extend boundary
409
18.7k
        if ( (nXRight >= pSep->mnXLeft) && (nXLeft <= pSep->mnXLeft) )
410
5.26k
            pSep->mnXLeft = nXLeft;
411
412
        // new separation overlapping from right? -> extend boundary
413
18.7k
        if ( (nXLeft <= pSep->mnXRight) && (nXRight > pSep->mnXRight) )
414
3.94k
        {
415
3.94k
            pSep->mnXRight = nXRight;
416
3.94k
            break;
417
3.94k
        }
418
419
        // not inserted, but last element? -> add to the end of the list
420
14.8k
        if ( !pSep->mpNextSep && (nXLeft > pSep->mnXRight) )
421
8.22k
        {
422
8.22k
            pNewSep             = new ImplRegionBandSep;
423
8.22k
            pNewSep->mnXLeft    = nXLeft;
424
8.22k
            pNewSep->mnXRight   = nXRight;
425
8.22k
            pNewSep->mbRemoved  = false;
426
427
8.22k
            pSep->mpNextSep     = pNewSep;
428
8.22k
            pNewSep->mpNextSep  = nullptr;
429
8.22k
            break;
430
8.22k
        }
431
432
6.61k
        pPrevSep = pSep;
433
6.61k
        pSep = pSep->mpNextSep;
434
6.61k
    }
435
436
20.6k
    OptimizeBand();
437
20.6k
}
438
439
void ImplRegionBand::Intersect( tools::Long nXLeft, tools::Long nXRight )
440
80.4k
{
441
80.4k
    SAL_WARN_IF( nXLeft > nXRight, "vcl", "ImplRegionBand::Intersect(): nxLeft > nXRight" );
442
443
    // band has been touched
444
80.4k
    mbTouched = true;
445
446
    // band empty? -> nothing to do
447
80.4k
    if ( !mpFirstSep )
448
27.0k
        return;
449
450
    // process real intersection
451
53.4k
    ImplRegionBandSep* pSep = mpFirstSep;
452
107k
    while ( pSep )
453
53.5k
    {
454
        // new separation completely outside? -> remove separation
455
53.5k
        if ( (nXRight < pSep->mnXLeft) || (nXLeft > pSep->mnXRight) )
456
            // will be removed from the optimizer
457
3.72k
            pSep->mbRemoved = true;
458
459
        // new separation overlapping from left? -> reduce right boundary
460
53.5k
        if ( (nXLeft <= pSep->mnXLeft) &&
461
50.1k
             (nXRight <= pSep->mnXRight) &&
462
39.9k
             (nXRight >= pSep->mnXLeft) )
463
37.2k
            pSep->mnXRight = nXRight;
464
465
        // new separation overlapping from right? -> reduce right boundary
466
53.5k
        if ( (nXLeft >= pSep->mnXLeft) &&
467
47.9k
             (nXLeft <= pSep->mnXRight) &&
468
46.9k
             (nXRight >= pSep->mnXRight) )
469
44.9k
            pSep->mnXLeft = nXLeft;
470
471
        // new separation within the actual one? -> reduce both boundaries
472
53.5k
        if ( (nXLeft >= pSep->mnXLeft) && (nXRight <= pSep->mnXRight) )
473
37.5k
        {
474
37.5k
            pSep->mnXRight = nXRight;
475
37.5k
            pSep->mnXLeft = nXLeft;
476
37.5k
        }
477
478
53.5k
        pSep = pSep->mpNextSep;
479
53.5k
    }
480
481
53.4k
    OptimizeBand();
482
53.4k
}
483
484
void ImplRegionBand::Exclude( tools::Long nXLeft, tools::Long nXRight )
485
172k
{
486
172k
    SAL_WARN_IF( nXLeft > nXRight, "vcl", "ImplRegionBand::Exclude(): nxLeft > nXRight" );
487
488
    // band has been touched
489
172k
    mbTouched = true;
490
491
    // band empty? -> nothing to do
492
172k
    if ( !mpFirstSep )
493
157k
        return;
494
495
    // process real exclusion
496
15.5k
    ImplRegionBandSep* pNewSep;
497
15.5k
    ImplRegionBandSep* pPrevSep = nullptr;
498
15.5k
    ImplRegionBandSep* pSep = mpFirstSep;
499
34.7k
    while ( pSep  )
500
19.1k
    {
501
19.1k
        bool bSepProcessed = false;
502
503
        // new separation completely overlapping? -> remove separation
504
19.1k
        if ( (nXLeft <= pSep->mnXLeft) && (nXRight >= pSep->mnXRight) )
505
992
        {
506
            // will be removed from the optimizer
507
992
            pSep->mbRemoved = true;
508
992
            bSepProcessed = true;
509
992
        }
510
511
        // new separation overlapping from left? -> reduce boundary
512
19.1k
        if ( !bSepProcessed )
513
18.1k
        {
514
18.1k
            if ( (nXRight >= pSep->mnXLeft) && (nXLeft <= pSep->mnXLeft) )
515
242
            {
516
242
                pSep->mnXLeft = nXRight+1;
517
242
                bSepProcessed = true;
518
242
            }
519
18.1k
        }
520
521
        // new separation overlapping from right? -> reduce boundary
522
19.1k
        if ( !bSepProcessed )
523
17.8k
        {
524
17.8k
            if ( (nXLeft <= pSep->mnXRight) && (nXRight > pSep->mnXRight) )
525
351
            {
526
351
                pSep->mnXRight = nXLeft-1;
527
351
                bSepProcessed = true;
528
351
            }
529
17.8k
        }
530
531
        // new separation within the actual one? -> reduce boundary
532
        // and add new entry for reminder
533
19.1k
        if ( !bSepProcessed )
534
17.5k
        {
535
17.5k
            if ( (nXLeft >= pSep->mnXLeft) && (nXRight <= pSep->mnXRight) )
536
21
            {
537
21
                pNewSep             = new ImplRegionBandSep;
538
21
                pNewSep->mnXLeft    = pSep->mnXLeft;
539
21
                pNewSep->mnXRight   = nXLeft-1;
540
21
                pNewSep->mbRemoved  = false;
541
542
21
                pSep->mnXLeft = nXRight+1;
543
544
                // connections from the new separation
545
21
                pNewSep->mpNextSep = pSep;
546
547
                // connections to the new separation
548
21
                if ( pSep == mpFirstSep )
549
16
                    mpFirstSep = pNewSep;
550
5
                else
551
5
                    pPrevSep->mpNextSep = pNewSep;
552
21
            }
553
17.5k
        }
554
555
19.1k
        pPrevSep = pSep;
556
19.1k
        pSep = pSep->mpNextSep;
557
19.1k
    }
558
559
15.5k
    OptimizeBand();
560
15.5k
}
561
562
void ImplRegionBand::XOr( tools::Long nXLeft, tools::Long nXRight )
563
0
{
564
0
    SAL_WARN_IF( nXLeft > nXRight, "vcl", "ImplRegionBand::XOr(): nxLeft > nXRight" );
565
566
    // #i46602# Reworked rectangle Xor
567
568
    // In general, we can distinguish 11 cases of intersection
569
    // (details below). The old implementation explicitly handled 7
570
    // cases (numbered in the order of appearance, use CVS to get your
571
    // hands on the old version), therefore, I've sticked to that
572
    // order, and added four more cases. The code below references
573
    // those numbers via #1, #2, etc.
574
575
    // Num Mnem        newX:oldX newY:oldY  Description                                             Result          Can quit?
576
577
    // #1  Empty band      -         -      The band is empty, thus, simply add new bandSep         just add        Yes
578
579
    // #2  apart           -         -      The rectangles are disjunct, add new one as is          just add        Yes
580
581
    // #3  atop            ==        ==     The rectangles are _exactly_ the same, remove existing  just remove     Yes
582
583
    // #4  around          <         >      The new rectangle extends the old to both sides         intersect       No
584
585
    // #5  left            <         <      The new rectangle is left of the old (but intersects)   intersect       Yes
586
587
    // #5b left-atop       <         ==     The new is left of the old, and coincides on the right  intersect       Yes
588
589
    // #6  right           >         >      The new is right of the old (but intersects)            intersect       No
590
591
    // #6b right-atop      ==        >      The new is right of the old, and coincides on the left  intersect       No
592
593
    // #7 inside           >         <      The new is fully inside the old                         intersect       Yes
594
595
    // #8 inside-right     >         ==     The new is fully inside the old, coincides on the right intersect       Yes
596
597
    // #9 inside-left      ==        <      The new is fully inside the old, coincides on the left  intersect       Yes
598
599
    // Then, to correctly perform XOr, the segment that's switched off
600
    // (i.e. the overlapping part of the old and the new segment) must
601
    // be extended by one pixel value at each border:
602
    //           1   1
603
    // 0   4     0   4
604
    // 111100000001111
605
606
    // Clearly, the leading band sep now goes from 0 to 3, and the
607
    // trailing band sep from 11 to 14. This mimics the xor look of a
608
    // bitmap operation.
609
610
    // band empty? -> add element
611
0
    if ( !mpFirstSep )
612
0
    {
613
0
        mpFirstSep              = new ImplRegionBandSep;
614
0
        mpFirstSep->mnXLeft     = nXLeft;
615
0
        mpFirstSep->mnXRight    = nXRight;
616
0
        mpFirstSep->mbRemoved   = false;
617
0
        mpFirstSep->mpNextSep   = nullptr;
618
0
        return;
619
0
    }
620
621
    // process real xor
622
0
    ImplRegionBandSep* pNewSep;
623
0
    ImplRegionBandSep* pPrevSep = nullptr;
624
0
    ImplRegionBandSep* pSep = mpFirstSep;
625
626
0
    while ( pSep  )
627
0
    {
628
0
        tools::Long nOldLeft( pSep->mnXLeft );
629
0
        tools::Long nOldRight( pSep->mnXRight );
630
631
        // did the current segment actually touch the new rect? If
632
        // not, skip all comparisons, go on, loop and try to find
633
        // intersecting bandSep
634
0
        if( nXLeft <= nOldRight )
635
0
        {
636
0
            if( nXRight < nOldLeft )
637
0
            {
638
                // #2
639
640
                // add _before_ current bandSep
641
0
                pNewSep             = new ImplRegionBandSep;
642
0
                pNewSep->mnXLeft    = nXLeft;
643
0
                pNewSep->mnXRight   = nXRight;
644
0
                pNewSep->mpNextSep  = pSep;
645
0
                pNewSep->mbRemoved  = false;
646
647
                // connections from the new separation
648
0
                pNewSep->mpNextSep = pSep;
649
650
                // connections to the new separation
651
0
                if ( pSep == mpFirstSep )
652
0
                    mpFirstSep = pNewSep;
653
0
                else
654
0
                    pPrevSep->mpNextSep = pNewSep;
655
0
                pPrevSep = nullptr; // do not run accidentally into the "right" case when breaking the loop
656
0
                break;
657
0
            }
658
0
            else if( nXLeft == nOldLeft && nXRight == nOldRight )
659
0
            {
660
                // #3
661
0
                pSep->mbRemoved = true;
662
0
                pPrevSep = nullptr; // do not run accidentally into the "right" case when breaking the loop
663
0
                break;
664
0
            }
665
0
            else if( nXLeft != nOldLeft && nXRight == nOldRight )
666
0
            {
667
                // # 5b, 8
668
0
                if( nXLeft < nOldLeft )
669
0
                {
670
0
                    nXRight = nOldLeft; // 5b
671
0
                }
672
0
                else
673
0
                {
674
0
                    nXRight = nXLeft; // 8
675
0
                    nXLeft = nOldLeft;
676
0
                }
677
678
0
                pSep->mnXLeft = nXLeft;
679
0
                pSep->mnXRight = nXRight-1;
680
681
0
                pPrevSep = nullptr; // do not run accidentally into the "right" case when breaking the loop
682
0
                break;
683
0
            }
684
0
            else if( nXLeft == nOldLeft && nXRight != nOldRight )
685
0
            {
686
                // # 6b, 9
687
688
0
                if( nXRight > nOldRight )
689
0
                {
690
0
                    nXLeft = nOldRight+1; // 6b
691
692
                    // cannot break here, simply mark segment as removed,
693
                    // and go on with adapted nXLeft/nXRight
694
0
                    pSep->mbRemoved = true;
695
0
                }
696
0
                else
697
0
                {
698
0
                    pSep->mnXLeft = nXRight+1; // 9
699
700
0
                    pPrevSep = nullptr; // do not run accidentally into the "right" case when breaking the loop
701
0
                    break;
702
0
                }
703
0
            }
704
0
            else // if( nXLeft != nOldLeft && nXRight != nOldRight ) follows automatically
705
0
            {
706
                // #4,5,6,7
707
0
                SAL_WARN_IF( nXLeft == nOldLeft || nXRight == nOldRight, "vcl",
708
0
                            "ImplRegionBand::XOr(): Case 4,5,6,7 expected all coordinates to be not equal!" );
709
710
                // The plain-jane check would look like this:
711
712
                // if( nXLeft < nOldLeft )
713
                // {
714
                //     // #4,5
715
                //     if( nXRight > nOldRight )
716
                //     {
717
                //        // #4
718
                //     }
719
                //     else
720
                //     {
721
                //         // #5 done!
722
                //     }
723
                // }
724
                // else
725
                // {
726
                //     // #6,7
727
                //     if( nXRight > nOldRight )
728
                //     {
729
                //         // #6
730
                //     }
731
                //     else
732
                //     {
733
                //         // #7 done!
734
                //     }
735
                // }
736
737
                // but since we generally don't have to care whether
738
                // it's 4 or 6 (only that we must not stop processing
739
                // here), condensed that in such a way that only the
740
                // coordinates get shuffled into correct ordering.
741
742
0
                if( nXLeft < nOldLeft )
743
0
                    ::std::swap( nOldLeft, nXLeft );
744
745
0
                bool bDone( false );
746
747
0
                if( nXRight < nOldRight )
748
0
                {
749
0
                    ::std::swap( nOldRight, nXRight );
750
0
                    bDone = true;
751
0
                }
752
753
                // now, nOldLeft<nXLeft<=nOldRight<nXRight always
754
                // holds. Note that we need the nXLeft<=nOldRight here, as
755
                // the intersection part might be only one pixel (original
756
                // nXLeft==nXRight)
757
0
                SAL_WARN_IF( nOldLeft==nXLeft || nXLeft>nOldRight || nOldRight>=nXRight, "vcl",
758
0
                            "ImplRegionBand::XOr(): Case 4,5,6,7 expected coordinates to be ordered now!" );
759
760
0
                pSep->mnXLeft = nOldLeft;
761
0
                pSep->mnXRight = nXLeft-1;
762
763
0
                nXLeft = nOldRight+1;
764
                // nxRight is already setup correctly
765
766
0
                if( bDone )
767
0
                {
768
                    // add behind current bandSep
769
0
                    pNewSep = new ImplRegionBandSep;
770
771
0
                    pNewSep->mnXLeft    = nXLeft;
772
0
                    pNewSep->mnXRight   = nXRight;
773
0
                    pNewSep->mpNextSep  = pSep->mpNextSep;
774
0
                    pNewSep->mbRemoved  = false;
775
776
                    // connections from the new separation
777
0
                    pSep->mpNextSep = pNewSep;
778
779
0
                    pPrevSep = nullptr; // do not run accidentally into the "right" case when breaking the loop
780
0
                    break;
781
0
                }
782
0
            }
783
0
        }
784
785
0
        pPrevSep = pSep;
786
0
        pSep = pSep->mpNextSep;
787
0
    }
788
789
    // new separation completely right of existing bandSeps ?
790
0
    if( pPrevSep && nXLeft >= pPrevSep->mnXRight )
791
0
    {
792
0
        pNewSep             = new ImplRegionBandSep;
793
0
        pNewSep->mnXLeft    = nXLeft;
794
0
        pNewSep->mnXRight   = nXRight;
795
0
        pNewSep->mpNextSep  = nullptr;
796
0
        pNewSep->mbRemoved  = false;
797
798
        // connections from the new separation
799
0
        pPrevSep->mpNextSep = pNewSep;
800
0
    }
801
802
0
    OptimizeBand();
803
0
}
804
805
bool ImplRegionBand::Contains( tools::Long nX )
806
0
{
807
0
    ImplRegionBandSep* pSep = mpFirstSep;
808
0
    while ( pSep )
809
0
    {
810
0
        if ( (pSep->mnXLeft <= nX) && (pSep->mnXRight >= nX) )
811
0
            return true;
812
813
0
        pSep = pSep->mpNextSep;
814
0
    }
815
816
0
    return false;
817
0
}
818
819
tools::Long ImplRegionBand::GetXLeftBoundary() const
820
174k
{
821
174k
    SAL_WARN_IF(mpFirstSep == nullptr, "vcl", "ImplRegionBand::XLeftBoundary -> no separation in band!");
822
823
174k
    return mpFirstSep ? mpFirstSep->mnXLeft : 0;
824
174k
}
825
826
tools::Long ImplRegionBand::GetXRightBoundary() const
827
174k
{
828
174k
    SAL_WARN_IF( mpFirstSep == nullptr, "vcl", "ImplRegionBand::XRightBoundary -> no separation in band!" );
829
174k
    if (!mpFirstSep)
830
2.66k
        return 0;
831
    // search last separation
832
172k
    ImplRegionBandSep* pSep = mpFirstSep;
833
172k
    while ( pSep->mpNextSep )
834
622
        pSep = pSep->mpNextSep;
835
172k
    return pSep->mnXRight;
836
174k
}
837
838
bool ImplRegionBand::operator==( const ImplRegionBand& rRegionBand ) const
839
40.2k
{
840
40.2k
    ImplRegionBandSep*   pOwnRectBandSep = mpFirstSep;
841
40.2k
    ImplRegionBandSep*   pSecondRectBandSep = rRegionBand.mpFirstSep;
842
72.1k
    while ( pOwnRectBandSep && pSecondRectBandSep )
843
35.3k
    {
844
        // get boundaries of current rectangle
845
35.3k
        tools::Long nOwnXLeft = pOwnRectBandSep->mnXLeft;
846
35.3k
        tools::Long nSecondXLeft = pSecondRectBandSep->mnXLeft;
847
35.3k
        if ( nOwnXLeft != nSecondXLeft )
848
2.15k
            return false;
849
850
33.2k
        tools::Long nOwnXRight = pOwnRectBandSep->mnXRight;
851
33.2k
        tools::Long nSecondXRight = pSecondRectBandSep->mnXRight;
852
33.2k
        if ( nOwnXRight != nSecondXRight )
853
1.35k
            return false;
854
855
        // get next separation from current band
856
31.8k
        pOwnRectBandSep = pOwnRectBandSep->mpNextSep;
857
858
        // get next separation from current band
859
31.8k
        pSecondRectBandSep = pSecondRectBandSep->mpNextSep;
860
31.8k
    }
861
862
    // different number of separations?
863
36.7k
    return !(pOwnRectBandSep || pSecondRectBandSep);
864
40.2k
}
865
866
ImplRegionBand* ImplRegionBand::SplitBand (const sal_Int32 nY)
867
0
{
868
0
    OSL_ASSERT(nY>mnYTop);
869
0
    OSL_ASSERT(nY<=mnYBottom);
870
871
    // Create a copy of the given band (we tell the constructor to copy the points together
872
    // with the seps.)
873
0
    ImplRegionBand* pLowerBand = new ImplRegionBand(*this, false);
874
875
    // Adapt vertical coordinates.
876
0
    mnYBottom = nY-1;
877
0
    pLowerBand->mnYTop = nY;
878
879
    // Insert new band into list of bands.
880
0
    pLowerBand->mpNextBand = mpNextBand;
881
0
    mpNextBand = pLowerBand;
882
0
    pLowerBand->mpPrevBand = this;
883
0
    if (pLowerBand->mpNextBand != nullptr)
884
0
        pLowerBand->mpNextBand->mpPrevBand = pLowerBand;
885
886
0
    return pLowerBand;
887
0
}
888
889
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */