Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / source / gdi / region.cxx
blobcec2de3f6b37a847c9aedf742090184f457877e8
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 <limits.h>
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>
39 namespace
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();
55 if (nSize < 2)
56 continue;
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())
66 return false;
68 aPoint = aNextPoint;
70 // Compare closing edge.
71 if (aLastPoint.X() != aPoint.X() && aLastPoint.Y() != aPoint.Y())
72 return false;
74 return true;
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.
82 @param rPolyPolygon
83 A set of zero, one, or more polygons, nested or not, that are
84 converted into a list of bands.
85 @return
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>() );
95 long nLineId = 0;
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();
105 if (nSize < 2)
106 continue;
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));
110 Point aEnd;
111 for (sal_uInt16 nPoint = 1; nPoint <= nSize; ++nPoint, aStart=aEnd)
113 // We take the implicit closing edge into account by mapping
114 // index nSize to 0.
115 aEnd = aPoly.GetPoint(nPoint%nSize);
116 if (aStart.Y() == aEnd.Y())
118 // Horizontal lines are ignored.
119 continue;
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.
142 if (pBand!=nullptr
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.
159 if (pBand!=nullptr
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
173 // coordinates.
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);
181 return pRegionBand;
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)
189 long nLineID = 0;
191 // initialisation and creation of Bands
192 std::unique_ptr<RegionBand> pRegionBand( o3tl::make_unique<RegionBand>() );
193 pRegionBand->CreateBandRange(rPolygonBoundingBox.Top(), rPolygonBoundingBox.Bottom());
195 // insert polygons
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!
205 if ( nSize <= 2 )
206 continue;
208 // band the polygon
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++ );
224 return pRegionBand;
226 } // end of anonymous namespace
228 namespace vcl {
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());
252 if(!aRect.IsEmpty())
254 if(ImplIsPolygonRectilinear(aPolyPolygon))
256 // For rectilinear polygons there is an optimized band conversion.
257 pRetval = ImplRectilinearPolygonToBands(aPolyPolygon);
259 else
261 pRetval = ImplGeneralPolygonToBands(aPolyPolygon, aRect);
264 // Convert points into seps.
265 if(pRetval)
267 pRetval->processPoints();
269 // Optimize list of bands. Adjacent bands with identical lists
270 // of seps are joined.
271 if(!pRetval->OptimizeBandList())
273 pRetval.reset();
280 return pRetval;
283 tools::PolyPolygon vcl::Region::ImplCreatePolyPolygonFromRegionBand() const
285 tools::PolyPolygon aRetval;
287 if(getRegionBand())
289 RectangleVector aRectangles;
290 GetRegionRectangles(aRectangles);
292 for(RectangleVector::const_iterator aRectIter(aRectangles.begin()); aRectIter != aRectangles.end(); ++aRectIter)
294 aRetval.Insert( tools::Polygon(*aRectIter) );
297 else
299 OSL_ENSURE(false, "Called with no local RegionBand (!)");
302 return aRetval;
305 basegfx::B2DPolyPolygon vcl::Region::ImplCreateB2DPolyPolygonFromRegionBand() const
307 tools::PolyPolygon aPoly(ImplCreatePolyPolygonFromRegionBand());
309 return aPoly.getB2DPolyPolygon();
312 Region::Region(bool bIsNull)
313 : mpB2DPolyPolygon(),
314 mpPolyPolygon(),
315 mpRegionBand(),
316 mbIsNull(bIsNull)
320 Region::Region(const tools::Rectangle& rRect)
321 : mpB2DPolyPolygon(),
322 mpPolyPolygon(),
323 mpRegionBand(),
324 mbIsNull(false)
326 mpRegionBand.reset(rRect.IsEmpty() ? nullptr : new RegionBand(rRect));
329 Region::Region(const tools::Polygon& rPolygon)
330 : mpB2DPolyPolygon(),
331 mpPolyPolygon(),
332 mpRegionBand(),
333 mbIsNull(false)
336 if(rPolygon.GetSize())
338 ImplCreatePolyPolyRegion(rPolygon);
342 Region::Region(const tools::PolyPolygon& rPolyPoly)
343 : mpB2DPolyPolygon(),
344 mpPolyPolygon(),
345 mpRegionBand(),
346 mbIsNull(false)
349 if(rPolyPoly.Count())
351 ImplCreatePolyPolyRegion(rPolyPoly);
355 Region::Region(const basegfx::B2DPolyPolygon& rPolyPoly)
356 : mpB2DPolyPolygon(),
357 mpPolyPolygon(),
358 mpRegionBand(),
359 mbIsNull(false)
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;
385 Region::~Region()
389 void vcl::Region::ImplCreatePolyPolyRegion( const tools::PolyPolygon& rPolyPoly )
391 const sal_uInt16 nPolyCount = rPolyPoly.Count();
393 if(nPolyCount)
395 // polypolygon empty? -> empty region
396 const tools::Rectangle aRect(rPolyPoly.GetBoundRect());
398 if(!aRect.IsEmpty())
400 // width OR height == 1 ? => Rectangular region
401 if((1 == aRect.GetWidth()) || (1 == aRect.GetHeight()) || rPolyPoly.IsRect())
403 mpRegionBand.reset(new RegionBand(aRect));
405 else
407 mpPolyPolygon.reset(new tools::PolyPolygon(rPolyPoly));
410 mbIsNull = false;
415 void vcl::Region::ImplCreatePolyPolyRegion( const basegfx::B2DPolyPolygon& rPolyPoly )
417 if(rPolyPoly.count() && !rPolyPoly.getB2DRange().isEmpty())
419 mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(rPolyPoly));
420 mbIsNull = false;
424 void vcl::Region::Move( long nHorzMove, long nVertMove )
426 if(IsNull() || IsEmpty())
428 // empty or null need no move
429 return;
432 if(!nHorzMove && !nVertMove)
434 // no move defined
435 return;
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);
465 else
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
476 return;
479 if(basegfx::fTools::equalZero(fScaleX) && basegfx::fTools::equalZero(fScaleY))
481 // no scale defined
482 return;
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);
512 else
514 OSL_ENSURE(false, "Region::Scale error: impossible combination (!)");
518 bool vcl::Region::Union( const tools::Rectangle& rRect )
520 if(rRect.IsEmpty())
522 // empty rectangle will not expand the existing union, nothing to do
523 return true;
526 if(IsEmpty())
528 // no local data, the union will be equal to source. Create using rectangle
529 *this = rRect;
530 return true;
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
543 *this = rRect;
545 else
547 // get the other B2DPolyPolygon and use logical Or-Operation
548 const basegfx::B2DPolygon aRectPoly(
549 basegfx::tools::createPolygonFromRect(
550 basegfx::B2DRectangle(
551 rRect.Left(),
552 rRect.Top(),
553 rRect.Right(),
554 rRect.Bottom())));
555 const basegfx::B2DPolyPolygon aClip(
556 basegfx::tools::solvePolygonOperationOr(
557 aThisPolyPoly,
558 basegfx::B2DPolyPolygon(aRectPoly)));
559 *this = vcl::Region(aClip);
562 return true;
565 // only region band mode possibility left here or null/empty
566 const RegionBand* pCurrent = getRegionBand();
568 if(!pCurrent)
570 // no region band, create using the rectangle
571 *this = rRect;
572 return true;
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);
586 // process union
587 pNew->Union(nLeft, nTop, nRight, nBottom);
589 // cleanup
590 if(!pNew->OptimizeBandList())
592 pNew.reset();
595 mpRegionBand = std::move(pNew);
596 return true;
599 bool vcl::Region::Intersect( const tools::Rectangle& rRect )
601 if ( rRect.IsEmpty() )
603 // empty rectangle will create empty region
604 SetEmpty();
605 return true;
608 if(IsNull())
610 // null region (everything) intersect with rect will give rect
611 *this = rRect;
612 return true;
615 if(IsEmpty())
617 // no content, cannot get more empty
618 return true;
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(),
629 basegfx::B2DRange(
630 rRect.Left(),
631 rRect.Top(),
632 rRect.Right() + 1,
633 rRect.Bottom() + 1),
634 true,
635 false));
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
648 aPoly.Clip(rRect);
650 mpB2DPolyPolygon.reset();
651 mpPolyPolygon.reset(aPoly.Count() ? new tools::PolyPolygon(aPoly) : nullptr);
652 mpRegionBand.reset();
655 return true;
658 // only region band mode possibility left here or null/empty
659 const RegionBand* pCurrent = getRegionBand();
661 if(!pCurrent)
663 // region is empty -> nothing to do!
664 return true;
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);
678 // process intersect
679 pNew->Intersect(nLeft, nTop, nRight, nBottom);
681 // cleanup
682 if(!pNew->OptimizeBandList())
684 pNew.reset();
687 mpRegionBand = std::move(pNew);
688 return true;
691 bool vcl::Region::Exclude( const tools::Rectangle& rRect )
693 if ( rRect.IsEmpty() )
695 // excluding nothing will do no change
696 return true;
699 if(IsEmpty())
701 // cannot exclude from empty, done
702 return true;
705 if(IsNull())
707 // error; cannot exclude from null region since this is not representable
708 // in the data
709 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
710 return true;
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
723 return true;
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);
735 return true;
738 // only region band mode possibility left here or null/empty
739 const RegionBand* pCurrent = getRegionBand();
741 if(!pCurrent)
743 // empty? -> done!
744 return true;
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);
758 // process exclude
759 pNew->Exclude(nLeft, nTop, nRight, nBottom);
761 // cleanup
762 if(!pNew->OptimizeBandList())
764 pNew.reset();
767 mpRegionBand = std::move(pNew);
768 return true;
771 bool vcl::Region::XOr( const tools::Rectangle& rRect )
773 if ( rRect.IsEmpty() )
775 // empty rectangle will not change local content
776 return true;
779 if(IsEmpty())
781 // rRect will be the xored-form (local off, rect on)
782 *this = rRect;
783 return true;
786 if(IsNull())
788 // error; cannot exclude from null region since this is not representable
789 // in the data
790 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
791 return true;
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
804 *this = rRect;
805 return true;
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);
817 return true;
820 // only region band mode possibility left here or null/empty
821 const RegionBand* pCurrent = getRegionBand();
823 if(!pCurrent)
825 // rRect will be the xored-form (local off, rect on)
826 *this = rRect;
827 return true;
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);
842 // process xor
843 pNew->XOr(nLeft, nTop, nRight, nBottom);
845 // cleanup
846 if(!pNew->OptimizeBandList())
848 pNew.reset();
851 mpRegionBand = std::move(pNew);
852 return true;
855 bool vcl::Region::Union( const vcl::Region& rRegion )
857 if(rRegion.IsEmpty())
859 // no extension at all
860 return true;
863 if(rRegion.IsNull())
865 // extending with null region -> null region
866 *this = vcl::Region(true);
867 return true;
870 if(IsEmpty())
872 // local is empty, union will give source region
873 *this = rRegion;
874 return true;
877 if(IsNull())
879 // already fully expanded (is null region), cannot be extended
880 return true;
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
893 *this = rRegion;
894 return true;
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 );
905 return true;
908 // only region band mode possibility left here or null/empty
909 const RegionBand* pCurrent = getRegionBand();
911 if(!pCurrent)
913 // local is empty, union will give source region
914 *this = rRegion;
915 return true;
918 const RegionBand* pSource = rRegion.getRegionBand();
920 if(!pSource)
922 // no extension at all
923 return true;
926 // prepare source and target
927 std::unique_ptr<RegionBand> pNew( o3tl::make_unique<RegionBand>(*pCurrent));
929 // union with source
930 pNew->Union(*pSource);
932 // cleanup
933 if(!pNew->OptimizeBandList())
935 pNew.reset();
938 mpRegionBand = std::move(pNew);
939 return true;
942 bool vcl::Region::Intersect( const vcl::Region& rRegion )
944 // same instance data? -> nothing to do!
945 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
947 return true;
950 if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
952 return true;
955 if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
957 return true;
960 if(rRegion.IsNull())
962 // source region is null-region, intersect will not change local region
963 return true;
966 if(IsNull())
968 // when local region is null-region, intersect will be equal to source
969 *this = rRegion;
970 return true;
973 if(rRegion.IsEmpty())
975 // source region is empty, intersection will always be empty
976 SetEmpty();
977 return true;
980 if(IsEmpty())
982 // local region is empty, cannot get more empty than that. Nothing to do
983 return true;
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
994 return true;
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
1003 SetEmpty();
1004 return true;
1007 const basegfx::B2DPolyPolygon aClip(
1008 basegfx::tools::clipPolyPolygonOnPolyPolygon(
1009 aOtherPolyPoly,
1010 aThisPolyPoly,
1011 true,
1012 false));
1013 *this = vcl::Region( aClip );
1014 return true;
1017 // only region band mode possibility left here or null/empty
1018 const RegionBand* pCurrent = getRegionBand();
1020 if(!pCurrent)
1022 // local region is empty, cannot get more empty than that. Nothing to do
1023 return true;
1026 const RegionBand* pSource = rRegion.getRegionBand();
1028 if(!pSource)
1030 // source region is empty, intersection will always be empty
1031 SetEmpty();
1032 return true;
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;
1043 else
1045 // prepare new regionBand
1046 std::unique_ptr<RegionBand> pNew( o3tl::make_unique<RegionBand>(*pCurrent));
1048 // intersect with source
1049 pNew->Intersect(*pSource);
1051 // cleanup
1052 if(!pNew->OptimizeBandList())
1054 pNew.reset();
1057 mpRegionBand = std::move(pNew);
1060 return true;
1063 bool vcl::Region::Exclude( const vcl::Region& rRegion )
1065 if ( rRegion.IsEmpty() )
1067 // excluding nothing will do no change
1068 return true;
1071 if ( rRegion.IsNull() )
1073 // excluding everything will create empty region
1074 SetEmpty();
1075 return true;
1078 if(IsEmpty())
1080 // cannot exclude from empty, done
1081 return true;
1084 if(IsNull())
1086 // error; cannot exclude from null region since this is not representable
1087 // in the data
1088 OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
1089 return true;
1092 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1094 // get this B2DPolyPolygon
1095 basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
1097 if(!aThisPolyPoly.count())
1099 // cannot exclude from empty, done
1100 return true;
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 );
1111 return true;
1114 // only region band mode possibility left here or null/empty
1115 const RegionBand* pCurrent = getRegionBand();
1117 if(!pCurrent)
1119 // cannot exclude from empty, done
1120 return true;
1123 const RegionBand* pSource = rRegion.getRegionBand();
1125 if(!pSource)
1127 // excluding nothing will do no change
1128 return true;
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));
1137 // cleanup
1138 if(!bSuccess)
1140 pNew.reset();
1143 mpRegionBand = std::move(pNew);
1144 return true;
1147 bool vcl::Region::XOr( const vcl::Region& rRegion )
1149 if ( rRegion.IsEmpty() )
1151 // empty region will not change local content
1152 return true;
1155 if ( rRegion.IsNull() )
1157 // error; cannot exclude null region from local since this is not representable
1158 // in the data
1159 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1160 return true;
1163 if(IsEmpty())
1165 // rRect will be the xored-form (local off, rect on)
1166 *this = rRegion;
1167 return true;
1170 if(IsNull())
1172 // error: cannot exclude from null region since this is not representable
1173 // in the data
1174 OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1175 return false;
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)
1186 *this = rRegion;
1187 return true;
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 );
1198 return true;
1201 // only region band mode possibility left here or null/empty
1202 const RegionBand* pCurrent = getRegionBand();
1204 if(!pCurrent)
1206 // rRect will be the xored-form (local off, rect on)
1207 *this = rRegion;
1208 return true;
1211 const RegionBand* pSource = rRegion.getRegionBand();
1213 if(!pSource)
1215 // empty region will not change local content
1216 return true;
1219 // prepare source and target
1220 std::unique_ptr<RegionBand> pNew( o3tl::make_unique<RegionBand>(*pCurrent));
1222 // union with source
1223 pNew->XOr(*pSource);
1225 // cleanup
1226 if(!pNew->OptimizeBandList())
1228 pNew.reset();
1231 mpRegionBand = std::move(pNew);
1233 return true;
1236 tools::Rectangle vcl::Region::GetBoundRect() const
1238 if(IsEmpty())
1240 // no internal data? -> region is empty!
1241 return tools::Rectangle();
1244 if(IsNull())
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();
1261 else
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();
1275 if(getRegionBand())
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();
1299 if(getRegionBand())
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();
1327 if(getRegionBand())
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
1360 if(IsEmpty())
1362 // no point can be in empty region
1363 return false;
1366 if(IsNull())
1368 // all points are inside null-region
1369 return true;
1372 // Too expensive (?)
1373 //if(mpImplRegion->getRegionPolyPoly())
1375 // return mpImplRegion->getRegionPolyPoly()->IsInside( rPoint );
1378 // ensure RegionBand existence
1379 const RegionBand* pRegionBand = GetAsRegionBand();
1381 if(pRegionBand)
1383 return pRegionBand->IsInside(rPoint);
1386 return false;
1389 bool vcl::Region::IsOver( const tools::Rectangle& rRect ) const
1391 if(IsEmpty())
1393 // nothing can be over something empty
1394 return false;
1397 if(IsNull())
1399 // everything is over null region
1400 return true;
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() )
1416 return false;
1418 if( getB2DPolyPolygon() )
1419 return basegfx::tools::isRectangle( *getB2DPolyPolygon() );
1421 if( getPolyPolygon() )
1422 return getPolyPolygon()->IsRect();
1424 if( getRegionBand() )
1425 return (getRegionBand()->getRectangleCount() == 1);
1427 return false;
1430 void vcl::Region::SetNull()
1432 // reset all content
1433 mpB2DPolyPolygon.reset();
1434 mpPolyPolygon.reset();
1435 mpRegionBand.reset();
1436 mbIsNull = true;
1439 void vcl::Region::SetEmpty()
1441 // reset all content
1442 mpB2DPolyPolygon.reset();
1443 mpPolyPolygon.reset();
1444 mpRegionBand.reset();
1445 mbIsNull = false;
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;
1456 return *this;
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;
1467 return *this;
1470 Region& vcl::Region::operator=( const tools::Rectangle& rRect )
1472 mpB2DPolyPolygon.reset();
1473 mpPolyPolygon.reset();
1474 mpRegionBand.reset(rRect.IsEmpty() ? nullptr : new RegionBand(rRect));
1475 mbIsNull = false;
1477 return *this;
1480 bool vcl::Region::operator==( const vcl::Region& rRegion ) const
1482 if(IsNull() && rRegion.IsNull())
1484 // both are null region
1485 return true;
1488 if(IsEmpty() && rRegion.IsEmpty())
1490 // both are empty
1491 return true;
1494 if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
1496 // same instance data? -> equal
1497 return true;
1500 if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
1502 // same instance data? -> equal
1503 return true;
1506 if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
1508 // same instance data? -> equal
1509 return true;
1512 if(IsNull() || IsEmpty())
1514 return false;
1517 if(rRegion.IsNull() || rRegion.IsEmpty())
1519 return false;
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
1536 GetAsPolyPolygon();
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,
1544 // but better check
1545 if(rRegion.getRegionBand() && getRegionBand())
1547 return *rRegion.getRegionBand() == *getRegionBand();
1550 // should not happen, but better deny equality
1551 return false;
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
1561 rRegion.SetEmpty();
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)
1574 case REGION_NULL:
1576 rRegion.SetNull();
1577 break;
1580 case REGION_EMPTY:
1582 rRegion.SetEmpty();
1583 break;
1586 default:
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 );
1598 if(bHasPolyPolygon)
1600 tools::PolyPolygon* pNewPoly = new tools::PolyPolygon();
1601 ReadPolyPolygon( rIStrm, *pNewPoly );
1602 rRegion.mpPolyPolygon.reset(pNewPoly);
1606 break;
1610 return rIStrm;
1613 SvStream& WriteRegion( SvStream& rOStrm, const vcl::Region& rRegion )
1615 const sal_uInt16 nVersion(2);
1616 VersionCompat aCompat(rOStrm, StreamMode::WRITE, nVersion);
1618 // put version
1619 rOStrm.WriteUInt16( nVersion );
1621 // put type
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 (!)");
1629 bEmpty = true;
1632 if(!bEmpty && rRegion.getPolyPolygon() && 0 == rRegion.getPolyPolygon()->Count())
1634 OSL_ENSURE(false, "Region with empty PolyPolygon, should not be created (!)");
1635 bEmpty = true;
1638 if(bEmpty)
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 );
1653 // get RegionBand
1654 const RegionBand* pRegionBand = rRegion.getRegionBand();
1656 if(pRegionBand)
1658 pRegionBand->save(rOStrm);
1660 else
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 );
1673 if(bHasPolyPolygon)
1675 // #i105373#
1676 tools::PolyPolygon aNoCurvePolyPolygon;
1677 rRegion.GetAsPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon);
1679 WritePolyPolygon( rOStrm, aNoCurvePolyPolygon );
1682 return rOStrm;
1685 void vcl::Region::GetRegionRectangles(RectangleVector& rTarget) const
1687 // clear returnvalues
1688 rTarget.clear();
1690 // ensure RegionBand existence
1691 const RegionBand* pRegionBand = GetAsRegionBand();
1693 if(pRegionBand)
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) ) )
1712 bIsRect = true;
1714 if( pRectOut )
1716 long nSwap;
1718 if( nX2 < nX1 )
1720 nSwap = nX2;
1721 nX2 = nX1;
1722 nX1 = nSwap;
1725 if( nY2 < nY1 )
1727 nSwap = nY2;
1728 nY2 = nY1;
1729 nY1 = nSwap;
1732 if( nX2 != nX1 )
1734 nX2--;
1737 if( nY2 != nY1 )
1739 nY2--;
1742 pRectOut->Left() = nX1;
1743 pRectOut->Right() = nX2;
1744 pRectOut->Top() = nY1;
1745 pRectOut->Bottom() = nY2;
1750 return bIsRect;
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 ) )
1769 nPolygonRects++;
1771 else
1773 nPolygonPolygons++;
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 );
1793 else
1795 aResult.XOr( vcl::Region(rPoly) );
1799 return aResult;
1802 } /* namespace vcl */
1804 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */