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