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