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