bump product version to 6.4.0.3
[LibreOffice.git] / vcl / source / gdi / region.cxx
blob4f830a2195e88a7fb28d727b022236031fce8406
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 <memory>
21 #include <tools/vcompat.hxx>
22 #include <tools/stream.hxx>
23 #include <osl/diagnose.h>
24 #include <sal/log.hxx>
25 #include <vcl/canvastools.hxx>
26 #include <vcl/region.hxx>
27 #include <regionband.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>
35 #include <tools/poly.hxx>
37 namespace
39 /** Return <TRUE/> when the given polygon is rectilinear and oriented so that
40 all sides are either horizontal or vertical.
42 bool ImplIsPolygonRectilinear (const tools::PolyPolygon& rPolyPoly)
44 // Iterate over all polygons.
45 const sal_uInt16 nPolyCount = rPolyPoly.Count();
46 for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly)
48 const tools::Polygon& aPoly = rPolyPoly.GetObject(nPoly);
50 // Iterate over all edges of the current polygon.
51 const sal_uInt16 nSize = aPoly.GetSize();
53 if (nSize < 2)
54 continue;
55 Point aPoint (aPoly.GetPoint(0));
56 const Point aLastPoint (aPoint);
57 for (sal_uInt16 nPoint = 1; nPoint < nSize; ++nPoint)
59 const Point aNextPoint (aPoly.GetPoint(nPoint));
60 // When there is at least one edge that is neither vertical nor
61 // horizontal then the entire polygon is not rectilinear (and
62 // oriented along primary axes.)
63 if (aPoint.X() != aNextPoint.X() && aPoint.Y() != aNextPoint.Y())
64 return false;
66 aPoint = aNextPoint;
68 // Compare closing edge.
69 if (aLastPoint.X() != aPoint.X() && aLastPoint.Y() != aPoint.Y())
70 return false;
72 return true;
75 /** Convert a rectilinear polygon (that is oriented along the primary axes)
76 to a list of bands. For this special form of polygon we can use an
77 optimization that prevents the creation of one band per y value.
78 However, it still is possible that some temporary bands are created that
79 later can be optimized away.
80 @param rPolyPolygon
81 A set of zero, one, or more polygons, nested or not, that are
82 converted into a list of bands.
83 @return
84 A new RegionBand object is returned that contains the bands that
85 represent the given poly-polygon.
87 std::unique_ptr<RegionBand> ImplRectilinearPolygonToBands(const tools::PolyPolygon& rPolyPoly)
89 OSL_ASSERT(ImplIsPolygonRectilinear (rPolyPoly));
91 // Create a new RegionBand object as container of the bands.
92 std::unique_ptr<RegionBand> pRegionBand( std::make_unique<RegionBand>() );
93 long nLineId = 0;
95 // Iterate over all polygons.
96 const sal_uInt16 nPolyCount = rPolyPoly.Count();
97 for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly)
99 const tools::Polygon& aPoly = rPolyPoly.GetObject(nPoly);
101 // Iterate over all edges of the current polygon.
102 const sal_uInt16 nSize = aPoly.GetSize();
103 if (nSize < 2)
104 continue;
105 // Avoid fetching every point twice (each point is the start point
106 // of one and the end point of another edge.)
107 Point aStart (aPoly.GetPoint(0));
108 Point aEnd;
109 for (sal_uInt16 nPoint = 1; nPoint <= nSize; ++nPoint, aStart=aEnd)
111 // We take the implicit closing edge into account by mapping
112 // index nSize to 0.
113 aEnd = aPoly.GetPoint(nPoint%nSize);
114 if (aStart.Y() == aEnd.Y())
116 // Horizontal lines are ignored.
117 continue;
120 // At this point the line has to be vertical.
121 OSL_ASSERT(aStart.X() == aEnd.X());
123 // Sort y-coordinates to simplify the algorithm and store the
124 // direction separately. The direction is calculated as it is
125 // in other places (but seems to be the wrong way.)
126 const long nTop (::std::min(aStart.Y(), aEnd.Y()));
127 const long nBottom (::std::max(aStart.Y(), aEnd.Y()));
128 const LineType eLineType (aStart.Y() > aEnd.Y() ? LineType::Descending : LineType::Ascending);
130 // Make sure that the current line is covered by bands.
131 pRegionBand->ImplAddMissingBands(nTop,nBottom);
133 // Find top-most band that may contain nTop.
134 ImplRegionBand* pBand = pRegionBand->ImplGetFirstRegionBand();
135 while (pBand!=nullptr && pBand->mnYBottom < nTop)
136 pBand = pBand->mpNextBand;
137 ImplRegionBand* pTopBand = pBand;
138 // If necessary split the band at nTop so that nTop is contained
139 // in the lower band.
140 if (pBand!=nullptr
141 // Prevent the current band from becoming 0 pixel high
142 && pBand->mnYTop<nTop
143 // this allows the lowest pixel of the band to be split off
144 && pBand->mnYBottom>=nTop
145 // do not split a band that is just one pixel high
146 && pBand->mnYTop<pBand->mnYBottom-1)
148 // Split the top band.
149 pTopBand = pBand->SplitBand(nTop);
152 // Advance to band that may contain nBottom.
153 while (pBand!=nullptr && pBand->mnYBottom < nBottom)
154 pBand = pBand->mpNextBand;
155 // The lowest band may have to be split at nBottom so that
156 // nBottom itself remains in the upper band.
157 if (pBand!=nullptr
158 // allow the current band becoming 1 pixel high
159 && pBand->mnYTop<=nBottom
160 // prevent splitting off a band that is 0 pixel high
161 && pBand->mnYBottom>nBottom
162 // do not split a band that is just one pixel high
163 && pBand->mnYTop<pBand->mnYBottom-1)
165 // Split the bottom band.
166 pBand->SplitBand(nBottom+1);
169 // Note that we remember the top band (in pTopBand) but not the
170 // bottom band. The later can be determined by comparing y
171 // coordinates.
173 // Add the x-value as point to all bands in the nTop->nBottom range.
174 for (pBand=pTopBand; pBand!=nullptr&&pBand->mnYTop<=nBottom; pBand=pBand->mpNextBand)
175 pBand->InsertPoint(aStart.X(), nLineId++, true, eLineType);
179 return pRegionBand;
182 /** Convert a general polygon (one for which ImplIsPolygonRectilinear()
183 returns <FALSE/>) to bands.
185 std::unique_ptr<RegionBand> ImplGeneralPolygonToBands(const tools::PolyPolygon& rPolyPoly, const tools::Rectangle& rPolygonBoundingBox)
187 long nLineID = 0;
189 // initialisation and creation of Bands
190 std::unique_ptr<RegionBand> pRegionBand( std::make_unique<RegionBand>() );
191 pRegionBand->CreateBandRange(rPolygonBoundingBox.Top(), rPolygonBoundingBox.Bottom());
193 // insert polygons
194 const sal_uInt16 nPolyCount = rPolyPoly.Count();
196 for ( sal_uInt16 nPoly = 0; nPoly < nPolyCount; nPoly++ )
198 // get reference to current polygon
199 const tools::Polygon& aPoly = rPolyPoly.GetObject( nPoly );
200 const sal_uInt16 nSize = aPoly.GetSize();
202 // not enough points ( <= 2 )? -> nothing to do!
203 if ( nSize <= 2 )
204 continue;
206 // band the polygon
207 for ( sal_uInt16 nPoint = 1; nPoint < nSize; nPoint++ )
209 pRegionBand->InsertLine( aPoly.GetPoint(nPoint-1), aPoly.GetPoint(nPoint), nLineID++ );
212 // close polygon with line from first point to last point, if necessary
213 const Point rLastPoint = aPoly.GetPoint(nSize-1);
214 const Point rFirstPoint = aPoly.GetPoint(0);
216 if ( rLastPoint != rFirstPoint )
218 pRegionBand->InsertLine( rLastPoint, rFirstPoint, nLineID++ );
222 return pRegionBand;
224 } // end of anonymous namespace
226 namespace vcl {
228 bool vcl::Region::IsEmpty() const
230 return !mbIsNull && !mpB2DPolyPolygon.get() && !mpPolyPolygon.get() && !mpRegionBand.get();
234 static std::unique_ptr<RegionBand> ImplCreateRegionBandFromPolyPolygon(const tools::PolyPolygon& rPolyPolygon)
236 std::unique_ptr<RegionBand> pRetval;
238 if(rPolyPolygon.Count())
240 // ensure to subdivide when bezier segments are used, it's going to
241 // be expanded to rectangles
242 tools::PolyPolygon aPolyPolygon;
244 rPolyPolygon.AdaptiveSubdivide(aPolyPolygon);
246 if(aPolyPolygon.Count())
248 const tools::Rectangle aRect(aPolyPolygon.GetBoundRect());
250 if(!aRect.IsEmpty())
252 if(ImplIsPolygonRectilinear(aPolyPolygon))
254 // For rectilinear polygons there is an optimized band conversion.
255 pRetval = ImplRectilinearPolygonToBands(aPolyPolygon);
257 else
259 pRetval = ImplGeneralPolygonToBands(aPolyPolygon, aRect);
262 // Convert points into seps.
263 if(pRetval)
265 pRetval->processPoints();
267 // Optimize list of bands. Adjacent bands with identical lists
268 // of seps are joined.
269 if(!pRetval->OptimizeBandList())
271 pRetval.reset();
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 (auto const& rectangle : aRectangles)
292 aRetval.Insert( tools::Polygon(rectangle) );
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 tools::Rectangle& rRect)
319 : mpB2DPolyPolygon(),
320 mpPolyPolygon(),
321 mpRegionBand(),
322 mbIsNull(false)
324 mpRegionBand.reset(rRect.IsEmpty() ? nullptr : new RegionBand(rRect));
327 Region::Region(const tools::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&) = default;
368 Region::Region(vcl::Region&& rRegion) noexcept
369 : mpB2DPolyPolygon(std::move(rRegion.mpB2DPolyPolygon)),
370 mpPolyPolygon(std::move(rRegion.mpPolyPolygon)),
371 mpRegionBand(std::move(rRegion.mpRegionBand)),
372 mbIsNull(rRegion.mbIsNull)
374 rRegion.mbIsNull = true;
377 Region::~Region() = default;
379 void vcl::Region::ImplCreatePolyPolyRegion( const tools::PolyPolygon& rPolyPoly )
381 const sal_uInt16 nPolyCount = rPolyPoly.Count();
383 if(nPolyCount)
385 // polypolygon empty? -> empty region
386 const tools::Rectangle aRect(rPolyPoly.GetBoundRect());
388 if(!aRect.IsEmpty())
390 // width OR height == 1 ? => Rectangular region
391 if((1 == aRect.GetWidth()) || (1 == aRect.GetHeight()) || rPolyPoly.IsRect())
393 mpRegionBand.reset(new RegionBand(aRect));
395 else
397 mpPolyPolygon.reset(new tools::PolyPolygon(rPolyPoly));
400 mbIsNull = false;
405 void vcl::Region::ImplCreatePolyPolyRegion( const basegfx::B2DPolyPolygon& rPolyPoly )
407 if(rPolyPoly.count() && !rPolyPoly.getB2DRange().isEmpty())
409 mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(rPolyPoly));
410 mbIsNull = false;
414 void vcl::Region::Move( long nHorzMove, long nVertMove )
416 if(IsNull() || IsEmpty())
418 // empty or null need no move
419 return;
422 if(!nHorzMove && !nVertMove)
424 // no move defined
425 return;
428 if(getB2DPolyPolygon())
430 basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon());
432 aPoly.transform(basegfx::utils::createTranslateB2DHomMatrix(nHorzMove, nVertMove));
433 mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : nullptr);
434 mpPolyPolygon.reset();
435 mpRegionBand.reset();
437 else if(getPolyPolygon())
439 tools::PolyPolygon aPoly(*getPolyPolygon());
441 aPoly.Move(nHorzMove, nVertMove);
442 mpB2DPolyPolygon.reset();
443 mpPolyPolygon.reset(aPoly.Count() ? new tools::PolyPolygon(aPoly) : nullptr);
444 mpRegionBand.reset();
446 else if(getRegionBand())
448 RegionBand* pNew = new RegionBand(*getRegionBand());
450 pNew->Move(nHorzMove, nVertMove);
451 mpB2DPolyPolygon.reset();
452 mpPolyPolygon.reset();
453 mpRegionBand.reset(pNew);
455 else
457 OSL_ENSURE(false, "Region::Move error: impossible combination (!)");
461 void vcl::Region::Scale( double fScaleX, double fScaleY )
463 if(IsNull() || IsEmpty())
465 // empty or null need no scale
466 return;
469 if(basegfx::fTools::equalZero(fScaleX) && basegfx::fTools::equalZero(fScaleY))
471 // no scale defined
472 return;
475 if(getB2DPolyPolygon())
477 basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon());
479 aPoly.transform(basegfx::utils::createScaleB2DHomMatrix(fScaleX, fScaleY));
480 mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : nullptr);
481 mpPolyPolygon.reset();
482 mpRegionBand.reset();
484 else if(getPolyPolygon())
486 tools::PolyPolygon aPoly(*getPolyPolygon());
488 aPoly.Scale(fScaleX, fScaleY);
489 mpB2DPolyPolygon.reset();
490 mpPolyPolygon.reset(aPoly.Count() ? new tools::PolyPolygon(aPoly) : nullptr);
491 mpRegionBand.reset();
493 else if(getRegionBand())
495 RegionBand* pNew = new RegionBand(*getRegionBand());
497 pNew->Scale(fScaleX, fScaleY);
498 mpB2DPolyPolygon.reset();
499 mpPolyPolygon.reset();
500 mpRegionBand.reset(pNew);
502 else
504 OSL_ENSURE(false, "Region::Scale error: impossible combination (!)");
508 void vcl::Region::Union( const tools::Rectangle& rRect )
510 if(rRect.IsEmpty())
512 // empty rectangle will not expand the existing union, nothing to do
513 return;
516 if(IsEmpty())
518 // no local data, the union will be equal to source. Create using rectangle
519 *this = rRect;
520 return;
523 if(HasPolyPolygonOrB2DPolyPolygon())
525 // get this B2DPolyPolygon, solve on polygon base
526 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
528 aThisPolyPoly = basegfx::utils::prepareForPolygonOperation(aThisPolyPoly);
530 if(!aThisPolyPoly.count())
532 // no local polygon, use the rectangle as new region
533 *this = rRect;
535 else
537 // get the other B2DPolyPolygon and use logical Or-Operation
538 const basegfx::B2DPolygon aRectPoly(
539 basegfx::utils::createPolygonFromRect(
540 vcl::unotools::b2DRectangleFromRectangle(rRect)));
541 const basegfx::B2DPolyPolygon aClip(
542 basegfx::utils::solvePolygonOperationOr(
543 aThisPolyPoly,
544 basegfx::B2DPolyPolygon(aRectPoly)));
545 *this = vcl::Region(aClip);
548 return;
551 // only region band mode possibility left here or null/empty
552 const RegionBand* pCurrent = getRegionBand();
554 if(!pCurrent)
556 // no region band, create using the rectangle
557 *this = rRect;
558 return;
561 std::unique_ptr<RegionBand> pNew( std::make_unique<RegionBand>(*pCurrent));
563 // get justified rectangle
564 const long nLeft(std::min(rRect.Left(), rRect.Right()));
565 const long nTop(std::min(rRect.Top(), rRect.Bottom()));
566 const long nRight(std::max(rRect.Left(), rRect.Right()));
567 const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
569 // insert bands if the boundaries are not already in the list
570 pNew->InsertBands(nTop, nBottom);
572 // process union
573 pNew->Union(nLeft, nTop, nRight, nBottom);
575 // cleanup
576 if(!pNew->OptimizeBandList())
578 pNew.reset();
581 mpRegionBand = std::move(pNew);
584 void vcl::Region::Intersect( const tools::Rectangle& rRect )
586 if ( rRect.IsEmpty() )
588 // empty rectangle will create empty region
589 SetEmpty();
590 return;
593 if(IsNull())
595 // null region (everything) intersect with rect will give rect
596 *this = rRect;
597 return;
600 if(IsEmpty())
602 // no content, cannot get more empty
603 return;
606 if(HasPolyPolygonOrB2DPolyPolygon())
608 // if polygon data prefer double precision, the other will be lost (if buffered)
609 if(getB2DPolyPolygon())
611 const basegfx::B2DPolyPolygon aPoly(
612 basegfx::utils::clipPolyPolygonOnRange(
613 *getB2DPolyPolygon(),
614 basegfx::B2DRange(
615 rRect.Left(),
616 rRect.Top(),
617 rRect.Right() + 1,
618 rRect.Bottom() + 1),
619 true,
620 false));
622 mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : nullptr);
623 mpPolyPolygon.reset();
624 mpRegionBand.reset();
626 else // if(getPolyPolygon())
628 tools::PolyPolygon aPoly(*getPolyPolygon());
630 // use the PolyPolygon::Clip method for rectangles, this is
631 // fairly simple (does not even use GPC) and saves us from
632 // unnecessary banding
633 aPoly.Clip(rRect);
635 mpB2DPolyPolygon.reset();
636 mpPolyPolygon.reset(aPoly.Count() ? new tools::PolyPolygon(aPoly) : nullptr);
637 mpRegionBand.reset();
640 return;
643 // only region band mode possibility left here or null/empty
644 const RegionBand* pCurrent = getRegionBand();
646 if(!pCurrent)
648 // region is empty -> nothing to do!
649 return;
652 std::unique_ptr<RegionBand> pNew( std::make_unique<RegionBand>(*pCurrent));
654 // get justified rectangle
655 const long nLeft(std::min(rRect.Left(), rRect.Right()));
656 const long nTop(std::min(rRect.Top(), rRect.Bottom()));
657 const long nRight(std::max(rRect.Left(), rRect.Right()));
658 const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
660 // insert bands if the boundaries are not already in the list
661 pNew->InsertBands(nTop, nBottom);
663 // process intersect
664 pNew->Intersect(nLeft, nTop, nRight, nBottom);
666 // cleanup
667 if(!pNew->OptimizeBandList())
669 pNew.reset();
672 mpRegionBand = std::move(pNew);
675 void vcl::Region::Exclude( const tools::Rectangle& rRect )
677 if ( rRect.IsEmpty() )
679 // excluding nothing will do no change
680 return;
683 if(IsEmpty())
685 // cannot exclude from empty, done
686 return;
689 if(IsNull())
691 // error; cannot exclude from null region since this is not representable
692 // in the data
693 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
694 return;
697 if( HasPolyPolygonOrB2DPolyPolygon() )
699 // get this B2DPolyPolygon
700 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
702 aThisPolyPoly = basegfx::utils::prepareForPolygonOperation(aThisPolyPoly);
704 if(!aThisPolyPoly.count())
706 // when local polygon is empty, nothing can be excluded
707 return;
710 // get the other B2DPolyPolygon
711 const basegfx::B2DPolygon aRectPoly(
712 basegfx::utils::createPolygonFromRect(
713 vcl::unotools::b2DRectangleFromRectangle(rRect)));
714 const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly);
715 const basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationDiff(aThisPolyPoly, aOtherPolyPoly);
717 *this = vcl::Region(aClip);
719 return;
722 // only region band mode possibility left here or null/empty
723 const RegionBand* pCurrent = getRegionBand();
725 if(!pCurrent)
727 // empty? -> done!
728 return;
731 std::unique_ptr<RegionBand> pNew( std::make_unique<RegionBand>(*pCurrent));
733 // get justified rectangle
734 const long nLeft(std::min(rRect.Left(), rRect.Right()));
735 const long nTop(std::min(rRect.Top(), rRect.Bottom()));
736 const long nRight(std::max(rRect.Left(), rRect.Right()));
737 const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
739 // insert bands if the boundaries are not already in the list
740 pNew->InsertBands(nTop, nBottom);
742 // process exclude
743 pNew->Exclude(nLeft, nTop, nRight, nBottom);
745 // cleanup
746 if(!pNew->OptimizeBandList())
748 pNew.reset();
751 mpRegionBand = std::move(pNew);
754 void vcl::Region::XOr( const tools::Rectangle& rRect )
756 if ( rRect.IsEmpty() )
758 // empty rectangle will not change local content
759 return;
762 if(IsEmpty())
764 // rRect will be the xored-form (local off, rect on)
765 *this = rRect;
766 return;
769 if(IsNull())
771 // error; cannot exclude from null region since this is not representable
772 // in the data
773 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
774 return;
777 if( HasPolyPolygonOrB2DPolyPolygon() )
779 // get this B2DPolyPolygon
780 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
782 aThisPolyPoly = basegfx::utils::prepareForPolygonOperation( aThisPolyPoly );
784 if(!aThisPolyPoly.count())
786 // no local content, XOr will be equal to rectangle
787 *this = rRect;
788 return;
791 // get the other B2DPolyPolygon
792 const basegfx::B2DPolygon aRectPoly(
793 basegfx::utils::createPolygonFromRect(
794 vcl::unotools::b2DRectangleFromRectangle(rRect)));
795 const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly);
796 const basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationXor(aThisPolyPoly, aOtherPolyPoly);
798 *this = vcl::Region(aClip);
800 return;
803 // only region band mode possibility left here or null/empty
804 const RegionBand* pCurrent = getRegionBand();
806 if(!pCurrent)
808 // rRect will be the xored-form (local off, rect on)
809 *this = rRect;
810 return;
813 // only region band mode possibility left here or null/empty
814 std::unique_ptr<RegionBand> pNew( std::make_unique<RegionBand>(*getRegionBand()));
816 // get justified rectangle
817 const long nLeft(std::min(rRect.Left(), rRect.Right()));
818 const long nTop(std::min(rRect.Top(), rRect.Bottom()));
819 const long nRight(std::max(rRect.Left(), rRect.Right()));
820 const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
822 // insert bands if the boundaries are not already in the list
823 pNew->InsertBands(nTop, nBottom);
825 // process xor
826 pNew->XOr(nLeft, nTop, nRight, nBottom);
828 // cleanup
829 if(!pNew->OptimizeBandList())
831 pNew.reset();
834 mpRegionBand = std::move(pNew);
837 void vcl::Region::Union( const vcl::Region& rRegion )
839 if(rRegion.IsEmpty())
841 // no extension at all
842 return;
845 if(rRegion.IsNull())
847 // extending with null region -> null region
848 *this = vcl::Region(true);
849 return;
852 if(IsEmpty())
854 // local is empty, union will give source region
855 *this = rRegion;
856 return;
859 if(IsNull())
861 // already fully expanded (is null region), cannot be extended
862 return;
865 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
867 // get this B2DPolyPolygon
868 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
870 aThisPolyPoly = basegfx::utils::prepareForPolygonOperation(aThisPolyPoly);
872 if(!aThisPolyPoly.count())
874 // when no local content, union will be equal to rRegion
875 *this = rRegion;
876 return;
879 // get the other B2DPolyPolygon
880 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
881 aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation(aOtherPolyPoly);
883 // use logical OR operation
884 basegfx::B2DPolyPolygon aClip(basegfx::utils::solvePolygonOperationOr(aThisPolyPoly, aOtherPolyPoly));
886 *this = vcl::Region( aClip );
887 return;
890 // only region band mode possibility left here or null/empty
891 const RegionBand* pCurrent = getRegionBand();
893 if(!pCurrent)
895 // local is empty, union will give source region
896 *this = rRegion;
897 return;
900 const RegionBand* pSource = rRegion.getRegionBand();
902 if(!pSource)
904 // no extension at all
905 return;
908 // prepare source and target
909 std::unique_ptr<RegionBand> pNew( std::make_unique<RegionBand>(*pCurrent));
911 // union with source
912 pNew->Union(*pSource);
914 // cleanup
915 if(!pNew->OptimizeBandList())
917 pNew.reset();
920 mpRegionBand = std::move(pNew);
923 void vcl::Region::Intersect( const vcl::Region& rRegion )
925 // same instance data? -> nothing to do!
926 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
928 return;
931 if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
933 return;
936 if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
938 return;
941 if(rRegion.IsNull())
943 // source region is null-region, intersect will not change local region
944 return;
947 if(IsNull())
949 // when local region is null-region, intersect will be equal to source
950 *this = rRegion;
951 return;
954 if(rRegion.IsEmpty())
956 // source region is empty, intersection will always be empty
957 SetEmpty();
958 return;
961 if(IsEmpty())
963 // local region is empty, cannot get more empty than that. Nothing to do
964 return;
967 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
969 // get this B2DPolyPolygon
970 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
972 if(!aThisPolyPoly.count())
974 // local region is empty, cannot get more empty than that. Nothing to do
975 return;
978 // get the other B2DPolyPolygon
979 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
981 if(!aOtherPolyPoly.count())
983 // source region is empty, intersection will always be empty
984 SetEmpty();
985 return;
988 const basegfx::B2DPolyPolygon aClip(
989 basegfx::utils::clipPolyPolygonOnPolyPolygon(
990 aOtherPolyPoly,
991 aThisPolyPoly,
992 true,
993 false));
994 *this = vcl::Region( aClip );
995 return;
998 // only region band mode possibility left here or null/empty
999 const RegionBand* pCurrent = getRegionBand();
1001 if(!pCurrent)
1003 // local region is empty, cannot get more empty than that. Nothing to do
1004 return;
1007 const RegionBand* pSource = rRegion.getRegionBand();
1009 if(!pSource)
1011 // source region is empty, intersection will always be empty
1012 SetEmpty();
1013 return;
1016 // both RegionBands exist and are not empty
1017 if(pCurrent->getRectangleCount() + 2 < pSource->getRectangleCount())
1019 // when we have less rectangles, turn around the call
1020 vcl::Region aTempRegion = rRegion;
1021 aTempRegion.Intersect( *this );
1022 *this = aTempRegion;
1024 else
1026 // prepare new regionBand
1027 std::unique_ptr<RegionBand> pNew( std::make_unique<RegionBand>(*pCurrent));
1029 // intersect with source
1030 pNew->Intersect(*pSource);
1032 // cleanup
1033 if(!pNew->OptimizeBandList())
1035 pNew.reset();
1038 mpRegionBand = std::move(pNew);
1042 void vcl::Region::Exclude( const vcl::Region& rRegion )
1044 if ( rRegion.IsEmpty() )
1046 // excluding nothing will do no change
1047 return;
1050 if ( rRegion.IsNull() )
1052 // excluding everything will create empty region
1053 SetEmpty();
1054 return;
1057 if(IsEmpty())
1059 // cannot exclude from empty, done
1060 return;
1063 if(IsNull())
1065 // error; cannot exclude from null region since this is not representable
1066 // in the data
1067 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
1068 return;
1071 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1073 // get this B2DPolyPolygon
1074 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
1076 if(!aThisPolyPoly.count())
1078 // cannot exclude from empty, done
1079 return;
1082 aThisPolyPoly = basegfx::utils::prepareForPolygonOperation( aThisPolyPoly );
1084 // get the other B2DPolyPolygon
1085 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
1086 aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation( aOtherPolyPoly );
1088 basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationDiff( aThisPolyPoly, aOtherPolyPoly );
1089 *this = vcl::Region( aClip );
1090 return;
1093 // only region band mode possibility left here or null/empty
1094 const RegionBand* pCurrent = getRegionBand();
1096 if(!pCurrent)
1098 // cannot exclude from empty, done
1099 return;
1102 const RegionBand* pSource = rRegion.getRegionBand();
1104 if(!pSource)
1106 // excluding nothing will do no change
1107 return;
1110 // prepare source and target
1111 std::unique_ptr<RegionBand> pNew( std::make_unique<RegionBand>(*pCurrent));
1113 // union with source
1114 const bool bSuccess(pNew->Exclude(*pSource));
1116 // cleanup
1117 if(!bSuccess)
1119 pNew.reset();
1122 mpRegionBand = std::move(pNew);
1125 bool vcl::Region::XOr( const vcl::Region& rRegion )
1127 if ( rRegion.IsEmpty() )
1129 // empty region will not change local content
1130 return true;
1133 if ( rRegion.IsNull() )
1135 // error; cannot exclude null region from local since this is not representable
1136 // in the data
1137 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1138 return true;
1141 if(IsEmpty())
1143 // rRect will be the xored-form (local off, rect on)
1144 *this = rRegion;
1145 return true;
1148 if(IsNull())
1150 // error: cannot exclude from null region since this is not representable
1151 // in the data
1152 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1153 return false;
1156 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1158 // get this B2DPolyPolygon
1159 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
1161 if(!aThisPolyPoly.count())
1163 // rRect will be the xored-form (local off, rect on)
1164 *this = rRegion;
1165 return true;
1168 aThisPolyPoly = basegfx::utils::prepareForPolygonOperation( aThisPolyPoly );
1170 // get the other B2DPolyPolygon
1171 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
1172 aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation( aOtherPolyPoly );
1174 basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationXor( aThisPolyPoly, aOtherPolyPoly );
1175 *this = vcl::Region( aClip );
1176 return true;
1179 // only region band mode possibility left here or null/empty
1180 const RegionBand* pCurrent = getRegionBand();
1182 if(!pCurrent)
1184 // rRect will be the xored-form (local off, rect on)
1185 *this = rRegion;
1186 return true;
1189 const RegionBand* pSource = rRegion.getRegionBand();
1191 if(!pSource)
1193 // empty region will not change local content
1194 return true;
1197 // prepare source and target
1198 std::unique_ptr<RegionBand> pNew( std::make_unique<RegionBand>(*pCurrent));
1200 // union with source
1201 pNew->XOr(*pSource);
1203 // cleanup
1204 if(!pNew->OptimizeBandList())
1206 pNew.reset();
1209 mpRegionBand = std::move(pNew);
1211 return true;
1214 tools::Rectangle vcl::Region::GetBoundRect() const
1216 if(IsEmpty())
1218 // no internal data? -> region is empty!
1219 return tools::Rectangle();
1222 if(IsNull())
1224 // error; null region has no BoundRect
1225 // OSL_ENSURE(false, "Region::GetBoundRect error: null region has unlimited bound rect, not representable (!)");
1226 return tools::Rectangle();
1229 // prefer double precision source
1230 if(getB2DPolyPolygon())
1232 const basegfx::B2DRange aRange(basegfx::utils::getRange(*getB2DPolyPolygon()));
1234 if(aRange.isEmpty())
1236 // emulate PolyPolygon::GetBoundRect() when empty polygon
1237 return tools::Rectangle();
1239 else
1241 // #i122149# corrected rounding, no need for ceil() and floor() here
1242 return tools::Rectangle(
1243 basegfx::fround(aRange.getMinX()), basegfx::fround(aRange.getMinY()),
1244 basegfx::fround(aRange.getMaxX()), basegfx::fround(aRange.getMaxY()));
1248 if(getPolyPolygon())
1250 return getPolyPolygon()->GetBoundRect();
1253 if(getRegionBand())
1255 return getRegionBand()->GetBoundRect();
1258 return tools::Rectangle();
1261 tools::PolyPolygon vcl::Region::GetAsPolyPolygon() const
1263 if(getPolyPolygon())
1265 return *getPolyPolygon();
1268 if(getB2DPolyPolygon())
1270 // the polygon needs to be converted, buffer the down conversion
1271 const tools::PolyPolygon aPolyPolgon(*getB2DPolyPolygon());
1272 const_cast< vcl::Region* >(this)->mpPolyPolygon.reset(new tools::PolyPolygon(aPolyPolgon));
1274 return *getPolyPolygon();
1277 if(getRegionBand())
1279 // the BandRegion needs to be converted, buffer the conversion
1280 const tools::PolyPolygon aPolyPolgon(ImplCreatePolyPolygonFromRegionBand());
1281 const_cast< vcl::Region* >(this)->mpPolyPolygon.reset(new tools::PolyPolygon(aPolyPolgon));
1283 return *getPolyPolygon();
1286 return tools::PolyPolygon();
1289 basegfx::B2DPolyPolygon vcl::Region::GetAsB2DPolyPolygon() const
1291 if(getB2DPolyPolygon())
1293 return *getB2DPolyPolygon();
1296 if(getPolyPolygon())
1298 // the polygon needs to be converted, buffer the up conversion. This will be preferred from now.
1299 const basegfx::B2DPolyPolygon aB2DPolyPolygon(getPolyPolygon()->getB2DPolyPolygon());
1300 const_cast< vcl::Region* >(this)->mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(aB2DPolyPolygon));
1302 return *getB2DPolyPolygon();
1305 if(getRegionBand())
1307 // the BandRegion needs to be converted, buffer the conversion
1308 const basegfx::B2DPolyPolygon aB2DPolyPolygon(ImplCreateB2DPolyPolygonFromRegionBand());
1309 const_cast< vcl::Region* >(this)->mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(aB2DPolyPolygon));
1311 return *getB2DPolyPolygon();
1314 return basegfx::B2DPolyPolygon();
1317 const RegionBand* vcl::Region::GetAsRegionBand() const
1319 if(!getRegionBand())
1321 if(getB2DPolyPolygon())
1323 // convert B2DPolyPolygon to RegionBand, buffer it and return it
1324 const_cast< vcl::Region* >(this)->mpRegionBand = ImplCreateRegionBandFromPolyPolygon(tools::PolyPolygon(*getB2DPolyPolygon()));
1326 else if(getPolyPolygon())
1328 // convert B2DPolyPolygon to RegionBand, buffer it and return it
1329 const_cast< vcl::Region* >(this)->mpRegionBand = ImplCreateRegionBandFromPolyPolygon(*getPolyPolygon());
1333 return getRegionBand();
1336 bool vcl::Region::IsInside( const Point& rPoint ) const
1338 if(IsEmpty())
1340 // no point can be in empty region
1341 return false;
1344 if(IsNull())
1346 // all points are inside null-region
1347 return true;
1350 // Too expensive (?)
1351 //if(mpImplRegion->getRegionPolyPoly())
1353 // return mpImplRegion->getRegionPolyPoly()->IsInside( rPoint );
1356 // ensure RegionBand existence
1357 const RegionBand* pRegionBand = GetAsRegionBand();
1359 if(pRegionBand)
1361 return pRegionBand->IsInside(rPoint);
1364 return false;
1367 bool vcl::Region::IsOver( const tools::Rectangle& rRect ) const
1369 if(IsEmpty())
1371 // nothing can be over something empty
1372 return false;
1375 if(IsNull())
1377 // everything is over null region
1378 return true;
1381 // Can we optimize this ??? - is used in StarDraw for brushes pointers
1382 // Why we have no IsOver for Regions ???
1383 // create region from rectangle and intersect own region
1384 vcl::Region aRegion(rRect);
1385 aRegion.Intersect( *this );
1387 // rectangle is over if include is not empty
1388 return !aRegion.IsEmpty();
1391 bool vcl::Region::IsRectangle() const
1393 if( IsEmpty() || IsNull() )
1394 return false;
1396 if( getB2DPolyPolygon() )
1397 return basegfx::utils::isRectangle( *getB2DPolyPolygon() );
1399 if( getPolyPolygon() )
1400 return getPolyPolygon()->IsRect();
1402 if( getRegionBand() )
1403 return (getRegionBand()->getRectangleCount() == 1);
1405 return false;
1408 void vcl::Region::SetNull()
1410 // reset all content
1411 mpB2DPolyPolygon.reset();
1412 mpPolyPolygon.reset();
1413 mpRegionBand.reset();
1414 mbIsNull = true;
1417 void vcl::Region::SetEmpty()
1419 // reset all content
1420 mpB2DPolyPolygon.reset();
1421 mpPolyPolygon.reset();
1422 mpRegionBand.reset();
1423 mbIsNull = false;
1426 Region& vcl::Region::operator=( const vcl::Region& ) = default;
1428 Region& vcl::Region::operator=( vcl::Region&& rRegion ) noexcept
1430 mpB2DPolyPolygon = std::move(rRegion.mpB2DPolyPolygon);
1431 mpPolyPolygon = std::move(rRegion.mpPolyPolygon);
1432 mpRegionBand = std::move(rRegion.mpRegionBand);
1433 mbIsNull = rRegion.mbIsNull;
1434 rRegion.mbIsNull = true;
1436 return *this;
1439 Region& vcl::Region::operator=( const tools::Rectangle& rRect )
1441 mpB2DPolyPolygon.reset();
1442 mpPolyPolygon.reset();
1443 mpRegionBand.reset(rRect.IsEmpty() ? nullptr : new RegionBand(rRect));
1444 mbIsNull = false;
1446 return *this;
1449 bool vcl::Region::operator==( const vcl::Region& rRegion ) const
1451 if(IsNull() && rRegion.IsNull())
1453 // both are null region
1454 return true;
1457 if(IsEmpty() && rRegion.IsEmpty())
1459 // both are empty
1460 return true;
1463 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
1465 // same instance data? -> equal
1466 return true;
1469 if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
1471 // same instance data? -> equal
1472 return true;
1475 if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
1477 // same instance data? -> equal
1478 return true;
1481 if(IsNull() || IsEmpty())
1483 return false;
1486 if(rRegion.IsNull() || rRegion.IsEmpty())
1488 return false;
1491 if(rRegion.getB2DPolyPolygon() || getB2DPolyPolygon())
1493 // one of both has a B2DPolyPolygon based region, ensure both have it
1494 // by evtl. conversion
1495 GetAsB2DPolyPolygon();
1496 rRegion.GetAsB2DPolyPolygon();
1498 return *rRegion.getB2DPolyPolygon() == *getB2DPolyPolygon();
1501 if(rRegion.getPolyPolygon() || getPolyPolygon())
1503 // one of both has a B2DPolyPolygon based region, ensure both have it
1504 // by evtl. conversion
1505 GetAsPolyPolygon();
1506 rRegion.GetAsPolyPolygon();
1508 return *rRegion.getPolyPolygon() == *getPolyPolygon();
1511 // both are not empty or null (see above) and if content supported polygon
1512 // data the comparison is already done. Only both on RegionBand base can be left,
1513 // but better check
1514 if(rRegion.getRegionBand() && getRegionBand())
1516 return *rRegion.getRegionBand() == *getRegionBand();
1519 // should not happen, but better deny equality
1520 return false;
1523 SvStream& ReadRegion(SvStream& rIStrm, vcl::Region& rRegion)
1525 VersionCompat aCompat(rIStrm, StreamMode::READ);
1526 sal_uInt16 nVersion(0);
1527 sal_uInt16 nTmp16(0);
1529 // clear region to be loaded
1530 rRegion.SetEmpty();
1532 // get version of streamed region
1533 rIStrm.ReadUInt16( nVersion );
1535 // get type of region
1536 rIStrm.ReadUInt16( nTmp16 );
1538 enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX };
1539 RegionType meStreamedType = static_cast<RegionType>(nTmp16);
1541 switch(meStreamedType)
1543 case REGION_NULL:
1545 rRegion.SetNull();
1546 break;
1549 case REGION_EMPTY:
1551 rRegion.SetEmpty();
1552 break;
1555 default:
1557 RegionBand* pNewRegionBand = new RegionBand();
1558 bool bSuccess = pNewRegionBand->load(rIStrm);
1559 rRegion.mpRegionBand.reset(pNewRegionBand);
1561 bool bHasPolyPolygon(false);
1562 if (aCompat.GetVersion() >= 2)
1564 rIStrm.ReadCharAsBool( bHasPolyPolygon );
1566 if (bHasPolyPolygon)
1568 tools::PolyPolygon* pNewPoly = new tools::PolyPolygon();
1569 ReadPolyPolygon( rIStrm, *pNewPoly );
1570 rRegion.mpPolyPolygon.reset(pNewPoly);
1574 if (!bSuccess && !bHasPolyPolygon)
1576 SAL_WARN("vcl.gdi", "bad region band:" << bHasPolyPolygon);
1577 rRegion.SetNull();
1580 break;
1584 return rIStrm;
1587 SvStream& WriteRegion( SvStream& rOStrm, const vcl::Region& rRegion )
1589 const sal_uInt16 nVersion(2);
1590 VersionCompat aCompat(rOStrm, StreamMode::WRITE, nVersion);
1592 // put version
1593 rOStrm.WriteUInt16( nVersion );
1595 // put type
1596 enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX };
1597 RegionType aRegionType(REGION_COMPLEX);
1598 bool bEmpty(rRegion.IsEmpty());
1600 if(!bEmpty && rRegion.getB2DPolyPolygon() && 0 == rRegion.getB2DPolyPolygon()->count())
1602 OSL_ENSURE(false, "Region with empty B2DPolyPolygon, should not be created (!)");
1603 bEmpty = true;
1606 if(!bEmpty && rRegion.getPolyPolygon() && 0 == rRegion.getPolyPolygon()->Count())
1608 OSL_ENSURE(false, "Region with empty PolyPolygon, should not be created (!)");
1609 bEmpty = true;
1612 if(bEmpty)
1614 aRegionType = REGION_EMPTY;
1616 else if(rRegion.IsNull())
1618 aRegionType = REGION_NULL;
1620 else if(rRegion.getRegionBand() && rRegion.getRegionBand()->isSingleRectangle())
1622 aRegionType = REGION_RECTANGLE;
1625 rOStrm.WriteUInt16( aRegionType );
1627 // get RegionBand
1628 const RegionBand* pRegionBand = rRegion.getRegionBand();
1630 if(pRegionBand)
1632 pRegionBand->save(rOStrm);
1634 else
1636 // for compatibility, write an empty RegionBand (will only write
1637 // the end marker STREAMENTRY_END, but this *is* needed)
1638 const RegionBand aRegionBand;
1640 aRegionBand.save(rOStrm);
1643 // write polypolygon if available
1644 const bool bHasPolyPolygon(rRegion.HasPolyPolygonOrB2DPolyPolygon());
1645 rOStrm.WriteBool( bHasPolyPolygon );
1647 if(bHasPolyPolygon)
1649 // #i105373#
1650 tools::PolyPolygon aNoCurvePolyPolygon;
1651 rRegion.GetAsPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon);
1653 WritePolyPolygon( rOStrm, aNoCurvePolyPolygon );
1656 return rOStrm;
1659 void vcl::Region::GetRegionRectangles(RectangleVector& rTarget) const
1661 // clear returnvalues
1662 rTarget.clear();
1664 // ensure RegionBand existence
1665 const RegionBand* pRegionBand = GetAsRegionBand();
1667 if(pRegionBand)
1669 pRegionBand->GetRegionRectangles(rTarget);
1673 static bool ImplPolygonRectTest( const tools::Polygon& rPoly, tools::Rectangle* pRectOut = nullptr )
1675 bool bIsRect = false;
1676 const Point* pPoints = rPoly.GetConstPointAry();
1677 sal_uInt16 nPoints = rPoly.GetSize();
1679 if( nPoints == 4 || (nPoints == 5 && pPoints[0] == pPoints[4]) )
1681 long nX1 = pPoints[0].X(), nX2 = pPoints[2].X(), nY1 = pPoints[0].Y(), nY2 = pPoints[2].Y();
1683 if( ( (pPoints[1].X() == nX1 && pPoints[3].X() == nX2) && (pPoints[1].Y() == nY2 && pPoints[3].Y() == nY1) )
1684 || ( (pPoints[1].X() == nX2 && pPoints[3].X() == nX1) && (pPoints[1].Y() == nY1 && pPoints[3].Y() == nY2) ) )
1686 bIsRect = true;
1688 if( pRectOut )
1690 long nSwap;
1692 if( nX2 < nX1 )
1694 nSwap = nX2;
1695 nX2 = nX1;
1696 nX1 = nSwap;
1699 if( nY2 < nY1 )
1701 nSwap = nY2;
1702 nY2 = nY1;
1703 nY1 = nSwap;
1706 if( nX2 != nX1 )
1708 nX2--;
1711 if( nY2 != nY1 )
1713 nY2--;
1716 pRectOut->SetLeft( nX1 );
1717 pRectOut->SetRight( nX2 );
1718 pRectOut->SetTop( nY1 );
1719 pRectOut->SetBottom( nY2 );
1724 return bIsRect;
1727 vcl::Region vcl::Region::GetRegionFromPolyPolygon( const tools::PolyPolygon& rPolyPoly )
1729 //return vcl::Region( rPolyPoly );
1731 // check if it's worth extracting the XOr'ing the Rectangles
1732 // empiricism shows that break even between XOr'ing rectangles separately
1733 // and ImplCreateRegionBandFromPolyPolygon is at half rectangles/half polygons
1734 int nPolygonRects = 0, nPolygonPolygons = 0;
1735 int nPolygons = rPolyPoly.Count();
1737 for( int i = 0; i < nPolygons; i++ )
1739 const tools::Polygon& rPoly = rPolyPoly[i];
1741 if( ImplPolygonRectTest( rPoly ) )
1743 nPolygonRects++;
1745 else
1747 nPolygonPolygons++;
1751 if( nPolygonPolygons > nPolygonRects )
1753 return vcl::Region( rPolyPoly );
1756 vcl::Region aResult;
1757 tools::Rectangle aRect;
1759 for( int i = 0; i < nPolygons; i++ )
1761 const tools::Polygon& rPoly = rPolyPoly[i];
1763 if( ImplPolygonRectTest( rPoly, &aRect ) )
1765 aResult.XOr( aRect );
1767 else
1769 aResult.XOr( vcl::Region(rPoly) );
1773 return aResult;
1776 } /* namespace vcl */
1778 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */