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