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 {
28 public:
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 
40  Impl()
41  : meStyle (GradientStyle::Linear)
42  , maStartColor(COL_BLACK)
43  , maEndColor(COL_WHITE)
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 
83 Gradient::Gradient() = default;
84 
85 Gradient::Gradient( const Gradient& ) = default;
86 
87 Gradient::Gradient( Gradient&& ) = default;
88 
90  const Color& rStartColor, const Color& rEndColor )
91 {
92  mpImplGradient->meStyle = eStyle;
93  mpImplGradient->maStartColor = rStartColor;
94  mpImplGradient->maEndColor = rEndColor;
95 }
96 
97 Gradient::~Gradient() = default;
98 
99 
101 {
102  return mpImplGradient->meStyle;
103 }
104 
106 {
107  mpImplGradient->meStyle = eStyle;
108 }
109 
111 {
112  return mpImplGradient->maStartColor;
113 }
114 
115 void Gradient::SetStartColor( const Color& rColor )
116 {
117  mpImplGradient->maStartColor = rColor;
118 }
119 
121 {
122  return mpImplGradient->maEndColor;
123 }
124 
125 void 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 
140 sal_uInt16 Gradient::GetBorder() const
141 {
142  return mpImplGradient->mnBorder;
143 }
144 
145 void Gradient::SetBorder( sal_uInt16 nBorder )
146 {
147  mpImplGradient->mnBorder = nBorder;
148 }
149 
150 sal_uInt16 Gradient::GetOfsX() const
151 {
152  return mpImplGradient->mnOfsX;
153 }
154 
155 void Gradient::SetOfsX( sal_uInt16 nOfsX )
156 {
157  mpImplGradient->mnOfsX = nOfsX;
158 }
159 
160 sal_uInt16 Gradient::GetOfsY() const
161 {
162  return mpImplGradient->mnOfsY;
163 }
164 
165 void Gradient::SetOfsY( sal_uInt16 nOfsY )
166 {
167  mpImplGradient->mnOfsY = nOfsY;
168 }
169 
170 sal_uInt16 Gradient::GetStartIntensity() const
171 {
172  return mpImplGradient->mnIntensityStart;
173 }
174 
175 void Gradient::SetStartIntensity( sal_uInt16 nIntens )
176 {
177  mpImplGradient->mnIntensityStart = nIntens;
178 }
179 
180 sal_uInt16 Gradient::GetEndIntensity() const
181 {
182  return mpImplGradient->mnIntensityEnd;
183 }
184 
185 void Gradient::SetEndIntensity( sal_uInt16 nIntens )
186 {
187  mpImplGradient->mnIntensityEnd = nIntens;
188 }
189 
190 sal_uInt16 Gradient::GetSteps() const
191 {
192  return mpImplGradient->mnStepCount;
193 }
194 
195 void Gradient::SetSteps( sal_uInt16 nSteps )
196 {
197  mpImplGradient->mnStepCount = nSteps;
198 }
199 
200 void 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() == GradientStyle::Linear || GetStyle() == 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() == GradientStyle::Square || GetStyle() == 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() == GradientStyle::Radial )
246  {
247  // Calculation of radii for circle
248  aSize.setWidth( static_cast<tools::Long>(0.5 + sqrt(static_cast<double>(aSize.Width())*static_cast<double>(aSize.Width()) + static_cast<double>(aSize.Height())*static_cast<double>(aSize.Height()))) );
249  aSize.setHeight( aSize.Width() );
250  }
251  else if( GetStyle() == 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 
292 Gradient& Gradient::operator=( const Gradient& ) = default;
293 
294 Gradient& Gradient::operator=( Gradient&& ) = default;
295 
296 bool Gradient::operator==( const Gradient& rGradient ) const
297 {
298  return mpImplGradient == rGradient.mpImplGradient;
299 }
300 
301 const sal_uInt32 GRADIENT_DEFAULT_STEPCOUNT = 0;
302 
304 {
305  tools::Rectangle aRect(rRect);
306  aRect.Justify();
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() == GradientStyle::Linear || GetStyle() == 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() == GradientStyle::Linear || GetStyle() == 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() == 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  tools::Long nTempColor = nStartRed;
405  nStartRed = nEndRed;
406  nEndRed = nTempColor;
407  nTempColor = nStartGreen;
408  nStartGreen = nEndGreen;
409  nEndGreen = nTempColor;
410  nTempColor = nStartBlue;
411  nStartBlue = nEndBlue;
412  nEndBlue = nTempColor;
413  }
414 
415  sal_uInt8 nRed;
416  sal_uInt8 nGreen;
417  sal_uInt8 nBlue;
418 
419  // Create border
420  tools::Rectangle aBorderRect = aRect;
421  tools::Polygon aPoly( 4 );
422  if (fBorder > 0.0)
423  {
424  nRed = static_cast<sal_uInt8>(nStartRed);
425  nGreen = static_cast<sal_uInt8>(nStartGreen);
426  nBlue = static_cast<sal_uInt8>(nStartBlue);
427 
428  rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
429 
430  aBorderRect.SetBottom( static_cast<tools::Long>( aBorderRect.Top() + fBorder ) );
431  aRect.SetTop( aBorderRect.Bottom() );
432  aPoly[0] = aBorderRect.TopLeft();
433  aPoly[1] = aBorderRect.TopRight();
434  aPoly[2] = aBorderRect.BottomRight();
435  aPoly[3] = aBorderRect.BottomLeft();
436  aPoly.Rotate( aCenter, nAngle );
437 
438  rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
439 
440  if ( !bLinear)
441  {
442  aBorderRect = aMirrorRect;
443  aBorderRect.SetTop( static_cast<tools::Long>( aBorderRect.Bottom() - fBorder ) );
444  aMirrorRect.SetBottom( aBorderRect.Top() );
445  aPoly[0] = aBorderRect.TopLeft();
446  aPoly[1] = aBorderRect.TopRight();
447  aPoly[2] = aBorderRect.BottomRight();
448  aPoly[3] = aBorderRect.BottomLeft();
449  aPoly.Rotate( aCenter, nAngle );
450 
451  rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
452  }
453  }
454 
455  tools::Long nStepCount = GetMetafileSteps(aRect);
456 
457  // minimal three steps and maximal as max color steps
458  tools::Long nAbsRedSteps = std::abs( nEndRed - nStartRed );
459  tools::Long nAbsGreenSteps = std::abs( nEndGreen - nStartGreen );
460  tools::Long nAbsBlueSteps = std::abs( nEndBlue - nStartBlue );
461  tools::Long nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps );
462  nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps );
463  tools::Long nSteps = std::min( nStepCount, nMaxColorSteps );
464  if ( nSteps < 3)
465  {
466  nSteps = 3;
467  }
468 
469  double fScanInc = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps);
470  double fGradientLine = static_cast<double>(aRect.Top());
471  double fMirrorGradientLine = static_cast<double>(aMirrorRect.Bottom());
472 
473  const double fStepsMinus1 = static_cast<double>(nSteps) - 1.0;
474  if ( !bLinear)
475  {
476  nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap
477  }
478  for ( tools::Long i = 0; i < nSteps; i++ )
479  {
480  // linear interpolation of color
481  double fAlpha = static_cast<double>(i) / fStepsMinus1;
482  double fTempColor = static_cast<double>(nStartRed) * (1.0-fAlpha) + static_cast<double>(nEndRed) * fAlpha;
483  nRed = GetGradientColorValue(static_cast<tools::Long>(fTempColor));
484  fTempColor = static_cast<double>(nStartGreen) * (1.0-fAlpha) + static_cast<double>(nEndGreen) * fAlpha;
485  nGreen = GetGradientColorValue(static_cast<tools::Long>(fTempColor));
486  fTempColor = static_cast<double>(nStartBlue) * (1.0-fAlpha) + static_cast<double>(nEndBlue) * fAlpha;
487  nBlue = GetGradientColorValue(static_cast<tools::Long>(fTempColor));
488 
489  rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
490 
491  // Polygon for this color step
492  aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(i) * fScanInc ) );
493  aRect.SetBottom( static_cast<tools::Long>( fGradientLine + ( static_cast<double>(i) + 1.0 ) * fScanInc ) );
494  aPoly[0] = aRect.TopLeft();
495  aPoly[1] = aRect.TopRight();
496  aPoly[2] = aRect.BottomRight();
497  aPoly[3] = aRect.BottomLeft();
498  aPoly.Rotate( aCenter, nAngle );
499 
500  rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
501 
502  if ( !bLinear )
503  {
504  aMirrorRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(i) * fScanInc ) );
505  aMirrorRect.SetTop( static_cast<tools::Long>( fMirrorGradientLine - (static_cast<double>(i) + 1.0)* fScanInc ) );
506  aPoly[0] = aMirrorRect.TopLeft();
507  aPoly[1] = aMirrorRect.TopRight();
508  aPoly[2] = aMirrorRect.BottomRight();
509  aPoly[3] = aMirrorRect.BottomLeft();
510  aPoly.Rotate( aCenter, nAngle );
511 
512  rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
513  }
514  }
515  if ( bLinear)
516  return;
517 
518  // draw middle polygon with end color
519  nRed = GetGradientColorValue(nEndRed);
520  nGreen = GetGradientColorValue(nEndGreen);
521  nBlue = GetGradientColorValue(nEndBlue);
522 
523  rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
524 
525  aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(nSteps) * fScanInc ) );
526  aRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(nSteps) * fScanInc ) );
527  aPoly[0] = aRect.TopLeft();
528  aPoly[1] = aRect.TopRight();
529  aPoly[2] = aRect.BottomRight();
530  aPoly[3] = aRect.BottomLeft();
531  aPoly.Rotate( aCenter, nAngle );
532 
533  rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
534 
535 }
536 
538 {
539  // Determine if we output via Polygon or PolyPolygon
540  // For all rasteroperations other than Overpaint always use PolyPolygon,
541  // as we will get wrong results if we output multiple times on top of each other.
542  // Also for printers always use PolyPolygon, as not all printers
543  // can print polygons on top of each other.
544 
545  tools::Rectangle aRect;
546  Point aCenter;
547  GetBoundRect(rRect, aRect, aCenter);
548 
549  std::optional<tools::PolyPolygon> xPolyPoly;
550  xPolyPoly = tools::PolyPolygon( 2 );
551 
552  // last parameter - true if complex gradient, false if linear
553  tools::Long nStepCount = GetMetafileSteps(rRect);
554 
555  // at least three steps and at most the number of colour differences
556  tools::Long nSteps = std::max(nStepCount, tools::Long(2));
557 
558  Color aStartCol(GetStartColor());
559  Color aEndCol(GetEndColor());
560 
561  tools::Long nStartRed = (static_cast<tools::Long>(aStartCol.GetRed()) * GetStartIntensity()) / 100;
562  tools::Long nStartGreen = (static_cast<tools::Long>(aStartCol.GetGreen()) * GetStartIntensity()) / 100;
563  tools::Long nStartBlue = (static_cast<tools::Long>(aStartCol.GetBlue()) * GetStartIntensity()) / 100;
564 
565  tools::Long nEndRed = (static_cast<tools::Long>(aEndCol.GetRed()) * GetEndIntensity()) / 100;
566  tools::Long nEndGreen = (static_cast<tools::Long>(aEndCol.GetGreen()) * GetEndIntensity()) / 100;
567  tools::Long nEndBlue = (static_cast<tools::Long>(aEndCol.GetBlue()) * GetEndIntensity()) / 100;
568 
569  tools::Long nRedSteps = nEndRed - nStartRed;
570  tools::Long nGreenSteps = nEndGreen - nStartGreen;
571  tools::Long nBlueSteps = nEndBlue - nStartBlue;
572 
573  tools::Long nCalcSteps = std::abs(nRedSteps);
574  tools::Long nTempSteps = std::abs(nGreenSteps);
575 
576  if (nTempSteps > nCalcSteps)
577  nCalcSteps = nTempSteps;
578 
579  nTempSteps = std::abs( nBlueSteps );
580 
581  if (nTempSteps > nCalcSteps)
582  nCalcSteps = nTempSteps;
583 
584  if (nCalcSteps < nSteps)
585  nSteps = nCalcSteps;
586 
587  if ( !nSteps )
588  nSteps = 1;
589 
590  // determine output limits and stepsizes for all directions
591  tools::Polygon aPoly;
592  double fScanLeft = aRect.Left();
593  double fScanTop = aRect.Top();
594  double fScanRight = aRect.Right();
595  double fScanBottom = aRect.Bottom();
596  double fScanIncX = static_cast<double>(aRect.GetWidth()) / static_cast<double>(nSteps) * 0.5;
597  double fScanIncY = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps) * 0.5;
598 
599  // all gradients are rendered as nested rectangles which shrink
600  // equally in each dimension - except for 'square' gradients
601  // which shrink to a central vertex but are not per-se square.
602  if (GetStyle() != GradientStyle::Square)
603  {
604  fScanIncY = std::min( fScanIncY, fScanIncX );
605  fScanIncX = fScanIncY;
606  }
607  sal_uInt8 nRed = static_cast<sal_uInt8>(nStartRed), nGreen = static_cast<sal_uInt8>(nStartGreen), nBlue = static_cast<sal_uInt8>(nStartBlue);
608  bool bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output
609 
610  rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
611 
612  aPoly = rRect;
613  xPolyPoly->Insert( aPoly );
614  xPolyPoly->Insert( aPoly );
615 
616  // loop to output Polygon/PolyPolygon sequentially
617  for( tools::Long i = 1; i < nSteps; i++ )
618  {
619  // calculate new Polygon
620  fScanLeft += fScanIncX;
621  aRect.SetLeft( static_cast<tools::Long>( fScanLeft ) );
622  fScanTop += fScanIncY;
623  aRect.SetTop( static_cast<tools::Long>( fScanTop ) );
624  fScanRight -= fScanIncX;
625  aRect.SetRight( static_cast<tools::Long>( fScanRight ) );
626  fScanBottom -= fScanIncY;
627  aRect.SetBottom( static_cast<tools::Long>( fScanBottom ) );
628 
629  if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
630  break;
631 
632  if (GetStyle() == GradientStyle::Radial || GetStyle() == GradientStyle::Elliptical)
633  aPoly = tools::Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
634  else
635  aPoly = tools::Polygon( aRect );
636 
637  aPoly.Rotate(aCenter, GetAngle() % 3600_deg10);
638 
639  // adapt colour accordingly
640  const tools::Long nStepIndex = ( xPolyPoly ? i : ( i + 1 ) );
641  nRed = GetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) );
642  nGreen = GetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) );
643  nBlue = GetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) );
644 
645  bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output
646 
647  xPolyPoly->Replace( xPolyPoly->GetObject( 1 ), 0 );
648  xPolyPoly->Replace( aPoly, 1 );
649 
650  rMetaFile.AddAction( new MetaPolyPolygonAction( *xPolyPoly ) );
651 
652  // #107349# Set fill color _after_ geometry painting:
653  // xPolyPoly's geometry is the band from last iteration's
654  // aPoly to current iteration's aPoly. The window outdev
655  // path (see else below), on the other hand, paints the
656  // full aPoly. Thus, here, we're painting the band before
657  // the one painted in the window outdev path below. To get
658  // matching colors, have to delay color setting here.
659  rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
660  }
661 
662  const tools::Polygon& rPoly = xPolyPoly->GetObject( 1 );
663 
664  if( rPoly.GetBoundRect().IsEmpty() )
665  return;
666 
667  // #107349# Paint last polygon with end color only if loop
668  // has generated output. Otherwise, the current
669  // (i.e. start) color is taken, to generate _any_ output.
670  if( bPaintLastPolygon )
671  {
672  nRed = GetGradientColorValue( nEndRed );
673  nGreen = GetGradientColorValue( nEndGreen );
674  nBlue = GetGradientColorValue( nEndBlue );
675  }
676 
677  rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
678  rMetaFile.AddAction( new MetaPolygonAction( rPoly ) );
679 }
680 
681 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void SetEndColor(const Color &rColor)
GradientStyle GetStyle() const
sal_uInt8 GetRed() const
tools::Long AdjustRight(tools::Long nHorzMoveDelta)
void setWidth(tools::Long nWidth)
constexpr tools::Long Left() const
sal_uInt16 GetOfsY() const
Degree10 mnAngle
long Long
sal_uInt8 GetLuminance() const
sal_uInt16 GetBorder() const
void SetBorder(sal_uInt16 nBorder)
sal_uInt16 mnIntensityEnd
void SetEndIntensity(sal_uInt16 nIntens)
Gradient & operator=(const Gradient &rGradient)
void SetSteps(sal_uInt16 nSteps)
void SetOfsY(sal_uInt16 nOfsY)
constexpr Point BottomLeft() const
void SetStartColor(const Color &rColor)
constexpr tools::Long GetWidth() const
void DrawComplexGradientToMetafile(tools::Rectangle const &rRect, GDIMetaFile &rMetaFile) const
GradientStyle
const sal_uInt32 GRADIENT_DEFAULT_STEPCOUNT
tools::Long AdjustBottom(tools::Long nVertMoveDelta)
sal_uInt8 GetBlue() const
constexpr bool IsEmpty() const
void Rotate(const Point &rCenter, double fSin, double fCos)
constexpr void SetLeft(tools::Long v)
static sal_uInt8 GetGradientColorValue(tools::Long nValue)
void GetBoundRect(const tools::Rectangle &rRect, tools::Rectangle &rBoundRect, Point &rCenter) const
int i
void SetSize(const Size &rSize)
sal_uInt16 GetOfsX() const
tools::Long GetMetafileSteps(tools::Rectangle const &rRect) const
void AddGradientActions(tools::Rectangle const &rRect, GDIMetaFile &rMetaFile)
Impl(const Impl &rImplGradient)
constexpr tools::Long Right() const
constexpr tools::Long Top() const
sal_uInt16 GetEndIntensity() const
sal_uInt16 mnOfsX
double toRadians(D x)
sal_uInt16 mnStepCount
constexpr void SetRight(tools::Long v)
bool operator==(const Gradient &rGradient) const
constexpr void SetBottom(tools::Long v)
void DrawLinearGradientToMetafile(tools::Rectangle const &rRect, GDIMetaFile &rMetaFile) const
void SetOfsX(sal_uInt16 nOfsX)
constexpr void SetTop(tools::Long v)
constexpr Point Center() const
sal_uInt16 GetSteps() const
constexpr Point TopLeft() const
tools::Long AdjustTop(tools::Long nVertMoveDelta)
const Color & GetStartColor() const
constexpr tools::Long Bottom() const
sal_uInt8 GetGreen() const
void SetAngle(Degree10 nAngle)
constexpr Size GetSize() const
Degree10 GetAngle() const
void MakeGrayscale()
unsigned char sal_uInt8
::o3tl::cow_wrapper< Impl > mpImplGradient
Definition: gradient.hxx:41
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:581
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_WHITE
constexpr Point TopRight() const
const Color & GetEndColor() const
GradientStyle meStyle
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_BLACK
sal_uInt16 mnIntensityStart
sal_uInt16 mnOfsY
constexpr Point BottomRight() const
tools::Long AdjustLeft(tools::Long nHorzMoveDelta)
tools::Rectangle GetBoundRect() const
void SetStyle(GradientStyle eStyle)
sal_uInt16 GetStartIntensity() const
bool operator==(const Impl &rImpl_Gradient) const
sal_uInt16 mnBorder
void SetStartIntensity(sal_uInt16 nIntens)
constexpr tools::Long GetHeight() const