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/debug.hxx>
21 : #include <tools/line.hxx>
22 : #include <tools/poly.hxx>
23 :
24 : #include <vcl/gradient.hxx>
25 : #include <vcl/metaact.hxx>
26 : #include <vcl/gdimtf.hxx>
27 : #include <vcl/salbtype.hxx>
28 : #include <vcl/hatch.hxx>
29 : #include <vcl/window.hxx>
30 : #include <vcl/virdev.hxx>
31 : #include <vcl/outdev.hxx>
32 : #include <vcl/settings.hxx>
33 :
34 : #include "pdfwriter_impl.hxx"
35 :
36 : #include "window.h"
37 : #include "salframe.hxx"
38 : #include "salgdi.hxx"
39 : #include "svdata.hxx"
40 : #include "outdata.hxx"
41 :
42 : #include <basegfx/polygon/b2dpolygon.hxx>
43 : #include <basegfx/polygon/b2dpolypolygon.hxx>
44 : #include <basegfx/matrix/b2dhommatrix.hxx>
45 : #include <boost/scoped_array.hpp>
46 : #include <boost/scoped_ptr.hpp>
47 :
48 : #define HATCH_MAXPOINTS 1024
49 : #define GRADIENT_DEFAULT_STEPCOUNT 0
50 :
51 14612 : extern "C" int SAL_CALL ImplHatchCmpFnc( const void* p1, const void* p2 )
52 : {
53 14612 : const long nX1 = ( (Point*) p1 )->X();
54 14612 : const long nX2 = ( (Point*) p2 )->X();
55 14612 : const long nY1 = ( (Point*) p1 )->Y();
56 14612 : const long nY2 = ( (Point*) p2 )->Y();
57 :
58 14612 : return ( nX1 > nX2 ? 1 : nX1 == nX2 ? nY1 > nY2 ? 1: nY1 == nY2 ? 0 : -1 : -1 );
59 : }
60 :
61 1520296 : void OutputDevice::ImplDrawPolygon( const Polygon& rPoly, const PolyPolygon* pClipPolyPoly )
62 : {
63 1520296 : if( pClipPolyPoly )
64 0 : ImplDrawPolyPolygon( rPoly, pClipPolyPoly );
65 : else
66 : {
67 1520296 : sal_uInt16 nPoints = rPoly.GetSize();
68 :
69 1520296 : if ( nPoints < 2 )
70 1520296 : return;
71 :
72 1520296 : const SalPoint* pPtAry = (const SalPoint*)rPoly.GetConstPointAry();
73 1520296 : mpGraphics->DrawPolygon( nPoints, pPtAry, this );
74 : }
75 : }
76 :
77 0 : void OutputDevice::ImplDrawPolyPolygon( const PolyPolygon& rPolyPoly, const PolyPolygon* pClipPolyPoly )
78 : {
79 : PolyPolygon* pPolyPoly;
80 :
81 0 : if( pClipPolyPoly )
82 : {
83 0 : pPolyPoly = new PolyPolygon;
84 0 : rPolyPoly.GetIntersection( *pClipPolyPoly, *pPolyPoly );
85 : }
86 : else
87 0 : pPolyPoly = (PolyPolygon*) &rPolyPoly;
88 :
89 0 : if( pPolyPoly->Count() == 1 )
90 : {
91 0 : const Polygon rPoly = pPolyPoly->GetObject( 0 );
92 0 : sal_uInt16 nSize = rPoly.GetSize();
93 :
94 0 : if( nSize >= 2 )
95 : {
96 0 : const SalPoint* pPtAry = (const SalPoint*)rPoly.GetConstPointAry();
97 0 : mpGraphics->DrawPolygon( nSize, pPtAry, this );
98 0 : }
99 : }
100 0 : else if( pPolyPoly->Count() )
101 : {
102 0 : sal_uInt16 nCount = pPolyPoly->Count();
103 0 : boost::scoped_array<sal_uInt32> pPointAry(new sal_uInt32[nCount]);
104 0 : boost::scoped_array<PCONSTSALPOINT> pPointAryAry(new PCONSTSALPOINT[nCount]);
105 0 : sal_uInt16 i = 0;
106 0 : do
107 : {
108 0 : const Polygon& rPoly = pPolyPoly->GetObject( i );
109 0 : sal_uInt16 nSize = rPoly.GetSize();
110 0 : if ( nSize )
111 : {
112 0 : pPointAry[i] = nSize;
113 0 : pPointAryAry[i] = (PCONSTSALPOINT)rPoly.GetConstPointAry();
114 0 : i++;
115 : }
116 : else
117 0 : nCount--;
118 : }
119 : while( i < nCount );
120 :
121 0 : if( nCount == 1 )
122 0 : mpGraphics->DrawPolygon( pPointAry[0], pPointAryAry[0], this );
123 : else
124 0 : mpGraphics->DrawPolyPolygon( nCount, pPointAry.get(), pPointAryAry.get(), this );
125 : }
126 :
127 0 : if( pClipPolyPoly )
128 0 : delete pPolyPoly;
129 0 : }
130 :
131 4526361 : inline sal_uInt8 ImplGetGradientColorValue( long nValue )
132 : {
133 4526361 : if ( nValue < 0 )
134 0 : return 0;
135 4526361 : else if ( nValue > 0xFF )
136 0 : return 0xFF;
137 : else
138 4526361 : return (sal_uInt8)nValue;
139 : }
140 :
141 75823 : long OutputDevice::ImplGetGradientStepCount( long nMinRect )
142 : {
143 75823 : long nInc = (nMinRect < 50) ? 2 : 4;
144 :
145 75823 : return nInc;
146 : }
147 :
148 75823 : long OutputDevice::ImplGetGradientSteps( const Gradient& rGradient, const Rectangle& rRect, bool bMtf, bool bComplex )
149 : {
150 : // calculate step count
151 75823 : long nStepCount = rGradient.GetSteps();
152 : long nMinRect;
153 :
154 : // generate nStepCount, if not passed
155 75823 : if (bComplex)
156 0 : nMinRect = std::min( rRect.GetWidth(), rRect.GetHeight() );
157 : else
158 75823 : nMinRect = rRect.GetHeight();
159 :
160 75823 : if ( !nStepCount )
161 : {
162 : long nInc;
163 :
164 75823 : nInc = ImplGetGradientStepCount (nMinRect);
165 75823 : if ( !nInc || bMtf )
166 0 : nInc = 1;
167 75823 : nStepCount = nMinRect / nInc;
168 : }
169 :
170 75823 : return nStepCount;
171 : }
172 :
173 75823 : void OutputDevice::ImplDrawLinearGradient( const Rectangle& rRect,
174 : const Gradient& rGradient,
175 : bool bMtf, const PolyPolygon* pClipPolyPoly )
176 : {
177 : // get BoundRect of rotated rectangle
178 75823 : Rectangle aRect;
179 75823 : Point aCenter;
180 75823 : sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
181 :
182 75823 : rGradient.GetBoundRect( rRect, aRect, aCenter );
183 :
184 75823 : bool bLinear = (rGradient.GetStyle() == GradientStyle_LINEAR);
185 75823 : double fBorder = rGradient.GetBorder() * aRect.GetHeight() / 100.0;
186 75823 : if ( !bLinear )
187 : {
188 2 : fBorder /= 2.0;
189 : }
190 75823 : Rectangle aMirrorRect = aRect; // used in style axial
191 75823 : aMirrorRect.Top() = ( aRect.Top() + aRect.Bottom() ) / 2;
192 75823 : if ( !bLinear )
193 : {
194 2 : aRect.Bottom() = aMirrorRect.Top();
195 : }
196 :
197 : // colour-intensities of start- and finish; change if needed
198 : long nFactor;
199 75823 : Color aStartCol = rGradient.GetStartColor();
200 75823 : Color aEndCol = rGradient.GetEndColor();
201 75823 : long nStartRed = aStartCol.GetRed();
202 75823 : long nStartGreen = aStartCol.GetGreen();
203 75823 : long nStartBlue = aStartCol.GetBlue();
204 75823 : long nEndRed = aEndCol.GetRed();
205 75823 : long nEndGreen = aEndCol.GetGreen();
206 75823 : long nEndBlue = aEndCol.GetBlue();
207 75823 : nFactor = rGradient.GetStartIntensity();
208 75823 : nStartRed = (nStartRed * nFactor) / 100;
209 75823 : nStartGreen = (nStartGreen * nFactor) / 100;
210 75823 : nStartBlue = (nStartBlue * nFactor) / 100;
211 75823 : nFactor = rGradient.GetEndIntensity();
212 75823 : nEndRed = (nEndRed * nFactor) / 100;
213 75823 : nEndGreen = (nEndGreen * nFactor) / 100;
214 75823 : nEndBlue = (nEndBlue * nFactor) / 100;
215 :
216 : // gradient style axial has exchanged start and end colors
217 75823 : if ( !bLinear)
218 : {
219 2 : long nTempColor = nStartRed;
220 2 : nStartRed = nEndRed;
221 2 : nEndRed = nTempColor;
222 2 : nTempColor = nStartGreen;
223 2 : nStartGreen = nEndGreen;
224 2 : nEndGreen = nTempColor;
225 2 : nTempColor = nStartBlue;
226 2 : nStartBlue = nEndBlue;
227 2 : nEndBlue = nTempColor;
228 : }
229 :
230 : sal_uInt8 nRed;
231 : sal_uInt8 nGreen;
232 : sal_uInt8 nBlue;
233 :
234 : // Create border
235 75823 : Rectangle aBorderRect = aRect;
236 75823 : Polygon aPoly( 4 );
237 75823 : if (fBorder > 0.0)
238 : {
239 0 : nRed = (sal_uInt8)nStartRed;
240 0 : nGreen = (sal_uInt8)nStartGreen;
241 0 : nBlue = (sal_uInt8)nStartBlue;
242 0 : if ( bMtf )
243 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
244 : else
245 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
246 :
247 0 : aBorderRect.Bottom() = (long)( aBorderRect.Top() + fBorder );
248 0 : aRect.Top() = aBorderRect.Bottom();
249 0 : aPoly[0] = aBorderRect.TopLeft();
250 0 : aPoly[1] = aBorderRect.TopRight();
251 0 : aPoly[2] = aBorderRect.BottomRight();
252 0 : aPoly[3] = aBorderRect.BottomLeft();
253 0 : aPoly.Rotate( aCenter, nAngle );
254 0 : if ( bMtf )
255 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
256 : else
257 0 : ImplDrawPolygon( aPoly, pClipPolyPoly );
258 0 : if ( !bLinear)
259 : {
260 0 : aBorderRect = aMirrorRect;
261 0 : aBorderRect.Top() = (long) ( aBorderRect.Bottom() - fBorder );
262 0 : aMirrorRect.Bottom() = aBorderRect.Top();
263 0 : aPoly[0] = aBorderRect.TopLeft();
264 0 : aPoly[1] = aBorderRect.TopRight();
265 0 : aPoly[2] = aBorderRect.BottomRight();
266 0 : aPoly[3] = aBorderRect.BottomLeft();
267 0 : aPoly.Rotate( aCenter, nAngle );
268 0 : if ( bMtf )
269 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
270 : else
271 0 : ImplDrawPolygon( aPoly, pClipPolyPoly );
272 : }
273 : }
274 :
275 : // calculate step count
276 75823 : long nStepCount = ImplGetGradientSteps( rGradient, aRect, bMtf );
277 :
278 : // minimal three steps and maximal as max color steps
279 75823 : long nAbsRedSteps = std::abs( nEndRed - nStartRed );
280 75823 : long nAbsGreenSteps = std::abs( nEndGreen - nStartGreen );
281 75823 : long nAbsBlueSteps = std::abs( nEndBlue - nStartBlue );
282 75823 : long nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps );
283 75823 : nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps );
284 75823 : long nSteps = std::min( nStepCount, nMaxColorSteps );
285 75823 : if ( nSteps < 3)
286 : {
287 22 : nSteps = 3;
288 : }
289 :
290 75823 : double fScanInc = ((double)aRect.GetHeight()) / (double) nSteps;
291 75823 : double fGradientLine = (double)aRect.Top();
292 75823 : double fMirrorGradientLine = (double) aMirrorRect.Bottom();
293 :
294 75823 : double fAlpha = 0.0;
295 75823 : const double fStepsMinus1 = ((double)nSteps) - 1.0;
296 : double fTempColor;
297 75823 : if ( !bLinear)
298 : {
299 2 : nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap
300 : }
301 1584608 : for ( long i = 0; i < nSteps; i++ )
302 : {
303 : // linear interpolation of color
304 1508785 : fAlpha = ((double)i) / fStepsMinus1;
305 1508785 : fTempColor = ((double)nStartRed) * (1.0-fAlpha) + ((double)nEndRed) * fAlpha;
306 1508785 : nRed = ImplGetGradientColorValue((long)fTempColor);
307 1508785 : fTempColor = ((double)nStartGreen) * (1.0-fAlpha) + ((double)nEndGreen) * fAlpha;
308 1508785 : nGreen = ImplGetGradientColorValue((long)fTempColor);
309 1508785 : fTempColor = ((double)nStartBlue) * (1.0-fAlpha) + ((double)nEndBlue) * fAlpha;
310 1508785 : nBlue = ImplGetGradientColorValue((long)fTempColor);
311 1508785 : if ( bMtf )
312 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
313 : else
314 1508785 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
315 :
316 : // Polygon for this color step
317 1508785 : aRect.Top() = (long)( fGradientLine + ((double) i) * fScanInc );
318 1508785 : aRect.Bottom() = (long)( fGradientLine + ( ((double) i) + 1.0 ) * fScanInc + fScanInc*.1 );
319 1508785 : aPoly[0] = aRect.TopLeft();
320 1508785 : aPoly[1] = aRect.TopRight();
321 1508785 : aPoly[2] = aRect.BottomRight();
322 1508785 : aPoly[3] = aRect.BottomLeft();
323 1508785 : aPoly.Rotate( aCenter, nAngle );
324 1508785 : if ( bMtf )
325 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
326 : else
327 1508785 : ImplDrawPolygon( aPoly, pClipPolyPoly );
328 1508785 : if ( !bLinear )
329 : {
330 39 : aMirrorRect.Bottom() = (long)( fMirrorGradientLine - ((double) i) * fScanInc );
331 39 : aMirrorRect.Top() = (long)( fMirrorGradientLine - (((double) i) + 1.0)* fScanInc );
332 39 : aPoly[0] = aMirrorRect.TopLeft();
333 39 : aPoly[1] = aMirrorRect.TopRight();
334 39 : aPoly[2] = aMirrorRect.BottomRight();
335 39 : aPoly[3] = aMirrorRect.BottomLeft();
336 39 : aPoly.Rotate( aCenter, nAngle );
337 39 : if ( bMtf )
338 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
339 : else
340 39 : ImplDrawPolygon( aPoly, pClipPolyPoly );
341 : }
342 : }
343 75823 : if ( !bLinear)
344 : {
345 : // draw middle polygon with end color
346 2 : nRed = ImplGetGradientColorValue(nEndRed);
347 2 : nGreen = ImplGetGradientColorValue(nEndGreen);
348 2 : nBlue = ImplGetGradientColorValue(nEndBlue);
349 2 : if ( bMtf )
350 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
351 : else
352 2 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
353 :
354 2 : aRect.Top() = (long)( fGradientLine + ((double)nSteps) * fScanInc );
355 2 : aRect.Bottom() = (long)( fMirrorGradientLine - ((double) nSteps) * fScanInc );
356 2 : aPoly[0] = aRect.TopLeft();
357 2 : aPoly[1] = aRect.TopRight();
358 2 : aPoly[2] = aRect.BottomRight();
359 2 : aPoly[3] = aRect.BottomLeft();
360 2 : aPoly.Rotate( aCenter, nAngle );
361 2 : if ( bMtf )
362 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
363 : else
364 2 : ImplDrawPolygon( aPoly, pClipPolyPoly );
365 75823 : }
366 75823 : }
367 :
368 0 : void OutputDevice::ImplDrawComplexGradient( const Rectangle& rRect,
369 : const Gradient& rGradient,
370 : bool bMtf, const PolyPolygon* pClipPolyPoly )
371 : {
372 : // Determine if we output via Polygon or PolyPolygon
373 : // For all rasteroperations other then Overpaint always use PolyPolygon,
374 : // as we will get wrong results if we output multiple times on top of each other.
375 : // Also for printers always use PolyPolygon, as not all printers
376 : // can print polygons on top of each other.
377 :
378 0 : boost::scoped_ptr<PolyPolygon> pPolyPoly;
379 0 : Rectangle aRect;
380 0 : Point aCenter;
381 0 : Color aStartCol( rGradient.GetStartColor() );
382 0 : Color aEndCol( rGradient.GetEndColor() );
383 0 : long nStartRed = ( (long) aStartCol.GetRed() * rGradient.GetStartIntensity() ) / 100;
384 0 : long nStartGreen = ( (long) aStartCol.GetGreen() * rGradient.GetStartIntensity() ) / 100;
385 0 : long nStartBlue = ( (long) aStartCol.GetBlue() * rGradient.GetStartIntensity() ) / 100;
386 0 : long nEndRed = ( (long) aEndCol.GetRed() * rGradient.GetEndIntensity() ) / 100;
387 0 : long nEndGreen = ( (long) aEndCol.GetGreen() * rGradient.GetEndIntensity() ) / 100;
388 0 : long nEndBlue = ( (long) aEndCol.GetBlue() * rGradient.GetEndIntensity() ) / 100;
389 0 : long nRedSteps = nEndRed - nStartRed;
390 0 : long nGreenSteps = nEndGreen - nStartGreen;
391 0 : long nBlueSteps = nEndBlue - nStartBlue;
392 0 : sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
393 :
394 0 : rGradient.GetBoundRect( rRect, aRect, aCenter );
395 :
396 0 : if ( UsePolyPolygonForComplexGradient() || bMtf )
397 0 : pPolyPoly.reset(new PolyPolygon( 2 ));
398 :
399 : // last parameter - true if complex gradient, false if linear
400 0 : long nStepCount = ImplGetGradientSteps( rGradient, rRect, bMtf, true );
401 :
402 : // at least three steps and at most the number of colour differences
403 0 : long nSteps = std::max( nStepCount, 2L );
404 0 : long nCalcSteps = std::abs( nRedSteps );
405 0 : long nTempSteps = std::abs( nGreenSteps );
406 0 : if ( nTempSteps > nCalcSteps )
407 0 : nCalcSteps = nTempSteps;
408 0 : nTempSteps = std::abs( nBlueSteps );
409 0 : if ( nTempSteps > nCalcSteps )
410 0 : nCalcSteps = nTempSteps;
411 0 : if ( nCalcSteps < nSteps )
412 0 : nSteps = nCalcSteps;
413 0 : if ( !nSteps )
414 0 : nSteps = 1;
415 :
416 : // determine output limits and stepsizes for all directions
417 0 : Polygon aPoly;
418 0 : double fScanLeft = aRect.Left();
419 0 : double fScanTop = aRect.Top();
420 0 : double fScanRight = aRect.Right();
421 0 : double fScanBottom = aRect.Bottom();
422 0 : double fScanIncX = (double) aRect.GetWidth() / (double) nSteps * 0.5;
423 0 : double fScanIncY = (double) aRect.GetHeight() / (double) nSteps * 0.5;
424 :
425 : // all gradients are rendered as nested rectangles which shrink
426 : // equally in each dimension - except for 'square' gradients
427 : // which shrink to a central vertex but are not per-se square.
428 0 : if( rGradient.GetStyle() != GradientStyle_SQUARE )
429 : {
430 0 : fScanIncY = std::min( fScanIncY, fScanIncX );
431 0 : fScanIncX = fScanIncY;
432 : }
433 0 : sal_uInt8 nRed = (sal_uInt8) nStartRed, nGreen = (sal_uInt8) nStartGreen, nBlue = (sal_uInt8) nStartBlue;
434 0 : bool bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output
435 :
436 0 : if( bMtf )
437 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
438 : else
439 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
440 :
441 0 : if( pPolyPoly )
442 : {
443 0 : pPolyPoly->Insert( aPoly = rRect );
444 0 : pPolyPoly->Insert( aPoly );
445 : }
446 : else
447 : {
448 : // extend rect, to avoid missing bounding line
449 0 : Rectangle aExtRect( rRect );
450 :
451 0 : aExtRect.Left() -= 1;
452 0 : aExtRect.Top() -= 1;
453 0 : aExtRect.Right() += 1;
454 0 : aExtRect.Bottom() += 1;
455 :
456 0 : ImplDrawPolygon( aPoly = aExtRect, pClipPolyPoly );
457 : }
458 :
459 : // loop to output Polygone/PolyPolygone sequentially
460 0 : for( long i = 1; i < nSteps; i++ )
461 : {
462 : // calculate new Polygon
463 0 : aRect.Left() = (long)( fScanLeft += fScanIncX );
464 0 : aRect.Top() = (long)( fScanTop += fScanIncY );
465 0 : aRect.Right() = (long)( fScanRight -= fScanIncX );
466 0 : aRect.Bottom() = (long)( fScanBottom -= fScanIncY );
467 :
468 0 : if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
469 0 : break;
470 :
471 0 : if( rGradient.GetStyle() == GradientStyle_RADIAL || rGradient.GetStyle() == GradientStyle_ELLIPTICAL )
472 0 : aPoly = Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
473 : else
474 0 : aPoly = Polygon( aRect );
475 :
476 0 : aPoly.Rotate( aCenter, nAngle );
477 :
478 : // adapt colour accordingly
479 0 : const long nStepIndex = ( ( pPolyPoly ) ? i : ( i + 1 ) );
480 0 : nRed = ImplGetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) );
481 0 : nGreen = ImplGetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) );
482 0 : nBlue = ImplGetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) );
483 :
484 : // either slow PolyPolygon output or fast Polygon-Paiting
485 0 : if( pPolyPoly )
486 : {
487 0 : bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output
488 :
489 0 : pPolyPoly->Replace( pPolyPoly->GetObject( 1 ), 0 );
490 0 : pPolyPoly->Replace( aPoly, 1 );
491 :
492 0 : if( bMtf )
493 0 : mpMetaFile->AddAction( new MetaPolyPolygonAction( *pPolyPoly ) );
494 : else
495 0 : ImplDrawPolyPolygon( *pPolyPoly, pClipPolyPoly );
496 :
497 : // #107349# Set fill color _after_ geometry painting:
498 : // pPolyPoly's geometry is the band from last iteration's
499 : // aPoly to current iteration's aPoly. The window outdev
500 : // path (see else below), on the other hand, paints the
501 : // full aPoly. Thus, here, we're painting the band before
502 : // the one painted in the window outdev path below. To get
503 : // matching colors, have to delay color setting here.
504 0 : if( bMtf )
505 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
506 : else
507 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
508 : }
509 : else
510 : {
511 : // #107349# Set fill color _before_ geometry painting
512 0 : if( bMtf )
513 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
514 : else
515 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
516 :
517 0 : ImplDrawPolygon( aPoly, pClipPolyPoly );
518 : }
519 : }
520 :
521 : // we should draw last inner Polygon if we output PolyPolygon
522 0 : if( pPolyPoly )
523 : {
524 0 : const Polygon& rPoly = pPolyPoly->GetObject( 1 );
525 :
526 0 : if( !rPoly.GetBoundRect().IsEmpty() )
527 : {
528 : // #107349# Paint last polygon with end color only if loop
529 : // has generated output. Otherwise, the current
530 : // (i.e. start) color is taken, to generate _any_ output.
531 0 : if( bPaintLastPolygon )
532 : {
533 0 : nRed = ImplGetGradientColorValue( nEndRed );
534 0 : nGreen = ImplGetGradientColorValue( nEndGreen );
535 0 : nBlue = ImplGetGradientColorValue( nEndBlue );
536 : }
537 :
538 0 : if( bMtf )
539 : {
540 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
541 0 : mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) );
542 : }
543 : else
544 : {
545 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
546 0 : ImplDrawPolygon( rPoly, pClipPolyPoly );
547 : }
548 : }
549 0 : }
550 0 : }
551 :
552 0 : Color OutputDevice::GetSingleColorGradientFill()
553 : {
554 0 : Color aColor;
555 :
556 : // we should never call on this function if any of these aren't set!
557 : assert( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) );
558 :
559 0 : if ( mnDrawMode & DRAWMODE_BLACKGRADIENT )
560 0 : aColor = Color( COL_BLACK );
561 0 : else if ( mnDrawMode & DRAWMODE_WHITEGRADIENT )
562 0 : aColor = Color( COL_WHITE );
563 0 : else if ( mnDrawMode & DRAWMODE_SETTINGSGRADIENT )
564 0 : aColor = GetSettings().GetStyleSettings().GetWindowColor();
565 :
566 0 : if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT )
567 : {
568 0 : aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80,
569 0 : ( aColor.GetGreen() >> 1 ) | 0x80,
570 0 : ( aColor.GetBlue() >> 1 ) | 0x80 );
571 : }
572 :
573 0 : return aColor;
574 : }
575 :
576 0 : void OutputDevice::SetGrayscaleColors( Gradient &rGradient )
577 : {
578 : // this should only be called with the drawing mode is for grayscale or ghosted gradients
579 : assert ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) );
580 :
581 0 : Color aStartCol( rGradient.GetStartColor() );
582 0 : Color aEndCol( rGradient.GetEndColor() );
583 :
584 0 : if ( mnDrawMode & DRAWMODE_GRAYGRADIENT )
585 : {
586 0 : sal_uInt8 cStartLum = aStartCol.GetLuminance(), cEndLum = aEndCol.GetLuminance();
587 0 : aStartCol = Color( cStartLum, cStartLum, cStartLum );
588 0 : aEndCol = Color( cEndLum, cEndLum, cEndLum );
589 : }
590 :
591 0 : if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT )
592 : {
593 0 : aStartCol = Color( ( aStartCol.GetRed() >> 1 ) | 0x80,
594 0 : ( aStartCol.GetGreen() >> 1 ) | 0x80,
595 0 : ( aStartCol.GetBlue() >> 1 ) | 0x80 );
596 :
597 0 : aEndCol = Color( ( aEndCol.GetRed() >> 1 ) | 0x80,
598 0 : ( aEndCol.GetGreen() >> 1 ) | 0x80,
599 0 : ( aEndCol.GetBlue() >> 1 ) | 0x80 );
600 : }
601 :
602 0 : rGradient.SetStartColor( aStartCol );
603 0 : rGradient.SetEndColor( aEndCol );
604 0 : }
605 :
606 167667 : void OutputDevice::DrawGradient( const Rectangle& rRect,
607 : const Gradient& rGradient )
608 : {
609 167667 : if ( mnDrawMode & DRAWMODE_NOGRADIENT )
610 0 : return; // nothing to draw!
611 :
612 167667 : if ( mbInitClipRegion )
613 156858 : ImplInitClipRegion();
614 :
615 167667 : if ( mbOutputClipped )
616 21566 : return;
617 :
618 146101 : if ( !rRect.IsEmpty() )
619 : {
620 146101 : if ( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) )
621 : {
622 0 : Color aColor = GetSingleColorGradientFill();
623 :
624 0 : Push( PUSH_LINECOLOR | PUSH_FILLCOLOR );
625 0 : SetLineColor( aColor );
626 0 : SetFillColor( aColor );
627 0 : DrawRect( rRect );
628 0 : Pop();
629 0 : return;
630 : }
631 :
632 146101 : Gradient aGradient( rGradient );
633 :
634 146101 : if ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) )
635 : {
636 0 : SetGrayscaleColors( aGradient );
637 : }
638 :
639 146101 : if( mpMetaFile )
640 4 : mpMetaFile->AddAction( new MetaGradientAction( rRect, aGradient ) );
641 :
642 146101 : if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
643 4 : return;
644 :
645 146097 : if ( !Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
646 : {
647 : // convert rectangle to pixels
648 146097 : Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
649 146097 : aRect.Justify();
650 :
651 : // do nothing if the rectangle is empty
652 146097 : if ( !aRect.IsEmpty() )
653 : {
654 : // we need a graphics
655 146097 : if ( !mpGraphics && !ImplGetGraphics() )
656 0 : return;
657 :
658 : // secure clip region
659 146097 : Push( PUSH_CLIPREGION );
660 146097 : IntersectClipRegion( rRect );
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 146097 : aRect.Left()--;
665 146097 : aRect.Top()--;
666 146097 : aRect.Right()++;
667 146097 : aRect.Bottom()++;
668 :
669 146097 : if ( mbInitClipRegion )
670 146097 : ImplInitClipRegion();
671 :
672 146097 : if ( !mbOutputClipped )
673 : {
674 : // gradients are drawn without border
675 75823 : if ( mbLineColor || mbInitLineColor )
676 : {
677 75808 : mpGraphics->SetLineColor();
678 75808 : mbInitLineColor = true;
679 : }
680 :
681 75823 : mbInitFillColor = true;
682 :
683 : // calculate step count if necessary
684 75823 : if ( !aGradient.GetSteps() )
685 75823 : aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
686 :
687 75823 : if( aGradient.GetStyle() == GradientStyle_LINEAR || aGradient.GetStyle() == GradientStyle_AXIAL )
688 75823 : ImplDrawLinearGradient( aRect, aGradient, false, NULL );
689 : else
690 0 : ImplDrawComplexGradient( aRect, aGradient, false, NULL );
691 : }
692 :
693 146097 : Pop();
694 : }
695 146097 : }
696 : }
697 :
698 146097 : if( mpAlphaVDev )
699 : {
700 : // #i32109#: Make gradient area opaque
701 0 : mpAlphaVDev->ImplFillOpaqueRectangle( rRect );
702 : }
703 : }
704 :
705 2 : void OutputDevice::ClipAndDrawGradientMetafile ( const Gradient &rGradient, const PolyPolygon &rPolyPoly )
706 : {
707 2 : const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
708 2 : const bool bOldOutput = IsOutputEnabled();
709 :
710 2 : EnableOutput( false );
711 2 : Push( PUSH_RASTEROP );
712 2 : SetRasterOp( ROP_XOR );
713 2 : DrawGradient( aBoundRect, rGradient );
714 2 : SetFillColor( COL_BLACK );
715 2 : SetRasterOp( ROP_0 );
716 2 : DrawPolyPolygon( rPolyPoly );
717 2 : SetRasterOp( ROP_XOR );
718 2 : DrawGradient( aBoundRect, rGradient );
719 2 : Pop();
720 2 : EnableOutput( bOldOutput );
721 2 : }
722 :
723 0 : void OutputDevice::ClipAndDrawGradientToBounds ( Gradient &rGradient, const PolyPolygon &rPolyPoly )
724 : {
725 0 : ClipAndDrawGradient ( rGradient, rPolyPoly );
726 0 : }
727 :
728 0 : void OutputDevice::ClipAndDrawGradient ( Gradient &rGradient, const PolyPolygon &rPolyPoly )
729 : {
730 0 : const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
731 :
732 0 : if( !Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
733 : {
734 : // convert rectangle to pixels
735 0 : Rectangle aRect( ImplLogicToDevicePixel( aBoundRect ) );
736 0 : aRect.Justify();
737 :
738 : // do nothing if the rectangle is empty
739 0 : if ( !aRect.IsEmpty() )
740 : {
741 0 : if( !mpGraphics && !ImplGetGraphics() )
742 0 : return;
743 :
744 0 : if( mbInitClipRegion )
745 0 : ImplInitClipRegion();
746 :
747 0 : if( !mbOutputClipped )
748 : {
749 0 : PolyPolygon aClipPolyPoly( ImplLogicToDevicePixel( rPolyPoly ) );
750 :
751 : // draw gradients without border
752 0 : if( mbLineColor || mbInitLineColor )
753 : {
754 0 : mpGraphics->SetLineColor();
755 0 : mbInitLineColor = true;
756 : }
757 :
758 0 : mbInitFillColor = true;
759 :
760 : // calculate step count if necessary
761 0 : if ( !rGradient.GetSteps() )
762 0 : rGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
763 :
764 0 : if( rGradient.GetStyle() == GradientStyle_LINEAR || rGradient.GetStyle() == GradientStyle_AXIAL )
765 0 : ImplDrawLinearGradient( aRect, rGradient, false, &aClipPolyPoly );
766 : else
767 0 : ImplDrawComplexGradient( aRect, rGradient, false, &aClipPolyPoly );
768 : }
769 : }
770 : }
771 : }
772 :
773 4 : void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly,
774 : const Gradient& rGradient )
775 : {
776 4 : if ( mnDrawMode & DRAWMODE_NOGRADIENT )
777 0 : return; // nothing to draw!
778 :
779 4 : if ( mbInitClipRegion )
780 2 : ImplInitClipRegion();
781 :
782 4 : if ( mbOutputClipped )
783 0 : return;
784 :
785 4 : if ( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() )
786 : {
787 4 : if ( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) )
788 : {
789 0 : Color aColor = GetSingleColorGradientFill();
790 :
791 0 : Push( PUSH_LINECOLOR | PUSH_FILLCOLOR );
792 0 : SetLineColor( aColor );
793 0 : SetFillColor( aColor );
794 0 : DrawPolyPolygon( rPolyPoly );
795 0 : Pop();
796 0 : return;
797 : }
798 :
799 4 : Gradient aGradient( rGradient );
800 :
801 4 : if ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) )
802 : {
803 0 : SetGrayscaleColors( aGradient );
804 : }
805 :
806 4 : if( mpMetaFile )
807 : {
808 4 : if ( rPolyPoly.IsRect() )
809 : {
810 2 : const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
811 2 : mpMetaFile->AddAction( new MetaGradientAction( aBoundRect, aGradient ) );
812 : }
813 : else
814 : {
815 2 : mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN" ) );
816 2 : mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) );
817 :
818 2 : ClipAndDrawGradientMetafile ( rGradient, rPolyPoly );
819 :
820 2 : mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END" ) );
821 : }
822 : }
823 :
824 4 : if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
825 4 : return;
826 :
827 0 : ClipAndDrawGradientToBounds ( aGradient, rPolyPoly );
828 : }
829 :
830 0 : if( mpAlphaVDev )
831 0 : mpAlphaVDev->DrawPolyPolygon( rPolyPoly );
832 : }
833 :
834 0 : void OutputDevice::AddGradientActions( const Rectangle& rRect, const Gradient& rGradient,
835 : GDIMetaFile& rMtf )
836 : {
837 :
838 0 : Rectangle aRect( rRect );
839 :
840 0 : aRect.Justify();
841 :
842 : // do nothing if the rectangle is empty
843 0 : if ( !aRect.IsEmpty() )
844 : {
845 0 : Gradient aGradient( rGradient );
846 0 : GDIMetaFile* pOldMtf = mpMetaFile;
847 :
848 0 : mpMetaFile = &rMtf;
849 0 : mpMetaFile->AddAction( new MetaPushAction( PUSH_ALL ) );
850 0 : mpMetaFile->AddAction( new MetaISectRectClipRegionAction( aRect ) );
851 0 : mpMetaFile->AddAction( new MetaLineColorAction( Color(), false ) );
852 :
853 : // because we draw with no border line, we have to expand gradient
854 : // rect to avoid missing lines on the right and bottom edge
855 0 : aRect.Left()--;
856 0 : aRect.Top()--;
857 0 : aRect.Right()++;
858 0 : aRect.Bottom()++;
859 :
860 : // calculate step count if necessary
861 0 : if ( !aGradient.GetSteps() )
862 0 : aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
863 :
864 0 : if( aGradient.GetStyle() == GradientStyle_LINEAR || aGradient.GetStyle() == GradientStyle_AXIAL )
865 0 : ImplDrawLinearGradient( aRect, aGradient, true, NULL );
866 : else
867 0 : ImplDrawComplexGradient( aRect, aGradient, true, NULL );
868 :
869 0 : mpMetaFile->AddAction( new MetaPopAction() );
870 0 : mpMetaFile = pOldMtf;
871 : }
872 0 : }
873 :
874 283 : void OutputDevice::DrawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch )
875 : {
876 :
877 283 : Hatch aHatch( rHatch );
878 :
879 283 : if ( mnDrawMode & ( DRAWMODE_BLACKLINE | DRAWMODE_WHITELINE |
880 : DRAWMODE_GRAYLINE | DRAWMODE_GHOSTEDLINE |
881 : DRAWMODE_SETTINGSLINE ) )
882 : {
883 0 : Color aColor( rHatch.GetColor() );
884 :
885 0 : if ( mnDrawMode & DRAWMODE_BLACKLINE )
886 0 : aColor = Color( COL_BLACK );
887 0 : else if ( mnDrawMode & DRAWMODE_WHITELINE )
888 0 : aColor = Color( COL_WHITE );
889 0 : else if ( mnDrawMode & DRAWMODE_GRAYLINE )
890 : {
891 0 : const sal_uInt8 cLum = aColor.GetLuminance();
892 0 : aColor = Color( cLum, cLum, cLum );
893 : }
894 0 : else if( mnDrawMode & DRAWMODE_SETTINGSLINE )
895 : {
896 0 : aColor = GetSettings().GetStyleSettings().GetFontColor();
897 : }
898 :
899 0 : if ( mnDrawMode & DRAWMODE_GHOSTEDLINE )
900 : {
901 0 : aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80,
902 0 : ( aColor.GetGreen() >> 1 ) | 0x80,
903 0 : ( aColor.GetBlue() >> 1 ) | 0x80);
904 : }
905 :
906 0 : aHatch.SetColor( aColor );
907 : }
908 :
909 283 : if( mpMetaFile )
910 15 : mpMetaFile->AddAction( new MetaHatchAction( rPolyPoly, aHatch ) );
911 :
912 283 : if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
913 15 : return;
914 :
915 268 : if( !mpGraphics && !ImplGetGraphics() )
916 0 : return;
917 :
918 268 : if( mbInitClipRegion )
919 0 : ImplInitClipRegion();
920 :
921 268 : if( mbOutputClipped )
922 0 : return;
923 :
924 268 : if( rPolyPoly.Count() )
925 : {
926 268 : PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
927 268 : GDIMetaFile* pOldMetaFile = mpMetaFile;
928 268 : bool bOldMap = mbMap;
929 :
930 268 : aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
931 268 : aHatch.SetDistance( ImplLogicWidthToDevicePixel( aHatch.GetDistance() ) );
932 :
933 268 : mpMetaFile = NULL;
934 268 : EnableMapMode( false );
935 268 : Push( PUSH_LINECOLOR );
936 268 : SetLineColor( aHatch.GetColor() );
937 268 : ImplInitLineColor();
938 268 : ImplDrawHatch( aPolyPoly, aHatch, false );
939 268 : Pop();
940 268 : EnableMapMode( bOldMap );
941 268 : mpMetaFile = pOldMetaFile;
942 : }
943 :
944 268 : if( mpAlphaVDev )
945 0 : mpAlphaVDev->DrawHatch( rPolyPoly, rHatch );
946 : }
947 :
948 14 : void OutputDevice::AddHatchActions( const PolyPolygon& rPolyPoly, const Hatch& rHatch,
949 : GDIMetaFile& rMtf )
950 : {
951 :
952 14 : PolyPolygon aPolyPoly( rPolyPoly );
953 14 : aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME | POLY_OPTIMIZE_CLOSE );
954 :
955 14 : if( aPolyPoly.Count() )
956 : {
957 14 : GDIMetaFile* pOldMtf = mpMetaFile;
958 :
959 14 : mpMetaFile = &rMtf;
960 14 : mpMetaFile->AddAction( new MetaPushAction( PUSH_ALL ) );
961 14 : mpMetaFile->AddAction( new MetaLineColorAction( rHatch.GetColor(), true ) );
962 14 : ImplDrawHatch( aPolyPoly, rHatch, true );
963 14 : mpMetaFile->AddAction( new MetaPopAction() );
964 14 : mpMetaFile = pOldMtf;
965 14 : }
966 14 : }
967 :
968 282 : void OutputDevice::ImplDrawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch, bool bMtf )
969 : {
970 282 : if(rPolyPoly.Count())
971 : {
972 : // #i115630# ImplDrawHatch does not work with beziers included in the polypolygon, take care of that
973 282 : bool bIsCurve(false);
974 :
975 564 : for(sal_uInt16 a(0); !bIsCurve && a < rPolyPoly.Count(); a++)
976 : {
977 282 : if(rPolyPoly[a].HasFlags())
978 : {
979 0 : bIsCurve = true;
980 : }
981 : }
982 :
983 282 : if(bIsCurve)
984 : {
985 : OSL_ENSURE(false, "ImplDrawHatch does *not* support curves, falling back to AdaptiveSubdivide()...");
986 0 : PolyPolygon aPolyPoly;
987 :
988 0 : rPolyPoly.AdaptiveSubdivide(aPolyPoly);
989 0 : ImplDrawHatch(aPolyPoly, rHatch, bMtf);
990 : }
991 : else
992 : {
993 282 : Rectangle aRect( rPolyPoly.GetBoundRect() );
994 282 : const long nLogPixelWidth = ImplDevicePixelToLogicWidth( 1 );
995 282 : const long nWidth = ImplDevicePixelToLogicWidth( std::max( ImplLogicWidthToDevicePixel( rHatch.GetDistance() ), 3L ) );
996 282 : boost::scoped_array<Point> pPtBuffer(new Point[ HATCH_MAXPOINTS ]);
997 282 : Point aPt1, aPt2, aEndPt1;
998 282 : Size aInc;
999 :
1000 : // Single hatch
1001 282 : aRect.Left() -= nLogPixelWidth; aRect.Top() -= nLogPixelWidth; aRect.Right() += nLogPixelWidth; aRect.Bottom() += nLogPixelWidth;
1002 282 : ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle(), aPt1, aPt2, aInc, aEndPt1 );
1003 14699 : do
1004 : {
1005 14699 : ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer.get(), bMtf );
1006 14699 : aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
1007 14699 : aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
1008 : }
1009 14699 : while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
1010 :
1011 282 : if( ( rHatch.GetStyle() == HATCH_DOUBLE ) || ( rHatch.GetStyle() == HATCH_TRIPLE ) )
1012 : {
1013 : // Double hatch
1014 4 : ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 900, aPt1, aPt2, aInc, aEndPt1 );
1015 208 : do
1016 : {
1017 208 : ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer.get(), bMtf );
1018 208 : aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
1019 208 : aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
1020 : }
1021 208 : while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
1022 :
1023 4 : if( rHatch.GetStyle() == HATCH_TRIPLE )
1024 : {
1025 : // Triple hatch
1026 0 : ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 450, aPt1, aPt2, aInc, aEndPt1 );
1027 0 : do
1028 : {
1029 0 : ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer.get(), bMtf );
1030 0 : aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
1031 0 : aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
1032 : }
1033 0 : while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
1034 : }
1035 282 : }
1036 : }
1037 : }
1038 282 : }
1039 :
1040 286 : void OutputDevice::ImplCalcHatchValues( const Rectangle& rRect, long nDist, sal_uInt16 nAngle10,
1041 : Point& rPt1, Point& rPt2, Size& rInc, Point& rEndPt1 )
1042 : {
1043 286 : Point aRef;
1044 286 : long nAngle = nAngle10 % 1800;
1045 286 : long nOffset = 0;
1046 :
1047 286 : if( nAngle > 900 )
1048 4 : nAngle -= 1800;
1049 :
1050 286 : aRef = ( !IsRefPoint() ? rRect.TopLeft() : GetRefPoint() );
1051 :
1052 286 : if( 0 == nAngle )
1053 : {
1054 272 : rInc = Size( 0, nDist );
1055 272 : rPt1 = rRect.TopLeft();
1056 272 : rPt2 = rRect.TopRight();
1057 272 : rEndPt1 = rRect.BottomLeft();
1058 :
1059 272 : if( aRef.Y() <= rRect.Top() )
1060 272 : nOffset = ( ( rRect.Top() - aRef.Y() ) % nDist );
1061 : else
1062 0 : nOffset = ( nDist - ( ( aRef.Y() - rRect.Top() ) % nDist ) );
1063 :
1064 272 : rPt1.Y() -= nOffset;
1065 272 : rPt2.Y() -= nOffset;
1066 : }
1067 14 : else if( 900 == nAngle )
1068 : {
1069 6 : rInc = Size( nDist, 0 );
1070 6 : rPt1 = rRect.TopLeft();
1071 6 : rPt2 = rRect.BottomLeft();
1072 6 : rEndPt1 = rRect.TopRight();
1073 :
1074 6 : if( aRef.X() <= rRect.Left() )
1075 6 : nOffset = ( rRect.Left() - aRef.X() ) % nDist;
1076 : else
1077 0 : nOffset = nDist - ( ( aRef.X() - rRect.Left() ) % nDist );
1078 :
1079 6 : rPt1.X() -= nOffset;
1080 6 : rPt2.X() -= nOffset;
1081 : }
1082 8 : else if( nAngle >= -450 && nAngle <= 450 )
1083 : {
1084 8 : const double fAngle = F_PI1800 * labs( nAngle );
1085 8 : const double fTan = tan( fAngle );
1086 8 : const long nYOff = FRound( ( rRect.Right() - rRect.Left() ) * fTan );
1087 : long nPY;
1088 :
1089 8 : rInc = Size( 0, nDist = FRound( nDist / cos( fAngle ) ) );
1090 :
1091 8 : if( nAngle > 0 )
1092 : {
1093 4 : rPt1 = rRect.TopLeft();
1094 4 : rPt2 = Point( rRect.Right(), rRect.Top() - nYOff );
1095 4 : rEndPt1 = Point( rRect.Left(), rRect.Bottom() + nYOff );
1096 4 : nPY = FRound( aRef.Y() - ( ( rPt1.X() - aRef.X() ) * fTan ) );
1097 : }
1098 : else
1099 : {
1100 4 : rPt1 = rRect.TopRight();
1101 4 : rPt2 = Point( rRect.Left(), rRect.Top() - nYOff );
1102 4 : rEndPt1 = Point( rRect.Right(), rRect.Bottom() + nYOff );
1103 4 : nPY = FRound( aRef.Y() + ( ( rPt1.X() - aRef.X() ) * fTan ) );
1104 : }
1105 :
1106 8 : if( nPY <= rPt1.Y() )
1107 4 : nOffset = ( rPt1.Y() - nPY ) % nDist;
1108 : else
1109 4 : nOffset = nDist - ( ( nPY - rPt1.Y() ) % nDist );
1110 :
1111 8 : rPt1.Y() -= nOffset;
1112 8 : rPt2.Y() -= nOffset;
1113 : }
1114 : else
1115 : {
1116 0 : const double fAngle = F_PI1800 * labs( nAngle );
1117 0 : const double fTan = tan( fAngle );
1118 0 : const long nXOff = FRound( ( rRect.Bottom() - rRect.Top() ) / fTan );
1119 : long nPX;
1120 :
1121 0 : rInc = Size( nDist = FRound( nDist / sin( fAngle ) ), 0 );
1122 :
1123 0 : if( nAngle > 0 )
1124 : {
1125 0 : rPt1 = rRect.TopLeft();
1126 0 : rPt2 = Point( rRect.Left() - nXOff, rRect.Bottom() );
1127 0 : rEndPt1 = Point( rRect.Right() + nXOff, rRect.Top() );
1128 0 : nPX = FRound( aRef.X() - ( ( rPt1.Y() - aRef.Y() ) / fTan ) );
1129 : }
1130 : else
1131 : {
1132 0 : rPt1 = rRect.BottomLeft();
1133 0 : rPt2 = Point( rRect.Left() - nXOff, rRect.Top() );
1134 0 : rEndPt1 = Point( rRect.Right() + nXOff, rRect.Bottom() );
1135 0 : nPX = FRound( aRef.X() + ( ( rPt1.Y() - aRef.Y() ) / fTan ) );
1136 : }
1137 :
1138 0 : if( nPX <= rPt1.X() )
1139 0 : nOffset = ( rPt1.X() - nPX ) % nDist;
1140 : else
1141 0 : nOffset = nDist - ( ( nPX - rPt1.X() ) % nDist );
1142 :
1143 0 : rPt1.X() -= nOffset;
1144 0 : rPt2.X() -= nOffset;
1145 : }
1146 286 : }
1147 :
1148 14907 : void OutputDevice::ImplDrawHatchLine( const Line& rLine, const PolyPolygon& rPolyPoly,
1149 : Point* pPtBuffer, bool bMtf )
1150 : {
1151 : double fX, fY;
1152 14907 : long nAdd, nPCounter = 0;
1153 :
1154 29814 : for( long nPoly = 0, nPolyCount = rPolyPoly.Count(); nPoly < nPolyCount; nPoly++ )
1155 : {
1156 14907 : const Polygon& rPoly = rPolyPoly[ (sal_uInt16) nPoly ];
1157 :
1158 14907 : if( rPoly.GetSize() > 1 )
1159 : {
1160 14907 : Line aCurSegment( rPoly[ 0 ], Point() );
1161 :
1162 75277 : for( long i = 1, nCount = rPoly.GetSize(); i <= nCount; i++ )
1163 : {
1164 60370 : aCurSegment.SetEnd( rPoly[ (sal_uInt16)( i % nCount ) ] );
1165 60370 : nAdd = 0;
1166 :
1167 60370 : if( rLine.Intersection( aCurSegment, fX, fY ) )
1168 : {
1169 57500 : if( ( fabs( fX - aCurSegment.GetStart().X() ) <= 0.0000001 ) &&
1170 28272 : ( fabs( fY - aCurSegment.GetStart().Y() ) <= 0.0000001 ) )
1171 : {
1172 99 : const Line aPrevSegment( rPoly[ (sal_uInt16)( ( i > 1 ) ? ( i - 2 ) : ( nCount - 1 ) ) ], aCurSegment.GetStart() );
1173 99 : const double fPrevDistance = rLine.GetDistance( aPrevSegment.GetStart() );
1174 99 : const double fCurDistance = rLine.GetDistance( aCurSegment.GetEnd() );
1175 :
1176 99 : if( ( fPrevDistance <= 0.0 && fCurDistance > 0.0 ) ||
1177 0 : ( fPrevDistance > 0.0 && fCurDistance < 0.0 ) )
1178 : {
1179 95 : nAdd = 1;
1180 : }
1181 : }
1182 57302 : else if( ( fabs( fX - aCurSegment.GetEnd().X() ) <= 0.0000001 ) &&
1183 28173 : ( fabs( fY - aCurSegment.GetEnd().Y() ) <= 0.0000001 ) )
1184 : {
1185 99 : const Line aNextSegment( aCurSegment.GetEnd(), rPoly[ (sal_uInt16)( ( i + 1 ) % nCount ) ] );
1186 :
1187 198 : if( ( fabs( rLine.GetDistance( aNextSegment.GetEnd() ) ) <= 0.0000001 ) &&
1188 99 : ( rLine.GetDistance( aCurSegment.GetStart() ) > 0.0 ) )
1189 : {
1190 99 : nAdd = 1;
1191 : }
1192 : }
1193 : else
1194 29030 : nAdd = 1;
1195 :
1196 29228 : if( nAdd )
1197 29224 : pPtBuffer[ nPCounter++ ] = Point( FRound( fX ), FRound( fY ) );
1198 : }
1199 :
1200 60370 : aCurSegment.SetStart( aCurSegment.GetEnd() );
1201 : }
1202 : }
1203 : }
1204 :
1205 14907 : if( nPCounter > 1 )
1206 : {
1207 14612 : qsort( pPtBuffer, nPCounter, sizeof( Point ), ImplHatchCmpFnc );
1208 :
1209 14612 : if( nPCounter & 1 )
1210 0 : nPCounter--;
1211 :
1212 14612 : if( bMtf )
1213 : {
1214 1430 : for( long i = 0; i < nPCounter; i += 2 )
1215 715 : mpMetaFile->AddAction( new MetaLineAction( pPtBuffer[ i ], pPtBuffer[ i + 1 ] ) );
1216 : }
1217 : else
1218 : {
1219 27794 : for( long i = 0; i < nPCounter; i += 2 )
1220 : {
1221 13897 : if( mpPDFWriter )
1222 : {
1223 0 : mpPDFWriter->drawLine( pPtBuffer[ i ], pPtBuffer[ i+1 ] );
1224 : }
1225 : else
1226 : {
1227 13897 : const Point aPt1( ImplLogicToDevicePixel( pPtBuffer[ i ] ) );
1228 13897 : const Point aPt2( ImplLogicToDevicePixel( pPtBuffer[ i + 1 ] ) );
1229 13897 : mpGraphics->DrawLine( aPt1.X(), aPt1.Y(), aPt2.X(), aPt2.Y(), this );
1230 : }
1231 : }
1232 : }
1233 : }
1234 15423 : }
1235 :
1236 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|