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 .
22 #include <tools/vcompat.hxx>
23 #include <tools/stream.hxx>
24 #include <tools/helpers.hxx>
25 #include <osl/diagnose.h>
26 #include <vcl/region.hxx>
27 #include <regionband.hxx>
29 #include <basegfx/matrix/b2dhommatrix.hxx>
30 #include <basegfx/polygon/b2dpolypolygontools.hxx>
31 #include <basegfx/polygon/b2dpolygontools.hxx>
32 #include <basegfx/polygon/b2dpolygonclipper.hxx>
33 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
34 #include <basegfx/range/b2drange.hxx>
35 #include <basegfx/matrix/b2dhommatrixtools.hxx>
36 #include <tools/poly.hxx>
37 #include <o3tl/make_unique.hxx>
41 /** Return <TRUE/> when the given polygon is rectilinear and oriented so that
42 all sides are either horizontal or vertical.
44 bool ImplIsPolygonRectilinear (const tools::PolyPolygon
& rPolyPoly
)
46 // Iterate over all polygons.
47 const sal_uInt16 nPolyCount
= rPolyPoly
.Count();
48 for (sal_uInt16 nPoly
= 0; nPoly
< nPolyCount
; ++nPoly
)
50 const tools::Polygon
& aPoly
= rPolyPoly
.GetObject(nPoly
);
52 // Iterate over all edges of the current polygon.
53 const sal_uInt16 nSize
= aPoly
.GetSize();
57 Point
aPoint (aPoly
.GetPoint(0));
58 const Point
aLastPoint (aPoint
);
59 for (sal_uInt16 nPoint
= 1; nPoint
< nSize
; ++nPoint
)
61 const Point
aNextPoint (aPoly
.GetPoint(nPoint
));
62 // When there is at least one edge that is neither vertical nor
63 // horizontal then the entire polygon is not rectilinear (and
64 // oriented along primary axes.)
65 if (aPoint
.X() != aNextPoint
.X() && aPoint
.Y() != aNextPoint
.Y())
70 // Compare closing edge.
71 if (aLastPoint
.X() != aPoint
.X() && aLastPoint
.Y() != aPoint
.Y())
77 /** Convert a rectilinear polygon (that is oriented along the primary axes)
78 to a list of bands. For this special form of polygon we can use an
79 optimization that prevents the creation of one band per y value.
80 However, it still is possible that some temporary bands are created that
81 later can be optimized away.
83 A set of zero, one, or more polygons, nested or not, that are
84 converted into a list of bands.
86 A new RegionBand object is returned that contains the bands that
87 represent the given poly-polygon.
89 std::unique_ptr
<RegionBand
> ImplRectilinearPolygonToBands(const tools::PolyPolygon
& rPolyPoly
)
91 OSL_ASSERT(ImplIsPolygonRectilinear (rPolyPoly
));
93 // Create a new RegionBand object as container of the bands.
94 std::unique_ptr
<RegionBand
> pRegionBand( o3tl::make_unique
<RegionBand
>() );
97 // Iterate over all polygons.
98 const sal_uInt16 nPolyCount
= rPolyPoly
.Count();
99 for (sal_uInt16 nPoly
= 0; nPoly
< nPolyCount
; ++nPoly
)
101 const tools::Polygon
& aPoly
= rPolyPoly
.GetObject(nPoly
);
103 // Iterate over all edges of the current polygon.
104 const sal_uInt16 nSize
= aPoly
.GetSize();
107 // Avoid fetching every point twice (each point is the start point
108 // of one and the end point of another edge.)
109 Point
aStart (aPoly
.GetPoint(0));
111 for (sal_uInt16 nPoint
= 1; nPoint
<= nSize
; ++nPoint
, aStart
=aEnd
)
113 // We take the implicit closing edge into account by mapping
115 aEnd
= aPoly
.GetPoint(nPoint
%nSize
);
116 if (aStart
.Y() == aEnd
.Y())
118 // Horizontal lines are ignored.
122 // At this point the line has to be vertical.
123 OSL_ASSERT(aStart
.X() == aEnd
.X());
125 // Sort y-coordinates to simplify the algorithm and store the
126 // direction separately. The direction is calculated as it is
127 // in other places (but seems to be the wrong way.)
128 const long nTop (::std::min(aStart
.Y(), aEnd
.Y()));
129 const long nBottom (::std::max(aStart
.Y(), aEnd
.Y()));
130 const LineType
eLineType (aStart
.Y() > aEnd
.Y() ? LINE_DESCENDING
: LINE_ASCENDING
);
132 // Make sure that the current line is covered by bands.
133 pRegionBand
->ImplAddMissingBands(nTop
,nBottom
);
135 // Find top-most band that may contain nTop.
136 ImplRegionBand
* pBand
= pRegionBand
->ImplGetFirstRegionBand();
137 while (pBand
!=nullptr && pBand
->mnYBottom
< nTop
)
138 pBand
= pBand
->mpNextBand
;
139 ImplRegionBand
* pTopBand
= pBand
;
140 // If necessary split the band at nTop so that nTop is contained
141 // in the lower band.
143 // Prevent the current band from becoming 0 pixel high
144 && pBand
->mnYTop
<nTop
145 // this allows the lowest pixel of the band to be split off
146 && pBand
->mnYBottom
>=nTop
147 // do not split a band that is just one pixel high
148 && pBand
->mnYTop
<pBand
->mnYBottom
)
150 // Split the top band.
151 pTopBand
= pBand
->SplitBand(nTop
);
154 // Advance to band that may contain nBottom.
155 while (pBand
!=nullptr && pBand
->mnYBottom
< nBottom
)
156 pBand
= pBand
->mpNextBand
;
157 // The lowest band may have to be split at nBottom so that
158 // nBottom itself remains in the upper band.
160 // allow the current band becoming 1 pixel high
161 && pBand
->mnYTop
<=nBottom
162 // prevent splitting off a band that is 0 pixel high
163 && pBand
->mnYBottom
>nBottom
164 // do not split a band that is just one pixel high
165 && pBand
->mnYTop
<pBand
->mnYBottom
)
167 // Split the bottom band.
168 pBand
->SplitBand(nBottom
+1);
171 // Note that we remember the top band (in pTopBand) but not the
172 // bottom band. The later can be determined by comparing y
175 // Add the x-value as point to all bands in the nTop->nBottom range.
176 for (pBand
=pTopBand
; pBand
!=nullptr&&pBand
->mnYTop
<=nBottom
; pBand
=pBand
->mpNextBand
)
177 pBand
->InsertPoint(aStart
.X(), nLineId
++, true, eLineType
);
184 /** Convert a general polygon (one for which ImplIsPolygonRectilinear()
185 returns <FALSE/>) to bands.
187 std::unique_ptr
<RegionBand
> ImplGeneralPolygonToBands(const tools::PolyPolygon
& rPolyPoly
, const tools::Rectangle
& rPolygonBoundingBox
)
191 // initialisation and creation of Bands
192 std::unique_ptr
<RegionBand
> pRegionBand( o3tl::make_unique
<RegionBand
>() );
193 pRegionBand
->CreateBandRange(rPolygonBoundingBox
.Top(), rPolygonBoundingBox
.Bottom());
196 const sal_uInt16 nPolyCount
= rPolyPoly
.Count();
198 for ( sal_uInt16 nPoly
= 0; nPoly
< nPolyCount
; nPoly
++ )
200 // get reference to current polygon
201 const tools::Polygon
& aPoly
= rPolyPoly
.GetObject( nPoly
);
202 const sal_uInt16 nSize
= aPoly
.GetSize();
204 // not enough points ( <= 2 )? -> nothing to do!
209 for ( sal_uInt16 nPoint
= 1; nPoint
< nSize
; nPoint
++ )
211 pRegionBand
->InsertLine( aPoly
.GetPoint(nPoint
-1), aPoly
.GetPoint(nPoint
), nLineID
++ );
214 // close polygon with line from first point to last point, if necessary
215 const Point rLastPoint
= aPoly
.GetPoint(nSize
-1);
216 const Point rFirstPoint
= aPoly
.GetPoint(0);
218 if ( rLastPoint
!= rFirstPoint
)
220 pRegionBand
->InsertLine( rLastPoint
, rFirstPoint
, nLineID
++ );
226 } // end of anonymous namespace
230 bool vcl::Region::IsEmpty() const
232 return !mbIsNull
&& !mpB2DPolyPolygon
.get() && !mpPolyPolygon
.get() && !mpRegionBand
.get();
236 std::unique_ptr
<RegionBand
> ImplCreateRegionBandFromPolyPolygon(const tools::PolyPolygon
& rPolyPolygon
)
238 std::unique_ptr
<RegionBand
> pRetval
;
240 if(rPolyPolygon
.Count())
242 // ensure to subdivide when bezier segments are used, it's going to
243 // be expanded to rectangles
244 tools::PolyPolygon aPolyPolygon
;
246 rPolyPolygon
.AdaptiveSubdivide(aPolyPolygon
);
248 if(aPolyPolygon
.Count())
250 const tools::Rectangle
aRect(aPolyPolygon
.GetBoundRect());
254 if(ImplIsPolygonRectilinear(aPolyPolygon
))
256 // For rectilinear polygons there is an optimized band conversion.
257 pRetval
= ImplRectilinearPolygonToBands(aPolyPolygon
);
261 pRetval
= ImplGeneralPolygonToBands(aPolyPolygon
, aRect
);
264 // Convert points into seps.
267 pRetval
->processPoints();
269 // Optimize list of bands. Adjacent bands with identical lists
270 // of seps are joined.
271 if(!pRetval
->OptimizeBandList())
283 tools::PolyPolygon
vcl::Region::ImplCreatePolyPolygonFromRegionBand() const
285 tools::PolyPolygon aRetval
;
289 RectangleVector aRectangles
;
290 GetRegionRectangles(aRectangles
);
292 for(RectangleVector::const_iterator
aRectIter(aRectangles
.begin()); aRectIter
!= aRectangles
.end(); ++aRectIter
)
294 aRetval
.Insert( tools::Polygon(*aRectIter
) );
299 OSL_ENSURE(false, "Called with no local RegionBand (!)");
305 basegfx::B2DPolyPolygon
vcl::Region::ImplCreateB2DPolyPolygonFromRegionBand() const
307 tools::PolyPolygon
aPoly(ImplCreatePolyPolygonFromRegionBand());
309 return aPoly
.getB2DPolyPolygon();
312 Region::Region(bool bIsNull
)
313 : mpB2DPolyPolygon(),
320 Region::Region(const tools::Rectangle
& rRect
)
321 : mpB2DPolyPolygon(),
326 mpRegionBand
.reset(rRect
.IsEmpty() ? nullptr : new RegionBand(rRect
));
329 Region::Region(const tools::Polygon
& rPolygon
)
330 : mpB2DPolyPolygon(),
336 if(rPolygon
.GetSize())
338 ImplCreatePolyPolyRegion(rPolygon
);
342 Region::Region(const tools::PolyPolygon
& rPolyPoly
)
343 : mpB2DPolyPolygon(),
349 if(rPolyPoly
.Count())
351 ImplCreatePolyPolyRegion(rPolyPoly
);
355 Region::Region(const basegfx::B2DPolyPolygon
& rPolyPoly
)
356 : mpB2DPolyPolygon(),
362 if(rPolyPoly
.count())
364 ImplCreatePolyPolyRegion(rPolyPoly
);
368 Region::Region(const vcl::Region
& rRegion
)
369 : mpB2DPolyPolygon(rRegion
.mpB2DPolyPolygon
),
370 mpPolyPolygon(rRegion
.mpPolyPolygon
),
371 mpRegionBand(rRegion
.mpRegionBand
),
372 mbIsNull(rRegion
.mbIsNull
)
376 Region::Region(vcl::Region
&& rRegion
)
377 : mpB2DPolyPolygon(std::move(rRegion
.mpB2DPolyPolygon
)),
378 mpPolyPolygon(std::move(rRegion
.mpPolyPolygon
)),
379 mpRegionBand(std::move(rRegion
.mpRegionBand
)),
380 mbIsNull(rRegion
.mbIsNull
)
382 rRegion
.mbIsNull
= true;
389 void vcl::Region::ImplCreatePolyPolyRegion( const tools::PolyPolygon
& rPolyPoly
)
391 const sal_uInt16 nPolyCount
= rPolyPoly
.Count();
395 // polypolygon empty? -> empty region
396 const tools::Rectangle
aRect(rPolyPoly
.GetBoundRect());
400 // width OR height == 1 ? => Rectangular region
401 if((1 == aRect
.GetWidth()) || (1 == aRect
.GetHeight()) || rPolyPoly
.IsRect())
403 mpRegionBand
.reset(new RegionBand(aRect
));
407 mpPolyPolygon
.reset(new tools::PolyPolygon(rPolyPoly
));
415 void vcl::Region::ImplCreatePolyPolyRegion( const basegfx::B2DPolyPolygon
& rPolyPoly
)
417 if(rPolyPoly
.count() && !rPolyPoly
.getB2DRange().isEmpty())
419 mpB2DPolyPolygon
.reset(new basegfx::B2DPolyPolygon(rPolyPoly
));
424 void vcl::Region::Move( long nHorzMove
, long nVertMove
)
426 if(IsNull() || IsEmpty())
428 // empty or null need no move
432 if(!nHorzMove
&& !nVertMove
)
438 if(getB2DPolyPolygon())
440 basegfx::B2DPolyPolygon
aPoly(*getB2DPolyPolygon());
442 aPoly
.transform(basegfx::tools::createTranslateB2DHomMatrix(nHorzMove
, nVertMove
));
443 mpB2DPolyPolygon
.reset(aPoly
.count() ? new basegfx::B2DPolyPolygon(aPoly
) : nullptr);
444 mpPolyPolygon
.reset();
445 mpRegionBand
.reset();
447 else if(getPolyPolygon())
449 tools::PolyPolygon
aPoly(*getPolyPolygon());
451 aPoly
.Move(nHorzMove
, nVertMove
);
452 mpB2DPolyPolygon
.reset();
453 mpPolyPolygon
.reset(aPoly
.Count() ? new tools::PolyPolygon(aPoly
) : nullptr);
454 mpRegionBand
.reset();
456 else if(getRegionBand())
458 RegionBand
* pNew
= new RegionBand(*getRegionBand());
460 pNew
->Move(nHorzMove
, nVertMove
);
461 mpB2DPolyPolygon
.reset();
462 mpPolyPolygon
.reset();
463 mpRegionBand
.reset(pNew
);
467 OSL_ENSURE(false, "Region::Move error: impossible combination (!)");
471 void vcl::Region::Scale( double fScaleX
, double fScaleY
)
473 if(IsNull() || IsEmpty())
475 // empty or null need no scale
479 if(basegfx::fTools::equalZero(fScaleX
) && basegfx::fTools::equalZero(fScaleY
))
485 if(getB2DPolyPolygon())
487 basegfx::B2DPolyPolygon
aPoly(*getB2DPolyPolygon());
489 aPoly
.transform(basegfx::tools::createScaleB2DHomMatrix(fScaleX
, fScaleY
));
490 mpB2DPolyPolygon
.reset(aPoly
.count() ? new basegfx::B2DPolyPolygon(aPoly
) : nullptr);
491 mpPolyPolygon
.reset();
492 mpRegionBand
.reset();
494 else if(getPolyPolygon())
496 tools::PolyPolygon
aPoly(*getPolyPolygon());
498 aPoly
.Scale(fScaleX
, fScaleY
);
499 mpB2DPolyPolygon
.reset();
500 mpPolyPolygon
.reset(aPoly
.Count() ? new tools::PolyPolygon(aPoly
) : nullptr);
501 mpRegionBand
.reset();
503 else if(getRegionBand())
505 RegionBand
* pNew
= new RegionBand(*getRegionBand());
507 pNew
->Scale(fScaleX
, fScaleY
);
508 mpB2DPolyPolygon
.reset();
509 mpPolyPolygon
.reset();
510 mpRegionBand
.reset(pNew
);
514 OSL_ENSURE(false, "Region::Scale error: impossible combination (!)");
518 bool vcl::Region::Union( const tools::Rectangle
& rRect
)
522 // empty rectangle will not expand the existing union, nothing to do
528 // no local data, the union will be equal to source. Create using rectangle
533 if(HasPolyPolygonOrB2DPolyPolygon())
535 // get this B2DPolyPolygon, solve on polygon base
536 basegfx::B2DPolyPolygon
aThisPolyPoly(GetAsB2DPolyPolygon());
538 aThisPolyPoly
= basegfx::tools::prepareForPolygonOperation(aThisPolyPoly
);
540 if(!aThisPolyPoly
.count())
542 // no local polygon, use the rectangle as new region
547 // get the other B2DPolyPolygon and use logical Or-Operation
548 const basegfx::B2DPolygon
aRectPoly(
549 basegfx::tools::createPolygonFromRect(
550 basegfx::B2DRectangle(
555 const basegfx::B2DPolyPolygon
aClip(
556 basegfx::tools::solvePolygonOperationOr(
558 basegfx::B2DPolyPolygon(aRectPoly
)));
559 *this = vcl::Region(aClip
);
565 // only region band mode possibility left here or null/empty
566 const RegionBand
* pCurrent
= getRegionBand();
570 // no region band, create using the rectangle
575 std::unique_ptr
<RegionBand
> pNew( o3tl::make_unique
<RegionBand
>(*pCurrent
));
577 // get justified rectangle
578 const long nLeft(std::min(rRect
.Left(), rRect
.Right()));
579 const long nTop(std::min(rRect
.Top(), rRect
.Bottom()));
580 const long nRight(std::max(rRect
.Left(), rRect
.Right()));
581 const long nBottom(std::max(rRect
.Top(), rRect
.Bottom()));
583 // insert bands if the boundaries are not already in the list
584 pNew
->InsertBands(nTop
, nBottom
);
587 pNew
->Union(nLeft
, nTop
, nRight
, nBottom
);
590 if(!pNew
->OptimizeBandList())
595 mpRegionBand
= std::move(pNew
);
599 bool vcl::Region::Intersect( const tools::Rectangle
& rRect
)
601 if ( rRect
.IsEmpty() )
603 // empty rectangle will create empty region
610 // null region (everything) intersect with rect will give rect
617 // no content, cannot get more empty
621 if(HasPolyPolygonOrB2DPolyPolygon())
623 // if polygon data prefer double precision, the other will be lost (if buffered)
624 if(getB2DPolyPolygon())
626 const basegfx::B2DPolyPolygon
aPoly(
627 basegfx::tools::clipPolyPolygonOnRange(
628 *getB2DPolyPolygon(),
637 mpB2DPolyPolygon
.reset(aPoly
.count() ? new basegfx::B2DPolyPolygon(aPoly
) : nullptr);
638 mpPolyPolygon
.reset();
639 mpRegionBand
.reset();
641 else // if(getPolyPolygon())
643 tools::PolyPolygon
aPoly(*getPolyPolygon());
645 // use the PolyPolygon::Clip method for rectangles, this is
646 // fairly simple (does not even use GPC) and saves us from
647 // unnecessary banding
650 mpB2DPolyPolygon
.reset();
651 mpPolyPolygon
.reset(aPoly
.Count() ? new tools::PolyPolygon(aPoly
) : nullptr);
652 mpRegionBand
.reset();
658 // only region band mode possibility left here or null/empty
659 const RegionBand
* pCurrent
= getRegionBand();
663 // region is empty -> nothing to do!
667 std::unique_ptr
<RegionBand
> pNew( o3tl::make_unique
<RegionBand
>(*pCurrent
));
669 // get justified rectangle
670 const long nLeft(std::min(rRect
.Left(), rRect
.Right()));
671 const long nTop(std::min(rRect
.Top(), rRect
.Bottom()));
672 const long nRight(std::max(rRect
.Left(), rRect
.Right()));
673 const long nBottom(std::max(rRect
.Top(), rRect
.Bottom()));
675 // insert bands if the boundaries are not already in the list
676 pNew
->InsertBands(nTop
, nBottom
);
679 pNew
->Intersect(nLeft
, nTop
, nRight
, nBottom
);
682 if(!pNew
->OptimizeBandList())
687 mpRegionBand
= std::move(pNew
);
691 bool vcl::Region::Exclude( const tools::Rectangle
& rRect
)
693 if ( rRect
.IsEmpty() )
695 // excluding nothing will do no change
701 // cannot exclude from empty, done
707 // error; cannot exclude from null region since this is not representable
709 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
713 if( HasPolyPolygonOrB2DPolyPolygon() )
715 // get this B2DPolyPolygon
716 basegfx::B2DPolyPolygon
aThisPolyPoly(GetAsB2DPolyPolygon());
718 aThisPolyPoly
= basegfx::tools::prepareForPolygonOperation(aThisPolyPoly
);
720 if(!aThisPolyPoly
.count())
722 // when local polygon is empty, nothing can be excluded
726 // get the other B2DPolyPolygon
727 const basegfx::B2DPolygon
aRectPoly(
728 basegfx::tools::createPolygonFromRect(
729 basegfx::B2DRectangle(rRect
.Left(), rRect
.Top(), rRect
.Right(), rRect
.Bottom())));
730 const basegfx::B2DPolyPolygon
aOtherPolyPoly(aRectPoly
);
731 const basegfx::B2DPolyPolygon aClip
= basegfx::tools::solvePolygonOperationDiff(aThisPolyPoly
, aOtherPolyPoly
);
733 *this = vcl::Region(aClip
);
738 // only region band mode possibility left here or null/empty
739 const RegionBand
* pCurrent
= getRegionBand();
747 std::unique_ptr
<RegionBand
> pNew( o3tl::make_unique
<RegionBand
>(*pCurrent
));
749 // get justified rectangle
750 const long nLeft(std::min(rRect
.Left(), rRect
.Right()));
751 const long nTop(std::min(rRect
.Top(), rRect
.Bottom()));
752 const long nRight(std::max(rRect
.Left(), rRect
.Right()));
753 const long nBottom(std::max(rRect
.Top(), rRect
.Bottom()));
755 // insert bands if the boundaries are not already in the list
756 pNew
->InsertBands(nTop
, nBottom
);
759 pNew
->Exclude(nLeft
, nTop
, nRight
, nBottom
);
762 if(!pNew
->OptimizeBandList())
767 mpRegionBand
= std::move(pNew
);
771 bool vcl::Region::XOr( const tools::Rectangle
& rRect
)
773 if ( rRect
.IsEmpty() )
775 // empty rectangle will not change local content
781 // rRect will be the xored-form (local off, rect on)
788 // error; cannot exclude from null region since this is not representable
790 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
794 if( HasPolyPolygonOrB2DPolyPolygon() )
796 // get this B2DPolyPolygon
797 basegfx::B2DPolyPolygon
aThisPolyPoly(GetAsB2DPolyPolygon());
799 aThisPolyPoly
= basegfx::tools::prepareForPolygonOperation( aThisPolyPoly
);
801 if(!aThisPolyPoly
.count())
803 // no local content, XOr will be equal to rectangle
808 // get the other B2DPolyPolygon
809 const basegfx::B2DPolygon
aRectPoly(
810 basegfx::tools::createPolygonFromRect(
811 basegfx::B2DRectangle(rRect
.Left(), rRect
.Top(), rRect
.Right(), rRect
.Bottom())));
812 const basegfx::B2DPolyPolygon
aOtherPolyPoly(aRectPoly
);
813 const basegfx::B2DPolyPolygon aClip
= basegfx::tools::solvePolygonOperationXor(aThisPolyPoly
, aOtherPolyPoly
);
815 *this = vcl::Region(aClip
);
820 // only region band mode possibility left here or null/empty
821 const RegionBand
* pCurrent
= getRegionBand();
825 // rRect will be the xored-form (local off, rect on)
830 // only region band mode possibility left here or null/empty
831 std::unique_ptr
<RegionBand
> pNew( o3tl::make_unique
<RegionBand
>(*getRegionBand()));
833 // get justified rectangle
834 const long nLeft(std::min(rRect
.Left(), rRect
.Right()));
835 const long nTop(std::min(rRect
.Top(), rRect
.Bottom()));
836 const long nRight(std::max(rRect
.Left(), rRect
.Right()));
837 const long nBottom(std::max(rRect
.Top(), rRect
.Bottom()));
839 // insert bands if the boundaries are not already in the list
840 pNew
->InsertBands(nTop
, nBottom
);
843 pNew
->XOr(nLeft
, nTop
, nRight
, nBottom
);
846 if(!pNew
->OptimizeBandList())
851 mpRegionBand
= std::move(pNew
);
855 bool vcl::Region::Union( const vcl::Region
& rRegion
)
857 if(rRegion
.IsEmpty())
859 // no extension at all
865 // extending with null region -> null region
866 *this = vcl::Region(true);
872 // local is empty, union will give source region
879 // already fully expanded (is null region), cannot be extended
883 if( rRegion
.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
885 // get this B2DPolyPolygon
886 basegfx::B2DPolyPolygon
aThisPolyPoly(GetAsB2DPolyPolygon());
888 aThisPolyPoly
= basegfx::tools::prepareForPolygonOperation(aThisPolyPoly
);
890 if(!aThisPolyPoly
.count())
892 // when no local content, union will be equal to rRegion
897 // get the other B2DPolyPolygon
898 basegfx::B2DPolyPolygon
aOtherPolyPoly(rRegion
.GetAsB2DPolyPolygon());
899 aOtherPolyPoly
= basegfx::tools::prepareForPolygonOperation(aOtherPolyPoly
);
901 // use logical OR operation
902 basegfx::B2DPolyPolygon
aClip(basegfx::tools::solvePolygonOperationOr(aThisPolyPoly
, aOtherPolyPoly
));
904 *this = vcl::Region( aClip
);
908 // only region band mode possibility left here or null/empty
909 const RegionBand
* pCurrent
= getRegionBand();
913 // local is empty, union will give source region
918 const RegionBand
* pSource
= rRegion
.getRegionBand();
922 // no extension at all
926 // prepare source and target
927 std::unique_ptr
<RegionBand
> pNew( o3tl::make_unique
<RegionBand
>(*pCurrent
));
930 pNew
->Union(*pSource
);
933 if(!pNew
->OptimizeBandList())
938 mpRegionBand
= std::move(pNew
);
942 bool vcl::Region::Intersect( const vcl::Region
& rRegion
)
944 // same instance data? -> nothing to do!
945 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion
.getB2DPolyPolygon())
950 if(getPolyPolygon() && getPolyPolygon() == rRegion
.getPolyPolygon())
955 if(getRegionBand() && getRegionBand() == rRegion
.getRegionBand())
962 // source region is null-region, intersect will not change local region
968 // when local region is null-region, intersect will be equal to source
973 if(rRegion
.IsEmpty())
975 // source region is empty, intersection will always be empty
982 // local region is empty, cannot get more empty than that. Nothing to do
986 if( rRegion
.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
988 // get this B2DPolyPolygon
989 basegfx::B2DPolyPolygon
aThisPolyPoly(GetAsB2DPolyPolygon());
991 if(!aThisPolyPoly
.count())
993 // local region is empty, cannot get more empty than that. Nothing to do
997 // get the other B2DPolyPolygon
998 basegfx::B2DPolyPolygon
aOtherPolyPoly(rRegion
.GetAsB2DPolyPolygon());
1000 if(!aOtherPolyPoly
.count())
1002 // source region is empty, intersection will always be empty
1007 const basegfx::B2DPolyPolygon
aClip(
1008 basegfx::tools::clipPolyPolygonOnPolyPolygon(
1013 *this = vcl::Region( aClip
);
1017 // only region band mode possibility left here or null/empty
1018 const RegionBand
* pCurrent
= getRegionBand();
1022 // local region is empty, cannot get more empty than that. Nothing to do
1026 const RegionBand
* pSource
= rRegion
.getRegionBand();
1030 // source region is empty, intersection will always be empty
1035 // both RegionBands exist and are not empty
1036 if(pCurrent
->getRectangleCount() + 2 < pSource
->getRectangleCount())
1038 // when we have less rectangles, turn around the call
1039 vcl::Region aTempRegion
= rRegion
;
1040 aTempRegion
.Intersect( *this );
1041 *this = aTempRegion
;
1045 // prepare new regionBand
1046 std::unique_ptr
<RegionBand
> pNew( o3tl::make_unique
<RegionBand
>(*pCurrent
));
1048 // intersect with source
1049 pNew
->Intersect(*pSource
);
1052 if(!pNew
->OptimizeBandList())
1057 mpRegionBand
= std::move(pNew
);
1063 bool vcl::Region::Exclude( const vcl::Region
& rRegion
)
1065 if ( rRegion
.IsEmpty() )
1067 // excluding nothing will do no change
1071 if ( rRegion
.IsNull() )
1073 // excluding everything will create empty region
1080 // cannot exclude from empty, done
1086 // error; cannot exclude from null region since this is not representable
1088 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
1092 if( rRegion
.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1094 // get this B2DPolyPolygon
1095 basegfx::B2DPolyPolygon
aThisPolyPoly(GetAsB2DPolyPolygon());
1097 if(!aThisPolyPoly
.count())
1099 // cannot exclude from empty, done
1103 aThisPolyPoly
= basegfx::tools::prepareForPolygonOperation( aThisPolyPoly
);
1105 // get the other B2DPolyPolygon
1106 basegfx::B2DPolyPolygon
aOtherPolyPoly(rRegion
.GetAsB2DPolyPolygon());
1107 aOtherPolyPoly
= basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly
);
1109 basegfx::B2DPolyPolygon aClip
= basegfx::tools::solvePolygonOperationDiff( aThisPolyPoly
, aOtherPolyPoly
);
1110 *this = vcl::Region( aClip
);
1114 // only region band mode possibility left here or null/empty
1115 const RegionBand
* pCurrent
= getRegionBand();
1119 // cannot exclude from empty, done
1123 const RegionBand
* pSource
= rRegion
.getRegionBand();
1127 // excluding nothing will do no change
1131 // prepare source and target
1132 std::unique_ptr
<RegionBand
> pNew( o3tl::make_unique
<RegionBand
>(*pCurrent
));
1134 // union with source
1135 const bool bSuccess(pNew
->Exclude(*pSource
));
1143 mpRegionBand
= std::move(pNew
);
1147 bool vcl::Region::XOr( const vcl::Region
& rRegion
)
1149 if ( rRegion
.IsEmpty() )
1151 // empty region will not change local content
1155 if ( rRegion
.IsNull() )
1157 // error; cannot exclude null region from local since this is not representable
1159 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1165 // rRect will be the xored-form (local off, rect on)
1172 // error: cannot exclude from null region since this is not representable
1174 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1178 if( rRegion
.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1180 // get this B2DPolyPolygon
1181 basegfx::B2DPolyPolygon
aThisPolyPoly(GetAsB2DPolyPolygon());
1183 if(!aThisPolyPoly
.count())
1185 // rRect will be the xored-form (local off, rect on)
1190 aThisPolyPoly
= basegfx::tools::prepareForPolygonOperation( aThisPolyPoly
);
1192 // get the other B2DPolyPolygon
1193 basegfx::B2DPolyPolygon
aOtherPolyPoly(rRegion
.GetAsB2DPolyPolygon());
1194 aOtherPolyPoly
= basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly
);
1196 basegfx::B2DPolyPolygon aClip
= basegfx::tools::solvePolygonOperationXor( aThisPolyPoly
, aOtherPolyPoly
);
1197 *this = vcl::Region( aClip
);
1201 // only region band mode possibility left here or null/empty
1202 const RegionBand
* pCurrent
= getRegionBand();
1206 // rRect will be the xored-form (local off, rect on)
1211 const RegionBand
* pSource
= rRegion
.getRegionBand();
1215 // empty region will not change local content
1219 // prepare source and target
1220 std::unique_ptr
<RegionBand
> pNew( o3tl::make_unique
<RegionBand
>(*pCurrent
));
1222 // union with source
1223 pNew
->XOr(*pSource
);
1226 if(!pNew
->OptimizeBandList())
1231 mpRegionBand
= std::move(pNew
);
1236 tools::Rectangle
vcl::Region::GetBoundRect() const
1240 // no internal data? -> region is empty!
1241 return tools::Rectangle();
1246 // error; null region has no BoundRect
1247 // OSL_ENSURE(false, "Region::GetBoundRect error: null region has unlimited bound rect, not representable (!)");
1248 return tools::Rectangle();
1251 // prefer double precision source
1252 if(getB2DPolyPolygon())
1254 const basegfx::B2DRange
aRange(basegfx::tools::getRange(*getB2DPolyPolygon()));
1256 if(aRange
.isEmpty())
1258 // emulate PolyPolygon::GetBoundRect() when empty polygon
1259 return tools::Rectangle();
1263 // #i122149# corrected rounding, no need for ceil() and floor() here
1264 return tools::Rectangle(
1265 basegfx::fround(aRange
.getMinX()), basegfx::fround(aRange
.getMinY()),
1266 basegfx::fround(aRange
.getMaxX()), basegfx::fround(aRange
.getMaxY()));
1270 if(getPolyPolygon())
1272 return getPolyPolygon()->GetBoundRect();
1277 return getRegionBand()->GetBoundRect();
1280 return tools::Rectangle();
1283 const tools::PolyPolygon
vcl::Region::GetAsPolyPolygon() const
1285 if(getPolyPolygon())
1287 return *getPolyPolygon();
1290 if(getB2DPolyPolygon())
1292 // the polygon needs to be converted, buffer the down converion
1293 const tools::PolyPolygon
aPolyPolgon(*getB2DPolyPolygon());
1294 const_cast< vcl::Region
* >(this)->mpPolyPolygon
.reset(new tools::PolyPolygon(aPolyPolgon
));
1296 return *getPolyPolygon();
1301 // the BandRegion needs to be converted, buffer the converion
1302 const tools::PolyPolygon
aPolyPolgon(ImplCreatePolyPolygonFromRegionBand());
1303 const_cast< vcl::Region
* >(this)->mpPolyPolygon
.reset(new tools::PolyPolygon(aPolyPolgon
));
1305 return *getPolyPolygon();
1308 return tools::PolyPolygon();
1311 const basegfx::B2DPolyPolygon
vcl::Region::GetAsB2DPolyPolygon() const
1313 if(getB2DPolyPolygon())
1315 return *getB2DPolyPolygon();
1318 if(getPolyPolygon())
1320 // the polygon needs to be converted, buffer the up conversion. This will be preferred from now.
1321 const basegfx::B2DPolyPolygon
aB2DPolyPolygon(getPolyPolygon()->getB2DPolyPolygon());
1322 const_cast< vcl::Region
* >(this)->mpB2DPolyPolygon
.reset(new basegfx::B2DPolyPolygon(aB2DPolyPolygon
));
1324 return *getB2DPolyPolygon();
1329 // the BandRegion needs to be converted, buffer the converion
1330 const basegfx::B2DPolyPolygon
aB2DPolyPolygon(ImplCreateB2DPolyPolygonFromRegionBand());
1331 const_cast< vcl::Region
* >(this)->mpB2DPolyPolygon
.reset(new basegfx::B2DPolyPolygon(aB2DPolyPolygon
));
1333 return *getB2DPolyPolygon();
1336 return basegfx::B2DPolyPolygon();
1339 const RegionBand
* vcl::Region::GetAsRegionBand() const
1341 if(!getRegionBand())
1343 if(getB2DPolyPolygon())
1345 // convert B2DPolyPolygon to RegionBand, buffer it and return it
1346 const_cast< vcl::Region
* >(this)->mpRegionBand
= ImplCreateRegionBandFromPolyPolygon(tools::PolyPolygon(*getB2DPolyPolygon()));
1348 else if(getPolyPolygon())
1350 // convert B2DPolyPolygon to RegionBand, buffer it and return it
1351 const_cast< vcl::Region
* >(this)->mpRegionBand
= ImplCreateRegionBandFromPolyPolygon(*getPolyPolygon());
1355 return getRegionBand();
1358 bool vcl::Region::IsInside( const Point
& rPoint
) const
1362 // no point can be in empty region
1368 // all points are inside null-region
1372 // Too expensive (?)
1373 //if(mpImplRegion->getRegionPolyPoly())
1375 // return mpImplRegion->getRegionPolyPoly()->IsInside( rPoint );
1378 // ensure RegionBand existence
1379 const RegionBand
* pRegionBand
= GetAsRegionBand();
1383 return pRegionBand
->IsInside(rPoint
);
1389 bool vcl::Region::IsOver( const tools::Rectangle
& rRect
) const
1393 // nothing can be over something empty
1399 // everything is over null region
1403 // Can we optimize this ??? - is used in StarDraw for brushes pointers
1404 // Why we have no IsOver for Regions ???
1405 // create region from rectangle and intersect own region
1406 vcl::Region
aRegion(rRect
);
1407 aRegion
.Intersect( *this );
1409 // rectangle is over if include is not empty
1410 return !aRegion
.IsEmpty();
1413 bool vcl::Region::IsRectangle() const
1415 if( IsEmpty() || IsNull() )
1418 if( getB2DPolyPolygon() )
1419 return basegfx::tools::isRectangle( *getB2DPolyPolygon() );
1421 if( getPolyPolygon() )
1422 return getPolyPolygon()->IsRect();
1424 if( getRegionBand() )
1425 return (getRegionBand()->getRectangleCount() == 1);
1430 void vcl::Region::SetNull()
1432 // reset all content
1433 mpB2DPolyPolygon
.reset();
1434 mpPolyPolygon
.reset();
1435 mpRegionBand
.reset();
1439 void vcl::Region::SetEmpty()
1441 // reset all content
1442 mpB2DPolyPolygon
.reset();
1443 mpPolyPolygon
.reset();
1444 mpRegionBand
.reset();
1448 Region
& vcl::Region::operator=( const vcl::Region
& rRegion
)
1450 // reset all content
1451 mpB2DPolyPolygon
= rRegion
.mpB2DPolyPolygon
;
1452 mpPolyPolygon
= rRegion
.mpPolyPolygon
;
1453 mpRegionBand
= rRegion
.mpRegionBand
;
1454 mbIsNull
= rRegion
.mbIsNull
;
1459 Region
& vcl::Region::operator=( vcl::Region
&& rRegion
)
1461 mpB2DPolyPolygon
= std::move(rRegion
.mpB2DPolyPolygon
);
1462 mpPolyPolygon
= std::move(rRegion
.mpPolyPolygon
);
1463 mpRegionBand
= std::move(rRegion
.mpRegionBand
);
1464 mbIsNull
= rRegion
.mbIsNull
;
1465 rRegion
.mbIsNull
= true;
1470 Region
& vcl::Region::operator=( const tools::Rectangle
& rRect
)
1472 mpB2DPolyPolygon
.reset();
1473 mpPolyPolygon
.reset();
1474 mpRegionBand
.reset(rRect
.IsEmpty() ? nullptr : new RegionBand(rRect
));
1480 bool vcl::Region::operator==( const vcl::Region
& rRegion
) const
1482 if(IsNull() && rRegion
.IsNull())
1484 // both are null region
1488 if(IsEmpty() && rRegion
.IsEmpty())
1494 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion
.getB2DPolyPolygon())
1496 // same instance data? -> equal
1500 if(getPolyPolygon() && getPolyPolygon() == rRegion
.getPolyPolygon())
1502 // same instance data? -> equal
1506 if(getRegionBand() && getRegionBand() == rRegion
.getRegionBand())
1508 // same instance data? -> equal
1512 if(IsNull() || IsEmpty())
1517 if(rRegion
.IsNull() || rRegion
.IsEmpty())
1522 if(rRegion
.getB2DPolyPolygon() || getB2DPolyPolygon())
1524 // one of both has a B2DPolyPolygon based region, ensure both have it
1525 // by evtl. conversion
1526 GetAsB2DPolyPolygon();
1527 rRegion
.GetAsB2DPolyPolygon();
1529 return *rRegion
.getB2DPolyPolygon() == *getB2DPolyPolygon();
1532 if(rRegion
.getPolyPolygon() || getPolyPolygon())
1534 // one of both has a B2DPolyPolygon based region, ensure both have it
1535 // by evtl. conversion
1537 rRegion
.GetAsPolyPolygon();
1539 return *rRegion
.getPolyPolygon() == *getPolyPolygon();
1542 // both are not empty or null (see above) and if content supported polygon
1543 // data the comparison is already done. Only both on RegionBand base can be left,
1545 if(rRegion
.getRegionBand() && getRegionBand())
1547 return *rRegion
.getRegionBand() == *getRegionBand();
1550 // should not happen, but better deny equality
1554 SvStream
& ReadRegion(SvStream
& rIStrm
, vcl::Region
& rRegion
)
1556 VersionCompat
aCompat(rIStrm
, StreamMode::READ
);
1557 sal_uInt16
nVersion(0);
1558 sal_uInt16
nTmp16(0);
1560 // clear region to be loaded
1563 // get version of streamed region
1564 rIStrm
.ReadUInt16( nVersion
);
1566 // get type of region
1567 rIStrm
.ReadUInt16( nTmp16
);
1569 enum RegionType
{ REGION_NULL
, REGION_EMPTY
, REGION_RECTANGLE
, REGION_COMPLEX
};
1570 RegionType meStreamedType
= (RegionType
)nTmp16
;
1572 switch(meStreamedType
)
1588 RegionBand
* pNewRegionBand
= new RegionBand();
1589 pNewRegionBand
->load(rIStrm
);
1590 rRegion
.mpRegionBand
.reset(pNewRegionBand
);
1592 if(aCompat
.GetVersion() >= 2)
1594 bool bHasPolyPolygon(false);
1596 rIStrm
.ReadCharAsBool( bHasPolyPolygon
);
1600 tools::PolyPolygon
* pNewPoly
= new tools::PolyPolygon();
1601 ReadPolyPolygon( rIStrm
, *pNewPoly
);
1602 rRegion
.mpPolyPolygon
.reset(pNewPoly
);
1613 SvStream
& WriteRegion( SvStream
& rOStrm
, const vcl::Region
& rRegion
)
1615 const sal_uInt16
nVersion(2);
1616 VersionCompat
aCompat(rOStrm
, StreamMode::WRITE
, nVersion
);
1619 rOStrm
.WriteUInt16( nVersion
);
1622 enum RegionType
{ REGION_NULL
, REGION_EMPTY
, REGION_RECTANGLE
, REGION_COMPLEX
};
1623 RegionType
aRegionType(REGION_COMPLEX
);
1624 bool bEmpty(rRegion
.IsEmpty());
1626 if(!bEmpty
&& rRegion
.getB2DPolyPolygon() && 0 == rRegion
.getB2DPolyPolygon()->count())
1628 OSL_ENSURE(false, "Region with empty B2DPolyPolygon, should not be created (!)");
1632 if(!bEmpty
&& rRegion
.getPolyPolygon() && 0 == rRegion
.getPolyPolygon()->Count())
1634 OSL_ENSURE(false, "Region with empty PolyPolygon, should not be created (!)");
1640 aRegionType
= REGION_EMPTY
;
1642 else if(rRegion
.IsNull())
1644 aRegionType
= REGION_NULL
;
1646 else if(rRegion
.getRegionBand() && rRegion
.getRegionBand()->isSingleRectangle())
1648 aRegionType
= REGION_RECTANGLE
;
1651 rOStrm
.WriteUInt16( aRegionType
);
1654 const RegionBand
* pRegionBand
= rRegion
.getRegionBand();
1658 pRegionBand
->save(rOStrm
);
1662 // for compatibility, write an empty RegionBand (will only write
1663 // the end marker STREAMENTRY_END, but this *is* needed)
1664 const RegionBand aRegionBand
;
1666 aRegionBand
.save(rOStrm
);
1669 // write polypolygon if available
1670 const bool bHasPolyPolygon(rRegion
.HasPolyPolygonOrB2DPolyPolygon());
1671 rOStrm
.WriteBool( bHasPolyPolygon
);
1676 tools::PolyPolygon aNoCurvePolyPolygon
;
1677 rRegion
.GetAsPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon
);
1679 WritePolyPolygon( rOStrm
, aNoCurvePolyPolygon
);
1685 void vcl::Region::GetRegionRectangles(RectangleVector
& rTarget
) const
1687 // clear returnvalues
1690 // ensure RegionBand existence
1691 const RegionBand
* pRegionBand
= GetAsRegionBand();
1695 pRegionBand
->GetRegionRectangles(rTarget
);
1699 static inline bool ImplPolygonRectTest( const tools::Polygon
& rPoly
, tools::Rectangle
* pRectOut
= nullptr )
1701 bool bIsRect
= false;
1702 const Point
* pPoints
= rPoly
.GetConstPointAry();
1703 sal_uInt16 nPoints
= rPoly
.GetSize();
1705 if( nPoints
== 4 || (nPoints
== 5 && pPoints
[0] == pPoints
[4]) )
1707 long nX1
= pPoints
[0].X(), nX2
= pPoints
[2].X(), nY1
= pPoints
[0].Y(), nY2
= pPoints
[2].Y();
1709 if( ( (pPoints
[1].X() == nX1
&& pPoints
[3].X() == nX2
) && (pPoints
[1].Y() == nY2
&& pPoints
[3].Y() == nY1
) )
1710 || ( (pPoints
[1].X() == nX2
&& pPoints
[3].X() == nX1
) && (pPoints
[1].Y() == nY1
&& pPoints
[3].Y() == nY2
) ) )
1742 pRectOut
->Left() = nX1
;
1743 pRectOut
->Right() = nX2
;
1744 pRectOut
->Top() = nY1
;
1745 pRectOut
->Bottom() = nY2
;
1753 vcl::Region
vcl::Region::GetRegionFromPolyPolygon( const tools::PolyPolygon
& rPolyPoly
)
1755 //return vcl::Region( rPolyPoly );
1757 // check if it's worth extracting the XOr'ing the Rectangles
1758 // empiricism shows that break even between XOr'ing rectangles separately
1759 // and ImplCreateRegionBandFromPolyPolygon is at half rectangles/half polygons
1760 int nPolygonRects
= 0, nPolygonPolygons
= 0;
1761 int nPolygons
= rPolyPoly
.Count();
1763 for( int i
= 0; i
< nPolygons
; i
++ )
1765 const tools::Polygon
& rPoly
= rPolyPoly
[i
];
1767 if( ImplPolygonRectTest( rPoly
) )
1777 if( nPolygonPolygons
> nPolygonRects
)
1779 return vcl::Region( rPolyPoly
);
1782 vcl::Region aResult
;
1783 tools::Rectangle aRect
;
1785 for( int i
= 0; i
< nPolygons
; i
++ )
1787 const tools::Polygon
& rPoly
= rPolyPoly
[i
];
1789 if( ImplPolygonRectTest( rPoly
, &aRect
) )
1791 aResult
.XOr( aRect
);
1795 aResult
.XOr( vcl::Region(rPoly
) );
1802 } /* namespace vcl */
1804 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */