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