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 ImplCreatePolyPolyRegion( 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::ImplCreatePolyPolyRegion( const basegfx::B2DPolyPolygon& rPolyPoly ) 1230 { 1231 if (rPolyPoly.count()==0 || rPolyPoly.getB2DRange().isEmpty()) 1232 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1233 else 1234 mpImplRegion = new ImplRegion( rPolyPoly ); 1235 } 1236 1237 // ----------------------------------------------------------------------- 1238 1239 void Region::ImplPolyPolyRegionToBandRegionFunc() 1240 { 1241 // ensure to subdivide when bezier segemnts are used, it's going to 1242 // be expanded to rectangles 1243 PolyPolygon aPolyPoly; 1244 GetPolyPolygon().AdaptiveSubdivide(aPolyPoly); 1245 1246 if ( mpImplRegion->mnRefCount > 1 ) 1247 mpImplRegion->mnRefCount--; 1248 else 1249 delete mpImplRegion; 1250 1251 if ( aPolyPoly.Count() ) 1252 { 1253 // polypolygon empty? -> empty region 1254 const Rectangle aRect( aPolyPoly.GetBoundRect() ); 1255 1256 if ( !aRect.IsEmpty() ) 1257 { 1258 if (ImplIsPolygonRectilinear(aPolyPoly)) 1259 { 1260 // For rectilinear polygons there is an optimized band conversion. 1261 mpImplRegion = ImplRectilinearPolygonToBands(aPolyPoly); 1262 } 1263 else 1264 { 1265 mpImplRegion = ImplGeneralPolygonToBands(aPolyPoly, aRect); 1266 } 1267 1268 // Convert points into seps. 1269 ImplRegionBand* pRegionBand = mpImplRegion->mpFirstBand; 1270 while ( pRegionBand ) 1271 { 1272 // generate separations from the lines and process union 1273 pRegionBand->ProcessPoints(); 1274 pRegionBand = pRegionBand->mpNextBand; 1275 } 1276 1277 // Optimize list of bands. Adjacent bands with identical lists 1278 // of seps are joined. 1279 if ( !mpImplRegion->OptimizeBandList() ) 1280 { 1281 delete mpImplRegion; 1282 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1283 } 1284 } 1285 else 1286 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1287 } 1288 else 1289 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1290 } 1291 1292 // ----------------------------------------------------------------------- 1293 1294 void Region::Move( long nHorzMove, long nVertMove ) 1295 { 1296 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1297 1298 // no region data? -> nothing to do 1299 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1300 return; 1301 1302 // no own instance data? -> make own copy! 1303 if ( mpImplRegion->mnRefCount > 1 ) 1304 ImplCopyData(); 1305 1306 if ( mpImplRegion->mpPolyPoly ) 1307 mpImplRegion->mpPolyPoly->Move( nHorzMove, nVertMove ); 1308 else if( mpImplRegion->mpB2DPolyPoly ) 1309 { 1310 mpImplRegion->mpB2DPolyPoly->transform(basegfx::tools::createTranslateB2DHomMatrix(nHorzMove, nVertMove)); 1311 } 1312 else 1313 { 1314 ImplRegionBand* pBand = mpImplRegion->mpFirstBand; 1315 while ( pBand ) 1316 { 1317 // process the vertical move 1318 if ( nVertMove != 0) 1319 { 1320 pBand->mnYTop = pBand->mnYTop + nVertMove; 1321 pBand->mnYBottom = pBand->mnYBottom + nVertMove; 1322 } 1323 1324 // process the horizontal move 1325 if ( nHorzMove != 0) 1326 pBand->MoveX( nHorzMove ); 1327 1328 pBand = pBand->mpNextBand; 1329 } 1330 } 1331 } 1332 1333 // ----------------------------------------------------------------------- 1334 1335 void Region::Scale( double fScaleX, double fScaleY ) 1336 { 1337 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1338 1339 // no region data? -> nothing to do 1340 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1341 return; 1342 1343 // no own instance data? -> make own copy! 1344 if ( mpImplRegion->mnRefCount > 1 ) 1345 ImplCopyData(); 1346 1347 if ( mpImplRegion->mpPolyPoly ) 1348 mpImplRegion->mpPolyPoly->Scale( fScaleX, fScaleY ); 1349 else if( mpImplRegion->mpB2DPolyPoly ) 1350 { 1351 mpImplRegion->mpB2DPolyPoly->transform(basegfx::tools::createScaleB2DHomMatrix(fScaleX, fScaleY)); 1352 } 1353 else 1354 { 1355 ImplRegionBand* pBand = mpImplRegion->mpFirstBand; 1356 while ( pBand ) 1357 { 1358 // process the vertical move 1359 if ( fScaleY != 0.0 ) 1360 { 1361 pBand->mnYTop = FRound( pBand->mnYTop * fScaleY ); 1362 pBand->mnYBottom = FRound( pBand->mnYBottom * fScaleY ); 1363 } 1364 1365 // process the horizontal move 1366 if ( fScaleX != 0.0 ) 1367 pBand->ScaleX( fScaleX ); 1368 1369 pBand = pBand->mpNextBand; 1370 } 1371 } 1372 } 1373 1374 // ----------------------------------------------------------------------- 1375 1376 sal_Bool Region::Union( const Rectangle& rRect ) 1377 { 1378 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1379 1380 // is rectangle empty? -> nothing to do 1381 if ( rRect.IsEmpty() ) 1382 return sal_True; 1383 1384 if( HasPolyPolygon() ) 1385 { 1386 // get this B2DPolyPolygon 1387 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 1388 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 1389 1390 if( aThisPolyPoly.count() == 0 ) 1391 { 1392 *this = rRect; 1393 return true; 1394 } 1395 1396 // get the other B2DPolyPolygon 1397 basegfx::B2DPolygon aRectPoly( basegfx::tools::createPolygonFromRect( basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); 1398 basegfx::B2DPolyPolygon aOtherPolyPoly( aRectPoly ); 1399 1400 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationOr( aThisPolyPoly, aOtherPolyPoly ); 1401 *this = Region( aClip ); 1402 1403 return sal_True; 1404 } 1405 1406 ImplPolyPolyRegionToBandRegion(); 1407 1408 // no instance data? -> create! 1409 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1410 mpImplRegion = new ImplRegion(); 1411 1412 // no own instance data? -> make own copy! 1413 if ( mpImplRegion->mnRefCount > 1 ) 1414 ImplCopyData(); 1415 1416 // get justified rectangle 1417 long nLeft = Min( rRect.Left(), rRect.Right() ); 1418 long nTop = Min( rRect.Top(), rRect.Bottom() ); 1419 long nRight = Max( rRect.Left(), rRect.Right() ); 1420 long nBottom = Max( rRect.Top(), rRect.Bottom() ); 1421 1422 // insert bands if the boundaries are not allready in the list 1423 mpImplRegion->InsertBands( nTop, nBottom ); 1424 1425 // process union 1426 mpImplRegion->Union( nLeft, nTop, nRight, nBottom ); 1427 1428 // cleanup 1429 if ( !mpImplRegion->OptimizeBandList() ) 1430 { 1431 delete mpImplRegion; 1432 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1433 } 1434 1435 return sal_True; 1436 } 1437 1438 // ----------------------------------------------------------------------- 1439 1440 sal_Bool Region::Intersect( const Rectangle& rRect ) 1441 { 1442 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1443 1444 // is rectangle empty? -> nothing to do 1445 if ( rRect.IsEmpty() ) 1446 { 1447 // statische Object haben RefCount von 0 1448 if ( mpImplRegion->mnRefCount ) 1449 { 1450 if ( mpImplRegion->mnRefCount > 1 ) 1451 mpImplRegion->mnRefCount--; 1452 else 1453 delete mpImplRegion; 1454 } 1455 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1456 return sal_True; 1457 } 1458 1459 // #103137# Avoid banding for special cases 1460 if ( mpImplRegion->mpPolyPoly ) 1461 { 1462 // #127431# make ImplRegion unique, if not already. 1463 if( mpImplRegion->mnRefCount > 1 ) 1464 { 1465 mpImplRegion->mnRefCount--; 1466 mpImplRegion = new ImplRegion( *mpImplRegion->mpPolyPoly ); 1467 } 1468 1469 // use the PolyPolygon::Clip method for rectangles, this is 1470 // fairly simple (does not even use GPC) and saves us from 1471 // unnecessary banding 1472 mpImplRegion->mpPolyPoly->Clip( rRect ); 1473 1474 // The clipping above may lead to empty ClipRegion 1475 if(!mpImplRegion->mpPolyPoly->Count()) 1476 { 1477 // react on empty ClipRegion; ImplRegion already is unique (see above) 1478 delete mpImplRegion; 1479 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1480 } 1481 1482 return sal_True; 1483 } 1484 else if( mpImplRegion->mpB2DPolyPoly ) 1485 { 1486 // #127431# make ImplRegion unique, if not already. 1487 if( mpImplRegion->mnRefCount > 1 ) 1488 { 1489 mpImplRegion->mnRefCount--; 1490 mpImplRegion = new ImplRegion( *mpImplRegion->mpB2DPolyPoly ); 1491 } 1492 1493 *mpImplRegion->mpB2DPolyPoly = 1494 basegfx::tools::clipPolyPolygonOnRange( 1495 *mpImplRegion->mpB2DPolyPoly, 1496 basegfx::B2DRange( 1497 rRect.Left(), 1498 rRect.Top(), 1499 rRect.Right() + 1, 1500 rRect.Bottom() + 1), 1501 true, 1502 false); 1503 1504 // The clipping above may lead to empty ClipRegion 1505 if(!mpImplRegion->mpB2DPolyPoly->count()) 1506 { 1507 // react on empty ClipRegion; ImplRegion already is unique (see above) 1508 delete mpImplRegion; 1509 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1510 } 1511 1512 return sal_True; 1513 } 1514 else 1515 ImplPolyPolyRegionToBandRegion(); 1516 1517 // is region empty? -> nothing to do! 1518 if ( mpImplRegion == &aImplEmptyRegion ) 1519 return sal_True; 1520 1521 // get justified rectangle 1522 long nLeft = Min( rRect.Left(), rRect.Right() ); 1523 long nTop = Min( rRect.Top(), rRect.Bottom() ); 1524 long nRight = Max( rRect.Left(), rRect.Right() ); 1525 long nBottom = Max( rRect.Top(), rRect.Bottom() ); 1526 1527 // is own region NULL-region? -> copy data! 1528 if ( mpImplRegion == &aImplNullRegion ) 1529 { 1530 // create instance of implementation class 1531 mpImplRegion = new ImplRegion(); 1532 1533 // add band with boundaries of the rectangle 1534 mpImplRegion->mpFirstBand = new ImplRegionBand( nTop, nBottom ); 1535 1536 // Set left and right boundaries of the band 1537 mpImplRegion->mpFirstBand->Union( nLeft, nRight ); 1538 mpImplRegion->mnRectCount = 1; 1539 1540 return sal_True; 1541 } 1542 1543 // no own instance data? -> make own copy! 1544 if ( mpImplRegion->mnRefCount > 1 ) 1545 ImplCopyData(); 1546 1547 // insert bands if the boundaries are not allready in the list 1548 mpImplRegion->InsertBands( nTop, nBottom ); 1549 1550 // process intersections 1551 ImplRegionBand* pPrevBand = 0; 1552 ImplRegionBand* pBand = mpImplRegion->mpFirstBand; 1553 while ( pBand ) 1554 { 1555 // band within intersection boundary? -> process. otherwise remove 1556 if ( (pBand->mnYTop >= nTop) && 1557 (pBand->mnYBottom <= nBottom) ) 1558 { 1559 // process intersection 1560 pBand->Intersect( nLeft, nRight ); 1561 1562 pPrevBand = pBand; 1563 pBand = pBand->mpNextBand; 1564 } 1565 else 1566 { 1567 ImplRegionBand* pOldBand = pBand; 1568 if ( pBand == mpImplRegion->mpFirstBand ) 1569 mpImplRegion->mpFirstBand = pBand->mpNextBand; 1570 else 1571 pPrevBand->mpNextBand = pBand->mpNextBand; 1572 pBand = pBand->mpNextBand; 1573 delete pOldBand; 1574 } 1575 } 1576 1577 // cleanup 1578 if ( !mpImplRegion->OptimizeBandList() ) 1579 { 1580 delete mpImplRegion; 1581 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1582 } 1583 1584 return sal_True; 1585 } 1586 1587 // ----------------------------------------------------------------------- 1588 1589 sal_Bool Region::Exclude( const Rectangle& rRect ) 1590 { 1591 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1592 1593 // is rectangle empty? -> nothing to do 1594 if ( rRect.IsEmpty() ) 1595 return sal_True; 1596 1597 if( HasPolyPolygon() ) 1598 { 1599 // get this B2DPolyPolygon 1600 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 1601 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 1602 1603 if( aThisPolyPoly.count() == 0 ) 1604 return sal_True; 1605 1606 // get the other B2DPolyPolygon 1607 basegfx::B2DPolygon aRectPoly( basegfx::tools::createPolygonFromRect( basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); 1608 basegfx::B2DPolyPolygon aOtherPolyPoly( aRectPoly ); 1609 1610 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationDiff( aThisPolyPoly, aOtherPolyPoly ); 1611 *this = Region( aClip ); 1612 1613 return sal_True; 1614 } 1615 1616 ImplPolyPolyRegionToBandRegion(); 1617 1618 // no instance data? -> create! 1619 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1620 return sal_True; 1621 1622 // no own instance data? -> make own copy! 1623 if ( mpImplRegion->mnRefCount > 1 ) 1624 ImplCopyData(); 1625 1626 // get justified rectangle 1627 long nLeft = Min( rRect.Left(), rRect.Right() ); 1628 long nTop = Min( rRect.Top(), rRect.Bottom() ); 1629 long nRight = Max( rRect.Left(), rRect.Right() ); 1630 long nBottom = Max( rRect.Top(), rRect.Bottom() ); 1631 1632 // insert bands if the boundaries are not allready in the list 1633 mpImplRegion->InsertBands( nTop, nBottom ); 1634 1635 // process exclude 1636 mpImplRegion->Exclude( nLeft, nTop, nRight, nBottom ); 1637 1638 // cleanup 1639 if ( !mpImplRegion->OptimizeBandList() ) 1640 { 1641 delete mpImplRegion; 1642 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1643 } 1644 1645 return sal_True; 1646 } 1647 1648 // ----------------------------------------------------------------------- 1649 1650 sal_Bool Region::XOr( const Rectangle& rRect ) 1651 { 1652 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1653 1654 // is rectangle empty? -> nothing to do 1655 if ( rRect.IsEmpty() ) 1656 return sal_True; 1657 1658 if( HasPolyPolygon() ) 1659 { 1660 // get this B2DPolyPolygon 1661 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 1662 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 1663 1664 if( aThisPolyPoly.count() == 0 ) 1665 { 1666 *this = rRect; 1667 return sal_True; 1668 } 1669 1670 // get the other B2DPolyPolygon 1671 basegfx::B2DPolygon aRectPoly( basegfx::tools::createPolygonFromRect( basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); 1672 basegfx::B2DPolyPolygon aOtherPolyPoly( aRectPoly ); 1673 1674 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationXor( aThisPolyPoly, aOtherPolyPoly ); 1675 *this = Region( aClip ); 1676 1677 return sal_True; 1678 } 1679 1680 ImplPolyPolyRegionToBandRegion(); 1681 1682 // no instance data? -> create! 1683 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1684 mpImplRegion = new ImplRegion(); 1685 1686 // no own instance data? -> make own copy! 1687 if ( mpImplRegion->mnRefCount > 1 ) 1688 ImplCopyData(); 1689 1690 // get justified rectangle 1691 long nLeft = Min( rRect.Left(), rRect.Right() ); 1692 long nTop = Min( rRect.Top(), rRect.Bottom() ); 1693 long nRight = Max( rRect.Left(), rRect.Right() ); 1694 long nBottom = Max( rRect.Top(), rRect.Bottom() ); 1695 1696 // insert bands if the boundaries are not allready in the list 1697 mpImplRegion->InsertBands( nTop, nBottom ); 1698 1699 // process xor 1700 mpImplRegion->XOr( nLeft, nTop, nRight, nBottom ); 1701 1702 // cleanup 1703 if ( !mpImplRegion->OptimizeBandList() ) 1704 { 1705 delete mpImplRegion; 1706 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1707 } 1708 1709 return sal_True; 1710 } 1711 1712 // ----------------------------------------------------------------------- 1713 void Region::ImplUnionPolyPolygon( const Region& i_rRegion ) 1714 { 1715 // get this B2DPolyPolygon 1716 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 1717 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 1718 1719 if( aThisPolyPoly.count() == 0 ) 1720 { 1721 *this = i_rRegion; 1722 return; 1723 } 1724 1725 // get the other B2DPolyPolygon 1726 basegfx::B2DPolyPolygon aOtherPolyPoly( const_cast<Region&>(i_rRegion).ConvertToB2DPolyPolygon() ); 1727 aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly ); 1728 1729 1730 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationOr( aThisPolyPoly, aOtherPolyPoly ); 1731 1732 *this = Region( aClip ); 1733 } 1734 1735 sal_Bool Region::Union( const Region& rRegion ) 1736 { 1737 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1738 1739 if( rRegion.HasPolyPolygon() || HasPolyPolygon() ) 1740 { 1741 ImplUnionPolyPolygon( rRegion ); 1742 return sal_True; 1743 } 1744 1745 ImplPolyPolyRegionToBandRegion(); 1746 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); 1747 1748 // is region empty or null? -> nothing to do 1749 if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) ) 1750 return sal_True; 1751 1752 // no instance data? -> create! 1753 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1754 mpImplRegion = new ImplRegion(); 1755 1756 // no own instance data? -> make own copy! 1757 if ( mpImplRegion->mnRefCount > 1 ) 1758 ImplCopyData(); 1759 1760 // Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden 1761 ImplRegionBand* pBand = rRegion.mpImplRegion->mpFirstBand; 1762 while ( pBand ) 1763 { 1764 // insert bands if the boundaries are not allready in the list 1765 mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom ); 1766 1767 // process all elements of the list 1768 ImplRegionBandSep* pSep = pBand->mpFirstSep; 1769 while ( pSep ) 1770 { 1771 mpImplRegion->Union( pSep->mnXLeft, pBand->mnYTop, 1772 pSep->mnXRight, pBand->mnYBottom ); 1773 pSep = pSep->mpNextSep; 1774 } 1775 1776 pBand = pBand->mpNextBand; 1777 } 1778 1779 // cleanup 1780 if ( !mpImplRegion->OptimizeBandList() ) 1781 { 1782 delete mpImplRegion; 1783 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1784 } 1785 1786 return sal_True; 1787 } 1788 1789 // ----------------------------------------------------------------------- 1790 void Region::ImplIntersectWithPolyPolygon( const Region& i_rRegion ) 1791 { 1792 // get this B2DPolyPolygon 1793 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 1794 if( aThisPolyPoly.count() == 0 ) 1795 { 1796 *this = i_rRegion; 1797 return; 1798 } 1799 1800 // get the other B2DPolyPolygon 1801 basegfx::B2DPolyPolygon aOtherPolyPoly( const_cast<Region&>(i_rRegion).ConvertToB2DPolyPolygon() ); 1802 1803 basegfx::B2DPolyPolygon aClip = basegfx::tools::clipPolyPolygonOnPolyPolygon( aOtherPolyPoly, aThisPolyPoly, true, false ); 1804 *this = Region( aClip ); 1805 } 1806 1807 sal_Bool Region::Intersect( const Region& rRegion ) 1808 { 1809 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1810 1811 // same instance data? -> nothing to do! 1812 if ( mpImplRegion == rRegion.mpImplRegion ) 1813 return sal_True; 1814 1815 if( rRegion.HasPolyPolygon() || HasPolyPolygon() ) 1816 { 1817 ImplIntersectWithPolyPolygon( rRegion ); 1818 return sal_True; 1819 } 1820 1821 ImplPolyPolyRegionToBandRegion(); 1822 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); 1823 1824 if ( mpImplRegion == &aImplEmptyRegion ) 1825 return sal_True; 1826 1827 // is region null? -> nothing to do 1828 if ( rRegion.mpImplRegion == &aImplNullRegion ) 1829 return sal_True; 1830 1831 // is rectangle empty? -> nothing to do 1832 if ( rRegion.mpImplRegion == &aImplEmptyRegion ) 1833 { 1834 // statische Object haben RefCount von 0 1835 if ( mpImplRegion->mnRefCount ) 1836 { 1837 if ( mpImplRegion->mnRefCount > 1 ) 1838 mpImplRegion->mnRefCount--; 1839 else 1840 delete mpImplRegion; 1841 } 1842 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1843 return sal_True; 1844 } 1845 1846 // is own region NULL-region? -> copy data! 1847 if ( mpImplRegion == &aImplNullRegion) 1848 { 1849 mpImplRegion = rRegion.mpImplRegion; 1850 rRegion.mpImplRegion->mnRefCount++; 1851 return sal_True; 1852 } 1853 1854 // Wenn wir weniger Rechtecke haben, drehen wir den Intersect-Aufruf um 1855 if ( mpImplRegion->mnRectCount+2 < rRegion.mpImplRegion->mnRectCount ) 1856 { 1857 Region aTempRegion = rRegion; 1858 aTempRegion.Intersect( *this ); 1859 *this = aTempRegion; 1860 } 1861 else 1862 { 1863 // no own instance data? -> make own copy! 1864 if ( mpImplRegion->mnRefCount > 1 ) 1865 ImplCopyData(); 1866 1867 // mark all bands as untouched 1868 ImplRegionBand* pBand = mpImplRegion->mpFirstBand; 1869 while ( pBand ) 1870 { 1871 pBand->mbTouched = sal_False; 1872 pBand = pBand->mpNextBand; 1873 } 1874 1875 pBand = rRegion.mpImplRegion->mpFirstBand; 1876 while ( pBand ) 1877 { 1878 // insert bands if the boundaries are not allready in the list 1879 mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom ); 1880 1881 // process all elements of the list 1882 ImplRegionBandSep* pSep = pBand->mpFirstSep; 1883 while ( pSep ) 1884 { 1885 // left boundary? 1886 if ( pSep == pBand->mpFirstSep ) 1887 { 1888 // process intersection and do not remove untouched bands 1889 mpImplRegion->Exclude( LONG_MIN+1, pBand->mnYTop, 1890 pSep->mnXLeft-1, pBand->mnYBottom ); 1891 } 1892 1893 // right boundary? 1894 if ( pSep->mpNextSep == NULL ) 1895 { 1896 // process intersection and do not remove untouched bands 1897 mpImplRegion->Exclude( pSep->mnXRight+1, pBand->mnYTop, 1898 LONG_MAX-1, pBand->mnYBottom ); 1899 } 1900 else 1901 { 1902 // process intersection and do not remove untouched bands 1903 mpImplRegion->Exclude( pSep->mnXRight+1, pBand->mnYTop, 1904 pSep->mpNextSep->mnXLeft-1, pBand->mnYBottom ); 1905 } 1906 1907 pSep = pSep->mpNextSep; 1908 } 1909 1910 pBand = pBand->mpNextBand; 1911 } 1912 1913 // remove all untouched bands if bands allready left 1914 ImplRegionBand* pPrevBand = 0; 1915 pBand = mpImplRegion->mpFirstBand; 1916 while ( pBand ) 1917 { 1918 if ( !pBand->mbTouched ) 1919 { 1920 // save pointer 1921 ImplRegionBand* pOldBand = pBand; 1922 1923 // previous element of the list 1924 if ( pBand == mpImplRegion->mpFirstBand ) 1925 mpImplRegion->mpFirstBand = pBand->mpNextBand; 1926 else 1927 pPrevBand->mpNextBand = pBand->mpNextBand; 1928 1929 pBand = pBand->mpNextBand; 1930 delete pOldBand; 1931 } 1932 else 1933 { 1934 pPrevBand = pBand; 1935 pBand = pBand->mpNextBand; 1936 } 1937 } 1938 1939 // cleanup 1940 if ( !mpImplRegion->OptimizeBandList() ) 1941 { 1942 delete mpImplRegion; 1943 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 1944 } 1945 } 1946 1947 return sal_True; 1948 } 1949 1950 // ----------------------------------------------------------------------- 1951 void Region::ImplExcludePolyPolygon( const Region& i_rRegion ) 1952 { 1953 // get this B2DPolyPolygon 1954 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 1955 if( aThisPolyPoly.count() == 0 ) 1956 return; 1957 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 1958 1959 // get the other B2DPolyPolygon 1960 basegfx::B2DPolyPolygon aOtherPolyPoly( const_cast<Region&>(i_rRegion).ConvertToB2DPolyPolygon() ); 1961 aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly ); 1962 1963 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationDiff( aThisPolyPoly, aOtherPolyPoly ); 1964 *this = Region( aClip ); 1965 } 1966 1967 sal_Bool Region::Exclude( const Region& rRegion ) 1968 { 1969 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 1970 1971 if( rRegion.HasPolyPolygon() || HasPolyPolygon() ) 1972 { 1973 ImplExcludePolyPolygon( rRegion ); 1974 return sal_True; 1975 } 1976 1977 ImplPolyPolyRegionToBandRegion(); 1978 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); 1979 1980 // is region empty or null? -> nothing to do 1981 if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) ) 1982 return sal_True; 1983 1984 // no instance data? -> nothing to do 1985 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 1986 return sal_True; 1987 1988 // no own instance data? -> make own copy! 1989 if ( mpImplRegion->mnRefCount > 1 ) 1990 ImplCopyData(); 1991 1992 // Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden 1993 ImplRegionBand* pBand = rRegion.mpImplRegion->mpFirstBand; 1994 while ( pBand ) 1995 { 1996 // insert bands if the boundaries are not allready in the list 1997 mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom ); 1998 1999 // process all elements of the list 2000 ImplRegionBandSep* pSep = pBand->mpFirstSep; 2001 while ( pSep ) 2002 { 2003 mpImplRegion->Exclude( pSep->mnXLeft, pBand->mnYTop, 2004 pSep->mnXRight, pBand->mnYBottom ); 2005 pSep = pSep->mpNextSep; 2006 } 2007 2008 // Wir optimieren schon in der Schleife, da wir davon 2009 // ausgehen, das wir insgesammt weniger Baender ueberpruefen 2010 // muessen 2011 if ( !mpImplRegion->OptimizeBandList() ) 2012 { 2013 delete mpImplRegion; 2014 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 2015 break; 2016 } 2017 2018 pBand = pBand->mpNextBand; 2019 } 2020 2021 return sal_True; 2022 } 2023 2024 // ----------------------------------------------------------------------- 2025 void Region::ImplXOrPolyPolygon( const Region& i_rRegion ) 2026 { 2027 // get this B2DPolyPolygon 2028 basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); 2029 if( aThisPolyPoly.count() == 0 ) 2030 { 2031 *this = i_rRegion; 2032 return; 2033 } 2034 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 2035 2036 // get the other B2DPolyPolygon 2037 basegfx::B2DPolyPolygon aOtherPolyPoly( const_cast<Region&>(i_rRegion).ConvertToB2DPolyPolygon() ); 2038 aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly ); 2039 2040 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationXor( aThisPolyPoly, aOtherPolyPoly ); 2041 *this = Region( aClip ); 2042 } 2043 2044 sal_Bool Region::XOr( const Region& rRegion ) 2045 { 2046 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2047 2048 if( rRegion.HasPolyPolygon() || HasPolyPolygon() ) 2049 { 2050 ImplXOrPolyPolygon( rRegion ); 2051 return sal_True; 2052 } 2053 2054 ImplPolyPolyRegionToBandRegion(); 2055 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); 2056 2057 // is region empty or null? -> nothing to do 2058 if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) ) 2059 return sal_True; 2060 2061 // no own instance data? -> XOr = copy 2062 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2063 { 2064 *this = rRegion; 2065 return sal_True; 2066 } 2067 2068 // no own instance data? -> make own copy! 2069 if ( mpImplRegion->mnRefCount > 1 ) 2070 ImplCopyData(); 2071 2072 // Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden 2073 ImplRegionBand* pBand = rRegion.mpImplRegion->mpFirstBand; 2074 while ( pBand ) 2075 { 2076 // insert bands if the boundaries are not allready in the list 2077 mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom ); 2078 2079 // process all elements of the list 2080 ImplRegionBandSep* pSep = pBand->mpFirstSep; 2081 while ( pSep ) 2082 { 2083 mpImplRegion->XOr( pSep->mnXLeft, pBand->mnYTop, 2084 pSep->mnXRight, pBand->mnYBottom ); 2085 pSep = pSep->mpNextSep; 2086 } 2087 2088 pBand = pBand->mpNextBand; 2089 } 2090 2091 // cleanup 2092 if ( !mpImplRegion->OptimizeBandList() ) 2093 { 2094 delete mpImplRegion; 2095 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 2096 } 2097 2098 return sal_True; 2099 } 2100 2101 // ----------------------------------------------------------------------- 2102 2103 Rectangle Region::GetBoundRect() const 2104 { 2105 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2106 2107 Rectangle aRect; 2108 2109 // no internal data? -> region is empty! 2110 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2111 return aRect; 2112 2113 // PolyPolygon data im Imp structure? 2114 if ( mpImplRegion->mpPolyPoly ) 2115 return mpImplRegion->mpPolyPoly->GetBoundRect(); 2116 if( mpImplRegion->mpB2DPolyPoly ) 2117 { 2118 const basegfx::B2DRange aRange(basegfx::tools::getRange(*mpImplRegion->mpB2DPolyPoly)); 2119 2120 if(aRange.isEmpty()) 2121 { 2122 // emulate PolyPolygon::GetBoundRect() when empty polygon 2123 return Rectangle(); 2124 } 2125 else 2126 { 2127 return Rectangle( 2128 static_cast<sal_Int32>(floor(aRange.getMinX())), static_cast<sal_Int32>(floor(aRange.getMinY())), 2129 static_cast<sal_Int32>(ceil(aRange.getMaxX())), static_cast<sal_Int32>(ceil(aRange.getMaxY()))); 2130 } 2131 } 2132 2133 // no band in the list? -> region is empty! 2134 if ( !mpImplRegion->mpFirstBand ) 2135 return aRect; 2136 2137 // get the boundaries of the first band 2138 long nYTop = mpImplRegion->mpFirstBand->mnYTop; 2139 long nYBottom = mpImplRegion->mpFirstBand->mnYBottom; 2140 long nXLeft = mpImplRegion->mpFirstBand->GetXLeftBoundary(); 2141 long nXRight = mpImplRegion->mpFirstBand->GetXRightBoundary(); 2142 2143 // look in the band list (don't test first band again!) 2144 ImplRegionBand* pBand = mpImplRegion->mpFirstBand->mpNextBand; 2145 while ( pBand ) 2146 { 2147 nYBottom = pBand->mnYBottom; 2148 nXLeft = Min( nXLeft, pBand->GetXLeftBoundary() ); 2149 nXRight = Max( nXRight, pBand->GetXRightBoundary() ); 2150 2151 pBand = pBand->mpNextBand; 2152 } 2153 2154 // set rectangle 2155 aRect = Rectangle( nXLeft, nYTop, nXRight, nYBottom ); 2156 return aRect; 2157 } 2158 2159 // ----------------------------------------------------------------------- 2160 2161 sal_Bool Region::HasPolyPolygon() const 2162 { 2163 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2164 if( !mpImplRegion ) 2165 return false; 2166 if( mpImplRegion->mpPolyPoly ) 2167 return true; 2168 if( mpImplRegion->mpB2DPolyPoly ) 2169 return true; 2170 return false; 2171 } 2172 2173 // ----------------------------------------------------------------------- 2174 2175 PolyPolygon Region::GetPolyPolygon() const 2176 { 2177 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2178 2179 PolyPolygon aRet; 2180 2181 if( mpImplRegion->mpPolyPoly ) 2182 aRet = *mpImplRegion->mpPolyPoly; 2183 else if( mpImplRegion->mpB2DPolyPoly ) 2184 { 2185 // the polygon needs to be converted 2186 aRet = PolyPolygon( *mpImplRegion->mpB2DPolyPoly ); 2187 // TODO: cache the converted polygon? 2188 // mpImplRegion->mpB2DPolyPoly = aRet; 2189 } 2190 2191 return aRet; 2192 } 2193 2194 // ----------------------------------------------------------------------- 2195 2196 const basegfx::B2DPolyPolygon Region::GetB2DPolyPolygon() const 2197 { 2198 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2199 2200 basegfx::B2DPolyPolygon aRet; 2201 2202 if( mpImplRegion->mpB2DPolyPoly ) 2203 aRet = *mpImplRegion->mpB2DPolyPoly; 2204 else if( mpImplRegion->mpPolyPoly ) 2205 { 2206 // the polygon needs to be converted 2207 aRet = mpImplRegion->mpPolyPoly->getB2DPolyPolygon(); 2208 // TODO: cache the converted polygon? 2209 // mpImplRegion->mpB2DPolyPoly = aRet; 2210 } 2211 2212 return aRet; 2213 } 2214 2215 // ----------------------------------------------------------------------- 2216 2217 basegfx::B2DPolyPolygon Region::ConvertToB2DPolyPolygon() 2218 { 2219 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2220 2221 basegfx::B2DPolyPolygon aRet; 2222 2223 if( HasPolyPolygon() ) 2224 aRet = GetB2DPolyPolygon(); 2225 else 2226 { 2227 RegionHandle aHdl = BeginEnumRects(); 2228 Rectangle aSubRect; 2229 while( GetNextEnumRect( aHdl, aSubRect ) ) 2230 { 2231 basegfx::B2DPolygon aPoly( basegfx::tools::createPolygonFromRect( 2232 basegfx::B2DRectangle( aSubRect.Left(), aSubRect.Top(), aSubRect.Right(), aSubRect.Bottom() ) ) ); 2233 aRet.append( aPoly ); 2234 } 2235 EndEnumRects( aHdl ); 2236 } 2237 2238 return aRet; 2239 } 2240 2241 // ----------------------------------------------------------------------- 2242 2243 bool Region::ImplGetFirstRect( ImplRegionInfo& rImplRegionInfo, 2244 long& rX, long& rY, 2245 long& rWidth, long& rHeight ) const 2246 { 2247 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2248 2249 ((Region*)this)->ImplPolyPolyRegionToBandRegion(); 2250 2251 // no internal data? -> region is empty! 2252 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2253 return false; 2254 2255 // no band in the list? -> region is empty! 2256 if ( mpImplRegion->mpFirstBand == NULL ) 2257 return false; 2258 2259 // initialise pointer for first access 2260 ImplRegionBand* pCurrRectBand = mpImplRegion->mpFirstBand; 2261 ImplRegionBandSep* pCurrRectBandSep = pCurrRectBand->mpFirstSep; 2262 2263 DBG_ASSERT( pCurrRectBandSep != NULL, "Erstes Band wurde nicht optimiert." ); 2264 if ( !pCurrRectBandSep ) 2265 return false; 2266 2267 // get boundaries of current rectangle 2268 rX = pCurrRectBandSep->mnXLeft; 2269 rY = pCurrRectBand->mnYTop; 2270 rWidth = pCurrRectBandSep->mnXRight - pCurrRectBandSep->mnXLeft + 1; 2271 rHeight = pCurrRectBand->mnYBottom - pCurrRectBand->mnYTop + 1; 2272 2273 // save pointers 2274 rImplRegionInfo.mpVoidCurrRectBand = (void*)pCurrRectBand; 2275 rImplRegionInfo.mpVoidCurrRectBandSep = (void*)pCurrRectBandSep; 2276 2277 return true; 2278 } 2279 2280 // ----------------------------------------------------------------------- 2281 2282 bool Region::ImplGetNextRect( ImplRegionInfo& rImplRegionInfo, 2283 long& rX, long& rY, 2284 long& rWidth, long& rHeight ) const 2285 { 2286 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2287 2288 // no internal data? -> region is empty! 2289 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2290 return false; 2291 2292 // get last pointers 2293 ImplRegionBand* pCurrRectBand = (ImplRegionBand*)rImplRegionInfo.mpVoidCurrRectBand; 2294 ImplRegionBandSep* pCurrRectBandSep = (ImplRegionBandSep*)rImplRegionInfo.mpVoidCurrRectBandSep; 2295 2296 // get next separation from current band 2297 pCurrRectBandSep = pCurrRectBandSep->mpNextSep; 2298 2299 // no separation found? -> go to next band! 2300 if ( !pCurrRectBandSep ) 2301 { 2302 // get next band 2303 pCurrRectBand = pCurrRectBand->mpNextBand; 2304 2305 // no band found? -> not further rectangles! 2306 if( !pCurrRectBand ) 2307 return false; 2308 2309 // get first separation in current band 2310 pCurrRectBandSep = pCurrRectBand->mpFirstSep; 2311 } 2312 2313 // get boundaries of current rectangle 2314 rX = pCurrRectBandSep->mnXLeft; 2315 rY = pCurrRectBand->mnYTop; 2316 rWidth = pCurrRectBandSep->mnXRight - pCurrRectBandSep->mnXLeft + 1; 2317 rHeight = pCurrRectBand->mnYBottom - pCurrRectBand->mnYTop + 1; 2318 2319 // save new pointers 2320 rImplRegionInfo.mpVoidCurrRectBand = (void*)pCurrRectBand; 2321 rImplRegionInfo.mpVoidCurrRectBandSep = (void*)pCurrRectBandSep; 2322 2323 return true; 2324 } 2325 2326 // ----------------------------------------------------------------------- 2327 2328 RegionType Region::GetType() const 2329 { 2330 if ( mpImplRegion == &aImplEmptyRegion ) 2331 return REGION_EMPTY; 2332 else if ( mpImplRegion == &aImplNullRegion ) 2333 return REGION_NULL; 2334 else if ( mpImplRegion->mnRectCount == 1 ) 2335 return REGION_RECTANGLE; 2336 else 2337 return REGION_COMPLEX; 2338 } 2339 2340 // ----------------------------------------------------------------------- 2341 2342 sal_Bool Region::IsInside( const Point& rPoint ) const 2343 { 2344 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2345 2346 // PolyPolygon data im Imp structure? 2347 ((Region*)this)->ImplPolyPolyRegionToBandRegion(); 2348 /* 2349 if ( mpImplRegion->mpPolyPoly ) 2350 return mpImplRegion->mpPolyPoly->IsInside( rPoint ); 2351 */ 2352 2353 // no instance data? -> not inside 2354 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2355 return sal_False; 2356 2357 // search band list 2358 ImplRegionBand* pBand = mpImplRegion->mpFirstBand; 2359 while ( pBand ) 2360 { 2361 // is point within band? 2362 if ( (pBand->mnYTop <= rPoint.Y()) && 2363 (pBand->mnYBottom >= rPoint.Y()) ) 2364 { 2365 // is point within separation of the band? 2366 if ( pBand->IsInside( rPoint.X() ) ) 2367 return sal_True; 2368 else 2369 return sal_False; 2370 } 2371 2372 pBand = pBand->mpNextBand; 2373 } 2374 2375 return sal_False; 2376 } 2377 2378 // ----------------------------------------------------------------------- 2379 2380 sal_Bool Region::IsInside( const Rectangle& rRect ) const 2381 { 2382 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2383 2384 // is rectangle empty? -> not inside 2385 if ( rRect.IsEmpty() ) 2386 return sal_False; 2387 2388 // no instance data? -> not inside 2389 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2390 return sal_False; 2391 2392 // create region from rectangle and intersect own region 2393 Region aRegion = rRect; 2394 aRegion.Exclude( *this ); 2395 2396 // rectangle is inside if exclusion is empty 2397 return aRegion.IsEmpty(); 2398 } 2399 2400 // ----------------------------------------------------------------------- 2401 2402 sal_Bool Region::IsOver( const Rectangle& rRect ) const 2403 { 2404 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2405 2406 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2407 return sal_False; 2408 2409 // Can we optimize this ??? - is used in StarDraw for brushes pointers 2410 // Why we have no IsOver for Regions ??? 2411 // create region from rectangle and intersect own region 2412 Region aRegion = rRect; 2413 aRegion.Intersect( *this ); 2414 2415 // rectangle is over if include is not empty 2416 return !aRegion.IsEmpty(); 2417 } 2418 2419 // ----------------------------------------------------------------------- 2420 2421 void Region::SetNull() 2422 { 2423 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2424 2425 // statische Object haben RefCount von 0 2426 if ( mpImplRegion->mnRefCount ) 2427 { 2428 if ( mpImplRegion->mnRefCount > 1 ) 2429 mpImplRegion->mnRefCount--; 2430 else 2431 delete mpImplRegion; 2432 } 2433 2434 // set new type 2435 mpImplRegion = (ImplRegion*)(&aImplNullRegion); 2436 } 2437 2438 // ----------------------------------------------------------------------- 2439 2440 void Region::SetEmpty() 2441 { 2442 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2443 2444 // statische Object haben RefCount von 0 2445 if ( mpImplRegion->mnRefCount ) 2446 { 2447 if ( mpImplRegion->mnRefCount > 1 ) 2448 mpImplRegion->mnRefCount--; 2449 else 2450 delete mpImplRegion; 2451 } 2452 2453 // set new type 2454 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 2455 } 2456 2457 // ----------------------------------------------------------------------- 2458 2459 Region& Region::operator=( const Region& rRegion ) 2460 { 2461 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2462 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); 2463 DBG_ASSERT( rRegion.mpImplRegion->mnRefCount < 0xFFFFFFFE, "Region: RefCount overflow" ); 2464 2465 // Zuerst Referenzcounter erhoehen, damit man sich selbst zuweisen kann 2466 // RefCount == 0 fuer statische Objekte 2467 if ( rRegion.mpImplRegion->mnRefCount ) 2468 rRegion.mpImplRegion->mnRefCount++; 2469 2470 // statische Object haben RefCount von 0 2471 if ( mpImplRegion->mnRefCount ) 2472 { 2473 if ( mpImplRegion->mnRefCount > 1 ) 2474 mpImplRegion->mnRefCount--; 2475 else 2476 delete mpImplRegion; 2477 } 2478 2479 mpImplRegion = rRegion.mpImplRegion; 2480 return *this; 2481 } 2482 2483 // ----------------------------------------------------------------------- 2484 2485 Region& Region::operator=( const Rectangle& rRect ) 2486 { 2487 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2488 2489 // statische Object haben RefCount von 0 2490 if ( mpImplRegion->mnRefCount ) 2491 { 2492 if ( mpImplRegion->mnRefCount > 1 ) 2493 mpImplRegion->mnRefCount--; 2494 else 2495 delete mpImplRegion; 2496 } 2497 2498 ImplCreateRectRegion( rRect ); 2499 return *this; 2500 } 2501 2502 // ----------------------------------------------------------------------- 2503 2504 sal_Bool Region::operator==( const Region& rRegion ) const 2505 { 2506 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2507 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); 2508 2509 // reference to same object? -> equal! 2510 if ( mpImplRegion == rRegion.mpImplRegion ) 2511 return sal_True; 2512 2513 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2514 return sal_False; 2515 2516 if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) ) 2517 return sal_False; 2518 2519 if ( rRegion.mpImplRegion->mpPolyPoly && mpImplRegion->mpPolyPoly ) 2520 return *rRegion.mpImplRegion->mpPolyPoly == *mpImplRegion->mpPolyPoly; 2521 else 2522 { 2523 ((Region*)this)->ImplPolyPolyRegionToBandRegion(); 2524 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); 2525 2526 // Eine der beiden Regions kann jetzt Empty sein 2527 if ( mpImplRegion == rRegion.mpImplRegion ) 2528 return sal_True; 2529 2530 if ( mpImplRegion == &aImplEmptyRegion ) 2531 return sal_False; 2532 2533 if ( rRegion.mpImplRegion == &aImplEmptyRegion ) 2534 return sal_False; 2535 } 2536 2537 // initialise pointers 2538 ImplRegionBand* pOwnRectBand = mpImplRegion->mpFirstBand; 2539 ImplRegionBandSep* pOwnRectBandSep = pOwnRectBand->mpFirstSep; 2540 ImplRegionBand* pSecondRectBand = rRegion.mpImplRegion->mpFirstBand; 2541 ImplRegionBandSep* pSecondRectBandSep = pSecondRectBand->mpFirstSep; 2542 while ( pOwnRectBandSep && pSecondRectBandSep ) 2543 { 2544 // get boundaries of current rectangle 2545 long nOwnXLeft = pOwnRectBandSep->mnXLeft; 2546 long nSecondXLeft = pSecondRectBandSep->mnXLeft; 2547 if ( nOwnXLeft != nSecondXLeft ) 2548 return sal_False; 2549 2550 long nOwnYTop = pOwnRectBand->mnYTop; 2551 long nSecondYTop = pSecondRectBand->mnYTop; 2552 if ( nOwnYTop != nSecondYTop ) 2553 return sal_False; 2554 2555 long nOwnXRight = pOwnRectBandSep->mnXRight; 2556 long nSecondXRight = pSecondRectBandSep->mnXRight; 2557 if ( nOwnXRight != nSecondXRight ) 2558 return sal_False; 2559 2560 long nOwnYBottom = pOwnRectBand->mnYBottom; 2561 long nSecondYBottom = pSecondRectBand->mnYBottom; 2562 if ( nOwnYBottom != nSecondYBottom ) 2563 return sal_False; 2564 2565 // get next separation from current band 2566 pOwnRectBandSep = pOwnRectBandSep->mpNextSep; 2567 2568 // no separation found? -> go to next band! 2569 if ( !pOwnRectBandSep ) 2570 { 2571 // get next band 2572 pOwnRectBand = pOwnRectBand->mpNextBand; 2573 2574 // get first separation in current band 2575 if( pOwnRectBand ) 2576 pOwnRectBandSep = pOwnRectBand->mpFirstSep; 2577 } 2578 2579 // get next separation from current band 2580 pSecondRectBandSep = pSecondRectBandSep->mpNextSep; 2581 2582 // no separation found? -> go to next band! 2583 if ( !pSecondRectBandSep ) 2584 { 2585 // get next band 2586 pSecondRectBand = pSecondRectBand->mpNextBand; 2587 2588 // get first separation in current band 2589 if( pSecondRectBand ) 2590 pSecondRectBandSep = pSecondRectBand->mpFirstSep; 2591 } 2592 2593 if ( pOwnRectBandSep && !pSecondRectBandSep ) 2594 return sal_False; 2595 2596 if ( !pOwnRectBandSep && pSecondRectBandSep ) 2597 return sal_False; 2598 } 2599 2600 return sal_True; 2601 } 2602 2603 // ----------------------------------------------------------------------- 2604 2605 enum StreamEntryType { STREAMENTRY_BANDHEADER, STREAMENTRY_SEPARATION, STREAMENTRY_END }; 2606 2607 SvStream& operator>>( SvStream& rIStrm, Region& rRegion ) 2608 { 2609 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); 2610 2611 VersionCompat aCompat( rIStrm, STREAM_READ ); 2612 sal_uInt16 nVersion; 2613 sal_uInt16 nTmp16; 2614 2615 // statische Object haben RefCount von 0 2616 if ( rRegion.mpImplRegion->mnRefCount ) 2617 { 2618 if ( rRegion.mpImplRegion->mnRefCount > 1 ) 2619 rRegion.mpImplRegion->mnRefCount--; 2620 else 2621 delete rRegion.mpImplRegion; 2622 } 2623 2624 // get version of streamed region 2625 rIStrm >> nVersion; 2626 2627 // get type of region 2628 rIStrm >> nTmp16; 2629 2630 RegionType meStreamedType = (RegionType)nTmp16; 2631 2632 switch( meStreamedType ) 2633 { 2634 case REGION_NULL: 2635 rRegion.mpImplRegion = (ImplRegion*)&aImplNullRegion; 2636 break; 2637 2638 case REGION_EMPTY: 2639 rRegion.mpImplRegion = (ImplRegion*)&aImplEmptyRegion; 2640 break; 2641 2642 default: 2643 { 2644 // create instance of implementation class 2645 rRegion.mpImplRegion = new ImplRegion(); 2646 2647 // get header from first element 2648 rIStrm >> nTmp16; 2649 2650 // get all bands 2651 rRegion.mpImplRegion->mnRectCount = 0; 2652 ImplRegionBand* pCurrBand = NULL; 2653 while ( (StreamEntryType)nTmp16 != STREAMENTRY_END ) 2654 { 2655 // insert new band or new separation? 2656 if ( (StreamEntryType)nTmp16 == STREAMENTRY_BANDHEADER ) 2657 { 2658 long nYTop; 2659 long nYBottom; 2660 2661 rIStrm >> nYTop; 2662 rIStrm >> nYBottom; 2663 2664 // create band 2665 ImplRegionBand* pNewBand = new ImplRegionBand( nYTop, nYBottom ); 2666 2667 // first element? -> set as first into the list 2668 if ( !pCurrBand ) 2669 rRegion.mpImplRegion->mpFirstBand = pNewBand; 2670 else 2671 pCurrBand->mpNextBand = pNewBand; 2672 2673 // save pointer for next creation 2674 pCurrBand = pNewBand; 2675 } 2676 else 2677 { 2678 long nXLeft; 2679 long nXRight; 2680 2681 rIStrm >> nXLeft; 2682 rIStrm >> nXRight; 2683 2684 // add separation 2685 if ( pCurrBand ) 2686 { 2687 pCurrBand->Union( nXLeft, nXRight ); 2688 rRegion.mpImplRegion->mnRectCount++; 2689 } 2690 } 2691 2692 if( rIStrm.IsEof() ) 2693 { 2694 DBG_ERROR( "premature end of region stream" ); 2695 delete rRegion.mpImplRegion; 2696 rRegion.mpImplRegion = (ImplRegion*)&aImplEmptyRegion; 2697 return rIStrm; 2698 } 2699 2700 // get next header 2701 rIStrm >> nTmp16; 2702 } 2703 2704 if( aCompat.GetVersion() >= 2 ) 2705 { 2706 sal_Bool bHasPolyPolygon; 2707 2708 rIStrm >> bHasPolyPolygon; 2709 2710 if( bHasPolyPolygon ) 2711 { 2712 delete rRegion.mpImplRegion->mpPolyPoly; 2713 rRegion.mpImplRegion->mpPolyPoly = new PolyPolygon; 2714 rIStrm >> *( rRegion.mpImplRegion->mpPolyPoly ); 2715 } 2716 } 2717 } 2718 break; 2719 } 2720 2721 return rIStrm; 2722 } 2723 2724 // ----------------------------------------------------------------------- 2725 2726 SvStream& operator<<( SvStream& rOStrm, const Region& rRegion ) 2727 { 2728 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); 2729 2730 sal_uInt16 nVersion = 2; 2731 VersionCompat aCompat( rOStrm, STREAM_WRITE, nVersion ); 2732 Region aTmpRegion( rRegion ); 2733 2734 // use tmp region to avoid destruction of internal region (polypolygon) of rRegion 2735 aTmpRegion.ImplPolyPolyRegionToBandRegion(); 2736 2737 // put version 2738 rOStrm << nVersion; 2739 2740 // put type 2741 rOStrm << (sal_uInt16)aTmpRegion.GetType(); 2742 2743 // put all bands if not null or empty 2744 if ( (aTmpRegion.mpImplRegion != &aImplEmptyRegion) && (aTmpRegion.mpImplRegion != &aImplNullRegion) ) 2745 { 2746 ImplRegionBand* pBand = aTmpRegion.mpImplRegion->mpFirstBand; 2747 while ( pBand ) 2748 { 2749 // put boundaries 2750 rOStrm << (sal_uInt16) STREAMENTRY_BANDHEADER; 2751 rOStrm << pBand->mnYTop; 2752 rOStrm << pBand->mnYBottom; 2753 2754 // put separations of current band 2755 ImplRegionBandSep* pSep = pBand->mpFirstSep; 2756 while ( pSep ) 2757 { 2758 // put separation 2759 rOStrm << (sal_uInt16) STREAMENTRY_SEPARATION; 2760 rOStrm << pSep->mnXLeft; 2761 rOStrm << pSep->mnXRight; 2762 2763 // next separation from current band 2764 pSep = pSep->mpNextSep; 2765 } 2766 2767 pBand = pBand->mpNextBand; 2768 } 2769 2770 // put endmarker 2771 rOStrm << (sal_uInt16) STREAMENTRY_END; 2772 2773 // write polypolygon if available 2774 const sal_Bool bHasPolyPolygon = rRegion.HasPolyPolygon(); 2775 rOStrm << bHasPolyPolygon; 2776 2777 if( bHasPolyPolygon ) 2778 { 2779 // #i105373# 2780 PolyPolygon aNoCurvePolyPolygon; 2781 rRegion.GetPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon); 2782 2783 rOStrm << aNoCurvePolyPolygon; 2784 } 2785 } 2786 2787 return rOStrm; 2788 } 2789 2790 // ----------------------------------------------------------------------- 2791 2792 void Region::ImplBeginAddRect() 2793 { 2794 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2795 2796 // statische Object haben RefCount von 0 2797 if ( mpImplRegion->mnRefCount ) 2798 { 2799 if ( mpImplRegion->mnRefCount > 1 ) 2800 mpImplRegion->mnRefCount--; 2801 else 2802 delete mpImplRegion; 2803 } 2804 2805 // create fresh region 2806 mpImplRegion = new ImplRegion(); 2807 } 2808 2809 // ----------------------------------------------------------------------- 2810 2811 sal_Bool Region::ImplAddRect( const Rectangle& rRect ) 2812 { 2813 // Hier kein CheckThis, da nicht alle Daten auf Stand 2814 2815 if ( rRect.IsEmpty() ) 2816 return sal_True; 2817 2818 // get justified rectangle 2819 long nTop; 2820 long nBottom; 2821 long nLeft; 2822 long nRight; 2823 if ( rRect.Top() <= rRect.Bottom() ) 2824 { 2825 nTop = rRect.Top(); 2826 nBottom = rRect.Bottom(); 2827 } 2828 else 2829 { 2830 nTop = rRect.Bottom(); 2831 nBottom = rRect.Top(); 2832 } 2833 if ( rRect.Left() <= rRect.Right() ) 2834 { 2835 nLeft = rRect.Left(); 2836 nRight = rRect.Right(); 2837 } 2838 else 2839 { 2840 nLeft = rRect.Right(); 2841 nRight = rRect.Left(); 2842 } 2843 2844 if ( !mpImplRegion->mpLastCheckedBand ) 2845 { 2846 // create new band 2847 mpImplRegion->mpLastCheckedBand = new ImplRegionBand( nTop, nBottom ); 2848 2849 // set band as current 2850 mpImplRegion->mpFirstBand = mpImplRegion->mpLastCheckedBand; 2851 mpImplRegion->mpLastCheckedBand->Union( nLeft, nRight ); 2852 } 2853 else 2854 { 2855 DBG_ASSERT( nTop >= mpImplRegion->mpLastCheckedBand->mnYTop, 2856 "Region::ImplAddRect() - nTopY < nLastTopY" ); 2857 2858 // new band? create it! 2859 if ( (nTop != mpImplRegion->mpLastCheckedBand->mnYTop) || 2860 (nBottom != mpImplRegion->mpLastCheckedBand->mnYBottom) ) 2861 { 2862 // create new band 2863 ImplRegionBand* pNewRegionBand = new ImplRegionBand( nTop, nBottom ); 2864 2865 // append band to the end 2866 mpImplRegion->mpLastCheckedBand->mpNextBand = pNewRegionBand; 2867 2868 // skip to the new band 2869 mpImplRegion->mpLastCheckedBand = mpImplRegion->mpLastCheckedBand->mpNextBand; 2870 } 2871 2872 // Insert Sep 2873 mpImplRegion->mpLastCheckedBand->Union( nLeft, nRight ); 2874 } 2875 2876 return sal_True; 2877 } 2878 2879 // ----------------------------------------------------------------------- 2880 2881 void Region::ImplEndAddRect() 2882 { 2883 // check if we are empty 2884 if ( !mpImplRegion->mpFirstBand ) 2885 { 2886 delete mpImplRegion; 2887 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 2888 return; 2889 } 2890 2891 // check if we have somthing to optimize 2892 if ( !mpImplRegion->mpFirstBand->mpNextBand ) 2893 { 2894 // update mpImplRegion->mnRectCount, because no OptimizeBandList is called 2895 ImplRegionBandSep* pSep = mpImplRegion->mpFirstBand->mpFirstSep; 2896 mpImplRegion->mnRectCount = 0; 2897 while( pSep ) 2898 { 2899 mpImplRegion->mnRectCount++; 2900 pSep = pSep->mpNextSep; 2901 } 2902 2903 // Erst hier testen, da hier die Daten wieder stimmen 2904 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2905 return; 2906 } 2907 2908 // have to revert list? -> do it now! 2909 if ( mpImplRegion->mpFirstBand->mnYTop > 2910 mpImplRegion->mpFirstBand->mpNextBand->mnYTop ) 2911 { 2912 ImplRegionBand * pNewFirstRegionBand; 2913 2914 // initialize temp list with first element 2915 pNewFirstRegionBand = mpImplRegion->mpFirstBand; 2916 mpImplRegion->mpFirstBand = mpImplRegion->mpFirstBand->mpNextBand; 2917 pNewFirstRegionBand->mpNextBand = NULL; 2918 2919 // insert elements to the temp list 2920 while ( mpImplRegion->mpFirstBand ) 2921 { 2922 ImplRegionBand * pSavedRegionBand = pNewFirstRegionBand; 2923 pNewFirstRegionBand = mpImplRegion->mpFirstBand; 2924 mpImplRegion->mpFirstBand = mpImplRegion->mpFirstBand->mpNextBand; 2925 pNewFirstRegionBand->mpNextBand = pSavedRegionBand; 2926 } 2927 2928 // set temp list as new list 2929 mpImplRegion->mpFirstBand = pNewFirstRegionBand; 2930 } 2931 2932 // cleanup 2933 if ( !mpImplRegion->OptimizeBandList() ) 2934 { 2935 delete mpImplRegion; 2936 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); 2937 } 2938 2939 // Erst hier testen, da hier die Daten wieder stimmen 2940 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2941 } 2942 2943 // ----------------------------------------------------------------------- 2944 2945 sal_uLong Region::GetRectCount() const 2946 { 2947 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2948 2949 ((Region*)this)->ImplPolyPolyRegionToBandRegion(); 2950 2951 #ifdef DBG_UTIL 2952 sal_uLong nCount = 0; 2953 2954 // all bands if not null or empty 2955 if ( (mpImplRegion != &aImplEmptyRegion) && (mpImplRegion != &aImplNullRegion) ) 2956 { 2957 ImplRegionBand* pBand = mpImplRegion->mpFirstBand; 2958 while ( pBand ) 2959 { 2960 ImplRegionBandSep* pSep = pBand->mpFirstSep; 2961 while( pSep ) 2962 { 2963 nCount++; 2964 pSep = pSep->mpNextSep; 2965 } 2966 2967 pBand = pBand->mpNextBand; 2968 } 2969 } 2970 2971 DBG_ASSERT( mpImplRegion->mnRectCount == nCount, "Region: invalid mnRectCount!" ); 2972 #endif 2973 2974 return mpImplRegion->mnRectCount; 2975 } 2976 2977 // ----------------------------------------------------------------------- 2978 2979 RegionHandle Region::BeginEnumRects() 2980 { 2981 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 2982 2983 ImplPolyPolyRegionToBandRegion(); 2984 2985 // no internal data? -> region is empty! 2986 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) 2987 return 0; 2988 2989 // no band in the list? -> region is empty! 2990 if ( mpImplRegion->mpFirstBand == NULL ) 2991 { 2992 DBG_ASSERT( mpImplRegion->mpFirstBand, "Region::BeginEnumRects() First Band is Empty!" ); 2993 return 0; 2994 } 2995 2996 ImplRegionHandle* pData = new ImplRegionHandle; 2997 pData->mpRegion = new Region( *this ); 2998 pData->mbFirst = sal_True; 2999 3000 // save pointers 3001 pData->mpCurrRectBand = pData->mpRegion->mpImplRegion->mpFirstBand; 3002 pData->mpCurrRectBandSep = pData->mpCurrRectBand->mpFirstSep; 3003 3004 return (RegionHandle)pData; 3005 } 3006 3007 // ----------------------------------------------------------------------- 3008 3009 sal_Bool Region::GetEnumRects( RegionHandle pVoidData, Rectangle& rRect ) 3010 { 3011 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 3012 3013 ImplRegionHandle* pData = (ImplRegionHandle*)pVoidData; 3014 if ( !pData ) 3015 return sal_False; 3016 3017 if ( pData->mbFirst ) 3018 pData->mbFirst = sal_False; 3019 else 3020 { 3021 // get next separation from current band 3022 pData->mpCurrRectBandSep = pData->mpCurrRectBandSep->mpNextSep; 3023 3024 // no separation found? -> go to next band! 3025 if ( !pData->mpCurrRectBandSep ) 3026 { 3027 // get next band 3028 pData->mpCurrRectBand = pData->mpCurrRectBand->mpNextBand; 3029 3030 // no band found? -> not further rectangles! 3031 if ( !pData->mpCurrRectBand ) 3032 return sal_False; 3033 3034 // get first separation in current band 3035 pData->mpCurrRectBandSep = pData->mpCurrRectBand->mpFirstSep; 3036 } 3037 } 3038 3039 // get boundaries of current rectangle 3040 rRect.Top() = pData->mpCurrRectBand->mnYTop; 3041 rRect.Bottom() = pData->mpCurrRectBand->mnYBottom; 3042 rRect.Left() = pData->mpCurrRectBandSep->mnXLeft; 3043 rRect.Right() = pData->mpCurrRectBandSep->mnXRight; 3044 return sal_True; 3045 } 3046 3047 // ----------------------------------------------------------------------- 3048 3049 void Region::EndEnumRects( RegionHandle pVoidData ) 3050 { 3051 DBG_CHKTHIS( Region, ImplDbgTestRegion ); 3052 3053 ImplRegionHandle* pData = (ImplRegionHandle*)pVoidData; 3054 if ( !pData ) 3055 return; 3056 3057 // cleanup 3058 delete pData->mpRegion; 3059 delete pData; 3060 } 3061 3062 // ----------------------------------------------------------------------- 3063 3064 static inline bool ImplPolygonRectTest( const Polygon& rPoly, Rectangle* pRectOut = NULL ) 3065 { 3066 bool bIsRect = false; 3067 const Point* pPoints = rPoly.GetConstPointAry(); 3068 sal_uInt16 nPoints = rPoly.GetSize(); 3069 if( nPoints == 4 || (nPoints == 5 && pPoints[0] == pPoints[4]) ) 3070 { 3071 long nX1 = pPoints[0].X(), nX2 = pPoints[2].X(), 3072 nY1 = pPoints[0].Y(), nY2 = pPoints[2].Y(); 3073 if( ( (pPoints[1].X() == nX1 && pPoints[3].X() == nX2) && 3074 (pPoints[1].Y() == nY2 && pPoints[3].Y() == nY1) ) 3075 || 3076 ( (pPoints[1].X() == nX2 && pPoints[3].X() == nX1) && 3077 (pPoints[1].Y() == nY1 && pPoints[3].Y() == nY2) ) ) 3078 { 3079 bIsRect = true; 3080 if( pRectOut ) 3081 { 3082 long nSwap; 3083 if( nX2 < nX1 ) 3084 { 3085 nSwap = nX2; 3086 nX2 = nX1; 3087 nX1 = nSwap; 3088 } 3089 if( nY2 < nY1 ) 3090 { 3091 nSwap = nY2; 3092 nY2 = nY1; 3093 nY1 = nSwap; 3094 } 3095 if( nX2 != nX1 ) 3096 nX2--; 3097 if( nY2 != nY1 ) 3098 nY2--; 3099 pRectOut->Left() = nX1; 3100 pRectOut->Right() = nX2; 3101 pRectOut->Top() = nY1; 3102 pRectOut->Bottom() = nY2; 3103 } 3104 } 3105 } 3106 return bIsRect; 3107 } 3108 3109 Region Region::GetRegionFromPolyPolygon( const PolyPolygon& rPolyPoly ) 3110 { 3111 //return Region( rPolyPoly ); 3112 3113 // check if it's worth extracting the XOr'ing the Rectangles 3114 // empiricism shows that break even between XOr'ing rectangles separately 3115 // and ImplPolyPolyRegionToBandRegion is at half rectangles/half polygons 3116 int nPolygonRects = 0, nPolygonPolygons = 0; 3117 int nPolygons = rPolyPoly.Count(); 3118 3119 for( sal_uInt16 i = 0; i < nPolygons; i++ ) 3120 { 3121 const Polygon& rPoly = rPolyPoly[i]; 3122 if( ImplPolygonRectTest( rPoly ) ) 3123 nPolygonRects++; 3124 else 3125 nPolygonPolygons++; 3126 } 3127 if( nPolygonPolygons > nPolygonRects ) 3128 return Region( rPolyPoly ); 3129 3130 Region aResult; 3131 Rectangle aRect; 3132 for( sal_uInt16 i = 0; i < nPolygons; i++ ) 3133 { 3134 const Polygon& rPoly = rPolyPoly[i]; 3135 if( ImplPolygonRectTest( rPoly, &aRect ) ) 3136 aResult.XOr( aRect ); 3137 else 3138 aResult.XOr( Region(rPoly) ); 3139 } 3140 return aResult; 3141 } 3142