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