Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/outdev/polygon.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/types.h>
21
#include <basegfx/matrix/b2dhommatrix.hxx>
22
#include <tools/poly.hxx>
23
24
#include <vcl/rendercontext/AntialiasingFlags.hxx>
25
#include <vcl/metaact.hxx>
26
#include <vcl/virdev.hxx>
27
28
#include <salgdi.hxx>
29
30
#include <cassert>
31
#include <memory>
32
33
343k
#define OUTDEV_POLYPOLY_STACKBUF        32
34
35
void OutputDevice::DrawPolyPolygon( const tools::PolyPolygon& rPolyPoly )
36
1.24M
{
37
1.24M
    assert(!is_double_buffered_window());
38
39
1.24M
    if( mpMetaFile )
40
21.3k
        mpMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPoly ) );
41
42
1.24M
    sal_uInt16 nPoly = rPolyPoly.Count();
43
44
1.24M
    if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || !nPoly || ImplIsRecordLayout() )
45
49.0k
        return;
46
47
    // we need a graphics
48
1.19M
    if ( !mpGraphics && !AcquireGraphics() )
49
0
        return;
50
1.19M
    assert(mpGraphics);
51
52
1.19M
    if ( mbInitClipRegion )
53
389
        InitClipRegion();
54
55
1.19M
    if ( mbOutputClipped )
56
192
        return;
57
58
1.19M
    if ( mbInitLineColor )
59
1.89k
        InitLineColor();
60
61
1.19M
    if ( mbInitFillColor )
62
2.34k
        InitFillColor();
63
64
    // use b2dpolygon drawing if possible
65
1.19M
    if (RasterOp::OverPaint == GetRasterOp() && (IsLineColor() || IsFillColor()))
66
1.13M
    {
67
1.13M
        const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation());
68
1.13M
        basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPoly.getB2DPolyPolygon());
69
70
        // ensure closed - may be asserted, will prevent buffering
71
1.13M
        if(!aB2DPolyPolygon.isClosed())
72
38.9k
        {
73
38.9k
            aB2DPolyPolygon.setClosed(true);
74
38.9k
        }
75
76
1.13M
        if (IsFillColor())
77
1.13M
        {
78
1.13M
            mpGraphics->DrawPolyPolygon(
79
1.13M
                aTransform,
80
1.13M
                aB2DPolyPolygon,
81
1.13M
                0.0,
82
1.13M
                *this);
83
1.13M
        }
84
85
1.13M
        bool bSuccess(true);
86
1.13M
        if (IsLineColor())
87
4.89k
        {
88
4.89k
            const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
89
90
4.89k
            for(auto const& rPolygon : std::as_const(aB2DPolyPolygon))
91
19.4k
            {
92
19.4k
                bSuccess = mpGraphics->DrawPolyLine(
93
19.4k
                    aTransform,
94
19.4k
                    rPolygon,
95
19.4k
                    0.0,
96
19.4k
                    0.0, // tdf#124848 hairline
97
19.4k
                    nullptr, // MM01
98
19.4k
                    basegfx::B2DLineJoin::NONE,
99
19.4k
                    css::drawing::LineCap_BUTT,
100
19.4k
                    basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
101
19.4k
                    bPixelSnapHairline,
102
19.4k
                    *this);
103
19.4k
                if (!bSuccess)
104
0
                    break;
105
19.4k
            }
106
4.89k
        }
107
108
1.13M
        if(bSuccess)
109
1.13M
            return;
110
1.13M
    }
111
112
60.6k
    if ( nPoly == 1 )
113
7.05k
    {
114
        // #100127# Map to DrawPolygon
115
7.05k
        const tools::Polygon& aPoly = rPolyPoly.GetObject( 0 );
116
7.05k
        if( aPoly.GetSize() >= 2 )
117
7.04k
        {
118
7.04k
            GDIMetaFile* pOldMF = mpMetaFile;
119
7.04k
            mpMetaFile = nullptr;
120
121
7.04k
            DrawPolygon( aPoly );
122
123
7.04k
            mpMetaFile = pOldMF;
124
7.04k
        }
125
7.05k
    }
126
53.6k
    else
127
53.6k
    {
128
        // #100127# moved real tools::PolyPolygon draw to separate method,
129
        // have to call recursively, avoiding duplicate
130
        // ImplLogicToDevicePixel calls
131
53.6k
        ImplDrawPolyPolygon( nPoly, ImplLogicToDevicePixel( rPolyPoly ) );
132
53.6k
    }
133
60.6k
}
134
135
void OutputDevice::DrawPolygon( const basegfx::B2DPolygon& rB2DPolygon)
136
2.95k
{
137
2.95k
    assert(!is_double_buffered_window());
138
139
    // AW: Do NOT paint empty polygons
140
2.95k
    if(rB2DPolygon.count())
141
2.95k
    {
142
2.95k
        basegfx::B2DPolyPolygon aPP( rB2DPolygon );
143
2.95k
        DrawPolyPolygon( aPP );
144
2.95k
    }
145
2.95k
}
146
147
void OutputDevice::DrawPolygon( const tools::Polygon& rPoly )
148
2.21M
{
149
2.21M
    assert(!is_double_buffered_window());
150
151
2.21M
    if( mpMetaFile )
152
2.14M
        mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) );
153
154
2.21M
    sal_uInt16 nPoints = rPoly.GetSize();
155
156
2.21M
    if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || (nPoints < 2) || ImplIsRecordLayout() )
157
2.19M
        return;
158
159
    // we need a graphics
160
14.4k
    if ( !mpGraphics && !AcquireGraphics() )
161
0
        return;
162
14.4k
    assert(mpGraphics);
163
164
14.4k
    if ( mbInitClipRegion )
165
1.28k
        InitClipRegion();
166
167
14.4k
    if ( mbOutputClipped )
168
976
        return;
169
170
13.4k
    if ( mbInitLineColor )
171
653
        InitLineColor();
172
173
13.4k
    if ( mbInitFillColor )
174
1.83k
        InitFillColor();
175
176
    // use b2dpolygon drawing if possible
177
13.4k
    if (RasterOp::OverPaint == GetRasterOp() && (IsLineColor() || IsFillColor()))
178
5.99k
    {
179
5.99k
        const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation());
180
5.99k
        basegfx::B2DPolygon aB2DPolygon(rPoly.getB2DPolygon());
181
182
        // ensure closed - maybe assert, hinders buffering
183
5.99k
        if(!aB2DPolygon.isClosed())
184
3.07k
        {
185
3.07k
            aB2DPolygon.setClosed(true);
186
3.07k
        }
187
188
5.99k
        if (IsFillColor())
189
5.44k
        {
190
5.44k
            mpGraphics->DrawPolyPolygon(
191
5.44k
                aTransform,
192
5.44k
                basegfx::B2DPolyPolygon(aB2DPolygon),
193
5.44k
                0.0,
194
5.44k
                *this);
195
5.44k
        }
196
197
5.99k
        bool bSuccess(true);
198
5.99k
        if (IsLineColor())
199
3.03k
        {
200
3.03k
            const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
201
202
3.03k
            bSuccess = mpGraphics->DrawPolyLine(
203
3.03k
                aTransform,
204
3.03k
                aB2DPolygon,
205
3.03k
                0.0,
206
3.03k
                0.0, // tdf#124848 hairline
207
3.03k
                nullptr, // MM01
208
3.03k
                basegfx::B2DLineJoin::NONE,
209
3.03k
                css::drawing::LineCap_BUTT,
210
3.03k
                basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
211
3.03k
                bPixelSnapHairline,
212
3.03k
                *this);
213
3.03k
        }
214
215
5.99k
        if(bSuccess)
216
5.99k
            return;
217
5.99k
    }
218
219
7.45k
    tools::Polygon aPoly = ImplLogicToDevicePixel( rPoly );
220
7.45k
    const Point* pPtAry = aPoly.GetConstPointAry();
221
222
    // #100127# Forward beziers to sal, if any
223
7.45k
    if( aPoly.HasFlags() )
224
259
    {
225
259
        const PolyFlags* pFlgAry = aPoly.GetConstFlagAry();
226
259
        if( !mpGraphics->DrawPolygonBezier( nPoints, pPtAry, pFlgAry, *this ) )
227
259
        {
228
259
            aPoly = tools::Polygon::SubdivideBezier(aPoly);
229
259
            pPtAry = aPoly.GetConstPointAry();
230
259
            mpGraphics->DrawPolygon( aPoly.GetSize(), pPtAry, *this );
231
259
        }
232
259
    }
233
7.19k
    else
234
7.19k
    {
235
7.19k
        mpGraphics->DrawPolygon( nPoints, pPtAry, *this );
236
7.19k
    }
237
7.45k
}
238
239
// Caution: This method is nearly the same as
240
// OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly, double fTransparency),
241
// so when changes are made here do not forget to make changes there, too
242
243
void OutputDevice::DrawPolyPolygon( const basegfx::B2DPolyPolygon& rB2DPolyPoly )
244
3.94k
{
245
3.94k
    assert(!is_double_buffered_window());
246
247
3.94k
    if( mpMetaFile )
248
3.94k
        mpMetaFile->AddAction( new MetaPolyPolygonAction( tools::PolyPolygon( rB2DPolyPoly ) ) );
249
250
    // call helper
251
3.94k
    ImplDrawPolyPolygonWithB2DPolyPolygon(rB2DPolyPoly);
252
3.94k
}
253
254
void OutputDevice::ImplDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyPolygon& rB2DPolyPoly)
255
393k
{
256
    // Do not paint empty PolyPolygons
257
393k
    if(!rB2DPolyPoly.count() || !IsDeviceOutputNecessary())
258
3.94k
        return;
259
260
    // we need a graphics
261
389k
    if( !mpGraphics && !AcquireGraphics() )
262
0
        return;
263
389k
    assert(mpGraphics);
264
265
389k
    if( mbInitClipRegion )
266
0
        InitClipRegion();
267
268
389k
    if( mbOutputClipped )
269
0
        return;
270
271
389k
    if( mbInitLineColor )
272
0
        InitLineColor();
273
274
389k
    if( mbInitFillColor )
275
0
        InitFillColor();
276
277
389k
    bool bSuccess(false);
278
279
389k
    if (RasterOp::OverPaint == GetRasterOp() && (IsLineColor() || IsFillColor()))
280
152k
    {
281
152k
        const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation());
282
152k
        basegfx::B2DPolyPolygon aB2DPolyPolygon(rB2DPolyPoly);
283
152k
        bSuccess = true;
284
285
        // ensure closed - maybe assert, hinders buffering
286
152k
        if(!aB2DPolyPolygon.isClosed())
287
0
        {
288
0
            aB2DPolyPolygon.setClosed(true);
289
0
        }
290
291
152k
        if (IsFillColor())
292
152k
        {
293
152k
            mpGraphics->DrawPolyPolygon(
294
152k
                aTransform,
295
152k
                aB2DPolyPolygon,
296
152k
                0.0,
297
152k
                *this);
298
152k
        }
299
300
152k
        if (IsLineColor())
301
0
        {
302
0
            const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
303
304
0
            for(auto const& rPolygon : std::as_const(aB2DPolyPolygon))
305
0
            {
306
0
                bSuccess = mpGraphics->DrawPolyLine(
307
0
                    aTransform,
308
0
                    rPolygon,
309
0
                    (255 - GetLineColor().GetAlpha()) / 255.0,
310
0
                    0.0, // tdf#124848 hairline
311
0
                    nullptr, // MM01
312
0
                    basegfx::B2DLineJoin::NONE,
313
0
                    css::drawing::LineCap_BUTT,
314
0
                    basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
315
0
                    bPixelSnapHairline,
316
0
                    *this);
317
0
                if (!bSuccess)
318
0
                    break;
319
0
            }
320
0
        }
321
152k
    }
322
323
389k
    if (!bSuccess)
324
237k
    {
325
        // fallback to old polygon drawing if needed
326
237k
        const tools::PolyPolygon aToolsPolyPolygon(rB2DPolyPoly);
327
237k
        const tools::PolyPolygon aPixelPolyPolygon = ImplLogicToDevicePixel(aToolsPolyPolygon);
328
237k
        ImplDrawPolyPolygon(aPixelPolyPolygon.Count(), aPixelPolyPolygon);
329
237k
    }
330
389k
}
331
332
// #100127# Extracted from OutputDevice::DrawPolyPolygon()
333
void OutputDevice::ImplDrawPolyPolygon( sal_uInt16 nPoly, const tools::PolyPolygon& rPolyPoly )
334
343k
{
335
    // AW: This crashes on empty PolyPolygons, avoid that
336
343k
    if(!nPoly)
337
0
        return;
338
339
343k
    sal_uInt32 aStackAry1[OUTDEV_POLYPOLY_STACKBUF];
340
343k
    const Point* aStackAry2[OUTDEV_POLYPOLY_STACKBUF];
341
343k
    const PolyFlags* aStackAry3[OUTDEV_POLYPOLY_STACKBUF];
342
343k
    sal_uInt32* pPointAry;
343
343k
    const Point**    pPointAryAry;
344
343k
    const PolyFlags**  pFlagAryAry;
345
343k
    sal_uInt16 i = 0;
346
343k
    sal_uInt16 j = 0;
347
343k
    sal_uInt16 last = 0;
348
343k
    bool bHaveBezier = false;
349
343k
    if ( nPoly > OUTDEV_POLYPOLY_STACKBUF )
350
0
    {
351
0
        pPointAry       = new sal_uInt32[nPoly];
352
0
        pPointAryAry    = new const Point*[nPoly];
353
0
        pFlagAryAry     = new const PolyFlags*[nPoly];
354
0
    }
355
343k
    else
356
343k
    {
357
343k
        pPointAry       = aStackAry1;
358
343k
        pPointAryAry    = aStackAry2;
359
343k
        pFlagAryAry     = aStackAry3;
360
343k
    }
361
362
343k
    do
363
451k
    {
364
451k
        const tools::Polygon& rPoly = rPolyPoly.GetObject( i );
365
451k
        sal_uInt16 nSize = rPoly.GetSize();
366
451k
        if ( nSize )
367
451k
        {
368
451k
            pPointAry[j] = nSize;
369
451k
            pPointAryAry[j] = rPoly.GetConstPointAry();
370
451k
            pFlagAryAry[j] = rPoly.GetConstFlagAry();
371
451k
            last = i;
372
373
451k
            if( pFlagAryAry[j] )
374
262k
                bHaveBezier = true;
375
376
451k
            ++j;
377
451k
        }
378
451k
        ++i;
379
451k
    }
380
451k
    while ( i < nPoly );
381
382
343k
    if ( j == 1 )
383
237k
    {
384
        // #100127# Forward beziers to sal, if any
385
237k
        if( bHaveBezier )
386
156k
        {
387
156k
            if( !mpGraphics->DrawPolygonBezier( *pPointAry, *pPointAryAry, *pFlagAryAry, *this ) )
388
156k
            {
389
156k
                tools::Polygon aPoly = tools::Polygon::SubdivideBezier( rPolyPoly.GetObject( last ) );
390
156k
                mpGraphics->DrawPolygon( aPoly.GetSize(), aPoly.GetConstPointAry(), *this );
391
156k
            }
392
156k
        }
393
80.2k
        else
394
80.2k
        {
395
80.2k
            mpGraphics->DrawPolygon( *pPointAry, *pPointAryAry, *this );
396
80.2k
        }
397
237k
    }
398
106k
    else
399
106k
    {
400
        // #100127# Forward beziers to sal, if any
401
106k
        if( bHaveBezier )
402
52.5k
        {
403
52.5k
            if (!mpGraphics->DrawPolyPolygonBezier(j, pPointAry, pPointAryAry, pFlagAryAry, *this))
404
52.5k
            {
405
52.5k
                tools::PolyPolygon aPolyPoly = tools::PolyPolygon::SubdivideBezier( rPolyPoly );
406
52.5k
                ImplDrawPolyPolygon( aPolyPoly.Count(), aPolyPoly );
407
52.5k
            }
408
52.5k
        }
409
53.6k
        else
410
53.6k
        {
411
53.6k
            mpGraphics->DrawPolyPolygon( j, pPointAry, pPointAryAry, *this );
412
53.6k
        }
413
106k
    }
414
415
343k
    if ( pPointAry != aStackAry1 )
416
0
    {
417
0
        delete[] pPointAry;
418
0
        delete[] pPointAryAry;
419
0
        delete[] pFlagAryAry;
420
0
    }
421
343k
}
422
423
void OutputDevice::ImplDrawPolygon( const tools::Polygon& rPoly, const tools::PolyPolygon* pClipPolyPoly )
424
8.42M
{
425
8.42M
    if( pClipPolyPoly )
426
0
    {
427
0
        ImplDrawPolyPolygon( tools::PolyPolygon(rPoly), pClipPolyPoly );
428
0
    }
429
8.42M
    else
430
8.42M
    {
431
8.42M
        sal_uInt16 nPoints = rPoly.GetSize();
432
433
8.42M
        if ( nPoints < 2 )
434
36
            return;
435
436
8.42M
        const Point* pPtAry = rPoly.GetConstPointAry();
437
8.42M
        mpGraphics->DrawPolygon( nPoints, pPtAry, *this );
438
8.42M
    }
439
8.42M
}
440
441
void OutputDevice::ImplDrawPolyPolygon( const tools::PolyPolygon& rPolyPoly, const tools::PolyPolygon* pClipPolyPoly )
442
436k
{
443
436k
    tools::PolyPolygon* pPolyPoly;
444
445
436k
    if( pClipPolyPoly )
446
0
    {
447
0
        pPolyPoly = new tools::PolyPolygon;
448
0
        rPolyPoly.GetIntersection( *pClipPolyPoly, *pPolyPoly );
449
0
    }
450
436k
    else
451
436k
    {
452
436k
        pPolyPoly = const_cast<tools::PolyPolygon*>(&rPolyPoly);
453
436k
    }
454
436k
    if( pPolyPoly->Count() == 1 )
455
0
    {
456
0
        const tools::Polygon& rPoly = pPolyPoly->GetObject( 0 );
457
0
        sal_uInt16 nSize = rPoly.GetSize();
458
459
0
        if( nSize >= 2 )
460
0
        {
461
0
            const Point* pPtAry = rPoly.GetConstPointAry();
462
0
            mpGraphics->DrawPolygon( nSize, pPtAry, *this );
463
0
        }
464
0
    }
465
436k
    else if( pPolyPoly->Count() )
466
436k
    {
467
436k
        sal_uInt16 nCount = pPolyPoly->Count();
468
436k
        std::unique_ptr<sal_uInt32[]> pPointAry(new sal_uInt32[nCount]);
469
436k
        std::unique_ptr<const Point*[]> pPointAryAry(new const Point*[nCount]);
470
436k
        sal_uInt16 i = 0;
471
436k
        do
472
873k
        {
473
873k
            const tools::Polygon& rPoly = pPolyPoly->GetObject( i );
474
873k
            sal_uInt16 nSize = rPoly.GetSize();
475
873k
            if ( nSize )
476
873k
            {
477
873k
                pPointAry[i] = nSize;
478
873k
                pPointAryAry[i] = rPoly.GetConstPointAry();
479
873k
                i++;
480
873k
            }
481
0
            else
482
0
                nCount--;
483
873k
        }
484
873k
        while( i < nCount );
485
486
436k
        if( nCount == 1 )
487
0
            mpGraphics->DrawPolygon( pPointAry[0], pPointAryAry[0], *this );
488
436k
        else
489
436k
            mpGraphics->DrawPolyPolygon( nCount, pPointAry.get(), pPointAryAry.get(), *this );
490
436k
    }
491
492
436k
    if( pClipPolyPoly )
493
0
        delete pPolyPoly;
494
436k
}
495
496
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */