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