Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/vcl/source/outdev/gradient.cxx
Line
Count
Source (jump to first uncovered line)
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/poly.hxx>
21
22
#include <vcl/gradient.hxx>
23
#include <vcl/metaact.hxx>
24
#include <vcl/settings.hxx>
25
#include <vcl/virdev.hxx>
26
#include <vcl/window.hxx>
27
28
#include <salgdi.hxx>
29
30
#include <cassert>
31
#include <memory>
32
33
994
#define GRADIENT_DEFAULT_STEPCOUNT  0
34
35
void OutputDevice::DrawGradient( const tools::Rectangle& rRect,
36
                                 const Gradient& rGradient )
37
16.2k
{
38
16.2k
    assert(!is_double_buffered_window());
39
40
    // Convert rectangle to a tools::PolyPolygon by first converting to a Polygon
41
16.2k
    tools::Polygon aPolygon ( rRect );
42
16.2k
    tools::PolyPolygon aPolyPoly ( aPolygon );
43
44
16.2k
    DrawGradient ( aPolyPoly, rGradient );
45
16.2k
}
46
47
void OutputDevice::DrawGradient( const tools::PolyPolygon& rPolyPoly,
48
                                 const Gradient& rGradient )
49
16.2k
{
50
16.2k
    assert(!is_double_buffered_window());
51
52
16.2k
    if (mbInitClipRegion)
53
9.54k
        InitClipRegion();
54
    // don't return on mbOutputClipped here, as we may need to draw the clipped metafile, even if the output is clipped
55
56
16.2k
    if ( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() )
57
16.1k
    {
58
16.1k
        if ( mnDrawMode & ( DrawModeFlags::BlackGradient | DrawModeFlags::WhiteGradient | DrawModeFlags::SettingsGradient) )
59
0
        {
60
0
            Color aColor = GetSingleColorGradientFill();
61
62
0
            Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
63
0
            SetLineColor( aColor );
64
0
            SetFillColor( aColor );
65
0
            DrawPolyPolygon( rPolyPoly );
66
0
            Pop();
67
0
            return;
68
0
        }
69
70
16.1k
        Gradient aGradient( rGradient );
71
72
16.1k
        if ( mnDrawMode & DrawModeFlags::GrayGradient )
73
4
            aGradient.MakeGrayscale();
74
75
16.1k
        DrawGradientToMetafile( rPolyPoly, rGradient );
76
77
16.1k
        if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
78
0
            return;
79
80
        // Clip and then draw the gradient
81
16.1k
        if( !tools::Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
82
14.1k
        {
83
14.1k
            const tools::Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
84
85
            // convert rectangle to pixels
86
14.1k
            tools::Rectangle aRect( ImplLogicToDevicePixel( aBoundRect ) );
87
14.1k
            aRect.Normalize();
88
89
            // do nothing if the rectangle is empty
90
14.1k
            if ( !aRect.IsEmpty() )
91
14.1k
            {
92
14.1k
                tools::PolyPolygon aClixPolyPoly( ImplLogicToDevicePixel( rPolyPoly ) );
93
14.1k
                bool bDrawn = false;
94
95
14.1k
                if( !mpGraphics && !AcquireGraphics() )
96
0
                    return;
97
98
                // secure clip region
99
14.1k
                Push( vcl::PushFlags::CLIPREGION );
100
14.1k
                IntersectClipRegion( aBoundRect );
101
102
14.1k
                if (mbInitClipRegion)
103
14.1k
                    InitClipRegion();
104
105
                // try to draw gradient natively
106
14.1k
                if (!mbOutputClipped)
107
11.0k
                    bDrawn = mpGraphics->DrawGradient( aClixPolyPoly, aGradient, *this );
108
109
14.1k
                if (!bDrawn && !mbOutputClipped)
110
4.35k
                {
111
                    // draw gradients without border
112
4.35k
                    if( mbLineColor || mbInitLineColor )
113
4.02k
                    {
114
4.02k
                        mpGraphics->SetLineColor();
115
4.02k
                        mbInitLineColor = true;
116
4.02k
                    }
117
118
4.35k
                    mbInitFillColor = true;
119
120
                    // calculate step count if necessary
121
4.35k
                    if ( !aGradient.GetSteps() )
122
994
                        aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
123
124
4.35k
                    if ( rPolyPoly.IsRect() )
125
4.35k
                    {
126
                        // because we draw with no border line, we have to expand gradient
127
                        // rect to avoid missing lines on the right and bottom edge
128
4.35k
                        aRect.AdjustLeft( -1 );
129
4.35k
                        aRect.AdjustTop( -1 );
130
4.35k
                        aRect.AdjustRight( 1 );
131
4.35k
                        aRect.AdjustBottom( 1 );
132
4.35k
                    }
133
134
                    // if the clipping polypolygon is a rectangle, then it's the same size as the bounding of the
135
                    // polypolygon, so pass in a NULL for the clipping parameter
136
4.35k
                    if( aGradient.GetStyle() == css::awt::GradientStyle_LINEAR || rGradient.GetStyle() == css::awt::GradientStyle_AXIAL )
137
2.70k
                        DrawLinearGradient( aRect, aGradient, aClixPolyPoly.IsRect() ? nullptr : &aClixPolyPoly );
138
1.65k
                    else
139
1.65k
                        DrawComplexGradient( aRect, aGradient, aClixPolyPoly.IsRect() ? nullptr : &aClixPolyPoly );
140
4.35k
                }
141
142
14.1k
                Pop();
143
14.1k
            }
144
14.1k
        }
145
16.1k
    }
146
147
16.2k
    if( mpAlphaVDev )
148
4
    {
149
4
        mpAlphaVDev->Push(vcl::PushFlags::FILLCOLOR);
150
4
        mpAlphaVDev->SetFillColor( COL_ALPHA_OPAQUE );
151
4
        mpAlphaVDev->DrawPolyPolygon( rPolyPoly );
152
4
        mpAlphaVDev->Pop();
153
4
    }
154
16.2k
}
155
156
void OutputDevice::ClipAndDrawGradientMetafile ( const Gradient &rGradient, const tools::PolyPolygon &rPolyPoly )
157
0
{
158
0
    const bool  bOldOutput = IsOutputEnabled();
159
0
    EnableOutput( false );
160
161
0
    Push( vcl::PushFlags::CLIPREGION );
162
0
    SetClipRegion( vcl::Region( rPolyPoly ) );
163
0
    DrawGradient( rPolyPoly.GetBoundRect(), rGradient );
164
0
    Pop();
165
166
0
    EnableOutput( bOldOutput );
167
0
}
168
169
void OutputDevice::DrawGradientToMetafile ( const tools::PolyPolygon& rPolyPoly,
170
                                            const Gradient& rGradient )
171
16.1k
{
172
16.1k
    assert(!is_double_buffered_window());
173
174
16.1k
    if ( !mpMetaFile )
175
16.1k
        return;
176
177
0
    if ( !(rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize()) )
178
0
        return;
179
180
0
    const tools::Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
181
182
0
    if (aBoundRect.IsEmpty())
183
0
        return;
184
185
0
    Gradient aGradient( rGradient );
186
187
0
    if (mnDrawMode & DrawModeFlags::GrayGradient)
188
0
        aGradient.MakeGrayscale();
189
190
0
    if ( rPolyPoly.IsRect() )
191
0
    {
192
0
        mpMetaFile->AddAction( new MetaGradientAction( aBoundRect, std::move(aGradient) ) );
193
0
    }
194
0
    else
195
0
    {
196
0
        mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN"_ostr ) );
197
0
        mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) );
198
199
0
        ClipAndDrawGradientMetafile ( rGradient, rPolyPoly );
200
201
0
        mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END"_ostr ) );
202
0
    }
203
0
}
204
205
namespace
206
{
207
    sal_uInt8 GetGradientColorValue( tools::Long nValue )
208
6.89M
    {
209
6.89M
        if ( nValue < 0 )
210
0
            return 0;
211
6.89M
        else if ( nValue > 0xFF )
212
6.24M
            return 0xFF;
213
645k
        else
214
645k
            return static_cast<sal_uInt8>(nValue);
215
6.89M
    }
216
}
217
218
void OutputDevice::DrawLinearGradient( const tools::Rectangle& rRect,
219
                                       const Gradient& rGradient,
220
                                       const tools::PolyPolygon* pClixPolyPoly )
221
2.70k
{
222
2.70k
    assert(!is_double_buffered_window());
223
224
    // get BoundRect of rotated rectangle
225
2.70k
    tools::Rectangle aRect;
226
2.70k
    Point     aCenter;
227
2.70k
    Degree10  nAngle = rGradient.GetAngle() % 3600_deg10;
228
229
2.70k
    rGradient.GetBoundRect( rRect, aRect, aCenter );
230
231
2.70k
    bool bLinear = (rGradient.GetStyle() == css::awt::GradientStyle_LINEAR);
232
2.70k
    double fBorder = rGradient.GetBorder() * aRect.GetHeight() / 100.0;
233
2.70k
    if ( !bLinear )
234
3
    {
235
3
        fBorder /= 2.0;
236
3
    }
237
2.70k
    tools::Rectangle aMirrorRect = aRect; // used in style axial
238
2.70k
    aMirrorRect.SetTop( ( aRect.Top() + aRect.Bottom() ) / 2 );
239
2.70k
    if ( !bLinear )
240
3
    {
241
3
        aRect.SetBottom( aMirrorRect.Top() );
242
3
    }
243
244
    // colour-intensities of start- and finish; change if needed
245
2.70k
    tools::Long    nFactor;
246
2.70k
    Color   aStartCol   = rGradient.GetStartColor();
247
2.70k
    Color   aEndCol     = rGradient.GetEndColor();
248
2.70k
    tools::Long    nStartRed   = aStartCol.GetRed();
249
2.70k
    tools::Long    nStartGreen = aStartCol.GetGreen();
250
2.70k
    tools::Long    nStartBlue  = aStartCol.GetBlue();
251
2.70k
    tools::Long    nEndRed     = aEndCol.GetRed();
252
2.70k
    tools::Long    nEndGreen   = aEndCol.GetGreen();
253
2.70k
    tools::Long    nEndBlue    = aEndCol.GetBlue();
254
2.70k
    nFactor     = rGradient.GetStartIntensity();
255
2.70k
    nStartRed   = (nStartRed   * nFactor) / 100;
256
2.70k
    nStartGreen = (nStartGreen * nFactor) / 100;
257
2.70k
    nStartBlue  = (nStartBlue  * nFactor) / 100;
258
2.70k
    nFactor     = rGradient.GetEndIntensity();
259
2.70k
    nEndRed     = (nEndRed   * nFactor) / 100;
260
2.70k
    nEndGreen   = (nEndGreen * nFactor) / 100;
261
2.70k
    nEndBlue    = (nEndBlue  * nFactor) / 100;
262
263
    // gradient style axial has exchanged start and end colors
264
2.70k
    if ( !bLinear)
265
3
    {
266
3
        std::swap( nStartRed, nEndRed );
267
3
        std::swap( nStartGreen, nEndGreen );
268
3
        std::swap( nStartBlue, nEndBlue );
269
3
    }
270
271
2.70k
    sal_uInt8   nRed;
272
2.70k
    sal_uInt8   nGreen;
273
2.70k
    sal_uInt8   nBlue;
274
275
    // Create border
276
2.70k
    tools::Rectangle aBorderRect = aRect;
277
2.70k
    tools::Polygon aPoly( 4 );
278
2.70k
    if (fBorder > 0.0)
279
144
    {
280
144
        nRed        = static_cast<sal_uInt8>(nStartRed);
281
144
        nGreen      = static_cast<sal_uInt8>(nStartGreen);
282
144
        nBlue       = static_cast<sal_uInt8>(nStartBlue);
283
284
144
        mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
285
286
144
        aBorderRect.SetBottom( static_cast<tools::Long>( aBorderRect.Top() + fBorder ) );
287
144
        aRect.SetTop( aBorderRect.Bottom() );
288
144
        aPoly[0] = aBorderRect.TopLeft();
289
144
        aPoly[1] = aBorderRect.TopRight();
290
144
        aPoly[2] = aBorderRect.BottomRight();
291
144
        aPoly[3] = aBorderRect.BottomLeft();
292
144
        aPoly.Rotate( aCenter, nAngle );
293
294
144
        ImplDrawPolygon( aPoly, pClixPolyPoly );
295
296
144
        if ( !bLinear)
297
0
        {
298
0
            aBorderRect = aMirrorRect;
299
0
            aBorderRect.SetTop( static_cast<tools::Long>( aBorderRect.Bottom() - fBorder ) );
300
0
            aMirrorRect.SetBottom( aBorderRect.Top() );
301
0
            aPoly[0] = aBorderRect.TopLeft();
302
0
            aPoly[1] = aBorderRect.TopRight();
303
0
            aPoly[2] = aBorderRect.BottomRight();
304
0
            aPoly[3] = aBorderRect.BottomLeft();
305
0
            aPoly.Rotate( aCenter, nAngle );
306
307
0
            ImplDrawPolygon( aPoly, pClixPolyPoly );
308
0
        }
309
144
    }
310
311
    // calculate step count
312
2.70k
    tools::Long nStepCount = GetGradientSteps(rGradient, aRect);
313
314
    // minimal three steps and maximal as max color steps
315
2.70k
    tools::Long   nAbsRedSteps   = std::abs( nEndRed   - nStartRed );
316
2.70k
    tools::Long   nAbsGreenSteps = std::abs( nEndGreen - nStartGreen );
317
2.70k
    tools::Long   nAbsBlueSteps  = std::abs( nEndBlue  - nStartBlue );
318
2.70k
    tools::Long   nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps );
319
2.70k
    nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps );
320
2.70k
    tools::Long nSteps = std::min( nStepCount, nMaxColorSteps );
321
2.70k
    if ( nSteps < 3)
322
2.66k
    {
323
2.66k
        nSteps = 3;
324
2.66k
    }
325
326
2.70k
    double fScanInc = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps);
327
2.70k
    double fGradientLine = static_cast<double>(aRect.Top());
328
2.70k
    double fMirrorGradientLine = static_cast<double>(aMirrorRect.Bottom());
329
330
2.70k
    const double fStepsMinus1 = static_cast<double>(nSteps) - 1.0;
331
2.70k
    if ( !bLinear)
332
3
    {
333
3
        nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap
334
3
    }
335
336
2.29M
    for ( tools::Long i = 0; i < nSteps; i++ )
337
2.29M
    {
338
        // linear interpolation of color
339
2.29M
        const double fAlpha = static_cast<double>(i) / fStepsMinus1;
340
2.29M
        double fTempColor = static_cast<double>(nStartRed) * (1.0-fAlpha) + static_cast<double>(nEndRed) * fAlpha;
341
2.29M
        nRed = GetGradientColorValue(static_cast<tools::Long>(fTempColor));
342
2.29M
        fTempColor = static_cast<double>(nStartGreen) * (1.0-fAlpha) + static_cast<double>(nEndGreen) * fAlpha;
343
2.29M
        nGreen = GetGradientColorValue(static_cast<tools::Long>(fTempColor));
344
2.29M
        fTempColor = static_cast<double>(nStartBlue) * (1.0-fAlpha) + static_cast<double>(nEndBlue) * fAlpha;
345
2.29M
        nBlue = GetGradientColorValue(static_cast<tools::Long>(fTempColor));
346
347
2.29M
        mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
348
349
        // Polygon for this color step
350
2.29M
        aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(i) * fScanInc ) );
351
2.29M
        aRect.SetBottom( static_cast<tools::Long>( fGradientLine + ( static_cast<double>(i) + 1.0 ) * fScanInc ) );
352
2.29M
        aPoly[0] = aRect.TopLeft();
353
2.29M
        aPoly[1] = aRect.TopRight();
354
2.29M
        aPoly[2] = aRect.BottomRight();
355
2.29M
        aPoly[3] = aRect.BottomLeft();
356
2.29M
        aPoly.Rotate( aCenter, nAngle );
357
358
2.29M
        ImplDrawPolygon( aPoly, pClixPolyPoly );
359
360
2.29M
        if ( !bLinear )
361
461
        {
362
461
            aMirrorRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(i) * fScanInc ) );
363
461
            aMirrorRect.SetTop( static_cast<tools::Long>( fMirrorGradientLine - (static_cast<double>(i) + 1.0)* fScanInc ) );
364
461
            aPoly[0] = aMirrorRect.TopLeft();
365
461
            aPoly[1] = aMirrorRect.TopRight();
366
461
            aPoly[2] = aMirrorRect.BottomRight();
367
461
            aPoly[3] = aMirrorRect.BottomLeft();
368
461
            aPoly.Rotate( aCenter, nAngle );
369
370
461
            ImplDrawPolygon( aPoly, pClixPolyPoly );
371
461
        }
372
2.29M
    }
373
2.70k
    if ( bLinear)
374
2.70k
        return;
375
376
    // draw middle polygon with end color
377
3
    nRed = GetGradientColorValue(nEndRed);
378
3
    nGreen = GetGradientColorValue(nEndGreen);
379
3
    nBlue = GetGradientColorValue(nEndBlue);
380
381
3
    mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
382
383
3
    aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(nSteps) * fScanInc ) );
384
3
    aRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(nSteps) * fScanInc ) );
385
3
    aPoly[0] = aRect.TopLeft();
386
3
    aPoly[1] = aRect.TopRight();
387
3
    aPoly[2] = aRect.BottomRight();
388
3
    aPoly[3] = aRect.BottomLeft();
389
3
    aPoly.Rotate( aCenter, nAngle );
390
391
3
    ImplDrawPolygon( aPoly, pClixPolyPoly );
392
393
3
}
394
395
bool OutputDevice::is_double_buffered_window() const
396
0
{
397
0
    auto pOwnerWindow = GetOwnerWindow();
398
0
    return pOwnerWindow && pOwnerWindow->SupportsDoubleBuffering();
399
0
}
400
401
void OutputDevice::DrawComplexGradient( const tools::Rectangle& rRect,
402
                                        const Gradient& rGradient,
403
                                        const tools::PolyPolygon* pClixPolyPoly )
404
1.65k
{
405
1.65k
    assert(!is_double_buffered_window());
406
407
    // Determine if we output via Polygon or PolyPolygon
408
    // For all rasteroperations other than Overpaint always use PolyPolygon,
409
    // as we will get wrong results if we output multiple times on top of each other.
410
    // Also for printers always use PolyPolygon, as not all printers
411
    // can print polygons on top of each other.
412
413
1.65k
    std::optional<tools::PolyPolygon> xPolyPoly;
414
1.65k
    tools::Rectangle       aRect;
415
1.65k
    Point           aCenter;
416
1.65k
    Color           aStartCol( rGradient.GetStartColor() );
417
1.65k
    Color           aEndCol( rGradient.GetEndColor() );
418
1.65k
    tools::Long            nStartRed = ( static_cast<tools::Long>(aStartCol.GetRed()) * rGradient.GetStartIntensity() ) / 100;
419
1.65k
    tools::Long            nStartGreen = ( static_cast<tools::Long>(aStartCol.GetGreen()) * rGradient.GetStartIntensity() ) / 100;
420
1.65k
    tools::Long            nStartBlue = ( static_cast<tools::Long>(aStartCol.GetBlue()) * rGradient.GetStartIntensity() ) / 100;
421
1.65k
    tools::Long            nEndRed = ( static_cast<tools::Long>(aEndCol.GetRed()) * rGradient.GetEndIntensity() ) / 100;
422
1.65k
    tools::Long            nEndGreen = ( static_cast<tools::Long>(aEndCol.GetGreen()) * rGradient.GetEndIntensity() ) / 100;
423
1.65k
    tools::Long            nEndBlue = ( static_cast<tools::Long>(aEndCol.GetBlue()) * rGradient.GetEndIntensity() ) / 100;
424
1.65k
    tools::Long            nRedSteps = nEndRed - nStartRed;
425
1.65k
    tools::Long            nGreenSteps = nEndGreen - nStartGreen;
426
1.65k
    tools::Long            nBlueSteps = nEndBlue   - nStartBlue;
427
1.65k
    Degree10       nAngle = rGradient.GetAngle() % 3600_deg10;
428
429
1.65k
    rGradient.GetBoundRect( rRect, aRect, aCenter );
430
431
1.65k
    if ( UsePolyPolygonForComplexGradient() )
432
1.65k
        xPolyPoly = tools::PolyPolygon( 2 );
433
434
1.65k
    tools::Long nStepCount = GetGradientSteps(rGradient, rRect);
435
436
    // at least three steps and at most the number of colour differences
437
1.65k
    tools::Long nSteps = std::max( nStepCount, tools::Long(2) );
438
1.65k
    tools::Long nCalcSteps  = std::abs( nRedSteps );
439
1.65k
    tools::Long nTempSteps = std::abs( nGreenSteps );
440
1.65k
    if ( nTempSteps > nCalcSteps )
441
10
        nCalcSteps = nTempSteps;
442
1.65k
    nTempSteps = std::abs( nBlueSteps );
443
1.65k
    if ( nTempSteps > nCalcSteps )
444
72
        nCalcSteps = nTempSteps;
445
1.65k
    if ( nCalcSteps < nSteps )
446
1.54k
        nSteps = nCalcSteps;
447
1.65k
    if ( !nSteps )
448
1.54k
        nSteps = 1;
449
450
    // determine output limits and stepsizes for all directions
451
1.65k
    tools::Polygon aPoly;
452
1.65k
    double  fScanLeft = aRect.Left();
453
1.65k
    double  fScanTop = aRect.Top();
454
1.65k
    double  fScanRight = aRect.Right();
455
1.65k
    double  fScanBottom = aRect.Bottom();
456
1.65k
    double fScanIncX = static_cast<double>(aRect.GetWidth()) / static_cast<double>(nSteps) * 0.5;
457
1.65k
    double fScanIncY = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps) * 0.5;
458
459
    // all gradients are rendered as nested rectangles which shrink
460
    // equally in each dimension - except for 'square' gradients
461
    // which shrink to a central vertex but are not per-se square.
462
1.65k
    if( rGradient.GetStyle() != css::awt::GradientStyle_SQUARE )
463
1.65k
    {
464
1.65k
        fScanIncY = std::min( fScanIncY, fScanIncX );
465
1.65k
        fScanIncX = fScanIncY;
466
1.65k
    }
467
1.65k
    sal_uInt8   nRed = static_cast<sal_uInt8>(nStartRed), nGreen = static_cast<sal_uInt8>(nStartGreen), nBlue = static_cast<sal_uInt8>(nStartBlue);
468
1.65k
    bool    bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output
469
470
1.65k
    mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
471
472
1.65k
    if( xPolyPoly )
473
1.65k
    {
474
1.65k
        aPoly = tools::Polygon(rRect);
475
1.65k
        xPolyPoly->Insert( aPoly );
476
1.65k
        xPolyPoly->Insert( aPoly );
477
1.65k
    }
478
0
    else
479
0
    {
480
        // extend rect, to avoid missing bounding line
481
0
        tools::Rectangle aExtRect( rRect );
482
483
0
        aExtRect.AdjustLeft( -1 );
484
0
        aExtRect.AdjustTop( -1 );
485
0
        aExtRect.AdjustRight(1 );
486
0
        aExtRect.AdjustBottom(1 );
487
488
0
        aPoly = tools::Polygon(aExtRect);
489
0
        ImplDrawPolygon( aPoly, pClixPolyPoly );
490
0
    }
491
492
    // loop to output Polygon/PolyPolygon sequentially
493
4.50k
    for( tools::Long i = 1; i < nSteps; i++ )
494
2.86k
    {
495
        // calculate new Polygon
496
2.86k
        fScanLeft += fScanIncX;
497
2.86k
        aRect.SetLeft( static_cast<tools::Long>( fScanLeft ) );
498
2.86k
        fScanTop += fScanIncY;
499
2.86k
        aRect.SetTop( static_cast<tools::Long>( fScanTop ) );
500
2.86k
        fScanRight -= fScanIncX;
501
2.86k
        aRect.SetRight( static_cast<tools::Long>( fScanRight ) );
502
2.86k
        fScanBottom -= fScanIncY;
503
2.86k
        aRect.SetBottom( static_cast<tools::Long>( fScanBottom ) );
504
505
2.86k
        if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
506
12
            break;
507
508
2.85k
        if( rGradient.GetStyle() == css::awt::GradientStyle_RADIAL || rGradient.GetStyle() == css::awt::GradientStyle_ELLIPTICAL )
509
0
            aPoly = tools::Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
510
2.85k
        else
511
2.85k
            aPoly = tools::Polygon( aRect );
512
513
2.85k
        aPoly.Rotate( aCenter, nAngle );
514
515
        // adapt colour accordingly
516
2.85k
        const tools::Long nStepIndex = ( xPolyPoly ? i : ( i + 1 ) );
517
2.85k
        nRed = GetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) );
518
2.85k
        nGreen = GetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) );
519
2.85k
        nBlue = GetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) );
520
521
        // either slow tools::PolyPolygon output or fast Polygon-Painting
522
2.85k
        if( xPolyPoly )
523
2.85k
        {
524
2.85k
            bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output
525
526
2.85k
            xPolyPoly->Replace( xPolyPoly->GetObject( 1 ), 0 );
527
2.85k
            xPolyPoly->Replace( aPoly, 1 );
528
529
2.85k
            ImplDrawPolyPolygon( *xPolyPoly, pClixPolyPoly );
530
531
            // #107349# Set fill color _after_ geometry painting:
532
            // xPolyPoly's geometry is the band from last iteration's
533
            // aPoly to current iteration's aPoly. The window outdev
534
            // path (see else below), on the other hand, paints the
535
            // full aPoly. Thus, here, we're painting the band before
536
            // the one painted in the window outdev path below. To get
537
            // matching colors, have to delay color setting here.
538
2.85k
            mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
539
2.85k
        }
540
0
        else
541
0
        {
542
            // #107349# Set fill color _before_ geometry painting
543
0
            mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
544
545
0
            ImplDrawPolygon( aPoly, pClixPolyPoly );
546
0
        }
547
2.85k
    }
548
549
    // we should draw last inner Polygon if we output PolyPolygon
550
1.65k
    if( !xPolyPoly )
551
0
        return;
552
553
1.65k
    const tools::Polygon& rPoly = xPolyPoly->GetObject( 1 );
554
555
1.65k
    if( rPoly.GetBoundRect().IsEmpty() )
556
0
        return;
557
558
    // #107349# Paint last polygon with end color only if loop
559
    // has generated output. Otherwise, the current
560
    // (i.e. start) color is taken, to generate _any_ output.
561
1.65k
    if( bPaintLastPolygon )
562
100
    {
563
100
        nRed = GetGradientColorValue( nEndRed );
564
100
        nGreen = GetGradientColorValue( nEndGreen );
565
100
        nBlue = GetGradientColorValue( nEndBlue );
566
100
    }
567
568
1.65k
    mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
569
1.65k
    ImplDrawPolygon( rPoly, pClixPolyPoly );
570
1.65k
}
571
572
tools::Long OutputDevice::GetGradientStepCount( tools::Long nMinRect )
573
994
{
574
994
    tools::Long nInc = (nMinRect < 50) ? 2 : 4;
575
576
994
    return nInc;
577
994
}
578
579
tools::Long OutputDevice::GetGradientSteps(Gradient const& rGradient, tools::Rectangle const& rRect)
580
4.35k
{
581
    // calculate step count
582
4.35k
    tools::Long nStepCount = rGradient.GetSteps();
583
584
4.35k
    if (nStepCount)
585
3.36k
        return nStepCount;
586
587
994
    tools::Long nMinRect = 0;
588
589
994
    if (rGradient.GetStyle() == css::awt::GradientStyle_LINEAR || rGradient.GetStyle() == css::awt::GradientStyle_AXIAL)
590
2
        nMinRect = rRect.GetHeight();
591
992
    else
592
992
        nMinRect = std::min(rRect.GetWidth(), rRect.GetHeight());
593
594
994
    tools::Long nInc = GetGradientStepCount(nMinRect);
595
596
994
    if (!nInc)
597
0
        nInc = 1;
598
599
994
    return nMinRect / nInc;
600
4.35k
}
601
602
Color OutputDevice::GetSingleColorGradientFill()
603
0
{
604
0
    Color aColor;
605
606
    // we should never call on this function if any of these aren't set!
607
0
    assert( mnDrawMode & ( DrawModeFlags::BlackGradient | DrawModeFlags::WhiteGradient | DrawModeFlags::SettingsGradient) );
608
609
0
    if ( mnDrawMode & DrawModeFlags::BlackGradient )
610
0
        aColor = COL_BLACK;
611
0
    else if ( mnDrawMode & DrawModeFlags::WhiteGradient )
612
0
        aColor = COL_WHITE;
613
0
    else if ( mnDrawMode & DrawModeFlags::SettingsGradient )
614
0
    {
615
0
        if (mnDrawMode & DrawModeFlags::SettingsForSelection)
616
0
            aColor = GetSettings().GetStyleSettings().GetHighlightColor();
617
0
        else
618
0
            aColor = GetSettings().GetStyleSettings().GetWindowColor();
619
0
    }
620
621
0
    return aColor;
622
0
}
623
624
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */