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