Remove duplicated include
[LibreOffice.git] / vcl / source / gdi / region.cxx
blob6298e3052bee776309fd07a0ae5273c271e1387f
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>
36 #include <comphelper/configuration.hxx>
38 namespace
40 /** Return <TRUE/> when the given polygon is rectilinear and oriented so that
41 all sides are either horizontal or vertical.
43 bool ImplIsPolygonRectilinear (const tools::PolyPolygon& rPolyPoly)
45 // Iterate over all polygons.
46 const sal_uInt16 nPolyCount = rPolyPoly.Count();
47 for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly)
49 const tools::Polygon& aPoly = rPolyPoly.GetObject(nPoly);
51 // Iterate over all edges of the current polygon.
52 const sal_uInt16 nSize = aPoly.GetSize();
54 if (nSize < 2)
55 continue;
56 Point aPoint (aPoly.GetPoint(0));
57 const Point aLastPoint (aPoint);
58 for (sal_uInt16 nPoint = 1; nPoint < nSize; ++nPoint)
60 const Point aNextPoint (aPoly.GetPoint(nPoint));
61 // When there is at least one edge that is neither vertical nor
62 // horizontal then the entire polygon is not rectilinear (and
63 // oriented along primary axes.)
64 if (aPoint.X() != aNextPoint.X() && aPoint.Y() != aNextPoint.Y())
65 return false;
67 aPoint = aNextPoint;
69 // Compare closing edge.
70 if (aLastPoint.X() != aPoint.X() && aLastPoint.Y() != aPoint.Y())
71 return false;
73 return true;
76 /** Convert a rectilinear polygon (that is oriented along the primary axes)
77 to a list of bands. For this special form of polygon we can use an
78 optimization that prevents the creation of one band per y value.
79 However, it still is possible that some temporary bands are created that
80 later can be optimized away.
81 @param rPolyPolygon
82 A set of zero, one, or more polygons, nested or not, that are
83 converted into a list of bands.
84 @return
85 A new RegionBand object is returned that contains the bands that
86 represent the given poly-polygon.
88 std::shared_ptr<RegionBand> ImplRectilinearPolygonToBands(const tools::PolyPolygon& rPolyPoly)
90 OSL_ASSERT(ImplIsPolygonRectilinear (rPolyPoly));
92 // Create a new RegionBand object as container of the bands.
93 std::shared_ptr<RegionBand> pRegionBand( std::make_shared<RegionBand>() );
94 tools::Long nLineId = 0;
96 // Iterate over all polygons.
97 const sal_uInt16 nPolyCount = rPolyPoly.Count();
98 for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly)
100 const tools::Polygon& aPoly = rPolyPoly.GetObject(nPoly);
102 // Iterate over all edges of the current polygon.
103 const sal_uInt16 nSize = aPoly.GetSize();
104 if (nSize < 2)
105 continue;
106 // Avoid fetching every point twice (each point is the start point
107 // of one and the end point of another edge.)
108 Point aStart (aPoly.GetPoint(0));
109 Point aEnd;
110 for (sal_uInt16 nPoint = 1; nPoint <= nSize; ++nPoint, aStart=aEnd)
112 // We take the implicit closing edge into account by mapping
113 // index nSize to 0.
114 aEnd = aPoly.GetPoint(nPoint%nSize);
115 if (aStart.Y() == aEnd.Y())
117 // Horizontal lines are ignored.
118 continue;
121 // At this point the line has to be vertical.
122 OSL_ASSERT(aStart.X() == aEnd.X());
124 // Sort y-coordinates to simplify the algorithm and store the
125 // direction separately. The direction is calculated as it is
126 // in other places (but seems to be the wrong way.)
127 const tools::Long nTop (::std::min(aStart.Y(), aEnd.Y()));
128 const tools::Long nBottom (::std::max(aStart.Y(), aEnd.Y()));
129 const LineType eLineType (aStart.Y() > aEnd.Y() ? LineType::Descending : LineType::Ascending);
131 // Make sure that the current line is covered by bands.
132 pRegionBand->ImplAddMissingBands(nTop,nBottom);
134 // Find top-most band that may contain nTop.
135 ImplRegionBand* pBand = pRegionBand->ImplGetFirstRegionBand();
136 while (pBand!=nullptr && pBand->mnYBottom < nTop)
137 pBand = pBand->mpNextBand;
138 ImplRegionBand* pTopBand = pBand;
139 // If necessary split the band at nTop so that nTop is contained
140 // in the lower band.
141 if (pBand!=nullptr
142 // Prevent the current band from becoming 0 pixel high
143 && pBand->mnYTop<nTop
144 // this allows the lowest pixel of the band to be split off
145 && pBand->mnYBottom>=nTop
146 // do not split a band that is just one pixel high
147 && pBand->mnYTop<pBand->mnYBottom-1)
149 // Split the top band.
150 pTopBand = pBand->SplitBand(nTop);
153 // Advance to band that may contain nBottom.
154 while (pBand!=nullptr && pBand->mnYBottom < nBottom)
155 pBand = pBand->mpNextBand;
156 // The lowest band may have to be split at nBottom so that
157 // nBottom itself remains in the upper band.
158 if (pBand!=nullptr
159 // allow the current band becoming 1 pixel high
160 && pBand->mnYTop<=nBottom
161 // prevent splitting off a band that is 0 pixel high
162 && pBand->mnYBottom>nBottom
163 // do not split a band that is just one pixel high
164 && pBand->mnYTop<pBand->mnYBottom-1)
166 // Split the bottom band.
167 pBand->SplitBand(nBottom+1);
170 // Note that we remember the top band (in pTopBand) but not the
171 // bottom band. The later can be determined by comparing y
172 // coordinates.
174 // Add the x-value as point to all bands in the nTop->nBottom range.
175 for (pBand=pTopBand; pBand!=nullptr&&pBand->mnYTop<=nBottom; pBand=pBand->mpNextBand)
176 pBand->InsertPoint(aStart.X(), nLineId++, true, eLineType);
180 return pRegionBand;
183 /** Convert a general polygon (one for which ImplIsPolygonRectilinear()
184 returns <FALSE/>) to bands.
186 std::shared_ptr<RegionBand> ImplGeneralPolygonToBands(const tools::PolyPolygon& rPolyPoly, const tools::Rectangle& rPolygonBoundingBox)
188 tools::Long nLineID = 0;
190 // initialisation and creation of Bands
191 std::shared_ptr<RegionBand> pRegionBand( std::make_shared<RegionBand>() );
192 pRegionBand->CreateBandRange(rPolygonBoundingBox.Top(), rPolygonBoundingBox.Bottom());
194 // insert polygons
195 const sal_uInt16 nPolyCount = rPolyPoly.Count();
197 for ( sal_uInt16 nPoly = 0; nPoly < nPolyCount; nPoly++ )
199 // get reference to current polygon
200 const tools::Polygon& aPoly = rPolyPoly.GetObject( nPoly );
201 const sal_uInt16 nSize = aPoly.GetSize();
203 // not enough points ( <= 2 )? -> nothing to do!
204 if ( nSize <= 2 )
205 continue;
207 // band the polygon
208 for ( sal_uInt16 nPoint = 1; nPoint < nSize; nPoint++ )
210 pRegionBand->InsertLine( aPoly.GetPoint(nPoint-1), aPoly.GetPoint(nPoint), nLineID++ );
213 // close polygon with line from first point to last point, if necessary
214 const Point rLastPoint = aPoly.GetPoint(nSize-1);
215 const Point rFirstPoint = aPoly.GetPoint(0);
217 if ( rLastPoint != rFirstPoint )
219 pRegionBand->InsertLine( rLastPoint, rFirstPoint, nLineID++ );
223 return pRegionBand;
225 } // end of anonymous namespace
227 namespace vcl {
229 bool vcl::Region::IsEmpty() const
231 return !mbIsNull && !mpB2DPolyPolygon && !mpPolyPolygon && !mpRegionBand;
235 static std::shared_ptr<RegionBand> ImplCreateRegionBandFromPolyPolygon(const tools::PolyPolygon& rPolyPolygon)
237 std::shared_ptr<RegionBand> pRetval;
239 if(rPolyPolygon.Count())
241 // ensure to subdivide when bezier segments are used, it's going to
242 // be expanded to rectangles
243 tools::PolyPolygon aPolyPolygon;
245 rPolyPolygon.AdaptiveSubdivide(aPolyPolygon);
247 if(aPolyPolygon.Count())
249 const tools::Rectangle aRect(aPolyPolygon.GetBoundRect());
251 if(!aRect.IsEmpty())
253 if(ImplIsPolygonRectilinear(aPolyPolygon))
255 // For rectilinear polygons there is an optimized band conversion.
256 pRetval = ImplRectilinearPolygonToBands(aPolyPolygon);
258 else
260 pRetval = ImplGeneralPolygonToBands(aPolyPolygon, aRect);
263 // Convert points into seps.
264 if(pRetval)
266 pRetval->processPoints();
268 // Optimize list of bands. Adjacent bands with identical lists
269 // of seps are joined.
270 if(!pRetval->OptimizeBandList())
272 pRetval.reset();
279 return pRetval;
282 tools::PolyPolygon vcl::Region::ImplCreatePolyPolygonFromRegionBand() const
284 tools::PolyPolygon aRetval;
286 if(getRegionBand())
288 RectangleVector aRectangles;
289 GetRegionRectangles(aRectangles);
291 for (auto const& rectangle : aRectangles)
293 aRetval.Insert( tools::Polygon(rectangle) );
296 else
298 OSL_ENSURE(false, "Called with no local RegionBand (!)");
301 return aRetval;
304 basegfx::B2DPolyPolygon vcl::Region::ImplCreateB2DPolyPolygonFromRegionBand() const
306 tools::PolyPolygon aPoly(ImplCreatePolyPolygonFromRegionBand());
308 return aPoly.getB2DPolyPolygon();
311 Region::Region(bool bIsNull)
312 : mbIsNull(bIsNull)
316 Region::Region(const tools::Rectangle& rRect)
317 : mbIsNull(false)
319 if (!rRect.IsEmpty())
320 mpRegionBand = std::make_shared<RegionBand>(rRect);
323 Region::Region(const tools::Polygon& rPolygon)
324 : mbIsNull(false)
327 if(rPolygon.GetSize())
329 ImplCreatePolyPolyRegion(tools::PolyPolygon(rPolygon));
333 Region::Region(const tools::PolyPolygon& rPolyPoly)
334 : mbIsNull(false)
337 if(rPolyPoly.Count())
339 ImplCreatePolyPolyRegion(rPolyPoly);
343 Region::Region(const basegfx::B2DPolyPolygon& rPolyPoly)
344 : mbIsNull(false)
347 if(rPolyPoly.count())
349 ImplCreatePolyPolyRegion(rPolyPoly);
353 Region::Region(const vcl::Region&) = default;
355 Region::Region(vcl::Region&& rRegion) noexcept
356 : mpB2DPolyPolygon(std::move(rRegion.mpB2DPolyPolygon)),
357 mpPolyPolygon(std::move(rRegion.mpPolyPolygon)),
358 mpRegionBand(std::move(rRegion.mpRegionBand)),
359 mbIsNull(rRegion.mbIsNull)
361 rRegion.mbIsNull = true;
364 Region::~Region() = default;
366 void vcl::Region::ImplCreatePolyPolyRegion( const tools::PolyPolygon& rPolyPoly )
368 const sal_uInt16 nPolyCount = rPolyPoly.Count();
370 if(!nPolyCount)
371 return;
373 // polypolygon empty? -> empty region
374 const tools::Rectangle aRect(rPolyPoly.GetBoundRect());
376 if(aRect.IsEmpty())
377 return;
379 // width OR height == 1 ? => Rectangular region
380 if((1 == aRect.GetWidth()) || (1 == aRect.GetHeight()) || rPolyPoly.IsRect())
382 mpRegionBand = std::make_shared<RegionBand>(aRect);
384 else
386 mpPolyPolygon = rPolyPoly;
389 mbIsNull = false;
392 void vcl::Region::ImplCreatePolyPolyRegion( const basegfx::B2DPolyPolygon& rPolyPoly )
394 if(rPolyPoly.count() && !rPolyPoly.getB2DRange().isEmpty())
396 mpB2DPolyPolygon = rPolyPoly;
397 mbIsNull = false;
401 void vcl::Region::Move( tools::Long nHorzMove, tools::Long nVertMove )
403 if(IsNull() || IsEmpty())
405 // empty or null need no move
406 return;
409 if(!nHorzMove && !nVertMove)
411 // no move defined
412 return;
415 if(getB2DPolyPolygon())
417 basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon());
419 aPoly.transform(basegfx::utils::createTranslateB2DHomMatrix(nHorzMove, nVertMove));
420 if (aPoly.count())
421 mpB2DPolyPolygon = aPoly;
422 else
423 mpB2DPolyPolygon.reset();
424 mpPolyPolygon.reset();
425 mpRegionBand.reset();
427 else if(getPolyPolygon())
429 tools::PolyPolygon aPoly(*getPolyPolygon());
431 aPoly.Move(nHorzMove, nVertMove);
432 mpB2DPolyPolygon.reset();
433 if (aPoly.Count())
434 mpPolyPolygon = aPoly;
435 else
436 mpPolyPolygon.reset();
437 mpRegionBand.reset();
439 else if(getRegionBand())
441 std::shared_ptr<RegionBand> pNew = std::make_shared<RegionBand>(*getRegionBand());
443 pNew->Move(nHorzMove, nVertMove);
444 mpB2DPolyPolygon.reset();
445 mpPolyPolygon.reset();
446 mpRegionBand = std::move(pNew);
448 else
450 OSL_ENSURE(false, "Region::Move error: impossible combination (!)");
454 void vcl::Region::Scale( double fScaleX, double fScaleY )
456 if(IsNull() || IsEmpty())
458 // empty or null need no scale
459 return;
462 if(basegfx::fTools::equalZero(fScaleX) && basegfx::fTools::equalZero(fScaleY))
464 // no scale defined
465 return;
468 if(getB2DPolyPolygon())
470 basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon());
472 aPoly.transform(basegfx::utils::createScaleB2DHomMatrix(fScaleX, fScaleY));
473 if (aPoly.count())
474 mpB2DPolyPolygon = aPoly;
475 else
476 mpB2DPolyPolygon.reset();
477 mpPolyPolygon.reset();
478 mpRegionBand.reset();
480 else if(getPolyPolygon())
482 tools::PolyPolygon aPoly(*getPolyPolygon());
484 aPoly.Scale(fScaleX, fScaleY);
485 mpB2DPolyPolygon.reset();
486 if (aPoly.Count())
487 mpPolyPolygon = aPoly;
488 else
489 mpPolyPolygon.reset();
490 mpRegionBand.reset();
492 else if(getRegionBand())
494 std::shared_ptr<RegionBand> pNew = std::make_shared<RegionBand>(*getRegionBand());
496 pNew->Scale(fScaleX, fScaleY);
497 mpB2DPolyPolygon.reset();
498 mpPolyPolygon.reset();
499 mpRegionBand = std::move(pNew);
501 else
503 OSL_ENSURE(false, "Region::Scale error: impossible combination (!)");
507 void vcl::Region::Union( const tools::Rectangle& rRect )
509 if(rRect.IsEmpty())
511 // empty rectangle will not expand the existing union, nothing to do
512 return;
515 if(IsEmpty())
517 // no local data, the union will be equal to source. Create using rectangle
518 *this = rRect;
519 return;
522 if(HasPolyPolygonOrB2DPolyPolygon())
524 // get this B2DPolyPolygon, solve on polygon base
525 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
527 aThisPolyPoly = basegfx::utils::prepareForPolygonOperation(aThisPolyPoly);
529 if(!aThisPolyPoly.count())
531 // no local polygon, use the rectangle as new region
532 *this = rRect;
534 else
536 // get the other B2DPolyPolygon and use logical Or-Operation
537 const basegfx::B2DPolygon aRectPoly(
538 basegfx::utils::createPolygonFromRect(
539 vcl::unotools::b2DRectangleFromRectangle(rRect)));
540 const basegfx::B2DPolyPolygon aClip(
541 basegfx::utils::solvePolygonOperationOr(
542 aThisPolyPoly,
543 basegfx::B2DPolyPolygon(aRectPoly)));
544 *this = vcl::Region(aClip);
547 return;
550 // only region band mode possibility left here or null/empty
551 const RegionBand* pCurrent = getRegionBand();
553 if(!pCurrent)
555 // no region band, create using the rectangle
556 *this = rRect;
557 return;
560 std::shared_ptr<RegionBand> pNew = std::make_shared<RegionBand>(*pCurrent);
562 // get justified rectangle
563 const tools::Long nLeft(std::min(rRect.Left(), rRect.Right()));
564 const tools::Long nTop(std::min(rRect.Top(), rRect.Bottom()));
565 const tools::Long nRight(std::max(rRect.Left(), rRect.Right()));
566 const tools::Long nBottom(std::max(rRect.Top(), rRect.Bottom()));
568 // insert bands if the boundaries are not already in the list
569 pNew->InsertBands(nTop, nBottom);
571 // process union
572 pNew->Union(nLeft, nTop, nRight, nBottom);
574 // cleanup
575 if(!pNew->OptimizeBandList())
577 pNew.reset();
580 mpRegionBand = std::move(pNew);
583 void vcl::Region::Intersect( const tools::Rectangle& rRect )
585 if ( rRect.IsEmpty() )
587 // empty rectangle will create empty region
588 SetEmpty();
589 return;
592 if(IsNull())
594 // null region (everything) intersect with rect will give rect
595 *this = rRect;
596 return;
599 if(IsEmpty())
601 // no content, cannot get more empty
602 return;
605 if(HasPolyPolygonOrB2DPolyPolygon())
607 // if polygon data prefer double precision, the other will be lost (if buffered)
608 if(getB2DPolyPolygon())
610 const basegfx::B2DPolyPolygon aPoly(
611 basegfx::utils::clipPolyPolygonOnRange(
612 *getB2DPolyPolygon(),
613 basegfx::B2DRange(
614 rRect.Left(),
615 rRect.Top(),
616 rRect.Right() + 1,
617 rRect.Bottom() + 1),
618 true,
619 false));
621 if (aPoly.count())
622 mpB2DPolyPolygon = aPoly;
623 else
624 mpB2DPolyPolygon.reset();
625 mpPolyPolygon.reset();
626 mpRegionBand.reset();
628 else // if(getPolyPolygon())
630 tools::PolyPolygon aPoly(*getPolyPolygon());
632 // use the PolyPolygon::Clip method for rectangles, this is
633 // fairly simple (does not even use GPC) and saves us from
634 // unnecessary banding
635 aPoly.Clip(rRect);
637 mpB2DPolyPolygon.reset();
638 if (aPoly.Count())
639 mpPolyPolygon = aPoly;
640 else
641 mpPolyPolygon.reset();
642 mpRegionBand.reset();
645 return;
648 // only region band mode possibility left here or null/empty
649 const RegionBand* pCurrent = getRegionBand();
651 if(!pCurrent)
653 // region is empty -> nothing to do!
654 return;
657 std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));
659 // get justified rectangle
660 const tools::Long nLeft(std::min(rRect.Left(), rRect.Right()));
661 const tools::Long nTop(std::min(rRect.Top(), rRect.Bottom()));
662 const tools::Long nRight(std::max(rRect.Left(), rRect.Right()));
663 const tools::Long nBottom(std::max(rRect.Top(), rRect.Bottom()));
665 // insert bands if the boundaries are not already in the list
666 pNew->InsertBands(nTop, nBottom);
668 // process intersect
669 pNew->Intersect(nLeft, nTop, nRight, nBottom);
671 // cleanup
672 if(!pNew->OptimizeBandList())
674 pNew.reset();
677 mpRegionBand = std::move(pNew);
680 void vcl::Region::Exclude( const tools::Rectangle& rRect )
682 if ( rRect.IsEmpty() )
684 // excluding nothing will do no change
685 return;
688 if(IsEmpty())
690 // cannot exclude from empty, done
691 return;
694 if(IsNull())
696 // error; cannot exclude from null region since this is not representable
697 // in the data
698 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
699 return;
702 if( HasPolyPolygonOrB2DPolyPolygon() )
704 // get this B2DPolyPolygon
705 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
707 aThisPolyPoly = basegfx::utils::prepareForPolygonOperation(aThisPolyPoly);
709 if(!aThisPolyPoly.count())
711 // when local polygon is empty, nothing can be excluded
712 return;
715 // get the other B2DPolyPolygon
716 const basegfx::B2DPolygon aRectPoly(
717 basegfx::utils::createPolygonFromRect(
718 vcl::unotools::b2DRectangleFromRectangle(rRect)));
719 const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly);
720 const basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationDiff(aThisPolyPoly, aOtherPolyPoly);
722 *this = vcl::Region(aClip);
724 return;
727 // only region band mode possibility left here or null/empty
728 if(!mpRegionBand)
730 // empty? -> done!
731 return;
734 std::shared_ptr<RegionBand>& pNew = mpRegionBand;
735 // only make a copy if someone else is also using it
736 if (pNew.use_count() > 1)
737 pNew = std::make_shared<RegionBand>(*pNew);
739 // get justified rectangle
740 const tools::Long nLeft(std::min(rRect.Left(), rRect.Right()));
741 const tools::Long nTop(std::min(rRect.Top(), rRect.Bottom()));
742 const tools::Long nRight(std::max(rRect.Left(), rRect.Right()));
743 const tools::Long nBottom(std::max(rRect.Top(), rRect.Bottom()));
745 // insert bands if the boundaries are not already in the list
746 pNew->InsertBands(nTop, nBottom);
748 // process exclude
749 pNew->Exclude(nLeft, nTop, nRight, nBottom);
751 // cleanup
752 if(!pNew->OptimizeBandList())
753 pNew.reset();
756 void vcl::Region::XOr( const tools::Rectangle& rRect )
758 if ( rRect.IsEmpty() )
760 // empty rectangle will not change local content
761 return;
764 if(IsEmpty())
766 // rRect will be the xored-form (local off, rect on)
767 *this = rRect;
768 return;
771 if(IsNull())
773 // error; cannot exclude from null region since this is not representable
774 // in the data
775 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
776 return;
779 if( HasPolyPolygonOrB2DPolyPolygon() )
781 // get this B2DPolyPolygon
782 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
784 aThisPolyPoly = basegfx::utils::prepareForPolygonOperation( aThisPolyPoly );
786 if(!aThisPolyPoly.count())
788 // no local content, XOr will be equal to rectangle
789 *this = rRect;
790 return;
793 // get the other B2DPolyPolygon
794 const basegfx::B2DPolygon aRectPoly(
795 basegfx::utils::createPolygonFromRect(
796 vcl::unotools::b2DRectangleFromRectangle(rRect)));
797 const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly);
798 const basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationXor(aThisPolyPoly, aOtherPolyPoly);
800 *this = vcl::Region(aClip);
802 return;
805 // only region band mode possibility left here or null/empty
806 const RegionBand* pCurrent = getRegionBand();
808 if(!pCurrent)
810 // rRect will be the xored-form (local off, rect on)
811 *this = rRect;
812 return;
815 // only region band mode possibility left here or null/empty
816 std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*getRegionBand()));
818 // get justified rectangle
819 const tools::Long nLeft(std::min(rRect.Left(), rRect.Right()));
820 const tools::Long nTop(std::min(rRect.Top(), rRect.Bottom()));
821 const tools::Long nRight(std::max(rRect.Left(), rRect.Right()));
822 const tools::Long nBottom(std::max(rRect.Top(), rRect.Bottom()));
824 // insert bands if the boundaries are not already in the list
825 pNew->InsertBands(nTop, nBottom);
827 // process xor
828 pNew->XOr(nLeft, nTop, nRight, nBottom);
830 // cleanup
831 if(!pNew->OptimizeBandList())
833 pNew.reset();
836 mpRegionBand = std::move(pNew);
839 void vcl::Region::Union( const vcl::Region& rRegion )
841 if(rRegion.IsEmpty())
843 // no extension at all
844 return;
847 if(rRegion.IsNull())
849 // extending with null region -> null region
850 *this = vcl::Region(true);
851 return;
854 if(IsEmpty())
856 // local is empty, union will give source region
857 *this = rRegion;
858 return;
861 if(IsNull())
863 // already fully expanded (is null region), cannot be extended
864 return;
867 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
869 // get this B2DPolyPolygon
870 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
872 aThisPolyPoly = basegfx::utils::prepareForPolygonOperation(aThisPolyPoly);
874 if(!aThisPolyPoly.count())
876 // when no local content, union will be equal to rRegion
877 *this = rRegion;
878 return;
881 // get the other B2DPolyPolygon
882 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
883 aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation(aOtherPolyPoly);
885 // use logical OR operation
886 basegfx::B2DPolyPolygon aClip(basegfx::utils::solvePolygonOperationOr(aThisPolyPoly, aOtherPolyPoly));
888 *this = vcl::Region( aClip );
889 return;
892 // only region band mode possibility left here or null/empty
893 const RegionBand* pCurrent = getRegionBand();
895 if(!pCurrent)
897 // local is empty, union will give source region
898 *this = rRegion;
899 return;
902 const RegionBand* pSource = rRegion.getRegionBand();
904 if(!pSource)
906 // no extension at all
907 return;
910 // prepare source and target
911 std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));
913 // union with source
914 pNew->Union(*pSource);
916 // cleanup
917 if(!pNew->OptimizeBandList())
919 pNew.reset();
922 mpRegionBand = std::move(pNew);
925 void vcl::Region::Intersect( const vcl::Region& rRegion )
927 // same instance data? -> nothing to do!
928 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
930 return;
933 if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
935 return;
938 if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
940 return;
943 if(rRegion.IsNull())
945 // source region is null-region, intersect will not change local region
946 return;
949 if(IsNull())
951 // when local region is null-region, intersect will be equal to source
952 *this = rRegion;
953 return;
956 if(rRegion.IsEmpty())
958 // source region is empty, intersection will always be empty
959 SetEmpty();
960 return;
963 if(IsEmpty())
965 // local region is empty, cannot get more empty than that. Nothing to do
966 return;
969 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
971 // get this B2DPolyPolygon
972 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
974 if(!aThisPolyPoly.count())
976 // local region is empty, cannot get more empty than that. Nothing to do
977 return;
980 // get the other B2DPolyPolygon
981 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
983 if(!aOtherPolyPoly.count())
985 // source region is empty, intersection will always be empty
986 SetEmpty();
987 return;
990 static size_t gPointLimit = !comphelper::IsFuzzing() ? SAL_MAX_SIZE : 8192;
991 size_t nPointLimit(gPointLimit);
992 const basegfx::B2DPolyPolygon aClip(
993 basegfx::utils::clipPolyPolygonOnPolyPolygon(
994 aOtherPolyPoly,
995 aThisPolyPoly,
996 true,
997 false,
998 &nPointLimit));
999 *this = vcl::Region( aClip );
1000 return;
1003 // only region band mode possibility left here or null/empty
1004 const RegionBand* pCurrent = getRegionBand();
1006 if(!pCurrent)
1008 // local region is empty, cannot get more empty than that. Nothing to do
1009 return;
1012 const RegionBand* pSource = rRegion.getRegionBand();
1014 if(!pSource)
1016 // source region is empty, intersection will always be empty
1017 SetEmpty();
1018 return;
1021 // both RegionBands exist and are not empty
1022 if(pCurrent->getRectangleCount() + 2 < pSource->getRectangleCount())
1024 // when we have less rectangles, turn around the call
1025 vcl::Region aTempRegion = rRegion;
1026 aTempRegion.Intersect( *this );
1027 *this = aTempRegion;
1029 else
1031 // prepare new regionBand
1032 std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));
1034 // intersect with source
1035 pNew->Intersect(*pSource);
1037 // cleanup
1038 if(!pNew->OptimizeBandList())
1040 pNew.reset();
1043 mpRegionBand = std::move(pNew);
1047 void vcl::Region::Exclude( const vcl::Region& rRegion )
1049 if ( rRegion.IsEmpty() )
1051 // excluding nothing will do no change
1052 return;
1055 if ( rRegion.IsNull() )
1057 // excluding everything will create empty region
1058 SetEmpty();
1059 return;
1062 if(IsEmpty())
1064 // cannot exclude from empty, done
1065 return;
1068 if(IsNull())
1070 // error; cannot exclude from null region since this is not representable
1071 // in the data
1072 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
1073 return;
1076 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1078 // get this B2DPolyPolygon
1079 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
1081 if(!aThisPolyPoly.count())
1083 // cannot exclude from empty, done
1084 return;
1087 aThisPolyPoly = basegfx::utils::prepareForPolygonOperation( aThisPolyPoly );
1089 // get the other B2DPolyPolygon
1090 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
1091 aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation( aOtherPolyPoly );
1093 basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationDiff( aThisPolyPoly, aOtherPolyPoly );
1094 *this = vcl::Region( aClip );
1095 return;
1098 // only region band mode possibility left here or null/empty
1099 const RegionBand* pCurrent = getRegionBand();
1101 if(!pCurrent)
1103 // cannot exclude from empty, done
1104 return;
1107 const RegionBand* pSource = rRegion.getRegionBand();
1109 if(!pSource)
1111 // excluding nothing will do no change
1112 return;
1115 // prepare source and target
1116 std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));
1118 // union with source
1119 const bool bSuccess(pNew->Exclude(*pSource));
1121 // cleanup
1122 if(!bSuccess)
1124 pNew.reset();
1127 mpRegionBand = std::move(pNew);
1130 bool vcl::Region::XOr( const vcl::Region& rRegion )
1132 if ( rRegion.IsEmpty() )
1134 // empty region will not change local content
1135 return true;
1138 if ( rRegion.IsNull() )
1140 // error; cannot exclude null region from local since this is not representable
1141 // in the data
1142 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1143 return true;
1146 if(IsEmpty())
1148 // rRect will be the xored-form (local off, rect on)
1149 *this = rRegion;
1150 return true;
1153 if(IsNull())
1155 // error: cannot exclude from null region since this is not representable
1156 // in the data
1157 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1158 return false;
1161 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1163 // get this B2DPolyPolygon
1164 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
1166 if(!aThisPolyPoly.count())
1168 // rRect will be the xored-form (local off, rect on)
1169 *this = rRegion;
1170 return true;
1173 aThisPolyPoly = basegfx::utils::prepareForPolygonOperation( aThisPolyPoly );
1175 // get the other B2DPolyPolygon
1176 basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
1177 aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation( aOtherPolyPoly );
1179 basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationXor( aThisPolyPoly, aOtherPolyPoly );
1180 *this = vcl::Region( aClip );
1181 return true;
1184 // only region band mode possibility left here or null/empty
1185 const RegionBand* pCurrent = getRegionBand();
1187 if(!pCurrent)
1189 // rRect will be the xored-form (local off, rect on)
1190 *this = rRegion;
1191 return true;
1194 const RegionBand* pSource = rRegion.getRegionBand();
1196 if(!pSource)
1198 // empty region will not change local content
1199 return true;
1202 // prepare source and target
1203 std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));
1205 // union with source
1206 pNew->XOr(*pSource);
1208 // cleanup
1209 if(!pNew->OptimizeBandList())
1211 pNew.reset();
1214 mpRegionBand = std::move(pNew);
1216 return true;
1219 tools::Rectangle vcl::Region::GetBoundRect() const
1221 if(IsEmpty())
1223 // no internal data? -> region is empty!
1224 return tools::Rectangle();
1227 if(IsNull())
1229 // error; null region has no BoundRect
1230 // OSL_ENSURE(false, "Region::GetBoundRect error: null region has unlimited bound rect, not representable (!)");
1231 return tools::Rectangle();
1234 // prefer double precision source
1235 if(getB2DPolyPolygon())
1237 const basegfx::B2DRange aRange(basegfx::utils::getRange(*getB2DPolyPolygon()));
1239 if(aRange.isEmpty())
1241 // emulate PolyPolygon::GetBoundRect() when empty polygon
1242 return tools::Rectangle();
1244 else
1246 // #i122149# corrected rounding, no need for ceil() and floor() here
1247 return tools::Rectangle(
1248 basegfx::fround(aRange.getMinX()), basegfx::fround(aRange.getMinY()),
1249 basegfx::fround(aRange.getMaxX()), basegfx::fround(aRange.getMaxY()));
1253 if(getPolyPolygon())
1255 return getPolyPolygon()->GetBoundRect();
1258 if(getRegionBand())
1260 return getRegionBand()->GetBoundRect();
1263 return tools::Rectangle();
1266 tools::PolyPolygon vcl::Region::GetAsPolyPolygon() const
1268 if(getPolyPolygon())
1270 return *getPolyPolygon();
1273 if(getB2DPolyPolygon())
1275 // the polygon needs to be converted, buffer the down conversion
1276 const tools::PolyPolygon aPolyPolgon(*getB2DPolyPolygon());
1277 const_cast< vcl::Region* >(this)->mpPolyPolygon = aPolyPolgon;
1279 return *getPolyPolygon();
1282 if(getRegionBand())
1284 // the BandRegion needs to be converted, buffer the conversion
1285 const tools::PolyPolygon aPolyPolgon(ImplCreatePolyPolygonFromRegionBand());
1286 const_cast< vcl::Region* >(this)->mpPolyPolygon = aPolyPolgon;
1288 return *getPolyPolygon();
1291 return tools::PolyPolygon();
1294 basegfx::B2DPolyPolygon vcl::Region::GetAsB2DPolyPolygon() const
1296 if(getB2DPolyPolygon())
1298 return *getB2DPolyPolygon();
1301 if(getPolyPolygon())
1303 // the polygon needs to be converted, buffer the up conversion. This will be preferred from now.
1304 const basegfx::B2DPolyPolygon aB2DPolyPolygon(getPolyPolygon()->getB2DPolyPolygon());
1305 const_cast< vcl::Region* >(this)->mpB2DPolyPolygon = aB2DPolyPolygon;
1307 return *getB2DPolyPolygon();
1310 if(getRegionBand())
1312 // the BandRegion needs to be converted, buffer the conversion
1313 const basegfx::B2DPolyPolygon aB2DPolyPolygon(ImplCreateB2DPolyPolygonFromRegionBand());
1314 const_cast< vcl::Region* >(this)->mpB2DPolyPolygon = aB2DPolyPolygon;
1316 return *getB2DPolyPolygon();
1319 return basegfx::B2DPolyPolygon();
1322 const RegionBand* vcl::Region::GetAsRegionBand() const
1324 if(!getRegionBand())
1326 if(getB2DPolyPolygon())
1328 // convert B2DPolyPolygon to RegionBand, buffer it and return it
1329 const_cast< vcl::Region* >(this)->mpRegionBand = ImplCreateRegionBandFromPolyPolygon(tools::PolyPolygon(*getB2DPolyPolygon()));
1331 else if(getPolyPolygon())
1333 // convert B2DPolyPolygon to RegionBand, buffer it and return it
1334 const_cast< vcl::Region* >(this)->mpRegionBand = ImplCreateRegionBandFromPolyPolygon(*getPolyPolygon());
1338 return getRegionBand();
1341 bool vcl::Region::Contains( const Point& rPoint ) const
1343 if(IsEmpty())
1345 // no point can be in empty region
1346 return false;
1349 if(IsNull())
1351 // all points are inside null-region
1352 return true;
1355 // Too expensive (?)
1356 //if(mpImplRegion->getRegionPolyPoly())
1358 // return mpImplRegion->getRegionPolyPoly()->Contains( rPoint );
1361 // ensure RegionBand existence
1362 const RegionBand* pRegionBand = GetAsRegionBand();
1364 if(pRegionBand)
1366 return pRegionBand->Contains(rPoint);
1369 return false;
1372 bool vcl::Region::Overlaps( const tools::Rectangle& rRect ) const
1374 if(IsEmpty())
1376 // nothing can be over something empty
1377 return false;
1380 if(IsNull())
1382 // everything is over null region
1383 return true;
1386 // Can we optimize this ??? - is used in StarDraw for brushes pointers
1387 // Why we have no IsOver for Regions ???
1388 // create region from rectangle and intersect own region
1389 vcl::Region aRegion(rRect);
1390 aRegion.Intersect( *this );
1392 // rectangle is over if include is not empty
1393 return !aRegion.IsEmpty();
1396 bool vcl::Region::IsRectangle() const
1398 if( IsEmpty() || IsNull() )
1399 return false;
1401 if( getB2DPolyPolygon() )
1402 return basegfx::utils::isRectangle( *getB2DPolyPolygon() );
1404 if( getPolyPolygon() )
1405 return getPolyPolygon()->IsRect();
1407 if( getRegionBand() )
1408 return (getRegionBand()->getRectangleCount() == 1);
1410 return false;
1413 void vcl::Region::SetNull()
1415 // reset all content
1416 mpB2DPolyPolygon.reset();
1417 mpPolyPolygon.reset();
1418 mpRegionBand.reset();
1419 mbIsNull = true;
1422 void vcl::Region::SetEmpty()
1424 // reset all content
1425 mpB2DPolyPolygon.reset();
1426 mpPolyPolygon.reset();
1427 mpRegionBand.reset();
1428 mbIsNull = false;
1431 Region& vcl::Region::operator=( const vcl::Region& ) = default;
1433 Region& vcl::Region::operator=( vcl::Region&& rRegion ) noexcept
1435 mpB2DPolyPolygon = std::move(rRegion.mpB2DPolyPolygon);
1436 mpPolyPolygon = std::move(rRegion.mpPolyPolygon);
1437 mpRegionBand = std::move(rRegion.mpRegionBand);
1438 mbIsNull = rRegion.mbIsNull;
1439 rRegion.mbIsNull = true;
1441 return *this;
1444 Region& vcl::Region::operator=( const tools::Rectangle& rRect )
1446 mpB2DPolyPolygon.reset();
1447 mpPolyPolygon.reset();
1448 if (!rRect.IsEmpty())
1449 mpRegionBand = std::make_shared<RegionBand>(rRect);
1450 else
1451 mpRegionBand.reset();
1452 mbIsNull = false;
1454 return *this;
1457 bool vcl::Region::operator==( const vcl::Region& rRegion ) const
1459 if(IsNull() && rRegion.IsNull())
1461 // both are null region
1462 return true;
1465 if(IsEmpty() && rRegion.IsEmpty())
1467 // both are empty
1468 return true;
1471 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
1473 // same instance data? -> equal
1474 return true;
1477 if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
1479 // same instance data? -> equal
1480 return true;
1483 if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
1485 // same instance data? -> equal
1486 return true;
1489 if(IsNull() || IsEmpty())
1491 return false;
1494 if(rRegion.IsNull() || rRegion.IsEmpty())
1496 return false;
1499 if(rRegion.getB2DPolyPolygon() || getB2DPolyPolygon())
1501 // one of both has a B2DPolyPolygon based region, ensure both have it
1502 // by evtl. conversion
1503 GetAsB2DPolyPolygon();
1504 rRegion.GetAsB2DPolyPolygon();
1506 return *rRegion.getB2DPolyPolygon() == *getB2DPolyPolygon();
1509 if(rRegion.getPolyPolygon() || getPolyPolygon())
1511 // one of both has a B2DPolyPolygon based region, ensure both have it
1512 // by evtl. conversion
1513 GetAsPolyPolygon();
1514 rRegion.GetAsPolyPolygon();
1516 return *rRegion.getPolyPolygon() == *getPolyPolygon();
1519 // both are not empty or null (see above) and if content supported polygon
1520 // data the comparison is already done. Only both on RegionBand base can be left,
1521 // but better check
1522 if(rRegion.getRegionBand() && getRegionBand())
1524 return *rRegion.getRegionBand() == *getRegionBand();
1527 // should not happen, but better deny equality
1528 return false;
1531 SvStream& ReadRegion(SvStream& rIStrm, vcl::Region& rRegion)
1533 VersionCompatRead aCompat(rIStrm);
1534 sal_uInt16 nVersion(0);
1535 sal_uInt16 nTmp16(0);
1537 // clear region to be loaded
1538 rRegion.SetEmpty();
1540 // get version of streamed region
1541 rIStrm.ReadUInt16( nVersion );
1543 // get type of region
1544 rIStrm.ReadUInt16( nTmp16 );
1546 enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX };
1547 auto eStreamedType = nTmp16;
1549 switch (eStreamedType)
1551 case REGION_NULL:
1553 rRegion.SetNull();
1554 break;
1557 case REGION_EMPTY:
1559 rRegion.SetEmpty();
1560 break;
1563 default:
1565 std::shared_ptr<RegionBand> xNewRegionBand(std::make_shared<RegionBand>());
1566 bool bSuccess = xNewRegionBand->load(rIStrm);
1567 rRegion.mpRegionBand = xNewRegionBand;
1569 bool bHasPolyPolygon(false);
1570 if (aCompat.GetVersion() >= 2)
1572 rIStrm.ReadCharAsBool( bHasPolyPolygon );
1574 if (bHasPolyPolygon)
1576 tools::PolyPolygon aNewPoly;
1577 ReadPolyPolygon(rIStrm, aNewPoly);
1578 const auto nPolygons = aNewPoly.Count();
1579 if (nPolygons > 128)
1581 SAL_WARN("vcl.gdi", "suspiciously high no of polygons in clip:" << nPolygons);
1582 if (comphelper::IsFuzzing())
1583 aNewPoly.Clear();
1585 rRegion.mpPolyPolygon = aNewPoly;
1589 if (!bSuccess && !bHasPolyPolygon)
1591 SAL_WARN("vcl.gdi", "bad region band:" << bHasPolyPolygon);
1592 rRegion.SetNull();
1595 break;
1599 return rIStrm;
1602 SvStream& WriteRegion( SvStream& rOStrm, const vcl::Region& rRegion )
1604 const sal_uInt16 nVersion(2);
1605 VersionCompatWrite aCompat(rOStrm, nVersion);
1607 // put version
1608 rOStrm.WriteUInt16( nVersion );
1610 // put type
1611 enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX };
1612 RegionType aRegionType(REGION_COMPLEX);
1613 bool bEmpty(rRegion.IsEmpty());
1615 if(!bEmpty && rRegion.getB2DPolyPolygon() && 0 == rRegion.getB2DPolyPolygon()->count())
1617 OSL_ENSURE(false, "Region with empty B2DPolyPolygon, should not be created (!)");
1618 bEmpty = true;
1621 if(!bEmpty && rRegion.getPolyPolygon() && 0 == rRegion.getPolyPolygon()->Count())
1623 OSL_ENSURE(false, "Region with empty PolyPolygon, should not be created (!)");
1624 bEmpty = true;
1627 if(bEmpty)
1629 aRegionType = REGION_EMPTY;
1631 else if(rRegion.IsNull())
1633 aRegionType = REGION_NULL;
1635 else if(rRegion.getRegionBand() && rRegion.getRegionBand()->isSingleRectangle())
1637 aRegionType = REGION_RECTANGLE;
1640 rOStrm.WriteUInt16( aRegionType );
1642 // get RegionBand
1643 const RegionBand* pRegionBand = rRegion.getRegionBand();
1645 if(pRegionBand)
1647 pRegionBand->save(rOStrm);
1649 else
1651 // for compatibility, write an empty RegionBand (will only write
1652 // the end marker STREAMENTRY_END, but this *is* needed)
1653 const RegionBand aRegionBand;
1655 aRegionBand.save(rOStrm);
1658 // write polypolygon if available
1659 const bool bHasPolyPolygon(rRegion.HasPolyPolygonOrB2DPolyPolygon());
1660 rOStrm.WriteBool( bHasPolyPolygon );
1662 if(bHasPolyPolygon)
1664 // #i105373#
1665 tools::PolyPolygon aNoCurvePolyPolygon;
1666 rRegion.GetAsPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon);
1668 WritePolyPolygon( rOStrm, aNoCurvePolyPolygon );
1671 return rOStrm;
1674 void vcl::Region::GetRegionRectangles(RectangleVector& rTarget) const
1676 // clear returnvalues
1677 rTarget.clear();
1679 // ensure RegionBand existence
1680 const RegionBand* pRegionBand = GetAsRegionBand();
1682 if(pRegionBand)
1684 pRegionBand->GetRegionRectangles(rTarget);
1688 static bool ImplPolygonRectTest( const tools::Polygon& rPoly, tools::Rectangle* pRectOut = nullptr )
1690 bool bIsRect = false;
1691 const Point* pPoints = rPoly.GetConstPointAry();
1692 sal_uInt16 nPoints = rPoly.GetSize();
1694 if( nPoints == 4 || (nPoints == 5 && pPoints[0] == pPoints[4]) )
1696 tools::Long nX1 = pPoints[0].X(), nX2 = pPoints[2].X(), nY1 = pPoints[0].Y(), nY2 = pPoints[2].Y();
1698 if( ( (pPoints[1].X() == nX1 && pPoints[3].X() == nX2) && (pPoints[1].Y() == nY2 && pPoints[3].Y() == nY1) )
1699 || ( (pPoints[1].X() == nX2 && pPoints[3].X() == nX1) && (pPoints[1].Y() == nY1 && pPoints[3].Y() == nY2) ) )
1701 bIsRect = true;
1703 if( pRectOut )
1705 tools::Long nSwap;
1707 if( nX2 < nX1 )
1709 nSwap = nX2;
1710 nX2 = nX1;
1711 nX1 = nSwap;
1714 if( nY2 < nY1 )
1716 nSwap = nY2;
1717 nY2 = nY1;
1718 nY1 = nSwap;
1721 if( nX2 != nX1 )
1723 nX2--;
1726 if( nY2 != nY1 )
1728 nY2--;
1731 pRectOut->SetLeft( nX1 );
1732 pRectOut->SetRight( nX2 );
1733 pRectOut->SetTop( nY1 );
1734 pRectOut->SetBottom( nY2 );
1739 return bIsRect;
1742 vcl::Region vcl::Region::GetRegionFromPolyPolygon( const tools::PolyPolygon& rPolyPoly )
1744 //return vcl::Region( rPolyPoly );
1746 // check if it's worth extracting the XOr'ing the Rectangles
1747 // empiricism shows that break even between XOr'ing rectangles separately
1748 // and ImplCreateRegionBandFromPolyPolygon is at half rectangles/half polygons
1749 int nPolygonRects = 0, nPolygonPolygons = 0;
1750 int nPolygons = rPolyPoly.Count();
1752 for( int i = 0; i < nPolygons; i++ )
1754 const tools::Polygon& rPoly = rPolyPoly[i];
1756 if( ImplPolygonRectTest( rPoly ) )
1758 nPolygonRects++;
1760 else
1762 nPolygonPolygons++;
1766 if( nPolygonPolygons > nPolygonRects )
1768 return vcl::Region( rPolyPoly );
1771 vcl::Region aResult;
1772 tools::Rectangle aRect;
1774 for( int i = 0; i < nPolygons; i++ )
1776 const tools::Polygon& rPoly = rPolyPoly[i];
1778 if( ImplPolygonRectTest( rPoly, &aRect ) )
1780 aResult.XOr( aRect );
1782 else
1784 aResult.XOr( vcl::Region(rPoly) );
1788 return aResult;
1791 } /* namespace vcl */
1793 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */