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