1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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>
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();
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())
67 // Compare closing edge.
68 if (aLastPoint
.X() != aPoint
.X() && aLastPoint
.Y() != aPoint
.Y())
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.
80 A set of zero, one, or more polygons, nested or not, that are
81 converted into a list of bands.
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();
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();
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));
108 for (sal_uInt16 nPoint
= 1; nPoint
<= nSize
; ++nPoint
, aStart
=aEnd
)
110 // We take the implicit closing edge into account by mapping
112 aEnd
= aPoly
.GetPoint(nPoint
%nSize
);
113 if (aStart
.Y() == aEnd
.Y())
115 // Horizontal lines are ignored.
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.
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.
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
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
);
181 /** Convert a general polygon (one for which ImplIsPolygonRectilinear()
182 returns <FALSE/>) to bands.
184 RegionBand
* ImplGeneralPolygonToBands(const tools::PolyPolygon
& rPolyPoly
, const Rectangle
& rPolygonBoundingBox
)
188 // initialisation and creation of Bands
189 RegionBand
* pRegionBand
= new RegionBand();
190 pRegionBand
->CreateBandRange(rPolygonBoundingBox
.Top(), rPolygonBoundingBox
.Bottom());
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!
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
++ );
223 } // end of anonymous namespace
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());
251 if(ImplIsPolygonRectilinear(aPolyPolygon
))
253 // For rectilinear polygons there is an optimized band conversion.
254 pRetval
= ImplRectilinearPolygonToBands(aPolyPolygon
);
258 pRetval
= ImplGeneralPolygonToBands(aPolyPolygon
, aRect
);
261 // Convert points into seps.
264 pRetval
->processPoints();
266 // Optimize list of bands. Adjacent bands with identical lists
267 // of seps are joined.
268 if(!pRetval
->OptimizeBandList())
281 tools::PolyPolygon
vcl::Region::ImplCreatePolyPolygonFromRegionBand() const
283 tools::PolyPolygon aRetval
;
287 RectangleVector aRectangles
;
288 GetRegionRectangles(aRectangles
);
290 for(RectangleVector::const_iterator
aRectIter(aRectangles
.begin()); aRectIter
!= aRectangles
.end(); ++aRectIter
)
292 aRetval
.Insert(Polygon(*aRectIter
));
297 OSL_ENSURE(false, "Called with no local RegionBand (!)");
303 basegfx::B2DPolyPolygon
vcl::Region::ImplCreateB2DPolyPolygonFromRegionBand() const
305 tools::PolyPolygon
aPoly(ImplCreatePolyPolygonFromRegionBand());
307 return aPoly
.getB2DPolyPolygon();
310 Region::Region(bool bIsNull
)
311 : mpB2DPolyPolygon(),
318 Region::Region(const Rectangle
& rRect
)
319 : mpB2DPolyPolygon(),
324 mpRegionBand
.reset(rRect
.IsEmpty() ? 0 : new RegionBand(rRect
));
327 Region::Region(const Polygon
& rPolygon
)
328 : mpB2DPolyPolygon(),
334 if(rPolygon
.GetSize())
336 ImplCreatePolyPolyRegion(rPolygon
);
340 Region::Region(const tools::PolyPolygon
& rPolyPoly
)
341 : mpB2DPolyPolygon(),
347 if(rPolyPoly
.Count())
349 ImplCreatePolyPolyRegion(rPolyPoly
);
353 Region::Region(const basegfx::B2DPolyPolygon
& rPolyPoly
)
354 : mpB2DPolyPolygon(),
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
)
378 void vcl::Region::ImplCreatePolyPolyRegion( const tools::PolyPolygon
& rPolyPoly
)
380 const sal_uInt16 nPolyCount
= rPolyPoly
.Count();
384 // polypolygon empty? -> empty region
385 const Rectangle
aRect(rPolyPoly
.GetBoundRect());
389 // width OR height == 1 ? => Rectangular region
390 if((1 == aRect
.GetWidth()) || (1 == aRect
.GetHeight()) || rPolyPoly
.IsRect())
392 mpRegionBand
.reset(new RegionBand(aRect
));
396 mpPolyPolygon
.reset(new tools::PolyPolygon(rPolyPoly
));
404 void vcl::Region::ImplCreatePolyPolyRegion( const basegfx::B2DPolyPolygon
& rPolyPoly
)
406 if(rPolyPoly
.count() && !rPolyPoly
.getB2DRange().isEmpty())
408 mpB2DPolyPolygon
.reset(new basegfx::B2DPolyPolygon(rPolyPoly
));
413 void vcl::Region::Move( long nHorzMove
, long nVertMove
)
415 if(IsNull() || IsEmpty())
417 // empty or null need no move
421 if(!nHorzMove
&& !nVertMove
)
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
);
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
468 if(basegfx::fTools::equalZero(fScaleX
) && basegfx::fTools::equalZero(fScaleY
))
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
);
503 OSL_ENSURE(false, "Region::Scale error: impossible combination (!)");
507 bool vcl::Region::Union( const Rectangle
& rRect
)
511 // empty rectangle will not expand the existing union, nothing to do
517 // no local data, the union will be equal to source. Create using rectangle
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
536 // get the other B2DPolyPolygon and use logical Or-Operation
537 const basegfx::B2DPolygon
aRectPoly(
538 basegfx::tools::createPolygonFromRect(
539 basegfx::B2DRectangle(
544 const basegfx::B2DPolyPolygon
aClip(
545 basegfx::tools::solvePolygonOperationOr(
547 basegfx::B2DPolyPolygon(aRectPoly
)));
548 *this = vcl::Region(aClip
);
554 // only region band mode possibility left here or null/empty
555 const RegionBand
* pCurrent
= getRegionBand();
559 // no region band, create using the rectangle
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
);
576 pNew
->Union(nLeft
, nTop
, nRight
, nBottom
);
579 if(!pNew
->OptimizeBandList())
585 mpRegionBand
.reset(pNew
);
589 bool vcl::Region::Intersect( const Rectangle
& rRect
)
591 if ( rRect
.IsEmpty() )
593 // empty rectangle will create empty region
600 // null region (everything) intersect with rect will give rect
607 // no content, cannot get more empty
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(),
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
640 mpB2DPolyPolygon
.reset();
641 mpPolyPolygon
.reset(aPoly
.Count() ? new tools::PolyPolygon(aPoly
) : 0);
642 mpRegionBand
.reset();
648 // only region band mode possibility left here or null/empty
649 const RegionBand
* pCurrent
= getRegionBand();
653 // region is empty -> nothing to do!
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
);
669 pNew
->Intersect(nLeft
, nTop
, nRight
, nBottom
);
672 if(!pNew
->OptimizeBandList())
678 mpRegionBand
.reset(pNew
);
682 bool vcl::Region::Exclude( const Rectangle
& rRect
)
684 if ( rRect
.IsEmpty() )
686 // excluding nothing will do no change
692 // cannot exclude from empty, done
698 // error; cannot exclude from null region since this is not representable
700 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
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
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
);
729 // only region band mode possibility left here or null/empty
730 const RegionBand
* pCurrent
= getRegionBand();
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
);
750 pNew
->Exclude(nLeft
, nTop
, nRight
, nBottom
);
753 if(!pNew
->OptimizeBandList())
759 mpRegionBand
.reset(pNew
);
763 bool vcl::Region::XOr( const Rectangle
& rRect
)
765 if ( rRect
.IsEmpty() )
767 // empty rectangle will not change local content
773 // rRect will be the xored-form (local off, rect on)
780 // error; cannot exclude from null region since this is not representable
782 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
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
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
);
812 // only region band mode possibility left here or null/empty
813 const RegionBand
* pCurrent
= getRegionBand();
817 // rRect will be the xored-form (local off, rect on)
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
);
835 pNew
->XOr(nLeft
, nTop
, nRight
, nBottom
);
838 if(!pNew
->OptimizeBandList())
844 mpRegionBand
.reset(pNew
);
848 bool vcl::Region::Union( const vcl::Region
& rRegion
)
850 if(rRegion
.IsEmpty())
852 // no extension at all
858 // extending with null region -> null region
859 *this = vcl::Region(true);
865 // local is empty, union will give source region
872 // already fully expanded (is null region), cannot be extended
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
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
);
901 // only region band mode possibility left here or null/empty
902 const RegionBand
* pCurrent
= getRegionBand();
906 // local is empty, union will give source region
911 const RegionBand
* pSource
= rRegion
.getRegionBand();
915 // no extension at all
919 // prepare source and target
920 RegionBand
* pNew
= new RegionBand(*pCurrent
);
923 pNew
->Union(*pSource
);
926 if(!pNew
->OptimizeBandList())
932 mpRegionBand
.reset(pNew
);
936 bool vcl::Region::Intersect( const vcl::Region
& rRegion
)
938 // same instance data? -> nothing to do!
939 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion
.getB2DPolyPolygon())
944 if(getPolyPolygon() && getPolyPolygon() == rRegion
.getPolyPolygon())
949 if(getRegionBand() && getRegionBand() == rRegion
.getRegionBand())
956 // source region is null-region, intersect will not change local region
962 // when local region is null-region, intersect will be equal to source
967 if(rRegion
.IsEmpty())
969 // source region is empty, intersection will always be empty
976 // local region is empty, cannot get more emty than that. Nothing to do
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
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
1001 const basegfx::B2DPolyPolygon
aClip(
1002 basegfx::tools::clipPolyPolygonOnPolyPolygon(
1007 *this = vcl::Region( aClip
);
1011 // only region band mode possibility left here or null/empty
1012 const RegionBand
* pCurrent
= getRegionBand();
1016 // local region is empty, cannot get more emty than that. Nothing to do
1020 const RegionBand
* pSource
= rRegion
.getRegionBand();
1024 // source region is empty, intersection will always be empty
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
;
1039 // prepare new regionBand
1040 RegionBand
* pNew
= new RegionBand(*pCurrent
);
1042 // intersect with source
1043 pNew
->Intersect(*pSource
);
1046 if(!pNew
->OptimizeBandList())
1052 mpRegionBand
.reset(pNew
);
1058 bool vcl::Region::Exclude( const vcl::Region
& rRegion
)
1060 if ( rRegion
.IsEmpty() )
1062 // excluding nothing will do no change
1066 if ( rRegion
.IsNull() )
1068 // excluding everything will create empty region
1075 // cannot exclude from empty, done
1081 // error; cannot exclude from null region since this is not representable
1083 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
1087 if( rRegion
.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1089 // get this B2DPolyPolygon
1090 basegfx::B2DPolyPolygon
aThisPolyPoly(GetAsB2DPolyPolygon());
1092 if(!aThisPolyPoly
.count())
1094 // cannot exclude from empty, done
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
);
1109 // only region band mode possibility left here or null/empty
1110 const RegionBand
* pCurrent
= getRegionBand();
1114 // cannot exclude from empty, done
1118 const RegionBand
* pSource
= rRegion
.getRegionBand();
1122 // excluding nothing will do no change
1126 // prepare source and target
1127 RegionBand
* pNew
= new RegionBand(*pCurrent
);
1129 // union with source
1130 const bool bSuccess(pNew
->Exclude(*pSource
));
1139 mpRegionBand
.reset(pNew
);
1143 bool vcl::Region::XOr( const vcl::Region
& rRegion
)
1145 if ( rRegion
.IsEmpty() )
1147 // empty region will not change local content
1151 if ( rRegion
.IsNull() )
1153 // error; cannot exclude null region from local since this is not representable
1155 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1161 // rRect will be the xored-form (local off, rect on)
1168 // error: cannot exclude from null region since this is not representable
1170 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
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)
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
);
1197 // only region band mode possibility left here or null/empty
1198 const RegionBand
* pCurrent
= getRegionBand();
1202 // rRect will be the xored-form (local off, rect on)
1207 const RegionBand
* pSource
= rRegion
.getRegionBand();
1211 // empty region will not change local content
1215 // prepare source and target
1216 RegionBand
* pNew
= new RegionBand(*pCurrent
);
1218 // union with source
1219 pNew
->XOr(*pSource
);
1222 if(!pNew
->OptimizeBandList())
1228 mpRegionBand
.reset(pNew
);
1233 Rectangle
vcl::Region::GetBoundRect() const
1237 // no internal data? -> region is empty!
1243 // error; null region has no BoundRect
1244 // OSL_ENSURE(false, "Region::GetBoundRect error: null region has unlimitied bound rect, not representable (!)");
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
1260 // #i122149# corrected rounding, no need for ceil() and floor() here
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();
1274 return getRegionBand()->GetBoundRect();
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();
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();
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
1359 // no point can be in empty region
1365 // all points are inside null-region
1369 // Too expensive (?)
1370 //if(mpImplRegion->getRegionPolyPoly())
1372 // return mpImplRegion->getRegionPolyPoly()->IsInside( rPoint );
1375 // ensure RegionBand existence
1376 const RegionBand
* pRegionBand
= GetAsRegionBand();
1380 return pRegionBand
->IsInside(rPoint
);
1386 bool vcl::Region::IsInside( const Rectangle
& rRect
) const
1390 // no rectangle can be in empty region
1396 // rectangle always inside null-region
1400 if ( rRect
.IsEmpty() )
1402 // is rectangle empty? -> not inside
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
1418 // nothing can be over something empty
1424 // everything is over null region
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() )
1443 if( getB2DPolyPolygon() )
1444 return basegfx::tools::isRectangle( *getB2DPolyPolygon() );
1446 if( getPolyPolygon() )
1447 return getPolyPolygon()->IsRect();
1449 if( getRegionBand() )
1450 return (getRegionBand()->getRectangleCount() == 1);
1455 void vcl::Region::SetNull()
1457 // reset all content
1458 mpB2DPolyPolygon
.reset();
1459 mpPolyPolygon
.reset();
1460 mpRegionBand
.reset();
1464 void vcl::Region::SetEmpty()
1466 // reset all content
1467 mpB2DPolyPolygon
.reset();
1468 mpPolyPolygon
.reset();
1469 mpRegionBand
.reset();
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
;
1484 Region
& vcl::Region::operator=( const Rectangle
& rRect
)
1486 mpB2DPolyPolygon
.reset();
1487 mpPolyPolygon
.reset();
1488 mpRegionBand
.reset(rRect
.IsEmpty() ? 0 : new RegionBand(rRect
));
1494 bool vcl::Region::operator==( const vcl::Region
& rRegion
) const
1496 if(IsNull() && rRegion
.IsNull())
1498 // both are null region
1502 if(IsEmpty() && rRegion
.IsEmpty())
1508 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion
.getB2DPolyPolygon())
1510 // same instance data? -> equal
1514 if(getPolyPolygon() && getPolyPolygon() == rRegion
.getPolyPolygon())
1516 // same instance data? -> equal
1520 if(getRegionBand() && getRegionBand() == rRegion
.getRegionBand())
1522 // same instance data? -> equal
1526 if(IsNull() || IsEmpty())
1531 if(rRegion
.IsNull() || rRegion
.IsEmpty())
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
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,
1559 if(rRegion
.getRegionBand() && getRegionBand())
1561 return *rRegion
.getRegionBand() == *getRegionBand();
1564 // should not happen, but better deny equality
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
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
)
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
);
1614 tools::PolyPolygon
* pNewPoly
= new tools::PolyPolygon();
1615 ReadPolyPolygon( rIStrm
, *pNewPoly
);
1616 rRegion
.mpPolyPolygon
.reset(pNewPoly
);
1627 SvStream
& WriteRegion( SvStream
& rOStrm
, const vcl::Region
& rRegion
)
1629 const sal_uInt16
nVersion(2);
1630 VersionCompat
aCompat(rOStrm
, StreamMode::WRITE
, nVersion
);
1633 rOStrm
.WriteUInt16( nVersion
);
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 (!)");
1646 if(!bEmpty
&& rRegion
.getPolyPolygon() && 0 == rRegion
.getPolyPolygon()->Count())
1648 OSL_ENSURE(false, "Region with empty PolyPolygon, should not be created (!)");
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
);
1668 const RegionBand
* pRegionBand
= rRegion
.getRegionBand();
1672 pRegionBand
->save(rOStrm
);
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
);
1690 tools::PolyPolygon aNoCurvePolyPolygon
;
1691 rRegion
.GetAsPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon
);
1693 WritePolyPolygon( rOStrm
, aNoCurvePolyPolygon
);
1699 void vcl::Region::GetRegionRectangles(RectangleVector
& rTarget
) const
1701 // clear returnvalues
1704 // ensure RegionBand existence
1705 const RegionBand
* pRegionBand
= GetAsRegionBand();
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
) ) )
1756 pRectOut
->Left() = nX1
;
1757 pRectOut
->Right() = nX2
;
1758 pRectOut
->Top() = nY1
;
1759 pRectOut
->Bottom() = nY2
;
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
) )
1791 if( nPolygonPolygons
> nPolygonRects
)
1793 return vcl::Region( rPolyPoly
);
1796 vcl::Region aResult
;
1799 for( sal_uInt16 i
= 0; i
< nPolygons
; i
++ )
1801 const Polygon
& rPoly
= rPolyPoly
[i
];
1803 if( ImplPolygonRectTest( rPoly
, &aRect
) )
1805 aResult
.XOr( aRect
);
1809 aResult
.XOr( vcl::Region(rPoly
) );
1816 } /* namespace vcl */
1818 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */