Coverage Report

Created: 2026-11-06 11:29

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