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