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/virdev.hxx>
24 : #include <vcl/outdev.hxx>
25 : #include <vcl/settings.hxx>
26 :
27 : #include "salgdi.hxx"
28 :
29 : #define GRADIENT_DEFAULT_STEPCOUNT 0
30 :
31 278091 : void OutputDevice::DrawGradient( const Rectangle& rRect,
32 : const Gradient& rGradient )
33 : {
34 : // Convert rectangle to a tools::PolyPolygon by first converting to a Polygon
35 278091 : Polygon aPolygon ( rRect );
36 556182 : tools::PolyPolygon aPolyPoly ( aPolygon );
37 :
38 556182 : DrawGradient ( aPolyPoly, rGradient );
39 278091 : }
40 :
41 278103 : void OutputDevice::DrawGradient( const tools::PolyPolygon& rPolyPoly,
42 : const Gradient& rGradient )
43 : {
44 278103 : if ( mnDrawMode & DRAWMODE_NOGRADIENT )
45 0 : return; // nothing to draw!
46 :
47 278103 : if ( mbInitClipRegion )
48 268483 : InitClipRegion();
49 :
50 278103 : if ( mbOutputClipped )
51 19132 : return;
52 :
53 258971 : if ( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() )
54 : {
55 258971 : if ( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) )
56 : {
57 0 : Color aColor = GetSingleColorGradientFill();
58 :
59 0 : Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
60 0 : SetLineColor( aColor );
61 0 : SetFillColor( aColor );
62 0 : DrawPolyPolygon( rPolyPoly );
63 0 : Pop();
64 0 : return;
65 : }
66 :
67 258971 : Gradient aGradient( rGradient );
68 :
69 258971 : if ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) )
70 : {
71 0 : SetGrayscaleColors( aGradient );
72 : }
73 :
74 258971 : if( mpMetaFile )
75 : {
76 24 : const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
77 :
78 24 : if ( rPolyPoly.IsRect() )
79 : {
80 12 : mpMetaFile->AddAction( new MetaGradientAction( aBoundRect, aGradient ) );
81 : }
82 : else
83 : {
84 12 : mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN" ) );
85 12 : mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) );
86 :
87 12 : Push( PushFlags::CLIPREGION );
88 12 : IntersectClipRegion(vcl::Region(rPolyPoly));
89 12 : DrawGradient( aBoundRect, rGradient );
90 12 : Pop();
91 :
92 12 : mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END" ) );
93 : }
94 : }
95 :
96 258971 : if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
97 24 : return;
98 :
99 : // Clip and then draw the gradient
100 258947 : if( !Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
101 : {
102 258947 : const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
103 :
104 : // convert rectangle to pixels
105 258947 : Rectangle aRect( ImplLogicToDevicePixel( aBoundRect ) );
106 258947 : aRect.Justify();
107 :
108 : // do nothing if the rectangle is empty
109 258947 : if ( !aRect.IsEmpty() )
110 : {
111 258947 : if( !mpGraphics && !AcquireGraphics() )
112 0 : return;
113 :
114 : // secure clip region
115 258947 : Push( PushFlags::CLIPREGION );
116 258947 : IntersectClipRegion( aBoundRect );
117 :
118 258947 : if( mbInitClipRegion )
119 258947 : InitClipRegion();
120 :
121 258947 : if( !mbOutputClipped )
122 : {
123 138190 : tools::PolyPolygon aClipPolyPoly( ImplLogicToDevicePixel( rPolyPoly ) );
124 :
125 : // draw gradients without border
126 138190 : if( mbLineColor || mbInitLineColor )
127 : {
128 138164 : mpGraphics->SetLineColor();
129 138164 : mbInitLineColor = true;
130 : }
131 :
132 138190 : mbInitFillColor = true;
133 :
134 : // calculate step count if necessary
135 138190 : if ( !aGradient.GetSteps() )
136 138190 : aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
137 :
138 138190 : if ( rPolyPoly.IsRect() )
139 : {
140 : // because we draw with no border line, we have to expand gradient
141 : // rect to avoid missing lines on the right and bottom edge
142 138190 : aRect.Left()--;
143 138190 : aRect.Top()--;
144 138190 : aRect.Right()++;
145 138190 : aRect.Bottom()++;
146 : }
147 :
148 : // if the clipping polypolygon is a rectangle, then it's the same size as the bounding of the
149 : // polypolygon, so pass in a NULL for the clipping parameter
150 138190 : if( aGradient.GetStyle() == GradientStyle_LINEAR || rGradient.GetStyle() == GradientStyle_AXIAL )
151 138190 : DrawLinearGradient( aRect, aGradient, false, aClipPolyPoly.IsRect() ? NULL : &aClipPolyPoly );
152 : else
153 0 : DrawComplexGradient( aRect, aGradient, false, aClipPolyPoly.IsRect() ? NULL : &aClipPolyPoly );
154 : }
155 :
156 258947 : Pop();
157 : }
158 258947 : }
159 : }
160 :
161 258947 : if( mpAlphaVDev )
162 0 : mpAlphaVDev->DrawPolyPolygon( rPolyPoly );
163 : }
164 :
165 : namespace
166 : {
167 8654772 : inline sal_uInt8 GetGradientColorValue( long nValue )
168 : {
169 8654772 : if ( nValue < 0 )
170 0 : return 0;
171 8654772 : else if ( nValue > 0xFF )
172 0 : return 0xFF;
173 : else
174 8654772 : return (sal_uInt8)nValue;
175 : }
176 : }
177 :
178 138190 : void OutputDevice::DrawLinearGradient( const Rectangle& rRect,
179 : const Gradient& rGradient,
180 : bool bMtf, const tools::PolyPolygon* pClipPolyPoly )
181 : {
182 : // get BoundRect of rotated rectangle
183 138190 : Rectangle aRect;
184 138190 : Point aCenter;
185 138190 : sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
186 :
187 138190 : rGradient.GetBoundRect( rRect, aRect, aCenter );
188 :
189 138190 : bool bLinear = (rGradient.GetStyle() == GradientStyle_LINEAR);
190 138190 : double fBorder = rGradient.GetBorder() * aRect.GetHeight() / 100.0;
191 138190 : if ( !bLinear )
192 : {
193 0 : fBorder /= 2.0;
194 : }
195 138190 : Rectangle aMirrorRect = aRect; // used in style axial
196 138190 : aMirrorRect.Top() = ( aRect.Top() + aRect.Bottom() ) / 2;
197 138190 : if ( !bLinear )
198 : {
199 0 : aRect.Bottom() = aMirrorRect.Top();
200 : }
201 :
202 : // colour-intensities of start- and finish; change if needed
203 : long nFactor;
204 138190 : Color aStartCol = rGradient.GetStartColor();
205 138190 : Color aEndCol = rGradient.GetEndColor();
206 138190 : long nStartRed = aStartCol.GetRed();
207 138190 : long nStartGreen = aStartCol.GetGreen();
208 138190 : long nStartBlue = aStartCol.GetBlue();
209 138190 : long nEndRed = aEndCol.GetRed();
210 138190 : long nEndGreen = aEndCol.GetGreen();
211 138190 : long nEndBlue = aEndCol.GetBlue();
212 138190 : nFactor = rGradient.GetStartIntensity();
213 138190 : nStartRed = (nStartRed * nFactor) / 100;
214 138190 : nStartGreen = (nStartGreen * nFactor) / 100;
215 138190 : nStartBlue = (nStartBlue * nFactor) / 100;
216 138190 : nFactor = rGradient.GetEndIntensity();
217 138190 : nEndRed = (nEndRed * nFactor) / 100;
218 138190 : nEndGreen = (nEndGreen * nFactor) / 100;
219 138190 : nEndBlue = (nEndBlue * nFactor) / 100;
220 :
221 : // gradient style axial has exchanged start and end colors
222 138190 : if ( !bLinear)
223 : {
224 0 : long nTempColor = nStartRed;
225 0 : nStartRed = nEndRed;
226 0 : nEndRed = nTempColor;
227 0 : nTempColor = nStartGreen;
228 0 : nStartGreen = nEndGreen;
229 0 : nEndGreen = nTempColor;
230 0 : nTempColor = nStartBlue;
231 0 : nStartBlue = nEndBlue;
232 0 : nEndBlue = nTempColor;
233 : }
234 :
235 : sal_uInt8 nRed;
236 : sal_uInt8 nGreen;
237 : sal_uInt8 nBlue;
238 :
239 : // Create border
240 138190 : Rectangle aBorderRect = aRect;
241 138190 : Polygon aPoly( 4 );
242 138190 : if (fBorder > 0.0)
243 : {
244 0 : nRed = (sal_uInt8)nStartRed;
245 0 : nGreen = (sal_uInt8)nStartGreen;
246 0 : nBlue = (sal_uInt8)nStartBlue;
247 0 : if ( bMtf )
248 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
249 : else
250 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
251 :
252 0 : aBorderRect.Bottom() = (long)( aBorderRect.Top() + fBorder );
253 0 : aRect.Top() = aBorderRect.Bottom();
254 0 : aPoly[0] = aBorderRect.TopLeft();
255 0 : aPoly[1] = aBorderRect.TopRight();
256 0 : aPoly[2] = aBorderRect.BottomRight();
257 0 : aPoly[3] = aBorderRect.BottomLeft();
258 0 : aPoly.Rotate( aCenter, nAngle );
259 0 : if ( bMtf )
260 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
261 : else
262 0 : ImplDrawPolygon( aPoly, pClipPolyPoly );
263 0 : if ( !bLinear)
264 : {
265 0 : aBorderRect = aMirrorRect;
266 0 : aBorderRect.Top() = (long) ( aBorderRect.Bottom() - fBorder );
267 0 : aMirrorRect.Bottom() = aBorderRect.Top();
268 0 : aPoly[0] = aBorderRect.TopLeft();
269 0 : aPoly[1] = aBorderRect.TopRight();
270 0 : aPoly[2] = aBorderRect.BottomRight();
271 0 : aPoly[3] = aBorderRect.BottomLeft();
272 0 : aPoly.Rotate( aCenter, nAngle );
273 0 : if ( bMtf )
274 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
275 : else
276 0 : ImplDrawPolygon( aPoly, pClipPolyPoly );
277 : }
278 : }
279 :
280 : // calculate step count
281 138190 : long nStepCount = GetGradientSteps( rGradient, aRect, bMtf );
282 :
283 : // minimal three steps and maximal as max color steps
284 138190 : long nAbsRedSteps = std::abs( nEndRed - nStartRed );
285 138190 : long nAbsGreenSteps = std::abs( nEndGreen - nStartGreen );
286 138190 : long nAbsBlueSteps = std::abs( nEndBlue - nStartBlue );
287 138190 : long nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps );
288 138190 : nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps );
289 138190 : long nSteps = std::min( nStepCount, nMaxColorSteps );
290 138190 : if ( nSteps < 3)
291 : {
292 42 : nSteps = 3;
293 : }
294 :
295 138190 : double fScanInc = ((double)aRect.GetHeight()) / (double) nSteps;
296 138190 : double fGradientLine = (double)aRect.Top();
297 138190 : double fMirrorGradientLine = (double) aMirrorRect.Bottom();
298 :
299 138190 : double fAlpha = 0.0;
300 138190 : const double fStepsMinus1 = ((double)nSteps) - 1.0;
301 : double fTempColor;
302 138190 : if ( !bLinear)
303 : {
304 0 : nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap
305 : }
306 3023114 : for ( long i = 0; i < nSteps; i++ )
307 : {
308 : // linear interpolation of color
309 2884924 : fAlpha = ((double)i) / fStepsMinus1;
310 2884924 : fTempColor = ((double)nStartRed) * (1.0-fAlpha) + ((double)nEndRed) * fAlpha;
311 2884924 : nRed = GetGradientColorValue((long)fTempColor);
312 2884924 : fTempColor = ((double)nStartGreen) * (1.0-fAlpha) + ((double)nEndGreen) * fAlpha;
313 2884924 : nGreen = GetGradientColorValue((long)fTempColor);
314 2884924 : fTempColor = ((double)nStartBlue) * (1.0-fAlpha) + ((double)nEndBlue) * fAlpha;
315 2884924 : nBlue = GetGradientColorValue((long)fTempColor);
316 2884924 : if ( bMtf )
317 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
318 : else
319 2884924 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
320 :
321 : // Polygon for this color step
322 2884924 : aRect.Top() = (long)( fGradientLine + ((double) i) * fScanInc );
323 2884924 : aRect.Bottom() = (long)( fGradientLine + ( ((double) i) + 1.0 ) * fScanInc );
324 2884924 : aPoly[0] = aRect.TopLeft();
325 2884924 : aPoly[1] = aRect.TopRight();
326 2884924 : aPoly[2] = aRect.BottomRight();
327 2884924 : aPoly[3] = aRect.BottomLeft();
328 2884924 : aPoly.Rotate( aCenter, nAngle );
329 2884924 : if ( bMtf )
330 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
331 : else
332 2884924 : ImplDrawPolygon( aPoly, pClipPolyPoly );
333 2884924 : if ( !bLinear )
334 : {
335 0 : aMirrorRect.Bottom() = (long)( fMirrorGradientLine - ((double) i) * fScanInc );
336 0 : aMirrorRect.Top() = (long)( fMirrorGradientLine - (((double) i) + 1.0)* fScanInc );
337 0 : aPoly[0] = aMirrorRect.TopLeft();
338 0 : aPoly[1] = aMirrorRect.TopRight();
339 0 : aPoly[2] = aMirrorRect.BottomRight();
340 0 : aPoly[3] = aMirrorRect.BottomLeft();
341 0 : aPoly.Rotate( aCenter, nAngle );
342 0 : if ( bMtf )
343 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
344 : else
345 0 : ImplDrawPolygon( aPoly, pClipPolyPoly );
346 : }
347 : }
348 138190 : if ( !bLinear)
349 : {
350 : // draw middle polygon with end color
351 0 : nRed = GetGradientColorValue(nEndRed);
352 0 : nGreen = GetGradientColorValue(nEndGreen);
353 0 : nBlue = GetGradientColorValue(nEndBlue);
354 0 : if ( bMtf )
355 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
356 : else
357 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
358 :
359 0 : aRect.Top() = (long)( fGradientLine + ((double)nSteps) * fScanInc );
360 0 : aRect.Bottom() = (long)( fMirrorGradientLine - ((double) nSteps) * fScanInc );
361 0 : aPoly[0] = aRect.TopLeft();
362 0 : aPoly[1] = aRect.TopRight();
363 0 : aPoly[2] = aRect.BottomRight();
364 0 : aPoly[3] = aRect.BottomLeft();
365 0 : aPoly.Rotate( aCenter, nAngle );
366 0 : if ( bMtf )
367 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
368 : else
369 0 : ImplDrawPolygon( aPoly, pClipPolyPoly );
370 138190 : }
371 138190 : }
372 :
373 0 : void OutputDevice::DrawComplexGradient( const Rectangle& rRect,
374 : const Gradient& rGradient,
375 : bool bMtf, const tools::PolyPolygon* pClipPolyPoly )
376 : {
377 : // Determine if we output via Polygon or PolyPolygon
378 : // For all rasteroperations other then Overpaint always use PolyPolygon,
379 : // as we will get wrong results if we output multiple times on top of each other.
380 : // Also for printers always use PolyPolygon, as not all printers
381 : // can print polygons on top of each other.
382 :
383 0 : boost::scoped_ptr<tools::PolyPolygon> pPolyPoly;
384 0 : Rectangle aRect;
385 0 : Point aCenter;
386 0 : Color aStartCol( rGradient.GetStartColor() );
387 0 : Color aEndCol( rGradient.GetEndColor() );
388 0 : long nStartRed = ( (long) aStartCol.GetRed() * rGradient.GetStartIntensity() ) / 100;
389 0 : long nStartGreen = ( (long) aStartCol.GetGreen() * rGradient.GetStartIntensity() ) / 100;
390 0 : long nStartBlue = ( (long) aStartCol.GetBlue() * rGradient.GetStartIntensity() ) / 100;
391 0 : long nEndRed = ( (long) aEndCol.GetRed() * rGradient.GetEndIntensity() ) / 100;
392 0 : long nEndGreen = ( (long) aEndCol.GetGreen() * rGradient.GetEndIntensity() ) / 100;
393 0 : long nEndBlue = ( (long) aEndCol.GetBlue() * rGradient.GetEndIntensity() ) / 100;
394 0 : long nRedSteps = nEndRed - nStartRed;
395 0 : long nGreenSteps = nEndGreen - nStartGreen;
396 0 : long nBlueSteps = nEndBlue - nStartBlue;
397 0 : sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
398 :
399 0 : rGradient.GetBoundRect( rRect, aRect, aCenter );
400 :
401 0 : if ( UsePolyPolygonForComplexGradient() || bMtf )
402 0 : pPolyPoly.reset(new tools::PolyPolygon( 2 ));
403 :
404 : // last parameter - true if complex gradient, false if linear
405 0 : long nStepCount = GetGradientSteps( rGradient, rRect, bMtf, true );
406 :
407 : // at least three steps and at most the number of colour differences
408 0 : long nSteps = std::max( nStepCount, 2L );
409 0 : long nCalcSteps = std::abs( nRedSteps );
410 0 : long nTempSteps = std::abs( nGreenSteps );
411 0 : if ( nTempSteps > nCalcSteps )
412 0 : nCalcSteps = nTempSteps;
413 0 : nTempSteps = std::abs( nBlueSteps );
414 0 : if ( nTempSteps > nCalcSteps )
415 0 : nCalcSteps = nTempSteps;
416 0 : if ( nCalcSteps < nSteps )
417 0 : nSteps = nCalcSteps;
418 0 : if ( !nSteps )
419 0 : nSteps = 1;
420 :
421 : // determine output limits and stepsizes for all directions
422 0 : Polygon aPoly;
423 0 : double fScanLeft = aRect.Left();
424 0 : double fScanTop = aRect.Top();
425 0 : double fScanRight = aRect.Right();
426 0 : double fScanBottom = aRect.Bottom();
427 0 : double fScanIncX = (double) aRect.GetWidth() / (double) nSteps * 0.5;
428 0 : double fScanIncY = (double) aRect.GetHeight() / (double) nSteps * 0.5;
429 :
430 : // all gradients are rendered as nested rectangles which shrink
431 : // equally in each dimension - except for 'square' gradients
432 : // which shrink to a central vertex but are not per-se square.
433 0 : if( rGradient.GetStyle() != GradientStyle_SQUARE )
434 : {
435 0 : fScanIncY = std::min( fScanIncY, fScanIncX );
436 0 : fScanIncX = fScanIncY;
437 : }
438 0 : sal_uInt8 nRed = (sal_uInt8) nStartRed, nGreen = (sal_uInt8) nStartGreen, nBlue = (sal_uInt8) nStartBlue;
439 0 : bool bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output
440 :
441 0 : if( bMtf )
442 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
443 : else
444 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
445 :
446 0 : if( pPolyPoly )
447 : {
448 0 : pPolyPoly->Insert( aPoly = rRect );
449 0 : pPolyPoly->Insert( aPoly );
450 : }
451 : else
452 : {
453 : // extend rect, to avoid missing bounding line
454 0 : Rectangle aExtRect( rRect );
455 :
456 0 : aExtRect.Left() -= 1;
457 0 : aExtRect.Top() -= 1;
458 0 : aExtRect.Right() += 1;
459 0 : aExtRect.Bottom() += 1;
460 :
461 0 : ImplDrawPolygon( aPoly = aExtRect, pClipPolyPoly );
462 : }
463 :
464 : // loop to output Polygone/PolyPolygone sequentially
465 0 : for( long i = 1; i < nSteps; i++ )
466 : {
467 : // calculate new Polygon
468 0 : aRect.Left() = (long)( fScanLeft += fScanIncX );
469 0 : aRect.Top() = (long)( fScanTop += fScanIncY );
470 0 : aRect.Right() = (long)( fScanRight -= fScanIncX );
471 0 : aRect.Bottom() = (long)( fScanBottom -= fScanIncY );
472 :
473 0 : if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
474 0 : break;
475 :
476 0 : if( rGradient.GetStyle() == GradientStyle_RADIAL || rGradient.GetStyle() == GradientStyle_ELLIPTICAL )
477 0 : aPoly = Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
478 : else
479 0 : aPoly = Polygon( aRect );
480 :
481 0 : aPoly.Rotate( aCenter, nAngle );
482 :
483 : // adapt colour accordingly
484 0 : const long nStepIndex = ( ( pPolyPoly ) ? i : ( i + 1 ) );
485 0 : nRed = GetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) );
486 0 : nGreen = GetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) );
487 0 : nBlue = GetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) );
488 :
489 : // either slow tools::PolyPolygon output or fast Polygon-Paiting
490 0 : if( pPolyPoly )
491 : {
492 0 : bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output
493 :
494 0 : pPolyPoly->Replace( pPolyPoly->GetObject( 1 ), 0 );
495 0 : pPolyPoly->Replace( aPoly, 1 );
496 :
497 0 : if( bMtf )
498 0 : mpMetaFile->AddAction( new MetaPolyPolygonAction( *pPolyPoly ) );
499 : else
500 0 : ImplDrawPolyPolygon( *pPolyPoly, pClipPolyPoly );
501 :
502 : // #107349# Set fill color _after_ geometry painting:
503 : // pPolyPoly's geometry is the band from last iteration's
504 : // aPoly to current iteration's aPoly. The window outdev
505 : // path (see else below), on the other hand, paints the
506 : // full aPoly. Thus, here, we're painting the band before
507 : // the one painted in the window outdev path below. To get
508 : // matching colors, have to delay color setting here.
509 0 : if( bMtf )
510 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
511 : else
512 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
513 : }
514 : else
515 : {
516 : // #107349# Set fill color _before_ geometry painting
517 0 : if( bMtf )
518 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
519 : else
520 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
521 :
522 0 : ImplDrawPolygon( aPoly, pClipPolyPoly );
523 : }
524 : }
525 :
526 : // we should draw last inner Polygon if we output PolyPolygon
527 0 : if( pPolyPoly )
528 : {
529 0 : const Polygon& rPoly = pPolyPoly->GetObject( 1 );
530 :
531 0 : if( !rPoly.GetBoundRect().IsEmpty() )
532 : {
533 : // #107349# Paint last polygon with end color only if loop
534 : // has generated output. Otherwise, the current
535 : // (i.e. start) color is taken, to generate _any_ output.
536 0 : if( bPaintLastPolygon )
537 : {
538 0 : nRed = GetGradientColorValue( nEndRed );
539 0 : nGreen = GetGradientColorValue( nEndGreen );
540 0 : nBlue = GetGradientColorValue( nEndBlue );
541 : }
542 :
543 0 : if( bMtf )
544 : {
545 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
546 0 : mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) );
547 : }
548 : else
549 : {
550 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
551 0 : ImplDrawPolygon( rPoly, pClipPolyPoly );
552 : }
553 : }
554 0 : }
555 0 : }
556 :
557 138190 : long OutputDevice::GetGradientStepCount( long nMinRect )
558 : {
559 138190 : long nInc = (nMinRect < 50) ? 2 : 4;
560 :
561 138190 : return nInc;
562 : }
563 :
564 138190 : long OutputDevice::GetGradientSteps( const Gradient& rGradient, const Rectangle& rRect, bool bMtf, bool bComplex )
565 : {
566 : // calculate step count
567 138190 : long nStepCount = rGradient.GetSteps();
568 : long nMinRect;
569 :
570 : // generate nStepCount, if not passed
571 138190 : if (bComplex)
572 0 : nMinRect = std::min( rRect.GetWidth(), rRect.GetHeight() );
573 : else
574 138190 : nMinRect = rRect.GetHeight();
575 :
576 138190 : if ( !nStepCount )
577 : {
578 : long nInc;
579 :
580 138190 : nInc = GetGradientStepCount (nMinRect);
581 138190 : if ( !nInc || bMtf )
582 0 : nInc = 1;
583 138190 : nStepCount = nMinRect / nInc;
584 : }
585 :
586 138190 : return nStepCount;
587 : }
588 :
589 0 : Color OutputDevice::GetSingleColorGradientFill()
590 : {
591 0 : Color aColor;
592 :
593 : // we should never call on this function if any of these aren't set!
594 : assert( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) );
595 :
596 0 : if ( mnDrawMode & DRAWMODE_BLACKGRADIENT )
597 0 : aColor = Color( COL_BLACK );
598 0 : else if ( mnDrawMode & DRAWMODE_WHITEGRADIENT )
599 0 : aColor = Color( COL_WHITE );
600 0 : else if ( mnDrawMode & DRAWMODE_SETTINGSGRADIENT )
601 0 : aColor = GetSettings().GetStyleSettings().GetWindowColor();
602 :
603 0 : if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT )
604 : {
605 0 : aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80,
606 0 : ( aColor.GetGreen() >> 1 ) | 0x80,
607 0 : ( aColor.GetBlue() >> 1 ) | 0x80 );
608 : }
609 :
610 0 : return aColor;
611 : }
612 :
613 0 : void OutputDevice::SetGrayscaleColors( Gradient &rGradient )
614 : {
615 : // this should only be called with the drawing mode is for grayscale or ghosted gradients
616 : assert ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) );
617 :
618 0 : Color aStartCol( rGradient.GetStartColor() );
619 0 : Color aEndCol( rGradient.GetEndColor() );
620 :
621 0 : if ( mnDrawMode & DRAWMODE_GRAYGRADIENT )
622 : {
623 0 : sal_uInt8 cStartLum = aStartCol.GetLuminance(), cEndLum = aEndCol.GetLuminance();
624 0 : aStartCol = Color( cStartLum, cStartLum, cStartLum );
625 0 : aEndCol = Color( cEndLum, cEndLum, cEndLum );
626 : }
627 :
628 0 : if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT )
629 : {
630 0 : aStartCol = Color( ( aStartCol.GetRed() >> 1 ) | 0x80,
631 0 : ( aStartCol.GetGreen() >> 1 ) | 0x80,
632 0 : ( aStartCol.GetBlue() >> 1 ) | 0x80 );
633 :
634 0 : aEndCol = Color( ( aEndCol.GetRed() >> 1 ) | 0x80,
635 0 : ( aEndCol.GetGreen() >> 1 ) | 0x80,
636 0 : ( aEndCol.GetBlue() >> 1 ) | 0x80 );
637 : }
638 :
639 0 : rGradient.SetStartColor( aStartCol );
640 0 : rGradient.SetEndColor( aEndCol );
641 0 : }
642 :
643 0 : void OutputDevice::AddGradientActions( const Rectangle& rRect, const Gradient& rGradient,
644 : GDIMetaFile& rMtf )
645 : {
646 :
647 0 : Rectangle aRect( rRect );
648 :
649 0 : aRect.Justify();
650 :
651 : // do nothing if the rectangle is empty
652 0 : if ( !aRect.IsEmpty() )
653 : {
654 0 : Gradient aGradient( rGradient );
655 0 : GDIMetaFile* pOldMtf = mpMetaFile;
656 :
657 0 : mpMetaFile = &rMtf;
658 0 : mpMetaFile->AddAction( new MetaPushAction( PushFlags::ALL ) );
659 0 : mpMetaFile->AddAction( new MetaISectRectClipRegionAction( aRect ) );
660 0 : mpMetaFile->AddAction( new MetaLineColorAction( Color(), false ) );
661 :
662 : // because we draw with no border line, we have to expand gradient
663 : // rect to avoid missing lines on the right and bottom edge
664 0 : aRect.Left()--;
665 0 : aRect.Top()--;
666 0 : aRect.Right()++;
667 0 : aRect.Bottom()++;
668 :
669 : // calculate step count if necessary
670 0 : if ( !aGradient.GetSteps() )
671 0 : aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
672 :
673 0 : if( aGradient.GetStyle() == GradientStyle_LINEAR || aGradient.GetStyle() == GradientStyle_AXIAL )
674 0 : DrawLinearGradient( aRect, aGradient, true, NULL );
675 : else
676 0 : DrawComplexGradient( aRect, aGradient, true, NULL );
677 :
678 0 : mpMetaFile->AddAction( new MetaPopAction() );
679 0 : mpMetaFile = pOldMtf;
680 : }
681 1233 : }
682 :
683 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|