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