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/gradient.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 <tools/gen.hxx>
21
22
#include <vcl/gradient.hxx>
23
#include <vcl/metaact.hxx>
24
#include <vcl/rendercontext/State.hxx>
25
#include <cmath>
26
27
class Gradient::Impl
28
{
29
public:
30
    css::awt::GradientStyle       meStyle;
31
    Color               maStartColor;
32
    Color               maEndColor;
33
    Degree10            mnAngle;
34
    sal_uInt16          mnBorder;
35
    sal_uInt16          mnOfsX;
36
    sal_uInt16          mnOfsY;
37
    sal_uInt16          mnIntensityStart;
38
    sal_uInt16          mnIntensityEnd;
39
    sal_uInt16          mnStepCount;
40
41
    Impl()
42
25.5k
        : meStyle (css::awt::GradientStyle_LINEAR)
43
25.5k
        , maStartColor(COL_BLACK)
44
25.5k
        , maEndColor(COL_WHITE)
45
25.5k
        , mnAngle(0)
46
25.5k
        , mnBorder(0)
47
25.5k
        , mnOfsX(50)
48
25.5k
        , mnOfsY(50)
49
25.5k
        , mnIntensityStart(100)
50
25.5k
        , mnIntensityEnd(100)
51
25.5k
        , mnStepCount(0)
52
25.5k
    {
53
25.5k
    }
54
55
    Impl(const Impl& rImplGradient)
56
6.03k
        : meStyle (rImplGradient.meStyle)
57
6.03k
        , maStartColor(rImplGradient.maStartColor)
58
6.03k
        , maEndColor(rImplGradient.maEndColor)
59
6.03k
        , mnAngle(rImplGradient.mnAngle)
60
6.03k
        , mnBorder(rImplGradient.mnBorder)
61
6.03k
        , mnOfsX(rImplGradient.mnOfsX)
62
6.03k
        , mnOfsY(rImplGradient.mnOfsY)
63
6.03k
        , mnIntensityStart(rImplGradient.mnIntensityStart)
64
6.03k
        , mnIntensityEnd(rImplGradient.mnIntensityEnd)
65
6.03k
        , mnStepCount(rImplGradient.mnStepCount)
66
6.03k
    {
67
6.03k
    }
68
69
    bool operator==(const Impl& rImpl_Gradient) const
70
0
    {
71
0
        return (meStyle == rImpl_Gradient.meStyle)
72
0
            && (mnAngle  == rImpl_Gradient.mnAngle)
73
0
            && (mnBorder == rImpl_Gradient.mnBorder)
74
0
            && (mnOfsX == rImpl_Gradient.mnOfsX)
75
0
            && (mnOfsY == rImpl_Gradient.mnOfsY)
76
0
            && (mnStepCount == rImpl_Gradient.mnStepCount)
77
0
            && (mnIntensityStart == rImpl_Gradient.mnIntensityStart)
78
0
            && (mnIntensityEnd == rImpl_Gradient.mnIntensityEnd)
79
0
            && (maStartColor == rImpl_Gradient.maStartColor)
80
0
            && (maEndColor == rImpl_Gradient.maEndColor);
81
0
    }
82
};
83
84
25.5k
Gradient::Gradient() = default;
85
86
44.3k
Gradient::Gradient( const Gradient& ) = default;
87
88
24.9k
Gradient::Gradient( Gradient&& ) = default;
89
90
Gradient::Gradient( css::awt::GradientStyle eStyle,
91
                    const Color& rStartColor, const Color& rEndColor )
92
38
{
93
38
    mpImplGradient->meStyle         = eStyle;
94
38
    mpImplGradient->maStartColor    = rStartColor;
95
38
    mpImplGradient->maEndColor      = rEndColor;
96
38
}
97
98
94.8k
Gradient::~Gradient() = default;
99
100
101
css::awt::GradientStyle Gradient::GetStyle() const
102
768k
{
103
768k
    return mpImplGradient->meStyle;
104
768k
}
105
106
void Gradient::SetStyle( css::awt::GradientStyle eStyle )
107
25.5k
{
108
25.5k
    mpImplGradient->meStyle = eStyle;
109
25.5k
}
110
111
const Color& Gradient::GetStartColor() const
112
8.88k
{
113
8.88k
    return mpImplGradient->maStartColor;
114
8.88k
}
115
116
void Gradient::SetStartColor( const Color& rColor )
117
25.5k
{
118
25.5k
    mpImplGradient->maStartColor = rColor;
119
25.5k
}
120
121
const Color& Gradient::GetEndColor() const
122
8.84k
{
123
8.84k
    return mpImplGradient->maEndColor;
124
8.84k
}
125
126
void Gradient::SetEndColor( const Color& rColor )
127
25.5k
{
128
25.5k
    mpImplGradient->maEndColor = rColor;
129
25.5k
}
130
131
Degree10 Gradient::GetAngle() const
132
21.0k
{
133
21.0k
    return mpImplGradient->mnAngle;
134
21.0k
}
135
136
void Gradient::SetAngle( Degree10 nAngle )
137
30.7k
{
138
30.7k
    mpImplGradient->mnAngle = nAngle;
139
30.7k
}
140
141
sal_uInt16 Gradient::GetBorder() const
142
12.8k
{
143
12.8k
    return mpImplGradient->mnBorder;
144
12.8k
}
145
146
void Gradient::SetBorder( sal_uInt16 nBorder )
147
25.5k
{
148
25.5k
    mpImplGradient->mnBorder = nBorder;
149
25.5k
}
150
151
sal_uInt16 Gradient::GetOfsX() const
152
2.53k
{
153
2.53k
    return mpImplGradient->mnOfsX;
154
2.53k
}
155
156
void Gradient::SetOfsX( sal_uInt16 nOfsX )
157
25.5k
{
158
25.5k
    mpImplGradient->mnOfsX = nOfsX;
159
25.5k
}
160
161
sal_uInt16 Gradient::GetOfsY() const
162
2.53k
{
163
2.53k
    return mpImplGradient->mnOfsY;
164
2.53k
}
165
166
void Gradient::SetOfsY( sal_uInt16 nOfsY )
167
25.5k
{
168
25.5k
    mpImplGradient->mnOfsY = nOfsY;
169
25.5k
}
170
171
sal_uInt16 Gradient::GetStartIntensity() const
172
21.0k
{
173
21.0k
    return mpImplGradient->mnIntensityStart;
174
21.0k
}
175
176
void Gradient::SetStartIntensity( sal_uInt16 nIntens )
177
25.5k
{
178
25.5k
    mpImplGradient->mnIntensityStart = nIntens;
179
25.5k
}
180
181
sal_uInt16 Gradient::GetEndIntensity() const
182
21.0k
{
183
21.0k
    return mpImplGradient->mnIntensityEnd;
184
21.0k
}
185
186
void Gradient::SetEndIntensity( sal_uInt16 nIntens )
187
25.5k
{
188
25.5k
    mpImplGradient->mnIntensityEnd = nIntens;
189
25.5k
}
190
191
sal_uInt16 Gradient::GetSteps() const
192
14.6k
{
193
14.6k
    return mpImplGradient->mnStepCount;
194
14.6k
}
195
196
void Gradient::SetSteps( sal_uInt16 nSteps )
197
26.3k
{
198
26.3k
    mpImplGradient->mnStepCount = nSteps;
199
26.3k
}
200
201
void Gradient::GetBoundRect( const tools::Rectangle& rRect, tools::Rectangle& rBoundRect, Point& rCenter ) const
202
8.69k
{
203
8.69k
    tools::Rectangle aRect( rRect );
204
8.69k
    Degree10 nAngle = GetAngle() % 3600_deg10;
205
206
8.69k
    if( GetStyle() == css::awt::GradientStyle_LINEAR || GetStyle() == css::awt::GradientStyle_AXIAL )
207
6.15k
    {
208
6.15k
        const double    fAngle = toRadians(nAngle);
209
6.15k
        const double    fWidth = aRect.GetWidth();
210
6.15k
        const double    fHeight = aRect.GetHeight();
211
6.15k
        double  fDX     = fWidth  * fabs( cos( fAngle ) ) +
212
6.15k
                          fHeight * fabs( sin( fAngle ) );
213
6.15k
        double  fDY     = fHeight * fabs( cos( fAngle ) ) +
214
6.15k
                          fWidth  * fabs( sin( fAngle ) );
215
6.15k
        fDX     = (fDX - fWidth)  * 0.5 + 0.5;
216
6.15k
        fDY     = (fDY - fHeight) * 0.5 + 0.5;
217
6.15k
        aRect.AdjustLeft( -static_cast<tools::Long>(fDX) );
218
6.15k
        aRect.AdjustRight(static_cast<tools::Long>(fDX) );
219
6.15k
        aRect.AdjustTop( -static_cast<tools::Long>(fDY) );
220
6.15k
        aRect.AdjustBottom(static_cast<tools::Long>(fDY) );
221
222
6.15k
        rBoundRect = aRect;
223
6.15k
        rCenter = rRect.Center();
224
6.15k
    }
225
2.53k
    else
226
2.53k
    {
227
2.53k
        if( GetStyle() == css::awt::GradientStyle_SQUARE || GetStyle() == css::awt::GradientStyle_RECT )
228
11
        {
229
11
            const double    fAngle = toRadians(nAngle);
230
11
            const double    fWidth = aRect.GetWidth();
231
11
            const double    fHeight = aRect.GetHeight();
232
11
            double          fDX = fWidth  * fabs( cos( fAngle ) ) + fHeight * fabs( sin( fAngle ) );
233
11
            double          fDY = fHeight * fabs( cos( fAngle ) ) + fWidth  * fabs( sin( fAngle ) );
234
235
11
            fDX = ( fDX - fWidth  ) * 0.5 + 0.5;
236
11
            fDY = ( fDY - fHeight ) * 0.5 + 0.5;
237
238
11
            aRect.AdjustLeft( -static_cast<tools::Long>(fDX) );
239
11
            aRect.AdjustRight(static_cast<tools::Long>(fDX) );
240
11
            aRect.AdjustTop( -static_cast<tools::Long>(fDY) );
241
11
            aRect.AdjustBottom(static_cast<tools::Long>(fDY) );
242
11
        }
243
244
2.53k
        Size aSize( aRect.GetSize() );
245
246
2.53k
        if( GetStyle() == css::awt::GradientStyle_RADIAL )
247
1.65k
        {
248
            // Calculation of radii for circle
249
1.65k
            aSize.setWidth( static_cast<tools::Long>(0.5 + std::hypot(aSize.Width(), aSize.Height())) );
250
1.65k
            aSize.setHeight( aSize.Width() );
251
1.65k
        }
252
879
        else if( GetStyle() == css::awt::GradientStyle_ELLIPTICAL )
253
10
        {
254
            // Calculation of radii for ellipse
255
10
            aSize.setWidth( static_cast<tools::Long>( 0.5 + static_cast<double>(aSize.Width())  * M_SQRT2 ) );
256
10
            aSize.setHeight( static_cast<tools::Long>( 0.5 + static_cast<double>(aSize.Height()) * M_SQRT2) );
257
10
        }
258
259
        // Calculate new centers
260
2.53k
        tools::Long    nZWidth = aRect.GetWidth() * static_cast<tools::Long>(GetOfsX()) / 100;
261
2.53k
        tools::Long    nZHeight = aRect.GetHeight() * static_cast<tools::Long>(GetOfsY()) / 100;
262
2.53k
        tools::Long    nBorderX = static_cast<tools::Long>(GetBorder()) * aSize.Width()  / 100;
263
2.53k
        tools::Long    nBorderY = static_cast<tools::Long>(GetBorder()) * aSize.Height() / 100;
264
2.53k
        rCenter = Point( aRect.Left() + nZWidth, aRect.Top() + nZHeight );
265
266
        // Respect borders
267
2.53k
        aSize.AdjustWidth( -nBorderX );
268
2.53k
        aSize.AdjustHeight( -nBorderY );
269
270
        // Recalculate output rectangle
271
2.53k
        aRect.SetLeft( rCenter.X() - ( aSize.Width() >> 1 ) );
272
2.53k
        aRect.SetTop( rCenter.Y() - ( aSize.Height() >> 1 ) );
273
274
2.53k
        aRect.SetSize( aSize );
275
2.53k
        rBoundRect = aRect;
276
2.53k
    }
277
8.69k
}
278
279
void Gradient::MakeGrayscale()
280
24
{
281
24
    Color aStartCol(GetStartColor());
282
24
    Color aEndCol(GetEndColor());
283
24
    sal_uInt8 cStartLum = aStartCol.GetLuminance();
284
24
    sal_uInt8 cEndLum = aEndCol.GetLuminance();
285
286
24
    aStartCol = Color(cStartLum, cStartLum, cStartLum);
287
24
    aEndCol = Color(cEndLum, cEndLum, cEndLum);
288
289
24
    SetStartColor(aStartCol);
290
24
    SetEndColor(aEndCol);
291
24
}
292
293
0
Gradient& Gradient::operator=( const Gradient& ) = default;
294
295
0
Gradient& Gradient::operator=( Gradient&& ) = default;
296
297
bool Gradient::operator==( const Gradient& rGradient ) const
298
0
{
299
0
    return mpImplGradient == rGradient.mpImplGradient;
300
0
}
301
302
const sal_uInt32 GRADIENT_DEFAULT_STEPCOUNT = 0;
303
304
void Gradient::AddGradientActions(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile)
305
0
{
306
0
    tools::Rectangle aRect(rRect);
307
0
    aRect.Normalize();
308
309
    // do nothing if the rectangle is empty
310
0
    if (aRect.IsEmpty())
311
0
        return;
312
313
0
    rMetaFile.AddAction(new MetaPushAction(vcl::PushFlags::ALL));
314
0
    rMetaFile.AddAction(new MetaISectRectClipRegionAction( aRect));
315
0
    rMetaFile.AddAction(new MetaLineColorAction(Color(), false));
316
317
    // because we draw with no border line, we have to expand gradient
318
    // rect to avoid missing lines on the right and bottom edge
319
0
    aRect.AdjustLeft( -1 );
320
0
    aRect.AdjustTop( -1 );
321
0
    aRect.AdjustRight( 1 );
322
0
    aRect.AdjustBottom( 1 );
323
324
    // calculate step count if necessary
325
0
    if (!GetSteps())
326
0
        SetSteps(GRADIENT_DEFAULT_STEPCOUNT);
327
328
0
    if (GetStyle() == css::awt::GradientStyle_LINEAR || GetStyle() == css::awt::GradientStyle_AXIAL)
329
0
        DrawLinearGradientToMetafile(aRect, rMetaFile);
330
0
    else
331
0
        DrawComplexGradientToMetafile(aRect, rMetaFile);
332
333
0
    rMetaFile.AddAction(new MetaPopAction());
334
0
}
335
336
tools::Long Gradient::GetMetafileSteps(tools::Rectangle const& rRect) const
337
0
{
338
    // calculate step count
339
0
    tools::Long nStepCount = GetSteps();
340
341
0
    if (nStepCount)
342
0
        return nStepCount;
343
344
0
    if (GetStyle() == css::awt::GradientStyle_LINEAR || GetStyle() == css::awt::GradientStyle_AXIAL)
345
0
        return rRect.GetHeight();
346
0
    else
347
0
        return std::min(rRect.GetWidth(), rRect.GetHeight());
348
0
}
349
350
351
static sal_uInt8 GetGradientColorValue(tools::Long nValue)
352
0
{
353
0
    if ( nValue < 0 )
354
0
        return 0;
355
0
    else if ( nValue > 0xFF )
356
0
        return 0xFF;
357
0
    else
358
0
        return static_cast<sal_uInt8>(nValue);
359
0
}
360
361
void Gradient::DrawLinearGradientToMetafile(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) const
362
0
{
363
    // get BoundRect of rotated rectangle
364
0
    tools::Rectangle aRect;
365
0
    Point aCenter;
366
0
    Degree10 nAngle = GetAngle() % 3600_deg10;
367
368
0
    GetBoundRect(rRect, aRect, aCenter);
369
370
0
    bool bLinear = (GetStyle() == css::awt::GradientStyle_LINEAR);
371
0
    double fBorder = GetBorder() * aRect.GetHeight() / 100.0;
372
0
    if ( !bLinear )
373
0
    {
374
0
        fBorder /= 2.0;
375
0
    }
376
0
    tools::Rectangle aMirrorRect = aRect; // used in style axial
377
0
    aMirrorRect.SetTop( ( aRect.Top() + aRect.Bottom() ) / 2 );
378
0
    if ( !bLinear )
379
0
    {
380
0
        aRect.SetBottom( aMirrorRect.Top() );
381
0
    }
382
383
    // colour-intensities of start- and finish; change if needed
384
0
    tools::Long    nFactor;
385
0
    Color   aStartCol   = GetStartColor();
386
0
    Color   aEndCol     = GetEndColor();
387
0
    tools::Long    nStartRed   = aStartCol.GetRed();
388
0
    tools::Long    nStartGreen = aStartCol.GetGreen();
389
0
    tools::Long    nStartBlue  = aStartCol.GetBlue();
390
0
    tools::Long    nEndRed     = aEndCol.GetRed();
391
0
    tools::Long    nEndGreen   = aEndCol.GetGreen();
392
0
    tools::Long    nEndBlue    = aEndCol.GetBlue();
393
0
    nFactor     = GetStartIntensity();
394
0
    nStartRed   = (nStartRed   * nFactor) / 100;
395
0
    nStartGreen = (nStartGreen * nFactor) / 100;
396
0
    nStartBlue  = (nStartBlue  * nFactor) / 100;
397
0
    nFactor     = GetEndIntensity();
398
0
    nEndRed     = (nEndRed   * nFactor) / 100;
399
0
    nEndGreen   = (nEndGreen * nFactor) / 100;
400
0
    nEndBlue    = (nEndBlue  * nFactor) / 100;
401
402
    // gradient style axial has exchanged start and end colors
403
0
    if ( !bLinear)
404
0
    {
405
0
        std::swap( nStartRed, nEndRed );
406
0
        std::swap( nStartGreen, nEndGreen );
407
0
        std::swap( nStartBlue, nEndBlue );
408
0
    }
409
410
0
    sal_uInt8   nRed;
411
0
    sal_uInt8   nGreen;
412
0
    sal_uInt8   nBlue;
413
414
    // Create border
415
0
    tools::Rectangle aBorderRect = aRect;
416
0
    tools::Polygon aPoly( 4 );
417
0
    if (fBorder > 0.0)
418
0
    {
419
0
        nRed        = static_cast<sal_uInt8>(nStartRed);
420
0
        nGreen      = static_cast<sal_uInt8>(nStartGreen);
421
0
        nBlue       = static_cast<sal_uInt8>(nStartBlue);
422
423
0
        rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
424
425
0
        aBorderRect.SetBottom( static_cast<tools::Long>( aBorderRect.Top() + fBorder ) );
426
0
        aRect.SetTop( aBorderRect.Bottom() );
427
0
        aPoly[0] = aBorderRect.TopLeft();
428
0
        aPoly[1] = aBorderRect.TopRight();
429
0
        aPoly[2] = aBorderRect.BottomRight();
430
0
        aPoly[3] = aBorderRect.BottomLeft();
431
0
        aPoly.Rotate( aCenter, nAngle );
432
433
0
        rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
434
435
0
        if ( !bLinear)
436
0
        {
437
0
            aBorderRect = aMirrorRect;
438
0
            aBorderRect.SetTop( static_cast<tools::Long>( aBorderRect.Bottom() - fBorder ) );
439
0
            aMirrorRect.SetBottom( aBorderRect.Top() );
440
0
            aPoly[0] = aBorderRect.TopLeft();
441
0
            aPoly[1] = aBorderRect.TopRight();
442
0
            aPoly[2] = aBorderRect.BottomRight();
443
0
            aPoly[3] = aBorderRect.BottomLeft();
444
0
            aPoly.Rotate( aCenter, nAngle );
445
446
0
            rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
447
0
        }
448
0
    }
449
450
0
    tools::Long nStepCount = GetMetafileSteps(aRect);
451
452
    // minimal three steps and maximal as max color steps
453
0
    tools::Long   nAbsRedSteps   = std::abs( nEndRed   - nStartRed );
454
0
    tools::Long   nAbsGreenSteps = std::abs( nEndGreen - nStartGreen );
455
0
    tools::Long   nAbsBlueSteps  = std::abs( nEndBlue  - nStartBlue );
456
0
    tools::Long   nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps );
457
0
    nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps );
458
0
    tools::Long nSteps = std::min( nStepCount, nMaxColorSteps );
459
0
    if ( nSteps < 3)
460
0
    {
461
0
        nSteps = 3;
462
0
    }
463
464
0
    double fScanInc = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps);
465
0
    double fGradientLine = static_cast<double>(aRect.Top());
466
0
    double fMirrorGradientLine = static_cast<double>(aMirrorRect.Bottom());
467
468
0
    const double fStepsMinus1 = static_cast<double>(nSteps) - 1.0;
469
0
    if ( !bLinear)
470
0
    {
471
0
        nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap
472
0
    }
473
0
    for ( tools::Long i = 0; i < nSteps; i++ )
474
0
    {
475
        // linear interpolation of color
476
0
        double fAlpha = static_cast<double>(i) / fStepsMinus1;
477
0
        double fTempColor = static_cast<double>(nStartRed) * (1.0-fAlpha) + static_cast<double>(nEndRed) * fAlpha;
478
0
        nRed = GetGradientColorValue(static_cast<tools::Long>(fTempColor));
479
0
        fTempColor = static_cast<double>(nStartGreen) * (1.0-fAlpha) + static_cast<double>(nEndGreen) * fAlpha;
480
0
        nGreen = GetGradientColorValue(static_cast<tools::Long>(fTempColor));
481
0
        fTempColor = static_cast<double>(nStartBlue) * (1.0-fAlpha) + static_cast<double>(nEndBlue) * fAlpha;
482
0
        nBlue = GetGradientColorValue(static_cast<tools::Long>(fTempColor));
483
484
0
        rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
485
486
        // Polygon for this color step
487
0
        aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(i) * fScanInc ) );
488
0
        aRect.SetBottom( static_cast<tools::Long>( fGradientLine + ( static_cast<double>(i) + 1.0 ) * fScanInc ) );
489
0
        aPoly[0] = aRect.TopLeft();
490
0
        aPoly[1] = aRect.TopRight();
491
0
        aPoly[2] = aRect.BottomRight();
492
0
        aPoly[3] = aRect.BottomLeft();
493
0
        aPoly.Rotate( aCenter, nAngle );
494
495
0
        rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
496
497
0
        if ( !bLinear )
498
0
        {
499
0
            aMirrorRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(i) * fScanInc ) );
500
0
            aMirrorRect.SetTop( static_cast<tools::Long>( fMirrorGradientLine - (static_cast<double>(i) + 1.0)* fScanInc ) );
501
0
            aPoly[0] = aMirrorRect.TopLeft();
502
0
            aPoly[1] = aMirrorRect.TopRight();
503
0
            aPoly[2] = aMirrorRect.BottomRight();
504
0
            aPoly[3] = aMirrorRect.BottomLeft();
505
0
            aPoly.Rotate( aCenter, nAngle );
506
507
0
            rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
508
0
        }
509
0
    }
510
0
    if ( bLinear)
511
0
        return;
512
513
    // draw middle polygon with end color
514
0
    nRed = GetGradientColorValue(nEndRed);
515
0
    nGreen = GetGradientColorValue(nEndGreen);
516
0
    nBlue = GetGradientColorValue(nEndBlue);
517
518
0
    rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
519
520
0
    aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(nSteps) * fScanInc ) );
521
0
    aRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(nSteps) * fScanInc ) );
522
0
    aPoly[0] = aRect.TopLeft();
523
0
    aPoly[1] = aRect.TopRight();
524
0
    aPoly[2] = aRect.BottomRight();
525
0
    aPoly[3] = aRect.BottomLeft();
526
0
    aPoly.Rotate( aCenter, nAngle );
527
528
0
    rMetaFile.AddAction( new MetaPolygonAction( std::move(aPoly) ) );
529
530
0
}
531
532
void Gradient::DrawComplexGradientToMetafile(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) const
533
0
{
534
    // Determine if we output via Polygon or PolyPolygon
535
    // For all rasteroperations other than Overpaint always use PolyPolygon,
536
    // as we will get wrong results if we output multiple times on top of each other.
537
    // Also for printers always use PolyPolygon, as not all printers
538
    // can print polygons on top of each other.
539
540
0
    tools::Rectangle aRect;
541
0
    Point aCenter;
542
0
    GetBoundRect(rRect, aRect, aCenter);
543
544
0
    std::optional<tools::PolyPolygon> xPolyPoly;
545
0
    xPolyPoly = tools::PolyPolygon( 2 );
546
547
    // last parameter - true if complex gradient, false if linear
548
0
    tools::Long nStepCount = GetMetafileSteps(rRect);
549
550
    // at least three steps and at most the number of colour differences
551
0
    tools::Long nSteps = std::max(nStepCount, tools::Long(2));
552
553
0
    Color aStartCol(GetStartColor());
554
0
    Color aEndCol(GetEndColor());
555
556
0
    tools::Long nStartRed = (static_cast<tools::Long>(aStartCol.GetRed()) * GetStartIntensity()) / 100;
557
0
    tools::Long nStartGreen = (static_cast<tools::Long>(aStartCol.GetGreen()) * GetStartIntensity()) / 100;
558
0
    tools::Long nStartBlue = (static_cast<tools::Long>(aStartCol.GetBlue()) * GetStartIntensity()) / 100;
559
560
0
    tools::Long nEndRed = (static_cast<tools::Long>(aEndCol.GetRed()) * GetEndIntensity()) / 100;
561
0
    tools::Long nEndGreen = (static_cast<tools::Long>(aEndCol.GetGreen()) * GetEndIntensity()) / 100;
562
0
    tools::Long nEndBlue = (static_cast<tools::Long>(aEndCol.GetBlue()) * GetEndIntensity()) / 100;
563
564
0
    tools::Long nRedSteps = nEndRed - nStartRed;
565
0
    tools::Long nGreenSteps = nEndGreen - nStartGreen;
566
0
    tools::Long nBlueSteps = nEndBlue - nStartBlue;
567
568
0
    tools::Long nCalcSteps  = std::abs(nRedSteps);
569
0
    tools::Long nTempSteps = std::abs(nGreenSteps);
570
571
0
    if (nTempSteps > nCalcSteps)
572
0
        nCalcSteps = nTempSteps;
573
574
0
    nTempSteps = std::abs( nBlueSteps );
575
576
0
    if (nTempSteps > nCalcSteps)
577
0
        nCalcSteps = nTempSteps;
578
579
0
    if (nCalcSteps < nSteps)
580
0
        nSteps = nCalcSteps;
581
582
0
    if ( !nSteps )
583
0
        nSteps = 1;
584
585
    // determine output limits and stepsizes for all directions
586
0
    tools::Polygon aPoly;
587
0
    double  fScanLeft = aRect.Left();
588
0
    double  fScanTop = aRect.Top();
589
0
    double  fScanRight = aRect.Right();
590
0
    double  fScanBottom = aRect.Bottom();
591
0
    double fScanIncX = static_cast<double>(aRect.GetWidth()) / static_cast<double>(nSteps) * 0.5;
592
0
    double fScanIncY = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps) * 0.5;
593
594
    // all gradients are rendered as nested rectangles which shrink
595
    // equally in each dimension - except for 'square' gradients
596
    // which shrink to a central vertex but are not per-se square.
597
0
    if (GetStyle() != css::awt::GradientStyle_SQUARE)
598
0
    {
599
0
        fScanIncY = std::min( fScanIncY, fScanIncX );
600
0
        fScanIncX = fScanIncY;
601
0
    }
602
0
    sal_uInt8   nRed = static_cast<sal_uInt8>(nStartRed), nGreen = static_cast<sal_uInt8>(nStartGreen), nBlue = static_cast<sal_uInt8>(nStartBlue);
603
0
    bool    bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output
604
605
0
    rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
606
607
0
    aPoly = tools::Polygon(rRect);
608
0
    xPolyPoly->Insert( aPoly );
609
0
    xPolyPoly->Insert( aPoly );
610
611
    // loop to output Polygon/PolyPolygon sequentially
612
0
    for( tools::Long i = 1; i < nSteps; i++ )
613
0
    {
614
        // calculate new Polygon
615
0
        fScanLeft += fScanIncX;
616
0
        aRect.SetLeft( static_cast<tools::Long>( fScanLeft ) );
617
0
        fScanTop += fScanIncY;
618
0
        aRect.SetTop( static_cast<tools::Long>( fScanTop ) );
619
0
        fScanRight -= fScanIncX;
620
0
        aRect.SetRight( static_cast<tools::Long>( fScanRight ) );
621
0
        fScanBottom -= fScanIncY;
622
0
        aRect.SetBottom( static_cast<tools::Long>( fScanBottom ) );
623
624
0
        if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
625
0
            break;
626
627
0
        if (GetStyle() == css::awt::GradientStyle_RADIAL || GetStyle() == css::awt::GradientStyle_ELLIPTICAL)
628
0
            aPoly = tools::Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
629
0
        else
630
0
            aPoly = tools::Polygon( aRect );
631
632
0
        aPoly.Rotate(aCenter, GetAngle() % 3600_deg10);
633
634
        // adapt colour accordingly
635
0
        const tools::Long nStepIndex = ( xPolyPoly ? i : ( i + 1 ) );
636
0
        nRed = GetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) );
637
0
        nGreen = GetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) );
638
0
        nBlue = GetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) );
639
640
0
        bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output
641
642
0
        xPolyPoly->Replace( xPolyPoly->GetObject( 1 ), 0 );
643
0
        xPolyPoly->Replace( aPoly, 1 );
644
645
0
        rMetaFile.AddAction( new MetaPolyPolygonAction( *xPolyPoly ) );
646
647
        // #107349# Set fill color _after_ geometry painting:
648
        // xPolyPoly's geometry is the band from last iteration's
649
        // aPoly to current iteration's aPoly. The window outdev
650
        // path (see else below), on the other hand, paints the
651
        // full aPoly. Thus, here, we're painting the band before
652
        // the one painted in the window outdev path below. To get
653
        // matching colors, have to delay color setting here.
654
0
        rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
655
0
    }
656
657
0
    const tools::Polygon& rPoly = xPolyPoly->GetObject( 1 );
658
659
0
    if( rPoly.GetBoundRect().IsEmpty() )
660
0
        return;
661
662
    // #107349# Paint last polygon with end color only if loop
663
    // has generated output. Otherwise, the current
664
    // (i.e. start) color is taken, to generate _any_ output.
665
0
    if( bPaintLastPolygon )
666
0
    {
667
0
        nRed = GetGradientColorValue( nEndRed );
668
0
        nGreen = GetGradientColorValue( nEndGreen );
669
0
        nBlue = GetGradientColorValue( nEndBlue );
670
0
    }
671
672
0
    rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
673
0
    rMetaFile.AddAction( new MetaPolygonAction( rPoly ) );
674
0
}
675
676
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */