xref: /aoo42x/main/vcl/source/gdi/region.cxx (revision e6f63103)
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     */
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     */
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     */
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 
237 bool Region::IsEmpty() const
238 {
239     return !mbIsNull && !mpB2DPolyPolygon.get() && !mpPolyPolygon.get() && !mpRegionBand.get();
240 }
241 
242 bool Region::IsNull() const
243 {
244     return mbIsNull;
245 }
246 
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 
295 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 
317 basegfx::B2DPolyPolygon Region::ImplCreateB2DPolyPolygonFromRegionBand() const
318 {
319     PolyPolygon aPoly(ImplCreatePolyPolygonFromRegionBand());
320 
321     return aPoly.getB2DPolyPolygon();
322 }
323 
324 Region::Region(bool bIsNull)
325 :   mpB2DPolyPolygon(),
326     mpPolyPolygon(),
327     mpRegionBand(),
328     mbIsNull(bIsNull)
329 {
330 }
331 
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 
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 
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 
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 
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 
391 Region::~Region()
392 {
393 }
394 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
1250 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             return Rectangle(
1278                 static_cast<sal_Int32>(floor(aRange.getMinX())), static_cast<sal_Int32>(floor(aRange.getMinY())),
1279                 static_cast<sal_Int32>(ceil(aRange.getMaxX())), static_cast<sal_Int32>(ceil(aRange.getMaxY())));
1280         }
1281 	}
1282 
1283 	if(getPolyPolygon())
1284     {
1285 		return getPolyPolygon()->GetBoundRect();
1286     }
1287 
1288     if(getRegionBand())
1289     {
1290         return getRegionBand()->GetBoundRect();
1291     }
1292 
1293 	return Rectangle();
1294 }
1295 
1296 const PolyPolygon Region::GetAsPolyPolygon() const
1297 {
1298     if(getPolyPolygon())
1299     {
1300         return *getPolyPolygon();
1301     }
1302 
1303     if(getB2DPolyPolygon())
1304     {
1305         // the polygon needs to be converted, buffer the down converion
1306         const PolyPolygon aPolyPolgon(*getB2DPolyPolygon());
1307         const_cast< Region* >(this)->mpPolyPolygon.reset(new PolyPolygon(aPolyPolgon));
1308 
1309         return *getPolyPolygon();
1310     }
1311 
1312     if(getRegionBand())
1313     {
1314         // the BandRegion needs to be converted, buffer the converion
1315         const PolyPolygon aPolyPolgon(ImplCreatePolyPolygonFromRegionBand());
1316         const_cast< Region* >(this)->mpPolyPolygon.reset(new PolyPolygon(aPolyPolgon));
1317 
1318         return *getPolyPolygon();
1319     }
1320 
1321     return PolyPolygon();
1322 }
1323 
1324 const basegfx::B2DPolyPolygon Region::GetAsB2DPolyPolygon() const
1325 {
1326     if(getB2DPolyPolygon())
1327     {
1328         return *getB2DPolyPolygon();
1329     }
1330 
1331     if(getPolyPolygon())
1332     {
1333         // the polygon needs to be converted, buffer the up conversion. This will be preferred from now.
1334         const basegfx::B2DPolyPolygon aB2DPolyPolygon(getPolyPolygon()->getB2DPolyPolygon());
1335         const_cast< Region* >(this)->mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(aB2DPolyPolygon));
1336 
1337         return *getB2DPolyPolygon();
1338     }
1339 
1340     if(getRegionBand())
1341     {
1342         // the BandRegion needs to be converted, buffer the converion
1343         const basegfx::B2DPolyPolygon aB2DPolyPolygon(ImplCreateB2DPolyPolygonFromRegionBand());
1344         const_cast< Region* >(this)->mpB2DPolyPolygon.reset(new basegfx::B2DPolyPolygon(aB2DPolyPolygon));
1345 
1346         return *getB2DPolyPolygon();
1347     }
1348 
1349     return basegfx::B2DPolyPolygon();
1350 }
1351 
1352 const RegionBand* Region::GetAsRegionBand() const
1353 {
1354     if(!getRegionBand())
1355     {
1356         if(getB2DPolyPolygon())
1357         {
1358             // convert B2DPolyPolygon to RegionBand, buffer it and return it
1359             const_cast< Region* >(this)->mpRegionBand.reset(ImplCreateRegionBandFromPolyPolygon(PolyPolygon(*getB2DPolyPolygon())));
1360         }
1361         else if(getPolyPolygon())
1362         {
1363             // convert B2DPolyPolygon to RegionBand, buffer it and return it
1364             const_cast< Region* >(this)->mpRegionBand.reset(ImplCreateRegionBandFromPolyPolygon(*getPolyPolygon()));
1365         }
1366     }
1367 
1368     return getRegionBand();
1369 }
1370 
1371 bool Region::IsInside( const Point& rPoint ) const
1372 {
1373 	if(IsEmpty())
1374     {
1375         // no point can be in empty region
1376 		return false;
1377     }
1378 
1379     if(IsNull())
1380     {
1381         // all points are inside null-region
1382         return true;
1383     }
1384 
1385     // Too expensive (?)
1386 	//if(mpImplRegion->getRegionPolyPoly())
1387     //{
1388 	//	return mpImplRegion->getRegionPolyPoly()->IsInside( rPoint );
1389     //}
1390 
1391     // ensure RegionBand existance
1392     const_cast< Region* >(this)->GetAsRegionBand();
1393     const RegionBand* pRegionBand = getRegionBand();
1394 
1395     if(pRegionBand)
1396     {
1397         return pRegionBand->IsInside(rPoint);
1398     }
1399 
1400     return false;
1401 }
1402 
1403 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 
1433 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 
1457 void Region::SetNull()
1458 {
1459 	// reset all content
1460     mpB2DPolyPolygon.reset();
1461     mpPolyPolygon.reset();
1462     mpRegionBand.reset();
1463     mbIsNull = true;
1464 }
1465 
1466 void Region::SetEmpty()
1467 {
1468 	// reset all content
1469     mpB2DPolyPolygon.reset();
1470     mpPolyPolygon.reset();
1471     mpRegionBand.reset();
1472     mbIsNull = false;
1473 }
1474 
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 
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 
1496 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 
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 
1629 SvStream& operator<<( SvStream& rOStrm, const Region& rRegion )
1630 {
1631     const sal_uInt16 nVersion(2);
1632     VersionCompat aCompat(rOStrm, STREAM_WRITE, nVersion);
1633     const RegionBand* pSourceRegionBand = rRegion.GetAsRegionBand();
1634 
1635     // put version
1636     rOStrm << nVersion;
1637 
1638     // put type
1639     enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX };
1640     RegionType aRegionType(REGION_COMPLEX);
1641     bool bEmpty(rRegion.IsEmpty());
1642 
1643     if(!bEmpty && rRegion.getB2DPolyPolygon() && 0 == rRegion.getB2DPolyPolygon()->count())
1644     {
1645         OSL_ENSURE(false, "Region with empty B2DPolyPolygon, should not be created (!)");
1646         bEmpty = true;
1647     }
1648 
1649     if(!bEmpty && rRegion.getPolyPolygon() && 0 == rRegion.getPolyPolygon()->Count())
1650     {
1651         OSL_ENSURE(false, "Region with empty PolyPolygon, should not be created (!)");
1652         bEmpty = true;
1653     }
1654 
1655     if(bEmpty)
1656     {
1657         aRegionType = REGION_EMPTY;
1658     }
1659     else if(rRegion.IsNull())
1660     {
1661         aRegionType = REGION_NULL;
1662     }
1663     else if(rRegion.getRegionBand() && rRegion.getRegionBand()->isSingleRectangle())
1664     {
1665         aRegionType = REGION_RECTANGLE;
1666     }
1667 
1668     rOStrm << (sal_uInt16)aRegionType;
1669 
1670     // get RegionBand
1671     const RegionBand* pRegionBand = rRegion.getRegionBand();
1672 
1673     if(pRegionBand)
1674     {
1675         pRegionBand->save(rOStrm);
1676     }
1677 
1678     // write polypolygon if available
1679     const sal_Bool bHasPolyPolygon(rRegion.HasPolyPolygonOrB2DPolyPolygon());
1680     rOStrm << bHasPolyPolygon;
1681 
1682     if(bHasPolyPolygon)
1683     {
1684         // #i105373#
1685         PolyPolygon aNoCurvePolyPolygon;
1686         rRegion.GetAsPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon);
1687 
1688         rOStrm << aNoCurvePolyPolygon;
1689     }
1690 
1691     return rOStrm;
1692 }
1693 
1694 void Region::GetRegionRectangles(RectangleVector& rTarget) const
1695 {
1696     // clear returnvalues
1697     rTarget.clear();
1698 
1699     // ensure RegionBand existance
1700     const_cast< Region* >(this)->GetAsRegionBand();
1701     const RegionBand* pRegionBand = getRegionBand();
1702 
1703     if(pRegionBand)
1704     {
1705         pRegionBand->GetRegionRectangles(rTarget);
1706     }
1707 }
1708 
1709 static inline bool ImplPolygonRectTest( const Polygon& rPoly, Rectangle* pRectOut = NULL )
1710 {
1711     bool bIsRect = false;
1712     const Point* pPoints = rPoly.GetConstPointAry();
1713     sal_uInt16 nPoints = rPoly.GetSize();
1714 
1715     if( nPoints == 4 || (nPoints == 5 && pPoints[0] == pPoints[4]) )
1716     {
1717         long nX1 = pPoints[0].X(), nX2 = pPoints[2].X(), nY1 = pPoints[0].Y(), nY2 = pPoints[2].Y();
1718 
1719         if( ( (pPoints[1].X() == nX1 && pPoints[3].X() == nX2) && (pPoints[1].Y() == nY2 && pPoints[3].Y() == nY1) )
1720          || ( (pPoints[1].X() == nX2 && pPoints[3].X() == nX1) && (pPoints[1].Y() == nY1 && pPoints[3].Y() == nY2) ) )
1721         {
1722             bIsRect = true;
1723 
1724             if( pRectOut )
1725             {
1726                 long nSwap;
1727 
1728                 if( nX2 < nX1 )
1729                 {
1730                     nSwap = nX2;
1731                     nX2 = nX1;
1732                     nX1 = nSwap;
1733                 }
1734 
1735                 if( nY2 < nY1 )
1736                 {
1737                     nSwap = nY2;
1738                     nY2 = nY1;
1739                     nY1 = nSwap;
1740                 }
1741 
1742                 if( nX2 != nX1 )
1743                 {
1744                     nX2--;
1745                 }
1746 
1747                 if( nY2 != nY1 )
1748                 {
1749                     nY2--;
1750                 }
1751 
1752                 pRectOut->Left()    = nX1;
1753                 pRectOut->Right()   = nX2;
1754                 pRectOut->Top()     = nY1;
1755                 pRectOut->Bottom()  = nY2;
1756             }
1757         }
1758     }
1759 
1760     return bIsRect;
1761 }
1762 
1763 Region Region::GetRegionFromPolyPolygon( const PolyPolygon& rPolyPoly )
1764 {
1765     //return Region( rPolyPoly );
1766 
1767     // check if it's worth extracting the XOr'ing the Rectangles
1768     // empiricism shows that break even between XOr'ing rectangles separately
1769     // and ImplCreateRegionBandFromPolyPolygon is at half rectangles/half polygons
1770     int nPolygonRects = 0, nPolygonPolygons = 0;
1771     int nPolygons = rPolyPoly.Count();
1772 
1773     for( sal_uInt16 i = 0; i < nPolygons; i++ )
1774     {
1775         const Polygon& rPoly = rPolyPoly[i];
1776 
1777         if( ImplPolygonRectTest( rPoly ) )
1778         {
1779             nPolygonRects++;
1780         }
1781         else
1782         {
1783             nPolygonPolygons++;
1784         }
1785     }
1786 
1787     if( nPolygonPolygons > nPolygonRects )
1788     {
1789         return Region( rPolyPoly );
1790     }
1791 
1792     Region aResult;
1793     Rectangle aRect;
1794 
1795     for( sal_uInt16 i = 0; i < nPolygons; i++ )
1796     {
1797         const Polygon& rPoly = rPolyPoly[i];
1798 
1799         if( ImplPolygonRectTest( rPoly, &aRect ) )
1800         {
1801             aResult.XOr( aRect );
1802         }
1803         else
1804         {
1805             aResult.XOr( Region(rPoly) );
1806         }
1807     }
1808 
1809     return aResult;
1810 }
1811 
1812 //////////////////////////////////////////////////////////////////////////////
1813 // eof
1814