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