update emoji autocorrect entries from po-files
[LibreOffice.git] / vcl / source / gdi / region.cxx
blob086739a62fffe75bd434d73efab9298acaf7d83d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <limits.h>
21 #include <tools/vcompat.hxx>
22 #include <tools/stream.hxx>
23 #include <tools/helpers.hxx>
24 #include <osl/diagnose.h>
25 #include <vcl/region.hxx>
26 #include <regionband.hxx>
28 #include <basegfx/matrix/b2dhommatrix.hxx>
29 #include <basegfx/polygon/b2dpolypolygontools.hxx>
30 #include <basegfx/polygon/b2dpolygontools.hxx>
31 #include <basegfx/polygon/b2dpolygonclipper.hxx>
32 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
33 #include <basegfx/range/b2drange.hxx>
34 #include <basegfx/matrix/b2dhommatrixtools.hxx>
36 namespace
38 /** Return <TRUE/> when the given polygon is rectiliner and oriented so that
39 all sides are either horizontal or vertical.
41 bool ImplIsPolygonRectilinear (const tools::PolyPolygon& rPolyPoly)
43 // Iterate over all polygons.
44 const sal_uInt16 nPolyCount = rPolyPoly.Count();
45 for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly)
47 const Polygon& aPoly = rPolyPoly.GetObject(nPoly);
49 // Iterate over all edges of the current polygon.
50 const sal_uInt16 nSize = aPoly.GetSize();
52 if (nSize < 2)
53 continue;
54 Point aPoint (aPoly.GetPoint(0));
55 const Point aLastPoint (aPoint);
56 for (sal_uInt16 nPoint = 1; nPoint < nSize; ++nPoint)
58 const Point aNextPoint (aPoly.GetPoint(nPoint));
59 // When there is at least one edge that is neither vertical nor
60 // horizontal then the entire polygon is not rectilinear (and
61 // oriented along primary axes.)
62 if (aPoint.X() != aNextPoint.X() && aPoint.Y() != aNextPoint.Y())
63 return false;
65 aPoint = aNextPoint;
67 // Compare closing edge.
68 if (aLastPoint.X() != aPoint.X() && aLastPoint.Y() != aPoint.Y())
69 return false;
71 return true;
74 /** Convert a rectilinear polygon (that is oriented along the primary axes)
75 to a list of bands. For this special form of polygon we can use an
76 optimization that prevents the creation of one band per y value.
77 However, it still is possible that some temporary bands are created that
78 later can be optimized away.
79 @param rPolyPolygon
80 A set of zero, one, or more polygons, nested or not, that are
81 converted into a list of bands.
82 @return
83 A new RegionBand object is returned that contains the bands that
84 represent the given poly-polygon.
86 RegionBand* ImplRectilinearPolygonToBands(const tools::PolyPolygon& rPolyPoly)
88 OSL_ASSERT(ImplIsPolygonRectilinear (rPolyPoly));
90 // Create a new RegionBand object as container of the bands.
91 RegionBand* pRegionBand = new RegionBand();
92 long nLineId = 0L;
94 // Iterate over all polygons.
95 const sal_uInt16 nPolyCount = rPolyPoly.Count();
96 for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly)
98 const Polygon& aPoly = rPolyPoly.GetObject(nPoly);
100 // Iterate over all edges of the current polygon.
101 const sal_uInt16 nSize = aPoly.GetSize();
102 if (nSize < 2)
103 continue;
104 // Avoid fetching every point twice (each point is the start point
105 // of one and the end point of another edge.)
106 Point aStart (aPoly.GetPoint(0));
107 Point aEnd;
108 for (sal_uInt16 nPoint = 1; nPoint <= nSize; ++nPoint, aStart=aEnd)
110 // We take the implicit closing edge into account by mapping
111 // index nSize to 0.
112 aEnd = aPoly.GetPoint(nPoint%nSize);
113 if (aStart.Y() == aEnd.Y())
115 // Horizontal lines are ignored.
116 continue;
119 // At this point the line has to be vertical.
120 OSL_ASSERT(aStart.X() == aEnd.X());
122 // Sort y-coordinates to simplify the algorithm and store the
123 // direction separately. The direction is calculated as it is
124 // in other places (but seems to be the wrong way.)
125 const long nTop (::std::min(aStart.Y(), aEnd.Y()));
126 const long nBottom (::std::max(aStart.Y(), aEnd.Y()));
127 const LineType eLineType (aStart.Y() > aEnd.Y() ? LINE_DESCENDING : LINE_ASCENDING);
129 // Make sure that the current line is covered by bands.
130 pRegionBand->ImplAddMissingBands(nTop,nBottom);
132 // Find top-most band that may contain nTop.
133 ImplRegionBand* pBand = pRegionBand->ImplGetFirstRegionBand();
134 while (pBand!=NULL && pBand->mnYBottom < nTop)
135 pBand = pBand->mpNextBand;
136 ImplRegionBand* pTopBand = pBand;
137 // If necessary split the band at nTop so that nTop is contained
138 // in the lower band.
139 if (pBand!=NULL
140 // Prevent the current band from becoming 0 pixel high
141 && pBand->mnYTop<nTop
142 // this allows the lowest pixel of the band to be split off
143 && pBand->mnYBottom>=nTop
144 // do not split a band that is just one pixel high
145 && pBand->mnYTop<pBand->mnYBottom)
147 // Split the top band.
148 pTopBand = pBand->SplitBand(nTop);
151 // Advance to band that may contain nBottom.
152 while (pBand!=NULL && pBand->mnYBottom < nBottom)
153 pBand = pBand->mpNextBand;
154 // The lowest band may have to be split at nBottom so that
155 // nBottom itself remains in the upper band.
156 if (pBand!=NULL
157 // allow the current band becoming 1 pixel high
158 && pBand->mnYTop<=nBottom
159 // prevent splitting off a band that is 0 pixel high
160 && pBand->mnYBottom>nBottom
161 // do not split a band that is just one pixel high
162 && pBand->mnYTop<pBand->mnYBottom)
164 // Split the bottom band.
165 pBand->SplitBand(nBottom+1);
168 // Note that we remember the top band (in pTopBand) but not the
169 // bottom band. The later can be determined by comparing y
170 // coordinates.
172 // Add the x-value as point to all bands in the nTop->nBottom range.
173 for (pBand=pTopBand; pBand!=NULL&&pBand->mnYTop<=nBottom; pBand=pBand->mpNextBand)
174 pBand->InsertPoint(aStart.X(), nLineId++, true, eLineType);
178 return pRegionBand;
181 /** Convert a general polygon (one for which ImplIsPolygonRectilinear()
182 returns <FALSE/>) to bands.
184 RegionBand* ImplGeneralPolygonToBands(const tools::PolyPolygon& rPolyPoly, const Rectangle& rPolygonBoundingBox)
186 long nLineID = 0L;
188 // initialisation and creation of Bands
189 RegionBand* pRegionBand = new RegionBand();
190 pRegionBand->CreateBandRange(rPolygonBoundingBox.Top(), rPolygonBoundingBox.Bottom());
192 // insert polygons
193 const sal_uInt16 nPolyCount = rPolyPoly.Count();
195 for ( sal_uInt16 nPoly = 0; nPoly < nPolyCount; nPoly++ )
197 // get reference to current polygon
198 const Polygon& aPoly = rPolyPoly.GetObject( nPoly );
199 const sal_uInt16 nSize = aPoly.GetSize();
201 // not enough points ( <= 2 )? -> nothing to do!
202 if ( nSize <= 2 )
203 continue;
205 // band the polygon
206 for ( sal_uInt16 nPoint = 1; nPoint < nSize; nPoint++ )
208 pRegionBand->InsertLine( aPoly.GetPoint(nPoint-1), aPoly.GetPoint(nPoint), nLineID++ );
211 // close polygon with line from first point to last point, if necessary
212 const Point rLastPoint = aPoly.GetPoint(nSize-1);
213 const Point rFirstPoint = aPoly.GetPoint(0);
215 if ( rLastPoint != rFirstPoint )
217 pRegionBand->InsertLine( rLastPoint, rFirstPoint, nLineID++ );
221 return pRegionBand;
223 } // end of anonymous namespace
225 namespace vcl {
227 bool vcl::Region::IsEmpty() const
229 return !mbIsNull && !mpB2DPolyPolygon.get() && !mpPolyPolygon.get() && !mpRegionBand.get();
233 RegionBand* ImplCreateRegionBandFromPolyPolygon(const tools::PolyPolygon& rPolyPolygon)
235 RegionBand* pRetval = 0;
237 if(rPolyPolygon.Count())
239 // ensure to subdivide when bezier segemnts are used, it's going to
240 // be expanded to rectangles
241 tools::PolyPolygon aPolyPolygon;
243 rPolyPolygon.AdaptiveSubdivide(aPolyPolygon);
245 if(aPolyPolygon.Count())
247 const Rectangle aRect(aPolyPolygon.GetBoundRect());
249 if(!aRect.IsEmpty())
251 if(ImplIsPolygonRectilinear(aPolyPolygon))
253 // For rectilinear polygons there is an optimized band conversion.
254 pRetval = ImplRectilinearPolygonToBands(aPolyPolygon);
256 else
258 pRetval = ImplGeneralPolygonToBands(aPolyPolygon, aRect);
261 // Convert points into seps.
262 if(pRetval)
264 pRetval->processPoints();
266 // Optimize list of bands. Adjacent bands with identical lists
267 // of seps are joined.
268 if(!pRetval->OptimizeBandList())
270 delete pRetval;
271 pRetval = 0;
278 return pRetval;
281 tools::PolyPolygon vcl::Region::ImplCreatePolyPolygonFromRegionBand() const
283 tools::PolyPolygon aRetval;
285 if(getRegionBand())
287 RectangleVector aRectangles;
288 GetRegionRectangles(aRectangles);
290 for(RectangleVector::const_iterator aRectIter(aRectangles.begin()); aRectIter != aRectangles.end(); ++aRectIter)
292 aRetval.Insert(Polygon(*aRectIter));
295 else
297 OSL_ENSURE(false, "Called with no local RegionBand (!)");
300 return aRetval;
303 basegfx::B2DPolyPolygon vcl::Region::ImplCreateB2DPolyPolygonFromRegionBand() const
305 tools::PolyPolygon aPoly(ImplCreatePolyPolygonFromRegionBand());
307 return aPoly.getB2DPolyPolygon();
310 Region::Region(bool bIsNull)
311 : mpB2DPolyPolygon(),
312 mpPolyPolygon(),
313 mpRegionBand(),
314 mbIsNull(bIsNull)
318 Region::Region(const Rectangle& rRect)
319 : mpB2DPolyPolygon(),
320 mpPolyPolygon(),
321 mpRegionBand(),
322 mbIsNull(false)
324 mpRegionBand.reset(rRect.IsEmpty() ? 0 : new RegionBand(rRect));
327 Region::Region(const Polygon& rPolygon)
328 : mpB2DPolyPolygon(),
329 mpPolyPolygon(),
330 mpRegionBand(),
331 mbIsNull(false)
334 if(rPolygon.GetSize())
336 ImplCreatePolyPolyRegion(rPolygon);
340 Region::Region(const tools::PolyPolygon& rPolyPoly)
341 : mpB2DPolyPolygon(),
342 mpPolyPolygon(),
343 mpRegionBand(),
344 mbIsNull(false)
347 if(rPolyPoly.Count())
349 ImplCreatePolyPolyRegion(rPolyPoly);
353 Region::Region(const basegfx::B2DPolyPolygon& rPolyPoly)
354 : mpB2DPolyPolygon(),
355 mpPolyPolygon(),
356 mpRegionBand(),
357 mbIsNull(false)
360 if(rPolyPoly.count())
362 ImplCreatePolyPolyRegion(rPolyPoly);
366 Region::Region(const vcl::Region& rRegion)
367 : mpB2DPolyPolygon(rRegion.mpB2DPolyPolygon),
368 mpPolyPolygon(rRegion.mpPolyPolygon),
369 mpRegionBand(rRegion.mpRegionBand),
370 mbIsNull(rRegion.mbIsNull)
374 Region::~Region()
378 void vcl::Region::ImplCreatePolyPolyRegion( const tools::PolyPolygon& rPolyPoly )
380 const sal_uInt16 nPolyCount = rPolyPoly.Count();
382 if(nPolyCount)
384 // polypolygon empty? -> empty region
385 const Rectangle aRect(rPolyPoly.GetBoundRect());
387 if(!aRect.IsEmpty())
389 // width OR height == 1 ? => Rectangular region
390 if((1 == aRect.GetWidth()) || (1 == aRect.GetHeight()) || rPolyPoly.IsRect())
392 mpRegionBand.reset(new RegionBand(aRect));
394 else
396 mpPolyPolygon.reset(new tools::PolyPolygon(rPolyPoly));
399 mbIsNull = false;
404 void vcl::Region::ImplCreatePolyPolyRegion( const basegfx::B2DPolyPolygon& rPolyPoly )
406 if(rPolyPoly.count() && !rPolyPoly.getB2DRange().isEmpty())
408 mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(rPolyPoly));
409 mbIsNull = false;
413 void vcl::Region::Move( long nHorzMove, long nVertMove )
415 if(IsNull() || IsEmpty())
417 // empty or null need no move
418 return;
421 if(!nHorzMove && !nVertMove)
423 // no move defined
424 return;
427 if(getB2DPolyPolygon())
429 basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon());
431 aPoly.transform(basegfx::tools::createTranslateB2DHomMatrix(nHorzMove, nVertMove));
432 mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : 0);
433 mpPolyPolygon.reset();
434 mpRegionBand.reset();
436 else if(getPolyPolygon())
438 tools::PolyPolygon aPoly(*getPolyPolygon());
440 aPoly.Move(nHorzMove, nVertMove);
441 mpB2DPolyPolygon.reset();
442 mpPolyPolygon.reset(aPoly.Count() ? new tools::PolyPolygon(aPoly) : 0);
443 mpRegionBand.reset();
445 else if(getRegionBand())
447 RegionBand* pNew = new RegionBand(*getRegionBand());
449 pNew->Move(nHorzMove, nVertMove);
450 mpB2DPolyPolygon.reset();
451 mpPolyPolygon.reset();
452 mpRegionBand.reset(pNew);
454 else
456 OSL_ENSURE(false, "Region::Move error: impossible combination (!)");
460 void vcl::Region::Scale( double fScaleX, double fScaleY )
462 if(IsNull() || IsEmpty())
464 // empty or null need no scale
465 return;
468 if(basegfx::fTools::equalZero(fScaleX) && basegfx::fTools::equalZero(fScaleY))
470 // no scale defined
471 return;
474 if(getB2DPolyPolygon())
476 basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon());
478 aPoly.transform(basegfx::tools::createScaleB2DHomMatrix(fScaleX, fScaleY));
479 mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : 0);
480 mpPolyPolygon.reset();
481 mpRegionBand.reset();
483 else if(getPolyPolygon())
485 tools::PolyPolygon aPoly(*getPolyPolygon());
487 aPoly.Scale(fScaleX, fScaleY);
488 mpB2DPolyPolygon.reset();
489 mpPolyPolygon.reset(aPoly.Count() ? new tools::PolyPolygon(aPoly) : 0);
490 mpRegionBand.reset();
492 else if(getRegionBand())
494 RegionBand* pNew = new RegionBand(*getRegionBand());
496 pNew->Scale(fScaleX, fScaleY);
497 mpB2DPolyPolygon.reset();
498 mpPolyPolygon.reset();
499 mpRegionBand.reset(pNew);
501 else
503 OSL_ENSURE(false, "Region::Scale error: impossible combination (!)");
507 bool vcl::Region::Union( const Rectangle& rRect )
509 if(rRect.IsEmpty())
511 // empty rectangle will not expand the existing union, nothing to do
512 return true;
515 if(IsEmpty())
517 // no local data, the union will be equal to source. Create using rectangle
518 *this = rRect;
519 return true;
522 if(HasPolyPolygonOrB2DPolyPolygon())
524 // get this B2DPolyPolygon, solve on polygon base
525 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
527 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation(aThisPolyPoly);
529 if(!aThisPolyPoly.count())
531 // no local polygon, use the rectangle as new region
532 *this = rRect;
534 else
536 // get the other B2DPolyPolygon and use logical Or-Operation
537 const basegfx::B2DPolygon aRectPoly(
538 basegfx::tools::createPolygonFromRect(
539 basegfx::B2DRectangle(
540 rRect.Left(),
541 rRect.Top(),
542 rRect.Right(),
543 rRect.Bottom())));
544 const basegfx::B2DPolyPolygon aClip(
545 basegfx::tools::solvePolygonOperationOr(
546 aThisPolyPoly,
547 basegfx::B2DPolyPolygon(aRectPoly)));
548 *this = vcl::Region(aClip);
551 return true;
554 // only region band mode possibility left here or null/empty
555 const RegionBand* pCurrent = getRegionBand();
557 if(!pCurrent)
559 // no region band, create using the rectangle
560 *this = rRect;
561 return true;
564 RegionBand* pNew = new RegionBand(*pCurrent);
566 // get justified rectangle
567 const long nLeft(std::min(rRect.Left(), rRect.Right()));
568 const long nTop(std::min(rRect.Top(), rRect.Bottom()));
569 const long nRight(std::max(rRect.Left(), rRect.Right()));
570 const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
572 // insert bands if the boundaries are not already in the list
573 pNew->InsertBands(nTop, nBottom);
575 // process union
576 pNew->Union(nLeft, nTop, nRight, nBottom);
578 // cleanup
579 if(!pNew->OptimizeBandList())
581 delete pNew;
582 pNew = 0;
585 mpRegionBand.reset(pNew);
586 return true;
589 bool vcl::Region::Intersect( const Rectangle& rRect )
591 if ( rRect.IsEmpty() )
593 // empty rectangle will create empty region
594 SetEmpty();
595 return true;
598 if(IsNull())
600 // null region (everything) intersect with rect will give rect
601 *this = rRect;
602 return true;
605 if(IsEmpty())
607 // no content, cannot get more empty
608 return true;
611 if(HasPolyPolygonOrB2DPolyPolygon())
613 // if polygon data prefer double precision, the other will be lost (if buffered)
614 if(getB2DPolyPolygon())
616 const basegfx::B2DPolyPolygon aPoly(
617 basegfx::tools::clipPolyPolygonOnRange(
618 *getB2DPolyPolygon(),
619 basegfx::B2DRange(
620 rRect.Left(),
621 rRect.Top(),
622 rRect.Right() + 1,
623 rRect.Bottom() + 1),
624 true,
625 false));
627 mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : 0);
628 mpPolyPolygon.reset();
629 mpRegionBand.reset();
631 else // if(getPolyPolygon())
633 tools::PolyPolygon aPoly(*getPolyPolygon());
635 // use the PolyPolygon::Clip method for rectangles, this is
636 // fairly simple (does not even use GPC) and saves us from
637 // unnecessary banding
638 aPoly.Clip(rRect);
640 mpB2DPolyPolygon.reset();
641 mpPolyPolygon.reset(aPoly.Count() ? new tools::PolyPolygon(aPoly) : 0);
642 mpRegionBand.reset();
645 return true;
648 // only region band mode possibility left here or null/empty
649 const RegionBand* pCurrent = getRegionBand();
651 if(!pCurrent)
653 // region is empty -> nothing to do!
654 return true;
657 RegionBand* pNew = new RegionBand(*pCurrent);
659 // get justified rectangle
660 const long nLeft(std::min(rRect.Left(), rRect.Right()));
661 const long nTop(std::min(rRect.Top(), rRect.Bottom()));
662 const long nRight(std::max(rRect.Left(), rRect.Right()));
663 const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
665 // insert bands if the boundaries are not already in the list
666 pNew->InsertBands(nTop, nBottom);
668 // process intersect
669 pNew->Intersect(nLeft, nTop, nRight, nBottom);
671 // cleanup
672 if(!pNew->OptimizeBandList())
674 delete pNew;
675 pNew = 0;
678 mpRegionBand.reset(pNew);
679 return true;
682 bool vcl::Region::Exclude( const Rectangle& rRect )
684 if ( rRect.IsEmpty() )
686 // excluding nothing will do no change
687 return true;
690 if(IsEmpty())
692 // cannot exclude from empty, done
693 return true;
696 if(IsNull())
698 // error; cannot exclude from null region since this is not representable
699 // in the data
700 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
701 return true;
704 if( HasPolyPolygonOrB2DPolyPolygon() )
706 // get this B2DPolyPolygon
707 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
709 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation(aThisPolyPoly);
711 if(!aThisPolyPoly.count())
713 // when local polygon is empty, nothing can be excluded
714 return true;
717 // get the other B2DPolyPolygon
718 const basegfx::B2DPolygon aRectPoly(
719 basegfx::tools::createPolygonFromRect(
720 basegfx::B2DRectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom())));
721 const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly);
722 const basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationDiff(aThisPolyPoly, aOtherPolyPoly);
724 *this = vcl::Region(aClip);
726 return true;
729 // only region band mode possibility left here or null/empty
730 const RegionBand* pCurrent = getRegionBand();
732 if(!pCurrent)
734 // empty? -> done!
735 return true;
738 RegionBand* pNew = new RegionBand(*pCurrent);
740 // get justified rectangle
741 const long nLeft(std::min(rRect.Left(), rRect.Right()));
742 const long nTop(std::min(rRect.Top(), rRect.Bottom()));
743 const long nRight(std::max(rRect.Left(), rRect.Right()));
744 const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
746 // insert bands if the boundaries are not already in the list
747 pNew->InsertBands(nTop, nBottom);
749 // process exclude
750 pNew->Exclude(nLeft, nTop, nRight, nBottom);
752 // cleanup
753 if(!pNew->OptimizeBandList())
755 delete pNew;
756 pNew = 0;
759 mpRegionBand.reset(pNew);
760 return true;
763 bool vcl::Region::XOr( const Rectangle& rRect )
765 if ( rRect.IsEmpty() )
767 // empty rectangle will not change local content
768 return true;
771 if(IsEmpty())
773 // rRect will be the xored-form (local off, rect on)
774 *this = rRect;
775 return true;
778 if(IsNull())
780 // error; cannot exclude from null region since this is not representable
781 // in the data
782 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
783 return true;
786 if( HasPolyPolygonOrB2DPolyPolygon() )
788 // get this B2DPolyPolygon
789 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
791 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly );
793 if(!aThisPolyPoly.count())
795 // no local content, XOr will be equal to rectangle
796 *this = rRect;
797 return true;
800 // get the other B2DPolyPolygon
801 const basegfx::B2DPolygon aRectPoly(
802 basegfx::tools::createPolygonFromRect(
803 basegfx::B2DRectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom())));
804 const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly);
805 const basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationXor(aThisPolyPoly, aOtherPolyPoly);
807 *this = vcl::Region(aClip);
809 return true;
812 // only region band mode possibility left here or null/empty
813 const RegionBand* pCurrent = getRegionBand();
815 if(!pCurrent)
817 // rRect will be the xored-form (local off, rect on)
818 *this = rRect;
819 return true;
822 // only region band mode possibility left here or null/empty
823 RegionBand* pNew = new RegionBand(*getRegionBand());
825 // get justified rectangle
826 const long nLeft(std::min(rRect.Left(), rRect.Right()));
827 const long nTop(std::min(rRect.Top(), rRect.Bottom()));
828 const long nRight(std::max(rRect.Left(), rRect.Right()));
829 const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
831 // insert bands if the boundaries are not already in the list
832 pNew->InsertBands(nTop, nBottom);
834 // process xor
835 pNew->XOr(nLeft, nTop, nRight, nBottom);
837 // cleanup
838 if(!pNew->OptimizeBandList())
840 delete pNew;
841 pNew = 0;
844 mpRegionBand.reset(pNew);
845 return true;
848 bool vcl::Region::Union( const vcl::Region& rRegion )
850 if(rRegion.IsEmpty())
852 // no extension at all
853 return true;
856 if(rRegion.IsNull())
858 // extending with null region -> null region
859 *this = vcl::Region(true);
860 return true;
863 if(IsEmpty())
865 // local is empty, union will give source region
866 *this = rRegion;
867 return true;
870 if(IsNull())
872 // already fully expanded (is null region), cannot be extended
873 return true;
876 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
878 // get this B2DPolyPolygon
879 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
881 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation(aThisPolyPoly);
883 if(!aThisPolyPoly.count())
885 // when no local content, union will be equal to rRegion
886 *this = rRegion;
887 return true;
890 // get the other B2DPolyPolygon
891 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
892 aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation(aOtherPolyPoly);
894 // use logical OR operation
895 basegfx::B2DPolyPolygon aClip(basegfx::tools::solvePolygonOperationOr(aThisPolyPoly, aOtherPolyPoly));
897 *this = vcl::Region( aClip );
898 return true;
901 // only region band mode possibility left here or null/empty
902 const RegionBand* pCurrent = getRegionBand();
904 if(!pCurrent)
906 // local is empty, union will give source region
907 *this = rRegion;
908 return true;
911 const RegionBand* pSource = rRegion.getRegionBand();
913 if(!pSource)
915 // no extension at all
916 return true;
919 // prepare source and target
920 RegionBand* pNew = new RegionBand(*pCurrent);
922 // union with source
923 pNew->Union(*pSource);
925 // cleanup
926 if(!pNew->OptimizeBandList())
928 delete pNew;
929 pNew = 0;
932 mpRegionBand.reset(pNew);
933 return true;
936 bool vcl::Region::Intersect( const vcl::Region& rRegion )
938 // same instance data? -> nothing to do!
939 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
941 return true;
944 if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
946 return true;
949 if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
951 return true;
954 if(rRegion.IsNull())
956 // source region is null-region, intersect will not change local region
957 return true;
960 if(IsNull())
962 // when local region is null-region, intersect will be equal to source
963 *this = rRegion;
964 return true;
967 if(rRegion.IsEmpty())
969 // source region is empty, intersection will always be empty
970 SetEmpty();
971 return true;
974 if(IsEmpty())
976 // local region is empty, cannot get more emty than that. Nothing to do
977 return true;
980 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
982 // get this B2DPolyPolygon
983 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
985 if(!aThisPolyPoly.count())
987 // local region is empty, cannot get more emty than that. Nothing to do
988 return true;
991 // get the other B2DPolyPolygon
992 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
994 if(!aOtherPolyPoly.count())
996 // source region is empty, intersection will always be empty
997 SetEmpty();
998 return true;
1001 const basegfx::B2DPolyPolygon aClip(
1002 basegfx::tools::clipPolyPolygonOnPolyPolygon(
1003 aOtherPolyPoly,
1004 aThisPolyPoly,
1005 true,
1006 false));
1007 *this = vcl::Region( aClip );
1008 return true;
1011 // only region band mode possibility left here or null/empty
1012 const RegionBand* pCurrent = getRegionBand();
1014 if(!pCurrent)
1016 // local region is empty, cannot get more emty than that. Nothing to do
1017 return true;
1020 const RegionBand* pSource = rRegion.getRegionBand();
1022 if(!pSource)
1024 // source region is empty, intersection will always be empty
1025 SetEmpty();
1026 return true;
1029 // both RegionBands exist and are not empty
1030 if(pCurrent->getRectangleCount() + 2 < pSource->getRectangleCount())
1032 // when we have less rectangles, turn around the call
1033 vcl::Region aTempRegion = rRegion;
1034 aTempRegion.Intersect( *this );
1035 *this = aTempRegion;
1037 else
1039 // prepare new regionBand
1040 RegionBand* pNew = new RegionBand(*pCurrent);
1042 // intersect with source
1043 pNew->Intersect(*pSource);
1045 // cleanup
1046 if(!pNew->OptimizeBandList())
1048 delete pNew;
1049 pNew = 0;
1052 mpRegionBand.reset(pNew);
1055 return true;
1058 bool vcl::Region::Exclude( const vcl::Region& rRegion )
1060 if ( rRegion.IsEmpty() )
1062 // excluding nothing will do no change
1063 return true;
1066 if ( rRegion.IsNull() )
1068 // excluding everything will create empty region
1069 SetEmpty();
1070 return true;
1073 if(IsEmpty())
1075 // cannot exclude from empty, done
1076 return true;
1079 if(IsNull())
1081 // error; cannot exclude from null region since this is not representable
1082 // in the data
1083 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
1084 return true;
1087 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1089 // get this B2DPolyPolygon
1090 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
1092 if(!aThisPolyPoly.count())
1094 // cannot exclude from empty, done
1095 return true;
1098 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly );
1100 // get the other B2DPolyPolygon
1101 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
1102 aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly );
1104 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationDiff( aThisPolyPoly, aOtherPolyPoly );
1105 *this = vcl::Region( aClip );
1106 return true;
1109 // only region band mode possibility left here or null/empty
1110 const RegionBand* pCurrent = getRegionBand();
1112 if(!pCurrent)
1114 // cannot exclude from empty, done
1115 return true;
1118 const RegionBand* pSource = rRegion.getRegionBand();
1120 if(!pSource)
1122 // excluding nothing will do no change
1123 return true;
1126 // prepare source and target
1127 RegionBand* pNew = new RegionBand(*pCurrent);
1129 // union with source
1130 const bool bSuccess(pNew->Exclude(*pSource));
1132 // cleanup
1133 if(!bSuccess)
1135 delete pNew;
1136 pNew = 0;
1139 mpRegionBand.reset(pNew);
1140 return true;
1143 bool vcl::Region::XOr( const vcl::Region& rRegion )
1145 if ( rRegion.IsEmpty() )
1147 // empty region will not change local content
1148 return true;
1151 if ( rRegion.IsNull() )
1153 // error; cannot exclude null region from local since this is not representable
1154 // in the data
1155 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1156 return true;
1159 if(IsEmpty())
1161 // rRect will be the xored-form (local off, rect on)
1162 *this = rRegion;
1163 return true;
1166 if(IsNull())
1168 // error: cannot exclude from null region since this is not representable
1169 // in the data
1170 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1171 return false;
1174 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1176 // get this B2DPolyPolygon
1177 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
1179 if(!aThisPolyPoly.count())
1181 // rRect will be the xored-form (local off, rect on)
1182 *this = rRegion;
1183 return true;
1186 aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly );
1188 // get the other B2DPolyPolygon
1189 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
1190 aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly );
1192 basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationXor( aThisPolyPoly, aOtherPolyPoly );
1193 *this = vcl::Region( aClip );
1194 return true;
1197 // only region band mode possibility left here or null/empty
1198 const RegionBand* pCurrent = getRegionBand();
1200 if(!pCurrent)
1202 // rRect will be the xored-form (local off, rect on)
1203 *this = rRegion;
1204 return true;
1207 const RegionBand* pSource = rRegion.getRegionBand();
1209 if(!pSource)
1211 // empty region will not change local content
1212 return true;
1215 // prepare source and target
1216 RegionBand* pNew = new RegionBand(*pCurrent);
1218 // union with source
1219 pNew->XOr(*pSource);
1221 // cleanup
1222 if(!pNew->OptimizeBandList())
1224 delete pNew;
1225 pNew = 0;
1228 mpRegionBand.reset(pNew);
1230 return true;
1233 Rectangle vcl::Region::GetBoundRect() const
1235 if(IsEmpty())
1237 // no internal data? -> region is empty!
1238 return Rectangle();
1241 if(IsNull())
1243 // error; null region has no BoundRect
1244 // OSL_ENSURE(false, "Region::GetBoundRect error: null region has unlimitied bound rect, not representable (!)");
1245 return Rectangle();
1248 // prefer double precision source
1249 if(getB2DPolyPolygon())
1251 const basegfx::B2DRange aRange(basegfx::tools::getRange(*getB2DPolyPolygon()));
1253 if(aRange.isEmpty())
1255 // emulate PolyPolygon::GetBoundRect() when empty polygon
1256 return Rectangle();
1258 else
1260 // #i122149# corrected rounding, no need for ceil() and floor() here
1261 return Rectangle(
1262 basegfx::fround(aRange.getMinX()), basegfx::fround(aRange.getMinY()),
1263 basegfx::fround(aRange.getMaxX()), basegfx::fround(aRange.getMaxY()));
1267 if(getPolyPolygon())
1269 return getPolyPolygon()->GetBoundRect();
1272 if(getRegionBand())
1274 return getRegionBand()->GetBoundRect();
1277 return Rectangle();
1280 const tools::PolyPolygon vcl::Region::GetAsPolyPolygon() const
1282 if(getPolyPolygon())
1284 return *getPolyPolygon();
1287 if(getB2DPolyPolygon())
1289 // the polygon needs to be converted, buffer the down converion
1290 const tools::PolyPolygon aPolyPolgon(*getB2DPolyPolygon());
1291 const_cast< vcl::Region* >(this)->mpPolyPolygon.reset(new tools::PolyPolygon(aPolyPolgon));
1293 return *getPolyPolygon();
1296 if(getRegionBand())
1298 // the BandRegion needs to be converted, buffer the converion
1299 const tools::PolyPolygon aPolyPolgon(ImplCreatePolyPolygonFromRegionBand());
1300 const_cast< vcl::Region* >(this)->mpPolyPolygon.reset(new tools::PolyPolygon(aPolyPolgon));
1302 return *getPolyPolygon();
1305 return tools::PolyPolygon();
1308 const basegfx::B2DPolyPolygon vcl::Region::GetAsB2DPolyPolygon() const
1310 if(getB2DPolyPolygon())
1312 return *getB2DPolyPolygon();
1315 if(getPolyPolygon())
1317 // the polygon needs to be converted, buffer the up conversion. This will be preferred from now.
1318 const basegfx::B2DPolyPolygon aB2DPolyPolygon(getPolyPolygon()->getB2DPolyPolygon());
1319 const_cast< vcl::Region* >(this)->mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(aB2DPolyPolygon));
1321 return *getB2DPolyPolygon();
1324 if(getRegionBand())
1326 // the BandRegion needs to be converted, buffer the converion
1327 const basegfx::B2DPolyPolygon aB2DPolyPolygon(ImplCreateB2DPolyPolygonFromRegionBand());
1328 const_cast< vcl::Region* >(this)->mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(aB2DPolyPolygon));
1330 return *getB2DPolyPolygon();
1333 return basegfx::B2DPolyPolygon();
1336 const RegionBand* vcl::Region::GetAsRegionBand() const
1338 if(!getRegionBand())
1340 if(getB2DPolyPolygon())
1342 // convert B2DPolyPolygon to RegionBand, buffer it and return it
1343 const_cast< vcl::Region* >(this)->mpRegionBand.reset(ImplCreateRegionBandFromPolyPolygon(tools::PolyPolygon(*getB2DPolyPolygon())));
1345 else if(getPolyPolygon())
1347 // convert B2DPolyPolygon to RegionBand, buffer it and return it
1348 const_cast< vcl::Region* >(this)->mpRegionBand.reset(ImplCreateRegionBandFromPolyPolygon(*getPolyPolygon()));
1352 return getRegionBand();
1355 bool vcl::Region::IsInside( const Point& rPoint ) const
1357 if(IsEmpty())
1359 // no point can be in empty region
1360 return false;
1363 if(IsNull())
1365 // all points are inside null-region
1366 return true;
1369 // Too expensive (?)
1370 //if(mpImplRegion->getRegionPolyPoly())
1372 // return mpImplRegion->getRegionPolyPoly()->IsInside( rPoint );
1375 // ensure RegionBand existence
1376 const RegionBand* pRegionBand = GetAsRegionBand();
1378 if(pRegionBand)
1380 return pRegionBand->IsInside(rPoint);
1383 return false;
1386 bool vcl::Region::IsInside( const Rectangle& rRect ) const
1388 if(IsEmpty())
1390 // no rectangle can be in empty region
1391 return false;
1394 if(IsNull())
1396 // rectangle always inside null-region
1397 return true;
1400 if ( rRect.IsEmpty() )
1402 // is rectangle empty? -> not inside
1403 return false;
1406 // create region from rectangle and intersect own region
1407 vcl::Region aRegion(rRect);
1408 aRegion.Exclude(*this);
1410 // rectangle is inside if exclusion is empty
1411 return aRegion.IsEmpty();
1414 bool vcl::Region::IsOver( const Rectangle& rRect ) const
1416 if(IsEmpty())
1418 // nothing can be over something empty
1419 return false;
1422 if(IsNull())
1424 // everything is over null region
1425 return true;
1428 // Can we optimize this ??? - is used in StarDraw for brushes pointers
1429 // Why we have no IsOver for Regions ???
1430 // create region from rectangle and intersect own region
1431 vcl::Region aRegion(rRect);
1432 aRegion.Intersect( *this );
1434 // rectangle is over if include is not empty
1435 return !aRegion.IsEmpty();
1438 bool vcl::Region::IsRectangle() const
1440 if( IsEmpty() || IsNull() )
1441 return false;
1443 if( getB2DPolyPolygon() )
1444 return basegfx::tools::isRectangle( *getB2DPolyPolygon() );
1446 if( getPolyPolygon() )
1447 return getPolyPolygon()->IsRect();
1449 if( getRegionBand() )
1450 return (getRegionBand()->getRectangleCount() == 1);
1452 return false;
1455 void vcl::Region::SetNull()
1457 // reset all content
1458 mpB2DPolyPolygon.reset();
1459 mpPolyPolygon.reset();
1460 mpRegionBand.reset();
1461 mbIsNull = true;
1464 void vcl::Region::SetEmpty()
1466 // reset all content
1467 mpB2DPolyPolygon.reset();
1468 mpPolyPolygon.reset();
1469 mpRegionBand.reset();
1470 mbIsNull = false;
1473 Region& vcl::Region::operator=( const vcl::Region& rRegion )
1475 // reset all content
1476 mpB2DPolyPolygon = rRegion.mpB2DPolyPolygon;
1477 mpPolyPolygon = rRegion.mpPolyPolygon;
1478 mpRegionBand = rRegion.mpRegionBand;
1479 mbIsNull = rRegion.mbIsNull;
1481 return *this;
1484 Region& vcl::Region::operator=( const Rectangle& rRect )
1486 mpB2DPolyPolygon.reset();
1487 mpPolyPolygon.reset();
1488 mpRegionBand.reset(rRect.IsEmpty() ? 0 : new RegionBand(rRect));
1489 mbIsNull = false;
1491 return *this;
1494 bool vcl::Region::operator==( const vcl::Region& rRegion ) const
1496 if(IsNull() && rRegion.IsNull())
1498 // both are null region
1499 return true;
1502 if(IsEmpty() && rRegion.IsEmpty())
1504 // both are empty
1505 return true;
1508 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
1510 // same instance data? -> equal
1511 return true;
1514 if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
1516 // same instance data? -> equal
1517 return true;
1520 if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
1522 // same instance data? -> equal
1523 return true;
1526 if(IsNull() || IsEmpty())
1528 return false;
1531 if(rRegion.IsNull() || rRegion.IsEmpty())
1533 return false;
1536 if(rRegion.getB2DPolyPolygon() || getB2DPolyPolygon())
1538 // one of both has a B2DPolyPolygon based region, ensure both have it
1539 // by evtl. conversion
1540 GetAsB2DPolyPolygon();
1541 rRegion.GetAsB2DPolyPolygon();
1543 return *rRegion.getB2DPolyPolygon() == *getB2DPolyPolygon();
1546 if(rRegion.getPolyPolygon() || getPolyPolygon())
1548 // one of both has a B2DPolyPolygon based region, ensure both have it
1549 // by evtl. conversion
1550 GetAsPolyPolygon();
1551 rRegion.GetAsPolyPolygon();
1553 return *rRegion.getPolyPolygon() == *getPolyPolygon();
1556 // both are not empty or null (see above) and if content supported polygon
1557 // data the comparison is already done. Only both on RegionBand base can be left,
1558 // but better check
1559 if(rRegion.getRegionBand() && getRegionBand())
1561 return *rRegion.getRegionBand() == *getRegionBand();
1564 // should not happen, but better deny equality
1565 return false;
1568 SvStream& ReadRegion(SvStream& rIStrm, vcl::Region& rRegion)
1570 VersionCompat aCompat(rIStrm, StreamMode::READ);
1571 sal_uInt16 nVersion(0);
1572 sal_uInt16 nTmp16(0);
1574 // clear region to be loaded
1575 rRegion.SetEmpty();
1577 // get version of streamed region
1578 rIStrm.ReadUInt16( nVersion );
1580 // get type of region
1581 rIStrm.ReadUInt16( nTmp16 );
1583 enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX };
1584 RegionType meStreamedType = (RegionType)nTmp16;
1586 switch(meStreamedType)
1588 case REGION_NULL:
1590 rRegion.SetNull();
1591 break;
1594 case REGION_EMPTY:
1596 rRegion.SetEmpty();
1597 break;
1600 default:
1602 RegionBand* pNewRegionBand = new RegionBand();
1603 pNewRegionBand->load(rIStrm);
1604 rRegion.mpRegionBand.reset(pNewRegionBand);
1606 if(aCompat.GetVersion() >= 2)
1608 bool bHasPolyPolygon(false);
1610 rIStrm.ReadCharAsBool( bHasPolyPolygon );
1612 if(bHasPolyPolygon)
1614 tools::PolyPolygon* pNewPoly = new tools::PolyPolygon();
1615 ReadPolyPolygon( rIStrm, *pNewPoly );
1616 rRegion.mpPolyPolygon.reset(pNewPoly);
1620 break;
1624 return rIStrm;
1627 SvStream& WriteRegion( SvStream& rOStrm, const vcl::Region& rRegion )
1629 const sal_uInt16 nVersion(2);
1630 VersionCompat aCompat(rOStrm, StreamMode::WRITE, nVersion);
1632 // put version
1633 rOStrm.WriteUInt16( nVersion );
1635 // put type
1636 enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX };
1637 RegionType aRegionType(REGION_COMPLEX);
1638 bool bEmpty(rRegion.IsEmpty());
1640 if(!bEmpty && rRegion.getB2DPolyPolygon() && 0 == rRegion.getB2DPolyPolygon()->count())
1642 OSL_ENSURE(false, "Region with empty B2DPolyPolygon, should not be created (!)");
1643 bEmpty = true;
1646 if(!bEmpty && rRegion.getPolyPolygon() && 0 == rRegion.getPolyPolygon()->Count())
1648 OSL_ENSURE(false, "Region with empty PolyPolygon, should not be created (!)");
1649 bEmpty = true;
1652 if(bEmpty)
1654 aRegionType = REGION_EMPTY;
1656 else if(rRegion.IsNull())
1658 aRegionType = REGION_NULL;
1660 else if(rRegion.getRegionBand() && rRegion.getRegionBand()->isSingleRectangle())
1662 aRegionType = REGION_RECTANGLE;
1665 rOStrm.WriteUInt16( aRegionType );
1667 // get RegionBand
1668 const RegionBand* pRegionBand = rRegion.getRegionBand();
1670 if(pRegionBand)
1672 pRegionBand->save(rOStrm);
1674 else
1676 // for compatibility, write an empty RegionBand (will only write
1677 // the end marker STREAMENTRY_END, but this *is* needed)
1678 const RegionBand aRegionBand;
1680 aRegionBand.save(rOStrm);
1683 // write polypolygon if available
1684 const bool bHasPolyPolygon(rRegion.HasPolyPolygonOrB2DPolyPolygon());
1685 rOStrm.WriteBool( bHasPolyPolygon );
1687 if(bHasPolyPolygon)
1689 // #i105373#
1690 tools::PolyPolygon aNoCurvePolyPolygon;
1691 rRegion.GetAsPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon);
1693 WritePolyPolygon( rOStrm, aNoCurvePolyPolygon );
1696 return rOStrm;
1699 void vcl::Region::GetRegionRectangles(RectangleVector& rTarget) const
1701 // clear returnvalues
1702 rTarget.clear();
1704 // ensure RegionBand existence
1705 const RegionBand* pRegionBand = GetAsRegionBand();
1707 if(pRegionBand)
1709 pRegionBand->GetRegionRectangles(rTarget);
1713 static inline bool ImplPolygonRectTest( const Polygon& rPoly, Rectangle* pRectOut = NULL )
1715 bool bIsRect = false;
1716 const Point* pPoints = rPoly.GetConstPointAry();
1717 sal_uInt16 nPoints = rPoly.GetSize();
1719 if( nPoints == 4 || (nPoints == 5 && pPoints[0] == pPoints[4]) )
1721 long nX1 = pPoints[0].X(), nX2 = pPoints[2].X(), nY1 = pPoints[0].Y(), nY2 = pPoints[2].Y();
1723 if( ( (pPoints[1].X() == nX1 && pPoints[3].X() == nX2) && (pPoints[1].Y() == nY2 && pPoints[3].Y() == nY1) )
1724 || ( (pPoints[1].X() == nX2 && pPoints[3].X() == nX1) && (pPoints[1].Y() == nY1 && pPoints[3].Y() == nY2) ) )
1726 bIsRect = true;
1728 if( pRectOut )
1730 long nSwap;
1732 if( nX2 < nX1 )
1734 nSwap = nX2;
1735 nX2 = nX1;
1736 nX1 = nSwap;
1739 if( nY2 < nY1 )
1741 nSwap = nY2;
1742 nY2 = nY1;
1743 nY1 = nSwap;
1746 if( nX2 != nX1 )
1748 nX2--;
1751 if( nY2 != nY1 )
1753 nY2--;
1756 pRectOut->Left() = nX1;
1757 pRectOut->Right() = nX2;
1758 pRectOut->Top() = nY1;
1759 pRectOut->Bottom() = nY2;
1764 return bIsRect;
1767 vcl::Region vcl::Region::GetRegionFromPolyPolygon( const tools::PolyPolygon& rPolyPoly )
1769 //return vcl::Region( rPolyPoly );
1771 // check if it's worth extracting the XOr'ing the Rectangles
1772 // empiricism shows that break even between XOr'ing rectangles separately
1773 // and ImplCreateRegionBandFromPolyPolygon is at half rectangles/half polygons
1774 int nPolygonRects = 0, nPolygonPolygons = 0;
1775 int nPolygons = rPolyPoly.Count();
1777 for( sal_uInt16 i = 0; i < nPolygons; i++ )
1779 const Polygon& rPoly = rPolyPoly[i];
1781 if( ImplPolygonRectTest( rPoly ) )
1783 nPolygonRects++;
1785 else
1787 nPolygonPolygons++;
1791 if( nPolygonPolygons > nPolygonRects )
1793 return vcl::Region( rPolyPoly );
1796 vcl::Region aResult;
1797 Rectangle aRect;
1799 for( sal_uInt16 i = 0; i < nPolygons; i++ )
1801 const Polygon& rPoly = rPolyPoly[i];
1803 if( ImplPolygonRectTest( rPoly, &aRect ) )
1805 aResult.XOr( aRect );
1807 else
1809 aResult.XOr( vcl::Region(rPoly) );
1813 return aResult;
1816 } /* namespace vcl */
1818 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */