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