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 :
21 : #include <tools/debug.hxx>
22 : #include <tools/line.hxx>
23 : #include <tools/poly.hxx>
24 :
25 : #include <vcl/gradient.hxx>
26 : #include <vcl/metaact.hxx>
27 : #include <vcl/gdimtf.hxx>
28 : #include <vcl/salbtype.hxx>
29 : #include <vcl/hatch.hxx>
30 : #include <vcl/window.hxx>
31 : #include <vcl/virdev.hxx>
32 : #include <vcl/outdev.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 :
46 : #define HATCH_MAXPOINTS 1024
47 : #define GRADIENT_DEFAULT_STEPCOUNT 0
48 :
49 12257 : extern "C" int SAL_CALL ImplHatchCmpFnc( const void* p1, const void* p2 )
50 : {
51 12257 : const long nX1 = ( (Point*) p1 )->X();
52 12257 : const long nX2 = ( (Point*) p2 )->X();
53 12257 : const long nY1 = ( (Point*) p1 )->Y();
54 12257 : const long nY2 = ( (Point*) p2 )->Y();
55 :
56 12257 : return ( nX1 > nX2 ? 1 : nX1 == nX2 ? nY1 > nY2 ? 1: nY1 == nY2 ? 0 : -1 : -1 );
57 : }
58 :
59 : DBG_NAMEEX( OutputDevice )
60 : DBG_NAMEEX( Gradient )
61 :
62 1439948 : void OutputDevice::ImplDrawPolygon( const Polygon& rPoly, const PolyPolygon* pClipPolyPoly )
63 : {
64 1439948 : if( pClipPolyPoly )
65 0 : ImplDrawPolyPolygon( rPoly, pClipPolyPoly );
66 : else
67 : {
68 1439948 : sal_uInt16 nPoints = rPoly.GetSize();
69 :
70 1439948 : if ( nPoints < 2 )
71 1439948 : return;
72 :
73 1439948 : const SalPoint* pPtAry = (const SalPoint*)rPoly.GetConstPointAry();
74 1439948 : mpGraphics->DrawPolygon( nPoints, pPtAry, this );
75 : }
76 : }
77 :
78 0 : void OutputDevice::ImplDrawPolyPolygon( const PolyPolygon& rPolyPoly, const PolyPolygon* pClipPolyPoly )
79 : {
80 : PolyPolygon* pPolyPoly;
81 :
82 0 : if( pClipPolyPoly )
83 : {
84 0 : pPolyPoly = new PolyPolygon;
85 0 : rPolyPoly.GetIntersection( *pClipPolyPoly, *pPolyPoly );
86 : }
87 : else
88 0 : pPolyPoly = (PolyPolygon*) &rPolyPoly;
89 :
90 0 : if( pPolyPoly->Count() == 1 )
91 : {
92 0 : const Polygon rPoly = pPolyPoly->GetObject( 0 );
93 0 : sal_uInt16 nSize = rPoly.GetSize();
94 :
95 0 : if( nSize >= 2 )
96 : {
97 0 : const SalPoint* pPtAry = (const SalPoint*)rPoly.GetConstPointAry();
98 0 : mpGraphics->DrawPolygon( nSize, pPtAry, this );
99 0 : }
100 : }
101 0 : else if( pPolyPoly->Count() )
102 : {
103 0 : sal_uInt16 nCount = pPolyPoly->Count();
104 0 : sal_uInt32* pPointAry = new sal_uInt32[nCount];
105 0 : PCONSTSALPOINT* pPointAryAry = new PCONSTSALPOINT[nCount];
106 0 : sal_uInt16 i = 0;
107 0 : do
108 : {
109 0 : const Polygon& rPoly = pPolyPoly->GetObject( i );
110 0 : sal_uInt16 nSize = rPoly.GetSize();
111 0 : if ( nSize )
112 : {
113 0 : pPointAry[i] = nSize;
114 0 : pPointAryAry[i] = (PCONSTSALPOINT)rPoly.GetConstPointAry();
115 0 : i++;
116 : }
117 : else
118 0 : nCount--;
119 : }
120 : while( i < nCount );
121 :
122 0 : if( nCount == 1 )
123 0 : mpGraphics->DrawPolygon( *pPointAry, *pPointAryAry, this );
124 : else
125 0 : mpGraphics->DrawPolyPolygon( nCount, pPointAry, pPointAryAry, this );
126 :
127 0 : delete[] pPointAry;
128 0 : delete[] pPointAryAry;
129 : }
130 :
131 0 : if( pClipPolyPoly )
132 0 : delete pPolyPoly;
133 0 : }
134 :
135 4299198 : inline sal_uInt8 ImplGetGradientColorValue( long nValue )
136 : {
137 4299198 : if ( nValue < 0 )
138 0 : return 0;
139 4299198 : else if ( nValue > 0xFF )
140 0 : return 0xFF;
141 : else
142 4299198 : return (sal_uInt8)nValue;
143 : }
144 :
145 69547 : void OutputDevice::ImplDrawLinearGradient( const Rectangle& rRect,
146 : const Gradient& rGradient,
147 : sal_Bool bMtf, const PolyPolygon* pClipPolyPoly )
148 : {
149 : // rotiertes BoundRect ausrechnen
150 69547 : Rectangle aRect;
151 69547 : Point aCenter;
152 69547 : sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
153 :
154 69547 : rGradient.GetBoundRect( rRect, aRect, aCenter );
155 :
156 : // Rand berechnen und Rechteck neu setzen
157 69547 : Rectangle aFullRect = aRect;
158 69547 : long nBorder = (long)rGradient.GetBorder() * aRect.GetHeight() / 100;
159 :
160 : // Rand berechnen und Rechteck neu setzen fuer linearen Farbverlauf
161 69547 : bool bLinear = (rGradient.GetStyle() == GradientStyle_LINEAR);
162 69547 : if ( bLinear )
163 : {
164 69547 : aRect.Top() += nBorder;
165 : }
166 : // Rand berechnen und Rechteck neu setzen fuer axiale Farbverlauf
167 : else
168 : {
169 0 : nBorder >>= 1;
170 :
171 0 : aRect.Top() += nBorder;
172 0 : aRect.Bottom() -= nBorder;
173 : }
174 :
175 : // Top darf nicht groesser als Bottom sein
176 69547 : aRect.Top() = std::min( aRect.Top(), (long)(aRect.Bottom() - 1) );
177 :
178 69547 : long nMinRect = aRect.GetHeight();
179 :
180 : // Intensitaeten von Start- und Endfarbe ggf. aendern und
181 : // Farbschrittweiten berechnen
182 : long nFactor;
183 69547 : Color aStartCol = rGradient.GetStartColor();
184 69547 : Color aEndCol = rGradient.GetEndColor();
185 69547 : long nStartRed = aStartCol.GetRed();
186 69547 : long nStartGreen = aStartCol.GetGreen();
187 69547 : long nStartBlue = aStartCol.GetBlue();
188 69547 : long nEndRed = aEndCol.GetRed();
189 69547 : long nEndGreen = aEndCol.GetGreen();
190 69547 : long nEndBlue = aEndCol.GetBlue();
191 69547 : nFactor = rGradient.GetStartIntensity();
192 69547 : nStartRed = (nStartRed * nFactor) / 100;
193 69547 : nStartGreen = (nStartGreen * nFactor) / 100;
194 69547 : nStartBlue = (nStartBlue * nFactor) / 100;
195 69547 : nFactor = rGradient.GetEndIntensity();
196 69547 : nEndRed = (nEndRed * nFactor) / 100;
197 69547 : nEndGreen = (nEndGreen * nFactor) / 100;
198 69547 : nEndBlue = (nEndBlue * nFactor) / 100;
199 69547 : long nRedSteps = nEndRed - nStartRed;
200 69547 : long nGreenSteps = nEndGreen - nStartGreen;
201 69547 : long nBlueSteps = nEndBlue - nStartBlue;
202 69547 : long nStepCount = rGradient.GetSteps();
203 :
204 : // Bei nicht linearen Farbverlaeufen haben wir nur die halben Steps
205 : // pro Farbe
206 69547 : if ( !bLinear )
207 : {
208 0 : nRedSteps <<= 1;
209 0 : nGreenSteps <<= 1;
210 0 : nBlueSteps <<= 1;
211 : }
212 :
213 : // Anzahl der Schritte berechnen, falls nichts uebergeben wurde
214 69547 : if ( !nStepCount )
215 : {
216 : long nInc;
217 :
218 69547 : if ( meOutDevType != OUTDEV_PRINTER && !bMtf )
219 : {
220 69547 : nInc = (nMinRect < 50) ? 2 : 4;
221 : }
222 : else
223 : {
224 : // #105998# Use display-equivalent step size calculation
225 0 : nInc = (nMinRect < 800) ? 10 : 20;
226 : }
227 :
228 69547 : nStepCount = nMinRect / nInc;
229 : }
230 : // minimal drei Schritte und maximal die Anzahl der Farbunterschiede
231 69547 : long nSteps = std::max( nStepCount, 2L );
232 69547 : long nCalcSteps = std::abs( nRedSteps );
233 69547 : long nTempSteps = std::abs( nGreenSteps );
234 69547 : if ( nTempSteps > nCalcSteps )
235 0 : nCalcSteps = nTempSteps;
236 69547 : nTempSteps = std::abs( nBlueSteps );
237 69547 : if ( nTempSteps > nCalcSteps )
238 0 : nCalcSteps = nTempSteps;
239 69547 : if ( nCalcSteps < nSteps )
240 2895 : nSteps = nCalcSteps;
241 69547 : if ( !nSteps )
242 22 : nSteps = 1;
243 :
244 : // Falls axialer Farbverlauf, muss die Schrittanzahl ungerade sein
245 69547 : if ( !bLinear && !(nSteps & 1) )
246 0 : nSteps++;
247 :
248 : // Berechnung ueber Double-Addition wegen Genauigkeit
249 69547 : double fScanLine = aRect.Top();
250 69547 : double fScanInc = (double)aRect.GetHeight() / (double)nSteps;
251 :
252 : // Startfarbe berechnen und setzen
253 : sal_uInt8 nRed;
254 : sal_uInt8 nGreen;
255 : sal_uInt8 nBlue;
256 : long nSteps2;
257 69547 : long nStepsHalf = 0;
258 69547 : if ( bLinear )
259 : {
260 : // Um 1 erhoeht, um die Border innerhalb der Schleife
261 : // zeichnen zu koennen
262 69547 : nSteps2 = nSteps + 1;
263 69547 : nRed = (sal_uInt8)nStartRed;
264 69547 : nGreen = (sal_uInt8)nStartGreen;
265 69547 : nBlue = (sal_uInt8)nStartBlue;
266 : }
267 : else
268 : {
269 : // Um 2 erhoeht, um die Border innerhalb der Schleife
270 : // zeichnen zu koennen
271 0 : nSteps2 = nSteps + 2;
272 0 : nRed = (sal_uInt8)nEndRed;
273 0 : nGreen = (sal_uInt8)nEndGreen;
274 0 : nBlue = (sal_uInt8)nEndBlue;
275 0 : nStepsHalf = nSteps >> 1;
276 : }
277 :
278 69547 : if ( bMtf )
279 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), sal_True ) );
280 : else
281 69547 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
282 :
283 : // Startpolygon erzeugen (== Borderpolygon)
284 69547 : Polygon aPoly( 4 );
285 139094 : Polygon aTempPoly( 2 );
286 139094 : Polygon aTempPoly2( 2 );
287 : /* n#710061 Use overlapping fills to avoid color
288 : * leak via gaps in some pdf viewers
289 : */
290 69547 : Point aOverLap( 0, fScanInc*.1 );
291 69547 : aPoly[0] = aFullRect.TopLeft();
292 69547 : aPoly[1] = aFullRect.TopRight();
293 69547 : aPoly[2] = aRect.TopRight();
294 69547 : aPoly[3] = aRect.TopLeft();
295 69547 : aPoly.Rotate( aCenter, nAngle );
296 69547 : aTempPoly[0] = aPoly[3];
297 69547 : aTempPoly[1] = aPoly[2];
298 :
299 :
300 : // Schleife, um rotierten Verlauf zu fuellen
301 1502613 : for ( long i = 0; i < nSteps2; i++ )
302 : {
303 : // berechnetesPolygon ausgeben
304 1433066 : if ( bMtf )
305 0 : mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
306 : else
307 1433066 : ImplDrawPolygon( aPoly, pClipPolyPoly );
308 :
309 : // neues Polygon berechnen
310 1433066 : aRect.Top() = (long)(fScanLine += fScanInc);
311 :
312 1433066 : aPoly[0] = aTempPoly[0];
313 1433066 : aPoly[1] = aTempPoly[1];
314 : // unteren Rand komplett fuellen
315 1433066 : if ( i == nSteps )
316 : {
317 69547 : aTempPoly[0] = aFullRect.BottomLeft();
318 69547 : aTempPoly[1] = aFullRect.BottomRight();
319 69547 : aTempPoly2 = aTempPoly;
320 : }
321 : else
322 : {
323 1363519 : aTempPoly[0] = aRect.TopLeft();
324 1363519 : aTempPoly[1] = aRect.TopRight();
325 1363519 : aTempPoly2[0]= aTempPoly[0] + aOverLap;
326 1363519 : aTempPoly2[1]= aTempPoly[1] + aOverLap;
327 : }
328 1433066 : aTempPoly2.Rotate( aCenter, nAngle );
329 1433066 : aTempPoly.Rotate( aCenter, nAngle );
330 :
331 1433066 : aPoly[2] = aTempPoly2[1];
332 1433066 : aPoly[3] = aTempPoly2[0];
333 :
334 : // Farbintensitaeten aendern...
335 : // fuer lineare FV
336 1433066 : if ( bLinear )
337 : {
338 1433066 : nRed = ImplGetGradientColorValue( nStartRed+((nRedSteps*i)/nSteps2) );
339 1433066 : nGreen = ImplGetGradientColorValue( nStartGreen+((nGreenSteps*i)/nSteps2) );
340 1433066 : nBlue = ImplGetGradientColorValue( nStartBlue+((nBlueSteps*i)/nSteps2) );
341 : }
342 : // fuer radiale FV
343 : else
344 : {
345 : // fuer axiale FV muss die letzte Farbe der ersten
346 : // Farbe entsprechen
347 : // #107350# Setting end color one step earlier, as the
348 : // last time we get here, we drop out of the loop later
349 : // on.
350 0 : if ( i >= nSteps )
351 : {
352 0 : nRed = (sal_uInt8)nEndRed;
353 0 : nGreen = (sal_uInt8)nEndGreen;
354 0 : nBlue = (sal_uInt8)nEndBlue;
355 : }
356 : else
357 : {
358 0 : if ( i <= nStepsHalf )
359 : {
360 0 : nRed = ImplGetGradientColorValue( nEndRed-((nRedSteps*i)/nSteps2) );
361 0 : nGreen = ImplGetGradientColorValue( nEndGreen-((nGreenSteps*i)/nSteps2) );
362 0 : nBlue = ImplGetGradientColorValue( nEndBlue-((nBlueSteps*i)/nSteps2) );
363 : }
364 : // genau die Mitte und hoeher
365 : else
366 : {
367 0 : long i2 = i - nStepsHalf;
368 0 : nRed = ImplGetGradientColorValue( nStartRed+((nRedSteps*i2)/nSteps2) );
369 0 : nGreen = ImplGetGradientColorValue( nStartGreen+((nGreenSteps*i2)/nSteps2) );
370 0 : nBlue = ImplGetGradientColorValue( nStartBlue+((nBlueSteps*i2)/nSteps2) );
371 : }
372 : }
373 : }
374 :
375 1433066 : if ( bMtf )
376 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), sal_True ) );
377 : else
378 1433066 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
379 69547 : }
380 69547 : }
381 :
382 0 : void OutputDevice::ImplDrawComplexGradient( const Rectangle& rRect,
383 : const Gradient& rGradient,
384 : sal_Bool bMtf, const PolyPolygon* pClipPolyPoly )
385 : {
386 : // Feststellen ob Ausgabe ueber Polygon oder PolyPolygon
387 : // Bei Rasteroperationen ungleich Overpaint immer PolyPolygone,
388 : // da es zu falschen Ergebnissen kommt, wenn man mehrfach uebereinander
389 : // ausgibt
390 : // Bei Druckern auch immer PolyPolygone, da nicht alle Drucker
391 : // das Uebereinanderdrucken von Polygonen koennen
392 : // Virtuelle Device werden auch ausgeklammert, da einige Treiber
393 : // ansonsten zu langsam sind
394 : PolyPolygon* pPolyPoly;
395 0 : Rectangle aRect;
396 0 : Point aCenter;
397 0 : Color aStartCol( rGradient.GetStartColor() );
398 0 : Color aEndCol( rGradient.GetEndColor() );
399 0 : long nStartRed = ( (long) aStartCol.GetRed() * rGradient.GetStartIntensity() ) / 100;
400 0 : long nStartGreen = ( (long) aStartCol.GetGreen() * rGradient.GetStartIntensity() ) / 100;
401 0 : long nStartBlue = ( (long) aStartCol.GetBlue() * rGradient.GetStartIntensity() ) / 100;
402 0 : long nEndRed = ( (long) aEndCol.GetRed() * rGradient.GetEndIntensity() ) / 100;
403 0 : long nEndGreen = ( (long) aEndCol.GetGreen() * rGradient.GetEndIntensity() ) / 100;
404 0 : long nEndBlue = ( (long) aEndCol.GetBlue() * rGradient.GetEndIntensity() ) / 100;
405 0 : long nRedSteps = nEndRed - nStartRed;
406 0 : long nGreenSteps = nEndGreen - nStartGreen;
407 0 : long nBlueSteps = nEndBlue - nStartBlue;
408 0 : long nStepCount = rGradient.GetSteps();
409 0 : sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
410 :
411 0 : rGradient.GetBoundRect( rRect, aRect, aCenter );
412 :
413 0 : if( (meRasterOp != ROP_OVERPAINT) || (meOutDevType != OUTDEV_WINDOW) || bMtf )
414 0 : pPolyPoly = new PolyPolygon( 2 );
415 : else
416 0 : pPolyPoly = NULL;
417 :
418 0 : long nMinRect = std::min( aRect.GetWidth(), aRect.GetHeight() );
419 :
420 : // Anzahl der Schritte berechnen, falls nichts uebergeben wurde
421 0 : if( !nStepCount )
422 : {
423 : long nInc;
424 :
425 0 : if ( meOutDevType != OUTDEV_PRINTER && !bMtf )
426 : {
427 0 : nInc = ( nMinRect < 50 ) ? 2 : 4;
428 : }
429 : else
430 : {
431 : // #105998# Use display-equivalent step size calculation
432 0 : nInc = (nMinRect < 800) ? 10 : 20;
433 : }
434 :
435 0 : nStepCount = nMinRect / nInc;
436 : }
437 :
438 : // minimal drei Schritte und maximal die Anzahl der Farbunterschiede
439 0 : long nSteps = std::max( nStepCount, 2L );
440 0 : long nCalcSteps = std::abs( nRedSteps );
441 0 : long nTempSteps = std::abs( nGreenSteps );
442 0 : if ( nTempSteps > nCalcSteps )
443 0 : nCalcSteps = nTempSteps;
444 0 : nTempSteps = std::abs( nBlueSteps );
445 0 : if ( nTempSteps > nCalcSteps )
446 0 : nCalcSteps = nTempSteps;
447 0 : if ( nCalcSteps < nSteps )
448 0 : nSteps = nCalcSteps;
449 0 : if ( !nSteps )
450 0 : nSteps = 1;
451 :
452 : // Ausgabebegrenzungen und Schrittweite fuer jede Richtung festlegen
453 0 : Polygon aPoly;
454 0 : double fScanLeft = aRect.Left();
455 0 : double fScanTop = aRect.Top();
456 0 : double fScanRight = aRect.Right();
457 0 : double fScanBottom = aRect.Bottom();
458 0 : double fScanIncX = (double) aRect.GetWidth() / (double) nSteps * 0.5;
459 0 : double fScanIncY = (double) aRect.GetHeight() / (double) nSteps * 0.5;
460 :
461 : // all gradients are rendered as nested rectangles which shrink
462 : // equally in each dimension - except for 'square' gradients
463 : // which shrink to a central vertex but are not per-se square.
464 0 : if( rGradient.GetStyle() != GradientStyle_SQUARE )
465 : {
466 0 : fScanIncY = std::min( fScanIncY, fScanIncX );
467 0 : fScanIncX = fScanIncY;
468 : }
469 :
470 0 : sal_uInt8 nRed = (sal_uInt8) nStartRed, nGreen = (sal_uInt8) nStartGreen, nBlue = (sal_uInt8) nStartBlue;
471 0 : bool bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output
472 :
473 0 : if( bMtf )
474 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), sal_True ) );
475 : else
476 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
477 :
478 0 : if( pPolyPoly )
479 : {
480 0 : pPolyPoly->Insert( aPoly = rRect );
481 0 : pPolyPoly->Insert( aPoly );
482 : }
483 : else
484 : {
485 : // extend rect, to avoid missing bounding line
486 0 : Rectangle aExtRect( rRect );
487 :
488 0 : aExtRect.Left() -= 1;
489 0 : aExtRect.Top() -= 1;
490 0 : aExtRect.Right() += 1;
491 0 : aExtRect.Bottom() += 1;
492 :
493 0 : ImplDrawPolygon( aPoly = aExtRect, pClipPolyPoly );
494 : }
495 :
496 : // Schleife, um nacheinander die Polygone/PolyPolygone auszugeben
497 0 : for( long i = 1; i < nSteps; i++ )
498 : {
499 : // neues Polygon berechnen
500 0 : aRect.Left() = (long)( fScanLeft += fScanIncX );
501 0 : aRect.Top() = (long)( fScanTop += fScanIncY );
502 0 : aRect.Right() = (long)( fScanRight -= fScanIncX );
503 0 : aRect.Bottom() = (long)( fScanBottom -= fScanIncY );
504 :
505 0 : if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
506 0 : break;
507 :
508 0 : if( rGradient.GetStyle() == GradientStyle_RADIAL || rGradient.GetStyle() == GradientStyle_ELLIPTICAL )
509 0 : aPoly = Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
510 : else
511 0 : aPoly = Polygon( aRect );
512 :
513 0 : aPoly.Rotate( aCenter, nAngle );
514 :
515 : // Farbe entsprechend anpassen
516 0 : const long nStepIndex = ( ( pPolyPoly != NULL ) ? i : ( i + 1 ) );
517 0 : nRed = ImplGetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) );
518 0 : nGreen = ImplGetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) );
519 0 : nBlue = ImplGetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) );
520 :
521 : // entweder langsame PolyPolygon-Ausgaben oder schnelles Polygon-Painting
522 0 : if( pPolyPoly )
523 : {
524 0 : bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output
525 :
526 0 : pPolyPoly->Replace( pPolyPoly->GetObject( 1 ), 0 );
527 0 : pPolyPoly->Replace( aPoly, 1 );
528 :
529 0 : if( bMtf )
530 0 : mpMetaFile->AddAction( new MetaPolyPolygonAction( *pPolyPoly ) );
531 : else
532 0 : ImplDrawPolyPolygon( *pPolyPoly, pClipPolyPoly );
533 :
534 : // #107349# Set fill color _after_ geometry painting:
535 : // pPolyPoly's geometry is the band from last iteration's
536 : // aPoly to current iteration's aPoly. The window outdev
537 : // path (see else below), on the other hand, paints the
538 : // full aPoly. Thus, here, we're painting the band before
539 : // the one painted in the window outdev path below. To get
540 : // matching colors, have to delay color setting here.
541 0 : if( bMtf )
542 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), sal_True ) );
543 : else
544 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
545 : }
546 : else
547 : {
548 : // #107349# Set fill color _before_ geometry painting
549 0 : if( bMtf )
550 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), sal_True ) );
551 : else
552 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
553 :
554 0 : ImplDrawPolygon( aPoly, pClipPolyPoly );
555 : }
556 : }
557 :
558 : // Falls PolyPolygon-Ausgabe, muessen wir noch ein letztes inneres Polygon zeichnen
559 0 : if( pPolyPoly )
560 : {
561 0 : const Polygon& rPoly = pPolyPoly->GetObject( 1 );
562 :
563 0 : if( !rPoly.GetBoundRect().IsEmpty() )
564 : {
565 : // #107349# Paint last polygon with end color only if loop
566 : // has generated output. Otherwise, the current
567 : // (i.e. start) color is taken, to generate _any_ output.
568 0 : if( bPaintLastPolygon )
569 : {
570 0 : nRed = ImplGetGradientColorValue( nEndRed );
571 0 : nGreen = ImplGetGradientColorValue( nEndGreen );
572 0 : nBlue = ImplGetGradientColorValue( nEndBlue );
573 : }
574 :
575 0 : if( bMtf )
576 : {
577 0 : mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), sal_True ) );
578 0 : mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) );
579 : }
580 : else
581 : {
582 0 : mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
583 0 : ImplDrawPolygon( rPoly, pClipPolyPoly );
584 : }
585 : }
586 :
587 0 : delete pPolyPoly;
588 0 : }
589 0 : }
590 :
591 155413 : void OutputDevice::DrawGradient( const Rectangle& rRect,
592 : const Gradient& rGradient )
593 : {
594 : DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
595 : DBG_CHKOBJ( &rGradient, Gradient, NULL );
596 :
597 155413 : if ( mnDrawMode & DRAWMODE_NOGRADIENT )
598 0 : return;
599 155413 : else if ( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) )
600 : {
601 0 : Color aColor;
602 :
603 0 : if ( mnDrawMode & DRAWMODE_BLACKGRADIENT )
604 0 : aColor = Color( COL_BLACK );
605 0 : else if ( mnDrawMode & DRAWMODE_WHITEGRADIENT )
606 0 : aColor = Color( COL_WHITE );
607 0 : else if ( mnDrawMode & DRAWMODE_SETTINGSGRADIENT )
608 0 : aColor = GetSettings().GetStyleSettings().GetWindowColor();
609 :
610 0 : if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT )
611 : {
612 0 : aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80,
613 0 : ( aColor.GetGreen() >> 1 ) | 0x80,
614 0 : ( aColor.GetBlue() >> 1 ) | 0x80 );
615 : }
616 :
617 0 : Push( PUSH_LINECOLOR | PUSH_FILLCOLOR );
618 0 : SetLineColor( aColor );
619 0 : SetFillColor( aColor );
620 0 : DrawRect( rRect );
621 0 : Pop();
622 0 : return;
623 : }
624 :
625 155413 : Gradient aGradient( rGradient );
626 :
627 155413 : if ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) )
628 : {
629 0 : Color aStartCol( aGradient.GetStartColor() );
630 0 : Color aEndCol( aGradient.GetEndColor() );
631 :
632 0 : if ( mnDrawMode & DRAWMODE_GRAYGRADIENT )
633 : {
634 0 : sal_uInt8 cStartLum = aStartCol.GetLuminance(), cEndLum = aEndCol.GetLuminance();
635 0 : aStartCol = Color( cStartLum, cStartLum, cStartLum );
636 0 : aEndCol = Color( cEndLum, cEndLum, cEndLum );
637 : }
638 :
639 0 : if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT )
640 : {
641 0 : aStartCol = Color( ( aStartCol.GetRed() >> 1 ) | 0x80,
642 0 : ( aStartCol.GetGreen() >> 1 ) | 0x80,
643 0 : ( aStartCol.GetBlue() >> 1 ) | 0x80 );
644 :
645 0 : aEndCol = Color( ( aEndCol.GetRed() >> 1 ) | 0x80,
646 0 : ( aEndCol.GetGreen() >> 1 ) | 0x80,
647 0 : ( aEndCol.GetBlue() >> 1 ) | 0x80 );
648 : }
649 :
650 0 : aGradient.SetStartColor( aStartCol );
651 0 : aGradient.SetEndColor( aEndCol );
652 : }
653 :
654 155413 : if( mpMetaFile )
655 0 : mpMetaFile->AddAction( new MetaGradientAction( rRect, aGradient ) );
656 :
657 155413 : if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
658 0 : return;
659 :
660 : // Rechteck in Pixel umrechnen
661 155413 : Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
662 155413 : aRect.Justify();
663 :
664 : // Wenn Rechteck leer ist, brauchen wir nichts machen
665 155413 : if ( !aRect.IsEmpty() )
666 : {
667 : // Clip Region sichern
668 155413 : Push( PUSH_CLIPREGION );
669 155413 : IntersectClipRegion( rRect );
670 :
671 : // because we draw with no border line, we have to expand gradient
672 : // rect to avoid missing lines on the right and bottom edge
673 155413 : aRect.Left()--;
674 155413 : aRect.Top()--;
675 155413 : aRect.Right()++;
676 155413 : aRect.Bottom()++;
677 :
678 : // we need a graphics
679 155413 : if ( !mpGraphics )
680 : {
681 0 : if ( !ImplGetGraphics() )
682 0 : return;
683 : }
684 :
685 155413 : if ( mbInitClipRegion )
686 155413 : ImplInitClipRegion();
687 :
688 155413 : if ( !mbOutputClipped )
689 : {
690 : // Gradienten werden ohne Umrandung gezeichnet
691 69547 : if ( mbLineColor || mbInitLineColor )
692 : {
693 69534 : mpGraphics->SetLineColor();
694 69534 : mbInitLineColor = sal_True;
695 : }
696 :
697 69547 : mbInitFillColor = sal_True;
698 :
699 : // calculate step count if necessary
700 69547 : if ( !aGradient.GetSteps() )
701 69547 : aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
702 :
703 69547 : if( aGradient.GetStyle() == GradientStyle_LINEAR || aGradient.GetStyle() == GradientStyle_AXIAL )
704 69547 : ImplDrawLinearGradient( aRect, aGradient, sal_False, NULL );
705 : else
706 0 : ImplDrawComplexGradient( aRect, aGradient, sal_False, NULL );
707 : }
708 :
709 155413 : Pop();
710 : }
711 :
712 155413 : if( mpAlphaVDev )
713 : {
714 : // #i32109#: Make gradient area opaque
715 0 : mpAlphaVDev->ImplFillOpaqueRectangle( rRect );
716 155413 : }
717 : }
718 :
719 0 : void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly,
720 : const Gradient& rGradient )
721 : {
722 : DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
723 : DBG_CHKOBJ( &rGradient, Gradient, NULL );
724 :
725 0 : if( mbInitClipRegion )
726 0 : ImplInitClipRegion();
727 :
728 0 : if( mbOutputClipped )
729 0 : return;
730 :
731 0 : if( !mpGraphics )
732 0 : if( !ImplGetGraphics() )
733 0 : return;
734 :
735 0 : if( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() && !( mnDrawMode & DRAWMODE_NOGRADIENT ) )
736 : {
737 0 : if ( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) )
738 : {
739 0 : Color aColor;
740 :
741 0 : if ( mnDrawMode & DRAWMODE_BLACKGRADIENT )
742 0 : aColor = Color( COL_BLACK );
743 0 : else if ( mnDrawMode & DRAWMODE_WHITEGRADIENT )
744 0 : aColor = Color( COL_WHITE );
745 0 : else if ( mnDrawMode & DRAWMODE_SETTINGSGRADIENT )
746 0 : aColor = GetSettings().GetStyleSettings().GetWindowColor();
747 :
748 0 : if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT )
749 : {
750 0 : aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80,
751 0 : ( aColor.GetGreen() >> 1 ) | 0x80,
752 0 : ( aColor.GetBlue() >> 1 ) | 0x80 );
753 : }
754 :
755 0 : Push( PUSH_LINECOLOR | PUSH_FILLCOLOR );
756 0 : SetLineColor( aColor );
757 0 : SetFillColor( aColor );
758 0 : DrawPolyPolygon( rPolyPoly );
759 0 : Pop();
760 0 : return;
761 : }
762 :
763 0 : if( mpMetaFile )
764 : {
765 0 : const Rectangle aRect( rPolyPoly.GetBoundRect() );
766 :
767 0 : mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN" ) );
768 0 : mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) );
769 :
770 0 : if( OUTDEV_PRINTER == meOutDevType )
771 : {
772 0 : Push( PUSH_CLIPREGION );
773 0 : IntersectClipRegion(Region(rPolyPoly));
774 0 : DrawGradient( aRect, rGradient );
775 0 : Pop();
776 : }
777 : else
778 : {
779 0 : const sal_Bool bOldOutput = IsOutputEnabled();
780 :
781 0 : EnableOutput( sal_False );
782 0 : Push( PUSH_RASTEROP );
783 0 : SetRasterOp( ROP_XOR );
784 0 : DrawGradient( aRect, rGradient );
785 0 : SetFillColor( COL_BLACK );
786 0 : SetRasterOp( ROP_0 );
787 0 : DrawPolyPolygon( rPolyPoly );
788 0 : SetRasterOp( ROP_XOR );
789 0 : DrawGradient( aRect, rGradient );
790 0 : Pop();
791 0 : EnableOutput( bOldOutput );
792 : }
793 :
794 0 : mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END" ) );
795 : }
796 :
797 0 : if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
798 0 : return;
799 :
800 0 : Gradient aGradient( rGradient );
801 :
802 0 : if ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) )
803 : {
804 0 : Color aStartCol( aGradient.GetStartColor() );
805 0 : Color aEndCol( aGradient.GetEndColor() );
806 :
807 0 : if ( mnDrawMode & DRAWMODE_GRAYGRADIENT )
808 : {
809 0 : sal_uInt8 cStartLum = aStartCol.GetLuminance(), cEndLum = aEndCol.GetLuminance();
810 0 : aStartCol = Color( cStartLum, cStartLum, cStartLum );
811 0 : aEndCol = Color( cEndLum, cEndLum, cEndLum );
812 : }
813 :
814 0 : if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT )
815 : {
816 0 : aStartCol = Color( ( aStartCol.GetRed() >> 1 ) | 0x80,
817 0 : ( aStartCol.GetGreen() >> 1 ) | 0x80,
818 0 : ( aStartCol.GetBlue() >> 1 ) | 0x80 );
819 :
820 0 : aEndCol = Color( ( aEndCol.GetRed() >> 1 ) | 0x80,
821 0 : ( aEndCol.GetGreen() >> 1 ) | 0x80,
822 0 : ( aEndCol.GetBlue() >> 1 ) | 0x80 );
823 : }
824 :
825 0 : aGradient.SetStartColor( aStartCol );
826 0 : aGradient.SetEndColor( aEndCol );
827 : }
828 :
829 0 : if( OUTDEV_PRINTER == meOutDevType || ImplGetSVData()->maGDIData.mbNoXORClipping )
830 : {
831 0 : const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
832 :
833 0 : if( !Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
834 : {
835 : // Rechteck in Pixel umrechnen
836 0 : Rectangle aRect( ImplLogicToDevicePixel( aBoundRect ) );
837 0 : aRect.Justify();
838 :
839 : // Wenn Rechteck leer ist, brauchen wir nichts machen
840 0 : if ( !aRect.IsEmpty() )
841 : {
842 0 : if( !mpGraphics && !ImplGetGraphics() )
843 0 : return;
844 :
845 0 : if( mbInitClipRegion )
846 0 : ImplInitClipRegion();
847 :
848 0 : if( !mbOutputClipped )
849 : {
850 0 : PolyPolygon aClipPolyPoly( ImplLogicToDevicePixel( rPolyPoly ) );
851 :
852 : // Gradienten werden ohne Umrandung gezeichnet
853 0 : if( mbLineColor || mbInitLineColor )
854 : {
855 0 : mpGraphics->SetLineColor();
856 0 : mbInitLineColor = sal_True;
857 : }
858 :
859 0 : mbInitFillColor = sal_True;
860 :
861 : // calculate step count if necessary
862 0 : if ( !aGradient.GetSteps() )
863 0 : aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
864 :
865 0 : if( aGradient.GetStyle() == GradientStyle_LINEAR || aGradient.GetStyle() == GradientStyle_AXIAL )
866 0 : ImplDrawLinearGradient( aRect, aGradient, sal_False, &aClipPolyPoly );
867 : else
868 0 : ImplDrawComplexGradient( aRect, aGradient, sal_False, &aClipPolyPoly );
869 : }
870 : }
871 : }
872 : }
873 : else
874 : {
875 0 : const PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
876 0 : const Rectangle aBoundRect( aPolyPoly.GetBoundRect() );
877 0 : Point aPoint;
878 0 : Rectangle aDstRect( aPoint, GetOutputSizePixel() );
879 :
880 0 : aDstRect.Intersection( aBoundRect );
881 :
882 0 : if( OUTDEV_WINDOW == meOutDevType )
883 : {
884 0 : const Region aPaintRgn( ( (Window*) this )->GetPaintRegion() );
885 :
886 0 : if( !aPaintRgn.IsNull() )
887 0 : aDstRect.Intersection( LogicToPixel( aPaintRgn ).GetBoundRect() );
888 : }
889 :
890 0 : if( !aDstRect.IsEmpty() )
891 : {
892 : VirtualDevice* pVDev;
893 0 : const Size aDstSize( aDstRect.GetSize() );
894 :
895 0 : if( HasAlpha() )
896 : {
897 : // #110958# Pay attention to alpha VDevs here, otherwise,
898 : // background will be wrong: Temp VDev has to have alpha, too.
899 0 : pVDev = new VirtualDevice( *this, 0, GetAlphaBitCount() > 1 ? 0 : 1 );
900 : }
901 : else
902 : {
903 : // nothing special here. Plain VDev
904 0 : pVDev = new VirtualDevice();
905 : }
906 :
907 0 : if( pVDev->SetOutputSizePixel( aDstSize) )
908 : {
909 0 : MapMode aVDevMap;
910 0 : const sal_Bool bOldMap = mbMap;
911 :
912 0 : EnableMapMode( sal_False );
913 :
914 0 : pVDev->DrawOutDev( Point(), aDstSize, aDstRect.TopLeft(), aDstSize, *this );
915 0 : pVDev->SetRasterOp( ROP_XOR );
916 0 : aVDevMap.SetOrigin( Point( -aDstRect.Left(), -aDstRect.Top() ) );
917 0 : pVDev->SetMapMode( aVDevMap );
918 0 : pVDev->DrawGradient( aBoundRect, aGradient );
919 0 : pVDev->SetFillColor( COL_BLACK );
920 0 : pVDev->SetRasterOp( ROP_0 );
921 0 : pVDev->DrawPolyPolygon( aPolyPoly );
922 0 : pVDev->SetRasterOp( ROP_XOR );
923 0 : pVDev->DrawGradient( aBoundRect, aGradient );
924 0 : aVDevMap.SetOrigin( Point() );
925 0 : pVDev->SetMapMode( aVDevMap );
926 0 : DrawOutDev( aDstRect.TopLeft(), aDstSize, Point(), aDstSize, *pVDev );
927 :
928 0 : EnableMapMode( bOldMap );
929 : }
930 :
931 0 : delete pVDev;
932 0 : }
933 0 : }
934 : }
935 :
936 0 : if( mpAlphaVDev )
937 0 : mpAlphaVDev->DrawPolyPolygon( rPolyPoly );
938 : }
939 :
940 0 : void OutputDevice::AddGradientActions( const Rectangle& rRect, const Gradient& rGradient,
941 : GDIMetaFile& rMtf )
942 : {
943 : DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
944 : DBG_CHKOBJ( &rGradient, Gradient, NULL );
945 :
946 0 : Rectangle aRect( rRect );
947 :
948 0 : aRect.Justify();
949 :
950 : // Wenn Rechteck leer ist, brauchen wir nichts machen
951 0 : if ( !aRect.IsEmpty() )
952 : {
953 0 : Gradient aGradient( rGradient );
954 0 : GDIMetaFile* pOldMtf = mpMetaFile;
955 :
956 0 : mpMetaFile = &rMtf;
957 0 : mpMetaFile->AddAction( new MetaPushAction( PUSH_ALL ) );
958 0 : mpMetaFile->AddAction( new MetaISectRectClipRegionAction( aRect ) );
959 0 : mpMetaFile->AddAction( new MetaLineColorAction( Color(), sal_False ) );
960 :
961 : // because we draw with no border line, we have to expand gradient
962 : // rect to avoid missing lines on the right and bottom edge
963 0 : aRect.Left()--;
964 0 : aRect.Top()--;
965 0 : aRect.Right()++;
966 0 : aRect.Bottom()++;
967 :
968 : // calculate step count if necessary
969 0 : if ( !aGradient.GetSteps() )
970 0 : aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
971 :
972 0 : if( aGradient.GetStyle() == GradientStyle_LINEAR || aGradient.GetStyle() == GradientStyle_AXIAL )
973 0 : ImplDrawLinearGradient( aRect, aGradient, sal_True, NULL );
974 : else
975 0 : ImplDrawComplexGradient( aRect, aGradient, sal_True, NULL );
976 :
977 0 : mpMetaFile->AddAction( new MetaPopAction() );
978 0 : mpMetaFile = pOldMtf;
979 : }
980 0 : }
981 :
982 238 : void OutputDevice::DrawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch )
983 : {
984 : DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
985 :
986 238 : Hatch aHatch( rHatch );
987 :
988 238 : if ( mnDrawMode & ( DRAWMODE_BLACKLINE | DRAWMODE_WHITELINE |
989 : DRAWMODE_GRAYLINE | DRAWMODE_GHOSTEDLINE |
990 : DRAWMODE_SETTINGSLINE ) )
991 : {
992 0 : Color aColor( rHatch.GetColor() );
993 :
994 0 : if ( mnDrawMode & DRAWMODE_BLACKLINE )
995 0 : aColor = Color( COL_BLACK );
996 0 : else if ( mnDrawMode & DRAWMODE_WHITELINE )
997 0 : aColor = Color( COL_WHITE );
998 0 : else if ( mnDrawMode & DRAWMODE_GRAYLINE )
999 : {
1000 0 : const sal_uInt8 cLum = aColor.GetLuminance();
1001 0 : aColor = Color( cLum, cLum, cLum );
1002 : }
1003 0 : else if( mnDrawMode & DRAWMODE_SETTINGSLINE )
1004 : {
1005 0 : aColor = GetSettings().GetStyleSettings().GetFontColor();
1006 : }
1007 :
1008 0 : if ( mnDrawMode & DRAWMODE_GHOSTEDLINE )
1009 : {
1010 0 : aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80,
1011 0 : ( aColor.GetGreen() >> 1 ) | 0x80,
1012 0 : ( aColor.GetBlue() >> 1 ) | 0x80);
1013 : }
1014 :
1015 0 : aHatch.SetColor( aColor );
1016 : }
1017 :
1018 238 : if( mpMetaFile )
1019 0 : mpMetaFile->AddAction( new MetaHatchAction( rPolyPoly, aHatch ) );
1020 :
1021 238 : if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
1022 0 : return;
1023 :
1024 238 : if( !mpGraphics && !ImplGetGraphics() )
1025 0 : return;
1026 :
1027 238 : if( mbInitClipRegion )
1028 0 : ImplInitClipRegion();
1029 :
1030 238 : if( mbOutputClipped )
1031 0 : return;
1032 :
1033 238 : if( rPolyPoly.Count() )
1034 : {
1035 238 : PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
1036 238 : GDIMetaFile* pOldMetaFile = mpMetaFile;
1037 238 : sal_Bool bOldMap = mbMap;
1038 :
1039 238 : aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
1040 238 : aHatch.SetDistance( ImplLogicWidthToDevicePixel( aHatch.GetDistance() ) );
1041 :
1042 238 : mpMetaFile = NULL;
1043 238 : EnableMapMode( sal_False );
1044 238 : Push( PUSH_LINECOLOR );
1045 238 : SetLineColor( aHatch.GetColor() );
1046 238 : ImplInitLineColor();
1047 238 : ImplDrawHatch( aPolyPoly, aHatch, sal_False );
1048 238 : Pop();
1049 238 : EnableMapMode( bOldMap );
1050 238 : mpMetaFile = pOldMetaFile;
1051 : }
1052 :
1053 238 : if( mpAlphaVDev )
1054 0 : mpAlphaVDev->DrawHatch( rPolyPoly, rHatch );
1055 : }
1056 :
1057 0 : void OutputDevice::AddHatchActions( const PolyPolygon& rPolyPoly, const Hatch& rHatch,
1058 : GDIMetaFile& rMtf )
1059 : {
1060 : DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
1061 :
1062 0 : PolyPolygon aPolyPoly( rPolyPoly );
1063 0 : aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME | POLY_OPTIMIZE_CLOSE );
1064 :
1065 0 : if( aPolyPoly.Count() )
1066 : {
1067 0 : GDIMetaFile* pOldMtf = mpMetaFile;
1068 :
1069 0 : mpMetaFile = &rMtf;
1070 0 : mpMetaFile->AddAction( new MetaPushAction( PUSH_ALL ) );
1071 0 : mpMetaFile->AddAction( new MetaLineColorAction( rHatch.GetColor(), sal_True ) );
1072 0 : ImplDrawHatch( aPolyPoly, rHatch, sal_True );
1073 0 : mpMetaFile->AddAction( new MetaPopAction() );
1074 0 : mpMetaFile = pOldMtf;
1075 0 : }
1076 0 : }
1077 :
1078 238 : void OutputDevice::ImplDrawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch, sal_Bool bMtf )
1079 : {
1080 238 : if(rPolyPoly.Count())
1081 : {
1082 : // #i115630# ImplDrawHatch does not work with beziers included in the polypolygon, take care of that
1083 238 : bool bIsCurve(false);
1084 :
1085 476 : for(sal_uInt16 a(0); !bIsCurve && a < rPolyPoly.Count(); a++)
1086 : {
1087 238 : if(rPolyPoly[a].HasFlags())
1088 : {
1089 0 : bIsCurve = true;
1090 : }
1091 : }
1092 :
1093 238 : if(bIsCurve)
1094 : {
1095 : OSL_ENSURE(false, "ImplDrawHatch does *not* support curves, falling back to AdaptiveSubdivide()...");
1096 0 : PolyPolygon aPolyPoly;
1097 :
1098 0 : rPolyPoly.AdaptiveSubdivide(aPolyPoly);
1099 0 : ImplDrawHatch(aPolyPoly, rHatch, bMtf);
1100 : }
1101 : else
1102 : {
1103 238 : Rectangle aRect( rPolyPoly.GetBoundRect() );
1104 238 : const long nLogPixelWidth = ImplDevicePixelToLogicWidth( 1 );
1105 238 : const long nWidth = ImplDevicePixelToLogicWidth( std::max( ImplLogicWidthToDevicePixel( rHatch.GetDistance() ), 3L ) );
1106 238 : Point* pPtBuffer = new Point[ HATCH_MAXPOINTS ];
1107 238 : Point aPt1, aPt2, aEndPt1;
1108 238 : Size aInc;
1109 :
1110 : // Single hatch
1111 238 : aRect.Left() -= nLogPixelWidth; aRect.Top() -= nLogPixelWidth; aRect.Right() += nLogPixelWidth; aRect.Bottom() += nLogPixelWidth;
1112 238 : ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle(), aPt1, aPt2, aInc, aEndPt1 );
1113 12495 : do
1114 : {
1115 12495 : ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer, bMtf );
1116 12495 : aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
1117 12495 : aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
1118 : }
1119 12495 : while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
1120 :
1121 238 : if( ( rHatch.GetStyle() == HATCH_DOUBLE ) || ( rHatch.GetStyle() == HATCH_TRIPLE ) )
1122 : {
1123 : // Double hatch
1124 0 : ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 900, aPt1, aPt2, aInc, aEndPt1 );
1125 0 : do
1126 : {
1127 0 : ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer, bMtf );
1128 0 : aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
1129 0 : aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
1130 : }
1131 0 : while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
1132 :
1133 0 : if( rHatch.GetStyle() == HATCH_TRIPLE )
1134 : {
1135 : // Triple hatch
1136 0 : ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 450, aPt1, aPt2, aInc, aEndPt1 );
1137 0 : do
1138 : {
1139 0 : ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer, bMtf );
1140 0 : aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
1141 0 : aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
1142 : }
1143 0 : while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
1144 : }
1145 : }
1146 :
1147 238 : delete[] pPtBuffer;
1148 : }
1149 : }
1150 238 : }
1151 :
1152 238 : void OutputDevice::ImplCalcHatchValues( const Rectangle& rRect, long nDist, sal_uInt16 nAngle10,
1153 : Point& rPt1, Point& rPt2, Size& rInc, Point& rEndPt1 )
1154 : {
1155 238 : Point aRef;
1156 238 : long nAngle = nAngle10 % 1800;
1157 238 : long nOffset = 0;
1158 :
1159 238 : if( nAngle > 900 )
1160 0 : nAngle -= 1800;
1161 :
1162 238 : aRef = ( !IsRefPoint() ? rRect.TopLeft() : GetRefPoint() );
1163 :
1164 238 : if( 0 == nAngle )
1165 : {
1166 238 : rInc = Size( 0, nDist );
1167 238 : rPt1 = rRect.TopLeft();
1168 238 : rPt2 = rRect.TopRight();
1169 238 : rEndPt1 = rRect.BottomLeft();
1170 :
1171 238 : if( aRef.Y() <= rRect.Top() )
1172 238 : nOffset = ( ( rRect.Top() - aRef.Y() ) % nDist );
1173 : else
1174 0 : nOffset = ( nDist - ( ( aRef.Y() - rRect.Top() ) % nDist ) );
1175 :
1176 238 : rPt1.Y() -= nOffset;
1177 238 : rPt2.Y() -= nOffset;
1178 : }
1179 0 : else if( 900 == nAngle )
1180 : {
1181 0 : rInc = Size( nDist, 0 );
1182 0 : rPt1 = rRect.TopLeft();
1183 0 : rPt2 = rRect.BottomLeft();
1184 0 : rEndPt1 = rRect.TopRight();
1185 :
1186 0 : if( aRef.X() <= rRect.Left() )
1187 0 : nOffset = ( rRect.Left() - aRef.X() ) % nDist;
1188 : else
1189 0 : nOffset = nDist - ( ( aRef.X() - rRect.Left() ) % nDist );
1190 :
1191 0 : rPt1.X() -= nOffset;
1192 0 : rPt2.X() -= nOffset;
1193 : }
1194 0 : else if( nAngle >= -450 && nAngle <= 450 )
1195 : {
1196 0 : const double fAngle = F_PI1800 * labs( nAngle );
1197 0 : const double fTan = tan( fAngle );
1198 0 : const long nYOff = FRound( ( rRect.Right() - rRect.Left() ) * fTan );
1199 : long nPY;
1200 :
1201 0 : rInc = Size( 0, nDist = FRound( nDist / cos( fAngle ) ) );
1202 :
1203 0 : if( nAngle > 0 )
1204 : {
1205 0 : rPt1 = rRect.TopLeft();
1206 0 : rPt2 = Point( rRect.Right(), rRect.Top() - nYOff );
1207 0 : rEndPt1 = Point( rRect.Left(), rRect.Bottom() + nYOff );
1208 0 : nPY = FRound( aRef.Y() - ( ( rPt1.X() - aRef.X() ) * fTan ) );
1209 : }
1210 : else
1211 : {
1212 0 : rPt1 = rRect.TopRight();
1213 0 : rPt2 = Point( rRect.Left(), rRect.Top() - nYOff );
1214 0 : rEndPt1 = Point( rRect.Right(), rRect.Bottom() + nYOff );
1215 0 : nPY = FRound( aRef.Y() + ( ( rPt1.X() - aRef.X() ) * fTan ) );
1216 : }
1217 :
1218 0 : if( nPY <= rPt1.Y() )
1219 0 : nOffset = ( rPt1.Y() - nPY ) % nDist;
1220 : else
1221 0 : nOffset = nDist - ( ( nPY - rPt1.Y() ) % nDist );
1222 :
1223 0 : rPt1.Y() -= nOffset;
1224 0 : rPt2.Y() -= nOffset;
1225 : }
1226 : else
1227 : {
1228 0 : const double fAngle = F_PI1800 * labs( nAngle );
1229 0 : const double fTan = tan( fAngle );
1230 0 : const long nXOff = FRound( ( rRect.Bottom() - rRect.Top() ) / fTan );
1231 : long nPX;
1232 :
1233 0 : rInc = Size( nDist = FRound( nDist / sin( fAngle ) ), 0 );
1234 :
1235 0 : if( nAngle > 0 )
1236 : {
1237 0 : rPt1 = rRect.TopLeft();
1238 0 : rPt2 = Point( rRect.Left() - nXOff, rRect.Bottom() );
1239 0 : rEndPt1 = Point( rRect.Right() + nXOff, rRect.Top() );
1240 0 : nPX = FRound( aRef.X() - ( ( rPt1.Y() - aRef.Y() ) / fTan ) );
1241 : }
1242 : else
1243 : {
1244 0 : rPt1 = rRect.BottomLeft();
1245 0 : rPt2 = Point( rRect.Left() - nXOff, rRect.Top() );
1246 0 : rEndPt1 = Point( rRect.Right() + nXOff, rRect.Bottom() );
1247 0 : nPX = FRound( aRef.X() + ( ( rPt1.Y() - aRef.Y() ) / fTan ) );
1248 : }
1249 :
1250 0 : if( nPX <= rPt1.X() )
1251 0 : nOffset = ( rPt1.X() - nPX ) % nDist;
1252 : else
1253 0 : nOffset = nDist - ( ( nPX - rPt1.X() ) % nDist );
1254 :
1255 0 : rPt1.X() -= nOffset;
1256 0 : rPt2.X() -= nOffset;
1257 : }
1258 238 : }
1259 :
1260 12495 : void OutputDevice::ImplDrawHatchLine( const Line& rLine, const PolyPolygon& rPolyPoly,
1261 : Point* pPtBuffer, sal_Bool bMtf )
1262 : {
1263 : double fX, fY;
1264 12495 : long nAdd, nPCounter = 0;
1265 :
1266 24990 : for( long nPoly = 0, nPolyCount = rPolyPoly.Count(); nPoly < nPolyCount; nPoly++ )
1267 : {
1268 12495 : const Polygon& rPoly = rPolyPoly[ (sal_uInt16) nPoly ];
1269 :
1270 12495 : if( rPoly.GetSize() > 1 )
1271 : {
1272 12495 : Line aCurSegment( rPoly[ 0 ], Point() );
1273 :
1274 62475 : for( long i = 1, nCount = rPoly.GetSize(); i <= nCount; i++ )
1275 : {
1276 49980 : aCurSegment.SetEnd( rPoly[ (sal_uInt16)( i % nCount ) ] );
1277 49980 : nAdd = 0;
1278 :
1279 49980 : if( rLine.Intersection( aCurSegment, fX, fY ) )
1280 : {
1281 49028 : if( ( fabs( fX - aCurSegment.GetStart().X() ) <= 0.0000001 ) &&
1282 24514 : ( fabs( fY - aCurSegment.GetStart().Y() ) <= 0.0000001 ) )
1283 : {
1284 0 : const Line aPrevSegment( rPoly[ (sal_uInt16)( ( i > 1 ) ? ( i - 2 ) : ( nCount - 1 ) ) ], aCurSegment.GetStart() );
1285 0 : const double fPrevDistance = rLine.GetDistance( aPrevSegment.GetStart() );
1286 0 : const double fCurDistance = rLine.GetDistance( aCurSegment.GetEnd() );
1287 :
1288 0 : if( ( fPrevDistance <= 0.0 && fCurDistance > 0.0 ) ||
1289 0 : ( fPrevDistance > 0.0 && fCurDistance < 0.0 ) )
1290 : {
1291 0 : nAdd = 1;
1292 : }
1293 : }
1294 49028 : else if( ( fabs( fX - aCurSegment.GetEnd().X() ) <= 0.0000001 ) &&
1295 24514 : ( fabs( fY - aCurSegment.GetEnd().Y() ) <= 0.0000001 ) )
1296 : {
1297 0 : const Line aNextSegment( aCurSegment.GetEnd(), rPoly[ (sal_uInt16)( ( i + 1 ) % nCount ) ] );
1298 :
1299 0 : if( ( fabs( rLine.GetDistance( aNextSegment.GetEnd() ) ) <= 0.0000001 ) &&
1300 0 : ( rLine.GetDistance( aCurSegment.GetStart() ) > 0.0 ) )
1301 : {
1302 0 : nAdd = 1;
1303 : }
1304 : }
1305 : else
1306 24514 : nAdd = 1;
1307 :
1308 24514 : if( nAdd )
1309 24514 : pPtBuffer[ nPCounter++ ] = Point( FRound( fX ), FRound( fY ) );
1310 : }
1311 :
1312 49980 : aCurSegment.SetStart( aCurSegment.GetEnd() );
1313 : }
1314 : }
1315 : }
1316 :
1317 12495 : if( nPCounter > 1 )
1318 : {
1319 12257 : qsort( pPtBuffer, nPCounter, sizeof( Point ), ImplHatchCmpFnc );
1320 :
1321 12257 : if( nPCounter & 1 )
1322 0 : nPCounter--;
1323 :
1324 12257 : if( bMtf )
1325 : {
1326 0 : for( long i = 0; i < nPCounter; i += 2 )
1327 0 : mpMetaFile->AddAction( new MetaLineAction( pPtBuffer[ i ], pPtBuffer[ i + 1 ] ) );
1328 : }
1329 : else
1330 : {
1331 24514 : for( long i = 0; i < nPCounter; i += 2 )
1332 : {
1333 12257 : if( mpPDFWriter )
1334 : {
1335 0 : mpPDFWriter->drawLine( pPtBuffer[ i ], pPtBuffer[ i+1 ] );
1336 : }
1337 : else
1338 : {
1339 12257 : const Point aPt1( ImplLogicToDevicePixel( pPtBuffer[ i ] ) );
1340 12257 : const Point aPt2( ImplLogicToDevicePixel( pPtBuffer[ i + 1 ] ) );
1341 12257 : mpGraphics->DrawLine( aPt1.X(), aPt1.Y(), aPt2.X(), aPt2.Y(), this );
1342 : }
1343 : }
1344 : }
1345 : }
1346 12960 : }
1347 :
1348 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|