update dev300-m58
[ooovba.git] / vcl / source / gdi / region.cxx
blob87a080114038298854eba84f6f20715d15eca022
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: region.cxx,v $
10 * $Revision: 1.18.36.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
34 #include <limits.h>
35 #include <tools/vcompat.hxx>
36 #include <vcl/salbtype.hxx>
37 #include <tools/stream.hxx>
38 #include <tools/debug.hxx>
39 #ifndef _REGION_H
40 #include <vcl/region.h>
41 #endif
42 #ifndef _REGION_HXX
43 #include <vcl/region.hxx>
44 #endif
45 #ifndef _REGBAND_HXX
46 #include <vcl/regband.hxx>
47 #endif
49 #include <basegfx/matrix/b2dhommatrix.hxx>
50 #include <basegfx/polygon/b2dpolypolygontools.hxx>
51 #include <basegfx/range/b2drange.hxx>
53 // =======================================================================
55 // ImplRegionBand
57 // Die Klassen RegionBand/ImplRegionBand speichert Regionen in Form von
58 // Rechtecken ab. Die Region ist in Y-Richtung in Baendern unterteilt, die
59 // wiederum ein oder mehrere Rechtecke mit der Hoehe des Bandes enthalten.
61 // Leere Baender werden entfernt.
63 // Polygone und PolyPolygone werden ebenfalls in Rechtecke zerlegt und in
64 // der Baendern abgelegt. Hierzu werden alle Punkte der einzelnen Polygone
65 // mit dem Bresenham-Algorithmus berechnet und in die Baender eingetragen.
66 // Nach der vollstaendigen Berechung aller Kanten werden die entsprechenden
67 // Rechntecke berechnet
69 // =======================================================================
71 static ImplRegionBase aImplNullRegion( 0 );
72 static ImplRegionBase aImplEmptyRegion( 0 );
74 // =======================================================================
76 DBG_NAME( Region )
77 DBG_NAMEEX( Polygon )
78 DBG_NAMEEX( PolyPolygon )
80 namespace {
82 /** Return <TRUE/> when the given polygon is rectiliner and oriented so that
83 all sides are either horizontal or vertical.
85 bool ImplIsPolygonRectilinear (const PolyPolygon& rPolyPoly)
87 // Iterate over all polygons.
88 const USHORT nPolyCount = rPolyPoly.Count();
89 for (USHORT nPoly = 0; nPoly < nPolyCount; ++nPoly)
91 const Polygon& aPoly = rPolyPoly.GetObject(nPoly);
93 // Iterate over all edges of the current polygon.
94 const USHORT nSize = aPoly.GetSize();
96 if (nSize < 2)
97 continue;
98 Point aPoint (aPoly.GetPoint(0));
99 const Point aLastPoint (aPoint);
100 for (USHORT nPoint = 1; nPoint < nSize; ++nPoint)
102 const Point aNextPoint (aPoly.GetPoint(nPoint));
103 // When there is at least one edge that is neither vertical nor
104 // horizontal then the entire polygon is not rectilinear (and
105 // oriented along primary axes.)
106 if (aPoint.X() != aNextPoint.X() && aPoint.Y() != aNextPoint.Y())
107 return false;
109 aPoint = aNextPoint;
111 // Compare closing edge.
112 if (aLastPoint.X() != aPoint.X() && aLastPoint.Y() != aPoint.Y())
113 return false;
115 return true;
120 /** This function is similar to the ImplRegion::InsertBands() method.
121 It creates a minimal set of missing bands so that the entire vertical
122 interval from nTop to nBottom is covered by bands.
124 void ImplAddMissingBands (
125 ImplRegion* pImplRegion,
126 const long nTop,
127 const long nBottom)
129 // Iterate over already existing bands and add missing bands atop the
130 // first and between two bands.
131 ImplRegionBand* pPreviousBand = NULL;
132 ImplRegionBand* pBand = pImplRegion->ImplGetFirstRegionBand();
133 long nCurrentTop (nTop);
134 while (pBand != NULL && nCurrentTop<nBottom)
136 if (nCurrentTop < pBand->mnYTop)
138 // Create new band above the current band.
139 ImplRegionBand* pAboveBand = new ImplRegionBand(
140 nCurrentTop,
141 ::std::min(nBottom,pBand->mnYTop-1));
142 pImplRegion->InsertBand(pPreviousBand, pAboveBand);
145 // Adapt the top of the interval to prevent overlapping bands.
146 nCurrentTop = ::std::max(nTop, pBand->mnYBottom+1);
148 // Advance to next band.
149 pPreviousBand = pBand;
150 pBand = pBand->mpNextBand;
153 // We still have to cover two cases:
154 // 1. The region does not yet contain any bands.
155 // 2. The intervall nTop->nBottom extends past the bottom most band.
156 if (nCurrentTop < nBottom && (pBand==NULL || nBottom>pBand->mnYBottom))
158 // When there is no previous band then the new one will be the
159 // first. Otherwise the new band is inserted behind the last band.
160 pImplRegion->InsertBand(
161 pPreviousBand,
162 new ImplRegionBand(
163 nCurrentTop,
164 nBottom));
170 /** Convert a rectilinear polygon (that is oriented along the primary axes)
171 to a list of bands. For this special form of polygon we can use an
172 optimization that prevents the creation of one band per y value.
173 However, it still is possible that some temporary bands are created that
174 later can be optimized away.
175 @param rPolyPolygon
176 A set of zero, one, or more polygons, nested or not, that are
177 converted into a list of bands.
178 @return
179 A new ImplRegion object is returned that contains the bands that
180 represent the given poly-polygon.
182 ImplRegion* ImplRectilinearPolygonToBands (const PolyPolygon& rPolyPoly)
184 OSL_ASSERT(ImplIsPolygonRectilinear (rPolyPoly));
186 // Create a new ImplRegion object as container of the bands.
187 ImplRegion* pImplRegion = new ImplRegion();
188 long nLineId = 0L;
190 // Iterate over all polygons.
191 const USHORT nPolyCount = rPolyPoly.Count();
192 for (USHORT nPoly = 0; nPoly < nPolyCount; ++nPoly)
194 const Polygon& aPoly = rPolyPoly.GetObject(nPoly);
196 // Iterate over all edges of the current polygon.
197 const USHORT nSize = aPoly.GetSize();
198 if (nSize < 2)
199 continue;
200 // Avoid fetching every point twice (each point is the start point
201 // of one and the end point of another edge.)
202 Point aStart (aPoly.GetPoint(0));
203 Point aEnd;
204 for (USHORT nPoint = 1; nPoint <= nSize; ++nPoint, aStart=aEnd)
206 // We take the implicit closing edge into account by mapping
207 // index nSize to 0.
208 aEnd = aPoly.GetPoint(nPoint%nSize);
209 if (aStart.Y() == aEnd.Y())
211 // Horizontal lines are ignored.
212 continue;
215 // At this point the line has to be vertical.
216 OSL_ASSERT(aStart.X() == aEnd.X());
218 // Sort y-coordinates to simplify the algorithm and store the
219 // direction seperately. The direction is calculated as it is
220 // in other places (but seems to be the wrong way.)
221 const long nTop (::std::min(aStart.Y(), aEnd.Y()));
222 const long nBottom (::std::max(aStart.Y(), aEnd.Y()));
223 const LineType eLineType (aStart.Y() > aEnd.Y() ? LINE_DESCENDING : LINE_ASCENDING);
225 // Make sure that the current line is covered by bands.
226 ImplAddMissingBands(pImplRegion, nTop,nBottom);
228 // Find top-most band that may contain nTop.
229 ImplRegionBand* pBand = pImplRegion->ImplGetFirstRegionBand();
230 while (pBand!=NULL && pBand->mnYBottom < nTop)
231 pBand = pBand->mpNextBand;
232 ImplRegionBand* pTopBand = pBand;
233 // If necessary split the band at nTop so that nTop is contained
234 // in the lower band.
235 if ( // Prevent the current band from becoming 0 pixel high
236 pBand->mnYTop<nTop
237 // this allows the lowest pixel of the band to be split off
238 && pBand->mnYBottom>=nTop
239 // do not split a band that is just one pixel high
240 && pBand->mnYTop<pBand->mnYBottom)
242 // Split the top band.
243 pTopBand = pBand->SplitBand(nTop);
246 // Advance to band that may contain nBottom.
247 while (pBand!=NULL && pBand->mnYBottom < nBottom)
248 pBand = pBand->mpNextBand;
249 // The lowest band may have to be split at nBottom so that
250 // nBottom itself remains in the upper band.
251 if ( // allow the current band becoming 1 pixel high
252 pBand->mnYTop<=nBottom
253 // prevent splitting off a band that is 0 pixel high
254 && pBand->mnYBottom>nBottom
255 // do not split a band that is just one pixel high
256 && pBand->mnYTop<pBand->mnYBottom)
258 // Split the bottom band.
259 pBand->SplitBand(nBottom+1);
262 // Note that we remember the top band (in pTopBand) but not the
263 // bottom band. The later can be determined by comparing y
264 // coordinates.
266 // Add the x-value as point to all bands in the nTop->nBottom range.
267 for (pBand=pTopBand; pBand!=NULL&&pBand->mnYTop<=nBottom; pBand=pBand->mpNextBand)
268 pBand->InsertPoint(aStart.X(), nLineId++, TRUE, eLineType);
272 return pImplRegion;
278 /** Convert a general polygon (one for which ImplIsPolygonRectilinear()
279 returns <FALSE/>) to bands.
281 ImplRegion* ImplGeneralPolygonToBands (
282 const PolyPolygon& rPolyPoly,
283 const Rectangle& rPolygonBoundingBox)
285 long nLineID = 0L;
287 // initialisation and creation of Bands
288 ImplRegion* pImplRegion = new ImplRegion();
289 pImplRegion->CreateBandRange( rPolygonBoundingBox.Top(), rPolygonBoundingBox.Bottom() );
291 // insert polygons
292 const USHORT nPolyCount = rPolyPoly.Count();
293 for ( USHORT nPoly = 0; nPoly < nPolyCount; nPoly++ )
295 // get reference to current polygon
296 const Polygon& aPoly = rPolyPoly.GetObject( nPoly );
297 const USHORT nSize = aPoly.GetSize();
299 // not enough points ( <= 2 )? -> nothing to do!
300 if ( nSize <= 2 )
301 continue;
303 // band the polygon
304 for ( USHORT nPoint = 1; nPoint < nSize; nPoint++ )
305 pImplRegion->InsertLine( aPoly.GetPoint(nPoint-1), aPoly.GetPoint(nPoint), nLineID++ );
307 // close polygon with line from first point to last point, if neccesary
308 const Point rLastPoint = aPoly.GetPoint(nSize-1);
309 const Point rFirstPoint = aPoly.GetPoint(0);
310 if ( rLastPoint != rFirstPoint )
311 pImplRegion->InsertLine( rLastPoint, rFirstPoint, nLineID++ );
314 return pImplRegion;
318 } // end of anonymous namespace
321 // -----------------------------------------------------------------------
323 #ifdef DBG_UTIL
324 const char* ImplDbgTestRegion( const void* pObj )
326 Region* pRegion = (Region*)pObj;
327 ImplRegion* pImplRegion = pRegion->ImplGetImplRegion();
329 if ( aImplNullRegion.mnRefCount )
330 return "Null-Region-RefCount modified";
331 if ( aImplNullRegion.mnRectCount )
332 return "Null-Region-RectCount modified";
333 if ( aImplNullRegion.mpPolyPoly )
334 return "Null-Region-PolyPoly modified";
335 if ( aImplEmptyRegion.mnRefCount )
336 return "Emptry-Region-RefCount modified";
337 if ( aImplEmptyRegion.mnRectCount )
338 return "Emptry-Region-RectCount modified";
339 if ( aImplEmptyRegion.mpPolyPoly )
340 return "Emptry-Region-PolyPoly modified";
342 if ( (pImplRegion != &aImplEmptyRegion) && (pImplRegion != &aImplNullRegion) )
344 ULONG nCount = 0;
345 const ImplRegionBand* pBand = pImplRegion->ImplGetFirstRegionBand();
346 while ( pBand )
348 if ( pBand->mnYBottom < pBand->mnYTop )
349 return "YBottom < YTop";
350 if ( pBand->mpNextBand )
352 if ( pBand->mnYBottom >= pBand->mpNextBand->mnYTop )
353 return "overlapping bands in region";
355 if ( pBand->mbTouched > 1 )
356 return "Band-mbTouched overwrite";
358 ImplRegionBandSep* pSep = pBand->mpFirstSep;
359 while ( pSep )
361 if ( pSep->mnXRight < pSep->mnXLeft )
362 return "XLeft < XRight";
363 if ( pSep->mpNextSep )
365 if ( pSep->mnXRight >= pSep->mpNextSep->mnXLeft )
366 return "overlapping separations in region";
368 if ( pSep->mbRemoved > 1 )
369 return "Sep-mbRemoved overwrite";
371 nCount++;
372 pSep = pSep->mpNextSep;
375 pBand = pBand->mpNextBand;
378 if ( pImplRegion->mnRectCount != nCount )
379 return "mnRetCount is not valid";
382 return NULL;
385 void TraceBands (const ImplRegionBand* pFirstBand)
387 int nBandIndex (0);
388 const ImplRegionBand* pBand = pFirstBand;
389 while (pBand != NULL)
391 OSL_TRACE(" band %d %d->%d : ", nBandIndex++,
392 pBand->mnYTop, pBand->mnYBottom);
394 ImplRegionBandPoint* pPoint = pBand->mpFirstBandPoint;
395 while (pPoint != NULL)
397 OSL_TRACE(" %d ", pPoint->mnX);
398 pPoint = pPoint->mpNextBandPoint;
400 OSL_TRACE(" | ");
402 ImplRegionBandSep* pSep = pBand->mpFirstSep;
403 while (pSep != NULL)
405 OSL_TRACE(" %d->%d ", pSep->mnXLeft, pSep->mnXRight);
406 pSep = pSep->mpNextSep;
408 OSL_TRACE("\n");
410 pBand = pBand->mpNextBand;
413 #endif
415 // =======================================================================
417 inline void Region::ImplPolyPolyRegionToBandRegion()
419 if( mpImplRegion->mpPolyPoly || mpImplRegion->mpB2DPolyPoly )
420 ImplPolyPolyRegionToBandRegionFunc();
423 // =======================================================================
425 ImplRegionBase::ImplRegionBase( int nRefCount )
426 : mnRefCount( nRefCount )
427 , mnRectCount( 0 )
428 , mpPolyPoly( NULL )
429 , mpB2DPolyPoly( NULL )
432 // ------------------------------------------------------------------------
434 ImplRegion::ImplRegion()
436 mpFirstBand = NULL;
437 mpLastCheckedBand = NULL;
440 // ------------------------------------------------------------------------
442 ImplRegion::ImplRegion( const PolyPolygon& rPolyPoly )
444 mpFirstBand = NULL;
445 mpLastCheckedBand = NULL;
446 mpPolyPoly = new PolyPolygon( rPolyPoly );
449 // ------------------------------------------------------------------------
451 ImplRegion::ImplRegion( const basegfx::B2DPolyPolygon& rPolyPoly )
453 mpFirstBand = NULL;
454 mpLastCheckedBand = NULL;
455 mpB2DPolyPoly = new basegfx::B2DPolyPolygon( rPolyPoly );
458 // -----------------------------------------------------------------------
460 ImplRegion::ImplRegion( const ImplRegion& rImplRegion )
461 : ImplRegionBase()
463 mpFirstBand = NULL;
464 mpLastCheckedBand = NULL;
465 mnRectCount = rImplRegion.mnRectCount;
467 if ( rImplRegion.mpPolyPoly )
468 mpPolyPoly = new PolyPolygon( *rImplRegion.mpPolyPoly );
469 else if( rImplRegion.mpB2DPolyPoly )
470 mpB2DPolyPoly = new basegfx::B2DPolyPolygon( *rImplRegion.mpB2DPolyPoly );
472 // insert band(s) into the list
473 ImplRegionBand* pNewBand;
474 ImplRegionBand* pPrevBand = 0;
475 ImplRegionBand* pBand = rImplRegion.mpFirstBand;
476 while ( pBand )
478 pNewBand = new ImplRegionBand( *pBand );
480 // first element? -> set as first into the list
481 if ( pBand == rImplRegion.mpFirstBand )
482 mpFirstBand = pNewBand;
483 else
484 pPrevBand->mpNextBand = pNewBand;
486 pPrevBand = pNewBand;
487 pBand = pBand->mpNextBand;
491 // -----------------------------------------------------------------------
493 ImplRegion::~ImplRegion()
495 DBG_ASSERT( (this != &aImplEmptyRegion) && (this != &aImplNullRegion),
496 "ImplRegion::~ImplRegion() - Empty oder NULL-Region" );
498 ImplRegionBand* pBand = mpFirstBand;
499 while ( pBand )
501 ImplRegionBand* pTempBand = pBand->mpNextBand;
502 delete pBand;
503 pBand = pTempBand;
507 // -----------------------------------------------------------------------
509 ImplRegionBase::~ImplRegionBase()
511 delete mpPolyPoly;
512 delete mpB2DPolyPoly;
515 // -----------------------------------------------------------------------
517 // create complete range of bands in single steps
519 void ImplRegion::CreateBandRange( long nYTop, long nYBottom )
521 // add top band
522 mpFirstBand = new ImplRegionBand( nYTop-1, nYTop-1 );
524 // begin first search from the first element
525 mpLastCheckedBand = mpFirstBand;
527 ImplRegionBand* pBand = mpFirstBand;
528 for ( int i = nYTop; i <= nYBottom+1; i++ )
530 // create new band
531 ImplRegionBand* pNewBand = new ImplRegionBand( i, i );
532 pBand->mpNextBand = pNewBand;
533 if ( pBand != mpFirstBand )
534 pNewBand->mpPrevBand = pBand;
536 pBand = pBand->mpNextBand;
540 // -----------------------------------------------------------------------
542 BOOL ImplRegion::InsertLine( const Point& rStartPt, const Point& rEndPt,
543 long nLineId )
545 long nX, nY;
547 // lines consisting of a single point do not interest here
548 if ( rStartPt == rEndPt )
549 return TRUE;
551 LineType eLineType = (rStartPt.Y() > rEndPt.Y()) ? LINE_DESCENDING : LINE_ASCENDING;
552 if ( rStartPt.X() == rEndPt.X() )
554 // vertical line
555 const long nEndY = rEndPt.Y();
557 nX = rStartPt.X();
558 nY = rStartPt.Y();
560 if( nEndY > nY )
562 for ( ; nY <= nEndY; nY++ )
564 Point aNewPoint( nX, nY );
565 InsertPoint( aNewPoint, nLineId,
566 (aNewPoint == rEndPt) || (aNewPoint == rStartPt),
567 eLineType );
570 else
572 for ( ; nY >= nEndY; nY-- )
574 Point aNewPoint( nX, nY );
575 InsertPoint( aNewPoint, nLineId,
576 (aNewPoint == rEndPt) || (aNewPoint == rStartPt),
577 eLineType );
581 else if ( rStartPt.Y() != rEndPt.Y() )
583 const long nDX = labs( rEndPt.X() - rStartPt.X() );
584 const long nDY = labs( rEndPt.Y() - rStartPt.Y() );
585 const long nStartX = rStartPt.X();
586 const long nStartY = rStartPt.Y();
587 const long nEndX = rEndPt.X();
588 const long nEndY = rEndPt.Y();
589 const long nXInc = ( nStartX < nEndX ) ? 1L : -1L;
590 const long nYInc = ( nStartY < nEndY ) ? 1L : -1L;
592 if ( nDX >= nDY )
594 const long nDYX = ( nDY - nDX ) << 1;
595 const long nDY2 = nDY << 1;
596 long nD = nDY2 - nDX;
598 for ( nX = nStartX, nY = nStartY; nX != nEndX; nX += nXInc )
600 InsertPoint( Point( nX, nY ), nLineId, nStartX == nX, eLineType );
602 if ( nD < 0L )
603 nD += nDY2;
604 else
605 nD += nDYX, nY += nYInc;
608 else
610 const long nDYX = ( nDX - nDY ) << 1;
611 const long nDY2 = nDX << 1;
612 long nD = nDY2 - nDY;
614 for ( nX = nStartX, nY = nStartY; nY != nEndY; nY += nYInc )
616 InsertPoint( Point( nX, nY ), nLineId, nStartY == nY, eLineType );
618 if ( nD < 0L )
619 nD += nDY2;
620 else
621 nD += nDYX, nX += nXInc;
625 // last point
626 InsertPoint( Point( nEndX, nEndY ), nLineId, TRUE, eLineType );
629 return TRUE;
632 // -----------------------------------------------------------------------
634 // search for appropriate place for the new point
636 BOOL ImplRegion::InsertPoint( const Point &rPoint, long nLineID,
637 BOOL bEndPoint, LineType eLineType )
639 DBG_ASSERT( mpFirstBand != NULL, "ImplRegion::InsertPoint - no bands available!" );
641 if ( rPoint.Y() == mpLastCheckedBand->mnYTop )
643 mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType );
644 return TRUE;
647 if ( rPoint.Y() > mpLastCheckedBand->mnYTop )
649 // Search ascending
650 while ( mpLastCheckedBand )
652 // Insert point if possible
653 if ( rPoint.Y() == mpLastCheckedBand->mnYTop )
655 mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType );
656 return TRUE;
659 mpLastCheckedBand = mpLastCheckedBand->mpNextBand;
662 DBG_ERROR( "ImplRegion::InsertPoint reached the end of the list!" );
664 else
666 // Search descending
667 while ( mpLastCheckedBand )
669 // Insert point if possible
670 if ( rPoint.Y() == mpLastCheckedBand->mnYTop )
672 mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType );
673 return TRUE;
676 mpLastCheckedBand = mpLastCheckedBand->mpPrevBand;
679 DBG_ERROR( "ImplRegion::InsertPoint reached the beginning of the list!" );
682 DBG_ERROR( "ImplRegion::InsertPoint point not inserted!" );
684 // reinitialize pointer (should never be reached!)
685 mpLastCheckedBand = mpFirstBand;
687 return FALSE;
690 // -----------------------------------------------------------------------
692 // search for appropriate places for the new bands
694 void ImplRegion::InsertBands( long nTop, long nBottom )
696 // region empty? -> set rectagle as first entry!
697 if ( !mpFirstBand )
699 // add band with boundaries of the rectangle
700 mpFirstBand = new ImplRegionBand( nTop, nBottom );
701 return;
704 // find/insert bands for the boundaries of the rectangle
705 BOOL bTopBoundaryInserted = FALSE;
706 BOOL bTop2BoundaryInserted = FALSE;
707 BOOL bBottomBoundaryInserted = FALSE;
709 // special case: top boundary is above the first band
710 ImplRegionBand* pNewBand;
711 if ( nTop < mpFirstBand->mnYTop )
713 // create new band above the first in the list
714 pNewBand = new ImplRegionBand( nTop, mpFirstBand->mnYTop );
715 if ( nBottom < mpFirstBand->mnYTop )
716 pNewBand->mnYBottom = nBottom;
718 // insert band into the list
719 pNewBand->mpNextBand = mpFirstBand;
720 mpFirstBand = pNewBand;
722 bTopBoundaryInserted = TRUE;
725 // insert band(s) into the list
726 ImplRegionBand* pBand = mpFirstBand;
727 while ( pBand )
729 // Insert Bands if possible
730 if ( !bTopBoundaryInserted )
731 bTopBoundaryInserted = InsertSingleBand( pBand, nTop - 1 );
733 if ( !bTop2BoundaryInserted )
734 bTop2BoundaryInserted = InsertSingleBand( pBand, nTop );
736 if ( !bBottomBoundaryInserted && (nTop != nBottom) )
737 bBottomBoundaryInserted = InsertSingleBand( pBand, nBottom );
739 // both boundaries inserted? -> nothing more to do
740 if ( bTopBoundaryInserted && bTop2BoundaryInserted && bBottomBoundaryInserted )
741 break;
743 // insert bands between two bands if neccessary
744 if ( pBand->mpNextBand )
746 if ( (pBand->mnYBottom + 1) < pBand->mpNextBand->mnYTop )
748 // copy band with list and set new boundary
749 pNewBand = new ImplRegionBand( pBand->mnYBottom+1,
750 pBand->mpNextBand->mnYTop-1 );
752 // insert band into the list
753 pNewBand->mpNextBand = pBand->mpNextBand;
754 pBand->mpNextBand = pNewBand;
758 pBand = pBand->mpNextBand;
762 // -----------------------------------------------------------------------
764 // create new band and insert it into the list
766 BOOL ImplRegion::InsertSingleBand( ImplRegionBand* pBand,
767 long nYBandPosition )
769 // boundary already included in band with height 1? -> nothing to do!
770 if ( (pBand->mnYTop == pBand->mnYBottom) &&
771 (nYBandPosition == pBand->mnYTop) )
772 return TRUE;
774 // insert single height band on top?
775 ImplRegionBand* pNewBand;
776 if ( nYBandPosition == pBand->mnYTop )
778 // copy band with list and set new boundary
779 pNewBand = new ImplRegionBand( *pBand );
780 pNewBand->mnYTop = nYBandPosition+1;
782 // insert band into the list
783 pNewBand->mpNextBand = pBand->mpNextBand;
784 pBand->mnYBottom = nYBandPosition;
785 pBand->mpNextBand = pNewBand;
787 return TRUE;
790 // top of new rectangle within the current band? -> insert new band and copy data
791 if ( (nYBandPosition > pBand->mnYTop) &&
792 (nYBandPosition < pBand->mnYBottom) )
794 // copy band with list and set new boundary
795 pNewBand = new ImplRegionBand( *pBand );
796 pNewBand->mnYTop = nYBandPosition;
798 // insert band into the list
799 pNewBand->mpNextBand = pBand->mpNextBand;
800 pBand->mnYBottom = nYBandPosition;
801 pBand->mpNextBand = pNewBand;
803 // copy band with list and set new boundary
804 pNewBand = new ImplRegionBand( *pBand );
805 pNewBand->mnYTop = nYBandPosition;
807 // insert band into the list
808 pBand->mpNextBand->mnYTop = nYBandPosition+1;
810 pNewBand->mpNextBand = pBand->mpNextBand;
811 pBand->mnYBottom = nYBandPosition - 1;
812 pBand->mpNextBand = pNewBand;
814 return TRUE;
817 // create new band behind the current in the list
818 if ( !pBand->mpNextBand )
820 if ( nYBandPosition == pBand->mnYBottom )
822 // copy band with list and set new boundary
823 pNewBand = new ImplRegionBand( *pBand );
824 pNewBand->mnYTop = pBand->mnYBottom;
825 pNewBand->mnYBottom = nYBandPosition;
827 pBand->mnYBottom = nYBandPosition-1;
829 // append band to the list
830 pBand->mpNextBand = pNewBand;
831 return TRUE;
834 if ( nYBandPosition > pBand->mnYBottom )
836 // create new band
837 pNewBand = new ImplRegionBand( pBand->mnYBottom + 1, nYBandPosition );
839 // append band to the list
840 pBand->mpNextBand = pNewBand;
841 return TRUE;
845 return FALSE;
848 // ------------------------------------------------------------------------
850 void ImplRegion::InsertBand (ImplRegionBand* pPreviousBand, ImplRegionBand* pBandToInsert)
852 OSL_ASSERT(pBandToInsert!=NULL);
854 if (pPreviousBand == NULL)
856 // Insert band before all others.
857 if (mpFirstBand != NULL)
858 mpFirstBand->mpPrevBand = pBandToInsert;
859 pBandToInsert->mpNextBand = mpFirstBand;
860 mpFirstBand = pBandToInsert;
862 else
864 // Insert band directly after pPreviousBand.
865 pBandToInsert->mpNextBand = pPreviousBand->mpNextBand;
866 pPreviousBand->mpNextBand = pBandToInsert;
867 pBandToInsert->mpPrevBand = pPreviousBand;
871 // ------------------------------------------------------------------------
873 void ImplRegion::Union( long nLeft, long nTop, long nRight, long nBottom )
875 DBG_ASSERT( nLeft <= nRight, "ImplRegion::Union() - nLeft > nRight" );
876 DBG_ASSERT( nTop <= nBottom, "ImplRegion::Union() - nTop > nBottom" );
878 // process union
879 ImplRegionBand* pBand = mpFirstBand;
880 while ( pBand )
882 if ( pBand->mnYTop >= nTop )
884 if ( pBand->mnYBottom <= nBottom )
885 pBand->Union( nLeft, nRight );
886 else
888 #ifdef DBG_UTIL
889 long nCurY = pBand->mnYBottom;
890 pBand = pBand->mpNextBand;
891 while ( pBand )
893 if ( (pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY) )
895 DBG_ERROR( "ImplRegion::Union() - Bands not sorted!" );
897 pBand = pBand->mpNextBand;
899 #endif
900 break;
904 pBand = pBand->mpNextBand;
908 // -----------------------------------------------------------------------
910 void ImplRegion::Exclude( long nLeft, long nTop, long nRight, long nBottom )
912 DBG_ASSERT( nLeft <= nRight, "ImplRegion::Exclude() - nLeft > nRight" );
913 DBG_ASSERT( nTop <= nBottom, "ImplRegion::Exclude() - nTop > nBottom" );
915 // process exclude
916 ImplRegionBand* pBand = mpFirstBand;
917 while ( pBand )
919 if ( pBand->mnYTop >= nTop )
921 if ( pBand->mnYBottom <= nBottom )
922 pBand->Exclude( nLeft, nRight );
923 else
925 #ifdef DBG_UTIL
926 long nCurY = pBand->mnYBottom;
927 pBand = pBand->mpNextBand;
928 while ( pBand )
930 if ( (pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY) )
932 DBG_ERROR( "ImplRegion::Exclude() - Bands not sorted!" );
934 pBand = pBand->mpNextBand;
936 #endif
937 break;
941 pBand = pBand->mpNextBand;
945 // -----------------------------------------------------------------------
947 void ImplRegion::XOr( long nLeft, long nTop, long nRight, long nBottom )
949 DBG_ASSERT( nLeft <= nRight, "ImplRegion::Exclude() - nLeft > nRight" );
950 DBG_ASSERT( nTop <= nBottom, "ImplRegion::Exclude() - nTop > nBottom" );
952 // process xor
953 ImplRegionBand* pBand = mpFirstBand;
954 while ( pBand )
956 if ( pBand->mnYTop >= nTop )
958 if ( pBand->mnYBottom <= nBottom )
959 pBand->XOr( nLeft, nRight );
960 else
962 #ifdef DBG_UTIL
963 long nCurY = pBand->mnYBottom;
964 pBand = pBand->mpNextBand;
965 while ( pBand )
967 if ( (pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY) )
969 DBG_ERROR( "ImplRegion::XOr() - Bands not sorted!" );
971 pBand = pBand->mpNextBand;
973 #endif
974 break;
978 pBand = pBand->mpNextBand;
982 // -----------------------------------------------------------------------
984 // remove empty bands
986 BOOL ImplRegion::OptimizeBandList()
988 DBG_ASSERT( (this != &aImplNullRegion) && (this != &aImplEmptyRegion),
989 "ImplRegion::OptimizeBandList() - Empty oder NULL-Region" );
991 mnRectCount = 0;
993 ImplRegionBand* pPrevBand = 0;
994 ImplRegionBand* pBand = mpFirstBand;
995 while ( pBand )
997 const BOOL bBTEqual = pBand->mpNextBand &&
998 (pBand->mnYBottom == pBand->mpNextBand->mnYTop);
1000 // no separation? -> remove!
1001 if ( pBand->IsEmpty() || (bBTEqual && (pBand->mnYBottom == pBand->mnYTop)) )
1003 // save pointer
1004 ImplRegionBand* pOldBand = pBand;
1006 // previous element of the list
1007 if ( pBand == mpFirstBand )
1008 mpFirstBand = pBand->mpNextBand;
1009 else
1010 pPrevBand->mpNextBand = pBand->mpNextBand;
1012 pBand = pBand->mpNextBand;
1013 delete pOldBand;
1015 else
1017 // fixup
1018 if ( bBTEqual )
1019 pBand->mnYBottom = pBand->mpNextBand->mnYTop-1;
1021 // this and next band with equal separations? -> combine!
1022 if ( pBand->mpNextBand &&
1023 ((pBand->mnYBottom+1) == pBand->mpNextBand->mnYTop) &&
1024 (*pBand == *pBand->mpNextBand) )
1026 // expand current height
1027 pBand->mnYBottom = pBand->mpNextBand->mnYBottom;
1029 // remove next band from list
1030 ImplRegionBand* pDeletedBand = pBand->mpNextBand;
1031 pBand->mpNextBand = pDeletedBand->mpNextBand;
1032 delete pDeletedBand;
1034 // check band again!
1036 else
1038 // count rectangles within band
1039 ImplRegionBandSep* pSep = pBand->mpFirstSep;
1040 while ( pSep )
1042 mnRectCount++;
1043 pSep = pSep->mpNextSep;
1046 pPrevBand = pBand;
1047 pBand = pBand->mpNextBand;
1052 #ifdef DBG_UTIL
1053 pBand = mpFirstBand;
1054 while ( pBand )
1056 DBG_ASSERT( pBand->mpFirstSep != NULL,
1057 "Exiting ImplRegion::OptimizeBandList(): empty band in region!" );
1059 if ( pBand->mnYBottom < pBand->mnYTop )
1060 DBG_ERROR( "ImplRegion::OptimizeBandList(): YBottomBoundary < YTopBoundary" );
1062 if ( pBand->mpNextBand )
1064 if ( pBand->mnYBottom >= pBand->mpNextBand->mnYTop )
1065 DBG_ERROR( "ImplRegion::OptimizeBandList(): overlapping bands in region!" );
1068 pBand = pBand->mpNextBand;
1070 #endif
1072 return (mnRectCount != 0);
1075 // =======================================================================
1077 void Region::ImplCopyData()
1079 mpImplRegion->mnRefCount--;
1080 mpImplRegion = new ImplRegion( *mpImplRegion );
1083 // =======================================================================
1085 Region::Region()
1087 DBG_CTOR( Region, ImplDbgTestRegion );
1089 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1092 // -----------------------------------------------------------------------
1094 Region::Region( RegionType eType )
1096 DBG_CTOR( Region, ImplDbgTestRegion );
1097 DBG_ASSERT( (eType == REGION_NULL) || (eType == REGION_EMPTY),
1098 "Region( RegionType ) - RegionType != EMPTY/NULL" );
1100 if ( eType == REGION_NULL )
1101 mpImplRegion = (ImplRegion*)(&aImplNullRegion);
1102 else
1103 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1106 // -----------------------------------------------------------------------
1108 Region::Region( const Rectangle& rRect )
1110 DBG_CTOR( Region, ImplDbgTestRegion );
1112 ImplCreateRectRegion( rRect );
1115 // -----------------------------------------------------------------------
1117 Region::Region( const Polygon& rPolygon )
1119 DBG_CTOR( Region, ImplDbgTestRegion );
1120 DBG_CHKOBJ( &rPolygon, Polygon, NULL );
1122 ImplCreatePolyPolyRegion( rPolygon );
1125 // -----------------------------------------------------------------------
1127 Region::Region( const PolyPolygon& rPolyPoly )
1129 DBG_CTOR( Region, ImplDbgTestRegion );
1130 DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL );
1132 ImplCreatePolyPolyRegion( rPolyPoly );
1135 // -----------------------------------------------------------------------
1137 Region::Region( const basegfx::B2DPolyPolygon& rPolyPoly )
1139 DBG_CTOR( Region, ImplDbgTestRegion );
1140 DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL );
1142 mpImplRegion = new ImplRegion( rPolyPoly );
1145 // -----------------------------------------------------------------------
1147 Region::Region( const Region& rRegion )
1149 DBG_CTOR( Region, ImplDbgTestRegion );
1150 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion );
1151 DBG_ASSERT( rRegion.mpImplRegion->mnRefCount < 0xFFFFFFFE, "Region: RefCount overflow" );
1153 // copy pointer to instance of implementation
1154 mpImplRegion = rRegion.mpImplRegion;
1155 if ( mpImplRegion->mnRefCount )
1156 mpImplRegion->mnRefCount++;
1159 // -----------------------------------------------------------------------
1161 Region::~Region()
1163 DBG_DTOR( Region, ImplDbgTestRegion );
1165 // statische Object haben RefCount von 0
1166 if ( mpImplRegion->mnRefCount )
1168 if ( mpImplRegion->mnRefCount > 1 )
1169 mpImplRegion->mnRefCount--;
1170 else
1171 delete mpImplRegion;
1175 // -----------------------------------------------------------------------
1177 void Region::ImplCreateRectRegion( const Rectangle& rRect )
1179 if ( rRect.IsEmpty() )
1180 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1181 else
1183 // get justified rectangle
1184 long nTop = Min( rRect.Top(), rRect.Bottom() );
1185 long nBottom = Max( rRect.Top(), rRect.Bottom() );
1186 long nLeft = Min( rRect.Left(), rRect.Right() );
1187 long nRight = Max( rRect.Left(), rRect.Right() );
1189 // create instance of implementation class
1190 mpImplRegion = new ImplRegion();
1192 // add band with boundaries of the rectangle
1193 mpImplRegion->mpFirstBand = new ImplRegionBand( nTop, nBottom );
1195 // Set left and right boundaries of the band
1196 mpImplRegion->mpFirstBand->Union( nLeft, nRight );
1197 mpImplRegion->mnRectCount = 1;
1201 // -----------------------------------------------------------------------
1203 void Region::ImplCreatePolyPolyRegion( const PolyPolygon& rPolyPoly )
1205 const USHORT nPolyCount = rPolyPoly.Count();
1206 if ( nPolyCount )
1208 // polypolygon empty? -> empty region
1209 const Rectangle aRect( rPolyPoly.GetBoundRect() );
1211 if ( !aRect.IsEmpty() )
1213 // width OR height == 1 ? => Rectangular region
1214 if ( (aRect.GetWidth() == 1)
1215 || (aRect.GetHeight() == 1)
1216 || rPolyPoly.IsRect() )
1218 ImplCreateRectRegion( aRect );
1220 else
1221 mpImplRegion = new ImplRegion( rPolyPoly );
1223 else
1224 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1226 else
1227 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1230 // -----------------------------------------------------------------------
1232 void Region::ImplPolyPolyRegionToBandRegionFunc()
1234 // ensure to subdivide when bezier segemnts are used, it's going to
1235 // be expanded to rectangles
1236 PolyPolygon aPolyPoly;
1237 GetPolyPolygon().AdaptiveSubdivide(aPolyPoly);
1239 if ( mpImplRegion->mnRefCount > 1 )
1240 mpImplRegion->mnRefCount--;
1241 else
1242 delete mpImplRegion;
1244 if ( aPolyPoly.Count() )
1246 // polypolygon empty? -> empty region
1247 const Rectangle aRect( aPolyPoly.GetBoundRect() );
1249 if ( !aRect.IsEmpty() )
1251 if (ImplIsPolygonRectilinear(aPolyPoly))
1253 // For rectilinear polygons there is an optimized band conversion.
1254 mpImplRegion = ImplRectilinearPolygonToBands(aPolyPoly);
1256 else
1258 mpImplRegion = ImplGeneralPolygonToBands(aPolyPoly, aRect);
1261 // Convert points into seps.
1262 ImplRegionBand* pRegionBand = mpImplRegion->mpFirstBand;
1263 while ( pRegionBand )
1265 // generate separations from the lines and process union
1266 pRegionBand->ProcessPoints();
1267 pRegionBand = pRegionBand->mpNextBand;
1270 // Optimize list of bands. Adjacent bands with identical lists
1271 // of seps are joined.
1272 if ( !mpImplRegion->OptimizeBandList() )
1274 delete mpImplRegion;
1275 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1278 else
1279 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1281 else
1282 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1285 // -----------------------------------------------------------------------
1287 void Region::Move( long nHorzMove, long nVertMove )
1289 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1291 // no region data? -> nothing to do
1292 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
1293 return;
1295 // no own instance data? -> make own copy!
1296 if ( mpImplRegion->mnRefCount > 1 )
1297 ImplCopyData();
1299 if ( mpImplRegion->mpPolyPoly )
1300 mpImplRegion->mpPolyPoly->Move( nHorzMove, nVertMove );
1301 else if( mpImplRegion->mpB2DPolyPoly )
1303 ::basegfx::B2DHomMatrix aTransform;
1304 aTransform.translate( nHorzMove, nVertMove );
1305 mpImplRegion->mpB2DPolyPoly->transform( aTransform );
1307 else
1309 ImplRegionBand* pBand = mpImplRegion->mpFirstBand;
1310 while ( pBand )
1312 // process the vertical move
1313 if ( nVertMove != 0)
1315 pBand->mnYTop = pBand->mnYTop + nVertMove;
1316 pBand->mnYBottom = pBand->mnYBottom + nVertMove;
1319 // process the horizontal move
1320 if ( nHorzMove != 0)
1321 pBand->MoveX( nHorzMove );
1323 pBand = pBand->mpNextBand;
1328 // -----------------------------------------------------------------------
1330 void Region::Scale( double fScaleX, double fScaleY )
1332 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1334 // no region data? -> nothing to do
1335 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
1336 return;
1338 // no own instance data? -> make own copy!
1339 if ( mpImplRegion->mnRefCount > 1 )
1340 ImplCopyData();
1342 if ( mpImplRegion->mpPolyPoly )
1343 mpImplRegion->mpPolyPoly->Scale( fScaleX, fScaleY );
1344 else if( mpImplRegion->mpB2DPolyPoly )
1346 ::basegfx::B2DHomMatrix aTransform;
1347 aTransform.scale( fScaleX, fScaleY );
1348 mpImplRegion->mpB2DPolyPoly->transform( aTransform );
1350 else
1352 ImplRegionBand* pBand = mpImplRegion->mpFirstBand;
1353 while ( pBand )
1355 // process the vertical move
1356 if ( fScaleY != 0.0 )
1358 pBand->mnYTop = FRound( pBand->mnYTop * fScaleY );
1359 pBand->mnYBottom = FRound( pBand->mnYBottom * fScaleY );
1362 // process the horizontal move
1363 if ( fScaleX != 0.0 )
1364 pBand->ScaleX( fScaleX );
1366 pBand = pBand->mpNextBand;
1371 // -----------------------------------------------------------------------
1373 BOOL Region::Union( const Rectangle& rRect )
1375 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1377 // is rectangle empty? -> nothing to do
1378 if ( rRect.IsEmpty() )
1379 return TRUE;
1381 ImplPolyPolyRegionToBandRegion();
1383 // no instance data? -> create!
1384 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
1385 mpImplRegion = new ImplRegion();
1387 // no own instance data? -> make own copy!
1388 if ( mpImplRegion->mnRefCount > 1 )
1389 ImplCopyData();
1391 // get justified rectangle
1392 long nLeft = Min( rRect.Left(), rRect.Right() );
1393 long nTop = Min( rRect.Top(), rRect.Bottom() );
1394 long nRight = Max( rRect.Left(), rRect.Right() );
1395 long nBottom = Max( rRect.Top(), rRect.Bottom() );
1397 // insert bands if the boundaries are not allready in the list
1398 mpImplRegion->InsertBands( nTop, nBottom );
1400 // process union
1401 mpImplRegion->Union( nLeft, nTop, nRight, nBottom );
1403 // cleanup
1404 if ( !mpImplRegion->OptimizeBandList() )
1406 delete mpImplRegion;
1407 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1410 return TRUE;
1413 // -----------------------------------------------------------------------
1415 BOOL Region::Intersect( const Rectangle& rRect )
1417 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1419 // is rectangle empty? -> nothing to do
1420 if ( rRect.IsEmpty() )
1422 // statische Object haben RefCount von 0
1423 if ( mpImplRegion->mnRefCount )
1425 if ( mpImplRegion->mnRefCount > 1 )
1426 mpImplRegion->mnRefCount--;
1427 else
1428 delete mpImplRegion;
1430 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1431 return TRUE;
1434 // #103137# Avoid banding for special cases
1435 if ( mpImplRegion->mpPolyPoly )
1437 // #127431# make ImplRegion unique, if not already.
1438 if( mpImplRegion->mnRefCount > 1 )
1440 mpImplRegion->mnRefCount--;
1441 mpImplRegion = new ImplRegion( *mpImplRegion->mpPolyPoly );
1444 // use the PolyPolygon::Clip method for rectangles, this is
1445 // fairly simple (does not even use GPC) and saves us from
1446 // unnecessary banding
1447 mpImplRegion->mpPolyPoly->Clip( rRect );
1449 return TRUE;
1451 else
1452 ImplPolyPolyRegionToBandRegion();
1454 // is region empty? -> nothing to do!
1455 if ( mpImplRegion == &aImplEmptyRegion )
1456 return TRUE;
1458 // get justified rectangle
1459 long nLeft = Min( rRect.Left(), rRect.Right() );
1460 long nTop = Min( rRect.Top(), rRect.Bottom() );
1461 long nRight = Max( rRect.Left(), rRect.Right() );
1462 long nBottom = Max( rRect.Top(), rRect.Bottom() );
1464 // is own region NULL-region? -> copy data!
1465 if ( mpImplRegion == &aImplNullRegion )
1467 // create instance of implementation class
1468 mpImplRegion = new ImplRegion();
1470 // add band with boundaries of the rectangle
1471 mpImplRegion->mpFirstBand = new ImplRegionBand( nTop, nBottom );
1473 // Set left and right boundaries of the band
1474 mpImplRegion->mpFirstBand->Union( nLeft, nRight );
1475 mpImplRegion->mnRectCount = 1;
1477 return TRUE;
1480 // no own instance data? -> make own copy!
1481 if ( mpImplRegion->mnRefCount > 1 )
1482 ImplCopyData();
1484 // insert bands if the boundaries are not allready in the list
1485 mpImplRegion->InsertBands( nTop, nBottom );
1487 // process intersections
1488 ImplRegionBand* pPrevBand = 0;
1489 ImplRegionBand* pBand = mpImplRegion->mpFirstBand;
1490 while ( pBand )
1492 // band within intersection boundary? -> process. otherwise remove
1493 if ( (pBand->mnYTop >= nTop) &&
1494 (pBand->mnYBottom <= nBottom) )
1496 // process intersection
1497 pBand->Intersect( nLeft, nRight );
1499 pPrevBand = pBand;
1500 pBand = pBand->mpNextBand;
1502 else
1504 ImplRegionBand* pOldBand = pBand;
1505 if ( pBand == mpImplRegion->mpFirstBand )
1506 mpImplRegion->mpFirstBand = pBand->mpNextBand;
1507 else
1508 pPrevBand->mpNextBand = pBand->mpNextBand;
1509 pBand = pBand->mpNextBand;
1510 delete pOldBand;
1514 // cleanup
1515 if ( !mpImplRegion->OptimizeBandList() )
1517 delete mpImplRegion;
1518 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1521 return TRUE;
1524 // -----------------------------------------------------------------------
1526 BOOL Region::Exclude( const Rectangle& rRect )
1528 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1530 // is rectangle empty? -> nothing to do
1531 if ( rRect.IsEmpty() )
1532 return TRUE;
1534 ImplPolyPolyRegionToBandRegion();
1536 // no instance data? -> create!
1537 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
1538 return TRUE;
1540 // no own instance data? -> make own copy!
1541 if ( mpImplRegion->mnRefCount > 1 )
1542 ImplCopyData();
1544 // get justified rectangle
1545 long nLeft = Min( rRect.Left(), rRect.Right() );
1546 long nTop = Min( rRect.Top(), rRect.Bottom() );
1547 long nRight = Max( rRect.Left(), rRect.Right() );
1548 long nBottom = Max( rRect.Top(), rRect.Bottom() );
1550 // insert bands if the boundaries are not allready in the list
1551 mpImplRegion->InsertBands( nTop, nBottom );
1553 // process exclude
1554 mpImplRegion->Exclude( nLeft, nTop, nRight, nBottom );
1556 // cleanup
1557 if ( !mpImplRegion->OptimizeBandList() )
1559 delete mpImplRegion;
1560 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1563 return TRUE;
1566 // -----------------------------------------------------------------------
1568 BOOL Region::XOr( const Rectangle& rRect )
1570 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1572 // is rectangle empty? -> nothing to do
1573 if ( rRect.IsEmpty() )
1574 return TRUE;
1576 ImplPolyPolyRegionToBandRegion();
1578 // no instance data? -> create!
1579 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
1580 mpImplRegion = new ImplRegion();
1582 // no own instance data? -> make own copy!
1583 if ( mpImplRegion->mnRefCount > 1 )
1584 ImplCopyData();
1586 // get justified rectangle
1587 long nLeft = Min( rRect.Left(), rRect.Right() );
1588 long nTop = Min( rRect.Top(), rRect.Bottom() );
1589 long nRight = Max( rRect.Left(), rRect.Right() );
1590 long nBottom = Max( rRect.Top(), rRect.Bottom() );
1592 // insert bands if the boundaries are not allready in the list
1593 mpImplRegion->InsertBands( nTop, nBottom );
1595 // process xor
1596 mpImplRegion->XOr( nLeft, nTop, nRight, nBottom );
1598 // cleanup
1599 if ( !mpImplRegion->OptimizeBandList() )
1601 delete mpImplRegion;
1602 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1605 return TRUE;
1608 // -----------------------------------------------------------------------
1610 BOOL Region::Union( const Region& rRegion )
1612 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1614 ImplPolyPolyRegionToBandRegion();
1615 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion();
1617 // is region empty or null? -> nothing to do
1618 if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) )
1619 return TRUE;
1621 // no instance data? -> create!
1622 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
1623 mpImplRegion = new ImplRegion();
1625 // no own instance data? -> make own copy!
1626 if ( mpImplRegion->mnRefCount > 1 )
1627 ImplCopyData();
1629 // Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden
1630 ImplRegionBand* pBand = rRegion.mpImplRegion->mpFirstBand;
1631 while ( pBand )
1633 // insert bands if the boundaries are not allready in the list
1634 mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom );
1636 // process all elements of the list
1637 ImplRegionBandSep* pSep = pBand->mpFirstSep;
1638 while ( pSep )
1640 mpImplRegion->Union( pSep->mnXLeft, pBand->mnYTop,
1641 pSep->mnXRight, pBand->mnYBottom );
1642 pSep = pSep->mpNextSep;
1645 pBand = pBand->mpNextBand;
1648 // cleanup
1649 if ( !mpImplRegion->OptimizeBandList() )
1651 delete mpImplRegion;
1652 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1655 return TRUE;
1658 // -----------------------------------------------------------------------
1660 BOOL Region::Intersect( const Region& rRegion )
1662 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1664 // same instance data? -> nothing to do!
1665 if ( mpImplRegion == rRegion.mpImplRegion )
1666 return TRUE;
1668 ImplPolyPolyRegionToBandRegion();
1669 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion();
1671 if ( mpImplRegion == &aImplEmptyRegion )
1672 return TRUE;
1674 // is region null? -> nothing to do
1675 if ( rRegion.mpImplRegion == &aImplNullRegion )
1676 return TRUE;
1678 // is rectangle empty? -> nothing to do
1679 if ( rRegion.mpImplRegion == &aImplEmptyRegion )
1681 // statische Object haben RefCount von 0
1682 if ( mpImplRegion->mnRefCount )
1684 if ( mpImplRegion->mnRefCount > 1 )
1685 mpImplRegion->mnRefCount--;
1686 else
1687 delete mpImplRegion;
1689 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1690 return TRUE;
1693 // is own region NULL-region? -> copy data!
1694 if ( mpImplRegion == &aImplNullRegion)
1696 mpImplRegion = rRegion.mpImplRegion;
1697 rRegion.mpImplRegion->mnRefCount++;
1698 return TRUE;
1701 // Wenn wir weniger Rechtecke haben, drehen wir den Intersect-Aufruf um
1702 if ( mpImplRegion->mnRectCount+2 < rRegion.mpImplRegion->mnRectCount )
1704 Region aTempRegion = rRegion;
1705 aTempRegion.Intersect( *this );
1706 *this = aTempRegion;
1708 else
1710 // no own instance data? -> make own copy!
1711 if ( mpImplRegion->mnRefCount > 1 )
1712 ImplCopyData();
1714 // mark all bands as untouched
1715 ImplRegionBand* pBand = mpImplRegion->mpFirstBand;
1716 while ( pBand )
1718 pBand->mbTouched = FALSE;
1719 pBand = pBand->mpNextBand;
1722 pBand = rRegion.mpImplRegion->mpFirstBand;
1723 while ( pBand )
1725 // insert bands if the boundaries are not allready in the list
1726 mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom );
1728 // process all elements of the list
1729 ImplRegionBandSep* pSep = pBand->mpFirstSep;
1730 while ( pSep )
1732 // left boundary?
1733 if ( pSep == pBand->mpFirstSep )
1735 // process intersection and do not remove untouched bands
1736 mpImplRegion->Exclude( LONG_MIN+1, pBand->mnYTop,
1737 pSep->mnXLeft-1, pBand->mnYBottom );
1740 // right boundary?
1741 if ( pSep->mpNextSep == NULL )
1743 // process intersection and do not remove untouched bands
1744 mpImplRegion->Exclude( pSep->mnXRight+1, pBand->mnYTop,
1745 LONG_MAX-1, pBand->mnYBottom );
1747 else
1749 // process intersection and do not remove untouched bands
1750 mpImplRegion->Exclude( pSep->mnXRight+1, pBand->mnYTop,
1751 pSep->mpNextSep->mnXLeft-1, pBand->mnYBottom );
1754 pSep = pSep->mpNextSep;
1757 pBand = pBand->mpNextBand;
1760 // remove all untouched bands if bands allready left
1761 ImplRegionBand* pPrevBand = 0;
1762 pBand = mpImplRegion->mpFirstBand;
1763 while ( pBand )
1765 if ( !pBand->mbTouched )
1767 // save pointer
1768 ImplRegionBand* pOldBand = pBand;
1770 // previous element of the list
1771 if ( pBand == mpImplRegion->mpFirstBand )
1772 mpImplRegion->mpFirstBand = pBand->mpNextBand;
1773 else
1774 pPrevBand->mpNextBand = pBand->mpNextBand;
1776 pBand = pBand->mpNextBand;
1777 delete pOldBand;
1779 else
1781 pPrevBand = pBand;
1782 pBand = pBand->mpNextBand;
1786 // cleanup
1787 if ( !mpImplRegion->OptimizeBandList() )
1789 delete mpImplRegion;
1790 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1794 return TRUE;
1797 // -----------------------------------------------------------------------
1799 BOOL Region::Exclude( const Region& rRegion )
1801 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1803 ImplPolyPolyRegionToBandRegion();
1804 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion();
1806 // is region empty or null? -> nothing to do
1807 if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) )
1808 return TRUE;
1810 // no instance data? -> nothing to do
1811 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
1812 return TRUE;
1814 // no own instance data? -> make own copy!
1815 if ( mpImplRegion->mnRefCount > 1 )
1816 ImplCopyData();
1818 // Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden
1819 ImplRegionBand* pBand = rRegion.mpImplRegion->mpFirstBand;
1820 while ( pBand )
1822 // insert bands if the boundaries are not allready in the list
1823 mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom );
1825 // process all elements of the list
1826 ImplRegionBandSep* pSep = pBand->mpFirstSep;
1827 while ( pSep )
1829 mpImplRegion->Exclude( pSep->mnXLeft, pBand->mnYTop,
1830 pSep->mnXRight, pBand->mnYBottom );
1831 pSep = pSep->mpNextSep;
1834 // Wir optimieren schon in der Schleife, da wir davon
1835 // ausgehen, das wir insgesammt weniger Baender ueberpruefen
1836 // muessen
1837 if ( !mpImplRegion->OptimizeBandList() )
1839 delete mpImplRegion;
1840 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1841 break;
1844 pBand = pBand->mpNextBand;
1847 return TRUE;
1850 // -----------------------------------------------------------------------
1852 BOOL Region::XOr( const Region& rRegion )
1854 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1856 ImplPolyPolyRegionToBandRegion();
1857 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion();
1859 // is region empty or null? -> nothing to do
1860 if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) )
1861 return TRUE;
1863 // no own instance data? -> XOr = copy
1864 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
1866 *this = rRegion;
1867 return TRUE;
1870 // no own instance data? -> make own copy!
1871 if ( mpImplRegion->mnRefCount > 1 )
1872 ImplCopyData();
1874 // Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden
1875 ImplRegionBand* pBand = rRegion.mpImplRegion->mpFirstBand;
1876 while ( pBand )
1878 // insert bands if the boundaries are not allready in the list
1879 mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom );
1881 // process all elements of the list
1882 ImplRegionBandSep* pSep = pBand->mpFirstSep;
1883 while ( pSep )
1885 mpImplRegion->XOr( pSep->mnXLeft, pBand->mnYTop,
1886 pSep->mnXRight, pBand->mnYBottom );
1887 pSep = pSep->mpNextSep;
1890 pBand = pBand->mpNextBand;
1893 // cleanup
1894 if ( !mpImplRegion->OptimizeBandList() )
1896 delete mpImplRegion;
1897 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
1900 return TRUE;
1903 // -----------------------------------------------------------------------
1905 Rectangle Region::GetBoundRect() const
1907 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1909 Rectangle aRect;
1911 // no internal data? -> region is empty!
1912 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
1913 return aRect;
1915 // PolyPolygon data im Imp structure?
1916 if ( mpImplRegion->mpPolyPoly )
1917 return mpImplRegion->mpPolyPoly->GetBoundRect();
1918 if( mpImplRegion->mpB2DPolyPoly )
1920 const basegfx::B2DRange aRange = basegfx::tools::getRange( *mpImplRegion->mpB2DPolyPoly );
1921 aRect.SetPos( Point( (int)aRange.getMinX(), (int)aRange.getMinY() ) );
1922 aRect.SetSize( Size( (int)aRange.getWidth(), (int)aRange.getHeight() ) );
1923 return aRect;
1926 // no band in the list? -> region is empty!
1927 if ( !mpImplRegion->mpFirstBand )
1928 return aRect;
1930 // get the boundaries of the first band
1931 long nYTop = mpImplRegion->mpFirstBand->mnYTop;
1932 long nYBottom = mpImplRegion->mpFirstBand->mnYBottom;
1933 long nXLeft = mpImplRegion->mpFirstBand->GetXLeftBoundary();
1934 long nXRight = mpImplRegion->mpFirstBand->GetXRightBoundary();
1936 // look in the band list (don't test first band again!)
1937 ImplRegionBand* pBand = mpImplRegion->mpFirstBand->mpNextBand;
1938 while ( pBand )
1940 nYBottom = pBand->mnYBottom;
1941 nXLeft = Min( nXLeft, pBand->GetXLeftBoundary() );
1942 nXRight = Max( nXRight, pBand->GetXRightBoundary() );
1944 pBand = pBand->mpNextBand;
1947 // set rectangle
1948 aRect = Rectangle( nXLeft, nYTop, nXRight, nYBottom );
1949 return aRect;
1952 // -----------------------------------------------------------------------
1954 BOOL Region::HasPolyPolygon() const
1956 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1957 if( !mpImplRegion )
1958 return false;
1959 if( mpImplRegion->mpPolyPoly )
1960 return true;
1961 if( mpImplRegion->mpB2DPolyPoly )
1962 return true;
1963 return false;
1966 // -----------------------------------------------------------------------
1968 PolyPolygon Region::GetPolyPolygon() const
1970 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1972 PolyPolygon aRet;
1974 if( mpImplRegion->mpPolyPoly )
1975 aRet = *mpImplRegion->mpPolyPoly;
1976 else if( mpImplRegion->mpB2DPolyPoly )
1978 // the polygon needs to be converted
1979 aRet = PolyPolygon( *mpImplRegion->mpB2DPolyPoly );
1980 // TODO: cache the converted polygon?
1981 // mpImplRegion->mpB2DPolyPoly = aRet;
1984 return aRet;
1987 // -----------------------------------------------------------------------
1989 const basegfx::B2DPolyPolygon Region::GetB2DPolyPolygon() const
1991 DBG_CHKTHIS( Region, ImplDbgTestRegion );
1993 basegfx::B2DPolyPolygon aRet;
1995 if( mpImplRegion->mpB2DPolyPoly )
1996 aRet = *mpImplRegion->mpB2DPolyPoly;
1997 else if( mpImplRegion->mpPolyPoly )
1999 // the polygon needs to be converted
2000 aRet = mpImplRegion->mpPolyPoly->getB2DPolyPolygon();
2001 // TODO: cache the converted polygon?
2002 // mpImplRegion->mpB2DPolyPoly = aRet;
2005 return aRet;
2008 // -----------------------------------------------------------------------
2010 BOOL Region::ImplGetFirstRect( ImplRegionInfo& rImplRegionInfo,
2011 long& rX, long& rY,
2012 long& rWidth, long& rHeight ) const
2014 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2016 ((Region*)this)->ImplPolyPolyRegionToBandRegion();
2018 // no internal data? -> region is empty!
2019 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
2020 return FALSE;
2022 // no band in the list? -> region is empty!
2023 if ( mpImplRegion->mpFirstBand == NULL )
2024 return FALSE;
2026 // initialise pointer for first access
2027 ImplRegionBand* pCurrRectBand = mpImplRegion->mpFirstBand;
2028 ImplRegionBandSep* pCurrRectBandSep = pCurrRectBand->mpFirstSep;
2030 DBG_ASSERT( pCurrRectBandSep != NULL, "Erstes Band wurde nicht optimiert." );
2031 if ( !pCurrRectBandSep )
2032 return FALSE;
2034 // get boundaries of current rectangle
2035 rX = pCurrRectBandSep->mnXLeft;
2036 rY = pCurrRectBand->mnYTop;
2037 rWidth = pCurrRectBandSep->mnXRight - pCurrRectBandSep->mnXLeft + 1;
2038 rHeight = pCurrRectBand->mnYBottom - pCurrRectBand->mnYTop + 1;
2040 // save pointers
2041 rImplRegionInfo.mpVoidCurrRectBand = (void*)pCurrRectBand;
2042 rImplRegionInfo.mpVoidCurrRectBandSep = (void*)pCurrRectBandSep;
2044 return TRUE;
2047 // -----------------------------------------------------------------------
2049 BOOL Region::ImplGetNextRect( ImplRegionInfo& rImplRegionInfo,
2050 long& rX, long& rY,
2051 long& rWidth, long& rHeight ) const
2053 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2055 // no internal data? -> region is empty!
2056 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
2057 return FALSE;
2059 // get last pointers
2060 ImplRegionBand* pCurrRectBand = (ImplRegionBand*)rImplRegionInfo.mpVoidCurrRectBand;
2061 ImplRegionBandSep* pCurrRectBandSep = (ImplRegionBandSep*)rImplRegionInfo.mpVoidCurrRectBandSep;
2063 // get next separation from current band
2064 pCurrRectBandSep = pCurrRectBandSep->mpNextSep;
2066 // no separation found? -> go to next band!
2067 if ( !pCurrRectBandSep )
2069 // get next band
2070 pCurrRectBand = pCurrRectBand->mpNextBand;
2072 // no band found? -> not further rectangles!
2073 if( !pCurrRectBand )
2074 return FALSE;
2076 // get first separation in current band
2077 pCurrRectBandSep = pCurrRectBand->mpFirstSep;
2080 // get boundaries of current rectangle
2081 rX = pCurrRectBandSep->mnXLeft;
2082 rY = pCurrRectBand->mnYTop;
2083 rWidth = pCurrRectBandSep->mnXRight - pCurrRectBandSep->mnXLeft + 1;
2084 rHeight = pCurrRectBand->mnYBottom - pCurrRectBand->mnYTop + 1;
2086 // save new pointers
2087 rImplRegionInfo.mpVoidCurrRectBand = (void*)pCurrRectBand;
2088 rImplRegionInfo.mpVoidCurrRectBandSep = (void*)pCurrRectBandSep;
2090 return TRUE;
2093 // -----------------------------------------------------------------------
2095 RegionType Region::GetType() const
2097 if ( mpImplRegion == &aImplEmptyRegion )
2098 return REGION_EMPTY;
2099 else if ( mpImplRegion == &aImplNullRegion )
2100 return REGION_NULL;
2101 else if ( mpImplRegion->mnRectCount == 1 )
2102 return REGION_RECTANGLE;
2103 else
2104 return REGION_COMPLEX;
2107 // -----------------------------------------------------------------------
2109 BOOL Region::IsInside( const Point& rPoint ) const
2111 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2113 // PolyPolygon data im Imp structure?
2114 ((Region*)this)->ImplPolyPolyRegionToBandRegion();
2116 if ( mpImplRegion->mpPolyPoly )
2117 return mpImplRegion->mpPolyPoly->IsInside( rPoint );
2120 // no instance data? -> not inside
2121 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
2122 return FALSE;
2124 // search band list
2125 ImplRegionBand* pBand = mpImplRegion->mpFirstBand;
2126 while ( pBand )
2128 // is point within band?
2129 if ( (pBand->mnYTop <= rPoint.Y()) &&
2130 (pBand->mnYBottom >= rPoint.Y()) )
2132 // is point within separation of the band?
2133 if ( pBand->IsInside( rPoint.X() ) )
2134 return TRUE;
2135 else
2136 return FALSE;
2139 pBand = pBand->mpNextBand;
2142 return FALSE;
2145 // -----------------------------------------------------------------------
2147 BOOL Region::IsInside( const Rectangle& rRect ) const
2149 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2151 // is rectangle empty? -> not inside
2152 if ( rRect.IsEmpty() )
2153 return FALSE;
2155 // no instance data? -> not inside
2156 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
2157 return FALSE;
2159 // create region from rectangle and intersect own region
2160 Region aRegion = rRect;
2161 aRegion.Exclude( *this );
2163 // rectangle is inside if exclusion is empty
2164 return aRegion.IsEmpty();
2167 // -----------------------------------------------------------------------
2169 BOOL Region::IsOver( const Rectangle& rRect ) const
2171 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2173 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
2174 return FALSE;
2176 // Can we optimize this ??? - is used in StarDraw for brushes pointers
2177 // Why we have no IsOver for Regions ???
2178 // create region from rectangle and intersect own region
2179 Region aRegion = rRect;
2180 aRegion.Intersect( *this );
2182 // rectangle is over if include is not empty
2183 return !aRegion.IsEmpty();
2186 // -----------------------------------------------------------------------
2188 void Region::SetNull()
2190 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2192 // statische Object haben RefCount von 0
2193 if ( mpImplRegion->mnRefCount )
2195 if ( mpImplRegion->mnRefCount > 1 )
2196 mpImplRegion->mnRefCount--;
2197 else
2198 delete mpImplRegion;
2201 // set new type
2202 mpImplRegion = (ImplRegion*)(&aImplNullRegion);
2205 // -----------------------------------------------------------------------
2207 void Region::SetEmpty()
2209 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2211 // statische Object haben RefCount von 0
2212 if ( mpImplRegion->mnRefCount )
2214 if ( mpImplRegion->mnRefCount > 1 )
2215 mpImplRegion->mnRefCount--;
2216 else
2217 delete mpImplRegion;
2220 // set new type
2221 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
2224 // -----------------------------------------------------------------------
2226 Region& Region::operator=( const Region& rRegion )
2228 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2229 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion );
2230 DBG_ASSERT( rRegion.mpImplRegion->mnRefCount < 0xFFFFFFFE, "Region: RefCount overflow" );
2232 // Zuerst Referenzcounter erhoehen, damit man sich selbst zuweisen kann
2233 // RefCount == 0 fuer statische Objekte
2234 if ( rRegion.mpImplRegion->mnRefCount )
2235 rRegion.mpImplRegion->mnRefCount++;
2237 // statische Object haben RefCount von 0
2238 if ( mpImplRegion->mnRefCount )
2240 if ( mpImplRegion->mnRefCount > 1 )
2241 mpImplRegion->mnRefCount--;
2242 else
2243 delete mpImplRegion;
2246 mpImplRegion = rRegion.mpImplRegion;
2247 return *this;
2250 // -----------------------------------------------------------------------
2252 Region& Region::operator=( const Rectangle& rRect )
2254 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2256 // statische Object haben RefCount von 0
2257 if ( mpImplRegion->mnRefCount )
2259 if ( mpImplRegion->mnRefCount > 1 )
2260 mpImplRegion->mnRefCount--;
2261 else
2262 delete mpImplRegion;
2265 ImplCreateRectRegion( rRect );
2266 return *this;
2269 // -----------------------------------------------------------------------
2271 BOOL Region::operator==( const Region& rRegion ) const
2273 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2274 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion );
2276 // reference to same object? -> equal!
2277 if ( mpImplRegion == rRegion.mpImplRegion )
2278 return TRUE;
2280 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
2281 return FALSE;
2283 if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) )
2284 return FALSE;
2286 if ( rRegion.mpImplRegion->mpPolyPoly && mpImplRegion->mpPolyPoly )
2287 return *rRegion.mpImplRegion->mpPolyPoly == *mpImplRegion->mpPolyPoly;
2288 else
2290 ((Region*)this)->ImplPolyPolyRegionToBandRegion();
2291 ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion();
2293 // Eine der beiden Regions kann jetzt Empty sein
2294 if ( mpImplRegion == rRegion.mpImplRegion )
2295 return TRUE;
2297 if ( mpImplRegion == &aImplEmptyRegion )
2298 return FALSE;
2300 if ( rRegion.mpImplRegion == &aImplEmptyRegion )
2301 return FALSE;
2304 // initialise pointers
2305 ImplRegionBand* pOwnRectBand = mpImplRegion->mpFirstBand;
2306 ImplRegionBandSep* pOwnRectBandSep = pOwnRectBand->mpFirstSep;
2307 ImplRegionBand* pSecondRectBand = rRegion.mpImplRegion->mpFirstBand;
2308 ImplRegionBandSep* pSecondRectBandSep = pSecondRectBand->mpFirstSep;
2309 while ( pOwnRectBandSep && pSecondRectBandSep )
2311 // get boundaries of current rectangle
2312 long nOwnXLeft = pOwnRectBandSep->mnXLeft;
2313 long nSecondXLeft = pSecondRectBandSep->mnXLeft;
2314 if ( nOwnXLeft != nSecondXLeft )
2315 return FALSE;
2317 long nOwnYTop = pOwnRectBand->mnYTop;
2318 long nSecondYTop = pSecondRectBand->mnYTop;
2319 if ( nOwnYTop != nSecondYTop )
2320 return FALSE;
2322 long nOwnXRight = pOwnRectBandSep->mnXRight;
2323 long nSecondXRight = pSecondRectBandSep->mnXRight;
2324 if ( nOwnXRight != nSecondXRight )
2325 return FALSE;
2327 long nOwnYBottom = pOwnRectBand->mnYBottom;
2328 long nSecondYBottom = pSecondRectBand->mnYBottom;
2329 if ( nOwnYBottom != nSecondYBottom )
2330 return FALSE;
2332 // get next separation from current band
2333 pOwnRectBandSep = pOwnRectBandSep->mpNextSep;
2335 // no separation found? -> go to next band!
2336 if ( !pOwnRectBandSep )
2338 // get next band
2339 pOwnRectBand = pOwnRectBand->mpNextBand;
2341 // get first separation in current band
2342 if( pOwnRectBand )
2343 pOwnRectBandSep = pOwnRectBand->mpFirstSep;
2346 // get next separation from current band
2347 pSecondRectBandSep = pSecondRectBandSep->mpNextSep;
2349 // no separation found? -> go to next band!
2350 if ( !pSecondRectBandSep )
2352 // get next band
2353 pSecondRectBand = pSecondRectBand->mpNextBand;
2355 // get first separation in current band
2356 if( pSecondRectBand )
2357 pSecondRectBandSep = pSecondRectBand->mpFirstSep;
2360 if ( pOwnRectBandSep && !pSecondRectBandSep )
2361 return FALSE;
2363 if ( !pOwnRectBandSep && pSecondRectBandSep )
2364 return FALSE;
2367 return TRUE;
2370 // -----------------------------------------------------------------------
2372 enum StreamEntryType { STREAMENTRY_BANDHEADER, STREAMENTRY_SEPARATION, STREAMENTRY_END };
2374 SvStream& operator>>( SvStream& rIStrm, Region& rRegion )
2376 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion );
2378 VersionCompat aCompat( rIStrm, STREAM_READ );
2379 UINT16 nVersion;
2380 UINT16 nTmp16;
2382 // statische Object haben RefCount von 0
2383 if ( rRegion.mpImplRegion->mnRefCount )
2385 if ( rRegion.mpImplRegion->mnRefCount > 1 )
2386 rRegion.mpImplRegion->mnRefCount--;
2387 else
2388 delete rRegion.mpImplRegion;
2391 // get version of streamed region
2392 rIStrm >> nVersion;
2394 // get type of region
2395 rIStrm >> nTmp16;
2397 RegionType meStreamedType = (RegionType)nTmp16;
2399 switch( meStreamedType )
2401 case REGION_NULL:
2402 rRegion.mpImplRegion = (ImplRegion*)&aImplNullRegion;
2403 break;
2405 case REGION_EMPTY:
2406 rRegion.mpImplRegion = (ImplRegion*)&aImplEmptyRegion;
2407 break;
2409 default:
2411 // create instance of implementation class
2412 rRegion.mpImplRegion = new ImplRegion();
2414 // get header from first element
2415 rIStrm >> nTmp16;
2417 // get all bands
2418 rRegion.mpImplRegion->mnRectCount = 0;
2419 ImplRegionBand* pCurrBand = NULL;
2420 while ( (StreamEntryType)nTmp16 != STREAMENTRY_END )
2422 // insert new band or new separation?
2423 if ( (StreamEntryType)nTmp16 == STREAMENTRY_BANDHEADER )
2425 long nYTop;
2426 long nYBottom;
2428 rIStrm >> nYTop;
2429 rIStrm >> nYBottom;
2431 // create band
2432 ImplRegionBand* pNewBand = new ImplRegionBand( nYTop, nYBottom );
2434 // first element? -> set as first into the list
2435 if ( !pCurrBand )
2436 rRegion.mpImplRegion->mpFirstBand = pNewBand;
2437 else
2438 pCurrBand->mpNextBand = pNewBand;
2440 // save pointer for next creation
2441 pCurrBand = pNewBand;
2443 else
2445 long nXLeft;
2446 long nXRight;
2448 rIStrm >> nXLeft;
2449 rIStrm >> nXRight;
2451 // add separation
2452 if ( pCurrBand )
2454 pCurrBand->Union( nXLeft, nXRight );
2455 rRegion.mpImplRegion->mnRectCount++;
2459 // get next header
2460 rIStrm >> nTmp16;
2463 if( aCompat.GetVersion() >= 2 )
2465 BOOL bHasPolyPolygon;
2467 rIStrm >> bHasPolyPolygon;
2469 if( bHasPolyPolygon )
2471 delete rRegion.mpImplRegion->mpPolyPoly;
2472 rRegion.mpImplRegion->mpPolyPoly = new PolyPolygon;
2473 rIStrm >> *( rRegion.mpImplRegion->mpPolyPoly );
2477 break;
2480 return rIStrm;
2483 // -----------------------------------------------------------------------
2485 SvStream& operator<<( SvStream& rOStrm, const Region& rRegion )
2487 DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion );
2489 UINT16 nVersion = 2;
2490 VersionCompat aCompat( rOStrm, STREAM_WRITE, nVersion );
2491 Region aTmpRegion( rRegion );
2493 // use tmp region to avoid destruction of internal region (polypolygon) of rRegion
2494 aTmpRegion.ImplPolyPolyRegionToBandRegion();
2496 // put version
2497 rOStrm << nVersion;
2499 // put type
2500 rOStrm << (UINT16)aTmpRegion.GetType();
2502 // put all bands if not null or empty
2503 if ( (aTmpRegion.mpImplRegion != &aImplEmptyRegion) && (aTmpRegion.mpImplRegion != &aImplNullRegion) )
2505 ImplRegionBand* pBand = aTmpRegion.mpImplRegion->mpFirstBand;
2506 while ( pBand )
2508 // put boundaries
2509 rOStrm << (UINT16) STREAMENTRY_BANDHEADER;
2510 rOStrm << pBand->mnYTop;
2511 rOStrm << pBand->mnYBottom;
2513 // put separations of current band
2514 ImplRegionBandSep* pSep = pBand->mpFirstSep;
2515 while ( pSep )
2517 // put separation
2518 rOStrm << (UINT16) STREAMENTRY_SEPARATION;
2519 rOStrm << pSep->mnXLeft;
2520 rOStrm << pSep->mnXRight;
2522 // next separation from current band
2523 pSep = pSep->mpNextSep;
2526 pBand = pBand->mpNextBand;
2529 // put endmarker
2530 rOStrm << (UINT16) STREAMENTRY_END;
2532 // write polypolygon if available
2533 const BOOL bHasPolyPolygon = rRegion.HasPolyPolygon();
2534 rOStrm << bHasPolyPolygon;
2536 if( bHasPolyPolygon )
2537 rOStrm << rRegion.GetPolyPolygon();
2540 return rOStrm;
2543 // -----------------------------------------------------------------------
2545 void Region::ImplBeginAddRect()
2547 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2549 // statische Object haben RefCount von 0
2550 if ( mpImplRegion->mnRefCount )
2552 if ( mpImplRegion->mnRefCount > 1 )
2553 mpImplRegion->mnRefCount--;
2554 else
2555 delete mpImplRegion;
2558 // create fresh region
2559 mpImplRegion = new ImplRegion();
2562 // -----------------------------------------------------------------------
2564 BOOL Region::ImplAddRect( const Rectangle& rRect )
2566 // Hier kein CheckThis, da nicht alle Daten auf Stand
2568 if ( rRect.IsEmpty() )
2569 return TRUE;
2571 // get justified rectangle
2572 long nTop;
2573 long nBottom;
2574 long nLeft;
2575 long nRight;
2576 if ( rRect.Top() <= rRect.Bottom() )
2578 nTop = rRect.Top();
2579 nBottom = rRect.Bottom();
2581 else
2583 nTop = rRect.Bottom();
2584 nBottom = rRect.Top();
2586 if ( rRect.Left() <= rRect.Right() )
2588 nLeft = rRect.Left();
2589 nRight = rRect.Right();
2591 else
2593 nLeft = rRect.Right();
2594 nRight = rRect.Left();
2597 if ( !mpImplRegion->mpLastCheckedBand )
2599 // create new band
2600 mpImplRegion->mpLastCheckedBand = new ImplRegionBand( nTop, nBottom );
2602 // set band as current
2603 mpImplRegion->mpFirstBand = mpImplRegion->mpLastCheckedBand;
2604 mpImplRegion->mpLastCheckedBand->Union( nLeft, nRight );
2606 else
2608 DBG_ASSERT( nTop >= mpImplRegion->mpLastCheckedBand->mnYTop,
2609 "Region::ImplAddRect() - nTopY < nLastTopY" );
2611 // new band? create it!
2612 if ( (nTop != mpImplRegion->mpLastCheckedBand->mnYTop) ||
2613 (nBottom != mpImplRegion->mpLastCheckedBand->mnYBottom) )
2615 // create new band
2616 ImplRegionBand* pNewRegionBand = new ImplRegionBand( nTop, nBottom );
2618 // append band to the end
2619 mpImplRegion->mpLastCheckedBand->mpNextBand = pNewRegionBand;
2621 // skip to the new band
2622 mpImplRegion->mpLastCheckedBand = mpImplRegion->mpLastCheckedBand->mpNextBand;
2625 // Insert Sep
2626 mpImplRegion->mpLastCheckedBand->Union( nLeft, nRight );
2629 return TRUE;
2632 // -----------------------------------------------------------------------
2634 void Region::ImplEndAddRect()
2636 // check if we are empty
2637 if ( !mpImplRegion->mpFirstBand )
2639 delete mpImplRegion;
2640 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
2641 return;
2644 // check if we have somthing to optimize
2645 if ( !mpImplRegion->mpFirstBand->mpNextBand )
2647 // update mpImplRegion->mnRectCount, because no OptimizeBandList is called
2648 ImplRegionBandSep* pSep = mpImplRegion->mpFirstBand->mpFirstSep;
2649 mpImplRegion->mnRectCount = 0;
2650 while( pSep )
2652 mpImplRegion->mnRectCount++;
2653 pSep = pSep->mpNextSep;
2656 // Erst hier testen, da hier die Daten wieder stimmen
2657 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2658 return;
2661 // have to revert list? -> do it now!
2662 if ( mpImplRegion->mpFirstBand->mnYTop >
2663 mpImplRegion->mpFirstBand->mpNextBand->mnYTop )
2665 ImplRegionBand * pNewFirstRegionBand;
2667 // initialize temp list with first element
2668 pNewFirstRegionBand = mpImplRegion->mpFirstBand;
2669 mpImplRegion->mpFirstBand = mpImplRegion->mpFirstBand->mpNextBand;
2670 pNewFirstRegionBand->mpNextBand = NULL;
2672 // insert elements to the temp list
2673 while ( mpImplRegion->mpFirstBand )
2675 ImplRegionBand * pSavedRegionBand = pNewFirstRegionBand;
2676 pNewFirstRegionBand = mpImplRegion->mpFirstBand;
2677 mpImplRegion->mpFirstBand = mpImplRegion->mpFirstBand->mpNextBand;
2678 pNewFirstRegionBand->mpNextBand = pSavedRegionBand;
2681 // set temp list as new list
2682 mpImplRegion->mpFirstBand = pNewFirstRegionBand;
2685 // cleanup
2686 if ( !mpImplRegion->OptimizeBandList() )
2688 delete mpImplRegion;
2689 mpImplRegion = (ImplRegion*)(&aImplEmptyRegion);
2692 // Erst hier testen, da hier die Daten wieder stimmen
2693 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2696 // -----------------------------------------------------------------------
2698 ULONG Region::GetRectCount() const
2700 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2702 ((Region*)this)->ImplPolyPolyRegionToBandRegion();
2704 #ifdef DBG_UTIL
2705 ULONG nCount = 0;
2707 // all bands if not null or empty
2708 if ( (mpImplRegion != &aImplEmptyRegion) && (mpImplRegion != &aImplNullRegion) )
2710 ImplRegionBand* pBand = mpImplRegion->mpFirstBand;
2711 while ( pBand )
2713 ImplRegionBandSep* pSep = pBand->mpFirstSep;
2714 while( pSep )
2716 nCount++;
2717 pSep = pSep->mpNextSep;
2720 pBand = pBand->mpNextBand;
2724 DBG_ASSERT( mpImplRegion->mnRectCount == nCount, "Region: invalid mnRectCount!" );
2725 #endif
2727 return mpImplRegion->mnRectCount;
2730 // -----------------------------------------------------------------------
2732 RegionHandle Region::BeginEnumRects()
2734 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2736 ImplPolyPolyRegionToBandRegion();
2738 // no internal data? -> region is empty!
2739 if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) )
2740 return 0;
2742 // no band in the list? -> region is empty!
2743 if ( mpImplRegion->mpFirstBand == NULL )
2745 DBG_ASSERT( mpImplRegion->mpFirstBand, "Region::BeginEnumRects() First Band is Empty!" );
2746 return 0;
2749 ImplRegionHandle* pData = new ImplRegionHandle;
2750 pData->mpRegion = new Region( *this );
2751 pData->mbFirst = TRUE;
2753 // save pointers
2754 pData->mpCurrRectBand = pData->mpRegion->mpImplRegion->mpFirstBand;
2755 pData->mpCurrRectBandSep = pData->mpCurrRectBand->mpFirstSep;
2757 return (RegionHandle)pData;
2760 // -----------------------------------------------------------------------
2762 BOOL Region::GetEnumRects( RegionHandle pVoidData, Rectangle& rRect )
2764 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2766 ImplRegionHandle* pData = (ImplRegionHandle*)pVoidData;
2767 if ( !pData )
2768 return FALSE;
2770 if ( pData->mbFirst )
2771 pData->mbFirst = FALSE;
2772 else
2774 // get next separation from current band
2775 pData->mpCurrRectBandSep = pData->mpCurrRectBandSep->mpNextSep;
2777 // no separation found? -> go to next band!
2778 if ( !pData->mpCurrRectBandSep )
2780 // get next band
2781 pData->mpCurrRectBand = pData->mpCurrRectBand->mpNextBand;
2783 // no band found? -> not further rectangles!
2784 if ( !pData->mpCurrRectBand )
2785 return FALSE;
2787 // get first separation in current band
2788 pData->mpCurrRectBandSep = pData->mpCurrRectBand->mpFirstSep;
2792 // get boundaries of current rectangle
2793 rRect.Top() = pData->mpCurrRectBand->mnYTop;
2794 rRect.Bottom() = pData->mpCurrRectBand->mnYBottom;
2795 rRect.Left() = pData->mpCurrRectBandSep->mnXLeft;
2796 rRect.Right() = pData->mpCurrRectBandSep->mnXRight;
2797 return TRUE;
2800 // -----------------------------------------------------------------------
2802 void Region::EndEnumRects( RegionHandle pVoidData )
2804 DBG_CHKTHIS( Region, ImplDbgTestRegion );
2806 ImplRegionHandle* pData = (ImplRegionHandle*)pVoidData;
2807 if ( !pData )
2808 return;
2810 // cleanup
2811 delete pData->mpRegion;
2812 delete pData;
2815 // -----------------------------------------------------------------------
2817 static inline bool ImplPolygonRectTest( const Polygon& rPoly, Rectangle* pRectOut = NULL )
2819 bool bIsRect = false;
2820 const Point* pPoints = rPoly.GetConstPointAry();
2821 USHORT nPoints = rPoly.GetSize();
2822 if( nPoints == 4 || (nPoints == 5 && pPoints[0] == pPoints[4]) )
2824 long nX1 = pPoints[0].X(), nX2 = pPoints[2].X(),
2825 nY1 = pPoints[0].Y(), nY2 = pPoints[2].Y();
2826 if( ( (pPoints[1].X() == nX1 && pPoints[3].X() == nX2) &&
2827 (pPoints[1].Y() == nY2 && pPoints[3].Y() == nY1) )
2829 ( (pPoints[1].X() == nX2 && pPoints[3].X() == nX1) &&
2830 (pPoints[1].Y() == nY1 && pPoints[3].Y() == nY2) ) )
2832 bIsRect = true;
2833 if( pRectOut )
2835 long nSwap;
2836 if( nX2 < nX1 )
2838 nSwap = nX2;
2839 nX2 = nX1;
2840 nX1 = nSwap;
2842 if( nY2 < nY1 )
2844 nSwap = nY2;
2845 nY2 = nY1;
2846 nY1 = nSwap;
2848 if( nX2 != nX1 )
2849 nX2--;
2850 if( nY2 != nY1 )
2851 nY2--;
2852 pRectOut->Left() = nX1;
2853 pRectOut->Right() = nX2;
2854 pRectOut->Top() = nY1;
2855 pRectOut->Bottom() = nY2;
2859 return bIsRect;
2862 Region Region::GetRegionFromPolyPolygon( const PolyPolygon& rPolyPoly )
2864 //return Region( rPolyPoly );
2866 // check if it's worth extracting the XOr'ing the Rectangles
2867 // empiricism shows that break even between XOr'ing rectangles separately
2868 // and ImplPolyPolyRegionToBandRegion is at half rectangles/half polygons
2869 int nPolygonRects = 0, nPolygonPolygons = 0;
2870 int nPolygons = rPolyPoly.Count();
2872 for( USHORT i = 0; i < nPolygons; i++ )
2874 const Polygon& rPoly = rPolyPoly[i];
2875 if( ImplPolygonRectTest( rPoly ) )
2876 nPolygonRects++;
2877 else
2878 nPolygonPolygons++;
2880 if( nPolygonPolygons > nPolygonRects )
2881 return Region( rPolyPoly );
2883 Region aResult;
2884 Rectangle aRect;
2885 for( USHORT i = 0; i < nPolygons; i++ )
2887 const Polygon& rPoly = rPolyPoly[i];
2888 if( ImplPolygonRectTest( rPoly, &aRect ) )
2889 aResult.XOr( aRect );
2890 else
2891 aResult.XOr( Region(rPoly) );
2893 return aResult;