Coverage Report

Created: 2025-11-16 09:57

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
526k
{
45
    // save boundaries
46
526k
    mnYTop              = nTop;
47
526k
    mnYBottom           = nBottom;
48
49
    // initialize lists
50
526k
    mpNextBand          = nullptr;
51
526k
    mpPrevBand          = nullptr;
52
526k
    mpFirstSep          = nullptr;
53
526k
    mpFirstBandPoint    = nullptr;
54
526k
    mbTouched           = false;
55
526k
}
56
57
ImplRegionBand::ImplRegionBand(
58
    const ImplRegionBand& rRegionBand,
59
    const bool bIgnorePoints)
60
204k
{
61
    // copy boundaries
62
204k
    mnYTop              = rRegionBand.mnYTop;
63
204k
    mnYBottom           = rRegionBand.mnYBottom;
64
204k
    mbTouched           = rRegionBand.mbTouched;
65
66
    // initialisation
67
204k
    mpNextBand          = nullptr;
68
204k
    mpPrevBand          = nullptr;
69
204k
    mpFirstSep          = nullptr;
70
204k
    mpFirstBandPoint    = nullptr;
71
72
    // copy all elements of the list with separations
73
204k
    ImplRegionBandSep* pNewSep;
74
204k
    ImplRegionBandSep* pPrevSep = nullptr;
75
204k
    ImplRegionBandSep* pSep = rRegionBand.mpFirstSep;
76
384k
    while ( pSep )
77
180k
    {
78
        // create new and copy data
79
180k
        pNewSep             = new ImplRegionBandSep;
80
180k
        pNewSep->mnXLeft    = pSep->mnXLeft;
81
180k
        pNewSep->mnXRight   = pSep->mnXRight;
82
180k
        pNewSep->mbRemoved  = pSep->mbRemoved;
83
180k
        pNewSep->mpNextSep  = nullptr;
84
180k
        if ( pSep == rRegionBand.mpFirstSep )
85
163k
            mpFirstSep = pNewSep;
86
17.5k
        else
87
17.5k
            pPrevSep->mpNextSep = pNewSep;
88
89
180k
        pPrevSep = pNewSep;
90
180k
        pSep = pSep->mpNextSep;
91
180k
    }
92
93
204k
    if (  bIgnorePoints)
94
204k
        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
730k
{
120
730k
    SAL_WARN_IF( mpFirstBandPoint != nullptr, "vcl", "ImplRegionBand::~ImplRegionBand -> pointlist not empty" );
121
122
    // delete elements of the list
123
730k
    ImplRegionBandSep* pSep = mpFirstSep;
124
1.25M
    while ( pSep )
125
524k
    {
126
524k
        ImplRegionBandSep* pTempSep = pSep->mpNextSep;
127
524k
        delete pSep;
128
524k
        pSep = pTempSep;
129
524k
    }
130
131
    // delete elements of the list
132
730k
    ImplRegionBandPoint* pPoint = mpFirstBandPoint;
133
730k
    while ( pPoint )
134
0
    {
135
0
        ImplRegionBandPoint* pTempPoint = pPoint->mpNextBandPoint;
136
0
        delete pPoint;
137
0
        pPoint = pTempPoint;
138
0
    }
139
730k
}
140
141
// generate separations from lines and process union with existing
142
// separations
143
144
void ImplRegionBand::ProcessPoints()
145
5.84k
{
146
    // check Pointlist
147
5.84k
    ImplRegionBandPoint* pRegionBandPoint = mpFirstBandPoint;
148
21.5k
    while ( pRegionBandPoint )
149
15.7k
    {
150
        // within list?
151
15.7k
        if ( pRegionBandPoint->mpNextBandPoint )
152
13.0k
        {
153
            // start/stop?
154
13.0k
            if ( pRegionBandPoint->mbEndPoint && pRegionBandPoint->mpNextBandPoint->mbEndPoint )
155
13.0k
            {
156
                // same direction? -> remove next point!
157
13.0k
                if ( pRegionBandPoint->meLineType == pRegionBandPoint->mpNextBandPoint->meLineType )
158
7.38k
                {
159
7.38k
                    ImplRegionBandPoint* pSaveRegionBandPoint = pRegionBandPoint->mpNextBandPoint;
160
7.38k
                    pRegionBandPoint->mpNextBandPoint = pRegionBandPoint->mpNextBandPoint->mpNextBandPoint;
161
7.38k
                    delete pSaveRegionBandPoint;
162
7.38k
                }
163
13.0k
            }
164
13.0k
        }
165
166
        // continue with next element in the list
167
15.7k
        pRegionBandPoint = pRegionBandPoint->mpNextBandPoint;
168
15.7k
    }
169
170
5.84k
    pRegionBandPoint = mpFirstBandPoint;
171
13.5k
    while ( pRegionBandPoint && pRegionBandPoint->mpNextBandPoint )
172
7.66k
    {
173
7.66k
        Union( pRegionBandPoint->mnX, pRegionBandPoint->mpNextBandPoint->mnX );
174
175
7.66k
        ImplRegionBandPoint* pNextBandPoint = pRegionBandPoint->mpNextBandPoint->mpNextBandPoint;
176
177
        // remove already processed points
178
7.66k
        delete pRegionBandPoint->mpNextBandPoint;
179
7.66k
        delete pRegionBandPoint;
180
181
        // continue with next element in the list
182
7.66k
        pRegionBandPoint = pNextBandPoint;
183
7.66k
    }
184
185
    // remove last element if necessary
186
5.84k
    delete pRegionBandPoint;
187
188
    // list is now empty
189
5.84k
    mpFirstBandPoint = nullptr;
190
5.84k
}
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
23.1k
{
198
23.1k
    if ( !mpFirstBandPoint )
199
3.95k
    {
200
3.95k
        mpFirstBandPoint                  = new ImplRegionBandPoint;
201
3.95k
        mpFirstBandPoint->mnX             = nX;
202
3.95k
        mpFirstBandPoint->mnLineId        = nLineId;
203
3.95k
        mpFirstBandPoint->mbEndPoint      = bEndPoint;
204
3.95k
        mpFirstBandPoint->meLineType      = eLineType;
205
3.95k
        mpFirstBandPoint->mpNextBandPoint = nullptr;
206
3.95k
        return true;
207
3.95k
    }
208
209
    // look if line already touched the band
210
19.1k
    ImplRegionBandPoint* pRegionBandPoint = mpFirstBandPoint;
211
19.1k
    ImplRegionBandPoint* pLastTestedRegionBandPoint = nullptr;
212
85.1k
    while( pRegionBandPoint )
213
65.9k
    {
214
65.9k
        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
65.9k
        pLastTestedRegionBandPoint = pRegionBandPoint;
255
65.9k
        pRegionBandPoint = pRegionBandPoint->mpNextBandPoint;
256
65.9k
    }
257
258
    // search appropriate position and insert point into the list
259
19.1k
    ImplRegionBandPoint* pNewRegionBandPoint;
260
261
19.1k
    pRegionBandPoint = mpFirstBandPoint;
262
19.1k
    pLastTestedRegionBandPoint = nullptr;
263
33.6k
    while ( pRegionBandPoint )
264
32.6k
    {
265
        // new point completely left? -> insert as first point
266
32.6k
        if ( nX <= pRegionBandPoint->mnX )
267
18.1k
        {
268
18.1k
            pNewRegionBandPoint                     = new ImplRegionBandPoint;
269
18.1k
            pNewRegionBandPoint->mnX                = nX;
270
18.1k
            pNewRegionBandPoint->mnLineId           = nLineId;
271
18.1k
            pNewRegionBandPoint->mbEndPoint         = bEndPoint;
272
18.1k
            pNewRegionBandPoint->meLineType         = eLineType;
273
18.1k
            pNewRegionBandPoint->mpNextBandPoint    = pRegionBandPoint;
274
275
            // connections to the new point
276
18.1k
            if ( !pLastTestedRegionBandPoint )
277
12.2k
                mpFirstBandPoint = pNewRegionBandPoint;
278
5.84k
            else
279
5.84k
                pLastTestedRegionBandPoint->mpNextBandPoint = pNewRegionBandPoint;
280
281
18.1k
            return true;
282
18.1k
        }
283
284
        // use next element
285
14.5k
        pLastTestedRegionBandPoint = pRegionBandPoint;
286
14.5k
        pRegionBandPoint = pRegionBandPoint->mpNextBandPoint;
287
14.5k
    }
288
289
    // not inserted -> add to the end of the list
290
1.06k
    pNewRegionBandPoint                     = new ImplRegionBandPoint;
291
1.06k
    pNewRegionBandPoint->mnX                = nX;
292
1.06k
    pNewRegionBandPoint->mnLineId           = nLineId;
293
1.06k
    pNewRegionBandPoint->mbEndPoint         = bEndPoint;
294
1.06k
    pNewRegionBandPoint->meLineType         = eLineType;
295
1.06k
    pNewRegionBandPoint->mpNextBandPoint    = nullptr;
296
297
    // connections to the new point
298
1.06k
    pLastTestedRegionBandPoint->mpNextBandPoint = pNewRegionBandPoint;
299
300
1.06k
    return true;
301
19.1k
}
302
303
void ImplRegionBand::MoveX( tools::Long nHorzMove )
304
4.08k
{
305
    // move all x-separations
306
4.08k
    ImplRegionBandSep* pSep = mpFirstSep;
307
8.27k
    while ( pSep )
308
4.18k
    {
309
4.18k
        pSep->mnXLeft  += nHorzMove;
310
4.18k
        pSep->mnXRight += nHorzMove;
311
4.18k
        pSep = pSep->mpNextSep;
312
4.18k
    }
313
4.08k
}
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
80.1k
{
330
80.1k
    ImplRegionBandSep* pPrevSep = nullptr;
331
80.1k
    ImplRegionBandSep* pSep = mpFirstSep;
332
183k
    while ( pSep )
333
103k
    {
334
        // remove?
335
103k
        if ( pSep->mbRemoved || (pSep->mnXRight < pSep->mnXLeft) )
336
16.9k
        {
337
16.9k
            ImplRegionBandSep* pOldSep = pSep;
338
16.9k
            if ( pSep == mpFirstSep )
339
12.4k
                mpFirstSep = pSep->mpNextSep;
340
4.53k
            else
341
4.53k
                pPrevSep->mpNextSep = pSep->mpNextSep;
342
16.9k
            pSep = pSep->mpNextSep;
343
16.9k
            delete pOldSep;
344
16.9k
            continue;
345
16.9k
        }
346
347
        // overlapping separations? -> combine!
348
86.4k
        if ( pSep->mpNextSep )
349
17.0k
        {
350
17.0k
            if ( (pSep->mnXRight+1) >= pSep->mpNextSep->mnXLeft )
351
2.84k
            {
352
2.84k
                if ( pSep->mpNextSep->mnXRight > pSep->mnXRight )
353
2.01k
                    pSep->mnXRight = pSep->mpNextSep->mnXRight;
354
355
2.84k
                ImplRegionBandSep* pOldSep = pSep->mpNextSep;
356
2.84k
                pSep->mpNextSep = pOldSep->mpNextSep;
357
2.84k
                delete pOldSep;
358
2.84k
                continue;
359
2.84k
            }
360
17.0k
        }
361
362
83.6k
        pPrevSep = pSep;
363
83.6k
        pSep = pSep->mpNextSep;
364
83.6k
    }
365
80.1k
}
366
367
void ImplRegionBand::Union( tools::Long nXLeft, tools::Long nXRight )
368
390k
{
369
390k
    SAL_WARN_IF( nXLeft > nXRight, "vcl", "ImplRegionBand::Union(): nxLeft > nXRight" );
370
371
    // band empty? -> add element
372
390k
    if ( !mpFirstSep )
373
349k
    {
374
349k
        mpFirstSep              = new ImplRegionBandSep;
375
349k
        mpFirstSep->mnXLeft     = nXLeft;
376
349k
        mpFirstSep->mnXRight    = nXRight;
377
349k
        mpFirstSep->mbRemoved   = false;
378
349k
        mpFirstSep->mpNextSep   = nullptr;
379
349k
        return;
380
349k
    }
381
382
    // process real union
383
40.7k
    ImplRegionBandSep* pNewSep;
384
40.7k
    ImplRegionBandSep* pPrevSep = nullptr;
385
40.7k
    ImplRegionBandSep* pSep = mpFirstSep;
386
45.8k
    while ( pSep )
387
44.8k
    {
388
        // new separation completely inside? nothing to do!
389
44.8k
        if ( (nXLeft >= pSep->mnXLeft) && (nXRight <= pSep->mnXRight) )
390
20.1k
            return;
391
392
        // new separation completely left? -> new separation!
393
24.6k
        if ( nXRight < pSep->mnXLeft )
394
5.72k
        {
395
5.72k
            pNewSep             = new ImplRegionBandSep;
396
5.72k
            pNewSep->mnXLeft    = nXLeft;
397
5.72k
            pNewSep->mnXRight   = nXRight;
398
5.72k
            pNewSep->mbRemoved  = false;
399
400
5.72k
            pNewSep->mpNextSep = pSep;
401
5.72k
            if ( pSep == mpFirstSep )
402
4.89k
                mpFirstSep = pNewSep;
403
825
            else
404
825
                pPrevSep->mpNextSep = pNewSep;
405
5.72k
            break;
406
5.72k
        }
407
408
        // new separation overlapping from left? -> extend boundary
409
18.9k
        if ( (nXRight >= pSep->mnXLeft) && (nXLeft <= pSep->mnXLeft) )
410
6.01k
            pSep->mnXLeft = nXLeft;
411
412
        // new separation overlapping from right? -> extend boundary
413
18.9k
        if ( (nXLeft <= pSep->mnXRight) && (nXRight > pSep->mnXRight) )
414
5.90k
        {
415
5.90k
            pSep->mnXRight = nXRight;
416
5.90k
            break;
417
5.90k
        }
418
419
        // not inserted, but last element? -> add to the end of the list
420
13.0k
        if ( !pSep->mpNextSep && (nXLeft > pSep->mnXRight) )
421
7.88k
        {
422
7.88k
            pNewSep             = new ImplRegionBandSep;
423
7.88k
            pNewSep->mnXLeft    = nXLeft;
424
7.88k
            pNewSep->mnXRight   = nXRight;
425
7.88k
            pNewSep->mbRemoved  = false;
426
427
7.88k
            pSep->mpNextSep     = pNewSep;
428
7.88k
            pNewSep->mpNextSep  = nullptr;
429
7.88k
            break;
430
7.88k
        }
431
432
5.13k
        pPrevSep = pSep;
433
5.13k
        pSep = pSep->mpNextSep;
434
5.13k
    }
435
436
20.5k
    OptimizeBand();
437
20.5k
}
438
439
void ImplRegionBand::Intersect( tools::Long nXLeft, tools::Long nXRight )
440
73.6k
{
441
73.6k
    SAL_WARN_IF( nXLeft > nXRight, "vcl", "ImplRegionBand::Intersect(): nxLeft > nXRight" );
442
443
    // band has been touched
444
73.6k
    mbTouched = true;
445
446
    // band empty? -> nothing to do
447
73.6k
    if ( !mpFirstSep )
448
29.3k
        return;
449
450
    // process real intersection
451
44.2k
    ImplRegionBandSep* pSep = mpFirstSep;
452
92.0k
    while ( pSep )
453
47.7k
    {
454
        // new separation completely outside? -> remove separation
455
47.7k
        if ( (nXRight < pSep->mnXLeft) || (nXLeft > pSep->mnXRight) )
456
            // will be removed from the optimizer
457
7.30k
            pSep->mbRemoved = true;
458
459
        // new separation overlapping from left? -> reduce right boundary
460
47.7k
        if ( (nXLeft <= pSep->mnXLeft) &&
461
40.3k
             (nXRight <= pSep->mnXRight) &&
462
28.8k
             (nXRight >= pSep->mnXLeft) )
463
23.9k
            pSep->mnXRight = nXRight;
464
465
        // new separation overlapping from right? -> reduce right boundary
466
47.7k
        if ( (nXLeft >= pSep->mnXLeft) &&
467
41.1k
             (nXLeft <= pSep->mnXRight) &&
468
38.8k
             (nXRight >= pSep->mnXRight) )
469
34.1k
            pSep->mnXLeft = nXLeft;
470
471
        // new separation within the actual one? -> reduce both boundaries
472
47.7k
        if ( (nXLeft >= pSep->mnXLeft) && (nXRight <= pSep->mnXRight) )
473
27.7k
        {
474
27.7k
            pSep->mnXRight = nXRight;
475
27.7k
            pSep->mnXLeft = nXLeft;
476
27.7k
        }
477
478
47.7k
        pSep = pSep->mpNextSep;
479
47.7k
    }
480
481
44.2k
    OptimizeBand();
482
44.2k
}
483
484
void ImplRegionBand::Exclude( tools::Long nXLeft, tools::Long nXRight )
485
166k
{
486
166k
    SAL_WARN_IF( nXLeft > nXRight, "vcl", "ImplRegionBand::Exclude(): nxLeft > nXRight" );
487
488
    // band has been touched
489
166k
    mbTouched = true;
490
491
    // band empty? -> nothing to do
492
166k
    if ( !mpFirstSep )
493
151k
        return;
494
495
    // process real exclusion
496
15.3k
    ImplRegionBandSep* pNewSep;
497
15.3k
    ImplRegionBandSep* pPrevSep = nullptr;
498
15.3k
    ImplRegionBandSep* pSep = mpFirstSep;
499
32.1k
    while ( pSep  )
500
16.8k
    {
501
16.8k
        bool bSepProcessed = false;
502
503
        // new separation completely overlapping? -> remove separation
504
16.8k
        if ( (nXLeft <= pSep->mnXLeft) && (nXRight >= pSep->mnXRight) )
505
1.48k
        {
506
            // will be removed from the optimizer
507
1.48k
            pSep->mbRemoved = true;
508
1.48k
            bSepProcessed = true;
509
1.48k
        }
510
511
        // new separation overlapping from left? -> reduce boundary
512
16.8k
        if ( !bSepProcessed )
513
15.3k
        {
514
15.3k
            if ( (nXRight >= pSep->mnXLeft) && (nXLeft <= pSep->mnXLeft) )
515
826
            {
516
826
                pSep->mnXLeft = nXRight+1;
517
826
                bSepProcessed = true;
518
826
            }
519
15.3k
        }
520
521
        // new separation overlapping from right? -> reduce boundary
522
16.8k
        if ( !bSepProcessed )
523
14.4k
        {
524
14.4k
            if ( (nXLeft <= pSep->mnXRight) && (nXRight > pSep->mnXRight) )
525
840
            {
526
840
                pSep->mnXRight = nXLeft-1;
527
840
                bSepProcessed = true;
528
840
            }
529
14.4k
        }
530
531
        // new separation within the actual one? -> reduce boundary
532
        // and add new entry for reminder
533
16.8k
        if ( !bSepProcessed )
534
13.6k
        {
535
13.6k
            if ( (nXLeft >= pSep->mnXLeft) && (nXRight <= pSep->mnXRight) )
536
26
            {
537
26
                pNewSep             = new ImplRegionBandSep;
538
26
                pNewSep->mnXLeft    = pSep->mnXLeft;
539
26
                pNewSep->mnXRight   = nXLeft-1;
540
26
                pNewSep->mbRemoved  = false;
541
542
26
                pSep->mnXLeft = nXRight+1;
543
544
                // connections from the new separation
545
26
                pNewSep->mpNextSep = pSep;
546
547
                // connections to the new separation
548
26
                if ( pSep == mpFirstSep )
549
19
                    mpFirstSep = pNewSep;
550
7
                else
551
7
                    pPrevSep->mpNextSep = pNewSep;
552
26
            }
553
13.6k
        }
554
555
16.8k
        pPrevSep = pSep;
556
16.8k
        pSep = pSep->mpNextSep;
557
16.8k
    }
558
559
15.3k
    OptimizeBand();
560
15.3k
}
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
199k
{
821
199k
    SAL_WARN_IF(mpFirstSep == nullptr, "vcl", "ImplRegionBand::XLeftBoundary -> no separation in band!");
822
823
199k
    return mpFirstSep ? mpFirstSep->mnXLeft : 0;
824
199k
}
825
826
tools::Long ImplRegionBand::GetXRightBoundary() const
827
199k
{
828
199k
    SAL_WARN_IF( mpFirstSep == nullptr, "vcl", "ImplRegionBand::XRightBoundary -> no separation in band!" );
829
199k
    if (!mpFirstSep)
830
4.89k
        return 0;
831
    // search last separation
832
194k
    ImplRegionBandSep* pSep = mpFirstSep;
833
194k
    while ( pSep->mpNextSep )
834
141
        pSep = pSep->mpNextSep;
835
194k
    return pSep->mnXRight;
836
199k
}
837
838
bool ImplRegionBand::operator==( const ImplRegionBand& rRegionBand ) const
839
47.4k
{
840
47.4k
    ImplRegionBandSep*   pOwnRectBandSep = mpFirstSep;
841
47.4k
    ImplRegionBandSep*   pSecondRectBandSep = rRegionBand.mpFirstSep;
842
76.8k
    while ( pOwnRectBandSep && pSecondRectBandSep )
843
43.4k
    {
844
        // get boundaries of current rectangle
845
43.4k
        tools::Long nOwnXLeft = pOwnRectBandSep->mnXLeft;
846
43.4k
        tools::Long nSecondXLeft = pSecondRectBandSep->mnXLeft;
847
43.4k
        if ( nOwnXLeft != nSecondXLeft )
848
7.97k
            return false;
849
850
35.4k
        tools::Long nOwnXRight = pOwnRectBandSep->mnXRight;
851
35.4k
        tools::Long nSecondXRight = pSecondRectBandSep->mnXRight;
852
35.4k
        if ( nOwnXRight != nSecondXRight )
853
6.11k
            return false;
854
855
        // get next separation from current band
856
29.3k
        pOwnRectBandSep = pOwnRectBandSep->mpNextSep;
857
858
        // get next separation from current band
859
29.3k
        pSecondRectBandSep = pSecondRectBandSep->mpNextSep;
860
29.3k
    }
861
862
    // different number of separations?
863
33.4k
    return !(pOwnRectBandSep || pSecondRectBandSep);
864
47.4k
}
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: */