LibreOffice Module vcl (master)  1
outdev/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 <memory>
21 #include <cassert>
22 
23 #include <tools/poly.hxx>
24 #include <vcl/gdimtf.hxx>
25 #include <vcl/gradient.hxx>
26 #include <vcl/metaact.hxx>
27 #include <vcl/settings.hxx>
28 #include <vcl/outdev.hxx>
29 #include <vcl/virdev.hxx>
30 #include <vcl/window.hxx>
31 
32 #include <salgdi.hxx>
33 
34 #define GRADIENT_DEFAULT_STEPCOUNT 0
35 
37  const Gradient& rGradient )
38 {
39  assert(!is_double_buffered_window());
40 
41  // Convert rectangle to a tools::PolyPolygon by first converting to a Polygon
42  tools::Polygon aPolygon ( rRect );
43  tools::PolyPolygon aPolyPoly ( aPolygon );
44 
45  DrawGradient ( aPolyPoly, rGradient );
46 }
47 
49  const Gradient& rGradient )
50 {
51  assert(!is_double_buffered_window());
52 
53  if (mbInitClipRegion)
55  // don't return on mbOutputClipped here, as we may need to draw the clipped metafile, even if the output is clipped
56 
57  if ( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() )
58  {
60  {
62 
64  SetLineColor( aColor );
65  SetFillColor( aColor );
66  DrawPolyPolygon( rPolyPoly );
67  Pop();
68  return;
69  }
70 
71  Gradient aGradient( rGradient );
72 
74  {
75  SetGrayscaleColors( aGradient );
76  }
77 
78  DrawGradientToMetafile( rPolyPoly, rGradient );
79 
81  return;
82 
83  // Clip and then draw the gradient
84  if( !tools::Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
85  {
86  const tools::Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
87 
88  // convert rectangle to pixels
89  tools::Rectangle aRect( ImplLogicToDevicePixel( aBoundRect ) );
90  aRect.Justify();
91 
92  // do nothing if the rectangle is empty
93  if ( !aRect.IsEmpty() )
94  {
95  tools::PolyPolygon aClixPolyPoly( ImplLogicToDevicePixel( rPolyPoly ) );
96  bool bDrawn = false;
97 
98  if( !mpGraphics && !AcquireGraphics() )
99  return;
100 
101  // secure clip region
103  IntersectClipRegion( aBoundRect );
104 
105  if (mbInitClipRegion)
106  InitClipRegion();
107 
108  // try to draw gradient natively
109  bDrawn = mpGraphics->DrawGradient( aClixPolyPoly, aGradient );
110 
111  if (!bDrawn && !mbOutputClipped)
112  {
113  // draw gradients without border
115  {
117  mbInitLineColor = true;
118  }
119 
120  mbInitFillColor = true;
121 
122  // calculate step count if necessary
123  if ( !aGradient.GetSteps() )
125 
126  if ( rPolyPoly.IsRect() )
127  {
128  // because we draw with no border line, we have to expand gradient
129  // rect to avoid missing lines on the right and bottom edge
130  aRect.AdjustLeft( -1 );
131  aRect.AdjustTop( -1 );
132  aRect.AdjustRight( 1 );
133  aRect.AdjustBottom( 1 );
134  }
135 
136  // if the clipping polypolygon is a rectangle, then it's the same size as the bounding of the
137  // polypolygon, so pass in a NULL for the clipping parameter
138  if( aGradient.GetStyle() == GradientStyle::Linear || rGradient.GetStyle() == GradientStyle::Axial )
139  DrawLinearGradient( aRect, aGradient, aClixPolyPoly.IsRect() ? nullptr : &aClixPolyPoly );
140  else
141  DrawComplexGradient( aRect, aGradient, aClixPolyPoly.IsRect() ? nullptr : &aClixPolyPoly );
142  }
143 
144  Pop();
145  }
146  }
147  }
148 
149  if( mpAlphaVDev )
150  mpAlphaVDev->DrawPolyPolygon( rPolyPoly );
151 }
152 
153 void OutputDevice::ClipAndDrawGradientMetafile ( const Gradient &rGradient, const tools::PolyPolygon &rPolyPoly )
154 {
155  const tools::Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
156  const bool bOldOutput = IsOutputEnabled();
157 
158  EnableOutput( false );
161  DrawGradient( aBoundRect, rGradient );
164  DrawPolyPolygon( rPolyPoly );
166  DrawGradient( aBoundRect, rGradient );
167  Pop();
168  EnableOutput( bOldOutput );
169 }
170 
172  const Gradient& rGradient )
173 {
174  assert(!is_double_buffered_window());
175 
176  if ( !mpMetaFile )
177  return;
178 
179  if ( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() )
180  {
181  Gradient aGradient( rGradient );
182 
184  {
185  SetGrayscaleColors( aGradient );
186  }
187 
188  const tools::Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
189 
190  if ( rPolyPoly.IsRect() )
191  {
192  mpMetaFile->AddAction( new MetaGradientAction( aBoundRect, aGradient ) );
193  }
194  else
195  {
196  mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN" ) );
197  mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) );
198 
199  ClipAndDrawGradientMetafile ( rGradient, rPolyPoly );
200 
201  mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END" ) );
202  }
203 
205  return;
206 
207  // Clip and then draw the gradient
208  if( !tools::Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
209  {
210  // convert rectangle to pixels
211  tools::Rectangle aRect( ImplLogicToDevicePixel( aBoundRect ) );
212  aRect.Justify();
213 
214  // do nothing if the rectangle is empty
215  if ( !aRect.IsEmpty() )
216  {
217  if( !mbOutputClipped )
218  {
219  // calculate step count if necessary
220  if ( !aGradient.GetSteps() )
222 
223  if ( rPolyPoly.IsRect() )
224  {
225  // because we draw with no border line, we have to expand gradient
226  // rect to avoid missing lines on the right and bottom edge
227  aRect.AdjustLeft( -1 );
228  aRect.AdjustTop( -1 );
229  aRect.AdjustRight( 1 );
230  aRect.AdjustBottom( 1 );
231  }
232 
233  // if the clipping polypolygon is a rectangle, then it's the same size as the bounding of the
234  // polypolygon, so pass in a NULL for the clipping parameter
235  if( aGradient.GetStyle() == GradientStyle::Linear || rGradient.GetStyle() == GradientStyle::Axial )
236  DrawLinearGradientToMetafile( aRect, aGradient );
237  else
238  DrawComplexGradientToMetafile( aRect, aGradient );
239  }
240  }
241  }
242  }
243 }
244 
245 namespace
246 {
247  sal_uInt8 GetGradientColorValue( long nValue )
248  {
249  if ( nValue < 0 )
250  return 0;
251  else if ( nValue > 0xFF )
252  return 0xFF;
253  else
254  return static_cast<sal_uInt8>(nValue);
255  }
256 }
257 
259  const Gradient& rGradient,
260  const tools::PolyPolygon* pClixPolyPoly )
261 {
262  assert(!is_double_buffered_window());
263 
264  // get BoundRect of rotated rectangle
265  tools::Rectangle aRect;
266  Point aCenter;
267  sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
268 
269  rGradient.GetBoundRect( rRect, aRect, aCenter );
270 
271  bool bLinear = (rGradient.GetStyle() == GradientStyle::Linear);
272  double fBorder = rGradient.GetBorder() * aRect.GetHeight() / 100.0;
273  if ( !bLinear )
274  {
275  fBorder /= 2.0;
276  }
277  tools::Rectangle aMirrorRect = aRect; // used in style axial
278  aMirrorRect.SetTop( ( aRect.Top() + aRect.Bottom() ) / 2 );
279  if ( !bLinear )
280  {
281  aRect.SetBottom( aMirrorRect.Top() );
282  }
283 
284  // colour-intensities of start- and finish; change if needed
285  long nFactor;
286  Color aStartCol = rGradient.GetStartColor();
287  Color aEndCol = rGradient.GetEndColor();
288  long nStartRed = aStartCol.GetRed();
289  long nStartGreen = aStartCol.GetGreen();
290  long nStartBlue = aStartCol.GetBlue();
291  long nEndRed = aEndCol.GetRed();
292  long nEndGreen = aEndCol.GetGreen();
293  long nEndBlue = aEndCol.GetBlue();
294  nFactor = rGradient.GetStartIntensity();
295  nStartRed = (nStartRed * nFactor) / 100;
296  nStartGreen = (nStartGreen * nFactor) / 100;
297  nStartBlue = (nStartBlue * nFactor) / 100;
298  nFactor = rGradient.GetEndIntensity();
299  nEndRed = (nEndRed * nFactor) / 100;
300  nEndGreen = (nEndGreen * nFactor) / 100;
301  nEndBlue = (nEndBlue * nFactor) / 100;
302 
303  // gradient style axial has exchanged start and end colors
304  if ( !bLinear)
305  {
306  long nTempColor = nStartRed;
307  nStartRed = nEndRed;
308  nEndRed = nTempColor;
309  nTempColor = nStartGreen;
310  nStartGreen = nEndGreen;
311  nEndGreen = nTempColor;
312  nTempColor = nStartBlue;
313  nStartBlue = nEndBlue;
314  nEndBlue = nTempColor;
315  }
316 
317  sal_uInt8 nRed;
318  sal_uInt8 nGreen;
319  sal_uInt8 nBlue;
320 
321  // Create border
322  tools::Rectangle aBorderRect = aRect;
323  tools::Polygon aPoly( 4 );
324  if (fBorder > 0.0)
325  {
326  nRed = static_cast<sal_uInt8>(nStartRed);
327  nGreen = static_cast<sal_uInt8>(nStartGreen);
328  nBlue = static_cast<sal_uInt8>(nStartBlue);
329 
330  mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
331 
332  aBorderRect.SetBottom( static_cast<long>( aBorderRect.Top() + fBorder ) );
333  aRect.SetTop( aBorderRect.Bottom() );
334  aPoly[0] = aBorderRect.TopLeft();
335  aPoly[1] = aBorderRect.TopRight();
336  aPoly[2] = aBorderRect.BottomRight();
337  aPoly[3] = aBorderRect.BottomLeft();
338  aPoly.Rotate( aCenter, nAngle );
339 
340  ImplDrawPolygon( aPoly, pClixPolyPoly );
341 
342  if ( !bLinear)
343  {
344  aBorderRect = aMirrorRect;
345  aBorderRect.SetTop( static_cast<long>( aBorderRect.Bottom() - fBorder ) );
346  aMirrorRect.SetBottom( aBorderRect.Top() );
347  aPoly[0] = aBorderRect.TopLeft();
348  aPoly[1] = aBorderRect.TopRight();
349  aPoly[2] = aBorderRect.BottomRight();
350  aPoly[3] = aBorderRect.BottomLeft();
351  aPoly.Rotate( aCenter, nAngle );
352 
353  ImplDrawPolygon( aPoly, pClixPolyPoly );
354  }
355  }
356 
357  // calculate step count
358  long nStepCount = GetGradientSteps( rGradient, aRect, false/*bMtf*/ );
359 
360  // minimal three steps and maximal as max color steps
361  long nAbsRedSteps = std::abs( nEndRed - nStartRed );
362  long nAbsGreenSteps = std::abs( nEndGreen - nStartGreen );
363  long nAbsBlueSteps = std::abs( nEndBlue - nStartBlue );
364  long nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps );
365  nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps );
366  long nSteps = std::min( nStepCount, nMaxColorSteps );
367  if ( nSteps < 3)
368  {
369  nSteps = 3;
370  }
371 
372  double fScanInc = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps);
373  double fGradientLine = static_cast<double>(aRect.Top());
374  double fMirrorGradientLine = static_cast<double>(aMirrorRect.Bottom());
375 
376  const double fStepsMinus1 = static_cast<double>(nSteps) - 1.0;
377  if ( !bLinear)
378  {
379  nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap
380  }
381  for ( long i = 0; i < nSteps; i++ )
382  {
383  // linear interpolation of color
384  const double fAlpha = static_cast<double>(i) / fStepsMinus1;
385  double fTempColor = static_cast<double>(nStartRed) * (1.0-fAlpha) + static_cast<double>(nEndRed) * fAlpha;
386  nRed = GetGradientColorValue(static_cast<long>(fTempColor));
387  fTempColor = static_cast<double>(nStartGreen) * (1.0-fAlpha) + static_cast<double>(nEndGreen) * fAlpha;
388  nGreen = GetGradientColorValue(static_cast<long>(fTempColor));
389  fTempColor = static_cast<double>(nStartBlue) * (1.0-fAlpha) + static_cast<double>(nEndBlue) * fAlpha;
390  nBlue = GetGradientColorValue(static_cast<long>(fTempColor));
391 
392  mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
393 
394  // Polygon for this color step
395  aRect.SetTop( static_cast<long>( fGradientLine + static_cast<double>(i) * fScanInc ) );
396  aRect.SetBottom( static_cast<long>( fGradientLine + ( static_cast<double>(i) + 1.0 ) * fScanInc ) );
397  aPoly[0] = aRect.TopLeft();
398  aPoly[1] = aRect.TopRight();
399  aPoly[2] = aRect.BottomRight();
400  aPoly[3] = aRect.BottomLeft();
401  aPoly.Rotate( aCenter, nAngle );
402 
403  ImplDrawPolygon( aPoly, pClixPolyPoly );
404 
405  if ( !bLinear )
406  {
407  aMirrorRect.SetBottom( static_cast<long>( fMirrorGradientLine - static_cast<double>(i) * fScanInc ) );
408  aMirrorRect.SetTop( static_cast<long>( fMirrorGradientLine - (static_cast<double>(i) + 1.0)* fScanInc ) );
409  aPoly[0] = aMirrorRect.TopLeft();
410  aPoly[1] = aMirrorRect.TopRight();
411  aPoly[2] = aMirrorRect.BottomRight();
412  aPoly[3] = aMirrorRect.BottomLeft();
413  aPoly.Rotate( aCenter, nAngle );
414 
415  ImplDrawPolygon( aPoly, pClixPolyPoly );
416  }
417  }
418  if ( bLinear)
419  return;
420 
421  // draw middle polygon with end color
422  nRed = GetGradientColorValue(nEndRed);
423  nGreen = GetGradientColorValue(nEndGreen);
424  nBlue = GetGradientColorValue(nEndBlue);
425 
426  mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
427 
428  aRect.SetTop( static_cast<long>( fGradientLine + static_cast<double>(nSteps) * fScanInc ) );
429  aRect.SetBottom( static_cast<long>( fMirrorGradientLine - static_cast<double>(nSteps) * fScanInc ) );
430  aPoly[0] = aRect.TopLeft();
431  aPoly[1] = aRect.TopRight();
432  aPoly[2] = aRect.BottomRight();
433  aPoly[3] = aRect.BottomLeft();
434  aPoly.Rotate( aCenter, nAngle );
435 
436  ImplDrawPolygon( aPoly, pClixPolyPoly );
437 
438 }
439 
441 {
442  const vcl::Window *pWindow = dynamic_cast<const vcl::Window*>(this);
443  return pWindow && pWindow->SupportsDoubleBuffering();
444 }
445 
447  const Gradient& rGradient,
448  const tools::PolyPolygon* pClixPolyPoly )
449 {
450  assert(!is_double_buffered_window());
451 
452  // Determine if we output via Polygon or PolyPolygon
453  // For all rasteroperations other than Overpaint always use PolyPolygon,
454  // as we will get wrong results if we output multiple times on top of each other.
455  // Also for printers always use PolyPolygon, as not all printers
456  // can print polygons on top of each other.
457 
458  std::unique_ptr<tools::PolyPolygon> xPolyPoly;
459  tools::Rectangle aRect;
460  Point aCenter;
461  Color aStartCol( rGradient.GetStartColor() );
462  Color aEndCol( rGradient.GetEndColor() );
463  long nStartRed = ( static_cast<long>(aStartCol.GetRed()) * rGradient.GetStartIntensity() ) / 100;
464  long nStartGreen = ( static_cast<long>(aStartCol.GetGreen()) * rGradient.GetStartIntensity() ) / 100;
465  long nStartBlue = ( static_cast<long>(aStartCol.GetBlue()) * rGradient.GetStartIntensity() ) / 100;
466  long nEndRed = ( static_cast<long>(aEndCol.GetRed()) * rGradient.GetEndIntensity() ) / 100;
467  long nEndGreen = ( static_cast<long>(aEndCol.GetGreen()) * rGradient.GetEndIntensity() ) / 100;
468  long nEndBlue = ( static_cast<long>(aEndCol.GetBlue()) * rGradient.GetEndIntensity() ) / 100;
469  long nRedSteps = nEndRed - nStartRed;
470  long nGreenSteps = nEndGreen - nStartGreen;
471  long nBlueSteps = nEndBlue - nStartBlue;
472  sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
473 
474  rGradient.GetBoundRect( rRect, aRect, aCenter );
475 
477  xPolyPoly.reset(new tools::PolyPolygon( 2 ));
478 
479  long nStepCount = GetGradientSteps( rGradient, rRect, false/*bMtf*/, true/*bComplex*/ );
480 
481  // at least three steps and at most the number of colour differences
482  long nSteps = std::max( nStepCount, 2L );
483  long nCalcSteps = std::abs( nRedSteps );
484  long nTempSteps = std::abs( nGreenSteps );
485  if ( nTempSteps > nCalcSteps )
486  nCalcSteps = nTempSteps;
487  nTempSteps = std::abs( nBlueSteps );
488  if ( nTempSteps > nCalcSteps )
489  nCalcSteps = nTempSteps;
490  if ( nCalcSteps < nSteps )
491  nSteps = nCalcSteps;
492  if ( !nSteps )
493  nSteps = 1;
494 
495  // determine output limits and stepsizes for all directions
496  tools::Polygon aPoly;
497  double fScanLeft = aRect.Left();
498  double fScanTop = aRect.Top();
499  double fScanRight = aRect.Right();
500  double fScanBottom = aRect.Bottom();
501  double fScanIncX = static_cast<double>(aRect.GetWidth()) / static_cast<double>(nSteps) * 0.5;
502  double fScanIncY = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps) * 0.5;
503 
504  // all gradients are rendered as nested rectangles which shrink
505  // equally in each dimension - except for 'square' gradients
506  // which shrink to a central vertex but are not per-se square.
507  if( rGradient.GetStyle() != GradientStyle::Square )
508  {
509  fScanIncY = std::min( fScanIncY, fScanIncX );
510  fScanIncX = fScanIncY;
511  }
512  sal_uInt8 nRed = static_cast<sal_uInt8>(nStartRed), nGreen = static_cast<sal_uInt8>(nStartGreen), nBlue = static_cast<sal_uInt8>(nStartBlue);
513  bool bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output
514 
515  mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
516 
517  if( xPolyPoly )
518  {
519  aPoly = rRect;
520  xPolyPoly->Insert( aPoly );
521  xPolyPoly->Insert( aPoly );
522  }
523  else
524  {
525  // extend rect, to avoid missing bounding line
526  tools::Rectangle aExtRect( rRect );
527 
528  aExtRect.AdjustLeft( -1 );
529  aExtRect.AdjustTop( -1 );
530  aExtRect.AdjustRight(1 );
531  aExtRect.AdjustBottom(1 );
532 
533  aPoly = aExtRect;
534  ImplDrawPolygon( aPoly, pClixPolyPoly );
535  }
536 
537  // loop to output Polygon/PolyPolygon sequentially
538  for( long i = 1; i < nSteps; i++ )
539  {
540  // calculate new Polygon
541  fScanLeft += fScanIncX;
542  aRect.SetLeft( static_cast<long>( fScanLeft ) );
543  fScanTop += fScanIncY;
544  aRect.SetTop( static_cast<long>( fScanTop ) );
545  fScanRight -= fScanIncX;
546  aRect.SetRight( static_cast<long>( fScanRight ) );
547  fScanBottom -= fScanIncY;
548  aRect.SetBottom( static_cast<long>( fScanBottom ) );
549 
550  if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
551  break;
552 
553  if( rGradient.GetStyle() == GradientStyle::Radial || rGradient.GetStyle() == GradientStyle::Elliptical )
554  aPoly = tools::Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
555  else
556  aPoly = tools::Polygon( aRect );
557 
558  aPoly.Rotate( aCenter, nAngle );
559 
560  // adapt colour accordingly
561  const long nStepIndex = ( xPolyPoly ? i : ( i + 1 ) );
562  nRed = GetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) );
563  nGreen = GetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) );
564  nBlue = GetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) );
565 
566  // either slow tools::PolyPolygon output or fast Polygon-Painting
567  if( xPolyPoly )
568  {
569  bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output
570 
571  xPolyPoly->Replace( xPolyPoly->GetObject( 1 ), 0 );
572  xPolyPoly->Replace( aPoly, 1 );
573 
574  ImplDrawPolyPolygon( *xPolyPoly, pClixPolyPoly );
575 
576  // #107349# Set fill color _after_ geometry painting:
577  // xPolyPoly's geometry is the band from last iteration's
578  // aPoly to current iteration's aPoly. The window outdev
579  // path (see else below), on the other hand, paints the
580  // full aPoly. Thus, here, we're painting the band before
581  // the one painted in the window outdev path below. To get
582  // matching colors, have to delay color setting here.
583  mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
584  }
585  else
586  {
587  // #107349# Set fill color _before_ geometry painting
588  mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
589 
590  ImplDrawPolygon( aPoly, pClixPolyPoly );
591  }
592  }
593 
594  // we should draw last inner Polygon if we output PolyPolygon
595  if( xPolyPoly )
596  {
597  const tools::Polygon& rPoly = xPolyPoly->GetObject( 1 );
598 
599  if( !rPoly.GetBoundRect().IsEmpty() )
600  {
601  // #107349# Paint last polygon with end color only if loop
602  // has generated output. Otherwise, the current
603  // (i.e. start) color is taken, to generate _any_ output.
604  if( bPaintLastPolygon )
605  {
606  nRed = GetGradientColorValue( nEndRed );
607  nGreen = GetGradientColorValue( nEndGreen );
608  nBlue = GetGradientColorValue( nEndBlue );
609  }
610 
611  mpGraphics->SetFillColor( Color( nRed, nGreen, nBlue ) );
612  ImplDrawPolygon( rPoly, pClixPolyPoly );
613  }
614  }
615 }
616 
618  const Gradient& rGradient )
619 {
620  assert(!is_double_buffered_window());
621 
622  // get BoundRect of rotated rectangle
623  tools::Rectangle aRect;
624  Point aCenter;
625  sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
626 
627  rGradient.GetBoundRect( rRect, aRect, aCenter );
628 
629  bool bLinear = (rGradient.GetStyle() == GradientStyle::Linear);
630  double fBorder = rGradient.GetBorder() * aRect.GetHeight() / 100.0;
631  if ( !bLinear )
632  {
633  fBorder /= 2.0;
634  }
635  tools::Rectangle aMirrorRect = aRect; // used in style axial
636  aMirrorRect.SetTop( ( aRect.Top() + aRect.Bottom() ) / 2 );
637  if ( !bLinear )
638  {
639  aRect.SetBottom( aMirrorRect.Top() );
640  }
641 
642  // colour-intensities of start- and finish; change if needed
643  long nFactor;
644  Color aStartCol = rGradient.GetStartColor();
645  Color aEndCol = rGradient.GetEndColor();
646  long nStartRed = aStartCol.GetRed();
647  long nStartGreen = aStartCol.GetGreen();
648  long nStartBlue = aStartCol.GetBlue();
649  long nEndRed = aEndCol.GetRed();
650  long nEndGreen = aEndCol.GetGreen();
651  long nEndBlue = aEndCol.GetBlue();
652  nFactor = rGradient.GetStartIntensity();
653  nStartRed = (nStartRed * nFactor) / 100;
654  nStartGreen = (nStartGreen * nFactor) / 100;
655  nStartBlue = (nStartBlue * nFactor) / 100;
656  nFactor = rGradient.GetEndIntensity();
657  nEndRed = (nEndRed * nFactor) / 100;
658  nEndGreen = (nEndGreen * nFactor) / 100;
659  nEndBlue = (nEndBlue * nFactor) / 100;
660 
661  // gradient style axial has exchanged start and end colors
662  if ( !bLinear)
663  {
664  long nTempColor = nStartRed;
665  nStartRed = nEndRed;
666  nEndRed = nTempColor;
667  nTempColor = nStartGreen;
668  nStartGreen = nEndGreen;
669  nEndGreen = nTempColor;
670  nTempColor = nStartBlue;
671  nStartBlue = nEndBlue;
672  nEndBlue = nTempColor;
673  }
674 
675  sal_uInt8 nRed;
676  sal_uInt8 nGreen;
677  sal_uInt8 nBlue;
678 
679  // Create border
680  tools::Rectangle aBorderRect = aRect;
681  tools::Polygon aPoly( 4 );
682  if (fBorder > 0.0)
683  {
684  nRed = static_cast<sal_uInt8>(nStartRed);
685  nGreen = static_cast<sal_uInt8>(nStartGreen);
686  nBlue = static_cast<sal_uInt8>(nStartBlue);
687 
688  mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
689 
690  aBorderRect.SetBottom( static_cast<long>( aBorderRect.Top() + fBorder ) );
691  aRect.SetTop( aBorderRect.Bottom() );
692  aPoly[0] = aBorderRect.TopLeft();
693  aPoly[1] = aBorderRect.TopRight();
694  aPoly[2] = aBorderRect.BottomRight();
695  aPoly[3] = aBorderRect.BottomLeft();
696  aPoly.Rotate( aCenter, nAngle );
697 
698  mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
699 
700  if ( !bLinear)
701  {
702  aBorderRect = aMirrorRect;
703  aBorderRect.SetTop( static_cast<long>( aBorderRect.Bottom() - fBorder ) );
704  aMirrorRect.SetBottom( aBorderRect.Top() );
705  aPoly[0] = aBorderRect.TopLeft();
706  aPoly[1] = aBorderRect.TopRight();
707  aPoly[2] = aBorderRect.BottomRight();
708  aPoly[3] = aBorderRect.BottomLeft();
709  aPoly.Rotate( aCenter, nAngle );
710 
711  mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
712  }
713  }
714 
715  long nStepCount = GetGradientSteps( rGradient, aRect, true/*bMtf*/ );
716 
717  // minimal three steps and maximal as max color steps
718  long nAbsRedSteps = std::abs( nEndRed - nStartRed );
719  long nAbsGreenSteps = std::abs( nEndGreen - nStartGreen );
720  long nAbsBlueSteps = std::abs( nEndBlue - nStartBlue );
721  long nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps );
722  nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps );
723  long nSteps = std::min( nStepCount, nMaxColorSteps );
724  if ( nSteps < 3)
725  {
726  nSteps = 3;
727  }
728 
729  double fScanInc = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps);
730  double fGradientLine = static_cast<double>(aRect.Top());
731  double fMirrorGradientLine = static_cast<double>(aMirrorRect.Bottom());
732 
733  const double fStepsMinus1 = static_cast<double>(nSteps) - 1.0;
734  if ( !bLinear)
735  {
736  nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap
737  }
738  for ( long i = 0; i < nSteps; i++ )
739  {
740  // linear interpolation of color
741  double fAlpha = static_cast<double>(i) / fStepsMinus1;
742  double fTempColor = static_cast<double>(nStartRed) * (1.0-fAlpha) + static_cast<double>(nEndRed) * fAlpha;
743  nRed = GetGradientColorValue(static_cast<long>(fTempColor));
744  fTempColor = static_cast<double>(nStartGreen) * (1.0-fAlpha) + static_cast<double>(nEndGreen) * fAlpha;
745  nGreen = GetGradientColorValue(static_cast<long>(fTempColor));
746  fTempColor = static_cast<double>(nStartBlue) * (1.0-fAlpha) + static_cast<double>(nEndBlue) * fAlpha;
747  nBlue = GetGradientColorValue(static_cast<long>(fTempColor));
748 
749  mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
750 
751  // Polygon for this color step
752  aRect.SetTop( static_cast<long>( fGradientLine + static_cast<double>(i) * fScanInc ) );
753  aRect.SetBottom( static_cast<long>( fGradientLine + ( static_cast<double>(i) + 1.0 ) * fScanInc ) );
754  aPoly[0] = aRect.TopLeft();
755  aPoly[1] = aRect.TopRight();
756  aPoly[2] = aRect.BottomRight();
757  aPoly[3] = aRect.BottomLeft();
758  aPoly.Rotate( aCenter, nAngle );
759 
760  mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
761 
762  if ( !bLinear )
763  {
764  aMirrorRect.SetBottom( static_cast<long>( fMirrorGradientLine - static_cast<double>(i) * fScanInc ) );
765  aMirrorRect.SetTop( static_cast<long>( fMirrorGradientLine - (static_cast<double>(i) + 1.0)* fScanInc ) );
766  aPoly[0] = aMirrorRect.TopLeft();
767  aPoly[1] = aMirrorRect.TopRight();
768  aPoly[2] = aMirrorRect.BottomRight();
769  aPoly[3] = aMirrorRect.BottomLeft();
770  aPoly.Rotate( aCenter, nAngle );
771 
772  mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
773  }
774  }
775  if ( bLinear)
776  return;
777 
778  // draw middle polygon with end color
779  nRed = GetGradientColorValue(nEndRed);
780  nGreen = GetGradientColorValue(nEndGreen);
781  nBlue = GetGradientColorValue(nEndBlue);
782 
783  mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
784 
785  aRect.SetTop( static_cast<long>( fGradientLine + static_cast<double>(nSteps) * fScanInc ) );
786  aRect.SetBottom( static_cast<long>( fMirrorGradientLine - static_cast<double>(nSteps) * fScanInc ) );
787  aPoly[0] = aRect.TopLeft();
788  aPoly[1] = aRect.TopRight();
789  aPoly[2] = aRect.BottomRight();
790  aPoly[3] = aRect.BottomLeft();
791  aPoly.Rotate( aCenter, nAngle );
792 
793  mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
794 
795 }
796 
798  const Gradient& rGradient )
799 {
800  assert(!is_double_buffered_window());
801 
802  // Determine if we output via Polygon or PolyPolygon
803  // For all rasteroperations other than Overpaint always use PolyPolygon,
804  // as we will get wrong results if we output multiple times on top of each other.
805  // Also for printers always use PolyPolygon, as not all printers
806  // can print polygons on top of each other.
807 
808  std::unique_ptr<tools::PolyPolygon> xPolyPoly;
809  tools::Rectangle aRect;
810  Point aCenter;
811  Color aStartCol( rGradient.GetStartColor() );
812  Color aEndCol( rGradient.GetEndColor() );
813  long nStartRed = ( static_cast<long>(aStartCol.GetRed()) * rGradient.GetStartIntensity() ) / 100;
814  long nStartGreen = ( static_cast<long>(aStartCol.GetGreen()) * rGradient.GetStartIntensity() ) / 100;
815  long nStartBlue = ( static_cast<long>(aStartCol.GetBlue()) * rGradient.GetStartIntensity() ) / 100;
816  long nEndRed = ( static_cast<long>(aEndCol.GetRed()) * rGradient.GetEndIntensity() ) / 100;
817  long nEndGreen = ( static_cast<long>(aEndCol.GetGreen()) * rGradient.GetEndIntensity() ) / 100;
818  long nEndBlue = ( static_cast<long>(aEndCol.GetBlue()) * rGradient.GetEndIntensity() ) / 100;
819  long nRedSteps = nEndRed - nStartRed;
820  long nGreenSteps = nEndGreen - nStartGreen;
821  long nBlueSteps = nEndBlue - nStartBlue;
822  sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
823 
824  rGradient.GetBoundRect( rRect, aRect, aCenter );
825 
826  xPolyPoly.reset(new tools::PolyPolygon( 2 ));
827 
828  // last parameter - true if complex gradient, false if linear
829  long nStepCount = GetGradientSteps( rGradient, rRect, true, true );
830 
831  // at least three steps and at most the number of colour differences
832  long nSteps = std::max( nStepCount, 2L );
833  long nCalcSteps = std::abs( nRedSteps );
834  long nTempSteps = std::abs( nGreenSteps );
835  if ( nTempSteps > nCalcSteps )
836  nCalcSteps = nTempSteps;
837  nTempSteps = std::abs( nBlueSteps );
838  if ( nTempSteps > nCalcSteps )
839  nCalcSteps = nTempSteps;
840  if ( nCalcSteps < nSteps )
841  nSteps = nCalcSteps;
842  if ( !nSteps )
843  nSteps = 1;
844 
845  // determine output limits and stepsizes for all directions
846  tools::Polygon aPoly;
847  double fScanLeft = aRect.Left();
848  double fScanTop = aRect.Top();
849  double fScanRight = aRect.Right();
850  double fScanBottom = aRect.Bottom();
851  double fScanIncX = static_cast<double>(aRect.GetWidth()) / static_cast<double>(nSteps) * 0.5;
852  double fScanIncY = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps) * 0.5;
853 
854  // all gradients are rendered as nested rectangles which shrink
855  // equally in each dimension - except for 'square' gradients
856  // which shrink to a central vertex but are not per-se square.
857  if( rGradient.GetStyle() != GradientStyle::Square )
858  {
859  fScanIncY = std::min( fScanIncY, fScanIncX );
860  fScanIncX = fScanIncY;
861  }
862  sal_uInt8 nRed = static_cast<sal_uInt8>(nStartRed), nGreen = static_cast<sal_uInt8>(nStartGreen), nBlue = static_cast<sal_uInt8>(nStartBlue);
863  bool bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output
864 
865  mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
866 
867  aPoly = rRect;
868  xPolyPoly->Insert( aPoly );
869  xPolyPoly->Insert( aPoly );
870 
871  // loop to output Polygon/PolyPolygon sequentially
872  for( long i = 1; i < nSteps; i++ )
873  {
874  // calculate new Polygon
875  fScanLeft += fScanIncX;
876  aRect.SetLeft( static_cast<long>( fScanLeft ) );
877  fScanTop += fScanIncY;
878  aRect.SetTop( static_cast<long>( fScanTop ) );
879  fScanRight -= fScanIncX;
880  aRect.SetRight( static_cast<long>( fScanRight ) );
881  fScanBottom -= fScanIncY;
882  aRect.SetBottom( static_cast<long>( fScanBottom ) );
883 
884  if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
885  break;
886 
887  if( rGradient.GetStyle() == GradientStyle::Radial || rGradient.GetStyle() == GradientStyle::Elliptical )
888  aPoly = tools::Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
889  else
890  aPoly = tools::Polygon( aRect );
891 
892  aPoly.Rotate( aCenter, nAngle );
893 
894  // adapt colour accordingly
895  const long nStepIndex = ( xPolyPoly ? i : ( i + 1 ) );
896  nRed = GetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) );
897  nGreen = GetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) );
898  nBlue = GetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) );
899 
900  bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output
901 
902  xPolyPoly->Replace( xPolyPoly->GetObject( 1 ), 0 );
903  xPolyPoly->Replace( aPoly, 1 );
904 
905  mpMetaFile->AddAction( new MetaPolyPolygonAction( *xPolyPoly ) );
906 
907  // #107349# Set fill color _after_ geometry painting:
908  // xPolyPoly's geometry is the band from last iteration's
909  // aPoly to current iteration's aPoly. The window outdev
910  // path (see else below), on the other hand, paints the
911  // full aPoly. Thus, here, we're painting the band before
912  // the one painted in the window outdev path below. To get
913  // matching colors, have to delay color setting here.
914  mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
915  }
916 
917  const tools::Polygon& rPoly = xPolyPoly->GetObject( 1 );
918 
919  if( !rPoly.GetBoundRect().IsEmpty() )
920  {
921  // #107349# Paint last polygon with end color only if loop
922  // has generated output. Otherwise, the current
923  // (i.e. start) color is taken, to generate _any_ output.
924  if( bPaintLastPolygon )
925  {
926  nRed = GetGradientColorValue( nEndRed );
927  nGreen = GetGradientColorValue( nEndGreen );
928  nBlue = GetGradientColorValue( nEndBlue );
929  }
930 
931  mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
932  mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) );
933  }
934 }
935 
937 {
938  long nInc = (nMinRect < 50) ? 2 : 4;
939 
940  return nInc;
941 }
942 
943 long OutputDevice::GetGradientSteps( const Gradient& rGradient, const tools::Rectangle& rRect, bool bMtf, bool bComplex )
944 {
945  // calculate step count
946  long nStepCount = rGradient.GetSteps();
947  long nMinRect;
948 
949  // generate nStepCount, if not passed
950  if (bComplex)
951  nMinRect = std::min( rRect.GetWidth(), rRect.GetHeight() );
952  else
953  nMinRect = rRect.GetHeight();
954 
955  if ( !nStepCount )
956  {
957  long nInc;
958 
959  nInc = GetGradientStepCount (nMinRect);
960  if ( !nInc || bMtf )
961  nInc = 1;
962  nStepCount = nMinRect / nInc;
963  }
964 
965  return nStepCount;
966 }
967 
969 {
970  Color aColor;
971 
972  // we should never call on this function if any of these aren't set!
974 
976  aColor = COL_BLACK;
978  aColor = COL_WHITE;
981 
982  return aColor;
983 }
984 
986 {
987  // this should only be called with the drawing mode is for grayscale gradients
989 
990  Color aStartCol( rGradient.GetStartColor() );
991  Color aEndCol( rGradient.GetEndColor() );
992 
993  if ( mnDrawMode & DrawModeFlags::GrayGradient )
994  {
995  sal_uInt8 cStartLum = aStartCol.GetLuminance(), cEndLum = aEndCol.GetLuminance();
996  aStartCol = Color( cStartLum, cStartLum, cStartLum );
997  aEndCol = Color( cEndLum, cEndLum, cEndLum );
998  }
999 
1000  rGradient.SetStartColor( aStartCol );
1001  rGradient.SetEndColor( aEndCol );
1002 }
1003 
1004 void OutputDevice::AddGradientActions( const tools::Rectangle& rRect, const Gradient& rGradient,
1005  GDIMetaFile& rMtf )
1006 {
1007 
1008  tools::Rectangle aRect( rRect );
1009 
1010  aRect.Justify();
1011 
1012  // do nothing if the rectangle is empty
1013  if ( aRect.IsEmpty() )
1014  return;
1015 
1016  Gradient aGradient( rGradient );
1017  GDIMetaFile* pOldMtf = mpMetaFile;
1018 
1019  mpMetaFile = &rMtf;
1020  mpMetaFile->AddAction( new MetaPushAction( PushFlags::ALL ) );
1021  mpMetaFile->AddAction( new MetaISectRectClipRegionAction( aRect ) );
1022  mpMetaFile->AddAction( new MetaLineColorAction( Color(), false ) );
1023 
1024  // because we draw with no border line, we have to expand gradient
1025  // rect to avoid missing lines on the right and bottom edge
1026  aRect.AdjustLeft( -1 );
1027  aRect.AdjustTop( -1 );
1028  aRect.AdjustRight( 1 );
1029  aRect.AdjustBottom( 1 );
1030 
1031  // calculate step count if necessary
1032  if ( !aGradient.GetSteps() )
1033  aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
1034 
1035  if( aGradient.GetStyle() == GradientStyle::Linear || aGradient.GetStyle() == GradientStyle::Axial )
1036  DrawLinearGradientToMetafile( aRect, aGradient );
1037  else
1038  DrawComplexGradientToMetafile( aRect, aGradient );
1039 
1040  mpMetaFile->AddAction( new MetaPopAction() );
1041  mpMetaFile = pOldMtf;
1042 
1043 }
1044 
1045 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void SetEndColor(const Color &rColor)
sal_uInt16 Count() const
void EnableOutput(bool bEnable=true)
Point TopLeft() const
long GetWidth() const
GradientStyle GetStyle() const
long GetHeight() const
constexpr::Color COL_BLACK(0x00, 0x00, 0x00)
sal_uInt8 GetRed() const
SAL_DLLPRIVATE bool ImplIsRecordLayout() const
Definition: outdev.cxx:650
virtual bool UsePolyPolygonForComplexGradient()=0
SAL_DLLPRIVATE void DrawComplexGradient(const tools::Rectangle &rRect, const Gradient &rGradient, const tools::PolyPolygon *pClipPolyPoly)
Point BottomLeft() const
long AdjustLeft(long nHorzMoveDelta)
DrawModeFlags mnDrawMode
Definition: outdev.hxx:355
const StyleSettings & GetStyleSettings() const
bool IsOutputEnabled() const
Definition: outdev.hxx:594
sal_uInt8 GetLuminance() const
bool mbOutputClipped
Definition: outdev.hxx:381
sal_uInt16 GetBorder() const
SAL_DLLPRIVATE bool is_double_buffered_window() const
SAL_DLLPRIVATE void DrawLinearGradientToMetafile(const tools::Rectangle &rRect, const Gradient &rGradient)
void IntersectClipRegion(const tools::Rectangle &rRect)
SAL_DLLPRIVATE Color GetSingleColorGradientFill()
#define GRADIENT_DEFAULT_STEPCOUNT
SAL_DLLPRIVATE void DrawGradientToMetafile(const tools::PolyPolygon &rPolyPoly, const Gradient &rGradient)
long AdjustBottom(long nVertMoveDelta)
bool IsEmpty() const
void SetSteps(sal_uInt16 nSteps)
long Right() const
SAL_DLLPRIVATE void DrawLinearGradient(const tools::Rectangle &rRect, const Gradient &rGradient, const tools::PolyPolygon *pClipPolyPoly)
void SetStartColor(const Color &rColor)
long Top() const
sal_uInt8 GetBlue() const
virtual long GetGradientStepCount(long nMinRect)
bool DrawGradient(const tools::PolyPolygon &rPolyPoly, const Gradient &rGradient)
void Rotate(const Point &rCenter, double fSin, double fCos)
SalGraphics * mpGraphics
Graphics context to draw on.
Definition: outdev.hxx:316
SAL_DLLPRIVATE long GetGradientSteps(const Gradient &rGradient, const tools::Rectangle &rRect, bool bMtf, bool bComplex=false)
Point BottomRight() const
virtual void SetFillColor()=0
void SetLineColor()
void GetBoundRect(const tools::Rectangle &rRect, tools::Rectangle &rBoundRect, Point &rCenter) const
void SetTop(long v)
int i
virtual void SetLineColor()=0
sal_uInt16 GetAngle() const
virtual bool AcquireGraphics() const =0
Acquire a graphics device that the output device uses to draw on.
bool IsRect() const
Size GetOutputSize() const
Definition: outdev.hxx:450
void SetRight(long v)
bool mbInitLineColor
Definition: outdev.hxx:384
void SetFillColor()
long Bottom() const
sal_uInt16 GetEndIntensity() const
void AddGradientActions(const tools::Rectangle &rRect, const Gradient &rGradient, GDIMetaFile &rMtf)
bool mbLineColor
Definition: outdev.hxx:382
const AllSettings & GetSettings() const
Definition: outdev.hxx:420
sal_uInt16 GetSteps() const
const Color & GetStartColor() const
SAL_DLLPRIVATE void SetGrayscaleColors(Gradient &rGradient)
Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1179
sal_uInt8 GetGreen() const
VclPtr< VirtualDevice > mpAlphaVDev
Definition: outdev.hxx:331
long AdjustRight(long nHorzMoveDelta)
SAL_DLLPRIVATE void DrawComplexGradientToMetafile(const tools::Rectangle &rRect, const Gradient &rGradient)
SAL_DLLPRIVATE void ImplDrawPolyPolygon(const tools::PolyPolygon &rPolyPoly, const tools::PolyPolygon *pClipPolyPoly)
Definition: polygon.cxx:463
unsigned char sal_uInt8
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:543
bool mbInitClipRegion
Definition: outdev.hxx:388
const Color & GetEndColor() const
void SetRasterOp(RasterOp eRasterOp)
void SetBottom(long v)
void DrawPolyPolygon(const tools::PolyPolygon &rPolyPoly)
Render the given poly-polygon.
Definition: polygon.cxx:36
long AdjustTop(long nVertMoveDelta)
virtual void InitClipRegion()
const Color & GetWindowColor() const
constexpr::Color COL_WHITE(0xFF, 0xFF, 0xFF)
long Left() const
bool mbInitFillColor
Definition: outdev.hxx:385
SAL_DLLPRIVATE tools::Rectangle ImplLogicToDevicePixel(const tools::Rectangle &rLogicRect) const
Convert a logical rectangle to a rectangle in physical device pixel units.
Definition: map.cxx:504
void SetLeft(long v)
tools::Rectangle GetBoundRect() const
tools::Rectangle GetBoundRect() const
bool IsDeviceOutputNecessary() const
Definition: outdev.hxx:595
void Push(PushFlags nFlags=PushFlags::ALL)
Definition: outdevstate.cxx:60
void DrawGradient(const tools::Rectangle &rRect, const Gradient &rGradient)
bool SupportsDoubleBuffering() const
Can the widget derived from this Window do the double-buffering via RenderContext properly...
Definition: window.cxx:3869
sal_uInt16 GetStartIntensity() const
virtual void ClipAndDrawGradientMetafile(const Gradient &rGradient, const tools::PolyPolygon &rPolyPoly)
Point TopRight() const
Point Center() const
GDIMetaFile * mpMetaFile
Definition: outdev.hxx:319
SAL_DLLPRIVATE void ImplDrawPolygon(const tools::Polygon &rPoly, const tools::PolyPolygon *pClipPolyPoly=nullptr)
Definition: polygon.cxx:445