xref: /aoo41x/main/vcl/source/gdi/region.cxx (revision d8ed516e)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_vcl.hxx"
24 
25 #include <limits.h>
26 #include <tools/vcompat.hxx>
27 #include <tools/stream.hxx>
28 #include <vcl/region.hxx>
29 #include <regionband.hxx>
30 #include <basegfx/matrix/b2dhommatrix.hxx>
31 #include <basegfx/polygon/b2dpolypolygontools.hxx>
32 #include <basegfx/polygon/b2dpolygontools.hxx>
33 #include <basegfx/polygon/b2dpolygonclipper.hxx>
34 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
35 #include <basegfx/range/b2drange.hxx>
36 #include <basegfx/matrix/b2dhommatrixtools.hxx>
37 
38 //////////////////////////////////////////////////////////////////////////////
39 
40 DBG_NAME( Region )
41 DBG_NAMEEX( Polygon )
42 DBG_NAMEEX( PolyPolygon )
43 
44 //////////////////////////////////////////////////////////////////////////////
45 
46 namespace
47 {
48     /** Return <TRUE/> when the given polygon is rectiliner and oriented so that
49         all sides are either horizontal or vertical.
50     */
ImplIsPolygonRectilinear(const PolyPolygon & rPolyPoly)51     bool ImplIsPolygonRectilinear (const PolyPolygon& rPolyPoly)
52     {
53         // Iterate over all polygons.
54         const sal_uInt16 nPolyCount = rPolyPoly.Count();
55         for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly)
56         {
57             const Polygon&	aPoly = rPolyPoly.GetObject(nPoly);
58 
59             // Iterate over all edges of the current polygon.
60             const sal_uInt16 nSize = aPoly.GetSize();
61 
62             if (nSize < 2)
63                 continue;
64             Point aPoint (aPoly.GetPoint(0));
65             const Point aLastPoint (aPoint);
66             for (sal_uInt16 nPoint = 1; nPoint < nSize; ++nPoint)
67             {
68                 const Point aNextPoint (aPoly.GetPoint(nPoint));
69                 // When there is at least one edge that is neither vertical nor
70                 // horizontal then the entire polygon is not rectilinear (and
71                 // oriented along primary axes.)
72                 if (aPoint.X() != aNextPoint.X() && aPoint.Y() != aNextPoint.Y())
73                     return false;
74 
75                 aPoint = aNextPoint;
76             }
77             // Compare closing edge.
78             if (aLastPoint.X() != aPoint.X() && aLastPoint.Y() != aPoint.Y())
79                 return false;
80         }
81         return true;
82     }
83 
84     /** Convert a rectilinear polygon (that is oriented along the primary axes)
85         to a list of bands.  For this special form of polygon we can use an
86         optimization that prevents the creation of one band per y value.
87         However, it still is possible that some temporary bands are created that
88         later can be optimized away.
89         @param rPolyPolygon
90             A set of zero, one, or more polygons, nested or not, that are
91             converted into a list of bands.
92         @return
93             A new RegionBand object is returned that contains the bands that
94             represent the given poly-polygon.
95     */
ImplRectilinearPolygonToBands(const PolyPolygon & rPolyPoly)96     RegionBand* ImplRectilinearPolygonToBands(const PolyPolygon& rPolyPoly)
97     {
98         OSL_ASSERT(ImplIsPolygonRectilinear (rPolyPoly));
99 
100         // Create a new RegionBand object as container of the bands.
101         RegionBand* pRegionBand = new RegionBand();
102         long nLineId = 0L;
103 
104         // Iterate over all polygons.
105 	    const sal_uInt16 nPolyCount = rPolyPoly.Count();
106         for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly)
107         {
108             const Polygon&	aPoly = rPolyPoly.GetObject(nPoly);
109 
110             // Iterate over all edges of the current polygon.
111             const sal_uInt16 nSize = aPoly.GetSize();
112             if (nSize < 2)
113                 continue;
114             // Avoid fetching every point twice (each point is the start point
115             // of one and the end point of another edge.)
116             Point aStart (aPoly.GetPoint(0));
117             Point aEnd;
118             for (sal_uInt16 nPoint = 1; nPoint <= nSize; ++nPoint, aStart=aEnd)
119             {
120                 // We take the implicit closing edge into account by mapping
121                 // index nSize to 0.
122                 aEnd = aPoly.GetPoint(nPoint%nSize);
123                 if (aStart.Y() == aEnd.Y())
124                 {
125                     // Horizontal lines are ignored.
126                     continue;
127                 }
128 
129                 // At this point the line has to be vertical.
130                 OSL_ASSERT(aStart.X() == aEnd.X());
131 
132                 // Sort y-coordinates to simplify the algorithm and store the
133                 // direction seperately.  The direction is calculated as it is
134                 // in other places (but seems to be the wrong way.)
135                 const long nTop (::std::min(aStart.Y(), aEnd.Y()));
136                 const long nBottom (::std::max(aStart.Y(), aEnd.Y()));
137                 const LineType eLineType (aStart.Y() > aEnd.Y() ? LINE_DESCENDING : LINE_ASCENDING);
138 
139                 // Make sure that the current line is covered by bands.
140                 pRegionBand->ImplAddMissingBands(nTop,nBottom);
141 
142                 // Find top-most band that may contain nTop.
143                 ImplRegionBand* pBand = pRegionBand->ImplGetFirstRegionBand();
144                 while (pBand!=NULL && pBand->mnYBottom < nTop)
145                     pBand = pBand->mpNextBand;
146                 ImplRegionBand* pTopBand = pBand;
147                 // If necessary split the band at nTop so that nTop is contained
148                 // in the lower band.
149                 if (pBand!=NULL
150                        // Prevent the current band from becoming 0 pixel high
151                     && pBand->mnYTop<nTop
152                        // this allows the lowest pixel of the band to be split off
153                     && pBand->mnYBottom>=nTop
154                        // do not split a band that is just one pixel high
155                     && pBand->mnYTop<pBand->mnYBottom)
156                 {
157                     // Split the top band.
158                     pTopBand = pBand->SplitBand(nTop);
159                 }
160 
161                 // Advance to band that may contain nBottom.
162                 while (pBand!=NULL && pBand->mnYBottom < nBottom)
163                     pBand = pBand->mpNextBand;
164                 // The lowest band may have to be split at nBottom so that
165                 // nBottom itself remains in the upper band.
166                 if (pBand!=NULL
167                        // allow the current band becoming 1 pixel high
168                     && pBand->mnYTop<=nBottom
169                        // prevent splitting off a band that is 0 pixel high
170                     && pBand->mnYBottom>nBottom
171                        // do not split a band that is just one pixel high
172                     && pBand->mnYTop<pBand->mnYBottom)
173                 {
174                     // Split the bottom band.
175                     pBand->SplitBand(nBottom+1);
176                 }
177 
178                 // Note that we remember the top band (in pTopBand) but not the
179                 // bottom band.  The later can be determined by comparing y
180                 // coordinates.
181 
182                 // Add the x-value as point to all bands in the nTop->nBottom range.
183                 for (pBand=pTopBand; pBand!=NULL&&pBand->mnYTop<=nBottom; pBand=pBand->mpNextBand)
184                     pBand->InsertPoint(aStart.X(), nLineId++, true, eLineType);
185             }
186         }
187 
188         return pRegionBand;
189     }
190 
191     /** Convert a general polygon (one for which ImplIsPolygonRectilinear()
192         returns <FALSE/>) to bands.
193     */
ImplGeneralPolygonToBands(const PolyPolygon & rPolyPoly,const Rectangle & rPolygonBoundingBox)194     RegionBand* ImplGeneralPolygonToBands(const PolyPolygon& rPolyPoly, const Rectangle& rPolygonBoundingBox)
195     {
196         long nLineID = 0L;
197 
198         // initialisation and creation of Bands
199         RegionBand* pRegionBand = new RegionBand();
200         pRegionBand->CreateBandRange(rPolygonBoundingBox.Top(), rPolygonBoundingBox.Bottom());
201 
202         // insert polygons
203 	    const sal_uInt16 nPolyCount = rPolyPoly.Count();
204 
205         for ( sal_uInt16 nPoly = 0; nPoly < nPolyCount; nPoly++ )
206         {
207             // get reference to current polygon
208             const Polygon&	aPoly = rPolyPoly.GetObject( nPoly );
209             const sal_uInt16	nSize = aPoly.GetSize();
210 
211             // not enough points ( <= 2 )? -> nothing to do!
212             if ( nSize <= 2 )
213                 continue;
214 
215             // band the polygon
216             for ( sal_uInt16 nPoint = 1; nPoint < nSize; nPoint++ )
217             {
218                 pRegionBand->InsertLine( aPoly.GetPoint(nPoint-1), aPoly.GetPoint(nPoint), nLineID++ );
219             }
220 
221             // close polygon with line from first point to last point, if neccesary
222             const Point rLastPoint = aPoly.GetPoint(nSize-1);
223             const Point rFirstPoint = aPoly.GetPoint(0);
224 
225             if ( rLastPoint != rFirstPoint )
226             {
227                 pRegionBand->InsertLine( rLastPoint, rFirstPoint, nLineID++ );
228             }
229         }
230 
231         return pRegionBand;
232     }
233 } // end of anonymous namespace
234 
235 //////////////////////////////////////////////////////////////////////////////
236 
IsEmpty() const237 bool Region::IsEmpty() const
238 {
239     return !mbIsNull && !mpB2DPolyPolygon.get() && !mpPolyPolygon.get() && !mpRegionBand.get();
240 }
241 
IsNull() const242 bool Region::IsNull() const
243 {
244     return mbIsNull;
245 }
246 
ImplCreateRegionBandFromPolyPolygon(const PolyPolygon & rPolyPolygon)247 RegionBand* ImplCreateRegionBandFromPolyPolygon(const PolyPolygon& rPolyPolygon)
248 {
249     RegionBand* pRetval = 0;
250 
251     if(rPolyPolygon.Count())
252     {
253         // ensure to subdivide when bezier segemnts are used, it's going to
254         // be expanded to rectangles
255         PolyPolygon aPolyPolygon;
256 
257         rPolyPolygon.AdaptiveSubdivide(aPolyPolygon);
258 
259         if(aPolyPolygon.Count())
260         {
261             const Rectangle aRect(aPolyPolygon.GetBoundRect());
262 
263             if(!aRect.IsEmpty())
264             {
265                 if(ImplIsPolygonRectilinear(aPolyPolygon))
266                 {
267                     // For rectilinear polygons there is an optimized band conversion.
268                     pRetval = ImplRectilinearPolygonToBands(aPolyPolygon);
269                 }
270                 else
271                 {
272                     pRetval = ImplGeneralPolygonToBands(aPolyPolygon, aRect);
273                 }
274 
275                 // Convert points into seps.
276                 if(pRetval)
277                 {
278                     pRetval->processPoints();
279 
280                     // Optimize list of bands.  Adjacent bands with identical lists
281                     // of seps are joined.
282                     if(!pRetval->OptimizeBandList())
283                     {
284                         delete pRetval;
285                         pRetval = 0;
286                     }
287                 }
288             }
289         }
290     }
291 
292     return pRetval;
293 }
294 
ImplCreatePolyPolygonFromRegionBand() const295 PolyPolygon Region::ImplCreatePolyPolygonFromRegionBand() const
296 {
297     PolyPolygon aRetval;
298 
299     if(getRegionBand())
300     {
301         RectangleVector aRectangles;
302         GetRegionRectangles(aRectangles);
303 
304         for(RectangleVector::const_iterator aRectIter(aRectangles.begin()); aRectIter != aRectangles.end(); aRectIter++)
305         {
306             aRetval.Insert(Polygon(*aRectIter));
307         }
308     }
309     else
310     {
311         OSL_ENSURE(false, "Called with no local RegionBand (!)");
312     }
313 
314     return aRetval;
315 }
316 
ImplCreateB2DPolyPolygonFromRegionBand() const317 basegfx::B2DPolyPolygon Region::ImplCreateB2DPolyPolygonFromRegionBand() const
318 {
319     PolyPolygon aPoly(ImplCreatePolyPolygonFromRegionBand());
320 
321     return aPoly.getB2DPolyPolygon();
322 }
323 
Region(bool bIsNull)324 Region::Region(bool bIsNull)
325 :   mpB2DPolyPolygon(),
326     mpPolyPolygon(),
327     mpRegionBand(),
328     mbIsNull(bIsNull)
329 {
330 }
331 
Region(const Rectangle & rRect)332 Region::Region(const Rectangle& rRect)
333 :   mpB2DPolyPolygon(),
334     mpPolyPolygon(),
335     mpRegionBand(),
336     mbIsNull(false)
337 {
338     mpRegionBand.reset(rRect.IsEmpty() ? 0 : new RegionBand(rRect));
339 }
340 
Region(const Polygon & rPolygon)341 Region::Region(const Polygon& rPolygon)
342 :   mpB2DPolyPolygon(),
343     mpPolyPolygon(),
344     mpRegionBand(),
345     mbIsNull(false)
346 {
347 	DBG_CHKOBJ( &rPolygon, Polygon, NULL );
348 
349     if(rPolygon.GetSize())
350     {
351     	ImplCreatePolyPolyRegion(rPolygon);
352     }
353 }
354 
Region(const PolyPolygon & rPolyPoly)355 Region::Region(const PolyPolygon& rPolyPoly)
356 :   mpB2DPolyPolygon(),
357     mpPolyPolygon(),
358     mpRegionBand(),
359     mbIsNull(false)
360 {
361 	DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL );
362 
363     if(rPolyPoly.Count())
364     {
365     	ImplCreatePolyPolyRegion(rPolyPoly);
366     }
367 }
368 
Region(const basegfx::B2DPolyPolygon & rPolyPoly)369 Region::Region(const basegfx::B2DPolyPolygon& rPolyPoly)
370 :   mpB2DPolyPolygon(),
371     mpPolyPolygon(),
372     mpRegionBand(),
373     mbIsNull(false)
374 {
375 	DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL );
376 
377     if(rPolyPoly.count())
378     {
379     	ImplCreatePolyPolyRegion(rPolyPoly);
380     }
381 }
382 
Region(const Region & rRegion)383 Region::Region(const Region& rRegion)
384 :   mpB2DPolyPolygon(rRegion.mpB2DPolyPolygon),
385     mpPolyPolygon(rRegion.mpPolyPolygon),
386     mpRegionBand(rRegion.mpRegionBand),
387     mbIsNull(rRegion.mbIsNull)
388 {
389 }
390 
~Region()391 Region::~Region()
392 {
393 }
394 
ImplCreatePolyPolyRegion(const PolyPolygon & rPolyPoly)395 void Region::ImplCreatePolyPolyRegion( const PolyPolygon& rPolyPoly )
396 {
397 	const sal_uInt16 nPolyCount = rPolyPoly.Count();
398 
399     if(nPolyCount)
400 	{
401 		// polypolygon empty? -> empty region
402 		const Rectangle aRect(rPolyPoly.GetBoundRect());
403 
404 		if(!aRect.IsEmpty())
405 		{
406 			// width OR height == 1 ? => Rectangular region
407 			if((1 == aRect.GetWidth()) || (1 == aRect.GetHeight()) || rPolyPoly.IsRect())
408             {
409                 mpRegionBand.reset(new RegionBand(aRect));
410             }
411 			else
412             {
413                 mpPolyPolygon.reset(new PolyPolygon(rPolyPoly));
414             }
415 
416             mbIsNull = false;
417 		}
418 	}
419 }
420 
ImplCreatePolyPolyRegion(const basegfx::B2DPolyPolygon & rPolyPoly)421 void Region::ImplCreatePolyPolyRegion( const basegfx::B2DPolyPolygon& rPolyPoly )
422 {
423     if(rPolyPoly.count() && !rPolyPoly.getB2DRange().isEmpty())
424     {
425         mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(rPolyPoly));
426         mbIsNull = false;
427     }
428 }
429 
Move(long nHorzMove,long nVertMove)430 void Region::Move( long nHorzMove, long nVertMove )
431 {
432 	if(IsNull() || IsEmpty())
433     {
434         // empty or null need no move
435 		return;
436     }
437 
438     if(!nHorzMove && !nVertMove)
439     {
440         // no move defined
441         return;
442     }
443 
444     if(getB2DPolyPolygon())
445     {
446         basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon());
447 
448         aPoly.transform(basegfx::tools::createTranslateB2DHomMatrix(nHorzMove, nVertMove));
449         mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : 0);
450         mpPolyPolygon.reset();
451         mpRegionBand.reset();
452     }
453     else if(getPolyPolygon())
454     {
455         PolyPolygon aPoly(*getPolyPolygon());
456 
457     	aPoly.Move(nHorzMove, nVertMove);
458         mpB2DPolyPolygon.reset();
459         mpPolyPolygon.reset(aPoly.Count() ? new PolyPolygon(aPoly) : 0);
460         mpRegionBand.reset();
461     }
462     else if(getRegionBand())
463     {
464         RegionBand* pNew = new RegionBand(*getRegionBand());
465 
466         pNew->Move(nHorzMove, nVertMove);
467         mpB2DPolyPolygon.reset();
468         mpPolyPolygon.reset();
469         mpRegionBand.reset(pNew);
470     }
471     else
472     {
473         OSL_ENSURE(false, "Region::Move error: impossible combination (!)");
474     }
475 }
476 
Scale(double fScaleX,double fScaleY)477 void Region::Scale( double fScaleX, double fScaleY )
478 {
479 	if(IsNull() || IsEmpty())
480     {
481         // empty or null need no scale
482 		return;
483     }
484 
485     if(basegfx::fTools::equalZero(fScaleX) && basegfx::fTools::equalZero(fScaleY))
486     {
487         // no scale defined
488         return;
489     }
490 
491     if(getB2DPolyPolygon())
492     {
493         basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon());
494 
495         aPoly.transform(basegfx::tools::createScaleB2DHomMatrix(fScaleX, fScaleY));
496         mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : 0);
497         mpPolyPolygon.reset();
498         mpRegionBand.reset();
499     }
500     else if(getPolyPolygon())
501     {
502         PolyPolygon aPoly(*getPolyPolygon());
503 
504         aPoly.Scale(fScaleX, fScaleY);
505         mpB2DPolyPolygon.reset();
506         mpPolyPolygon.reset(aPoly.Count() ? new PolyPolygon(aPoly) : 0);
507         mpRegionBand.reset();
508     }
509     else if(getRegionBand())
510     {
511         RegionBand* pNew = new RegionBand(*getRegionBand());
512 
513         pNew->Scale(fScaleX, fScaleY);
514         mpB2DPolyPolygon.reset();
515         mpPolyPolygon.reset();
516         mpRegionBand.reset(pNew);
517     }
518     else
519     {
520         OSL_ENSURE(false, "Region::Scale error: impossible combination (!)");
521     }
522 }
523 
Union(const Rectangle & rRect)524 bool Region::Union( const Rectangle& rRect )
525 {
526     if(rRect.IsEmpty())
527     {
528         // empty rectangle will not expand the existing union, nothing to do
529         return true;
530     }
531 
532     if(IsEmpty())
533     {
534         // no local data, the union will be equal to source. Create using rectangle
535         *this = rRect;
536         return true;
537     }
538 
539     if(HasPolyPolygonOrB2DPolyPolygon())
540     {
541         // get this B2DPolyPolygon, solve on polygon base
542         basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
543 
544         aThisPolyPoly = basegfx::tools::prepareForPolygonOperation(aThisPolyPoly);
545 
546         if(!aThisPolyPoly.count())
547         {
548             // no local polygon, use the rectangle as new region
549             *this = rRect;
550         }
551         else
552         {
553             // get the other B2DPolyPolygon and use logical Or-Operation
554             const basegfx::B2DPolygon aRectPoly(
555                 basegfx::tools::createPolygonFromRect(
556                     basegfx::B2DRectangle(
557                         rRect.Left(),
558                         rRect.Top(),
559                         rRect.Right(),
560                         rRect.Bottom())));
561             const basegfx::B2DPolyPolygon aClip(
562                 basegfx::tools::solvePolygonOperationOr(
563                     aThisPolyPoly,
564                     basegfx::B2DPolyPolygon(aRectPoly)));
565             *this = Region(aClip);
566         }
567 
568         return true;
569     }
570 
571     // only region band mode possibility left here or null/empty
572     const RegionBand* pCurrent = getRegionBand();
573 
574     if(!pCurrent)
575     {
576         // no region band, create using the rectangle
577         *this = rRect;
578         return true;
579     }
580 
581     RegionBand* pNew = new RegionBand(*pCurrent);
582 
583     // get justified rectangle
584     const long nLeft(std::min(rRect.Left(), rRect.Right()));
585     const long nTop(std::min(rRect.Top(), rRect.Bottom()));
586     const long nRight(std::max(rRect.Left(), rRect.Right()));
587     const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
588 
589     // insert bands if the boundaries are not allready in the list
590     pNew->InsertBands(nTop, nBottom);
591 
592     // process union
593     pNew->Union(nLeft, nTop, nRight, nBottom);
594 
595     // cleanup
596     if(!pNew->OptimizeBandList())
597     {
598         delete pNew;
599         pNew = 0;
600     }
601 
602     mpRegionBand.reset(pNew);
603     return true;
604 }
605 
Intersect(const Rectangle & rRect)606 bool Region::Intersect( const Rectangle& rRect )
607 {
608 	if ( rRect.IsEmpty() )
609 	{
610     	// empty rectangle will create empty region
611         SetEmpty();
612 		return true;
613 	}
614 
615     if(IsNull())
616     {
617         // null region (everything) intersect with rect will give rect
618         *this = rRect;
619         return true;
620     }
621 
622     if(IsEmpty())
623     {
624         // no content, cannot get more empty
625         return true;
626     }
627 
628     if(HasPolyPolygonOrB2DPolyPolygon())
629     {
630         // if polygon data prefer double precision, the other will be lost (if buffered)
631         if(getB2DPolyPolygon())
632         {
633             const basegfx::B2DPolyPolygon aPoly(
634                 basegfx::tools::clipPolyPolygonOnRange(
635                     *getB2DPolyPolygon(),
636                     basegfx::B2DRange(
637                         rRect.Left(),
638                         rRect.Top(),
639                         rRect.Right() + 1,
640                         rRect.Bottom() + 1),
641                     true,
642                     false));
643 
644             mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : 0);
645             mpPolyPolygon.reset();
646             mpRegionBand.reset();
647         }
648         else // if(getPolyPolygon())
649         {
650             PolyPolygon aPoly(*getPolyPolygon());
651 
652             // use the PolyPolygon::Clip method for rectangles, this is
653             // fairly simple (does not even use GPC) and saves us from
654             // unnecessary banding
655             aPoly.Clip(rRect);
656 
657             mpB2DPolyPolygon.reset();
658             mpPolyPolygon.reset(aPoly.Count() ? new PolyPolygon(aPoly) : 0);
659             mpRegionBand.reset();
660         }
661 
662         return true;
663     }
664 
665     // only region band mode possibility left here or null/empty
666     const RegionBand* pCurrent = getRegionBand();
667 
668     if(!pCurrent)
669     {
670         // region is empty -> nothing to do!
671         return true;
672     }
673 
674     RegionBand* pNew = new RegionBand(*pCurrent);
675 
676     // get justified rectangle
677     const long nLeft(std::min(rRect.Left(), rRect.Right()));
678     const long nTop(std::min(rRect.Top(), rRect.Bottom()));
679     const long nRight(std::max(rRect.Left(), rRect.Right()));
680     const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
681 
682     // insert bands if the boundaries are not allready in the list
683     pNew->InsertBands(nTop, nBottom);
684 
685     // process intersect
686     pNew->Intersect(nLeft, nTop, nRight, nBottom);
687 
688     // cleanup
689     if(!pNew->OptimizeBandList())
690     {
691         delete pNew;
692         pNew = 0;
693     }
694 
695     mpRegionBand.reset(pNew);
696     return true;
697 }
698 
Exclude(const Rectangle & rRect)699 bool Region::Exclude( const Rectangle& rRect )
700 {
701 	if ( rRect.IsEmpty() )
702     {
703     	// excluding nothing will do no change
704 		return true;
705     }
706 
707     if(IsEmpty())
708     {
709         // cannot exclude from empty, done
710         return true;
711     }
712 
713     if(IsNull())
714     {
715         // error; cannnot exclude from null region since this is not representable
716         // in the data
717         OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
718         return true;
719     }
720 
721 	if( HasPolyPolygonOrB2DPolyPolygon() )
722 	{
723 	    // get this B2DPolyPolygon
724 	    basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
725 
726         aThisPolyPoly = basegfx::tools::prepareForPolygonOperation(aThisPolyPoly);
727 
728 	    if(!aThisPolyPoly.count())
729         {
730             // when local polygon is empty, nothing can be excluded
731 	        return true;
732         }
733 
734 	    // get the other B2DPolyPolygon
735 	    const basegfx::B2DPolygon aRectPoly(
736             basegfx::tools::createPolygonFromRect(
737                 basegfx::B2DRectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom())));
738 	    const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly);
739 	    const basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationDiff(aThisPolyPoly, aOtherPolyPoly);
740 
741         *this = Region(aClip);
742 
743 	    return true;
744 	}
745 
746     // only region band mode possibility left here or null/empty
747     const RegionBand* pCurrent = getRegionBand();
748 
749     if(!pCurrent)
750     {
751         // empty? -> done!
752         return true;
753     }
754 
755     RegionBand* pNew = new RegionBand(*pCurrent);
756 
757     // get justified rectangle
758     const long nLeft(std::min(rRect.Left(), rRect.Right()));
759     const long nTop(std::min(rRect.Top(), rRect.Bottom()));
760     const long nRight(std::max(rRect.Left(), rRect.Right()));
761     const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
762 
763     // insert bands if the boundaries are not allready in the list
764     pNew->InsertBands(nTop, nBottom);
765 
766     // process exclude
767     pNew->Exclude(nLeft, nTop, nRight, nBottom);
768 
769     // cleanup
770     if(!pNew->OptimizeBandList())
771     {
772         delete pNew;
773         pNew = 0;
774     }
775 
776     mpRegionBand.reset(pNew);
777     return true;
778 }
779 
XOr(const Rectangle & rRect)780 bool Region::XOr( const Rectangle& rRect )
781 {
782 	if ( rRect.IsEmpty() )
783     {
784     	// empty rectangle will not change local content
785 		return true;
786     }
787 
788     if(IsEmpty())
789     {
790         // rRect will be the xored-form (local off, rect on)
791         *this = rRect;
792         return true;
793     }
794 
795     if(IsNull())
796     {
797         // error; cannnot exclude from null region since this is not representable
798         // in the data
799         OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
800         return true;
801     }
802 
803     if( HasPolyPolygonOrB2DPolyPolygon() )
804 	{
805 	    // get this B2DPolyPolygon
806 	    basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
807 
808         aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly );
809 
810 	    if(!aThisPolyPoly.count())
811 	    {
812             // no local content, XOr will be equal to rectangle
813 	        *this = rRect;
814 	        return true;
815 	    }
816 
817 	    // get the other B2DPolyPolygon
818 	    const basegfx::B2DPolygon aRectPoly(
819             basegfx::tools::createPolygonFromRect(
820                 basegfx::B2DRectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom())));
821 	    const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly);
822 	    const basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationXor(aThisPolyPoly, aOtherPolyPoly);
823 
824         *this = Region(aClip);
825 
826 	    return true;
827 	}
828 
829     // only region band mode possibility left here or null/empty
830     const RegionBand* pCurrent = getRegionBand();
831 
832     if(!pCurrent)
833     {
834         // rRect will be the xored-form (local off, rect on)
835         *this = rRect;
836         return true;
837     }
838 
839     // only region band mode possibility left here or null/empty
840     RegionBand* pNew = new RegionBand(*getRegionBand());
841 
842     // get justified rectangle
843     const long nLeft(std::min(rRect.Left(), rRect.Right()));
844     const long nTop(std::min(rRect.Top(), rRect.Bottom()));
845     const long nRight(std::max(rRect.Left(), rRect.Right()));
846     const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
847 
848     // insert bands if the boundaries are not allready in the list
849     pNew->InsertBands(nTop, nBottom);
850 
851     // process xor
852     pNew->XOr(nLeft, nTop, nRight, nBottom);
853 
854     // cleanup
855     if(!pNew->OptimizeBandList())
856     {
857         delete pNew;
858         pNew = 0;
859     }
860 
861     mpRegionBand.reset(pNew);
862     return true;
863 }
864 
Union(const Region & rRegion)865 bool Region::Union( const Region& rRegion )
866 {
867     if(rRegion.IsEmpty())
868     {
869         // no extension at all
870         return true;
871     }
872 
873     if(rRegion.IsNull())
874     {
875         // extending with null region -> null region
876         *this = Region(true);
877 	    return true;
878     }
879 
880     if(IsEmpty())
881     {
882         // local is empty, union will give source region
883         *this = rRegion;
884 	    return true;
885     }
886 
887     if(IsNull())
888     {
889         // already fully expanded (is null region), cannot be extended
890         return true;
891     }
892 
893 	if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
894 	{
895         // get this B2DPolyPolygon
896         basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
897 
898         aThisPolyPoly = basegfx::tools::prepareForPolygonOperation(aThisPolyPoly);
899 
900         if(!aThisPolyPoly.count())
901         {
902             // when no local content, union will be equal to rRegion
903             *this = rRegion;
904             return true;
905         }
906 
907         // get the other B2DPolyPolygon
908         basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
909         aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation(aOtherPolyPoly);
910 
911         // use logical OR operation
912         basegfx::B2DPolyPolygon aClip(basegfx::tools::solvePolygonOperationOr(aThisPolyPoly, aOtherPolyPoly));
913 
914         *this = Region( aClip );
915 	    return true;
916 	}
917 
918     // only region band mode possibility left here or null/empty
919     const RegionBand* pCurrent = getRegionBand();
920 
921     if(!pCurrent)
922     {
923         // local is empty, union will give source region
924         *this = rRegion;
925 	    return true;
926     }
927 
928     const RegionBand* pSource = rRegion.getRegionBand();
929 
930     if(!pSource)
931     {
932         // no extension at all
933         return true;
934     }
935 
936     // prepare source and target
937     RegionBand* pNew = new RegionBand(*pCurrent);
938 
939     // union with source
940     pNew->Union(*pSource);
941 
942     // cleanup
943     if(!pNew->OptimizeBandList())
944     {
945         delete pNew;
946         pNew = 0;
947     }
948 
949     mpRegionBand.reset(pNew);
950     return true;
951 }
952 
Intersect(const Region & rRegion)953 bool Region::Intersect( const Region& rRegion )
954 {
955     // same instance data? -> nothing to do!
956     if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
957     {
958         return true;
959     }
960 
961     if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
962     {
963         return true;
964     }
965 
966     if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
967     {
968         return true;
969     }
970 
971     if(rRegion.IsNull())
972     {
973         // source region is null-region, intersect will not change local region
974         return true;
975     }
976 
977     if(IsNull())
978     {
979         // when local region is null-region, intersect will be equal to source
980         *this = rRegion;
981         return true;
982     }
983 
984 	if(rRegion.IsEmpty())
985 	{
986         // source region is empty, intersection will always be empty
987         SetEmpty();
988 		return true;
989 	}
990 
991     if(IsEmpty())
992     {
993         // local region is empty, cannot get more emty than that. Nothing to do
994         return true;
995     }
996 
997     if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
998 	{
999         // get this B2DPolyPolygon
1000         basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
1001 
1002         if(!aThisPolyPoly.count())
1003         {
1004             // local region is empty, cannot get more emty than that. Nothing to do
1005             return true;
1006         }
1007 
1008         // get the other B2DPolyPolygon
1009         basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
1010 
1011         if(!aOtherPolyPoly.count())
1012         {
1013             // source region is empty, intersection will always be empty
1014             SetEmpty();
1015 		    return true;
1016         }
1017 
1018         const basegfx::B2DPolyPolygon aClip(
1019             basegfx::tools::clipPolyPolygonOnPolyPolygon(
1020                 aOtherPolyPoly,
1021                 aThisPolyPoly,
1022                 true,
1023                 false));
1024         *this = Region( aClip );
1025 	    return true;
1026 	}
1027 
1028     // only region band mode possibility left here or null/empty
1029     const RegionBand* pCurrent = getRegionBand();
1030 
1031     if(!pCurrent)
1032     {
1033         // local region is empty, cannot get more emty than that. Nothing to do
1034         return true;
1035     }
1036 
1037     const RegionBand* pSource = rRegion.getRegionBand();
1038 
1039     if(!pSource)
1040     {
1041         // source region is empty, intersection will always be empty
1042         SetEmpty();
1043 		return true;
1044     }
1045 
1046     // both RegionBands exist and are not empty
1047     if(pCurrent->getRectangleCount() + 2 < pSource->getRectangleCount())
1048     {
1049         // when we have less rectangles, turn around the call
1050         Region aTempRegion = rRegion;
1051         aTempRegion.Intersect( *this );
1052         *this = aTempRegion;
1053     }
1054     else
1055     {
1056         // prepare new regionBand
1057         RegionBand* pNew = pCurrent ? new RegionBand(*pCurrent) : new RegionBand();
1058 
1059         // intersect with source
1060         pNew->Intersect(*pSource);
1061 
1062         // cleanup
1063         if(!pNew->OptimizeBandList())
1064         {
1065             delete pNew;
1066             pNew = 0;
1067         }
1068 
1069         mpRegionBand.reset(pNew);
1070     }
1071 
1072     return true;
1073 }
1074 
Exclude(const Region & rRegion)1075 bool Region::Exclude( const Region& rRegion )
1076 {
1077 	if ( rRegion.IsEmpty() )
1078     {
1079     	// excluding nothing will do no change
1080 		return true;
1081     }
1082 
1083 	if ( rRegion.IsNull() )
1084     {
1085     	// excluding everything will create empty region
1086         SetEmpty();
1087 		return true;
1088     }
1089 
1090     if(IsEmpty())
1091     {
1092         // cannot exclude from empty, done
1093         return true;
1094     }
1095 
1096     if(IsNull())
1097     {
1098         // error; cannnot exclude from null region since this is not representable
1099         // in the data
1100         OSL_ENSURE(false, "Region::Exclude error: Cannot exclude from null region (!)");
1101         return true;
1102     }
1103 
1104 	if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1105 	{
1106         // get this B2DPolyPolygon
1107         basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
1108 
1109         if(!aThisPolyPoly.count())
1110         {
1111             // cannot exclude from empty, done
1112             return true;
1113         }
1114 
1115         aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly );
1116 
1117         // get the other B2DPolyPolygon
1118         basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
1119         aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly );
1120 
1121         basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationDiff( aThisPolyPoly, aOtherPolyPoly );
1122         *this = Region( aClip );
1123 	    return true;
1124 	}
1125 
1126     // only region band mode possibility left here or null/empty
1127     const RegionBand* pCurrent = getRegionBand();
1128 
1129     if(!pCurrent)
1130     {
1131         // cannot exclude from empty, done
1132         return true;
1133     }
1134 
1135     const RegionBand* pSource = rRegion.getRegionBand();
1136 
1137     if(!pSource)
1138     {
1139     	// excluding nothing will do no change
1140 		return true;
1141     }
1142 
1143     // prepare source and target
1144     RegionBand* pNew = new RegionBand(*pCurrent);
1145 
1146     // union with source
1147     const bool bSuccess(pNew->Exclude(*pSource));
1148 
1149     // cleanup
1150     if(!bSuccess)
1151     {
1152         delete pNew;
1153         pNew = 0;
1154     }
1155 
1156     mpRegionBand.reset(pNew);
1157 	return true;
1158 }
1159 
XOr(const Region & rRegion)1160 bool Region::XOr( const Region& rRegion )
1161 {
1162 	if ( rRegion.IsEmpty() )
1163     {
1164     	// empty region will not change local content
1165 		return true;
1166     }
1167 
1168 	if ( rRegion.IsNull() )
1169     {
1170         // error; cannnot exclude null region from local since this is not representable
1171         // in the data
1172         OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1173 		return true;
1174     }
1175 
1176     if(IsEmpty())
1177     {
1178         // rRect will be the xored-form (local off, rect on)
1179         *this = rRegion;
1180         return true;
1181     }
1182 
1183     if(IsNull())
1184     {
1185         // error; cannnot exclude from null region since this is not representable
1186         // in the data
1187         OSL_ENSURE(false, "Region::XOr error: Cannot XOr with null region (!)");
1188         return false;
1189     }
1190 
1191     if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
1192 	{
1193         // get this B2DPolyPolygon
1194         basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());
1195 
1196         if(!aThisPolyPoly.count())
1197         {
1198             // rRect will be the xored-form (local off, rect on)
1199             *this = rRegion;
1200             return true;
1201         }
1202 
1203         aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly );
1204 
1205         // get the other B2DPolyPolygon
1206         basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
1207         aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly );
1208 
1209         basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationXor( aThisPolyPoly, aOtherPolyPoly );
1210         *this = Region( aClip );
1211 	    return true;
1212 	}
1213 
1214     // only region band mode possibility left here or null/empty
1215     const RegionBand* pCurrent = getRegionBand();
1216 
1217     if(!pCurrent)
1218     {
1219         // rRect will be the xored-form (local off, rect on)
1220         *this = rRegion;
1221         return true;
1222     }
1223 
1224     const RegionBand* pSource = rRegion.getRegionBand();
1225 
1226     if(!pSource)
1227     {
1228     	// empty region will not change local content
1229 		return true;
1230     }
1231 
1232     // prepare source and target
1233     RegionBand* pNew = new RegionBand(*pCurrent);
1234 
1235     // union with source
1236     pNew->XOr(*pSource);
1237 
1238     // cleanup
1239     if(!pNew->OptimizeBandList())
1240     {
1241         delete pNew;
1242         pNew = 0;
1243     }
1244 
1245     mpRegionBand.reset(pNew);
1246 
1247 	return true;
1248 }
1249 
GetBoundRect() const1250 Rectangle Region::GetBoundRect() const
1251 {
1252 	if(IsEmpty())
1253     {
1254     	// no internal data? -> region is empty!
1255 		return Rectangle();
1256     }
1257 
1258 	if(IsNull())
1259     {
1260         // error; null region has no BoundRect
1261         // OSL_ENSURE(false, "Region::GetBoundRect error: null region has unlimitied bound rect, not representable (!)");
1262 		return Rectangle();
1263     }
1264 
1265     // prefer double precision source
1266 	if(getB2DPolyPolygon())
1267 	{
1268 		const basegfx::B2DRange aRange(basegfx::tools::getRange(*getB2DPolyPolygon()));
1269 
1270         if(aRange.isEmpty())
1271         {
1272             // emulate PolyPolygon::GetBoundRect() when empty polygon
1273             return Rectangle();
1274         }
1275         else
1276         {
1277             // #122149# corrected rounding, no need for ceil() and floor() here
1278             return Rectangle(
1279                 basegfx::fround(aRange.getMinX()), basegfx::fround(aRange.getMinY()),
1280                 basegfx::fround(aRange.getMaxX()), basegfx::fround(aRange.getMaxY()));
1281         }
1282 	}
1283 
1284 	if(getPolyPolygon())
1285     {
1286 		return getPolyPolygon()->GetBoundRect();
1287     }
1288 
1289     if(getRegionBand())
1290     {
1291         return getRegionBand()->GetBoundRect();
1292     }
1293 
1294 	return Rectangle();
1295 }
1296 
GetAsPolyPolygon() const1297 const PolyPolygon Region::GetAsPolyPolygon() const
1298 {
1299     if(getPolyPolygon())
1300     {
1301         return *getPolyPolygon();
1302     }
1303 
1304     if(getB2DPolyPolygon())
1305     {
1306         // the polygon needs to be converted, buffer the down converion
1307         const PolyPolygon aPolyPolgon(*getB2DPolyPolygon());
1308         const_cast< Region* >(this)->mpPolyPolygon.reset(new PolyPolygon(aPolyPolgon));
1309 
1310         return *getPolyPolygon();
1311     }
1312 
1313     if(getRegionBand())
1314     {
1315         // the BandRegion needs to be converted, buffer the converion
1316         const PolyPolygon aPolyPolgon(ImplCreatePolyPolygonFromRegionBand());
1317         const_cast< Region* >(this)->mpPolyPolygon.reset(new PolyPolygon(aPolyPolgon));
1318 
1319         return *getPolyPolygon();
1320     }
1321 
1322     return PolyPolygon();
1323 }
1324 
GetAsB2DPolyPolygon() const1325 const basegfx::B2DPolyPolygon Region::GetAsB2DPolyPolygon() const
1326 {
1327     if(getB2DPolyPolygon())
1328     {
1329         return *getB2DPolyPolygon();
1330     }
1331 
1332     if(getPolyPolygon())
1333     {
1334         // the polygon needs to be converted, buffer the up conversion. This will be preferred from now.
1335         const basegfx::B2DPolyPolygon aB2DPolyPolygon(getPolyPolygon()->getB2DPolyPolygon());
1336         const_cast< Region* >(this)->mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(aB2DPolyPolygon));
1337 
1338         return *getB2DPolyPolygon();
1339     }
1340 
1341     if(getRegionBand())
1342     {
1343         // the BandRegion needs to be converted, buffer the converion
1344         const basegfx::B2DPolyPolygon aB2DPolyPolygon(ImplCreateB2DPolyPolygonFromRegionBand());
1345         const_cast< Region* >(this)->mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(aB2DPolyPolygon));
1346 
1347         return *getB2DPolyPolygon();
1348     }
1349 
1350     return basegfx::B2DPolyPolygon();
1351 }
1352 
GetAsRegionBand() const1353 const RegionBand* Region::GetAsRegionBand() const
1354 {
1355     if(!getRegionBand())
1356     {
1357         if(getB2DPolyPolygon())
1358         {
1359             // convert B2DPolyPolygon to RegionBand, buffer it and return it
1360             const_cast< Region* >(this)->mpRegionBand.reset(ImplCreateRegionBandFromPolyPolygon(PolyPolygon(*getB2DPolyPolygon())));
1361         }
1362         else if(getPolyPolygon())
1363         {
1364             // convert B2DPolyPolygon to RegionBand, buffer it and return it
1365             const_cast< Region* >(this)->mpRegionBand.reset(ImplCreateRegionBandFromPolyPolygon(*getPolyPolygon()));
1366         }
1367     }
1368 
1369     return getRegionBand();
1370 }
1371 
IsInside(const Point & rPoint) const1372 bool Region::IsInside( const Point& rPoint ) const
1373 {
1374 	if(IsEmpty())
1375     {
1376         // no point can be in empty region
1377 		return false;
1378     }
1379 
1380     if(IsNull())
1381     {
1382         // all points are inside null-region
1383         return true;
1384     }
1385 
1386     // Too expensive (?)
1387 	//if(mpImplRegion->getRegionPolyPoly())
1388     //{
1389 	//	return mpImplRegion->getRegionPolyPoly()->IsInside( rPoint );
1390     //}
1391 
1392     // ensure RegionBand existance
1393     const RegionBand* pRegionBand = GetAsRegionBand();
1394 
1395     if(pRegionBand)
1396     {
1397         return pRegionBand->IsInside(rPoint);
1398     }
1399 
1400     return false;
1401 }
1402 
IsInside(const Rectangle & rRect) const1403 bool Region::IsInside( const Rectangle& rRect ) const
1404 {
1405 	if(IsEmpty())
1406     {
1407         // no rectangle can be in empty region
1408 		return false;
1409     }
1410 
1411     if(IsNull())
1412     {
1413         // rectangle always inside null-region
1414         return true;
1415     }
1416 
1417 	if ( rRect.IsEmpty() )
1418     {
1419         // is rectangle empty? -> not inside
1420 		return false;
1421     }
1422 
1423 	// create region from rectangle and intersect own region
1424 	Region aRegion(rRect);
1425 	aRegion.Exclude(*this);
1426 
1427 	// rectangle is inside if exclusion is empty
1428 	return aRegion.IsEmpty();
1429 }
1430 
1431 // -----------------------------------------------------------------------
1432 
IsOver(const Rectangle & rRect) const1433 bool Region::IsOver( const Rectangle& rRect ) const
1434 {
1435 	if(IsEmpty())
1436     {
1437         // nothing can be over something empty
1438 		return false;
1439     }
1440 
1441     if(IsNull())
1442     {
1443         // everything is over null region
1444         return true;
1445     }
1446 
1447 	// Can we optimize this ??? - is used in StarDraw for brushes pointers
1448 	// Why we have no IsOver for Regions ???
1449 	// create region from rectangle and intersect own region
1450 	Region aRegion(rRect);
1451 	aRegion.Intersect( *this );
1452 
1453 	// rectangle is over if include is not empty
1454 	return !aRegion.IsEmpty();
1455 }
1456 
SetNull()1457 void Region::SetNull()
1458 {
1459 	// reset all content
1460     mpB2DPolyPolygon.reset();
1461     mpPolyPolygon.reset();
1462     mpRegionBand.reset();
1463     mbIsNull = true;
1464 }
1465 
SetEmpty()1466 void Region::SetEmpty()
1467 {
1468 	// reset all content
1469     mpB2DPolyPolygon.reset();
1470     mpPolyPolygon.reset();
1471     mpRegionBand.reset();
1472     mbIsNull = false;
1473 }
1474 
operator =(const Region & rRegion)1475 Region& Region::operator=( const Region& rRegion )
1476 {
1477 	// reset all content
1478     mpB2DPolyPolygon = rRegion.mpB2DPolyPolygon;
1479     mpPolyPolygon = rRegion.mpPolyPolygon;
1480     mpRegionBand = rRegion.mpRegionBand;
1481     mbIsNull = rRegion.mbIsNull;
1482 
1483     return *this;
1484 }
1485 
operator =(const Rectangle & rRect)1486 Region& Region::operator=( const Rectangle& rRect )
1487 {
1488     mpB2DPolyPolygon.reset();
1489     mpPolyPolygon.reset();
1490     mpRegionBand.reset(rRect.IsEmpty() ? 0 : new RegionBand(rRect));
1491     mbIsNull = false;
1492 
1493     return *this;
1494 }
1495 
operator ==(const Region & rRegion) const1496 bool Region::operator==( const Region& rRegion ) const
1497 {
1498     if(IsNull() && rRegion.IsNull())
1499     {
1500         // both are null region
1501         return true;
1502     }
1503 
1504     if(IsEmpty() && rRegion.IsEmpty())
1505     {
1506         // both are empty
1507         return true;
1508     }
1509 
1510     if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
1511     {
1512         // same instance data? -> equal
1513         return true;
1514     }
1515 
1516     if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
1517     {
1518         // same instance data? -> equal
1519         return true;
1520     }
1521 
1522     if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
1523     {
1524         // same instance data? -> equal
1525         return true;
1526     }
1527 
1528 	if(IsNull() || IsEmpty())
1529     {
1530 		return false;
1531     }
1532 
1533 	if(rRegion.IsNull() || rRegion.IsEmpty())
1534     {
1535 		return false;
1536     }
1537 
1538     if(rRegion.getB2DPolyPolygon() || getB2DPolyPolygon())
1539     {
1540         // one of both has a B2DPolyPolygon based region, ensure both have it
1541         // by evtl. conversion
1542         const_cast< Region* >(this)->GetAsB2DPolyPolygon();
1543         const_cast< Region& >(rRegion).GetAsB2DPolyPolygon();
1544 
1545         return *rRegion.getB2DPolyPolygon() == *getB2DPolyPolygon();
1546     }
1547 
1548     if(rRegion.getPolyPolygon() || getPolyPolygon())
1549     {
1550         // one of both has a B2DPolyPolygon based region, ensure both have it
1551         // by evtl. conversion
1552         const_cast< Region* >(this)->GetAsPolyPolygon();
1553         const_cast< Region& >(rRegion).GetAsPolyPolygon();
1554 
1555         return *rRegion.getPolyPolygon() == *getPolyPolygon();
1556     }
1557 
1558     // both are not empty or null (see above) and if content supported polygon
1559     // data the comparison is already done. Only both on RegionBand base can be left,
1560     // but better check
1561     if(rRegion.getRegionBand() && getRegionBand())
1562     {
1563         return *rRegion.getRegionBand() == *getRegionBand();
1564     }
1565 
1566     // should not happen, but better deny equality
1567     return false;
1568 }
1569 
operator >>(SvStream & rIStrm,Region & rRegion)1570 SvStream& operator>>(SvStream& rIStrm, Region& rRegion)
1571 {
1572     VersionCompat aCompat(rIStrm, STREAM_READ);
1573     sal_uInt16 nVersion(0);
1574     sal_uInt16 nTmp16(0);
1575 
1576     // clear region to be loaded
1577     rRegion.SetEmpty();
1578 
1579     // get version of streamed region
1580     rIStrm >> nVersion;
1581 
1582     // get type of region
1583     rIStrm >> nTmp16;
1584 
1585     enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX };
1586     RegionType meStreamedType = (RegionType)nTmp16;
1587 
1588     switch(meStreamedType)
1589     {
1590         case REGION_NULL:
1591         {
1592             rRegion.SetNull();
1593             break;
1594         }
1595 
1596         case REGION_EMPTY:
1597         {
1598             rRegion.SetEmpty();
1599             break;
1600         }
1601 
1602         default:
1603         {
1604             RegionBand* pNewRegionBand = new RegionBand();
1605             pNewRegionBand->load(rIStrm);
1606             rRegion.mpRegionBand.reset(pNewRegionBand);
1607 
1608             if(aCompat.GetVersion() >= 2)
1609             {
1610                 sal_Bool bHasPolyPolygon(sal_False);
1611 
1612                 rIStrm >> bHasPolyPolygon;
1613 
1614                 if(bHasPolyPolygon)
1615                 {
1616                     PolyPolygon* pNewPoly = new PolyPolygon();
1617                     rIStrm >> *pNewPoly;
1618                     rRegion.mpPolyPolygon.reset(pNewPoly);
1619                 }
1620             }
1621 
1622             break;
1623         }
1624     }
1625 
1626     return rIStrm;
1627 }
1628 
operator <<(SvStream & rOStrm,const Region & rRegion)1629 SvStream& operator<<( SvStream& rOStrm, const Region& rRegion )
1630 {
1631     const sal_uInt16 nVersion(2);
1632     VersionCompat aCompat(rOStrm, STREAM_WRITE, nVersion);
1633 
1634     // put version
1635     rOStrm << nVersion;
1636 
1637     // put type
1638     enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX };
1639     RegionType aRegionType(REGION_COMPLEX);
1640     bool bEmpty(rRegion.IsEmpty());
1641 
1642     if(!bEmpty && rRegion.getB2DPolyPolygon() && 0 == rRegion.getB2DPolyPolygon()->count())
1643     {
1644         OSL_ENSURE(false, "Region with empty B2DPolyPolygon, should not be created (!)");
1645         bEmpty = true;
1646     }
1647 
1648     if(!bEmpty && rRegion.getPolyPolygon() && 0 == rRegion.getPolyPolygon()->Count())
1649     {
1650         OSL_ENSURE(false, "Region with empty PolyPolygon, should not be created (!)");
1651         bEmpty = true;
1652     }
1653 
1654     if(bEmpty)
1655     {
1656         aRegionType = REGION_EMPTY;
1657     }
1658     else if(rRegion.IsNull())
1659     {
1660         aRegionType = REGION_NULL;
1661     }
1662     else if(rRegion.getRegionBand() && rRegion.getRegionBand()->isSingleRectangle())
1663     {
1664         aRegionType = REGION_RECTANGLE;
1665     }
1666 
1667     rOStrm << (sal_uInt16)aRegionType;
1668 
1669     // get RegionBand
1670     const RegionBand* pRegionBand = rRegion.getRegionBand();
1671 
1672     if(pRegionBand)
1673     {
1674         pRegionBand->save(rOStrm);
1675     }
1676     else
1677     {
1678         // for compatibility, write an empty RegionBand (will only write
1679         // the end marker STREAMENTRY_END, but this *is* needed)
1680         const RegionBand aRegionBand;
1681 
1682         aRegionBand.save(rOStrm);
1683     }
1684 
1685     // write polypolygon if available
1686     const sal_Bool bHasPolyPolygon(rRegion.HasPolyPolygonOrB2DPolyPolygon());
1687     rOStrm << bHasPolyPolygon;
1688 
1689     if(bHasPolyPolygon)
1690     {
1691         // #i105373#
1692         PolyPolygon aNoCurvePolyPolygon;
1693         rRegion.GetAsPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon);
1694 
1695         rOStrm << aNoCurvePolyPolygon;
1696     }
1697 
1698     return rOStrm;
1699 }
1700 
GetRegionRectangles(RectangleVector & rTarget) const1701 void Region::GetRegionRectangles(RectangleVector& rTarget) const
1702 {
1703     // clear returnvalues
1704     rTarget.clear();
1705 
1706     // ensure RegionBand existance
1707     const RegionBand* pRegionBand = GetAsRegionBand();
1708 
1709     if(pRegionBand)
1710     {
1711         pRegionBand->GetRegionRectangles(rTarget);
1712     }
1713 }
1714 
ImplPolygonRectTest(const Polygon & rPoly,Rectangle * pRectOut=NULL)1715 static inline bool ImplPolygonRectTest( const Polygon& rPoly, Rectangle* pRectOut = NULL )
1716 {
1717     bool bIsRect = false;
1718     const Point* pPoints = rPoly.GetConstPointAry();
1719     sal_uInt16 nPoints = rPoly.GetSize();
1720 
1721     if( nPoints == 4 || (nPoints == 5 && pPoints[0] == pPoints[4]) )
1722     {
1723         long nX1 = pPoints[0].X(), nX2 = pPoints[2].X(), nY1 = pPoints[0].Y(), nY2 = pPoints[2].Y();
1724 
1725         if( ( (pPoints[1].X() == nX1 && pPoints[3].X() == nX2) && (pPoints[1].Y() == nY2 && pPoints[3].Y() == nY1) )
1726          || ( (pPoints[1].X() == nX2 && pPoints[3].X() == nX1) && (pPoints[1].Y() == nY1 && pPoints[3].Y() == nY2) ) )
1727         {
1728             bIsRect = true;
1729 
1730             if( pRectOut )
1731             {
1732                 long nSwap;
1733 
1734                 if( nX2 < nX1 )
1735                 {
1736                     nSwap = nX2;
1737                     nX2 = nX1;
1738                     nX1 = nSwap;
1739                 }
1740 
1741                 if( nY2 < nY1 )
1742                 {
1743                     nSwap = nY2;
1744                     nY2 = nY1;
1745                     nY1 = nSwap;
1746                 }
1747 
1748                 if( nX2 != nX1 )
1749                 {
1750                     nX2--;
1751                 }
1752 
1753                 if( nY2 != nY1 )
1754                 {
1755                     nY2--;
1756                 }
1757 
1758                 pRectOut->Left()    = nX1;
1759                 pRectOut->Right()   = nX2;
1760                 pRectOut->Top()     = nY1;
1761                 pRectOut->Bottom()  = nY2;
1762             }
1763         }
1764     }
1765 
1766     return bIsRect;
1767 }
1768 
GetRegionFromPolyPolygon(const PolyPolygon & rPolyPoly)1769 Region Region::GetRegionFromPolyPolygon( const PolyPolygon& rPolyPoly )
1770 {
1771     //return Region( rPolyPoly );
1772 
1773     // check if it's worth extracting the XOr'ing the Rectangles
1774     // empiricism shows that break even between XOr'ing rectangles separately
1775     // and ImplCreateRegionBandFromPolyPolygon is at half rectangles/half polygons
1776     int nPolygonRects = 0, nPolygonPolygons = 0;
1777     int nPolygons = rPolyPoly.Count();
1778 
1779     for( sal_uInt16 i = 0; i < nPolygons; i++ )
1780     {
1781         const Polygon& rPoly = rPolyPoly[i];
1782 
1783         if( ImplPolygonRectTest( rPoly ) )
1784         {
1785             nPolygonRects++;
1786         }
1787         else
1788         {
1789             nPolygonPolygons++;
1790         }
1791     }
1792 
1793     if( nPolygonPolygons > nPolygonRects )
1794     {
1795         return Region( rPolyPoly );
1796     }
1797 
1798     Region aResult;
1799     Rectangle aRect;
1800 
1801     for( sal_uInt16 i = 0; i < nPolygons; i++ )
1802     {
1803         const Polygon& rPoly = rPolyPoly[i];
1804 
1805         if( ImplPolygonRectTest( rPoly, &aRect ) )
1806         {
1807             aResult.XOr( aRect );
1808         }
1809         else
1810         {
1811             aResult.XOr( Region(rPoly) );
1812         }
1813     }
1814 
1815     return aResult;
1816 }
1817 
1818 //////////////////////////////////////////////////////////////////////////////
1819 // eof
1820