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