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