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 0 : extern "C" int SAL_CALL ImplHatchCmpFnc( const void* p1, const void* p2 )
52 : {
53 0 : const long nX1 = ( (Point*) p1 )->X();
54 0 : const long nX2 = ( (Point*) p2 )->X();
55 0 : const long nY1 = ( (Point*) p1 )->Y();
56 0 : const long nY2 = ( (Point*) p2 )->Y();
57 :
58 0 : return ( nX1 > nX2 ? 1 : nX1 == nX2 ? nY1 > nY2 ? 1: nY1 == nY2 ? 0 : -1 : -1 );
59 : }
60 :
61 0 : void OutputDevice::ImplDrawPolygon( const Polygon& rPoly, const PolyPolygon* pClipPolyPoly )
62 : {
63 0 : if( pClipPolyPoly )
64 0 : ImplDrawPolyPolygon( rPoly, pClipPolyPoly );
65 : else
66 : {
67 0 : sal_uInt16 nPoints = rPoly.GetSize();
68 :
69 0 : if ( nPoints < 2 )
70 0 : return;
71 :
72 0 : const SalPoint* pPtAry = (const SalPoint*)rPoly.GetConstPointAry();
73 0 : 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 0 : inline sal_uInt8 ImplGetGradientColorValue( long nValue )
132 : {
133 0 : if ( nValue < 0 )
134 0 : return 0;
135 0 : else if ( nValue > 0xFF )
136 0 : return 0xFF;
137 : else
138 0 : return (sal_uInt8)nValue;
139 : }
140 :
141 0 : long OutputDevice::ImplGetGradientStepCount( long nMinRect )
142 : {
143 0 : long nInc = (nMinRect < 50) ? 2 : 4;
144 :
145 0 : return nInc;
146 : }
147 :
148 0 : long OutputDevice::ImplGetGradientSteps( const Gradient& rGradient, const Rectangle& rRect, bool bMtf, bool bComplex )
149 : {
150 : // calculate step count
151 0 : long nStepCount = rGradient.GetSteps();
152 : long nMinRect;
153 :
154 : // generate nStepCount, if not passed
155 0 : if (bComplex)
156 0 : nMinRect = std::min( rRect.GetWidth(), rRect.GetHeight() );
157 : else
158 0 : nMinRect = rRect.GetHeight();
159 :
160 0 : if ( !nStepCount )
161 : {
162 : long nInc;
163 :
164 0 : nInc = ImplGetGradientStepCount (nMinRect);
165 0 : if ( !nInc || bMtf )
166 0 : nInc = 1;
167 0 : nStepCount = nMinRect / nInc;
168 : }
169 :
170 0 : return nStepCount;
171 : }
172 :
173 0 : void OutputDevice::ImplDrawLinearGradient( const Rectangle& rRect,
174 : const Gradient& rGradient,
175 : bool bMtf, const PolyPolygon* pClipPolyPoly )
176 : {
177 : // get BoundRect of rotated rectangle
178 0 : Rectangle aRect;
179 0 : Point aCenter;
180 0 : sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
181 :
182 0 : rGradient.GetBoundRect( rRect, aRect, aCenter );
183 :
184 0 : bool bLinear = (rGradient.GetStyle() == GradientStyle_LINEAR);
185 0 : double fBorder = rGradient.GetBorder() * aRect.GetHeight() / 100.0;
186 0 : if ( !bLinear )
187 : {
188 0 : fBorder /= 2.0;
189 : }
190 0 : Rectangle aMirrorRect = aRect; // used in style axial
191 0 : aMirrorRect.Top() = ( aRect.Top() + aRect.Bottom() ) / 2;
192 0 : if ( !bLinear )
193 : {
194 0 : aRect.Bottom() = aMirrorRect.Top();
195 : }
196 :
197 : // colour-intensities of start- and finish; change if needed
198 : long nFactor;
199 0 : Color aStartCol = rGradient.GetStartColor();
200 0 : Color aEndCol = rGradient.GetEndColor();
201 0 : long nStartRed = aStartCol.GetRed();
202 0 : long nStartGreen = aStartCol.GetGreen();
203 0 : long nStartBlue = aStartCol.GetBlue();
204 0 : long nEndRed = aEndCol.GetRed();
205 0 : long nEndGreen = aEndCol.GetGreen();
206 0 : long nEndBlue = aEndCol.GetBlue();
207 0 : nFactor = rGradient.GetStartIntensity();
208 0 : nStartRed = (nStartRed * nFactor) / 100;
209 0 : nStartGreen = (nStartGreen * nFactor) / 100;
210 0 : nStartBlue = (nStartBlue * nFactor) / 100;
211 0 : nFactor = rGradient.GetEndIntensity();
212 0 : nEndRed = (nEndRed * nFactor) / 100;
213 0 : nEndGreen = (nEndGreen * nFactor) / 100;
214 0 : nEndBlue = (nEndBlue * nFactor) / 100;
215 :
216 : // gradient style axial has exchanged start and end colors
217 0 : if ( !bLinear)
218 : {
219 0 : long nTempColor = nStartRed;
220 0 : nStartRed = nEndRed;
221 0 : nEndRed = nTempColor;
222 0 : nTempColor = nStartGreen;
223 0 : nStartGreen = nEndGreen;
224 0 : nEndGreen = nTempColor;
225 0 : nTempColor = nStartBlue;
226 0 : nStartBlue = nEndBlue;
227 0 : nEndBlue = nTempColor;
228 : }
229 :
230 : sal_uInt8 nRed;
231 : sal_uInt8 nGreen;
232 : sal_uInt8 nBlue;
233 :
234 : // Create border
235 0 : Rectangle aBorderRect = aRect;
236 0 : Polygon aPoly( 4 );
237 0 : 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 0 : long nStepCount = ImplGetGradientSteps( rGradient, aRect, bMtf );
277 :
278 : // minimal three steps and maximal as max color steps
279 0 : long nAbsRedSteps = std::abs( nEndRed - nStartRed );
280 0 : long nAbsGreenSteps = std::abs( nEndGreen - nStartGreen );
281 0 : long nAbsBlueSteps = std::abs( nEndBlue - nStartBlue );
282 0 : long nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps );
283 0 : nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps );
284 0 : long nSteps = std::min( nStepCount, nMaxColorSteps );
285 0 : if ( nSteps < 3)
286 : {
287 0 : nSteps = 3;
288 : }
289 :
290 0 : double fScanInc = ((double)aRect.GetHeight()) / (double) nSteps;
291 0 : double fGradientLine = (double)aRect.Top();
292 0 : double fMirrorGradientLine = (double) aMirrorRect.Bottom();
293 :
294 0 : double fAlpha = 0.0;
295 0 : const double fStepsMinus1 = ((double)nSteps) - 1.0;
296 : double fTempColor;
297 0 : if ( !bLinear)
298 : {
299 0 : nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap
300 : }
301 0 : for ( long i = 0; i < nSteps; i++ )
302 : {
303 : // linear interpolation of color
304 0 : fAlpha = ((double)i) / fStepsMinus1;
305 0 : fTempColor = ((double)nStartRed) * (1.0-fAlpha) + ((double)nEndRed) * fAlpha;
306 0 : nRed = ImplGetGradientColorValue((long)fTempColor);
307 0 : fTempColor = ((double)nStartGreen) * (1.0-fAlpha) + ((double)nEndGreen) * fAlpha;
308 0 : nGreen = ImplGetGradientColorValue((long)fTempColor);
309 0 : fTempColor = ((double)nStartBlue) * (1.0-fAlpha) + ((double)nEndBlue) * fAlpha;
310 0 : nBlue = ImplGetGradientColorValue((long)fTempColor);
311 0 : if ( bMtf )
312 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
313 : else
314 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
315 :
316 : // Polygon for this color step
317 0 : aRect.Top() = (long)( fGradientLine + ((double) i) * fScanInc );
318 0 : aRect.Bottom() = (long)( fGradientLine + ( ((double) i) + 1.0 ) * fScanInc + fScanInc*.1 );
319 0 : aPoly[0] = aRect.TopLeft();
320 0 : aPoly[1] = aRect.TopRight();
321 0 : aPoly[2] = aRect.BottomRight();
322 0 : aPoly[3] = aRect.BottomLeft();
323 0 : aPoly.Rotate( aCenter, nAngle );
324 0 : if ( bMtf )
325 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
326 : else
327 0 : ImplDrawPolygon( aPoly, pClipPolyPoly );
328 0 : if ( !bLinear )
329 : {
330 0 : aMirrorRect.Bottom() = (long)( fMirrorGradientLine - ((double) i) * fScanInc );
331 0 : aMirrorRect.Top() = (long)( fMirrorGradientLine - (((double) i) + 1.0)* fScanInc );
332 0 : aPoly[0] = aMirrorRect.TopLeft();
333 0 : aPoly[1] = aMirrorRect.TopRight();
334 0 : aPoly[2] = aMirrorRect.BottomRight();
335 0 : aPoly[3] = aMirrorRect.BottomLeft();
336 0 : aPoly.Rotate( aCenter, nAngle );
337 0 : if ( bMtf )
338 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
339 : else
340 0 : ImplDrawPolygon( aPoly, pClipPolyPoly );
341 : }
342 : }
343 0 : if ( !bLinear)
344 : {
345 : // draw middle polygon with end color
346 0 : nRed = ImplGetGradientColorValue(nEndRed);
347 0 : nGreen = ImplGetGradientColorValue(nEndGreen);
348 0 : nBlue = ImplGetGradientColorValue(nEndBlue);
349 0 : if ( bMtf )
350 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
351 : else
352 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
353 :
354 0 : aRect.Top() = (long)( fGradientLine + ((double)nSteps) * fScanInc );
355 0 : aRect.Bottom() = (long)( fMirrorGradientLine - ((double) nSteps) * fScanInc );
356 0 : aPoly[0] = aRect.TopLeft();
357 0 : aPoly[1] = aRect.TopRight();
358 0 : aPoly[2] = aRect.BottomRight();
359 0 : aPoly[3] = aRect.BottomLeft();
360 0 : aPoly.Rotate( aCenter, nAngle );
361 0 : if ( bMtf )
362 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
363 : else
364 0 : ImplDrawPolygon( aPoly, pClipPolyPoly );
365 0 : }
366 0 : }
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 0 : void OutputDevice::DrawGradient( const Rectangle& rRect,
607 : const Gradient& rGradient )
608 : {
609 0 : if ( mnDrawMode & DRAWMODE_NOGRADIENT )
610 0 : return; // nothing to draw!
611 :
612 0 : if ( mbInitClipRegion )
613 0 : ImplInitClipRegion();
614 :
615 0 : if ( mbOutputClipped )
616 0 : return;
617 :
618 0 : if ( !rRect.IsEmpty() )
619 : {
620 0 : 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 0 : Gradient aGradient( rGradient );
633 :
634 0 : if ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) )
635 : {
636 0 : SetGrayscaleColors( aGradient );
637 : }
638 :
639 0 : if( mpMetaFile )
640 0 : mpMetaFile->AddAction( new MetaGradientAction( rRect, aGradient ) );
641 :
642 0 : if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
643 0 : return;
644 :
645 0 : if ( !Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
646 : {
647 : // convert rectangle to pixels
648 0 : Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
649 0 : aRect.Justify();
650 :
651 : // do nothing if the rectangle is empty
652 0 : if ( !aRect.IsEmpty() )
653 : {
654 : // we need a graphics
655 0 : if ( !mpGraphics && !ImplGetGraphics() )
656 0 : return;
657 :
658 : // secure clip region
659 0 : Push( PUSH_CLIPREGION );
660 0 : 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 0 : aRect.Left()--;
665 0 : aRect.Top()--;
666 0 : aRect.Right()++;
667 0 : aRect.Bottom()++;
668 :
669 0 : if ( mbInitClipRegion )
670 0 : ImplInitClipRegion();
671 :
672 0 : if ( !mbOutputClipped )
673 : {
674 : // gradients are drawn without border
675 0 : if ( mbLineColor || mbInitLineColor )
676 : {
677 0 : mpGraphics->SetLineColor();
678 0 : mbInitLineColor = true;
679 : }
680 :
681 0 : mbInitFillColor = true;
682 :
683 : // calculate step count if necessary
684 0 : if ( !aGradient.GetSteps() )
685 0 : aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
686 :
687 0 : if( aGradient.GetStyle() == GradientStyle_LINEAR || aGradient.GetStyle() == GradientStyle_AXIAL )
688 0 : ImplDrawLinearGradient( aRect, aGradient, false, NULL );
689 : else
690 0 : ImplDrawComplexGradient( aRect, aGradient, false, NULL );
691 : }
692 :
693 0 : Pop();
694 : }
695 0 : }
696 : }
697 :
698 0 : if( mpAlphaVDev )
699 : {
700 : // #i32109#: Make gradient area opaque
701 0 : mpAlphaVDev->ImplFillOpaqueRectangle( rRect );
702 : }
703 : }
704 :
705 0 : void OutputDevice::ClipAndDrawGradientMetafile ( const Gradient &rGradient, const PolyPolygon &rPolyPoly )
706 : {
707 0 : const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
708 0 : const bool bOldOutput = IsOutputEnabled();
709 :
710 0 : EnableOutput( false );
711 0 : Push( PUSH_RASTEROP );
712 0 : SetRasterOp( ROP_XOR );
713 0 : DrawGradient( aBoundRect, rGradient );
714 0 : SetFillColor( COL_BLACK );
715 0 : SetRasterOp( ROP_0 );
716 0 : DrawPolyPolygon( rPolyPoly );
717 0 : SetRasterOp( ROP_XOR );
718 0 : DrawGradient( aBoundRect, rGradient );
719 0 : Pop();
720 0 : EnableOutput( bOldOutput );
721 0 : }
722 :
723 0 : void OutputDevice::ClipAndDrawGradient ( Gradient &rGradient, const PolyPolygon &rPolyPoly )
724 : {
725 0 : const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
726 :
727 0 : if( !Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
728 : {
729 : // convert rectangle to pixels
730 0 : Rectangle aRect( ImplLogicToDevicePixel( aBoundRect ) );
731 0 : aRect.Justify();
732 :
733 : // do nothing if the rectangle is empty
734 0 : if ( !aRect.IsEmpty() )
735 : {
736 0 : if( !mpGraphics && !ImplGetGraphics() )
737 0 : return;
738 :
739 0 : if( mbInitClipRegion )
740 0 : ImplInitClipRegion();
741 :
742 0 : if( !mbOutputClipped )
743 : {
744 0 : PolyPolygon aClipPolyPoly( ImplLogicToDevicePixel( rPolyPoly ) );
745 :
746 : // draw gradients without border
747 0 : if( mbLineColor || mbInitLineColor )
748 : {
749 0 : mpGraphics->SetLineColor();
750 0 : mbInitLineColor = true;
751 : }
752 :
753 0 : mbInitFillColor = true;
754 :
755 : // calculate step count if necessary
756 0 : if ( !rGradient.GetSteps() )
757 0 : rGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
758 :
759 0 : if( rGradient.GetStyle() == GradientStyle_LINEAR || rGradient.GetStyle() == GradientStyle_AXIAL )
760 0 : ImplDrawLinearGradient( aRect, rGradient, false, &aClipPolyPoly );
761 : else
762 0 : ImplDrawComplexGradient( aRect, rGradient, false, &aClipPolyPoly );
763 : }
764 : }
765 : }
766 : }
767 :
768 0 : void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly,
769 : const Gradient& rGradient )
770 : {
771 0 : if ( mnDrawMode & DRAWMODE_NOGRADIENT )
772 0 : return; // nothing to draw!
773 :
774 0 : if ( mbInitClipRegion )
775 0 : ImplInitClipRegion();
776 :
777 0 : if ( mbOutputClipped )
778 0 : return;
779 :
780 0 : if ( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() )
781 : {
782 0 : if ( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) )
783 : {
784 0 : Color aColor = GetSingleColorGradientFill();
785 :
786 0 : Push( PUSH_LINECOLOR | PUSH_FILLCOLOR );
787 0 : SetLineColor( aColor );
788 0 : SetFillColor( aColor );
789 0 : DrawPolyPolygon( rPolyPoly );
790 0 : Pop();
791 0 : return;
792 : }
793 :
794 0 : Gradient aGradient( rGradient );
795 :
796 0 : if ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) )
797 : {
798 0 : SetGrayscaleColors( aGradient );
799 : }
800 :
801 0 : if( mpMetaFile )
802 : {
803 0 : if ( rPolyPoly.IsRect() )
804 : {
805 0 : const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
806 0 : mpMetaFile->AddAction( new MetaGradientAction( aBoundRect, aGradient ) );
807 : }
808 : else
809 : {
810 0 : mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN" ) );
811 0 : mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) );
812 :
813 0 : ClipAndDrawGradientMetafile ( rGradient, rPolyPoly );
814 :
815 0 : mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END" ) );
816 : }
817 : }
818 :
819 0 : if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
820 0 : return;
821 :
822 0 : ClipAndDrawGradient ( aGradient, rPolyPoly );
823 : }
824 :
825 0 : if( mpAlphaVDev )
826 0 : mpAlphaVDev->DrawPolyPolygon( rPolyPoly );
827 : }
828 :
829 0 : void OutputDevice::AddGradientActions( const Rectangle& rRect, const Gradient& rGradient,
830 : GDIMetaFile& rMtf )
831 : {
832 :
833 0 : Rectangle aRect( rRect );
834 :
835 0 : aRect.Justify();
836 :
837 : // do nothing if the rectangle is empty
838 0 : if ( !aRect.IsEmpty() )
839 : {
840 0 : Gradient aGradient( rGradient );
841 0 : GDIMetaFile* pOldMtf = mpMetaFile;
842 :
843 0 : mpMetaFile = &rMtf;
844 0 : mpMetaFile->AddAction( new MetaPushAction( PUSH_ALL ) );
845 0 : mpMetaFile->AddAction( new MetaISectRectClipRegionAction( aRect ) );
846 0 : mpMetaFile->AddAction( new MetaLineColorAction( Color(), false ) );
847 :
848 : // because we draw with no border line, we have to expand gradient
849 : // rect to avoid missing lines on the right and bottom edge
850 0 : aRect.Left()--;
851 0 : aRect.Top()--;
852 0 : aRect.Right()++;
853 0 : aRect.Bottom()++;
854 :
855 : // calculate step count if necessary
856 0 : if ( !aGradient.GetSteps() )
857 0 : aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
858 :
859 0 : if( aGradient.GetStyle() == GradientStyle_LINEAR || aGradient.GetStyle() == GradientStyle_AXIAL )
860 0 : ImplDrawLinearGradient( aRect, aGradient, true, NULL );
861 : else
862 0 : ImplDrawComplexGradient( aRect, aGradient, true, NULL );
863 :
864 0 : mpMetaFile->AddAction( new MetaPopAction() );
865 0 : mpMetaFile = pOldMtf;
866 : }
867 0 : }
868 :
869 0 : void OutputDevice::DrawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch )
870 : {
871 :
872 0 : Hatch aHatch( rHatch );
873 :
874 0 : if ( mnDrawMode & ( DRAWMODE_BLACKLINE | DRAWMODE_WHITELINE |
875 : DRAWMODE_GRAYLINE | DRAWMODE_GHOSTEDLINE |
876 : DRAWMODE_SETTINGSLINE ) )
877 : {
878 0 : Color aColor( rHatch.GetColor() );
879 :
880 0 : if ( mnDrawMode & DRAWMODE_BLACKLINE )
881 0 : aColor = Color( COL_BLACK );
882 0 : else if ( mnDrawMode & DRAWMODE_WHITELINE )
883 0 : aColor = Color( COL_WHITE );
884 0 : else if ( mnDrawMode & DRAWMODE_GRAYLINE )
885 : {
886 0 : const sal_uInt8 cLum = aColor.GetLuminance();
887 0 : aColor = Color( cLum, cLum, cLum );
888 : }
889 0 : else if( mnDrawMode & DRAWMODE_SETTINGSLINE )
890 : {
891 0 : aColor = GetSettings().GetStyleSettings().GetFontColor();
892 : }
893 :
894 0 : if ( mnDrawMode & DRAWMODE_GHOSTEDLINE )
895 : {
896 0 : aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80,
897 0 : ( aColor.GetGreen() >> 1 ) | 0x80,
898 0 : ( aColor.GetBlue() >> 1 ) | 0x80);
899 : }
900 :
901 0 : aHatch.SetColor( aColor );
902 : }
903 :
904 0 : if( mpMetaFile )
905 0 : mpMetaFile->AddAction( new MetaHatchAction( rPolyPoly, aHatch ) );
906 :
907 0 : if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
908 0 : return;
909 :
910 0 : if( !mpGraphics && !ImplGetGraphics() )
911 0 : return;
912 :
913 0 : if( mbInitClipRegion )
914 0 : ImplInitClipRegion();
915 :
916 0 : if( mbOutputClipped )
917 0 : return;
918 :
919 0 : if( rPolyPoly.Count() )
920 : {
921 0 : PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
922 0 : GDIMetaFile* pOldMetaFile = mpMetaFile;
923 0 : bool bOldMap = mbMap;
924 :
925 0 : aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
926 0 : aHatch.SetDistance( ImplLogicWidthToDevicePixel( aHatch.GetDistance() ) );
927 :
928 0 : mpMetaFile = NULL;
929 0 : EnableMapMode( false );
930 0 : Push( PUSH_LINECOLOR );
931 0 : SetLineColor( aHatch.GetColor() );
932 0 : ImplInitLineColor();
933 0 : ImplDrawHatch( aPolyPoly, aHatch, false );
934 0 : Pop();
935 0 : EnableMapMode( bOldMap );
936 0 : mpMetaFile = pOldMetaFile;
937 : }
938 :
939 0 : if( mpAlphaVDev )
940 0 : mpAlphaVDev->DrawHatch( rPolyPoly, rHatch );
941 : }
942 :
943 0 : void OutputDevice::AddHatchActions( const PolyPolygon& rPolyPoly, const Hatch& rHatch,
944 : GDIMetaFile& rMtf )
945 : {
946 :
947 0 : PolyPolygon aPolyPoly( rPolyPoly );
948 0 : aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME | POLY_OPTIMIZE_CLOSE );
949 :
950 0 : if( aPolyPoly.Count() )
951 : {
952 0 : GDIMetaFile* pOldMtf = mpMetaFile;
953 :
954 0 : mpMetaFile = &rMtf;
955 0 : mpMetaFile->AddAction( new MetaPushAction( PUSH_ALL ) );
956 0 : mpMetaFile->AddAction( new MetaLineColorAction( rHatch.GetColor(), true ) );
957 0 : ImplDrawHatch( aPolyPoly, rHatch, true );
958 0 : mpMetaFile->AddAction( new MetaPopAction() );
959 0 : mpMetaFile = pOldMtf;
960 0 : }
961 0 : }
962 :
963 0 : void OutputDevice::ImplDrawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch, bool bMtf )
964 : {
965 0 : if(rPolyPoly.Count())
966 : {
967 : // #i115630# ImplDrawHatch does not work with beziers included in the polypolygon, take care of that
968 0 : bool bIsCurve(false);
969 :
970 0 : for(sal_uInt16 a(0); !bIsCurve && a < rPolyPoly.Count(); a++)
971 : {
972 0 : if(rPolyPoly[a].HasFlags())
973 : {
974 0 : bIsCurve = true;
975 : }
976 : }
977 :
978 0 : if(bIsCurve)
979 : {
980 : OSL_ENSURE(false, "ImplDrawHatch does *not* support curves, falling back to AdaptiveSubdivide()...");
981 0 : PolyPolygon aPolyPoly;
982 :
983 0 : rPolyPoly.AdaptiveSubdivide(aPolyPoly);
984 0 : ImplDrawHatch(aPolyPoly, rHatch, bMtf);
985 : }
986 : else
987 : {
988 0 : Rectangle aRect( rPolyPoly.GetBoundRect() );
989 0 : const long nLogPixelWidth = ImplDevicePixelToLogicWidth( 1 );
990 0 : const long nWidth = ImplDevicePixelToLogicWidth( std::max( ImplLogicWidthToDevicePixel( rHatch.GetDistance() ), 3L ) );
991 0 : boost::scoped_array<Point> pPtBuffer(new Point[ HATCH_MAXPOINTS ]);
992 0 : Point aPt1, aPt2, aEndPt1;
993 0 : Size aInc;
994 :
995 : // Single hatch
996 0 : aRect.Left() -= nLogPixelWidth; aRect.Top() -= nLogPixelWidth; aRect.Right() += nLogPixelWidth; aRect.Bottom() += nLogPixelWidth;
997 0 : ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle(), aPt1, aPt2, aInc, aEndPt1 );
998 0 : do
999 : {
1000 0 : ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer.get(), bMtf );
1001 0 : aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
1002 0 : aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
1003 : }
1004 0 : while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
1005 :
1006 0 : if( ( rHatch.GetStyle() == HATCH_DOUBLE ) || ( rHatch.GetStyle() == HATCH_TRIPLE ) )
1007 : {
1008 : // Double hatch
1009 0 : ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 900, aPt1, aPt2, aInc, aEndPt1 );
1010 0 : do
1011 : {
1012 0 : ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer.get(), bMtf );
1013 0 : aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
1014 0 : aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
1015 : }
1016 0 : while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
1017 :
1018 0 : if( rHatch.GetStyle() == HATCH_TRIPLE )
1019 : {
1020 : // Triple hatch
1021 0 : ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 450, aPt1, aPt2, aInc, aEndPt1 );
1022 0 : do
1023 : {
1024 0 : ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer.get(), bMtf );
1025 0 : aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
1026 0 : aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
1027 : }
1028 0 : while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
1029 : }
1030 0 : }
1031 : }
1032 : }
1033 0 : }
1034 :
1035 0 : void OutputDevice::ImplCalcHatchValues( const Rectangle& rRect, long nDist, sal_uInt16 nAngle10,
1036 : Point& rPt1, Point& rPt2, Size& rInc, Point& rEndPt1 )
1037 : {
1038 0 : Point aRef;
1039 0 : long nAngle = nAngle10 % 1800;
1040 0 : long nOffset = 0;
1041 :
1042 0 : if( nAngle > 900 )
1043 0 : nAngle -= 1800;
1044 :
1045 0 : aRef = ( !IsRefPoint() ? rRect.TopLeft() : GetRefPoint() );
1046 :
1047 0 : if( 0 == nAngle )
1048 : {
1049 0 : rInc = Size( 0, nDist );
1050 0 : rPt1 = rRect.TopLeft();
1051 0 : rPt2 = rRect.TopRight();
1052 0 : rEndPt1 = rRect.BottomLeft();
1053 :
1054 0 : if( aRef.Y() <= rRect.Top() )
1055 0 : nOffset = ( ( rRect.Top() - aRef.Y() ) % nDist );
1056 : else
1057 0 : nOffset = ( nDist - ( ( aRef.Y() - rRect.Top() ) % nDist ) );
1058 :
1059 0 : rPt1.Y() -= nOffset;
1060 0 : rPt2.Y() -= nOffset;
1061 : }
1062 0 : else if( 900 == nAngle )
1063 : {
1064 0 : rInc = Size( nDist, 0 );
1065 0 : rPt1 = rRect.TopLeft();
1066 0 : rPt2 = rRect.BottomLeft();
1067 0 : rEndPt1 = rRect.TopRight();
1068 :
1069 0 : if( aRef.X() <= rRect.Left() )
1070 0 : nOffset = ( rRect.Left() - aRef.X() ) % nDist;
1071 : else
1072 0 : nOffset = nDist - ( ( aRef.X() - rRect.Left() ) % nDist );
1073 :
1074 0 : rPt1.X() -= nOffset;
1075 0 : rPt2.X() -= nOffset;
1076 : }
1077 0 : else if( nAngle >= -450 && nAngle <= 450 )
1078 : {
1079 0 : const double fAngle = F_PI1800 * labs( nAngle );
1080 0 : const double fTan = tan( fAngle );
1081 0 : const long nYOff = FRound( ( rRect.Right() - rRect.Left() ) * fTan );
1082 : long nPY;
1083 :
1084 0 : rInc = Size( 0, nDist = FRound( nDist / cos( fAngle ) ) );
1085 :
1086 0 : if( nAngle > 0 )
1087 : {
1088 0 : rPt1 = rRect.TopLeft();
1089 0 : rPt2 = Point( rRect.Right(), rRect.Top() - nYOff );
1090 0 : rEndPt1 = Point( rRect.Left(), rRect.Bottom() + nYOff );
1091 0 : nPY = FRound( aRef.Y() - ( ( rPt1.X() - aRef.X() ) * fTan ) );
1092 : }
1093 : else
1094 : {
1095 0 : rPt1 = rRect.TopRight();
1096 0 : rPt2 = Point( rRect.Left(), rRect.Top() - nYOff );
1097 0 : rEndPt1 = Point( rRect.Right(), rRect.Bottom() + nYOff );
1098 0 : nPY = FRound( aRef.Y() + ( ( rPt1.X() - aRef.X() ) * fTan ) );
1099 : }
1100 :
1101 0 : if( nPY <= rPt1.Y() )
1102 0 : nOffset = ( rPt1.Y() - nPY ) % nDist;
1103 : else
1104 0 : nOffset = nDist - ( ( nPY - rPt1.Y() ) % nDist );
1105 :
1106 0 : rPt1.Y() -= nOffset;
1107 0 : rPt2.Y() -= nOffset;
1108 : }
1109 : else
1110 : {
1111 0 : const double fAngle = F_PI1800 * labs( nAngle );
1112 0 : const double fTan = tan( fAngle );
1113 0 : const long nXOff = FRound( ( rRect.Bottom() - rRect.Top() ) / fTan );
1114 : long nPX;
1115 :
1116 0 : rInc = Size( nDist = FRound( nDist / sin( fAngle ) ), 0 );
1117 :
1118 0 : if( nAngle > 0 )
1119 : {
1120 0 : rPt1 = rRect.TopLeft();
1121 0 : rPt2 = Point( rRect.Left() - nXOff, rRect.Bottom() );
1122 0 : rEndPt1 = Point( rRect.Right() + nXOff, rRect.Top() );
1123 0 : nPX = FRound( aRef.X() - ( ( rPt1.Y() - aRef.Y() ) / fTan ) );
1124 : }
1125 : else
1126 : {
1127 0 : rPt1 = rRect.BottomLeft();
1128 0 : rPt2 = Point( rRect.Left() - nXOff, rRect.Top() );
1129 0 : rEndPt1 = Point( rRect.Right() + nXOff, rRect.Bottom() );
1130 0 : nPX = FRound( aRef.X() + ( ( rPt1.Y() - aRef.Y() ) / fTan ) );
1131 : }
1132 :
1133 0 : if( nPX <= rPt1.X() )
1134 0 : nOffset = ( rPt1.X() - nPX ) % nDist;
1135 : else
1136 0 : nOffset = nDist - ( ( nPX - rPt1.X() ) % nDist );
1137 :
1138 0 : rPt1.X() -= nOffset;
1139 0 : rPt2.X() -= nOffset;
1140 : }
1141 0 : }
1142 :
1143 0 : void OutputDevice::ImplDrawHatchLine( const Line& rLine, const PolyPolygon& rPolyPoly,
1144 : Point* pPtBuffer, bool bMtf )
1145 : {
1146 : double fX, fY;
1147 0 : long nAdd, nPCounter = 0;
1148 :
1149 0 : for( long nPoly = 0, nPolyCount = rPolyPoly.Count(); nPoly < nPolyCount; nPoly++ )
1150 : {
1151 0 : const Polygon& rPoly = rPolyPoly[ (sal_uInt16) nPoly ];
1152 :
1153 0 : if( rPoly.GetSize() > 1 )
1154 : {
1155 0 : Line aCurSegment( rPoly[ 0 ], Point() );
1156 :
1157 0 : for( long i = 1, nCount = rPoly.GetSize(); i <= nCount; i++ )
1158 : {
1159 0 : aCurSegment.SetEnd( rPoly[ (sal_uInt16)( i % nCount ) ] );
1160 0 : nAdd = 0;
1161 :
1162 0 : if( rLine.Intersection( aCurSegment, fX, fY ) )
1163 : {
1164 0 : if( ( fabs( fX - aCurSegment.GetStart().X() ) <= 0.0000001 ) &&
1165 0 : ( fabs( fY - aCurSegment.GetStart().Y() ) <= 0.0000001 ) )
1166 : {
1167 0 : const Line aPrevSegment( rPoly[ (sal_uInt16)( ( i > 1 ) ? ( i - 2 ) : ( nCount - 1 ) ) ], aCurSegment.GetStart() );
1168 0 : const double fPrevDistance = rLine.GetDistance( aPrevSegment.GetStart() );
1169 0 : const double fCurDistance = rLine.GetDistance( aCurSegment.GetEnd() );
1170 :
1171 0 : if( ( fPrevDistance <= 0.0 && fCurDistance > 0.0 ) ||
1172 0 : ( fPrevDistance > 0.0 && fCurDistance < 0.0 ) )
1173 : {
1174 0 : nAdd = 1;
1175 : }
1176 : }
1177 0 : else if( ( fabs( fX - aCurSegment.GetEnd().X() ) <= 0.0000001 ) &&
1178 0 : ( fabs( fY - aCurSegment.GetEnd().Y() ) <= 0.0000001 ) )
1179 : {
1180 0 : const Line aNextSegment( aCurSegment.GetEnd(), rPoly[ (sal_uInt16)( ( i + 1 ) % nCount ) ] );
1181 :
1182 0 : if( ( fabs( rLine.GetDistance( aNextSegment.GetEnd() ) ) <= 0.0000001 ) &&
1183 0 : ( rLine.GetDistance( aCurSegment.GetStart() ) > 0.0 ) )
1184 : {
1185 0 : nAdd = 1;
1186 : }
1187 : }
1188 : else
1189 0 : nAdd = 1;
1190 :
1191 0 : if( nAdd )
1192 0 : pPtBuffer[ nPCounter++ ] = Point( FRound( fX ), FRound( fY ) );
1193 : }
1194 :
1195 0 : aCurSegment.SetStart( aCurSegment.GetEnd() );
1196 : }
1197 : }
1198 : }
1199 :
1200 0 : if( nPCounter > 1 )
1201 : {
1202 0 : qsort( pPtBuffer, nPCounter, sizeof( Point ), ImplHatchCmpFnc );
1203 :
1204 0 : if( nPCounter & 1 )
1205 0 : nPCounter--;
1206 :
1207 0 : if( bMtf )
1208 : {
1209 0 : for( long i = 0; i < nPCounter; i += 2 )
1210 0 : mpMetaFile->AddAction( new MetaLineAction( pPtBuffer[ i ], pPtBuffer[ i + 1 ] ) );
1211 : }
1212 : else
1213 : {
1214 0 : for( long i = 0; i < nPCounter; i += 2 )
1215 : {
1216 0 : if( mpPDFWriter )
1217 : {
1218 0 : mpPDFWriter->drawLine( pPtBuffer[ i ], pPtBuffer[ i+1 ] );
1219 : }
1220 : else
1221 : {
1222 0 : const Point aPt1( ImplLogicToDevicePixel( pPtBuffer[ i ] ) );
1223 0 : const Point aPt2( ImplLogicToDevicePixel( pPtBuffer[ i + 1 ] ) );
1224 0 : mpGraphics->DrawLine( aPt1.X(), aPt1.Y(), aPt2.X(), aPt2.Y(), this );
1225 : }
1226 : }
1227 : }
1228 : }
1229 3 : }
1230 :
1231 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|