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