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