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 <limits.h> 32 33 #include <tools/vcompat.hxx> 34 #include <tools/stream.hxx> 35 #include <tools/debug.hxx> 36 37 #include <vcl/region.hxx> 38 #include <vcl/regband.hxx> 39 #include <vcl/salbtype.hxx> 40 41 #include <region.h> 42 43 #include <basegfx/matrix/b2dhommatrix.hxx> 44 #include <basegfx/polygon/b2dpolypolygontools.hxx> 45 #include <basegfx/polygon/b2dpolygontools.hxx> 46 #include <basegfx/polygon/b2dpolygonclipper.hxx> 47 #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 48 #include <basegfx/range/b2drange.hxx> 49 #include <basegfx/matrix/b2dhommatrixtools.hxx> 50 51 // ======================================================================= 52 // 53 // ImplRegionBand 54 // 55 // Die Klassen RegionBand/ImplRegionBand speichert Regionen in Form von 56 // Rechtecken ab. Die Region ist in Y-Richtung in Baendern unterteilt, die 57 // wiederum ein oder mehrere Rechtecke mit der Hoehe des Bandes enthalten. 58 // 59 // Leere Baender werden entfernt. 60 // 61 // Polygone und PolyPolygone werden ebenfalls in Rechtecke zerlegt und in 62 // der Baendern abgelegt. Hierzu werden alle Punkte der einzelnen Polygone 63 // mit dem Bresenham-Algorithmus berechnet und in die Baender eingetragen. 64 // Nach der vollstaendigen Berechung aller Kanten werden die entsprechenden 65 // Rechntecke berechnet 66 67 // ======================================================================= 68 69 static ImplRegionBase aImplNullRegion( 0 ); 70 static ImplRegionBase aImplEmptyRegion( 0 ); 71 72 // ======================================================================= 73 74 DBG_NAME( Region ) 75 DBG_NAMEEX( Polygon ) 76 DBG_NAMEEX( PolyPolygon ) 77 78 namespace { 79 80 /** Return <TRUE/> when the given polygon is rectiliner and oriented so that 81 all sides are either horizontal or vertical. 82 */ 83 bool ImplIsPolygonRectilinear (const PolyPolygon& rPolyPoly) 84 { 85 // Iterate over all polygons. 86 const sal_uInt16 nPolyCount = rPolyPoly.Count(); 87 for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly) 88 { 89 const Polygon& aPoly = rPolyPoly.GetObject(nPoly); 90 91 // Iterate over all edges of the current polygon. 92 const sal_uInt16 nSize = aPoly.GetSize(); 93 94 if (nSize < 2) 95 continue; 96 Point aPoint (aPoly.GetPoint(0)); 97 const Point aLastPoint (aPoint); 98 for (sal_uInt16 nPoint = 1; nPoint < nSize; ++nPoint) 99 { 100 const Point aNextPoint (aPoly.GetPoint(nPoint)); 101 // When there is at least one edge that is neither vertical nor 102 // horizontal then the entire polygon is not rectilinear (and 103 // oriented along primary axes.) 104 if (aPoint.X() != aNextPoint.X() && aPoint.Y() != aNextPoint.Y()) 105 return false; 106 107 aPoint = aNextPoint; 108 } 109 // Compare closing edge. 110 if (aLastPoint.X() != aPoint.X() && aLastPoint.Y() != aPoint.Y()) 111 return false; 112 } 113 return true; 114 } 115 116 117 118 /** This function is similar to the ImplRegion::InsertBands() method. 119 It creates a minimal set of missing bands so that the entire vertical 120 interval from nTop to nBottom is covered by bands. 121 */ 122 void ImplAddMissingBands ( 123 ImplRegion* pImplRegion, 124 const long nTop, 125 const long nBottom) 126 { 127 // Iterate over already existing bands and add missing bands atop the 128 // first and between two bands. 129 ImplRegionBand* pPreviousBand = NULL; 130 ImplRegionBand* pBand = pImplRegion->ImplGetFirstRegionBand(); 131 long nCurrentTop (nTop); 132 while (pBand != NULL && nCurrentTop<nBottom) 133 { 134 if (nCurrentTop < pBand->mnYTop) 135 { 136 // Create new band above the current band. 137 ImplRegionBand* pAboveBand = new ImplRegionBand( 138 nCurrentTop, 139 ::std::min(nBottom,pBand->mnYTop-1)); 140 pImplRegion->InsertBand(pPreviousBand, pAboveBand); 141 } 142 143 // Adapt the top of the interval to prevent overlapping bands. 144 nCurrentTop = ::std::max(nTop, pBand->mnYBottom+1); 145 146 // Advance to next band. 147 pPreviousBand = pBand; 148 pBand = pBand->mpNextBand; 149 } 150 151 // We still have to cover two cases: 152 // 1. The region does not yet contain any bands. 153 // 2. The intervall nTop->nBottom extends past the bottom most band. 154 if (nCurrentTop <= nBottom 155 && (pBand==NULL || nBottom>pBand->mnYBottom)) 156 { 157 // When there is no previous band then the new one will be the 158 // first. Otherwise the new band is inserted behind the last band. 159 pImplRegion->InsertBand( 160 pPreviousBand, 161 new ImplRegionBand( 162 nCurrentTop, 163 nBottom)); 164 } 165 } 166 167 168 169 /** Convert a rectilinear polygon (that is oriented along the primary axes) 170 to a list of bands. For this special form of polygon we can use an 171 optimization that prevents the creation of one band per y value. 172 However, it still is possible that some temporary bands are created that 173 later can be optimized away. 174 @param rPolyPolygon 175 A set of zero, one, or more polygons, nested or not, that are 176 converted into a list of bands. 177 @return 178 A new ImplRegion object is returned that contains the bands that 179 represent the given poly-polygon. 180 */ 181 ImplRegion* ImplRectilinearPolygonToBands (const PolyPolygon& rPolyPoly) 182 { 183 OSL_ASSERT(ImplIsPolygonRectilinear (rPolyPoly)); 184 185 // Create a new ImplRegion object as container of the bands. 186 ImplRegion* pImplRegion = new ImplRegion(); 187 long nLineId = 0L; 188 189 // Iterate over all polygons. 190 const sal_uInt16 nPolyCount = rPolyPoly.Count(); 191 for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly) 192 { 193 const Polygon& aPoly = rPolyPoly.GetObject(nPoly); 194 195 // Iterate over all edges of the current polygon. 196 const sal_uInt16 nSize = aPoly.GetSize(); 197 if (nSize < 2) 198 continue; 199 // Avoid fetching every point twice (each point is the start point 200 // of one and the end point of another edge.) 201 Point aStart (aPoly.GetPoint(0)); 202 Point aEnd; 203 for (sal_uInt16 nPoint = 1; nPoint <= nSize; ++nPoint, aStart=aEnd) 204 { 205 // We take the implicit closing edge into account by mapping 206 // index nSize to 0. 207 aEnd = aPoly.GetPoint(nPoint%nSize); 208 if (aStart.Y() == aEnd.Y()) 209 { 210 // Horizontal lines are ignored. 211 continue; 212 } 213 214 // At this point the line has to be vertical. 215 OSL_ASSERT(aStart.X() == aEnd.X()); 216 217 // Sort y-coordinates to simplify the algorithm and store the 218 // direction seperately. The direction is calculated as it is 219 // in other places (but seems to be the wrong way.) 220 const long nTop (::std::min(aStart.Y(), aEnd.Y())); 221 const long nBottom (::std::max(aStart.Y(), aEnd.Y())); 222 const LineType eLineType (aStart.Y() > aEnd.Y() ? LINE_DESCENDING : LINE_ASCENDING); 223 224 // Make sure that the current line is covered by bands. 225 ImplAddMissingBands(pImplRegion, nTop,nBottom); 226 227 // Find top-most band that may contain nTop. 228 ImplRegionBand* pBand = pImplRegion->ImplGetFirstRegionBand(); 229 while (pBand!=NULL && pBand->mnYBottom < nTop) 230 pBand = pBand->mpNextBand; 231 ImplRegionBand* pTopBand = pBand; 232 // If necessary split the band at nTop so that nTop is contained 233 // in the lower band. 234 if (pBand!=NULL 235 // Prevent the current band from becoming 0 pixel high 236 && pBand->mnYTop<nTop 237 // this allows the lowest pixel of the band to be split off 238 && pBand->mnYBottom>=nTop 239 // do not split a band that is just one pixel high 240 && pBand->mnYTop<pBand->mnYBottom) 241 { 242 // Split the top band. 243 pTopBand = pBand->SplitBand(nTop); 244 } 245 246 // Advance to band that may contain nBottom. 247 while (pBand!=NULL && pBand->mnYBottom < nBottom) 248 pBand = pBand->mpNextBand; 249 // The lowest band may have to be split at nBottom so that 250 // nBottom itself remains in the upper band. 251 if (pBand!=NULL 252 // allow the current band becoming 1 pixel high 253 && pBand->mnYTop<=nBottom 254 // prevent splitting off a band that is 0 pixel high 255 && pBand->mnYBottom>nBottom 256 // do not split a band that is just one pixel high 257 && pBand->mnYTop<pBand->mnYBottom) 258 { 259 // Split the bottom band. 260 pBand->SplitBand(nBottom+1); 261 } 262 263 // Note that we remember the top band (in pTopBand) but not the 264 // bottom band. The later can be determined by comparing y 265 // coordinates. 266 267 // Add the x-value as point to all bands in the nTop->nBottom range. 268 for (pBand=pTopBand; pBand!=NULL&&pBand->mnYTop<=nBottom; pBand=pBand->mpNextBand) 269 pBand->InsertPoint(aStart.X(), nLineId++, sal_True, eLineType); 270 } 271 } 272 273 return pImplRegion; 274 } 275 276 277 278 279 /** Convert a general polygon (one for which ImplIsPolygonRectilinear() 280 returns <FALSE/>) to bands. 281 */ 282 ImplRegion* ImplGeneralPolygonToBands ( 283 const PolyPolygon& rPolyPoly, 284 const Rectangle& rPolygonBoundingBox) 285 { 286 long nLineID = 0L; 287 288 // initialisation and creation of Bands 289 ImplRegion* pImplRegion = new ImplRegion(); 290 pImplRegion->CreateBandRange( rPolygonBoundingBox.Top(), rPolygonBoundingBox.Bottom() ); 291 292 // insert polygons 293 const sal_uInt16 nPolyCount = rPolyPoly.Count(); 294 for ( sal_uInt16 nPoly = 0; nPoly < nPolyCount; nPoly++ ) 295 { 296 // get reference to current polygon 297 const Polygon& aPoly = rPolyPoly.GetObject( nPoly ); 298 const sal_uInt16 nSize = aPoly.GetSize(); 299 300 // not enough points ( <= 2 )? -> nothing to do! 301 if ( nSize <= 2 ) 302 continue; 303 304 // band the polygon 305 for ( sal_uInt16 nPoint = 1; nPoint < nSize; nPoint++ ) 306 pImplRegion->InsertLine( aPoly.GetPoint(nPoint-1), aPoly.GetPoint(nPoint), nLineID++ ); 307 308 // close polygon with line from first point to last point, if neccesary 309 const Point rLastPoint = aPoly.GetPoint(nSize-1); 310 const Point rFirstPoint = aPoly.GetPoint(0); 311 if ( rLastPoint != rFirstPoint ) 312 pImplRegion->InsertLine( rLastPoint, rFirstPoint, nLineID++ ); 313 } 314 315 return pImplRegion; 316 } 317 318 319 } // end of anonymous namespace 320 321 322 // ----------------------------------------------------------------------- 323 324 #ifdef DBG_UTIL 325 const char* ImplDbgTestRegion( const void* pObj ) 326 { 327 Region* pRegion = (Region*)pObj; 328 ImplRegion* pImplRegion = pRegion->ImplGetImplRegion(); 329 330 if ( aImplNullRegion.mnRefCount ) 331 return "Null-Region-RefCount modified"; 332 if ( aImplNullRegion.mnRectCount ) 333 return "Null-Region-RectCount modified"; 334 if ( aImplNullRegion.mpPolyPoly ) 335 return "Null-Region-PolyPoly modified"; 336 if ( aImplEmptyRegion.mnRefCount ) 337 return "Emptry-Region-RefCount modified"; 338 if ( aImplEmptyRegion.mnRectCount ) 339 return "Emptry-Region-RectCount modified"; 340 if ( aImplEmptyRegion.mpPolyPoly ) 341 return "Emptry-Region-PolyPoly modified"; 342 343 if ( (pImplRegion != &aImplEmptyRegion) && (pImplRegion != &aImplNullRegion) ) 344 { 345 sal_uLong nCount = 0; 346 const ImplRegionBand* pBand = pImplRegion->ImplGetFirstRegionBand(); 347 while ( pBand ) 348 { 349 if ( pBand->mnYBottom < pBand->mnYTop ) 350 return "YBottom < YTop"; 351 if ( pBand->mpNextBand ) 352 { 353 if ( pBand->mnYBottom >= pBand->mpNextBand->mnYTop ) 354 return "overlapping bands in region"; 355 } 356 if ( pBand->mbTouched > 1 ) 357 return "Band-mbTouched overwrite"; 358 359 ImplRegionBandSep* pSep = pBand->mpFirstSep; 360 while ( pSep ) 361 { 362 if ( pSep->mnXRight < pSep->mnXLeft ) 363 return "XLeft < XRight"; 364 if ( pSep->mpNextSep ) 365 { 366 if ( pSep->mnXRight >= pSep->mpNextSep->mnXLeft ) 367 return "overlapping separations in region"; 368 } 369 if ( pSep->mbRemoved > 1 ) 370 return "Sep-mbRemoved overwrite"; 371 372 nCount++; 373 pSep = pSep->mpNextSep; 374 } 375 376 pBand = pBand->mpNextBand; 377 } 378 379 if ( pImplRegion->mnRectCount != nCount ) 380 return "mnRetCount is not valid"; 381 } 382 383 return NULL; 384 } 385 386 void TraceBands (const ImplRegionBand* pFirstBand) 387 { 388 int nBandIndex (0); 389 const ImplRegionBand* pBand = pFirstBand; 390 while (pBand != NULL) 391 { 392 OSL_TRACE(" band %d %d->%d : ", nBandIndex++, 393 pBand->mnYTop, pBand->mnYBottom); 394 395 ImplRegionBandPoint* pPoint = pBand->mpFirstBandPoint; 396 while (pPoint != NULL) 397 { 398 OSL_TRACE(" %d ", pPoint->mnX); 399 pPoint = pPoint->mpNextBandPoint; 400 } 401 OSL_TRACE(" | "); 402 403 ImplRegionBandSep* pSep = pBand->mpFirstSep; 404 while (pSep != NULL) 405 { 406 OSL_TRACE(" %d->%d ", pSep->mnXLeft, pSep->mnXRight); 407 pSep = pSep->mpNextSep; 408 } 409 OSL_TRACE("\n"); 410 411 pBand = pBand->mpNextBand; 412 } 413 } 414 #endif 415 416 // ======================================================================= 417 418 inline void Region::ImplPolyPolyRegionToBandRegion() 419 { 420 if( mpImplRegion->mpPolyPoly || mpImplRegion->mpB2DPolyPoly ) 421 ImplPolyPolyRegionToBandRegionFunc(); 422 } 423 424 // ======================================================================= 425 426 ImplRegionBase::ImplRegionBase( int nRefCount ) 427 : mnRefCount( nRefCount ) 428 , mnRectCount( 0 ) 429 , mpPolyPoly( NULL ) 430 , mpB2DPolyPoly( NULL ) 431 {} 432 433 // ------------------------------------------------------------------------ 434 435 ImplRegion::ImplRegion() 436 { 437 mpFirstBand = NULL; 438 mpLastCheckedBand = NULL; 439 } 440 441 // ------------------------------------------------------------------------ 442 443 ImplRegion::ImplRegion( const PolyPolygon& rPolyPoly ) 444 { 445 mpFirstBand = NULL; 446 mpLastCheckedBand = NULL; 447 mpPolyPoly = new PolyPolygon( rPolyPoly ); 448 } 449 450 // ------------------------------------------------------------------------ 451 452 ImplRegion::ImplRegion( const basegfx::B2DPolyPolygon& rPolyPoly ) 453 { 454 mpFirstBand = NULL; 455 mpLastCheckedBand = NULL; 456 mpB2DPolyPoly = new basegfx::B2DPolyPolygon( rPolyPoly ); 457 } 458 459 // ----------------------------------------------------------------------- 460 461 ImplRegion::ImplRegion( const ImplRegion& rImplRegion ) 462 : ImplRegionBase() 463 { 464 mpFirstBand = NULL; 465 mpLastCheckedBand = NULL; 466 mnRectCount = rImplRegion.mnRectCount; 467 468 if ( rImplRegion.mpPolyPoly ) 469 mpPolyPoly = new PolyPolygon( *rImplRegion.mpPolyPoly ); 470 else if( rImplRegion.mpB2DPolyPoly ) 471 mpB2DPolyPoly = new basegfx::B2DPolyPolygon( *rImplRegion.mpB2DPolyPoly ); 472 473 // insert band(s) into the list 474 ImplRegionBand* pNewBand; 475 ImplRegionBand* pPrevBand = 0; 476 ImplRegionBand* pBand = rImplRegion.mpFirstBand; 477 while ( pBand ) 478 { 479 pNewBand = new ImplRegionBand( *pBand ); 480 481 // first element? -> set as first into the list 482 if ( pBand == rImplRegion.mpFirstBand ) 483 mpFirstBand = pNewBand; 484 else 485 pPrevBand->mpNextBand = pNewBand; 486 487 pPrevBand = pNewBand; 488 pBand = pBand->mpNextBand; 489 } 490 } 491 492 // ----------------------------------------------------------------------- 493 494 ImplRegion::~ImplRegion() 495 { 496 DBG_ASSERT( (this != &aImplEmptyRegion) && (this != &aImplNullRegion), 497 "ImplRegion::~ImplRegion() - Empty oder NULL-Region" ); 498 499 ImplRegionBand* pBand = mpFirstBand; 500 while ( pBand ) 501 { 502 ImplRegionBand* pTempBand = pBand->mpNextBand; 503 delete pBand; 504 pBand = pTempBand; 505 } 506 } 507 508 // ----------------------------------------------------------------------- 509 510 ImplRegionBase::~ImplRegionBase() 511 { 512 delete mpPolyPoly; 513 delete mpB2DPolyPoly; 514 } 515 516 // ----------------------------------------------------------------------- 517 // 518 // create complete range of bands in single steps 519 520 void ImplRegion::CreateBandRange( long nYTop, long nYBottom ) 521 { 522 // add top band 523 mpFirstBand = new ImplRegionBand( nYTop-1, nYTop-1 ); 524 525 // begin first search from the first element 526 mpLastCheckedBand = mpFirstBand; 527 528 ImplRegionBand* pBand = mpFirstBand; 529 for ( int i = nYTop; i <= nYBottom+1; i++ ) 530 { 531 // create new band 532 ImplRegionBand* pNewBand = new ImplRegionBand( i, i ); 533 pBand->mpNextBand = pNewBand; 534 if ( pBand != mpFirstBand ) 535 pNewBand->mpPrevBand = pBand; 536 537 pBand = pBand->mpNextBand; 538 } 539 } 540 541 // ----------------------------------------------------------------------- 542 543 sal_Bool ImplRegion::InsertLine( const Point& rStartPt, const Point& rEndPt, 544 long nLineId ) 545 { 546 long nX, nY; 547 548 // lines consisting of a single point do not interest here 549 if ( rStartPt == rEndPt ) 550 return sal_True; 551 552 LineType eLineType = (rStartPt.Y() > rEndPt.Y()) ? LINE_DESCENDING : LINE_ASCENDING; 553 if ( rStartPt.X() == rEndPt.X() ) 554 { 555 // vertical line 556 const long nEndY = rEndPt.Y(); 557 558 nX = rStartPt.X(); 559 nY = rStartPt.Y(); 560 561 if( nEndY > nY ) 562 { 563 for ( ; nY <= nEndY; nY++ ) 564 { 565 Point aNewPoint( nX, nY ); 566 InsertPoint( aNewPoint, nLineId, 567 (aNewPoint == rEndPt) || (aNewPoint == rStartPt), 568 eLineType ); 569 } 570 } 571 else 572 { 573 for ( ; nY >= nEndY; nY-- ) 574 { 575 Point aNewPoint( nX, nY ); 576 InsertPoint( aNewPoint, nLineId, 577 (aNewPoint == rEndPt) || (aNewPoint == rStartPt), 578 eLineType ); 579 } 580 } 581 } 582 else if ( rStartPt.Y() != rEndPt.Y() ) 583 { 584 const long nDX = labs( rEndPt.X() - rStartPt.X() ); 585 const long nDY = labs( rEndPt.Y() - rStartPt.Y() ); 586 const long nStartX = rStartPt.X(); 587 const long nStartY = rStartPt.Y(); 588 const long nEndX = rEndPt.X(); 589 const long nEndY = rEndPt.Y(); 590 const long nXInc = ( nStartX < nEndX ) ? 1L : -1L; 591 const long nYInc = ( nStartY < nEndY ) ? 1L : -1L; 592 593 if ( nDX >= nDY ) 594 { 595 const long nDYX = ( nDY - nDX ) << 1; 596 const long nDY2 = nDY << 1; 597 long nD = nDY2 - nDX; 598 599 for ( nX = nStartX, nY = nStartY; nX != nEndX; nX += nXInc ) 600 { 601 InsertPoint( Point( nX, nY ), nLineId, nStartX == nX, eLineType ); 602 603 if ( nD < 0L ) 604 nD += nDY2; 605 else 606 nD += nDYX, nY += nYInc; 607 } 608 } 609 else 610 { 611 const long nDYX = ( nDX - nDY ) << 1; 612 const long nDY2 = nDX << 1; 613 long nD = nDY2 - nDY; 614 615 for ( nX = nStartX, nY = nStartY; nY != nEndY; nY += nYInc ) 616 { 617 InsertPoint( Point( nX, nY ), nLineId, nStartY == nY, eLineType ); 618 619 if ( nD < 0L ) 620 nD += nDY2; 621 else 622 nD += nDYX, nX += nXInc; 623 } 624 } 625 626 // last point 627 InsertPoint( Point( nEndX, nEndY ), nLineId, sal_True, eLineType ); 628 } 629 630 return sal_True; 631 } 632 633 // ----------------------------------------------------------------------- 634 // 635 // search for appropriate place for the new point 636 637 sal_Bool ImplRegion::InsertPoint( const Point &rPoint, long nLineID, 638 sal_Bool bEndPoint, LineType eLineType ) 639 { 640 DBG_ASSERT( mpFirstBand != NULL, "ImplRegion::InsertPoint - no bands available!" ); 641 642 if ( rPoint.Y() == mpLastCheckedBand->mnYTop ) 643 { 644 mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType ); 645 return sal_True; 646 } 647 648 if ( rPoint.Y() > mpLastCheckedBand->mnYTop ) 649 { 650 // Search ascending 651 while ( mpLastCheckedBand ) 652 { 653 // Insert point if possible 654 if ( rPoint.Y() == mpLastCheckedBand->mnYTop ) 655 { 656 mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType ); 657 return sal_True; 658 } 659 660 mpLastCheckedBand = mpLastCheckedBand->mpNextBand; 661 } 662 663 DBG_ERROR( "ImplRegion::InsertPoint reached the end of the list!" ); 664 } 665 else 666 { 667 // Search descending 668 while ( mpLastCheckedBand ) 669 { 670 // Insert point if possible 671 if ( rPoint.Y() == mpLastCheckedBand->mnYTop ) 672 { 673 mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType ); 674 return sal_True; 675 } 676 677 mpLastCheckedBand = mpLastCheckedBand->mpPrevBand; 678 } 679 680 DBG_ERROR( "ImplRegion::InsertPoint reached the beginning of the list!" ); 681 } 682 683 DBG_ERROR( "ImplRegion::InsertPoint point not inserted!" ); 684 685 // reinitialize pointer (should never be reached!) 686 mpLastCheckedBand = mpFirstBand; 687 688 return sal_False; 689 } 690 691 // ----------------------------------------------------------------------- 692 // 693 // search for appropriate places for the new bands 694 695 void ImplRegion::InsertBands( long nTop, long nBottom ) 696 { 697 // region empty? -> set rectagle as first entry! 698 if ( !mpFirstBand ) 699 { 700 // add band with boundaries of the rectangle 701 mpFirstBand = new ImplRegionBand( nTop, nBottom ); 702 return; 703 } 704 705 // find/insert bands for the boundaries of the rectangle 706 sal_Bool bTopBoundaryInserted = sal_False; 707 sal_Bool bTop2BoundaryInserted = sal_False; 708 sal_Bool bBottomBoundaryInserted = sal_False; 709 710 // special case: top boundary is above the first band 711 ImplRegionBand* pNewBand; 712 if ( nTop < mpFirstBand->mnYTop ) 713 { 714 // create new band above the first in the list 715 pNewBand = new ImplRegionBand( nTop, mpFirstBand->mnYTop ); 716 if ( nBottom < mpFirstBand->mnYTop ) 717 pNewBand->mnYBottom = nBottom; 718 719 // insert band into the list 720 pNewBand->mpNextBand = mpFirstBand; 721 mpFirstBand = pNewBand; 722 723 bTopBoundaryInserted = sal_True; 724 } 725 726 // insert band(s) into the list 727 ImplRegionBand* pBand = mpFirstBand; 728 while ( pBand ) 729 { 730 // Insert Bands if possible 731 if ( !bTopBoundaryInserted ) 732 bTopBoundaryInserted = InsertSingleBand( pBand, nTop - 1 ); 733 734 if ( !bTop2BoundaryInserted ) 735 bTop2BoundaryInserted = InsertSingleBand( pBand, nTop ); 736 737 if ( !bBottomBoundaryInserted && (nTop != nBottom) ) 738 bBottomBoundaryInserted = InsertSingleBand( pBand, nBottom ); 739 740 // both boundaries inserted? -> nothing more to do 741 if ( bTopBoundaryInserted && bTop2BoundaryInserted && bBottomBoundaryInserted ) 742 break; 743 744 // insert bands between two bands if neccessary 745 if ( pBand->mpNextBand ) 746 { 747 if ( (pBand->mnYBottom + 1) < pBand->mpNextBand->mnYTop ) 748 { 749 // copy band with list and set new boundary 750 pNewBand = new ImplRegionBand( pBand->mnYBottom+1, 751 pBand->mpNextBand->mnYTop-1 ); 752 753 // insert band into the list 754 pNewBand->mpNextBand = pBand->mpNextBand; 755 pBand->mpNextBand = pNewBand; 756 } 757 } 758 759 pBand = pBand->mpNextBand; 760 } 761 } 762 763 // ----------------------------------------------------------------------- 764 // 765 // create new band and insert it into the list 766 767 sal_Bool ImplRegion::InsertSingleBand( ImplRegionBand* pBand, 768 long nYBandPosition ) 769 { 770 // boundary already included in band with height 1? -> nothing to do! 771 if ( (pBand->mnYTop == pBand->mnYBottom) && 772 (nYBandPosition == pBand->mnYTop) ) 773 return sal_True; 774 775 // insert single height band on top? 776 ImplRegionBand* pNewBand; 777 if ( nYBandPosition == pBand->mnYTop ) 778 { 779 // copy band with list and set new boundary 780 pNewBand = new ImplRegionBand( *pBand ); 781 pNewBand->mnYTop = nYBandPosition+1; 782 783 // insert band into the list 784 pNewBand->mpNextBand = pBand->mpNextBand; 785 pBand->mnYBottom = nYBandPosition; 786 pBand->mpNextBand = pNewBand; 787 788 return sal_True; 789 } 790 791 // top of new rectangle within the current band? -> insert new band and copy data 792 if ( (nYBandPosition > pBand->mnYTop) && 793 (nYBandPosition < pBand->mnYBottom) ) 794 { 795 // copy band with list and set new boundary 796 pNewBand = new ImplRegionBand( *pBand ); 797 pNewBand->mnYTop = nYBandPosition; 798 799 // insert band into the list 800 pNewBand->mpNextBand = pBand->mpNextBand; 801 pBand->mnYBottom = nYBandPosition; 802 pBand->mpNextBand = pNewBand; 803 804 // copy band with list and set new boundary 805 pNewBand = new ImplRegionBand( *pBand ); 806 pNewBand->mnYTop = nYBandPosition; 807 808 // insert band into the list 809 pBand->mpNextBand->mnYTop = nYBandPosition+1; 810 811 pNewBand->mpNextBand = pBand->mpNextBand; 812 pBand->mnYBottom = nYBandPosition - 1; 813 pBand->mpNextBand = pNewBand; 814 815 return sal_True; 816 } 817 818 // create new band behind the current in the list 819 if ( !pBand->mpNextBand ) 820 { 821 if ( nYBandPosition == pBand->mnYBottom ) 822 { 823 // copy band with list and set new boundary 824 pNewBand = new ImplRegionBand( *pBand ); 825 pNewBand->mnYTop = pBand->mnYBottom; 826 pNewBand->mnYBottom = nYBandPosition; 827 828 pBand->mnYBottom = nYBandPosition-1; 829 830 // append band to the list 831 pBand->mpNextBand = pNewBand; 832 return sal_True; 833 } 834 835 if ( nYBandPosition > pBand->mnYBottom ) 836 { 837 // create new band 838 pNewBand = new ImplRegionBand( pBand->mnYBottom + 1, nYBandPosition ); 839 840 // append band to the list 841 pBand->mpNextBand = pNewBand; 842 return sal_True; 843 } 844 } 845 846 return sal_False; 847 } 848 849 // ------------------------------------------------------------------------ 850 851 void ImplRegion::InsertBand (ImplRegionBand* pPreviousBand, ImplRegionBand* pBandToInsert) 852 { 853 OSL_ASSERT(pBandToInsert!=NULL); 854 855 if (pPreviousBand == NULL) 856 { 857 // Insert band before all others. 858 if (mpFirstBand != NULL) 859 mpFirstBand->mpPrevBand = pBandToInsert; 860 pBandToInsert->mpNextBand = mpFirstBand; 861 mpFirstBand = pBandToInsert; 862 } 863 else 864 { 865 // Insert band directly after pPreviousBand. 866 pBandToInsert->mpNextBand = pPreviousBand->mpNextBand; 867 pPreviousBand->mpNextBand = pBandToInsert; 868 pBandToInsert->mpPrevBand = pPreviousBand; 869 } 870 } 871 872 // ------------------------------------------------------------------------ 873 874 void ImplRegion::Union( long nLeft, long nTop, long nRight, long nBottom ) 875 { 876 DBG_ASSERT( nLeft <= nRight, "ImplRegion::Union() - nLeft > nRight" ); 877 DBG_ASSERT( nTop <= nBottom, "ImplRegion::Union() - nTop > nBottom" ); 878 879 // process union 880 ImplRegionBand* pBand = mpFirstBand; 881 while ( pBand ) 882 { 883 if ( pBand->mnYTop >= nTop ) 884 { 885 if ( pBand->mnYBottom <= nBottom ) 886 pBand->Union( nLeft, nRight ); 887 else 888 { 889 #ifdef DBG_UTIL 890 long nCurY = pBand->mnYBottom; 891 pBand = pBand->mpNextBand; 892 while ( pBand ) 893 { 894 if ( (pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY) ) 895 { 896 DBG_ERROR( "ImplRegion::Union() - Bands not sorted!" ); 897 } 898 pBand = pBand->mpNextBand; 899 } 900 #endif 901 break; 902 } 903 } 904 905 pBand = pBand->mpNextBand; 906 } 907 } 908 909 // ----------------------------------------------------------------------- 910 911 void ImplRegion::Exclude( long nLeft, long nTop, long nRight, long nBottom ) 912 { 913 DBG_ASSERT( nLeft <= nRight, "ImplRegion::Exclude() - nLeft > nRight" ); 914 DBG_ASSERT( nTop <= nBottom, "ImplRegion::Exclude() - nTop > nBottom" ); 915 916 // process exclude 917 ImplRegionBand* pBand = mpFirstBand; 918 while ( pBand ) 919 { 920 if ( pBand->mnYTop >= nTop ) 921 { 922 if ( pBand->mnYBottom <= nBottom ) 923 pBand->Exclude( nLeft, nRight ); 924 else 925 { 926 #ifdef DBG_UTIL 927 long nCurY = pBand->mnYBottom; 928 pBand = pBand->mpNextBand; 929 while ( pBand ) 930 { 931 if ( (pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY) ) 932 { 933 DBG_ERROR( "ImplRegion::Exclude() - Bands not sorted!" ); 934 } 935 pBand = pBand->mpNextBand; 936 } 937 #endif 938 break; 939 } 940 } 941 942 pBand = pBand->mpNextBand; 943 } 944 } 945 946 // ----------------------------------------------------------------------- 947 948 void ImplRegion::XOr( long nLeft, long nTop, long nRight, long nBottom ) 949 { 950 DBG_ASSERT( nLeft <= nRight, "ImplRegion::Exclude() - nLeft > nRight" ); 951 DBG_ASSERT( nTop <= nBottom, "ImplRegion::Exclude() - nTop > nBottom" ); 952 953 // process xor 954 ImplRegionBand* pBand = mpFirstBand; 955 while ( pBand ) 956 { 957 if ( pBand->mnYTop >= nTop ) 958 { 959 if ( pBand->mnYBottom <= nBottom ) 960 pBand->XOr( nLeft, nRight ); 961 else 962 { 963 #ifdef DBG_UTIL 964 long nCurY = pBand->mnYBottom; 965 pBand = pBand->mpNextBand; 966 while ( pBand ) 967 { 968 if ( (pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY) ) 969 { 970 DBG_ERROR( "ImplRegion::XOr() - Bands not sorted!" ); 971 } 972 pBand = pBand->mpNextBand; 973 } 974 #endif 975 break; 976 } 977 } 978 979 pBand = pBand->mpNextBand; 980 } 981 } 982 983 // ----------------------------------------------------------------------- 984 // 985 // remove empty bands 986 987 sal_Bool ImplRegion::OptimizeBandList() 988 { 989 DBG_ASSERT( (this != &aImplNullRegion) && (this != &aImplEmptyRegion), 990 "ImplRegion::OptimizeBandList() - Empty oder NULL-Region" ); 991 992 mnRectCount = 0; 993 994 ImplRegionBand* pPrevBand = 0; 995 ImplRegionBand* pBand = mpFirstBand; 996 while ( pBand ) 997 { 998 const sal_Bool bBTEqual = pBand->mpNextBand && 999 (pBand->mnYBottom == pBand->mpNextBand->mnYTop); 1000 1001 // no separation? -> remove! 1002 if ( pBand->IsEmpty() || (bBTEqual && (pBand->mnYBottom == pBand->mnYTop)) ) 1003 { 1004 // save pointer 1005 ImplRegionBand* pOldBand = pBand; 1006 1007 // previous element of the list 1008 if ( pBand == mpFirstBand ) 1009 mpFirstBand = pBand->mpNextBand; 1010 else 1011 pPrevBand->mpNextBand = pBand->mpNextBand; 1012 1013 pBand = pBand->mpNextBand; 1014 delete pOldBand; 1015 } 1016 else 1017 { 1018 // fixup 1019 if ( bBTEqual ) 1020 pBand->mnYBottom = pBand->mpNextBand->mnYTop-1; 1021 1022 // this and next band with equal separations? -> combine! 1023 if ( pBand->mpNextBand && 1024 ((pBand->mnYBottom+1) == pBand->mpNextBand->mnYTop) && 1025 (*pBand == *pBand->mpNextBand) ) 1026 { 1027 // expand current height 1028 pBand->mnYBottom = pBand->mpNextBand->mnYBottom; 1029 1030 // remove next band from list 1031 ImplRegionBand* pDeletedBand = pBand->mpNextBand; 1032 pBand->mpNextBand = pDeletedBand->mpNextBand; 1033 delete pDeletedBand; 1034 1035 // check band again! 1036 } 1037 else 1038 { 1039 // count rectangles within band 1040 ImplRegionBandSep* pSep = pBand->mpFirstSep; 1041 while ( pSep ) 1042 { 1043 mnRectCount++; 1044 pSep = pSep->mpNextSep; 1045 } 1046 1047 pPrevBand = pBand; 1048 pBand = pBand->mpNextBand; 1049 } 1050 } 1051 } 1052 1053 #ifdef DBG_UTIL 1054 pBand = mpFirstBand; 1055 while ( pBand ) 1056 { 1057 DBG_ASSERT( pBand->mpFirstSep != NULL, 1058 "Exiting ImplRegion::OptimizeBandList(): empty band in region!" ); 1059 1060 if ( pBand->mnYBottom < pBand->mnYTop ) 1061 DBG_ERROR( "ImplRegion::OptimizeBandList(): YBottomBoundary < YTopBoundary" ); 1062 1063 if ( pBand->mpNextBand ) 1064 { 1065 if ( pBand->mnYBottom >= pBand->mpNextBand->mnYTop ) 1066 DBG_ERROR( "ImplRegion::OptimizeBandList(): overlapping bands in region!" ); 1067 } 1068 1069 pBand = pBand->mpNextBand; 1070 } 1071 #endif 1072 1073 return (mnRectCount != 0); 1074 } 1075 1076 // ======================================================================= 1077 1078 void Region::ImplCopyData() 1079 { 1080 mpImplRegion->mnRefCount--; 1081 mpImplRegion = new ImplRegion( *mpImplRegion ); 1082 } 1083 1084 // ======================================================================= 1085 1086 Region::Region() 1087 { 1088 DBG_CTOR( Region, ImplDbgTestRegion ); 1089 1090 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1091 } 1092 1093 // ----------------------------------------------------------------------- 1094 1095 Region::Region( RegionType eType ) 1096 { 1097 DBG_CTOR( Region, ImplDbgTestRegion ); 1098 DBG_ASSERT( (eType == REGION_NULL) || (eType == REGION_EMPTY), 1099 "Region( RegionType ) - RegionType != EMPTY/NULL" ); 1100 1101 if ( eType == REGION_NULL ) 1102 mpImplRegion = (ImplRegion*)(&aImplNullRegion); 1103 else 1104 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1105 } 1106 1107 // ----------------------------------------------------------------------- 1108 1109 Region::Region( const Rectangle& rRect ) 1110 { 1111 DBG_CTOR( Region, ImplDbgTestRegion ); 1112 1113 ImplCreateRectRegion( rRect ); 1114 } 1115 1116 // ----------------------------------------------------------------------- 1117 1118 Region::Region( const Polygon& rPolygon ) 1119 { 1120 DBG_CTOR( Region, ImplDbgTestRegion ); 1121 DBG_CHKOBJ( &rPolygon, Polygon, NULL ); 1122 1123 ImplCreatePolyPolyRegion( rPolygon ); 1124 } 1125 1126 // ----------------------------------------------------------------------- 1127 1128 Region::Region( const PolyPolygon& rPolyPoly ) 1129 { 1130 DBG_CTOR( Region, ImplDbgTestRegion ); 1131 DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); 1132 1133 ImplCreatePolyPolyRegion( rPolyPoly ); 1134 } 1135 1136 // ----------------------------------------------------------------------- 1137 1138 Region::Region( const basegfx::B2DPolyPolygon& rPolyPoly ) 1139 { 1140 DBG_CTOR( Region, ImplDbgTestRegion ); 1141 DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); 1142 1143 mpImplRegion = new ImplRegion( rPolyPoly ); 1144 } 1145 1146 // ----------------------------------------------------------------------- 1147 1148 Region::Region( const Region& rRegion ) 1149 { 1150 DBG_CTOR( Region, ImplDbgTestRegion ); 1151 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); 1152 DBG_ASSERT( rRegion.mpImplRegion->mnRefCount < 0xFFFFFFFE, "Region: RefCount overflow" ); 1153 1154 // copy pointer to instance of implementation 1155 mpImplRegion = rRegion.mpImplRegion; 1156 if ( mpImplRegion->mnRefCount ) 1157 mpImplRegion->mnRefCount++; 1158 } 1159 1160 // ----------------------------------------------------------------------- 1161 1162 Region::~Region() 1163 { 1164 DBG_DTOR( Region, ImplDbgTestRegion ); 1165 1166 // statische Object haben RefCount von 0 1167 if ( mpImplRegion->mnRefCount ) 1168 { 1169 if ( mpImplRegion->mnRefCount > 1 ) 1170 mpImplRegion->mnRefCount--; 1171 else 1172 delete mpImplRegion; 1173 } 1174 } 1175 1176 // ----------------------------------------------------------------------- 1177 1178 void Region::ImplCreateRectRegion( const Rectangle& rRect ) 1179 { 1180 if ( rRect.IsEmpty() ) 1181 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1182 else 1183 { 1184 // get justified rectangle 1185 long nTop = Min( rRect.Top(), rRect.Bottom() ); 1186 long nBottom = Max( rRect.Top(), rRect.Bottom() ); 1187 long nLeft = Min( rRect.Left(), rRect.Right() ); 1188 long nRight = Max( rRect.Left(), rRect.Right() ); 1189 1190 // create instance of implementation class 1191 mpImplRegion = new ImplRegion(); 1192 1193 // add band with boundaries of the rectangle 1194 mpImplRegion->mpFirstBand = new ImplRegionBand( nTop, nBottom ); 1195 1196 // Set left and right boundaries of the band 1197 mpImplRegion->mpFirstBand->Union( nLeft, nRight ); 1198 mpImplRegion->mnRectCount = 1; 1199 } 1200 } 1201 1202 // ----------------------------------------------------------------------- 1203 1204 void Region::ImplCreatePolyPolyRegion( const PolyPolygon& rPolyPoly ) 1205 { 1206 const sal_uInt16 nPolyCount = rPolyPoly.Count(); 1207 if ( nPolyCount ) 1208 { 1209 // polypolygon empty? -> empty region 1210 const Rectangle aRect( rPolyPoly.GetBoundRect() ); 1211 1212 if ( !aRect.IsEmpty() ) 1213 { 1214 // width OR height == 1 ? => Rectangular region 1215 if ( (aRect.GetWidth() == 1) 1216 || (aRect.GetHeight() == 1) 1217 || rPolyPoly.IsRect() ) 1218 { 1219 ImplCreateRectRegion( aRect ); 1220 } 1221 else 1222 mpImplRegion = new ImplRegion( rPolyPoly ); 1223 } 1224 else 1225 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1226 } 1227 else 1228 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1229 } 1230 1231 // ----------------------------------------------------------------------- 1232 1233 void Region::ImplPolyPolyRegionToBandRegionFunc() 1234 { 1235 // ensure to subdivide when bezier segemnts are used, it's going to 1236 // be expanded to rectangles 1237 PolyPolygon aPolyPoly; 1238 GetPolyPolygon().AdaptiveSubdivide(aPolyPoly); 1239 1240 if ( mpImplRegion->mnRefCount > 1 ) 1241 mpImplRegion->mnRefCount--; 1242 else 1243 delete mpImplRegion; 1244 1245 if ( aPolyPoly.Count() ) 1246 { 1247 // polypolygon empty? -> empty region 1248 const Rectangle aRect( aPolyPoly.GetBoundRect() ); 1249 1250 if ( !aRect.IsEmpty() ) 1251 { 1252 if (ImplIsPolygonRectilinear(aPolyPoly)) 1253 { 1254 // For rectilinear polygons there is an optimized band conversion. 1255 mpImplRegion = ImplRectilinearPolygonToBands(aPolyPoly); 1256 } 1257 else 1258 { 1259 mpImplRegion = ImplGeneralPolygonToBands(aPolyPoly, aRect); 1260 } 1261 1262 // Convert points into seps. 1263 ImplRegionBand* pRegionBand = mpImplRegion->mpFirstBand; 1264 while ( pRegionBand ) 1265 { 1266 // generate separations from the lines and process union 1267 pRegionBand->ProcessPoints(); 1268 pRegionBand = pRegionBand->mpNextBand; 1269 } 1270 1271 // Optimize list of bands. Adjacent bands with identical lists 1272 // of seps are joined. 1273 if ( !mpImplRegion->OptimizeBandList() ) 1274 { 1275 delete mpImplRegion; 1276 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1277 } 1278 } 1279 else 1280 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1281 } 1282 else 1283 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1284 } 1285 1286 // ----------------------------------------------------------------------- 1287 1288 void Region::Move( long nHorzMove, long nVertMove ) 1289 { 1290 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1291 1292 // no region data? -> nothing to do 1293 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1294 return; 1295 1296 // no own instance data? -> make own copy! 1297 if ( mpImplRegion->mnRefCount > 1 ) 1298 ImplCopyData(); 1299 1300 if ( mpImplRegion->mpPolyPoly ) 1301 mpImplRegion->mpPolyPoly->Move( nHorzMove, nVertMove ); 1302 else if( mpImplRegion->mpB2DPolyPoly ) 1303 { 1304 mpImplRegion->mpB2DPolyPoly->transform(basegfx::tools::createTranslateB2DHomMatrix(nHorzMove, nVertMove)); 1305 } 1306 else 1307 { 1308 ImplRegionBand* pBand = mpImplRegion->mpFirstBand; 1309 while ( pBand ) 1310 { 1311 // process the vertical move 1312 if ( nVertMove != 0) 1313 { 1314 pBand->mnYTop = pBand->mnYTop + nVertMove; 1315 pBand->mnYBottom = pBand->mnYBottom + nVertMove; 1316 } 1317 1318 // process the horizontal move 1319 if ( nHorzMove != 0) 1320 pBand->MoveX( nHorzMove ); 1321 1322 pBand = pBand->mpNextBand; 1323 } 1324 } 1325 } 1326 1327 // ----------------------------------------------------------------------- 1328 1329 void Region::Scale( double fScaleX, double fScaleY ) 1330 { 1331 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1332 1333 // no region data? -> nothing to do 1334 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1335 return; 1336 1337 // no own instance data? -> make own copy! 1338 if ( mpImplRegion->mnRefCount > 1 ) 1339 ImplCopyData(); 1340 1341 if ( mpImplRegion->mpPolyPoly ) 1342 mpImplRegion->mpPolyPoly->Scale( fScaleX, fScaleY ); 1343 else if( mpImplRegion->mpB2DPolyPoly ) 1344 { 1345 mpImplRegion->mpB2DPolyPoly->transform(basegfx::tools::createScaleB2DHomMatrix(fScaleX, fScaleY)); 1346 } 1347 else 1348 { 1349 ImplRegionBand* pBand = mpImplRegion->mpFirstBand; 1350 while ( pBand ) 1351 { 1352 // process the vertical move 1353 if ( fScaleY != 0.0 ) 1354 { 1355 pBand->mnYTop = FRound( pBand->mnYTop * fScaleY ); 1356 pBand->mnYBottom = FRound( pBand->mnYBottom * fScaleY ); 1357 } 1358 1359 // process the horizontal move 1360 if ( fScaleX != 0.0 ) 1361 pBand->ScaleX( fScaleX ); 1362 1363 pBand = pBand->mpNextBand; 1364 } 1365 } 1366 } 1367 1368 // ----------------------------------------------------------------------- 1369 1370 sal_Bool Region::Union( const Rectangle& rRect ) 1371 { 1372 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1373 1374 // is rectangle empty? -> nothing to do 1375 if ( rRect.IsEmpty() ) 1376 return sal_True; 1377 1378 if( HasPolyPolygon() ) 1379 { 1380 // get this B2DPolyPolygon 1381 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 1382 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 1383 1384 if( aThisPolyPoly.count() == 0 ) 1385 { 1386 *this = rRect; 1387 return true; 1388 } 1389 1390 // get the other B2DPolyPolygon 1391 basegfx::B2DPolygon aRectPoly( basegfx::tools::createPolygonFromRect( basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); 1392 basegfx::B2DPolyPolygon aOtherPolyPoly( aRectPoly ); 1393 1394 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationOr( aThisPolyPoly, aOtherPolyPoly ); 1395 *this = Region( aClip ); 1396 1397 return sal_True; 1398 } 1399 1400 ImplPolyPolyRegionToBandRegion(); 1401 1402 // no instance data? -> create! 1403 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1404 mpImplRegion = new ImplRegion(); 1405 1406 // no own instance data? -> make own copy! 1407 if ( mpImplRegion->mnRefCount > 1 ) 1408 ImplCopyData(); 1409 1410 // get justified rectangle 1411 long nLeft = Min( rRect.Left(), rRect.Right() ); 1412 long nTop = Min( rRect.Top(), rRect.Bottom() ); 1413 long nRight = Max( rRect.Left(), rRect.Right() ); 1414 long nBottom = Max( rRect.Top(), rRect.Bottom() ); 1415 1416 // insert bands if the boundaries are not allready in the list 1417 mpImplRegion->InsertBands( nTop, nBottom ); 1418 1419 // process union 1420 mpImplRegion->Union( nLeft, nTop, nRight, nBottom ); 1421 1422 // cleanup 1423 if ( !mpImplRegion->OptimizeBandList() ) 1424 { 1425 delete mpImplRegion; 1426 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1427 } 1428 1429 return sal_True; 1430 } 1431 1432 // ----------------------------------------------------------------------- 1433 1434 sal_Bool Region::Intersect( const Rectangle& rRect ) 1435 { 1436 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1437 1438 // is rectangle empty? -> nothing to do 1439 if ( rRect.IsEmpty() ) 1440 { 1441 // statische Object haben RefCount von 0 1442 if ( mpImplRegion->mnRefCount ) 1443 { 1444 if ( mpImplRegion->mnRefCount > 1 ) 1445 mpImplRegion->mnRefCount--; 1446 else 1447 delete mpImplRegion; 1448 } 1449 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1450 return sal_True; 1451 } 1452 1453 // #103137# Avoid banding for special cases 1454 if ( mpImplRegion->mpPolyPoly ) 1455 { 1456 // #127431# make ImplRegion unique, if not already. 1457 if( mpImplRegion->mnRefCount > 1 ) 1458 { 1459 mpImplRegion->mnRefCount--; 1460 mpImplRegion = new ImplRegion( *mpImplRegion->mpPolyPoly ); 1461 } 1462 1463 // use the PolyPolygon::Clip method for rectangles, this is 1464 // fairly simple (does not even use GPC) and saves us from 1465 // unnecessary banding 1466 mpImplRegion->mpPolyPoly->Clip( rRect ); 1467 1468 return sal_True; 1469 } 1470 else if( mpImplRegion->mpB2DPolyPoly ) 1471 { 1472 // #127431# make ImplRegion unique, if not already. 1473 if( mpImplRegion->mnRefCount > 1 ) 1474 { 1475 mpImplRegion->mnRefCount--; 1476 mpImplRegion = new ImplRegion( *mpImplRegion->mpB2DPolyPoly ); 1477 } 1478 1479 *mpImplRegion->mpB2DPolyPoly = 1480 basegfx::tools::clipPolyPolygonOnRange( *mpImplRegion->mpB2DPolyPoly, 1481 basegfx::B2DRange( rRect.Left(), rRect.Top(), 1482 rRect.Right(), rRect.Bottom() ), 1483 true, false ); 1484 return sal_True; 1485 } 1486 else 1487 ImplPolyPolyRegionToBandRegion(); 1488 1489 // is region empty? -> nothing to do! 1490 if ( mpImplRegion == &aImplEmptyRegion ) 1491 return sal_True; 1492 1493 // get justified rectangle 1494 long nLeft = Min( rRect.Left(), rRect.Right() ); 1495 long nTop = Min( rRect.Top(), rRect.Bottom() ); 1496 long nRight = Max( rRect.Left(), rRect.Right() ); 1497 long nBottom = Max( rRect.Top(), rRect.Bottom() ); 1498 1499 // is own region NULL-region? -> copy data! 1500 if ( mpImplRegion == &aImplNullRegion ) 1501 { 1502 // create instance of implementation class 1503 mpImplRegion = new ImplRegion(); 1504 1505 // add band with boundaries of the rectangle 1506 mpImplRegion->mpFirstBand = new ImplRegionBand( nTop, nBottom ); 1507 1508 // Set left and right boundaries of the band 1509 mpImplRegion->mpFirstBand->Union( nLeft, nRight ); 1510 mpImplRegion->mnRectCount = 1; 1511 1512 return sal_True; 1513 } 1514 1515 // no own instance data? -> make own copy! 1516 if ( mpImplRegion->mnRefCount > 1 ) 1517 ImplCopyData(); 1518 1519 // insert bands if the boundaries are not allready in the list 1520 mpImplRegion->InsertBands( nTop, nBottom ); 1521 1522 // process intersections 1523 ImplRegionBand* pPrevBand = 0; 1524 ImplRegionBand* pBand = mpImplRegion->mpFirstBand; 1525 while ( pBand ) 1526 { 1527 // band within intersection boundary? -> process. otherwise remove 1528 if ( (pBand->mnYTop >= nTop) && 1529 (pBand->mnYBottom <= nBottom) ) 1530 { 1531 // process intersection 1532 pBand->Intersect( nLeft, nRight ); 1533 1534 pPrevBand = pBand; 1535 pBand = pBand->mpNextBand; 1536 } 1537 else 1538 { 1539 ImplRegionBand* pOldBand = pBand; 1540 if ( pBand == mpImplRegion->mpFirstBand ) 1541 mpImplRegion->mpFirstBand = pBand->mpNextBand; 1542 else 1543 pPrevBand->mpNextBand = pBand->mpNextBand; 1544 pBand = pBand->mpNextBand; 1545 delete pOldBand; 1546 } 1547 } 1548 1549 // cleanup 1550 if ( !mpImplRegion->OptimizeBandList() ) 1551 { 1552 delete mpImplRegion; 1553 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1554 } 1555 1556 return sal_True; 1557 } 1558 1559 // ----------------------------------------------------------------------- 1560 1561 sal_Bool Region::Exclude( const Rectangle& rRect ) 1562 { 1563 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1564 1565 // is rectangle empty? -> nothing to do 1566 if ( rRect.IsEmpty() ) 1567 return sal_True; 1568 1569 if( HasPolyPolygon() ) 1570 { 1571 // get this B2DPolyPolygon 1572 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 1573 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 1574 1575 if( aThisPolyPoly.count() == 0 ) 1576 return sal_True; 1577 1578 // get the other B2DPolyPolygon 1579 basegfx::B2DPolygon aRectPoly( basegfx::tools::createPolygonFromRect( basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); 1580 basegfx::B2DPolyPolygon aOtherPolyPoly( aRectPoly ); 1581 1582 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationDiff( aThisPolyPoly, aOtherPolyPoly ); 1583 *this = Region( aClip ); 1584 1585 return sal_True; 1586 } 1587 1588 ImplPolyPolyRegionToBandRegion(); 1589 1590 // no instance data? -> create! 1591 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1592 return sal_True; 1593 1594 // no own instance data? -> make own copy! 1595 if ( mpImplRegion->mnRefCount > 1 ) 1596 ImplCopyData(); 1597 1598 // get justified rectangle 1599 long nLeft = Min( rRect.Left(), rRect.Right() ); 1600 long nTop = Min( rRect.Top(), rRect.Bottom() ); 1601 long nRight = Max( rRect.Left(), rRect.Right() ); 1602 long nBottom = Max( rRect.Top(), rRect.Bottom() ); 1603 1604 // insert bands if the boundaries are not allready in the list 1605 mpImplRegion->InsertBands( nTop, nBottom ); 1606 1607 // process exclude 1608 mpImplRegion->Exclude( nLeft, nTop, nRight, nBottom ); 1609 1610 // cleanup 1611 if ( !mpImplRegion->OptimizeBandList() ) 1612 { 1613 delete mpImplRegion; 1614 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1615 } 1616 1617 return sal_True; 1618 } 1619 1620 // ----------------------------------------------------------------------- 1621 1622 sal_Bool Region::XOr( const Rectangle& rRect ) 1623 { 1624 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1625 1626 // is rectangle empty? -> nothing to do 1627 if ( rRect.IsEmpty() ) 1628 return sal_True; 1629 1630 if( HasPolyPolygon() ) 1631 { 1632 // get this B2DPolyPolygon 1633 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 1634 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 1635 1636 if( aThisPolyPoly.count() == 0 ) 1637 { 1638 *this = rRect; 1639 return sal_True; 1640 } 1641 1642 // get the other B2DPolyPolygon 1643 basegfx::B2DPolygon aRectPoly( basegfx::tools::createPolygonFromRect( basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); 1644 basegfx::B2DPolyPolygon aOtherPolyPoly( aRectPoly ); 1645 1646 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationXor( aThisPolyPoly, aOtherPolyPoly ); 1647 *this = Region( aClip ); 1648 1649 return sal_True; 1650 } 1651 1652 ImplPolyPolyRegionToBandRegion(); 1653 1654 // no instance data? -> create! 1655 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1656 mpImplRegion = new ImplRegion(); 1657 1658 // no own instance data? -> make own copy! 1659 if ( mpImplRegion->mnRefCount > 1 ) 1660 ImplCopyData(); 1661 1662 // get justified rectangle 1663 long nLeft = Min( rRect.Left(), rRect.Right() ); 1664 long nTop = Min( rRect.Top(), rRect.Bottom() ); 1665 long nRight = Max( rRect.Left(), rRect.Right() ); 1666 long nBottom = Max( rRect.Top(), rRect.Bottom() ); 1667 1668 // insert bands if the boundaries are not allready in the list 1669 mpImplRegion->InsertBands( nTop, nBottom ); 1670 1671 // process xor 1672 mpImplRegion->XOr( nLeft, nTop, nRight, nBottom ); 1673 1674 // cleanup 1675 if ( !mpImplRegion->OptimizeBandList() ) 1676 { 1677 delete mpImplRegion; 1678 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1679 } 1680 1681 return sal_True; 1682 } 1683 1684 // ----------------------------------------------------------------------- 1685 void Region::ImplUnionPolyPolygon( const Region& i_rRegion ) 1686 { 1687 // get this B2DPolyPolygon 1688 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 1689 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 1690 1691 if( aThisPolyPoly.count() == 0 ) 1692 { 1693 *this = i_rRegion; 1694 return; 1695 } 1696 1697 // get the other B2DPolyPolygon 1698 basegfx::B2DPolyPolygon aOtherPolyPoly( const_cast<Region&>(i_rRegion).ConvertToB2DPolyPolygon() ); 1699 aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly ); 1700 1701 1702 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationOr( aThisPolyPoly, aOtherPolyPoly ); 1703 1704 *this = Region( aClip ); 1705 } 1706 1707 sal_Bool Region::Union( const Region& rRegion ) 1708 { 1709 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1710 1711 if( rRegion.HasPolyPolygon() || HasPolyPolygon() ) 1712 { 1713 ImplUnionPolyPolygon( rRegion ); 1714 return sal_True; 1715 } 1716 1717 ImplPolyPolyRegionToBandRegion(); 1718 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); 1719 1720 // is region empty or null? -> nothing to do 1721 if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) ) 1722 return sal_True; 1723 1724 // no instance data? -> create! 1725 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1726 mpImplRegion = new ImplRegion(); 1727 1728 // no own instance data? -> make own copy! 1729 if ( mpImplRegion->mnRefCount > 1 ) 1730 ImplCopyData(); 1731 1732 // Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden 1733 ImplRegionBand* pBand = rRegion.mpImplRegion->mpFirstBand; 1734 while ( pBand ) 1735 { 1736 // insert bands if the boundaries are not allready in the list 1737 mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom ); 1738 1739 // process all elements of the list 1740 ImplRegionBandSep* pSep = pBand->mpFirstSep; 1741 while ( pSep ) 1742 { 1743 mpImplRegion->Union( pSep->mnXLeft, pBand->mnYTop, 1744 pSep->mnXRight, pBand->mnYBottom ); 1745 pSep = pSep->mpNextSep; 1746 } 1747 1748 pBand = pBand->mpNextBand; 1749 } 1750 1751 // cleanup 1752 if ( !mpImplRegion->OptimizeBandList() ) 1753 { 1754 delete mpImplRegion; 1755 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1756 } 1757 1758 return sal_True; 1759 } 1760 1761 // ----------------------------------------------------------------------- 1762 void Region::ImplIntersectWithPolyPolygon( const Region& i_rRegion ) 1763 { 1764 // get this B2DPolyPolygon 1765 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 1766 if( aThisPolyPoly.count() == 0 ) 1767 { 1768 *this = i_rRegion; 1769 return; 1770 } 1771 1772 // get the other B2DPolyPolygon 1773 basegfx::B2DPolyPolygon aOtherPolyPoly( const_cast<Region&>(i_rRegion).ConvertToB2DPolyPolygon() ); 1774 1775 basegfx::B2DPolyPolygon aClip = basegfx::tools::clipPolyPolygonOnPolyPolygon( aOtherPolyPoly, aThisPolyPoly, true, false ); 1776 *this = Region( aClip ); 1777 } 1778 1779 sal_Bool Region::Intersect( const Region& rRegion ) 1780 { 1781 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1782 1783 // same instance data? -> nothing to do! 1784 if ( mpImplRegion == rRegion.mpImplRegion ) 1785 return sal_True; 1786 1787 if( rRegion.HasPolyPolygon() || HasPolyPolygon() ) 1788 { 1789 ImplIntersectWithPolyPolygon( rRegion ); 1790 return sal_True; 1791 } 1792 1793 ImplPolyPolyRegionToBandRegion(); 1794 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); 1795 1796 if ( mpImplRegion == &aImplEmptyRegion ) 1797 return sal_True; 1798 1799 // is region null? -> nothing to do 1800 if ( rRegion.mpImplRegion == &aImplNullRegion ) 1801 return sal_True; 1802 1803 // is rectangle empty? -> nothing to do 1804 if ( rRegion.mpImplRegion == &aImplEmptyRegion ) 1805 { 1806 // statische Object haben RefCount von 0 1807 if ( mpImplRegion->mnRefCount ) 1808 { 1809 if ( mpImplRegion->mnRefCount > 1 ) 1810 mpImplRegion->mnRefCount--; 1811 else 1812 delete mpImplRegion; 1813 } 1814 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1815 return sal_True; 1816 } 1817 1818 // is own region NULL-region? -> copy data! 1819 if ( mpImplRegion == &aImplNullRegion) 1820 { 1821 mpImplRegion = rRegion.mpImplRegion; 1822 rRegion.mpImplRegion->mnRefCount++; 1823 return sal_True; 1824 } 1825 1826 // Wenn wir weniger Rechtecke haben, drehen wir den Intersect-Aufruf um 1827 if ( mpImplRegion->mnRectCount+2 < rRegion.mpImplRegion->mnRectCount ) 1828 { 1829 Region aTempRegion = rRegion; 1830 aTempRegion.Intersect( *this ); 1831 *this = aTempRegion; 1832 } 1833 else 1834 { 1835 // no own instance data? -> make own copy! 1836 if ( mpImplRegion->mnRefCount > 1 ) 1837 ImplCopyData(); 1838 1839 // mark all bands as untouched 1840 ImplRegionBand* pBand = mpImplRegion->mpFirstBand; 1841 while ( pBand ) 1842 { 1843 pBand->mbTouched = sal_False; 1844 pBand = pBand->mpNextBand; 1845 } 1846 1847 pBand = rRegion.mpImplRegion->mpFirstBand; 1848 while ( pBand ) 1849 { 1850 // insert bands if the boundaries are not allready in the list 1851 mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom ); 1852 1853 // process all elements of the list 1854 ImplRegionBandSep* pSep = pBand->mpFirstSep; 1855 while ( pSep ) 1856 { 1857 // left boundary? 1858 if ( pSep == pBand->mpFirstSep ) 1859 { 1860 // process intersection and do not remove untouched bands 1861 mpImplRegion->Exclude( LONG_MIN+1, pBand->mnYTop, 1862 pSep->mnXLeft-1, pBand->mnYBottom ); 1863 } 1864 1865 // right boundary? 1866 if ( pSep->mpNextSep == NULL ) 1867 { 1868 // process intersection and do not remove untouched bands 1869 mpImplRegion->Exclude( pSep->mnXRight+1, pBand->mnYTop, 1870 LONG_MAX-1, pBand->mnYBottom ); 1871 } 1872 else 1873 { 1874 // process intersection and do not remove untouched bands 1875 mpImplRegion->Exclude( pSep->mnXRight+1, pBand->mnYTop, 1876 pSep->mpNextSep->mnXLeft-1, pBand->mnYBottom ); 1877 } 1878 1879 pSep = pSep->mpNextSep; 1880 } 1881 1882 pBand = pBand->mpNextBand; 1883 } 1884 1885 // remove all untouched bands if bands allready left 1886 ImplRegionBand* pPrevBand = 0; 1887 pBand = mpImplRegion->mpFirstBand; 1888 while ( pBand ) 1889 { 1890 if ( !pBand->mbTouched ) 1891 { 1892 // save pointer 1893 ImplRegionBand* pOldBand = pBand; 1894 1895 // previous element of the list 1896 if ( pBand == mpImplRegion->mpFirstBand ) 1897 mpImplRegion->mpFirstBand = pBand->mpNextBand; 1898 else 1899 pPrevBand->mpNextBand = pBand->mpNextBand; 1900 1901 pBand = pBand->mpNextBand; 1902 delete pOldBand; 1903 } 1904 else 1905 { 1906 pPrevBand = pBand; 1907 pBand = pBand->mpNextBand; 1908 } 1909 } 1910 1911 // cleanup 1912 if ( !mpImplRegion->OptimizeBandList() ) 1913 { 1914 delete mpImplRegion; 1915 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1916 } 1917 } 1918 1919 return sal_True; 1920 } 1921 1922 // ----------------------------------------------------------------------- 1923 void Region::ImplExcludePolyPolygon( const Region& i_rRegion ) 1924 { 1925 // get this B2DPolyPolygon 1926 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 1927 if( aThisPolyPoly.count() == 0 ) 1928 return; 1929 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 1930 1931 // get the other B2DPolyPolygon 1932 basegfx::B2DPolyPolygon aOtherPolyPoly( const_cast<Region&>(i_rRegion).ConvertToB2DPolyPolygon() ); 1933 aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly ); 1934 1935 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationDiff( aThisPolyPoly, aOtherPolyPoly ); 1936 *this = Region( aClip ); 1937 } 1938 1939 sal_Bool Region::Exclude( const Region& rRegion ) 1940 { 1941 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1942 1943 if( rRegion.HasPolyPolygon() || HasPolyPolygon() ) 1944 { 1945 ImplExcludePolyPolygon( rRegion ); 1946 return sal_True; 1947 } 1948 1949 ImplPolyPolyRegionToBandRegion(); 1950 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); 1951 1952 // is region empty or null? -> nothing to do 1953 if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) ) 1954 return sal_True; 1955 1956 // no instance data? -> nothing to do 1957 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1958 return sal_True; 1959 1960 // no own instance data? -> make own copy! 1961 if ( mpImplRegion->mnRefCount > 1 ) 1962 ImplCopyData(); 1963 1964 // Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden 1965 ImplRegionBand* pBand = rRegion.mpImplRegion->mpFirstBand; 1966 while ( pBand ) 1967 { 1968 // insert bands if the boundaries are not allready in the list 1969 mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom ); 1970 1971 // process all elements of the list 1972 ImplRegionBandSep* pSep = pBand->mpFirstSep; 1973 while ( pSep ) 1974 { 1975 mpImplRegion->Exclude( pSep->mnXLeft, pBand->mnYTop, 1976 pSep->mnXRight, pBand->mnYBottom ); 1977 pSep = pSep->mpNextSep; 1978 } 1979 1980 // Wir optimieren schon in der Schleife, da wir davon 1981 // ausgehen, das wir insgesammt weniger Baender ueberpruefen 1982 // muessen 1983 if ( !mpImplRegion->OptimizeBandList() ) 1984 { 1985 delete mpImplRegion; 1986 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1987 break; 1988 } 1989 1990 pBand = pBand->mpNextBand; 1991 } 1992 1993 return sal_True; 1994 } 1995 1996 // ----------------------------------------------------------------------- 1997 void Region::ImplXOrPolyPolygon( const Region& i_rRegion ) 1998 { 1999 // get this B2DPolyPolygon 2000 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 2001 if( aThisPolyPoly.count() == 0 ) 2002 { 2003 *this = i_rRegion; 2004 return; 2005 } 2006 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 2007 2008 // get the other B2DPolyPolygon 2009 basegfx::B2DPolyPolygon aOtherPolyPoly( const_cast<Region&>(i_rRegion).ConvertToB2DPolyPolygon() ); 2010 aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly ); 2011 2012 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationXor( aThisPolyPoly, aOtherPolyPoly ); 2013 *this = Region( aClip ); 2014 } 2015 2016 sal_Bool Region::XOr( const Region& rRegion ) 2017 { 2018 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2019 2020 if( rRegion.HasPolyPolygon() || HasPolyPolygon() ) 2021 { 2022 ImplXOrPolyPolygon( rRegion ); 2023 return sal_True; 2024 } 2025 2026 ImplPolyPolyRegionToBandRegion(); 2027 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); 2028 2029 // is region empty or null? -> nothing to do 2030 if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) ) 2031 return sal_True; 2032 2033 // no own instance data? -> XOr = copy 2034 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2035 { 2036 *this = rRegion; 2037 return sal_True; 2038 } 2039 2040 // no own instance data? -> make own copy! 2041 if ( mpImplRegion->mnRefCount > 1 ) 2042 ImplCopyData(); 2043 2044 // Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden 2045 ImplRegionBand* pBand = rRegion.mpImplRegion->mpFirstBand; 2046 while ( pBand ) 2047 { 2048 // insert bands if the boundaries are not allready in the list 2049 mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom ); 2050 2051 // process all elements of the list 2052 ImplRegionBandSep* pSep = pBand->mpFirstSep; 2053 while ( pSep ) 2054 { 2055 mpImplRegion->XOr( pSep->mnXLeft, pBand->mnYTop, 2056 pSep->mnXRight, pBand->mnYBottom ); 2057 pSep = pSep->mpNextSep; 2058 } 2059 2060 pBand = pBand->mpNextBand; 2061 } 2062 2063 // cleanup 2064 if ( !mpImplRegion->OptimizeBandList() ) 2065 { 2066 delete mpImplRegion; 2067 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 2068 } 2069 2070 return sal_True; 2071 } 2072 2073 // ----------------------------------------------------------------------- 2074 2075 Rectangle Region::GetBoundRect() const 2076 { 2077 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2078 2079 Rectangle aRect; 2080 2081 // no internal data? -> region is empty! 2082 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2083 return aRect; 2084 2085 // PolyPolygon data im Imp structure? 2086 if ( mpImplRegion->mpPolyPoly ) 2087 return mpImplRegion->mpPolyPoly->GetBoundRect(); 2088 if( mpImplRegion->mpB2DPolyPoly ) 2089 { 2090 const basegfx::B2DRange aRange = basegfx::tools::getRange( *mpImplRegion->mpB2DPolyPoly ); 2091 aRect.SetPos( Point( (int)aRange.getMinX(), (int)aRange.getMinY() ) ); 2092 aRect.SetSize( Size( (int)aRange.getWidth(), (int)aRange.getHeight() ) ); 2093 return aRect; 2094 } 2095 2096 // no band in the list? -> region is empty! 2097 if ( !mpImplRegion->mpFirstBand ) 2098 return aRect; 2099 2100 // get the boundaries of the first band 2101 long nYTop = mpImplRegion->mpFirstBand->mnYTop; 2102 long nYBottom = mpImplRegion->mpFirstBand->mnYBottom; 2103 long nXLeft = mpImplRegion->mpFirstBand->GetXLeftBoundary(); 2104 long nXRight = mpImplRegion->mpFirstBand->GetXRightBoundary(); 2105 2106 // look in the band list (don't test first band again!) 2107 ImplRegionBand* pBand = mpImplRegion->mpFirstBand->mpNextBand; 2108 while ( pBand ) 2109 { 2110 nYBottom = pBand->mnYBottom; 2111 nXLeft = Min( nXLeft, pBand->GetXLeftBoundary() ); 2112 nXRight = Max( nXRight, pBand->GetXRightBoundary() ); 2113 2114 pBand = pBand->mpNextBand; 2115 } 2116 2117 // set rectangle 2118 aRect = Rectangle( nXLeft, nYTop, nXRight, nYBottom ); 2119 return aRect; 2120 } 2121 2122 // ----------------------------------------------------------------------- 2123 2124 sal_Bool Region::HasPolyPolygon() const 2125 { 2126 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2127 if( !mpImplRegion ) 2128 return false; 2129 if( mpImplRegion->mpPolyPoly ) 2130 return true; 2131 if( mpImplRegion->mpB2DPolyPoly ) 2132 return true; 2133 return false; 2134 } 2135 2136 // ----------------------------------------------------------------------- 2137 2138 PolyPolygon Region::GetPolyPolygon() const 2139 { 2140 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2141 2142 PolyPolygon aRet; 2143 2144 if( mpImplRegion->mpPolyPoly ) 2145 aRet = *mpImplRegion->mpPolyPoly; 2146 else if( mpImplRegion->mpB2DPolyPoly ) 2147 { 2148 // the polygon needs to be converted 2149 aRet = PolyPolygon( *mpImplRegion->mpB2DPolyPoly ); 2150 // TODO: cache the converted polygon? 2151 // mpImplRegion->mpB2DPolyPoly = aRet; 2152 } 2153 2154 return aRet; 2155 } 2156 2157 // ----------------------------------------------------------------------- 2158 2159 const basegfx::B2DPolyPolygon Region::GetB2DPolyPolygon() const 2160 { 2161 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2162 2163 basegfx::B2DPolyPolygon aRet; 2164 2165 if( mpImplRegion->mpB2DPolyPoly ) 2166 aRet = *mpImplRegion->mpB2DPolyPoly; 2167 else if( mpImplRegion->mpPolyPoly ) 2168 { 2169 // the polygon needs to be converted 2170 aRet = mpImplRegion->mpPolyPoly->getB2DPolyPolygon(); 2171 // TODO: cache the converted polygon? 2172 // mpImplRegion->mpB2DPolyPoly = aRet; 2173 } 2174 2175 return aRet; 2176 } 2177 2178 // ----------------------------------------------------------------------- 2179 2180 basegfx::B2DPolyPolygon Region::ConvertToB2DPolyPolygon() 2181 { 2182 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2183 2184 basegfx::B2DPolyPolygon aRet; 2185 2186 if( HasPolyPolygon() ) 2187 aRet = GetB2DPolyPolygon(); 2188 else 2189 { 2190 RegionHandle aHdl = BeginEnumRects(); 2191 Rectangle aSubRect; 2192 while( GetNextEnumRect( aHdl, aSubRect ) ) 2193 { 2194 basegfx::B2DPolygon aPoly( basegfx::tools::createPolygonFromRect( 2195 basegfx::B2DRectangle( aSubRect.Left(), aSubRect.Top(), aSubRect.Right(), aSubRect.Bottom() ) ) ); 2196 aRet.append( aPoly ); 2197 } 2198 EndEnumRects( aHdl ); 2199 } 2200 2201 return aRet; 2202 } 2203 2204 // ----------------------------------------------------------------------- 2205 2206 bool Region::ImplGetFirstRect( ImplRegionInfo& rImplRegionInfo, 2207 long& rX, long& rY, 2208 long& rWidth, long& rHeight ) const 2209 { 2210 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2211 2212 ((Region*)this)->ImplPolyPolyRegionToBandRegion(); 2213 2214 // no internal data? -> region is empty! 2215 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2216 return false; 2217 2218 // no band in the list? -> region is empty! 2219 if ( mpImplRegion->mpFirstBand == NULL ) 2220 return false; 2221 2222 // initialise pointer for first access 2223 ImplRegionBand* pCurrRectBand = mpImplRegion->mpFirstBand; 2224 ImplRegionBandSep* pCurrRectBandSep = pCurrRectBand->mpFirstSep; 2225 2226 DBG_ASSERT( pCurrRectBandSep != NULL, "Erstes Band wurde nicht optimiert." ); 2227 if ( !pCurrRectBandSep ) 2228 return false; 2229 2230 // get boundaries of current rectangle 2231 rX = pCurrRectBandSep->mnXLeft; 2232 rY = pCurrRectBand->mnYTop; 2233 rWidth = pCurrRectBandSep->mnXRight - pCurrRectBandSep->mnXLeft + 1; 2234 rHeight = pCurrRectBand->mnYBottom - pCurrRectBand->mnYTop + 1; 2235 2236 // save pointers 2237 rImplRegionInfo.mpVoidCurrRectBand = (void*)pCurrRectBand; 2238 rImplRegionInfo.mpVoidCurrRectBandSep = (void*)pCurrRectBandSep; 2239 2240 return true; 2241 } 2242 2243 // ----------------------------------------------------------------------- 2244 2245 bool Region::ImplGetNextRect( ImplRegionInfo& rImplRegionInfo, 2246 long& rX, long& rY, 2247 long& rWidth, long& rHeight ) const 2248 { 2249 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2250 2251 // no internal data? -> region is empty! 2252 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2253 return false; 2254 2255 // get last pointers 2256 ImplRegionBand* pCurrRectBand = (ImplRegionBand*)rImplRegionInfo.mpVoidCurrRectBand; 2257 ImplRegionBandSep* pCurrRectBandSep = (ImplRegionBandSep*)rImplRegionInfo.mpVoidCurrRectBandSep; 2258 2259 // get next separation from current band 2260 pCurrRectBandSep = pCurrRectBandSep->mpNextSep; 2261 2262 // no separation found? -> go to next band! 2263 if ( !pCurrRectBandSep ) 2264 { 2265 // get next band 2266 pCurrRectBand = pCurrRectBand->mpNextBand; 2267 2268 // no band found? -> not further rectangles! 2269 if( !pCurrRectBand ) 2270 return false; 2271 2272 // get first separation in current band 2273 pCurrRectBandSep = pCurrRectBand->mpFirstSep; 2274 } 2275 2276 // get boundaries of current rectangle 2277 rX = pCurrRectBandSep->mnXLeft; 2278 rY = pCurrRectBand->mnYTop; 2279 rWidth = pCurrRectBandSep->mnXRight - pCurrRectBandSep->mnXLeft + 1; 2280 rHeight = pCurrRectBand->mnYBottom - pCurrRectBand->mnYTop + 1; 2281 2282 // save new pointers 2283 rImplRegionInfo.mpVoidCurrRectBand = (void*)pCurrRectBand; 2284 rImplRegionInfo.mpVoidCurrRectBandSep = (void*)pCurrRectBandSep; 2285 2286 return true; 2287 } 2288 2289 // ----------------------------------------------------------------------- 2290 2291 RegionType Region::GetType() const 2292 { 2293 if ( mpImplRegion == &aImplEmptyRegion ) 2294 return REGION_EMPTY; 2295 else if ( mpImplRegion == &aImplNullRegion ) 2296 return REGION_NULL; 2297 else if ( mpImplRegion->mnRectCount == 1 ) 2298 return REGION_RECTANGLE; 2299 else 2300 return REGION_COMPLEX; 2301 } 2302 2303 // ----------------------------------------------------------------------- 2304 2305 sal_Bool Region::IsInside( const Point& rPoint ) const 2306 { 2307 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2308 2309 // PolyPolygon data im Imp structure? 2310 ((Region*)this)->ImplPolyPolyRegionToBandRegion(); 2311 /* 2312 if ( mpImplRegion->mpPolyPoly ) 2313 return mpImplRegion->mpPolyPoly->IsInside( rPoint ); 2314 */ 2315 2316 // no instance data? -> not inside 2317 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2318 return sal_False; 2319 2320 // search band list 2321 ImplRegionBand* pBand = mpImplRegion->mpFirstBand; 2322 while ( pBand ) 2323 { 2324 // is point within band? 2325 if ( (pBand->mnYTop <= rPoint.Y()) && 2326 (pBand->mnYBottom >= rPoint.Y()) ) 2327 { 2328 // is point within separation of the band? 2329 if ( pBand->IsInside( rPoint.X() ) ) 2330 return sal_True; 2331 else 2332 return sal_False; 2333 } 2334 2335 pBand = pBand->mpNextBand; 2336 } 2337 2338 return sal_False; 2339 } 2340 2341 // ----------------------------------------------------------------------- 2342 2343 sal_Bool Region::IsInside( const Rectangle& rRect ) const 2344 { 2345 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2346 2347 // is rectangle empty? -> not inside 2348 if ( rRect.IsEmpty() ) 2349 return sal_False; 2350 2351 // no instance data? -> not inside 2352 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2353 return sal_False; 2354 2355 // create region from rectangle and intersect own region 2356 Region aRegion = rRect; 2357 aRegion.Exclude( *this ); 2358 2359 // rectangle is inside if exclusion is empty 2360 return aRegion.IsEmpty(); 2361 } 2362 2363 // ----------------------------------------------------------------------- 2364 2365 sal_Bool Region::IsOver( const Rectangle& rRect ) const 2366 { 2367 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2368 2369 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2370 return sal_False; 2371 2372 // Can we optimize this ??? - is used in StarDraw for brushes pointers 2373 // Why we have no IsOver for Regions ??? 2374 // create region from rectangle and intersect own region 2375 Region aRegion = rRect; 2376 aRegion.Intersect( *this ); 2377 2378 // rectangle is over if include is not empty 2379 return !aRegion.IsEmpty(); 2380 } 2381 2382 // ----------------------------------------------------------------------- 2383 2384 void Region::SetNull() 2385 { 2386 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2387 2388 // statische Object haben RefCount von 0 2389 if ( mpImplRegion->mnRefCount ) 2390 { 2391 if ( mpImplRegion->mnRefCount > 1 ) 2392 mpImplRegion->mnRefCount--; 2393 else 2394 delete mpImplRegion; 2395 } 2396 2397 // set new type 2398 mpImplRegion = (ImplRegion*)(&aImplNullRegion); 2399 } 2400 2401 // ----------------------------------------------------------------------- 2402 2403 void Region::SetEmpty() 2404 { 2405 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2406 2407 // statische Object haben RefCount von 0 2408 if ( mpImplRegion->mnRefCount ) 2409 { 2410 if ( mpImplRegion->mnRefCount > 1 ) 2411 mpImplRegion->mnRefCount--; 2412 else 2413 delete mpImplRegion; 2414 } 2415 2416 // set new type 2417 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 2418 } 2419 2420 // ----------------------------------------------------------------------- 2421 2422 Region& Region::operator=( const Region& rRegion ) 2423 { 2424 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2425 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); 2426 DBG_ASSERT( rRegion.mpImplRegion->mnRefCount < 0xFFFFFFFE, "Region: RefCount overflow" ); 2427 2428 // Zuerst Referenzcounter erhoehen, damit man sich selbst zuweisen kann 2429 // RefCount == 0 fuer statische Objekte 2430 if ( rRegion.mpImplRegion->mnRefCount ) 2431 rRegion.mpImplRegion->mnRefCount++; 2432 2433 // statische Object haben RefCount von 0 2434 if ( mpImplRegion->mnRefCount ) 2435 { 2436 if ( mpImplRegion->mnRefCount > 1 ) 2437 mpImplRegion->mnRefCount--; 2438 else 2439 delete mpImplRegion; 2440 } 2441 2442 mpImplRegion = rRegion.mpImplRegion; 2443 return *this; 2444 } 2445 2446 // ----------------------------------------------------------------------- 2447 2448 Region& Region::operator=( const Rectangle& rRect ) 2449 { 2450 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2451 2452 // statische Object haben RefCount von 0 2453 if ( mpImplRegion->mnRefCount ) 2454 { 2455 if ( mpImplRegion->mnRefCount > 1 ) 2456 mpImplRegion->mnRefCount--; 2457 else 2458 delete mpImplRegion; 2459 } 2460 2461 ImplCreateRectRegion( rRect ); 2462 return *this; 2463 } 2464 2465 // ----------------------------------------------------------------------- 2466 2467 sal_Bool Region::operator==( const Region& rRegion ) const 2468 { 2469 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2470 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); 2471 2472 // reference to same object? -> equal! 2473 if ( mpImplRegion == rRegion.mpImplRegion ) 2474 return sal_True; 2475 2476 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2477 return sal_False; 2478 2479 if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) ) 2480 return sal_False; 2481 2482 if ( rRegion.mpImplRegion->mpPolyPoly && mpImplRegion->mpPolyPoly ) 2483 return *rRegion.mpImplRegion->mpPolyPoly == *mpImplRegion->mpPolyPoly; 2484 else 2485 { 2486 ((Region*)this)->ImplPolyPolyRegionToBandRegion(); 2487 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); 2488 2489 // Eine der beiden Regions kann jetzt Empty sein 2490 if ( mpImplRegion == rRegion.mpImplRegion ) 2491 return sal_True; 2492 2493 if ( mpImplRegion == &aImplEmptyRegion ) 2494 return sal_False; 2495 2496 if ( rRegion.mpImplRegion == &aImplEmptyRegion ) 2497 return sal_False; 2498 } 2499 2500 // initialise pointers 2501 ImplRegionBand* pOwnRectBand = mpImplRegion->mpFirstBand; 2502 ImplRegionBandSep* pOwnRectBandSep = pOwnRectBand->mpFirstSep; 2503 ImplRegionBand* pSecondRectBand = rRegion.mpImplRegion->mpFirstBand; 2504 ImplRegionBandSep* pSecondRectBandSep = pSecondRectBand->mpFirstSep; 2505 while ( pOwnRectBandSep && pSecondRectBandSep ) 2506 { 2507 // get boundaries of current rectangle 2508 long nOwnXLeft = pOwnRectBandSep->mnXLeft; 2509 long nSecondXLeft = pSecondRectBandSep->mnXLeft; 2510 if ( nOwnXLeft != nSecondXLeft ) 2511 return sal_False; 2512 2513 long nOwnYTop = pOwnRectBand->mnYTop; 2514 long nSecondYTop = pSecondRectBand->mnYTop; 2515 if ( nOwnYTop != nSecondYTop ) 2516 return sal_False; 2517 2518 long nOwnXRight = pOwnRectBandSep->mnXRight; 2519 long nSecondXRight = pSecondRectBandSep->mnXRight; 2520 if ( nOwnXRight != nSecondXRight ) 2521 return sal_False; 2522 2523 long nOwnYBottom = pOwnRectBand->mnYBottom; 2524 long nSecondYBottom = pSecondRectBand->mnYBottom; 2525 if ( nOwnYBottom != nSecondYBottom ) 2526 return sal_False; 2527 2528 // get next separation from current band 2529 pOwnRectBandSep = pOwnRectBandSep->mpNextSep; 2530 2531 // no separation found? -> go to next band! 2532 if ( !pOwnRectBandSep ) 2533 { 2534 // get next band 2535 pOwnRectBand = pOwnRectBand->mpNextBand; 2536 2537 // get first separation in current band 2538 if( pOwnRectBand ) 2539 pOwnRectBandSep = pOwnRectBand->mpFirstSep; 2540 } 2541 2542 // get next separation from current band 2543 pSecondRectBandSep = pSecondRectBandSep->mpNextSep; 2544 2545 // no separation found? -> go to next band! 2546 if ( !pSecondRectBandSep ) 2547 { 2548 // get next band 2549 pSecondRectBand = pSecondRectBand->mpNextBand; 2550 2551 // get first separation in current band 2552 if( pSecondRectBand ) 2553 pSecondRectBandSep = pSecondRectBand->mpFirstSep; 2554 } 2555 2556 if ( pOwnRectBandSep && !pSecondRectBandSep ) 2557 return sal_False; 2558 2559 if ( !pOwnRectBandSep && pSecondRectBandSep ) 2560 return sal_False; 2561 } 2562 2563 return sal_True; 2564 } 2565 2566 // ----------------------------------------------------------------------- 2567 2568 enum StreamEntryType { STREAMENTRY_BANDHEADER, STREAMENTRY_SEPARATION, STREAMENTRY_END }; 2569 2570 SvStream& operator>>( SvStream& rIStrm, Region& rRegion ) 2571 { 2572 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); 2573 2574 VersionCompat aCompat( rIStrm, STREAM_READ ); 2575 sal_uInt16 nVersion; 2576 sal_uInt16 nTmp16; 2577 2578 // statische Object haben RefCount von 0 2579 if ( rRegion.mpImplRegion->mnRefCount ) 2580 { 2581 if ( rRegion.mpImplRegion->mnRefCount > 1 ) 2582 rRegion.mpImplRegion->mnRefCount--; 2583 else 2584 delete rRegion.mpImplRegion; 2585 } 2586 2587 // get version of streamed region 2588 rIStrm >> nVersion; 2589 2590 // get type of region 2591 rIStrm >> nTmp16; 2592 2593 RegionType meStreamedType = (RegionType)nTmp16; 2594 2595 switch( meStreamedType ) 2596 { 2597 case REGION_NULL: 2598 rRegion.mpImplRegion = (ImplRegion*)&aImplNullRegion; 2599 break; 2600 2601 case REGION_EMPTY: 2602 rRegion.mpImplRegion = (ImplRegion*)&aImplEmptyRegion; 2603 break; 2604 2605 default: 2606 { 2607 // create instance of implementation class 2608 rRegion.mpImplRegion = new ImplRegion(); 2609 2610 // get header from first element 2611 rIStrm >> nTmp16; 2612 2613 // get all bands 2614 rRegion.mpImplRegion->mnRectCount = 0; 2615 ImplRegionBand* pCurrBand = NULL; 2616 while ( (StreamEntryType)nTmp16 != STREAMENTRY_END ) 2617 { 2618 // insert new band or new separation? 2619 if ( (StreamEntryType)nTmp16 == STREAMENTRY_BANDHEADER ) 2620 { 2621 long nYTop; 2622 long nYBottom; 2623 2624 rIStrm >> nYTop; 2625 rIStrm >> nYBottom; 2626 2627 // create band 2628 ImplRegionBand* pNewBand = new ImplRegionBand( nYTop, nYBottom ); 2629 2630 // first element? -> set as first into the list 2631 if ( !pCurrBand ) 2632 rRegion.mpImplRegion->mpFirstBand = pNewBand; 2633 else 2634 pCurrBand->mpNextBand = pNewBand; 2635 2636 // save pointer for next creation 2637 pCurrBand = pNewBand; 2638 } 2639 else 2640 { 2641 long nXLeft; 2642 long nXRight; 2643 2644 rIStrm >> nXLeft; 2645 rIStrm >> nXRight; 2646 2647 // add separation 2648 if ( pCurrBand ) 2649 { 2650 pCurrBand->Union( nXLeft, nXRight ); 2651 rRegion.mpImplRegion->mnRectCount++; 2652 } 2653 } 2654 2655 if( rIStrm.IsEof() ) 2656 { 2657 DBG_ERROR( "premature end of region stream" ); 2658 delete rRegion.mpImplRegion; 2659 rRegion.mpImplRegion = (ImplRegion*)&aImplEmptyRegion; 2660 return rIStrm; 2661 } 2662 2663 // get next header 2664 rIStrm >> nTmp16; 2665 } 2666 2667 if( aCompat.GetVersion() >= 2 ) 2668 { 2669 sal_Bool bHasPolyPolygon; 2670 2671 rIStrm >> bHasPolyPolygon; 2672 2673 if( bHasPolyPolygon ) 2674 { 2675 delete rRegion.mpImplRegion->mpPolyPoly; 2676 rRegion.mpImplRegion->mpPolyPoly = new PolyPolygon; 2677 rIStrm >> *( rRegion.mpImplRegion->mpPolyPoly ); 2678 } 2679 } 2680 } 2681 break; 2682 } 2683 2684 return rIStrm; 2685 } 2686 2687 // ----------------------------------------------------------------------- 2688 2689 SvStream& operator<<( SvStream& rOStrm, const Region& rRegion ) 2690 { 2691 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); 2692 2693 sal_uInt16 nVersion = 2; 2694 VersionCompat aCompat( rOStrm, STREAM_WRITE, nVersion ); 2695 Region aTmpRegion( rRegion ); 2696 2697 // use tmp region to avoid destruction of internal region (polypolygon) of rRegion 2698 aTmpRegion.ImplPolyPolyRegionToBandRegion(); 2699 2700 // put version 2701 rOStrm << nVersion; 2702 2703 // put type 2704 rOStrm << (sal_uInt16)aTmpRegion.GetType(); 2705 2706 // put all bands if not null or empty 2707 if ( (aTmpRegion.mpImplRegion != &aImplEmptyRegion) && (aTmpRegion.mpImplRegion != &aImplNullRegion) ) 2708 { 2709 ImplRegionBand* pBand = aTmpRegion.mpImplRegion->mpFirstBand; 2710 while ( pBand ) 2711 { 2712 // put boundaries 2713 rOStrm << (sal_uInt16) STREAMENTRY_BANDHEADER; 2714 rOStrm << pBand->mnYTop; 2715 rOStrm << pBand->mnYBottom; 2716 2717 // put separations of current band 2718 ImplRegionBandSep* pSep = pBand->mpFirstSep; 2719 while ( pSep ) 2720 { 2721 // put separation 2722 rOStrm << (sal_uInt16) STREAMENTRY_SEPARATION; 2723 rOStrm << pSep->mnXLeft; 2724 rOStrm << pSep->mnXRight; 2725 2726 // next separation from current band 2727 pSep = pSep->mpNextSep; 2728 } 2729 2730 pBand = pBand->mpNextBand; 2731 } 2732 2733 // put endmarker 2734 rOStrm << (sal_uInt16) STREAMENTRY_END; 2735 2736 // write polypolygon if available 2737 const sal_Bool bHasPolyPolygon = rRegion.HasPolyPolygon(); 2738 rOStrm << bHasPolyPolygon; 2739 2740 if( bHasPolyPolygon ) 2741 { 2742 // #i105373# 2743 PolyPolygon aNoCurvePolyPolygon; 2744 rRegion.GetPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon); 2745 2746 rOStrm << aNoCurvePolyPolygon; 2747 } 2748 } 2749 2750 return rOStrm; 2751 } 2752 2753 // ----------------------------------------------------------------------- 2754 2755 void Region::ImplBeginAddRect() 2756 { 2757 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2758 2759 // statische Object haben RefCount von 0 2760 if ( mpImplRegion->mnRefCount ) 2761 { 2762 if ( mpImplRegion->mnRefCount > 1 ) 2763 mpImplRegion->mnRefCount--; 2764 else 2765 delete mpImplRegion; 2766 } 2767 2768 // create fresh region 2769 mpImplRegion = new ImplRegion(); 2770 } 2771 2772 // ----------------------------------------------------------------------- 2773 2774 sal_Bool Region::ImplAddRect( const Rectangle& rRect ) 2775 { 2776 // Hier kein CheckThis, da nicht alle Daten auf Stand 2777 2778 if ( rRect.IsEmpty() ) 2779 return sal_True; 2780 2781 // get justified rectangle 2782 long nTop; 2783 long nBottom; 2784 long nLeft; 2785 long nRight; 2786 if ( rRect.Top() <= rRect.Bottom() ) 2787 { 2788 nTop = rRect.Top(); 2789 nBottom = rRect.Bottom(); 2790 } 2791 else 2792 { 2793 nTop = rRect.Bottom(); 2794 nBottom = rRect.Top(); 2795 } 2796 if ( rRect.Left() <= rRect.Right() ) 2797 { 2798 nLeft = rRect.Left(); 2799 nRight = rRect.Right(); 2800 } 2801 else 2802 { 2803 nLeft = rRect.Right(); 2804 nRight = rRect.Left(); 2805 } 2806 2807 if ( !mpImplRegion->mpLastCheckedBand ) 2808 { 2809 // create new band 2810 mpImplRegion->mpLastCheckedBand = new ImplRegionBand( nTop, nBottom ); 2811 2812 // set band as current 2813 mpImplRegion->mpFirstBand = mpImplRegion->mpLastCheckedBand; 2814 mpImplRegion->mpLastCheckedBand->Union( nLeft, nRight ); 2815 } 2816 else 2817 { 2818 DBG_ASSERT( nTop >= mpImplRegion->mpLastCheckedBand->mnYTop, 2819 "Region::ImplAddRect() - nTopY < nLastTopY" ); 2820 2821 // new band? create it! 2822 if ( (nTop != mpImplRegion->mpLastCheckedBand->mnYTop) || 2823 (nBottom != mpImplRegion->mpLastCheckedBand->mnYBottom) ) 2824 { 2825 // create new band 2826 ImplRegionBand* pNewRegionBand = new ImplRegionBand( nTop, nBottom ); 2827 2828 // append band to the end 2829 mpImplRegion->mpLastCheckedBand->mpNextBand = pNewRegionBand; 2830 2831 // skip to the new band 2832 mpImplRegion->mpLastCheckedBand = mpImplRegion->mpLastCheckedBand->mpNextBand; 2833 } 2834 2835 // Insert Sep 2836 mpImplRegion->mpLastCheckedBand->Union( nLeft, nRight ); 2837 } 2838 2839 return sal_True; 2840 } 2841 2842 // ----------------------------------------------------------------------- 2843 2844 void Region::ImplEndAddRect() 2845 { 2846 // check if we are empty 2847 if ( !mpImplRegion->mpFirstBand ) 2848 { 2849 delete mpImplRegion; 2850 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 2851 return; 2852 } 2853 2854 // check if we have somthing to optimize 2855 if ( !mpImplRegion->mpFirstBand->mpNextBand ) 2856 { 2857 // update mpImplRegion->mnRectCount, because no OptimizeBandList is called 2858 ImplRegionBandSep* pSep = mpImplRegion->mpFirstBand->mpFirstSep; 2859 mpImplRegion->mnRectCount = 0; 2860 while( pSep ) 2861 { 2862 mpImplRegion->mnRectCount++; 2863 pSep = pSep->mpNextSep; 2864 } 2865 2866 // Erst hier testen, da hier die Daten wieder stimmen 2867 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2868 return; 2869 } 2870 2871 // have to revert list? -> do it now! 2872 if ( mpImplRegion->mpFirstBand->mnYTop > 2873 mpImplRegion->mpFirstBand->mpNextBand->mnYTop ) 2874 { 2875 ImplRegionBand * pNewFirstRegionBand; 2876 2877 // initialize temp list with first element 2878 pNewFirstRegionBand = mpImplRegion->mpFirstBand; 2879 mpImplRegion->mpFirstBand = mpImplRegion->mpFirstBand->mpNextBand; 2880 pNewFirstRegionBand->mpNextBand = NULL; 2881 2882 // insert elements to the temp list 2883 while ( mpImplRegion->mpFirstBand ) 2884 { 2885 ImplRegionBand * pSavedRegionBand = pNewFirstRegionBand; 2886 pNewFirstRegionBand = mpImplRegion->mpFirstBand; 2887 mpImplRegion->mpFirstBand = mpImplRegion->mpFirstBand->mpNextBand; 2888 pNewFirstRegionBand->mpNextBand = pSavedRegionBand; 2889 } 2890 2891 // set temp list as new list 2892 mpImplRegion->mpFirstBand = pNewFirstRegionBand; 2893 } 2894 2895 // cleanup 2896 if ( !mpImplRegion->OptimizeBandList() ) 2897 { 2898 delete mpImplRegion; 2899 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 2900 } 2901 2902 // Erst hier testen, da hier die Daten wieder stimmen 2903 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2904 } 2905 2906 // ----------------------------------------------------------------------- 2907 2908 sal_uLong Region::GetRectCount() const 2909 { 2910 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2911 2912 ((Region*)this)->ImplPolyPolyRegionToBandRegion(); 2913 2914 #ifdef DBG_UTIL 2915 sal_uLong nCount = 0; 2916 2917 // all bands if not null or empty 2918 if ( (mpImplRegion != &aImplEmptyRegion) && (mpImplRegion != &aImplNullRegion) ) 2919 { 2920 ImplRegionBand* pBand = mpImplRegion->mpFirstBand; 2921 while ( pBand ) 2922 { 2923 ImplRegionBandSep* pSep = pBand->mpFirstSep; 2924 while( pSep ) 2925 { 2926 nCount++; 2927 pSep = pSep->mpNextSep; 2928 } 2929 2930 pBand = pBand->mpNextBand; 2931 } 2932 } 2933 2934 DBG_ASSERT( mpImplRegion->mnRectCount == nCount, "Region: invalid mnRectCount!" ); 2935 #endif 2936 2937 return mpImplRegion->mnRectCount; 2938 } 2939 2940 // ----------------------------------------------------------------------- 2941 2942 RegionHandle Region::BeginEnumRects() 2943 { 2944 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2945 2946 ImplPolyPolyRegionToBandRegion(); 2947 2948 // no internal data? -> region is empty! 2949 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2950 return 0; 2951 2952 // no band in the list? -> region is empty! 2953 if ( mpImplRegion->mpFirstBand == NULL ) 2954 { 2955 DBG_ASSERT( mpImplRegion->mpFirstBand, "Region::BeginEnumRects() First Band is Empty!" ); 2956 return 0; 2957 } 2958 2959 ImplRegionHandle* pData = new ImplRegionHandle; 2960 pData->mpRegion = new Region( *this ); 2961 pData->mbFirst = sal_True; 2962 2963 // save pointers 2964 pData->mpCurrRectBand = pData->mpRegion->mpImplRegion->mpFirstBand; 2965 pData->mpCurrRectBandSep = pData->mpCurrRectBand->mpFirstSep; 2966 2967 return (RegionHandle)pData; 2968 } 2969 2970 // ----------------------------------------------------------------------- 2971 2972 sal_Bool Region::GetEnumRects( RegionHandle pVoidData, Rectangle& rRect ) 2973 { 2974 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2975 2976 ImplRegionHandle* pData = (ImplRegionHandle*)pVoidData; 2977 if ( !pData ) 2978 return sal_False; 2979 2980 if ( pData->mbFirst ) 2981 pData->mbFirst = sal_False; 2982 else 2983 { 2984 // get next separation from current band 2985 pData->mpCurrRectBandSep = pData->mpCurrRectBandSep->mpNextSep; 2986 2987 // no separation found? -> go to next band! 2988 if ( !pData->mpCurrRectBandSep ) 2989 { 2990 // get next band 2991 pData->mpCurrRectBand = pData->mpCurrRectBand->mpNextBand; 2992 2993 // no band found? -> not further rectangles! 2994 if ( !pData->mpCurrRectBand ) 2995 return sal_False; 2996 2997 // get first separation in current band 2998 pData->mpCurrRectBandSep = pData->mpCurrRectBand->mpFirstSep; 2999 } 3000 } 3001 3002 // get boundaries of current rectangle 3003 rRect.Top() = pData->mpCurrRectBand->mnYTop; 3004 rRect.Bottom() = pData->mpCurrRectBand->mnYBottom; 3005 rRect.Left() = pData->mpCurrRectBandSep->mnXLeft; 3006 rRect.Right() = pData->mpCurrRectBandSep->mnXRight; 3007 return sal_True; 3008 } 3009 3010 // ----------------------------------------------------------------------- 3011 3012 void Region::EndEnumRects( RegionHandle pVoidData ) 3013 { 3014 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 3015 3016 ImplRegionHandle* pData = (ImplRegionHandle*)pVoidData; 3017 if ( !pData ) 3018 return; 3019 3020 // cleanup 3021 delete pData->mpRegion; 3022 delete pData; 3023 } 3024 3025 // ----------------------------------------------------------------------- 3026 3027 static inline bool ImplPolygonRectTest( const Polygon& rPoly, Rectangle* pRectOut = NULL ) 3028 { 3029 bool bIsRect = false; 3030 const Point* pPoints = rPoly.GetConstPointAry(); 3031 sal_uInt16 nPoints = rPoly.GetSize(); 3032 if( nPoints == 4 || (nPoints == 5 && pPoints[0] == pPoints[4]) ) 3033 { 3034 long nX1 = pPoints[0].X(), nX2 = pPoints[2].X(), 3035 nY1 = pPoints[0].Y(), nY2 = pPoints[2].Y(); 3036 if( ( (pPoints[1].X() == nX1 && pPoints[3].X() == nX2) && 3037 (pPoints[1].Y() == nY2 && pPoints[3].Y() == nY1) ) 3038 || 3039 ( (pPoints[1].X() == nX2 && pPoints[3].X() == nX1) && 3040 (pPoints[1].Y() == nY1 && pPoints[3].Y() == nY2) ) ) 3041 { 3042 bIsRect = true; 3043 if( pRectOut ) 3044 { 3045 long nSwap; 3046 if( nX2 < nX1 ) 3047 { 3048 nSwap = nX2; 3049 nX2 = nX1; 3050 nX1 = nSwap; 3051 } 3052 if( nY2 < nY1 ) 3053 { 3054 nSwap = nY2; 3055 nY2 = nY1; 3056 nY1 = nSwap; 3057 } 3058 if( nX2 != nX1 ) 3059 nX2--; 3060 if( nY2 != nY1 ) 3061 nY2--; 3062 pRectOut->Left() = nX1; 3063 pRectOut->Right() = nX2; 3064 pRectOut->Top() = nY1; 3065 pRectOut->Bottom() = nY2; 3066 } 3067 } 3068 } 3069 return bIsRect; 3070 } 3071 3072 Region Region::GetRegionFromPolyPolygon( const PolyPolygon& rPolyPoly ) 3073 { 3074 //return Region( rPolyPoly ); 3075 3076 // check if it's worth extracting the XOr'ing the Rectangles 3077 // empiricism shows that break even between XOr'ing rectangles separately 3078 // and ImplPolyPolyRegionToBandRegion is at half rectangles/half polygons 3079 int nPolygonRects = 0, nPolygonPolygons = 0; 3080 int nPolygons = rPolyPoly.Count(); 3081 3082 for( sal_uInt16 i = 0; i < nPolygons; i++ ) 3083 { 3084 const Polygon& rPoly = rPolyPoly[i]; 3085 if( ImplPolygonRectTest( rPoly ) ) 3086 nPolygonRects++; 3087 else 3088 nPolygonPolygons++; 3089 } 3090 if( nPolygonPolygons > nPolygonRects ) 3091 return Region( rPolyPoly ); 3092 3093 Region aResult; 3094 Rectangle aRect; 3095 for( sal_uInt16 i = 0; i < nPolygons; i++ ) 3096 { 3097 const Polygon& rPoly = rPolyPoly[i]; 3098 if( ImplPolygonRectTest( rPoly, &aRect ) ) 3099 aResult.XOr( aRect ); 3100 else 3101 aResult.XOr( Region(rPoly) ); 3102 } 3103 return aResult; 3104 } 3105