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 #include <tools/vcompat.hxx> 29 #include <tools/stream.hxx> 30 #include <vcl/region.hxx> 31 #include <regionband.hxx> 32 #include <basegfx/matrix/b2dhommatrix.hxx> 33 #include <basegfx/polygon/b2dpolypolygontools.hxx> 34 #include <basegfx/polygon/b2dpolygontools.hxx> 35 #include <basegfx/polygon/b2dpolygonclipper.hxx> 36 #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 37 #include <basegfx/range/b2drange.hxx> 38 #include <basegfx/matrix/b2dhommatrixtools.hxx> 39 40 ////////////////////////////////////////////////////////////////////////////// 41 42 DBG_NAME( Region ) 43 DBG_NAMEEX( Polygon ) 44 DBG_NAMEEX( PolyPolygon ) 45 46 ////////////////////////////////////////////////////////////////////////////// 47 48 namespace 49 { 50 /** Return <TRUE/> when the given polygon is rectilinear and oriented so that 51 all sides are either horizontal or vertical. 52 */ 53 bool ImplIsPolygonRectilinear (const PolyPolygon& rPolyPoly) 54 { 55 // Iterate over all polygons. 56 const sal_uInt16 nPolyCount = rPolyPoly.Count(); 57 for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly) 58 { 59 const Polygon& aPoly = rPolyPoly.GetObject(nPoly); 60 61 // Iterate over all edges of the current polygon. 62 const sal_uInt16 nSize = aPoly.GetSize(); 63 64 if (nSize < 2) 65 continue; 66 Point aPoint (aPoly.GetPoint(0)); 67 const Point aLastPoint (aPoint); 68 for (sal_uInt16 nPoint = 1; nPoint < nSize; ++nPoint) 69 { 70 const Point aNextPoint (aPoly.GetPoint(nPoint)); 71 // When there is at least one edge that is neither vertical nor 72 // horizontal then the entire polygon is not rectilinear (and 73 // oriented along primary axes.) 74 if (aPoint.X() != aNextPoint.X() && aPoint.Y() != aNextPoint.Y()) 75 return false; 76 77 aPoint = aNextPoint; 78 } 79 // Compare closing edge. 80 if (aLastPoint.X() != aPoint.X() && aLastPoint.Y() != aPoint.Y()) 81 return false; 82 } 83 return true; 84 } 85 86 /** Convert a rectilinear polygon (that is oriented along the primary axes) 87 to a list of bands. For this special form of polygon we can use an 88 optimization that prevents the creation of one band per y value. 89 However, it still is possible that some temporary bands are created that 90 later can be optimized away. 91 @param rPolyPolygon 92 A set of zero, one, or more polygons, nested or not, that are 93 converted into a list of bands. 94 @return 95 A new RegionBand object is returned that contains the bands that 96 represent the given poly-polygon. 97 */ 98 RegionBand* ImplRectilinearPolygonToBands(const PolyPolygon& rPolyPoly) 99 { 100 OSL_ASSERT(ImplIsPolygonRectilinear (rPolyPoly)); 101 102 // Create a new RegionBand object as container of the bands. 103 RegionBand* pRegionBand = new RegionBand(); 104 long nLineId = 0L; 105 106 // Iterate over all polygons. 107 const sal_uInt16 nPolyCount = rPolyPoly.Count(); 108 for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly) 109 { 110 const Polygon& aPoly = rPolyPoly.GetObject(nPoly); 111 112 // Iterate over all edges of the current polygon. 113 const sal_uInt16 nSize = aPoly.GetSize(); 114 if (nSize < 2) 115 continue; 116 // Avoid fetching every point twice (each point is the start point 117 // of one and the end point of another edge.) 118 Point aStart (aPoly.GetPoint(0)); 119 Point aEnd; 120 for (sal_uInt16 nPoint = 1; nPoint <= nSize; ++nPoint, aStart=aEnd) 121 { 122 // We take the implicit closing edge into account by mapping 123 // index nSize to 0. 124 aEnd = aPoly.GetPoint(nPoint%nSize); 125 if (aStart.Y() == aEnd.Y()) 126 { 127 // Horizontal lines are ignored. 128 continue; 129 } 130 131 // At this point the line has to be vertical. 132 OSL_ASSERT(aStart.X() == aEnd.X()); 133 134 // Sort y-coordinates to simplify the algorithm and store the 135 // direction separately. The direction is calculated as it is 136 // in other places (but seems to be the wrong way.) 137 const long nTop (::std::min(aStart.Y(), aEnd.Y())); 138 const long nBottom (::std::max(aStart.Y(), aEnd.Y())); 139 const LineType eLineType (aStart.Y() > aEnd.Y() ? LINE_DESCENDING : LINE_ASCENDING); 140 141 // Make sure that the current line is covered by bands. 142 pRegionBand->ImplAddMissingBands(nTop,nBottom); 143 144 // Find top-most band that may contain nTop. 145 ImplRegionBand* pBand = pRegionBand->ImplGetFirstRegionBand(); 146 while (pBand!=NULL && pBand->mnYBottom < nTop) 147 pBand = pBand->mpNextBand; 148 ImplRegionBand* pTopBand = pBand; 149 // If necessary split the band at nTop so that nTop is contained 150 // in the lower band. 151 if (pBand!=NULL 152 // Prevent the current band from becoming 0 pixel high 153 && pBand->mnYTop<nTop 154 // this allows the lowest pixel of the band to be split off 155 && pBand->mnYBottom>=nTop 156 // do not split a band that is just one pixel high 157 && pBand->mnYTop<pBand->mnYBottom) 158 { 159 // Split the top band. 160 pTopBand = pBand->SplitBand(nTop); 161 } 162 163 // Advance to band that may contain nBottom. 164 while (pBand!=NULL && pBand->mnYBottom < nBottom) 165 pBand = pBand->mpNextBand; 166 // The lowest band may have to be split at nBottom so that 167 // nBottom itself remains in the upper band. 168 if (pBand!=NULL 169 // allow the current band becoming 1 pixel high 170 && pBand->mnYTop<=nBottom 171 // prevent splitting off a band that is 0 pixel high 172 && pBand->mnYBottom>nBottom 173 // do not split a band that is just one pixel high 174 && pBand->mnYTop<pBand->mnYBottom) 175 { 176 // Split the bottom band. 177 pBand->SplitBand(nBottom+1); 178 } 179 180 // Note that we remember the top band (in pTopBand) but not the 181 // bottom band. The later can be determined by comparing y 182 // coordinates. 183 184 // Add the x-value as point to all bands in the nTop->nBottom range. 185 for (pBand=pTopBand; pBand!=NULL&&pBand->mnYTop<=nBottom; pBand=pBand->mpNextBand) 186 pBand->InsertPoint(aStart.X(), nLineId++, true, eLineType); 187 } 188 } 189 190 return pRegionBand; 191 } 192 193 /** Convert a general polygon (one for which ImplIsPolygonRectilinear() 194 returns <FALSE/>) to bands. 195 */ 196 RegionBand* ImplGeneralPolygonToBands(const PolyPolygon& rPolyPoly, const Rectangle& rPolygonBoundingBox) 197 { 198 long nLineID = 0L; 199 200 // initialisation and creation of Bands 201 RegionBand* pRegionBand = new RegionBand(); 202 pRegionBand->CreateBandRange(rPolygonBoundingBox.Top(), rPolygonBoundingBox.Bottom()); 203 204 // insert polygons 205 const sal_uInt16 nPolyCount = rPolyPoly.Count(); 206 207 for ( sal_uInt16 nPoly = 0; nPoly < nPolyCount; nPoly++ ) 208 { 209 // get reference to current polygon 210 const Polygon& aPoly = rPolyPoly.GetObject( nPoly ); 211 const sal_uInt16 nSize = aPoly.GetSize(); 212 213 // not enough points ( <= 2 )? -> nothing to do! 214 if ( nSize <= 2 ) 215 continue; 216 217 // band the polygon 218 for ( sal_uInt16 nPoint = 1; nPoint < nSize; nPoint++ ) 219 { 220 pRegionBand->InsertLine( aPoly.GetPoint(nPoint-1), aPoly.GetPoint(nPoint), nLineID++ ); 221 } 222 223 // close polygon with line from first point to last point, if necessary 224 const Point rLastPoint = aPoly.GetPoint(nSize-1); 225 const Point rFirstPoint = aPoly.GetPoint(0); 226 227 if ( rLastPoint != rFirstPoint ) 228 { 229 pRegionBand->InsertLine( rLastPoint, rFirstPoint, nLineID++ ); 230 } 231 } 232 233 return pRegionBand; 234 } 235 } // end of anonymous namespace 236 237 ////////////////////////////////////////////////////////////////////////////// 238 239 bool Region::IsEmpty() const 240 { 241 return !mbIsNull && !mpB2DPolyPolygon.get() && !mpPolyPolygon.get() && !mpRegionBand.get(); 242 } 243 244 bool Region::IsNull() const 245 { 246 return mbIsNull; 247 } 248 249 RegionBand* ImplCreateRegionBandFromPolyPolygon(const PolyPolygon& rPolyPolygon) 250 { 251 RegionBand* pRetval = 0; 252 253 if(rPolyPolygon.Count()) 254 { 255 // ensure to subdivide when bezier segemnts are used, it's going to 256 // be expanded to rectangles 257 PolyPolygon aPolyPolygon; 258 259 rPolyPolygon.AdaptiveSubdivide(aPolyPolygon); 260 261 if(aPolyPolygon.Count()) 262 { 263 const Rectangle aRect(aPolyPolygon.GetBoundRect()); 264 265 if(!aRect.IsEmpty()) 266 { 267 if(ImplIsPolygonRectilinear(aPolyPolygon)) 268 { 269 // For rectilinear polygons there is an optimized band conversion. 270 pRetval = ImplRectilinearPolygonToBands(aPolyPolygon); 271 } 272 else 273 { 274 pRetval = ImplGeneralPolygonToBands(aPolyPolygon, aRect); 275 } 276 277 // Convert points into seps. 278 if(pRetval) 279 { 280 pRetval->processPoints(); 281 282 // Optimize list of bands. Adjacent bands with identical lists 283 // of seps are joined. 284 if(!pRetval->OptimizeBandList()) 285 { 286 delete pRetval; 287 pRetval = 0; 288 } 289 } 290 } 291 } 292 } 293 294 return pRetval; 295 } 296 297 PolyPolygon Region::ImplCreatePolyPolygonFromRegionBand() const 298 { 299 PolyPolygon aRetval; 300 301 if(getRegionBand()) 302 { 303 RectangleVector aRectangles; 304 GetRegionRectangles(aRectangles); 305 306 for(RectangleVector::const_iterator aRectIter(aRectangles.begin()); aRectIter != aRectangles.end(); aRectIter++) 307 { 308 aRetval.Insert(Polygon(*aRectIter)); 309 } 310 } 311 else 312 { 313 OSL_ENSURE(false, "Called with no local RegionBand (!)"); 314 } 315 316 return aRetval; 317 } 318 319 basegfx::B2DPolyPolygon Region::ImplCreateB2DPolyPolygonFromRegionBand() const 320 { 321 PolyPolygon aPoly(ImplCreatePolyPolygonFromRegionBand()); 322 323 return aPoly.getB2DPolyPolygon(); 324 } 325 326 Region::Region(bool bIsNull) 327 : mpB2DPolyPolygon(), 328 mpPolyPolygon(), 329 mpRegionBand(), 330 mbIsNull(bIsNull) 331 { 332 } 333 334 Region::Region(const Rectangle& rRect) 335 : mpB2DPolyPolygon(), 336 mpPolyPolygon(), 337 mpRegionBand(), 338 mbIsNull(false) 339 { 340 mpRegionBand.reset(rRect.IsEmpty() ? 0 : new RegionBand(rRect)); 341 } 342 343 Region::Region(const Polygon& rPolygon) 344 : mpB2DPolyPolygon(), 345 mpPolyPolygon(), 346 mpRegionBand(), 347 mbIsNull(false) 348 { 349 DBG_CHKOBJ( &rPolygon, Polygon, NULL ); 350 351 if(rPolygon.GetSize()) 352 { 353 ImplCreatePolyPolyRegion(rPolygon); 354 } 355 } 356 357 Region::Region(const PolyPolygon& rPolyPoly) 358 : mpB2DPolyPolygon(), 359 mpPolyPolygon(), 360 mpRegionBand(), 361 mbIsNull(false) 362 { 363 DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); 364 365 if(rPolyPoly.Count()) 366 { 367 ImplCreatePolyPolyRegion(rPolyPoly); 368 } 369 } 370 371 Region::Region(const basegfx::B2DPolyPolygon& rPolyPoly) 372 : mpB2DPolyPolygon(), 373 mpPolyPolygon(), 374 mpRegionBand(), 375 mbIsNull(false) 376 { 377 DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); 378 379 if(rPolyPoly.count()) 380 { 381 ImplCreatePolyPolyRegion(rPolyPoly); 382 } 383 } 384 385 Region::Region(const Region& rRegion) 386 : mpB2DPolyPolygon(rRegion.mpB2DPolyPolygon), 387 mpPolyPolygon(rRegion.mpPolyPolygon), 388 mpRegionBand(rRegion.mpRegionBand), 389 mbIsNull(rRegion.mbIsNull) 390 { 391 } 392 393 Region::~Region() 394 { 395 } 396 397 void Region::ImplCreatePolyPolyRegion( const PolyPolygon& rPolyPoly ) 398 { 399 const sal_uInt16 nPolyCount = rPolyPoly.Count(); 400 401 if(nPolyCount) 402 { 403 // polypolygon empty? -> empty region 404 const Rectangle aRect(rPolyPoly.GetBoundRect()); 405 406 if(!aRect.IsEmpty()) 407 { 408 // width OR height == 1 ? => Rectangular region 409 if((1 == aRect.GetWidth()) || (1 == aRect.GetHeight()) || rPolyPoly.IsRect()) 410 { 411 mpRegionBand.reset(new RegionBand(aRect)); 412 } 413 else 414 { 415 mpPolyPolygon.reset(new PolyPolygon(rPolyPoly)); 416 } 417 418 mbIsNull = false; 419 } 420 } 421 } 422 423 void Region::ImplCreatePolyPolyRegion( const basegfx::B2DPolyPolygon& rPolyPoly ) 424 { 425 if(rPolyPoly.count() && !rPolyPoly.getB2DRange().isEmpty()) 426 { 427 mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(rPolyPoly)); 428 mbIsNull = false; 429 } 430 } 431 432 void Region::Move( long nHorzMove, long nVertMove ) 433 { 434 if(IsNull() || IsEmpty()) 435 { 436 // empty or null need no move 437 return; 438 } 439 440 if(!nHorzMove && !nVertMove) 441 { 442 // no move defined 443 return; 444 } 445 446 if(getB2DPolyPolygon()) 447 { 448 basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon()); 449 450 aPoly.transform(basegfx::tools::createTranslateB2DHomMatrix(nHorzMove, nVertMove)); 451 mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : 0); 452 mpPolyPolygon.reset(); 453 mpRegionBand.reset(); 454 } 455 else if(getPolyPolygon()) 456 { 457 PolyPolygon aPoly(*getPolyPolygon()); 458 459 aPoly.Move(nHorzMove, nVertMove); 460 mpB2DPolyPolygon.reset(); 461 mpPolyPolygon.reset(aPoly.Count() ? new PolyPolygon(aPoly) : 0); 462 mpRegionBand.reset(); 463 } 464 else if(getRegionBand()) 465 { 466 RegionBand* pNew = new RegionBand(*getRegionBand()); 467 468 pNew->Move(nHorzMove, nVertMove); 469 mpB2DPolyPolygon.reset(); 470 mpPolyPolygon.reset(); 471 mpRegionBand.reset(pNew); 472 } 473 else 474 { 475 OSL_ENSURE(false, "Region::Move error: impossible combination (!)"); 476 } 477 } 478 479 void Region::Scale( double fScaleX, double fScaleY ) 480 { 481 if(IsNull() || IsEmpty()) 482 { 483 // empty or null need no scale 484 return; 485 } 486 487 if(basegfx::fTools::equalZero(fScaleX) && basegfx::fTools::equalZero(fScaleY)) 488 { 489 // no scale defined 490 return; 491 } 492 493 if(getB2DPolyPolygon()) 494 { 495 basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon()); 496 497 aPoly.transform(basegfx::tools::createScaleB2DHomMatrix(fScaleX, fScaleY)); 498 mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : 0); 499 mpPolyPolygon.reset(); 500 mpRegionBand.reset(); 501 } 502 else if(getPolyPolygon()) 503 { 504 PolyPolygon aPoly(*getPolyPolygon()); 505 506 aPoly.Scale(fScaleX, fScaleY); 507 mpB2DPolyPolygon.reset(); 508 mpPolyPolygon.reset(aPoly.Count() ? new PolyPolygon(aPoly) : 0); 509 mpRegionBand.reset(); 510 } 511 else if(getRegionBand()) 512 { 513 RegionBand* pNew = new RegionBand(*getRegionBand()); 514 515 pNew->Scale(fScaleX, fScaleY); 516 mpB2DPolyPolygon.reset(); 517 mpPolyPolygon.reset(); 518 mpRegionBand.reset(pNew); 519 } 520 else 521 { 522 OSL_ENSURE(false, "Region::Scale error: impossible combination (!)"); 523 } 524 } 525 526 bool Region::Union( const Rectangle& rRect ) 527 { 528 if(rRect.IsEmpty()) 529 { 530 // empty rectangle will not expand the existing union, nothing to do 531 return true; 532 } 533 534 if(IsEmpty()) 535 { 536 // no local data, the union will be equal to source. Create using rectangle 537 *this = rRect; 538 return true; 539 } 540 541 if(HasPolyPolygonOrB2DPolyPolygon()) 542 { 543 // get this B2DPolyPolygon, solve on polygon base 544 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon()); 545 546 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation(aThisPolyPoly); 547 548 if(!aThisPolyPoly.count()) 549 { 550 // no local polygon, use the rectangle as new region 551 *this = rRect; 552 } 553 else 554 { 555 // get the other B2DPolyPolygon and use logical Or-Operation 556 const basegfx::B2DPolygon aRectPoly( 557 basegfx::tools::createPolygonFromRect( 558 basegfx::B2DRectangle( 559 rRect.Left(), 560 rRect.Top(), 561 rRect.Right(), 562 rRect.Bottom()))); 563 const basegfx::B2DPolyPolygon aClip( 564 basegfx::tools::solvePolygonOperationOr( 565 aThisPolyPoly, 566 basegfx::B2DPolyPolygon(aRectPoly))); 567 *this = Region(aClip); 568 } 569 570 return true; 571 } 572 573 // only region band mode possibility left here or null/empty 574 const RegionBand* pCurrent = getRegionBand(); 575 576 if(!pCurrent) 577 { 578 // no region band, create using the rectangle 579 *this = rRect; 580 return true; 581 } 582 583 RegionBand* pNew = new RegionBand(*pCurrent); 584 585 // get justified rectangle 586 const long nLeft(std::min(rRect.Left(), rRect.Right())); 587 const long nTop(std::min(rRect.Top(), rRect.Bottom())); 588 const long nRight(std::max(rRect.Left(), rRect.Right())); 589 const long nBottom(std::max(rRect.Top(), rRect.Bottom())); 590 591 // insert bands if the boundaries are not already in the list 592 pNew->InsertBands(nTop, nBottom); 593 594 // process union 595 pNew->Union(nLeft, nTop, nRight, nBottom); 596 597 // cleanup 598 if(!pNew->OptimizeBandList()) 599 { 600 delete pNew; 601 pNew = 0; 602 } 603 604 mpRegionBand.reset(pNew); 605 return true; 606 } 607 608 bool Region::Intersect( const Rectangle& rRect ) 609 { 610 if ( rRect.IsEmpty() ) 611 { 612 // empty rectangle will create empty region 613 SetEmpty(); 614 return true; 615 } 616 617 if(IsNull()) 618 { 619 // null region (everything) intersect with rect will give rect 620 *this = rRect; 621 return true; 622 } 623 624 if(IsEmpty()) 625 { 626 // no content, cannot get more empty 627 return true; 628 } 629 630 if(HasPolyPolygonOrB2DPolyPolygon()) 631 { 632 // if polygon data prefer double precision, the other will be lost (if buffered) 633 if(getB2DPolyPolygon()) 634 { 635 const basegfx::B2DPolyPolygon aPoly( 636 basegfx::tools::clipPolyPolygonOnRange( 637 *getB2DPolyPolygon(), 638 basegfx::B2DRange( 639 rRect.Left(), 640 rRect.Top(), 641 rRect.Right() + 1, 642 rRect.Bottom() + 1), 643 true, 644 false)); 645 646 mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : 0); 647 mpPolyPolygon.reset(); 648 mpRegionBand.reset(); 649 } 650 else // if(getPolyPolygon()) 651 { 652 PolyPolygon aPoly(*getPolyPolygon()); 653 654 // use the PolyPolygon::Clip method for rectangles, this is 655 // fairly simple (does not even use GPC) and saves us from 656 // unnecessary banding 657 aPoly.Clip(rRect); 658 659 mpB2DPolyPolygon.reset(); 660 mpPolyPolygon.reset(aPoly.Count() ? new PolyPolygon(aPoly) : 0); 661 mpRegionBand.reset(); 662 } 663 664 return true; 665 } 666 667 // only region band mode possibility left here or null/empty 668 const RegionBand* pCurrent = getRegionBand(); 669 670 if(!pCurrent) 671 { 672 // region is empty -> nothing to do! 673 return true; 674 } 675 676 RegionBand* pNew = new RegionBand(*pCurrent); 677 678 // get justified rectangle 679 const long nLeft(std::min(rRect.Left(), rRect.Right())); 680 const long nTop(std::min(rRect.Top(), rRect.Bottom())); 681 const long nRight(std::max(rRect.Left(), rRect.Right())); 682 const long nBottom(std::max(rRect.Top(), rRect.Bottom())); 683 684 // insert bands if the boundaries are not already in the list 685 pNew->InsertBands(nTop, nBottom); 686 687 // process intersect 688 pNew->Intersect(nLeft, nTop, nRight, nBottom); 689 690 // cleanup 691 if(!pNew->OptimizeBandList()) 692 { 693 delete pNew; 694 pNew = 0; 695 } 696 697 mpRegionBand.reset(pNew); 698 return true; 699 } 700 701 bool Region::Exclude( const Rectangle& rRect ) 702 { 703 if ( rRect.IsEmpty() ) 704 { 705 // excluding nothing will do no change 706 return true; 707 } 708 709 if(IsEmpty()) 710 { 711 // cannot exclude from empty, done 712 return true; 713 } 714 715 if(IsNull()) 716 { 717 // error; cannot exclude from null region since this is not representable 718 // in the data 719 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)"); 720 return true; 721 } 722 723 if( HasPolyPolygonOrB2DPolyPolygon() ) 724 { 725 // get this B2DPolyPolygon 726 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon()); 727 728 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation(aThisPolyPoly); 729 730 if(!aThisPolyPoly.count()) 731 { 732 // when local polygon is empty, nothing can be excluded 733 return true; 734 } 735 736 // get the other B2DPolyPolygon 737 const basegfx::B2DPolygon aRectPoly( 738 basegfx::tools::createPolygonFromRect( 739 basegfx::B2DRectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()))); 740 const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly); 741 const basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationDiff(aThisPolyPoly, aOtherPolyPoly); 742 743 *this = Region(aClip); 744 745 return true; 746 } 747 748 // only region band mode possibility left here or null/empty 749 const RegionBand* pCurrent = getRegionBand(); 750 751 if(!pCurrent) 752 { 753 // empty? -> done! 754 return true; 755 } 756 757 RegionBand* pNew = new RegionBand(*pCurrent); 758 759 // get justified rectangle 760 const long nLeft(std::min(rRect.Left(), rRect.Right())); 761 const long nTop(std::min(rRect.Top(), rRect.Bottom())); 762 const long nRight(std::max(rRect.Left(), rRect.Right())); 763 const long nBottom(std::max(rRect.Top(), rRect.Bottom())); 764 765 // insert bands if the boundaries are not already in the list 766 pNew->InsertBands(nTop, nBottom); 767 768 // process exclude 769 pNew->Exclude(nLeft, nTop, nRight, nBottom); 770 771 // cleanup 772 if(!pNew->OptimizeBandList()) 773 { 774 delete pNew; 775 pNew = 0; 776 } 777 778 mpRegionBand.reset(pNew); 779 return true; 780 } 781 782 bool Region::XOr( const Rectangle& rRect ) 783 { 784 if ( rRect.IsEmpty() ) 785 { 786 // empty rectangle will not change local content 787 return true; 788 } 789 790 if(IsEmpty()) 791 { 792 // rRect will be the xored-form (local off, rect on) 793 *this = rRect; 794 return true; 795 } 796 797 if(IsNull()) 798 { 799 // error; cannot exclude from null region since this is not representable 800 // in the data 801 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)"); 802 return true; 803 } 804 805 if( HasPolyPolygonOrB2DPolyPolygon() ) 806 { 807 // get this B2DPolyPolygon 808 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon()); 809 810 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 811 812 if(!aThisPolyPoly.count()) 813 { 814 // no local content, XOr will be equal to rectangle 815 *this = rRect; 816 return true; 817 } 818 819 // get the other B2DPolyPolygon 820 const basegfx::B2DPolygon aRectPoly( 821 basegfx::tools::createPolygonFromRect( 822 basegfx::B2DRectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()))); 823 const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly); 824 const basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationXor(aThisPolyPoly, aOtherPolyPoly); 825 826 *this = Region(aClip); 827 828 return true; 829 } 830 831 // only region band mode possibility left here or null/empty 832 const RegionBand* pCurrent = getRegionBand(); 833 834 if(!pCurrent) 835 { 836 // rRect will be the xored-form (local off, rect on) 837 *this = rRect; 838 return true; 839 } 840 841 // only region band mode possibility left here or null/empty 842 RegionBand* pNew = new RegionBand(*getRegionBand()); 843 844 // get justified rectangle 845 const long nLeft(std::min(rRect.Left(), rRect.Right())); 846 const long nTop(std::min(rRect.Top(), rRect.Bottom())); 847 const long nRight(std::max(rRect.Left(), rRect.Right())); 848 const long nBottom(std::max(rRect.Top(), rRect.Bottom())); 849 850 // insert bands if the boundaries are not already in the list 851 pNew->InsertBands(nTop, nBottom); 852 853 // process xor 854 pNew->XOr(nLeft, nTop, nRight, nBottom); 855 856 // cleanup 857 if(!pNew->OptimizeBandList()) 858 { 859 delete pNew; 860 pNew = 0; 861 } 862 863 mpRegionBand.reset(pNew); 864 return true; 865 } 866 867 bool Region::Union( const Region& rRegion ) 868 { 869 if(rRegion.IsEmpty()) 870 { 871 // no extension at all 872 return true; 873 } 874 875 if(rRegion.IsNull()) 876 { 877 // extending with null region -> null region 878 *this = Region(true); 879 return true; 880 } 881 882 if(IsEmpty()) 883 { 884 // local is empty, union will give source region 885 *this = rRegion; 886 return true; 887 } 888 889 if(IsNull()) 890 { 891 // already fully expanded (is null region), cannot be extended 892 return true; 893 } 894 895 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() ) 896 { 897 // get this B2DPolyPolygon 898 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon()); 899 900 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation(aThisPolyPoly); 901 902 if(!aThisPolyPoly.count()) 903 { 904 // when no local content, union will be equal to rRegion 905 *this = rRegion; 906 return true; 907 } 908 909 // get the other B2DPolyPolygon 910 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon()); 911 aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation(aOtherPolyPoly); 912 913 // use logical OR operation 914 basegfx::B2DPolyPolygon aClip(basegfx::tools::solvePolygonOperationOr(aThisPolyPoly, aOtherPolyPoly)); 915 916 *this = Region( aClip ); 917 return true; 918 } 919 920 // only region band mode possibility left here or null/empty 921 const RegionBand* pCurrent = getRegionBand(); 922 923 if(!pCurrent) 924 { 925 // local is empty, union will give source region 926 *this = rRegion; 927 return true; 928 } 929 930 const RegionBand* pSource = rRegion.getRegionBand(); 931 932 if(!pSource) 933 { 934 // no extension at all 935 return true; 936 } 937 938 // prepare source and target 939 RegionBand* pNew = new RegionBand(*pCurrent); 940 941 // union with source 942 pNew->Union(*pSource); 943 944 // cleanup 945 if(!pNew->OptimizeBandList()) 946 { 947 delete pNew; 948 pNew = 0; 949 } 950 951 mpRegionBand.reset(pNew); 952 return true; 953 } 954 955 bool Region::Intersect( const Region& rRegion ) 956 { 957 // same instance data? -> nothing to do! 958 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon()) 959 { 960 return true; 961 } 962 963 if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon()) 964 { 965 return true; 966 } 967 968 if(getRegionBand() && getRegionBand() == rRegion.getRegionBand()) 969 { 970 return true; 971 } 972 973 if(rRegion.IsNull()) 974 { 975 // source region is null-region, intersect will not change local region 976 return true; 977 } 978 979 if(IsNull()) 980 { 981 // when local region is null-region, intersect will be equal to source 982 *this = rRegion; 983 return true; 984 } 985 986 if(rRegion.IsEmpty()) 987 { 988 // source region is empty, intersection will always be empty 989 SetEmpty(); 990 return true; 991 } 992 993 if(IsEmpty()) 994 { 995 // local region is empty, cannot get more empty than that. Nothing to do 996 return true; 997 } 998 999 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() ) 1000 { 1001 // get this B2DPolyPolygon 1002 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon()); 1003 1004 if(!aThisPolyPoly.count()) 1005 { 1006 // local region is empty, cannot get more empty than that. Nothing to do 1007 return true; 1008 } 1009 1010 // get the other B2DPolyPolygon 1011 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon()); 1012 1013 if(!aOtherPolyPoly.count()) 1014 { 1015 // source region is empty, intersection will always be empty 1016 SetEmpty(); 1017 return true; 1018 } 1019 1020 const basegfx::B2DPolyPolygon aClip( 1021 basegfx::tools::clipPolyPolygonOnPolyPolygon( 1022 aOtherPolyPoly, 1023 aThisPolyPoly, 1024 true, 1025 false)); 1026 *this = Region( aClip ); 1027 return true; 1028 } 1029 1030 // only region band mode possibility left here or null/empty 1031 const RegionBand* pCurrent = getRegionBand(); 1032 1033 if(!pCurrent) 1034 { 1035 // local region is empty, cannot get more empty than that. Nothing to do 1036 return true; 1037 } 1038 1039 const RegionBand* pSource = rRegion.getRegionBand(); 1040 1041 if(!pSource) 1042 { 1043 // source region is empty, intersection will always be empty 1044 SetEmpty(); 1045 return true; 1046 } 1047 1048 // both RegionBands exist and are not empty 1049 if(pCurrent->getRectangleCount() + 2 < pSource->getRectangleCount()) 1050 { 1051 // when we have less rectangles, turn around the call 1052 Region aTempRegion = rRegion; 1053 aTempRegion.Intersect( *this ); 1054 *this = aTempRegion; 1055 } 1056 else 1057 { 1058 // prepare new regionBand 1059 RegionBand* pNew = pCurrent ? new RegionBand(*pCurrent) : new RegionBand(); 1060 1061 // intersect with source 1062 pNew->Intersect(*pSource); 1063 1064 // cleanup 1065 if(!pNew->OptimizeBandList()) 1066 { 1067 delete pNew; 1068 pNew = 0; 1069 } 1070 1071 mpRegionBand.reset(pNew); 1072 } 1073 1074 return true; 1075 } 1076 1077 bool Region::Exclude( const Region& rRegion ) 1078 { 1079 if ( rRegion.IsEmpty() ) 1080 { 1081 // excluding nothing will do no change 1082 return true; 1083 } 1084 1085 if ( rRegion.IsNull() ) 1086 { 1087 // excluding everything will create empty region 1088 SetEmpty(); 1089 return true; 1090 } 1091 1092 if(IsEmpty()) 1093 { 1094 // cannot exclude from empty, done 1095 return true; 1096 } 1097 1098 if(IsNull()) 1099 { 1100 // error; cannot exclude from null region since this is not representable 1101 // in the data 1102 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)"); 1103 return true; 1104 } 1105 1106 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() ) 1107 { 1108 // get this B2DPolyPolygon 1109 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon()); 1110 1111 if(!aThisPolyPoly.count()) 1112 { 1113 // cannot exclude from empty, done 1114 return true; 1115 } 1116 1117 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 1118 1119 // get the other B2DPolyPolygon 1120 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon()); 1121 aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly ); 1122 1123 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationDiff( aThisPolyPoly, aOtherPolyPoly ); 1124 *this = Region( aClip ); 1125 return true; 1126 } 1127 1128 // only region band mode possibility left here or null/empty 1129 const RegionBand* pCurrent = getRegionBand(); 1130 1131 if(!pCurrent) 1132 { 1133 // cannot exclude from empty, done 1134 return true; 1135 } 1136 1137 const RegionBand* pSource = rRegion.getRegionBand(); 1138 1139 if(!pSource) 1140 { 1141 // excluding nothing will do no change 1142 return true; 1143 } 1144 1145 // prepare source and target 1146 RegionBand* pNew = new RegionBand(*pCurrent); 1147 1148 // union with source 1149 const bool bSuccess(pNew->Exclude(*pSource)); 1150 1151 // cleanup 1152 if(!bSuccess) 1153 { 1154 delete pNew; 1155 pNew = 0; 1156 } 1157 1158 mpRegionBand.reset(pNew); 1159 return true; 1160 } 1161 1162 bool Region::XOr( const Region& rRegion ) 1163 { 1164 if ( rRegion.IsEmpty() ) 1165 { 1166 // empty region will not change local content 1167 return true; 1168 } 1169 1170 if ( rRegion.IsNull() ) 1171 { 1172 // error; cannot exclude null region from local since this is not representable 1173 // in the data 1174 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)"); 1175 return true; 1176 } 1177 1178 if(IsEmpty()) 1179 { 1180 // rRect will be the xored-form (local off, rect on) 1181 *this = rRegion; 1182 return true; 1183 } 1184 1185 if(IsNull()) 1186 { 1187 // error: cannot exclude from null region since this is not representable 1188 // in the data 1189 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)"); 1190 return false; 1191 } 1192 1193 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() ) 1194 { 1195 // get this B2DPolyPolygon 1196 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon()); 1197 1198 if(!aThisPolyPoly.count()) 1199 { 1200 // rRect will be the xored-form (local off, rect on) 1201 *this = rRegion; 1202 return true; 1203 } 1204 1205 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); 1206 1207 // get the other B2DPolyPolygon 1208 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon()); 1209 aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly ); 1210 1211 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationXor( aThisPolyPoly, aOtherPolyPoly ); 1212 *this = Region( aClip ); 1213 return true; 1214 } 1215 1216 // only region band mode possibility left here or null/empty 1217 const RegionBand* pCurrent = getRegionBand(); 1218 1219 if(!pCurrent) 1220 { 1221 // rRect will be the xored-form (local off, rect on) 1222 *this = rRegion; 1223 return true; 1224 } 1225 1226 const RegionBand* pSource = rRegion.getRegionBand(); 1227 1228 if(!pSource) 1229 { 1230 // empty region will not change local content 1231 return true; 1232 } 1233 1234 // prepare source and target 1235 RegionBand* pNew = new RegionBand(*pCurrent); 1236 1237 // union with source 1238 pNew->XOr(*pSource); 1239 1240 // cleanup 1241 if(!pNew->OptimizeBandList()) 1242 { 1243 delete pNew; 1244 pNew = 0; 1245 } 1246 1247 mpRegionBand.reset(pNew); 1248 1249 return true; 1250 } 1251 1252 Rectangle Region::GetBoundRect() const 1253 { 1254 if(IsEmpty()) 1255 { 1256 // no internal data? -> region is empty! 1257 return Rectangle(); 1258 } 1259 1260 if(IsNull()) 1261 { 1262 // error; null region has no BoundRect 1263 // OSL_ENSURE(false, "Region::GetBoundRect error: null region has unlimited bound rect, not representable (!)"); 1264 return Rectangle(); 1265 } 1266 1267 // prefer double precision source 1268 if(getB2DPolyPolygon()) 1269 { 1270 const basegfx::B2DRange aRange(basegfx::tools::getRange(*getB2DPolyPolygon())); 1271 1272 if(aRange.isEmpty()) 1273 { 1274 // emulate PolyPolygon::GetBoundRect() when empty polygon 1275 return Rectangle(); 1276 } 1277 else 1278 { 1279 // #122149# corrected rounding, no need for ceil() and floor() here 1280 return Rectangle( 1281 basegfx::fround(aRange.getMinX()), basegfx::fround(aRange.getMinY()), 1282 basegfx::fround(aRange.getMaxX()), basegfx::fround(aRange.getMaxY())); 1283 } 1284 } 1285 1286 if(getPolyPolygon()) 1287 { 1288 return getPolyPolygon()->GetBoundRect(); 1289 } 1290 1291 if(getRegionBand()) 1292 { 1293 return getRegionBand()->GetBoundRect(); 1294 } 1295 1296 return Rectangle(); 1297 } 1298 1299 const PolyPolygon Region::GetAsPolyPolygon() const 1300 { 1301 if(getPolyPolygon()) 1302 { 1303 return *getPolyPolygon(); 1304 } 1305 1306 if(getB2DPolyPolygon()) 1307 { 1308 // the polygon needs to be converted, buffer the down conversion 1309 const PolyPolygon aPolyPolgon(*getB2DPolyPolygon()); 1310 const_cast< Region* >(this)->mpPolyPolygon.reset(new PolyPolygon(aPolyPolgon)); 1311 1312 return *getPolyPolygon(); 1313 } 1314 1315 if(getRegionBand()) 1316 { 1317 // the BandRegion needs to be converted, buffer the conversion 1318 const PolyPolygon aPolyPolgon(ImplCreatePolyPolygonFromRegionBand()); 1319 const_cast< Region* >(this)->mpPolyPolygon.reset(new PolyPolygon(aPolyPolgon)); 1320 1321 return *getPolyPolygon(); 1322 } 1323 1324 return PolyPolygon(); 1325 } 1326 1327 const basegfx::B2DPolyPolygon Region::GetAsB2DPolyPolygon() const 1328 { 1329 if(getB2DPolyPolygon()) 1330 { 1331 return *getB2DPolyPolygon(); 1332 } 1333 1334 if(getPolyPolygon()) 1335 { 1336 // the polygon needs to be converted, buffer the up conversion. This will be preferred from now. 1337 const basegfx::B2DPolyPolygon aB2DPolyPolygon(getPolyPolygon()->getB2DPolyPolygon()); 1338 const_cast< Region* >(this)->mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(aB2DPolyPolygon)); 1339 1340 return *getB2DPolyPolygon(); 1341 } 1342 1343 if(getRegionBand()) 1344 { 1345 // the BandRegion needs to be converted, buffer the conversion 1346 const basegfx::B2DPolyPolygon aB2DPolyPolygon(ImplCreateB2DPolyPolygonFromRegionBand()); 1347 const_cast< Region* >(this)->mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(aB2DPolyPolygon)); 1348 1349 return *getB2DPolyPolygon(); 1350 } 1351 1352 return basegfx::B2DPolyPolygon(); 1353 } 1354 1355 const RegionBand* Region::GetAsRegionBand() const 1356 { 1357 if(!getRegionBand()) 1358 { 1359 if(getB2DPolyPolygon()) 1360 { 1361 // convert B2DPolyPolygon to RegionBand, buffer it and return it 1362 const_cast< Region* >(this)->mpRegionBand.reset(ImplCreateRegionBandFromPolyPolygon(PolyPolygon(*getB2DPolyPolygon()))); 1363 } 1364 else if(getPolyPolygon()) 1365 { 1366 // convert B2DPolyPolygon to RegionBand, buffer it and return it 1367 const_cast< Region* >(this)->mpRegionBand.reset(ImplCreateRegionBandFromPolyPolygon(*getPolyPolygon())); 1368 } 1369 } 1370 1371 return getRegionBand(); 1372 } 1373 1374 bool Region::IsInside( const Point& rPoint ) const 1375 { 1376 if(IsEmpty()) 1377 { 1378 // no point can be in empty region 1379 return false; 1380 } 1381 1382 if(IsNull()) 1383 { 1384 // all points are inside null-region 1385 return true; 1386 } 1387 1388 // Too expensive (?) 1389 //if(mpImplRegion->getRegionPolyPoly()) 1390 //{ 1391 // return mpImplRegion->getRegionPolyPoly()->IsInside( rPoint ); 1392 //} 1393 1394 // ensure RegionBand existence 1395 const RegionBand* pRegionBand = GetAsRegionBand(); 1396 1397 if(pRegionBand) 1398 { 1399 return pRegionBand->IsInside(rPoint); 1400 } 1401 1402 return false; 1403 } 1404 1405 bool Region::IsInside( const Rectangle& rRect ) const 1406 { 1407 if(IsEmpty()) 1408 { 1409 // no rectangle can be in empty region 1410 return false; 1411 } 1412 1413 if(IsNull()) 1414 { 1415 // rectangle always inside null-region 1416 return true; 1417 } 1418 1419 if ( rRect.IsEmpty() ) 1420 { 1421 // is rectangle empty? -> not inside 1422 return false; 1423 } 1424 1425 // create region from rectangle and intersect own region 1426 Region aRegion(rRect); 1427 aRegion.Exclude(*this); 1428 1429 // rectangle is inside if exclusion is empty 1430 return aRegion.IsEmpty(); 1431 } 1432 1433 // ----------------------------------------------------------------------- 1434 1435 bool Region::IsOver( const Rectangle& rRect ) const 1436 { 1437 if(IsEmpty()) 1438 { 1439 // nothing can be over something empty 1440 return false; 1441 } 1442 1443 if(IsNull()) 1444 { 1445 // everything is over null region 1446 return true; 1447 } 1448 1449 // Can we optimize this ??? - is used in Draw for brushes pointers 1450 // Why we have no IsOver for Regions ??? 1451 // create region from rectangle and intersect own region 1452 Region aRegion(rRect); 1453 aRegion.Intersect( *this ); 1454 1455 // rectangle is over if include is not empty 1456 return !aRegion.IsEmpty(); 1457 } 1458 1459 void Region::SetNull() 1460 { 1461 // reset all content 1462 mpB2DPolyPolygon.reset(); 1463 mpPolyPolygon.reset(); 1464 mpRegionBand.reset(); 1465 mbIsNull = true; 1466 } 1467 1468 void Region::SetEmpty() 1469 { 1470 // reset all content 1471 mpB2DPolyPolygon.reset(); 1472 mpPolyPolygon.reset(); 1473 mpRegionBand.reset(); 1474 mbIsNull = false; 1475 } 1476 1477 Region& Region::operator=( const Region& rRegion ) 1478 { 1479 // reset all content 1480 mpB2DPolyPolygon = rRegion.mpB2DPolyPolygon; 1481 mpPolyPolygon = rRegion.mpPolyPolygon; 1482 mpRegionBand = rRegion.mpRegionBand; 1483 mbIsNull = rRegion.mbIsNull; 1484 1485 return *this; 1486 } 1487 1488 Region& Region::operator=( const Rectangle& rRect ) 1489 { 1490 mpB2DPolyPolygon.reset(); 1491 mpPolyPolygon.reset(); 1492 mpRegionBand.reset(rRect.IsEmpty() ? 0 : new RegionBand(rRect)); 1493 mbIsNull = false; 1494 1495 return *this; 1496 } 1497 1498 bool Region::operator==( const Region& rRegion ) const 1499 { 1500 if(IsNull() && rRegion.IsNull()) 1501 { 1502 // both are null region 1503 return true; 1504 } 1505 1506 if(IsEmpty() && rRegion.IsEmpty()) 1507 { 1508 // both are empty 1509 return true; 1510 } 1511 1512 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon()) 1513 { 1514 // same instance data? -> equal 1515 return true; 1516 } 1517 1518 if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon()) 1519 { 1520 // same instance data? -> equal 1521 return true; 1522 } 1523 1524 if(getRegionBand() && getRegionBand() == rRegion.getRegionBand()) 1525 { 1526 // same instance data? -> equal 1527 return true; 1528 } 1529 1530 if(IsNull() || IsEmpty()) 1531 { 1532 return false; 1533 } 1534 1535 if(rRegion.IsNull() || rRegion.IsEmpty()) 1536 { 1537 return false; 1538 } 1539 1540 if(rRegion.getB2DPolyPolygon() || getB2DPolyPolygon()) 1541 { 1542 // one of both has a B2DPolyPolygon based region, ensure both have it 1543 // by evtl. conversion 1544 const_cast< Region* >(this)->GetAsB2DPolyPolygon(); 1545 const_cast< Region& >(rRegion).GetAsB2DPolyPolygon(); 1546 1547 return *rRegion.getB2DPolyPolygon() == *getB2DPolyPolygon(); 1548 } 1549 1550 if(rRegion.getPolyPolygon() || getPolyPolygon()) 1551 { 1552 // one of both has a B2DPolyPolygon based region, ensure both have it 1553 // by evtl. conversion 1554 const_cast< Region* >(this)->GetAsPolyPolygon(); 1555 const_cast< Region& >(rRegion).GetAsPolyPolygon(); 1556 1557 return *rRegion.getPolyPolygon() == *getPolyPolygon(); 1558 } 1559 1560 // both are not empty or null (see above) and if content supported polygon 1561 // data the comparison is already done. Only both on RegionBand base can be left, 1562 // but better check 1563 if(rRegion.getRegionBand() && getRegionBand()) 1564 { 1565 return *rRegion.getRegionBand() == *getRegionBand(); 1566 } 1567 1568 // should not happen, but better deny equality 1569 return false; 1570 } 1571 1572 SvStream& operator>>(SvStream& rIStrm, Region& rRegion) 1573 { 1574 VersionCompat aCompat(rIStrm, STREAM_READ); 1575 sal_uInt16 nVersion(0); 1576 sal_uInt16 nTmp16(0); 1577 1578 // clear region to be loaded 1579 rRegion.SetEmpty(); 1580 1581 // get version of streamed region 1582 rIStrm >> nVersion; 1583 1584 // get type of region 1585 rIStrm >> nTmp16; 1586 1587 enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX }; 1588 RegionType meStreamedType = (RegionType)nTmp16; 1589 1590 switch(meStreamedType) 1591 { 1592 case REGION_NULL: 1593 { 1594 rRegion.SetNull(); 1595 break; 1596 } 1597 1598 case REGION_EMPTY: 1599 { 1600 rRegion.SetEmpty(); 1601 break; 1602 } 1603 1604 default: 1605 { 1606 RegionBand* pNewRegionBand = new RegionBand(); 1607 pNewRegionBand->load(rIStrm); 1608 rRegion.mpRegionBand.reset(pNewRegionBand); 1609 1610 if(aCompat.GetVersion() >= 2) 1611 { 1612 sal_Bool bHasPolyPolygon(sal_False); 1613 1614 rIStrm >> bHasPolyPolygon; 1615 1616 if(bHasPolyPolygon) 1617 { 1618 PolyPolygon* pNewPoly = new PolyPolygon(); 1619 rIStrm >> *pNewPoly; 1620 rRegion.mpPolyPolygon.reset(pNewPoly); 1621 } 1622 } 1623 1624 break; 1625 } 1626 } 1627 1628 return rIStrm; 1629 } 1630 1631 SvStream& operator<<( SvStream& rOStrm, const Region& rRegion ) 1632 { 1633 const sal_uInt16 nVersion(2); 1634 VersionCompat aCompat(rOStrm, STREAM_WRITE, nVersion); 1635 1636 // put version 1637 rOStrm << nVersion; 1638 1639 // put type 1640 enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX }; 1641 RegionType aRegionType(REGION_COMPLEX); 1642 bool bEmpty(rRegion.IsEmpty()); 1643 1644 if(!bEmpty && rRegion.getB2DPolyPolygon() && 0 == rRegion.getB2DPolyPolygon()->count()) 1645 { 1646 OSL_ENSURE(false, "Region with empty B2DPolyPolygon, should not be created (!)"); 1647 bEmpty = true; 1648 } 1649 1650 if(!bEmpty && rRegion.getPolyPolygon() && 0 == rRegion.getPolyPolygon()->Count()) 1651 { 1652 OSL_ENSURE(false, "Region with empty PolyPolygon, should not be created (!)"); 1653 bEmpty = true; 1654 } 1655 1656 if(bEmpty) 1657 { 1658 aRegionType = REGION_EMPTY; 1659 } 1660 else if(rRegion.IsNull()) 1661 { 1662 aRegionType = REGION_NULL; 1663 } 1664 else if(rRegion.getRegionBand() && rRegion.getRegionBand()->isSingleRectangle()) 1665 { 1666 aRegionType = REGION_RECTANGLE; 1667 } 1668 1669 rOStrm << (sal_uInt16)aRegionType; 1670 1671 // get RegionBand 1672 const RegionBand* pRegionBand = rRegion.getRegionBand(); 1673 1674 if(pRegionBand) 1675 { 1676 pRegionBand->save(rOStrm); 1677 } 1678 else 1679 { 1680 // for compatibility, write an empty RegionBand (will only write 1681 // the end marker STREAMENTRY_END, but this *is* needed) 1682 const RegionBand aRegionBand; 1683 1684 aRegionBand.save(rOStrm); 1685 } 1686 1687 // write polypolygon if available 1688 const sal_Bool bHasPolyPolygon(rRegion.HasPolyPolygonOrB2DPolyPolygon()); 1689 rOStrm << bHasPolyPolygon; 1690 1691 if(bHasPolyPolygon) 1692 { 1693 // #i105373# 1694 PolyPolygon aNoCurvePolyPolygon; 1695 rRegion.GetAsPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon); 1696 1697 rOStrm << aNoCurvePolyPolygon; 1698 } 1699 1700 return rOStrm; 1701 } 1702 1703 void Region::GetRegionRectangles(RectangleVector& rTarget) const 1704 { 1705 // clear returnvalues 1706 rTarget.clear(); 1707 1708 // ensure RegionBand existence 1709 const RegionBand* pRegionBand = GetAsRegionBand(); 1710 1711 if(pRegionBand) 1712 { 1713 pRegionBand->GetRegionRectangles(rTarget); 1714 } 1715 } 1716 1717 static inline bool ImplPolygonRectTest( const Polygon& rPoly, Rectangle* pRectOut = NULL ) 1718 { 1719 bool bIsRect = false; 1720 const Point* pPoints = rPoly.GetConstPointAry(); 1721 sal_uInt16 nPoints = rPoly.GetSize(); 1722 1723 if( nPoints == 4 || (nPoints == 5 && pPoints[0] == pPoints[4]) ) 1724 { 1725 long nX1 = pPoints[0].X(), nX2 = pPoints[2].X(), nY1 = pPoints[0].Y(), nY2 = pPoints[2].Y(); 1726 1727 if( ( (pPoints[1].X() == nX1 && pPoints[3].X() == nX2) && (pPoints[1].Y() == nY2 && pPoints[3].Y() == nY1) ) 1728 || ( (pPoints[1].X() == nX2 && pPoints[3].X() == nX1) && (pPoints[1].Y() == nY1 && pPoints[3].Y() == nY2) ) ) 1729 { 1730 bIsRect = true; 1731 1732 if( pRectOut ) 1733 { 1734 long nSwap; 1735 1736 if( nX2 < nX1 ) 1737 { 1738 nSwap = nX2; 1739 nX2 = nX1; 1740 nX1 = nSwap; 1741 } 1742 1743 if( nY2 < nY1 ) 1744 { 1745 nSwap = nY2; 1746 nY2 = nY1; 1747 nY1 = nSwap; 1748 } 1749 1750 if( nX2 != nX1 ) 1751 { 1752 nX2--; 1753 } 1754 1755 if( nY2 != nY1 ) 1756 { 1757 nY2--; 1758 } 1759 1760 pRectOut->Left() = nX1; 1761 pRectOut->Right() = nX2; 1762 pRectOut->Top() = nY1; 1763 pRectOut->Bottom() = nY2; 1764 } 1765 } 1766 } 1767 1768 return bIsRect; 1769 } 1770 1771 Region Region::GetRegionFromPolyPolygon( const PolyPolygon& rPolyPoly ) 1772 { 1773 //return Region( rPolyPoly ); 1774 1775 // check if it's worth extracting the XOr'ing the Rectangles 1776 // empiricism shows that break even between XOr'ing rectangles separately 1777 // and ImplCreateRegionBandFromPolyPolygon is at half rectangles/half polygons 1778 int nPolygonRects = 0, nPolygonPolygons = 0; 1779 int nPolygons = rPolyPoly.Count(); 1780 1781 for( sal_uInt16 i = 0; i < nPolygons; i++ ) 1782 { 1783 const Polygon& rPoly = rPolyPoly[i]; 1784 1785 if( ImplPolygonRectTest( rPoly ) ) 1786 { 1787 nPolygonRects++; 1788 } 1789 else 1790 { 1791 nPolygonPolygons++; 1792 } 1793 } 1794 1795 if( nPolygonPolygons > nPolygonRects ) 1796 { 1797 return Region( rPolyPoly ); 1798 } 1799 1800 Region aResult; 1801 Rectangle aRect; 1802 1803 for( sal_uInt16 i = 0; i < nPolygons; i++ ) 1804 { 1805 const Polygon& rPoly = rPolyPoly[i]; 1806 1807 if( ImplPolygonRectTest( rPoly, &aRect ) ) 1808 { 1809 aResult.XOr( aRect ); 1810 } 1811 else 1812 { 1813 aResult.XOr( Region(rPoly) ); 1814 } 1815 } 1816 1817 return aResult; 1818 } 1819 1820 ////////////////////////////////////////////////////////////////////////////// 1821 // eof 1822