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