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