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