xref: /aoo42x/main/vcl/source/gdi/regionband.cxx (revision e6f63103)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_vcl.hxx"
24 
25 #include <tools/stream.hxx>
26 #include <tools/debug.hxx>
27 #include <regionband.hxx>
28 
29 //////////////////////////////////////////////////////////////////////////////
30 
31 DBG_NAME( RegionBand )
32 DBG_NAMEEX( Polygon )
33 DBG_NAMEEX( PolyPolygon )
34 
35 //////////////////////////////////////////////////////////////////////////////
36 
37 RegionBand::RegionBand()
38 :   mpFirstBand(0),
39     mpLastCheckedBand(0)
40 {
41     DBG_CTOR(RegionBand, ImplDbgTestRegionBand);
42 }
43 
44 RegionBand::RegionBand(const RegionBand& rRef)
45 :   mpFirstBand(0),
46     mpLastCheckedBand(0)
47 {
48     *this = rRef;
49     DBG_CTOR(RegionBand, ImplDbgTestRegionBand);
50 }
51 
52 RegionBand& RegionBand::operator=(const RegionBand& rRef)
53 {
54 	ImplRegionBand* pPrevBand = 0;
55 	ImplRegionBand* pBand = rRef.mpFirstBand;
56 
57     while(pBand)
58 	{
59 		ImplRegionBand* pNewBand = new ImplRegionBand(*pBand);
60 
61 		// first element? -> set as first into the list
62 		if(pBand == rRef.mpFirstBand)
63         {
64 			mpFirstBand = pNewBand;
65         }
66 		else
67         {
68 			pPrevBand->mpNextBand = pNewBand;
69         }
70 
71 		pPrevBand = pNewBand;
72 		pBand = pBand->mpNextBand;
73 	}
74 
75     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
76     DBG_CHKOBJ(&rRef, RegionBand, ImplDbgTestRegionBand);
77 
78     return *this;
79 }
80 
81 RegionBand::RegionBand(const Rectangle& rRect)
82 :   mpFirstBand(0),
83     mpLastCheckedBand(0)
84 {
85     const long nTop(std::min(rRect.Top(), rRect.Bottom()));
86     const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
87     const long nLeft(std::min(rRect.Left(), rRect.Right()));
88     const long nRight(std::max(rRect.Left(), rRect.Right()));
89 
90     // add band with boundaries of the rectangle
91     mpFirstBand = new ImplRegionBand(nTop, nBottom);
92 
93     // Set left and right boundaries of the band
94     mpFirstBand->Union(nLeft, nRight);
95 
96     DBG_CTOR(RegionBand, ImplDbgTestRegionBand);
97 }
98 
99 void RegionBand::implReset()
100 {
101 	ImplRegionBand* pBand = mpFirstBand;
102 
103     while(pBand)
104 	{
105 		ImplRegionBand* pTempBand = pBand->mpNextBand;
106 		delete pBand;
107 		pBand = pTempBand;
108 	}
109 
110     mpLastCheckedBand = 0;
111 
112     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
113 }
114 
115 RegionBand::~RegionBand()
116 {
117     implReset();
118     DBG_DTOR(RegionBand, ImplDbgTestRegionBand);
119 }
120 
121 bool RegionBand::operator==( const RegionBand& rRegionBand ) const
122 {
123     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
124     DBG_CHKOBJ(&rRegionBand, RegionBand, ImplDbgTestRegionBand);
125 
126 	// initialise pointers
127 	ImplRegionBand* 	 pOwnRectBand = mpFirstBand;
128 	ImplRegionBandSep*	 pOwnRectBandSep = pOwnRectBand->mpFirstSep;
129 	ImplRegionBand* 	 pSecondRectBand = rRegionBand.mpFirstBand;
130 	ImplRegionBandSep*	 pSecondRectBandSep = pSecondRectBand->mpFirstSep;
131 
132     while ( pOwnRectBandSep && pSecondRectBandSep )
133 	{
134 		// get boundaries of current rectangle
135 		long nOwnXLeft = pOwnRectBandSep->mnXLeft;
136 		long nSecondXLeft = pSecondRectBandSep->mnXLeft;
137 
138         if ( nOwnXLeft != nSecondXLeft )
139         {
140 			return false;
141         }
142 
143 		long nOwnYTop = pOwnRectBand->mnYTop;
144 		long nSecondYTop = pSecondRectBand->mnYTop;
145 
146         if ( nOwnYTop != nSecondYTop )
147         {
148 			return false;
149         }
150 
151 		long nOwnXRight = pOwnRectBandSep->mnXRight;
152 		long nSecondXRight = pSecondRectBandSep->mnXRight;
153 
154         if ( nOwnXRight != nSecondXRight )
155         {
156 			return false;
157         }
158 
159 		long nOwnYBottom = pOwnRectBand->mnYBottom;
160 		long nSecondYBottom = pSecondRectBand->mnYBottom;
161 
162         if ( nOwnYBottom != nSecondYBottom )
163         {
164 			return false;
165         }
166 
167 		// get next separation from current band
168 		pOwnRectBandSep = pOwnRectBandSep->mpNextSep;
169 
170 		// no separation found? -> go to next band!
171 		if ( !pOwnRectBandSep )
172 		{
173 			// get next band
174 			pOwnRectBand = pOwnRectBand->mpNextBand;
175 
176 			// get first separation in current band
177 			if( pOwnRectBand )
178             {
179 				pOwnRectBandSep = pOwnRectBand->mpFirstSep;
180             }
181 		}
182 
183 		// get next separation from current band
184 		pSecondRectBandSep = pSecondRectBandSep->mpNextSep;
185 
186 		// no separation found? -> go to next band!
187 		if ( !pSecondRectBandSep )
188 		{
189 			// get next band
190 			pSecondRectBand = pSecondRectBand->mpNextBand;
191 
192 			// get first separation in current band
193 			if( pSecondRectBand )
194             {
195 				pSecondRectBandSep = pSecondRectBand->mpFirstSep;
196             }
197 		}
198 
199 		if ( pOwnRectBandSep && !pSecondRectBandSep )
200         {
201 			return false;
202         }
203 
204 		if ( !pOwnRectBandSep && pSecondRectBandSep )
205         {
206 			return false;
207         }
208 	}
209 
210 	return true;
211 }
212 
213 enum StreamEntryType { STREAMENTRY_BANDHEADER, STREAMENTRY_SEPARATION, STREAMENTRY_END };
214 
215 void RegionBand::load(SvStream& rIStrm)
216 {
217     // clear this nstance's data
218     implReset();
219 
220     // get all bands
221     ImplRegionBand* pCurrBand = 0;
222 
223     // get header from first element
224     sal_uInt16 nTmp16(0);
225     rIStrm >> nTmp16;
226 
227     while(STREAMENTRY_END != (StreamEntryType)nTmp16)
228     {
229         // insert new band or new separation?
230         if(STREAMENTRY_BANDHEADER == (StreamEntryType)nTmp16)
231         {
232             long nYTop;
233             long nYBottom;
234 
235             rIStrm >> nYTop;
236             rIStrm >> nYBottom;
237 
238             // create band
239             ImplRegionBand* pNewBand = new ImplRegionBand( nYTop, nYBottom );
240 
241             // first element? -> set as first into the list
242             if ( !pCurrBand )
243             {
244                 mpFirstBand = pNewBand;
245             }
246             else
247             {
248                 pCurrBand->mpNextBand = pNewBand;
249             }
250 
251             // save pointer for next creation
252             pCurrBand = pNewBand;
253         }
254         else
255         {
256             long nXLeft;
257             long nXRight;
258 
259             rIStrm >> nXLeft;
260             rIStrm >> nXRight;
261 
262             // add separation
263             if ( pCurrBand )
264             {
265                 pCurrBand->Union( nXLeft, nXRight );
266             }
267         }
268 
269         if( rIStrm.IsEof() )
270         {
271             DBG_ERROR( "premature end of region stream" );
272             implReset();
273             return;
274         }
275 
276         // get next header
277         rIStrm >> nTmp16;
278     }
279 
280     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
281 }
282 
283 void RegionBand::save(SvStream& rOStrm) const
284 {
285     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
286     ImplRegionBand* pBand = mpFirstBand;
287 
288     while(pBand)
289     {
290         // put boundaries
291         rOStrm << (sal_uInt16)STREAMENTRY_BANDHEADER;
292         rOStrm << pBand->mnYTop;
293         rOStrm << pBand->mnYBottom;
294 
295         // put separations of current band
296         ImplRegionBandSep* pSep = pBand->mpFirstSep;
297 
298         while(pSep)
299         {
300             // put separation
301             rOStrm << (sal_uInt16)STREAMENTRY_SEPARATION;
302             rOStrm << pSep->mnXLeft;
303             rOStrm << pSep->mnXRight;
304 
305             // next separation from current band
306             pSep = pSep->mpNextSep;
307         }
308 
309         pBand = pBand->mpNextBand;
310     }
311 
312     // put endmarker
313     rOStrm << (sal_uInt16)STREAMENTRY_END;
314 }
315 
316 bool RegionBand::isSingleRectangle() const
317 {
318     // just one band?
319     if(mpFirstBand && !mpFirstBand->mpNextBand)
320     {
321         // just one sep?
322         if(mpFirstBand->mpFirstSep && !mpFirstBand->mpFirstSep->mpNextSep)
323         {
324             return true;
325         }
326     }
327 
328     return false;
329 }
330 
331 void RegionBand::InsertBand(ImplRegionBand* pPreviousBand, ImplRegionBand* pBandToInsert)
332 {
333     OSL_ASSERT(pBandToInsert!=NULL);
334 
335     if(!pPreviousBand)
336     {
337         // Insert band before all others.
338         if(mpFirstBand)
339         {
340             mpFirstBand->mpPrevBand = pBandToInsert;
341         }
342 
343         pBandToInsert->mpNextBand = mpFirstBand;
344         mpFirstBand = pBandToInsert;
345     }
346     else
347     {
348         // Insert band directly after pPreviousBand.
349         pBandToInsert->mpNextBand = pPreviousBand->mpNextBand;
350         pPreviousBand->mpNextBand = pBandToInsert;
351         pBandToInsert->mpPrevBand = pPreviousBand;
352     }
353 
354     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
355 }
356 
357 void RegionBand::processPoints()
358 {
359 	ImplRegionBand* pRegionBand = mpFirstBand;
360 
361     while(pRegionBand)
362 	{
363 		// generate separations from the lines and process union
364 		pRegionBand->ProcessPoints();
365 		pRegionBand = pRegionBand->mpNextBand;
366 	}
367 
368     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
369 }
370 
371 /** This function is similar to the RegionBand::InsertBands() method.
372     It creates a minimal set of missing bands so that the entire vertical
373     interval from nTop to nBottom is covered by bands.
374 */
375 void RegionBand::ImplAddMissingBands(const long nTop, const long nBottom)
376 {
377     // Iterate over already existing bands and add missing bands atop the
378     // first and between two bands.
379     ImplRegionBand* pPreviousBand = NULL;
380     ImplRegionBand* pBand = ImplGetFirstRegionBand();
381     long nCurrentTop (nTop);
382 
383     while (pBand != NULL && nCurrentTop<nBottom)
384     {
385         if (nCurrentTop < pBand->mnYTop)
386         {
387             // Create new band above the current band.
388             ImplRegionBand* pAboveBand = new ImplRegionBand(
389                 nCurrentTop,
390                 ::std::min(nBottom,pBand->mnYTop-1));
391             InsertBand(pPreviousBand, pAboveBand);
392         }
393 
394         // Adapt the top of the interval to prevent overlapping bands.
395         nCurrentTop = ::std::max(nTop, pBand->mnYBottom+1);
396 
397         // Advance to next band.
398         pPreviousBand = pBand;
399         pBand = pBand->mpNextBand;
400     }
401 
402     // We still have to cover two cases:
403     // 1. The region does not yet contain any bands.
404     // 2. The intervall nTop->nBottom extends past the bottom most band.
405     if (nCurrentTop <= nBottom
406         && (pBand==NULL || nBottom>pBand->mnYBottom))
407     {
408         // When there is no previous band then the new one will be the
409         // first.  Otherwise the new band is inserted behind the last band.
410         InsertBand(
411             pPreviousBand,
412             new ImplRegionBand(
413                 nCurrentTop,
414                 nBottom));
415     }
416 
417     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
418 }
419 
420 void RegionBand::CreateBandRange(long nYTop, long nYBottom)
421 {
422 	// add top band
423 	mpFirstBand = new ImplRegionBand( nYTop-1, nYTop-1 );
424 
425 	// begin first search from the first element
426 	mpLastCheckedBand = mpFirstBand;
427 	ImplRegionBand* pBand = mpFirstBand;
428 
429     for ( int i = nYTop; i <= nYBottom+1; i++ )
430 	{
431 		// create new band
432 		ImplRegionBand* pNewBand = new ImplRegionBand( i, i );
433 		pBand->mpNextBand = pNewBand;
434 
435         if ( pBand != mpFirstBand )
436         {
437 			pNewBand->mpPrevBand = pBand;
438         }
439 
440 		pBand = pBand->mpNextBand;
441 	}
442 
443     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
444 }
445 
446 bool RegionBand::InsertLine(const Point& rStartPt, const Point& rEndPt, long nLineId)
447 {
448 	long nX, nY;
449 
450 	// lines consisting of a single point do not interest here
451 	if ( rStartPt == rEndPt )
452     {
453         DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
454 		return true;
455     }
456 
457 	LineType eLineType = (rStartPt.Y() > rEndPt.Y()) ? LINE_DESCENDING : LINE_ASCENDING;
458 	if ( rStartPt.X() == rEndPt.X() )
459 	{
460 		// vertical line
461 		const long nEndY = rEndPt.Y();
462 
463 		nX = rStartPt.X();
464 		nY = rStartPt.Y();
465 
466 		if( nEndY > nY )
467 		{
468 			for ( ; nY <= nEndY; nY++ )
469 			{
470 				Point aNewPoint( nX, nY );
471 				InsertPoint( aNewPoint, nLineId,
472 							 (aNewPoint == rEndPt) || (aNewPoint == rStartPt),
473 							 eLineType );
474 			}
475 		}
476 		else
477 		{
478 			for ( ; nY >= nEndY; nY-- )
479 			{
480 				Point aNewPoint( nX, nY );
481 				InsertPoint( aNewPoint, nLineId,
482 							 (aNewPoint == rEndPt) || (aNewPoint == rStartPt),
483 							 eLineType );
484 			}
485 		}
486 	}
487 	else if ( rStartPt.Y() != rEndPt.Y() )
488 	{
489 		const long	nDX = labs( rEndPt.X() - rStartPt.X() );
490 		const long	nDY = labs( rEndPt.Y() - rStartPt.Y() );
491 		const long	nStartX = rStartPt.X();
492 		const long	nStartY = rStartPt.Y();
493 		const long	nEndX = rEndPt.X();
494 		const long	nEndY = rEndPt.Y();
495 		const long	nXInc = ( nStartX < nEndX ) ? 1L : -1L;
496 		const long	nYInc = ( nStartY < nEndY ) ? 1L : -1L;
497 
498 		if ( nDX >= nDY )
499 		{
500 			const long	nDYX = ( nDY - nDX ) << 1;
501 			const long	nDY2 = nDY << 1;
502 			long		nD = nDY2 - nDX;
503 
504 			for ( nX = nStartX, nY = nStartY; nX != nEndX; nX += nXInc )
505 			{
506 				InsertPoint( Point( nX, nY ), nLineId, nStartX == nX, eLineType );
507 
508 				if ( nD < 0L )
509 					nD += nDY2;
510 				else
511 					nD += nDYX, nY += nYInc;
512 			}
513 		}
514 		else
515 		{
516 			const long	nDYX = ( nDX - nDY ) << 1;
517 			const long	nDY2 = nDX << 1;
518 			long		nD = nDY2 - nDY;
519 
520 			for ( nX = nStartX, nY = nStartY; nY != nEndY; nY += nYInc )
521 			{
522 				InsertPoint( Point( nX, nY ), nLineId, nStartY == nY, eLineType );
523 
524 				if ( nD < 0L )
525 					nD += nDY2;
526 				else
527 					nD += nDYX, nX += nXInc;
528 			}
529 		}
530 
531 		// last point
532 		InsertPoint( Point( nEndX, nEndY ), nLineId, true, eLineType );
533 	}
534 
535     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
536 	return true;
537 }
538 
539 bool RegionBand::InsertPoint(const Point &rPoint, long nLineID, bool bEndPoint, LineType eLineType)
540 {
541 	DBG_ASSERT( mpFirstBand != NULL, "RegionBand::InsertPoint - no bands available!" );
542 
543 	if ( rPoint.Y() == mpLastCheckedBand->mnYTop )
544 	{
545 		mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType );
546 		return true;
547 	}
548 
549 	if ( rPoint.Y() > mpLastCheckedBand->mnYTop )
550 	{
551 		// Search ascending
552 		while ( mpLastCheckedBand )
553 		{
554 			// Insert point if possible
555 			if ( rPoint.Y() == mpLastCheckedBand->mnYTop )
556 			{
557 				mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType );
558                 DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
559 				return true;
560 			}
561 
562 			mpLastCheckedBand = mpLastCheckedBand->mpNextBand;
563 		}
564 
565 		DBG_ERROR( "RegionBand::InsertPoint reached the end of the list!" );
566 	}
567 	else
568 	{
569 		// Search descending
570 		while ( mpLastCheckedBand )
571 		{
572 			// Insert point if possible
573 			if ( rPoint.Y() == mpLastCheckedBand->mnYTop )
574 			{
575 				mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType );
576                 DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
577 				return true;
578 			}
579 
580 			mpLastCheckedBand = mpLastCheckedBand->mpPrevBand;
581 		}
582 
583 		DBG_ERROR( "RegionBand::InsertPoint reached the beginning of the list!" );
584 	}
585 
586 	DBG_ERROR( "RegionBand::InsertPoint point not inserted!" );
587 
588 	// reinitialize pointer (should never be reached!)
589 	mpLastCheckedBand = mpFirstBand;
590 
591     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
592 	return false;
593 }
594 
595 bool RegionBand::OptimizeBandList()
596 {
597 	ImplRegionBand* pPrevBand = 0;
598 	ImplRegionBand* pBand = mpFirstBand;
599 
600     while ( pBand )
601 	{
602 		const bool bBTEqual = pBand->mpNextBand && (pBand->mnYBottom == pBand->mpNextBand->mnYTop);
603 
604 		// no separation? -> remove!
605 		if ( pBand->IsEmpty() || (bBTEqual && (pBand->mnYBottom == pBand->mnYTop)) )
606 		{
607 			// save pointer
608 			ImplRegionBand* pOldBand = pBand;
609 
610 			// previous element of the list
611 			if ( pBand == mpFirstBand )
612 				mpFirstBand = pBand->mpNextBand;
613 			else
614 				pPrevBand->mpNextBand = pBand->mpNextBand;
615 
616 			pBand = pBand->mpNextBand;
617 			delete pOldBand;
618 		}
619 		else
620 		{
621 			// fixup
622 			if ( bBTEqual )
623 				pBand->mnYBottom = pBand->mpNextBand->mnYTop-1;
624 
625 			// this and next band with equal separations? -> combine!
626 			if ( pBand->mpNextBand &&
627 				 ((pBand->mnYBottom+1) == pBand->mpNextBand->mnYTop) &&
628 				 (*pBand == *pBand->mpNextBand) )
629 			{
630 				// expand current height
631 				pBand->mnYBottom = pBand->mpNextBand->mnYBottom;
632 
633 				// remove next band from list
634 				ImplRegionBand* pDeletedBand = pBand->mpNextBand;
635 				pBand->mpNextBand = pDeletedBand->mpNextBand;
636 				delete pDeletedBand;
637 
638 				// check band again!
639 			}
640 			else
641 			{
642 				// count rectangles within band
643 				ImplRegionBandSep* pSep = pBand->mpFirstSep;
644 				while ( pSep )
645 				{
646 					pSep = pSep->mpNextSep;
647 				}
648 
649 				pPrevBand = pBand;
650 				pBand = pBand->mpNextBand;
651 			}
652 		}
653 	}
654 
655 #ifdef DBG_UTIL
656 	pBand = mpFirstBand;
657 	while ( pBand )
658 	{
659 		DBG_ASSERT( pBand->mpFirstSep != NULL, "Exiting RegionBand::OptimizeBandList(): empty band in region!" );
660 
661 		if ( pBand->mnYBottom < pBand->mnYTop )
662 			DBG_ERROR( "RegionBand::OptimizeBandList(): YBottomBoundary < YTopBoundary" );
663 
664 		if ( pBand->mpNextBand )
665 		{
666 			if ( pBand->mnYBottom >= pBand->mpNextBand->mnYTop )
667 				DBG_ERROR( "RegionBand::OptimizeBandList(): overlapping bands in region!" );
668 		}
669 
670 		pBand = pBand->mpNextBand;
671 	}
672 #endif
673 
674     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
675 	return (0 != mpFirstBand);
676 }
677 
678 void RegionBand::Move(long nHorzMove, long nVertMove)
679 {
680     ImplRegionBand* pBand = mpFirstBand;
681 
682     while(pBand)
683 	{
684 		// process the vertical move
685 		if(nVertMove)
686 		{
687 			pBand->mnYTop = pBand->mnYTop + nVertMove;
688 			pBand->mnYBottom = pBand->mnYBottom + nVertMove;
689 		}
690 
691 		// process the horizontal move
692 		if(nHorzMove)
693         {
694 			pBand->MoveX(nHorzMove);
695         }
696 
697 		pBand = pBand->mpNextBand;
698 	}
699 
700     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
701 }
702 
703 void RegionBand::Scale(double fScaleX, double fScaleY)
704 {
705     ImplRegionBand* pBand = mpFirstBand;
706 
707     while(pBand)
708 	{
709 		// process the vertical move
710 		if(0.0 != fScaleY)
711 		{
712 			pBand->mnYTop = basegfx::fround(pBand->mnYTop * fScaleY);
713 			pBand->mnYBottom = basegfx::fround(pBand->mnYBottom * fScaleY);
714 		}
715 
716 		// process the horizontal move
717 		if(0.0 != fScaleX)
718         {
719 			pBand->ScaleX(fScaleX);
720         }
721 
722 		pBand = pBand->mpNextBand;
723 	}
724 
725     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
726 }
727 
728 void RegionBand::InsertBands(long nTop, long nBottom)
729 {
730 	// region empty? -> set rectagle as first entry!
731 	if ( !mpFirstBand )
732 	{
733 		// add band with boundaries of the rectangle
734 		mpFirstBand = new ImplRegionBand( nTop, nBottom );
735         DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
736 		return;
737 	}
738 
739 	// find/insert bands for the boundaries of the rectangle
740 	bool bTopBoundaryInserted = false;
741 	bool bTop2BoundaryInserted = false;
742 	bool bBottomBoundaryInserted = false;
743 
744 	// special case: top boundary is above the first band
745 	ImplRegionBand* pNewBand;
746 
747     if ( nTop < mpFirstBand->mnYTop )
748 	{
749 		// create new band above the first in the list
750 		pNewBand = new ImplRegionBand( nTop, mpFirstBand->mnYTop );
751 
752 		if ( nBottom < mpFirstBand->mnYTop )
753         {
754 			pNewBand->mnYBottom = nBottom;
755         }
756 
757 		// insert band into the list
758 		pNewBand->mpNextBand = mpFirstBand;
759 		mpFirstBand = pNewBand;
760 
761 		bTopBoundaryInserted = true;
762 	}
763 
764 	// insert band(s) into the list
765 	ImplRegionBand* pBand = mpFirstBand;
766 
767     while ( pBand )
768 	{
769 		// Insert Bands if possible
770 		if ( !bTopBoundaryInserted )
771         {
772 			bTopBoundaryInserted = InsertSingleBand( pBand, nTop - 1 );
773         }
774 
775 		if ( !bTop2BoundaryInserted )
776         {
777 			bTop2BoundaryInserted = InsertSingleBand( pBand, nTop );
778         }
779 
780 		if ( !bBottomBoundaryInserted && (nTop != nBottom) )
781         {
782 			bBottomBoundaryInserted = InsertSingleBand( pBand, nBottom );
783         }
784 
785 		// both boundaries inserted? -> nothing more to do
786 		if ( bTopBoundaryInserted && bTop2BoundaryInserted && bBottomBoundaryInserted )
787         {
788 			break;
789         }
790 
791 		// insert bands between two bands if neccessary
792 		if ( pBand->mpNextBand )
793 		{
794 			if ( (pBand->mnYBottom + 1) < pBand->mpNextBand->mnYTop )
795 			{
796 				// copy band with list and set new boundary
797 				pNewBand = new ImplRegionBand( pBand->mnYBottom+1, pBand->mpNextBand->mnYTop-1 );
798 
799 				// insert band into the list
800 				pNewBand->mpNextBand = pBand->mpNextBand;
801 				pBand->mpNextBand = pNewBand;
802 			}
803 		}
804 
805 		pBand = pBand->mpNextBand;
806 	}
807 
808     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
809 }
810 
811 bool RegionBand::InsertSingleBand(ImplRegionBand* pBand, long nYBandPosition)
812 {
813 	// boundary already included in band with height 1? -> nothing to do!
814 	if ( (pBand->mnYTop == pBand->mnYBottom) && (nYBandPosition == pBand->mnYTop) )
815     {
816         DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
817 		return true;
818     }
819 
820 	// insert single height band on top?
821 	ImplRegionBand* pNewBand;
822 
823     if ( nYBandPosition == pBand->mnYTop )
824 	{
825 		// copy band with list and set new boundary
826 		pNewBand = new ImplRegionBand( *pBand );
827 		pNewBand->mnYTop = nYBandPosition+1;
828 
829 		// insert band into the list
830 		pNewBand->mpNextBand = pBand->mpNextBand;
831 		pBand->mnYBottom = nYBandPosition;
832 		pBand->mpNextBand = pNewBand;
833 
834         DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
835 		return true;
836 	}
837 
838 	// top of new rectangle within the current band? -> insert new band and copy data
839 	if ( (nYBandPosition > pBand->mnYTop) && (nYBandPosition < pBand->mnYBottom) )
840 	{
841 		// copy band with list and set new boundary
842 		pNewBand = new ImplRegionBand( *pBand );
843 		pNewBand->mnYTop = nYBandPosition;
844 
845 		// insert band into the list
846 		pNewBand->mpNextBand = pBand->mpNextBand;
847 		pBand->mnYBottom = nYBandPosition;
848 		pBand->mpNextBand = pNewBand;
849 
850 		// copy band with list and set new boundary
851 		pNewBand = new ImplRegionBand( *pBand );
852 		pNewBand->mnYTop = nYBandPosition;
853 
854 		// insert band into the list
855 		pBand->mpNextBand->mnYTop = nYBandPosition+1;
856 
857 		pNewBand->mpNextBand = pBand->mpNextBand;
858 		pBand->mnYBottom = nYBandPosition - 1;
859 		pBand->mpNextBand = pNewBand;
860 
861         DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
862 		return true;
863 	}
864 
865 	// create new band behind the current in the list
866 	if ( !pBand->mpNextBand )
867 	{
868 		if ( nYBandPosition == pBand->mnYBottom )
869 		{
870 			// copy band with list and set new boundary
871 			pNewBand = new ImplRegionBand( *pBand );
872 			pNewBand->mnYTop = pBand->mnYBottom;
873 			pNewBand->mnYBottom = nYBandPosition;
874 
875 			pBand->mnYBottom = nYBandPosition-1;
876 
877 			// append band to the list
878 			pBand->mpNextBand = pNewBand;
879             DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
880 			return true;
881 		}
882 
883 		if ( nYBandPosition > pBand->mnYBottom )
884 		{
885 			// create new band
886 			pNewBand = new ImplRegionBand( pBand->mnYBottom + 1, nYBandPosition );
887 
888 			// append band to the list
889 			pBand->mpNextBand = pNewBand;
890             DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
891 			return true;
892 		}
893 	}
894 
895     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
896 	return false;
897 }
898 
899 void RegionBand::Union(long nLeft, long nTop, long nRight, long nBottom)
900 {
901 	DBG_ASSERT( nLeft <= nRight, "RegionBand::Union() - nLeft > nRight" );
902 	DBG_ASSERT( nTop <= nBottom, "RegionBand::Union() - nTop > nBottom" );
903 
904 	// process union
905 	ImplRegionBand* pBand = mpFirstBand;
906 	while ( pBand )
907 	{
908 		if ( pBand->mnYTop >= nTop )
909 		{
910 			if ( pBand->mnYBottom <= nBottom )
911 				pBand->Union( nLeft, nRight );
912 			else
913 			{
914 #ifdef DBG_UTIL
915 				long nCurY = pBand->mnYBottom;
916 				pBand = pBand->mpNextBand;
917 				while ( pBand )
918 				{
919 					if ( (pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY) )
920 					{
921 						DBG_ERROR( "RegionBand::Union() - Bands not sorted!" );
922 					}
923 					pBand = pBand->mpNextBand;
924 				}
925 #endif
926 				break;
927 			}
928 		}
929 
930 		pBand = pBand->mpNextBand;
931 	}
932 
933     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
934 }
935 
936 void RegionBand::Intersect(long nLeft, long nTop, long nRight, long nBottom)
937 {
938     // process intersections
939     ImplRegionBand* pPrevBand = 0;
940     ImplRegionBand* pBand = mpFirstBand;
941 
942     while(pBand)
943     {
944         // band within intersection boundary? -> process. otherwise remove
945         if((pBand->mnYTop >= nTop) && (pBand->mnYBottom <= nBottom))
946         {
947             // process intersection
948             pBand->Intersect(nLeft, nRight);
949             pPrevBand = pBand;
950             pBand = pBand->mpNextBand;
951         }
952         else
953         {
954             ImplRegionBand* pOldBand = pBand;
955 
956             if(pBand == mpFirstBand)
957             {
958                 mpFirstBand = pBand->mpNextBand;
959             }
960             else
961             {
962                 pPrevBand->mpNextBand = pBand->mpNextBand;
963             }
964 
965             pBand = pBand->mpNextBand;
966             delete pOldBand;
967         }
968     }
969 
970     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
971 }
972 
973 void RegionBand::Union(const RegionBand& rSource)
974 {
975 	// apply all rectangles from rSource to this
976 	ImplRegionBand* pBand = rSource.mpFirstBand;
977 
978     while ( pBand )
979 	{
980 		// insert bands if the boundaries are not allready in the list
981 		InsertBands(pBand->mnYTop, pBand->mnYBottom);
982 
983 		// process all elements of the list
984 		ImplRegionBandSep* pSep = pBand->mpFirstSep;
985 
986         while(pSep)
987 		{
988 			Union(pSep->mnXLeft, pBand->mnYTop, pSep->mnXRight, pBand->mnYBottom);
989 			pSep = pSep->mpNextSep;
990 		}
991 
992 		pBand = pBand->mpNextBand;
993 	}
994 
995     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
996 }
997 
998 void RegionBand::Exclude(long nLeft, long nTop, long nRight, long nBottom)
999 {
1000 	DBG_ASSERT( nLeft <= nRight, "RegionBand::Exclude() - nLeft > nRight" );
1001 	DBG_ASSERT( nTop <= nBottom, "RegionBand::Exclude() - nTop > nBottom" );
1002 
1003 	// process exclude
1004 	ImplRegionBand* pBand = mpFirstBand;
1005 
1006     while(pBand)
1007 	{
1008 		if(pBand->mnYTop >= nTop)
1009 		{
1010 			if(pBand->mnYBottom <= nBottom)
1011             {
1012 				pBand->Exclude(nLeft, nRight);
1013             }
1014 			else
1015 			{
1016 #ifdef DBG_UTIL
1017 				long nCurY = pBand->mnYBottom;
1018 				pBand = pBand->mpNextBand;
1019 
1020                 while(pBand)
1021 				{
1022 					if((pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY))
1023 					{
1024 						DBG_ERROR( "RegionBand::Exclude() - Bands not sorted!" );
1025 					}
1026 
1027                     pBand = pBand->mpNextBand;
1028 				}
1029 #endif
1030 				break;
1031 			}
1032 		}
1033 
1034 		pBand = pBand->mpNextBand;
1035 	}
1036 
1037     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
1038 }
1039 
1040 void RegionBand::XOr(long nLeft, long nTop, long nRight, long nBottom)
1041 {
1042 	DBG_ASSERT( nLeft <= nRight, "RegionBand::Exclude() - nLeft > nRight" );
1043 	DBG_ASSERT( nTop <= nBottom, "RegionBand::Exclude() - nTop > nBottom" );
1044 
1045 	// process xor
1046 	ImplRegionBand* pBand = mpFirstBand;
1047 
1048     while(pBand)
1049 	{
1050 		if(pBand->mnYTop >= nTop)
1051 		{
1052 			if(pBand->mnYBottom <= nBottom)
1053             {
1054 				pBand->XOr(nLeft, nRight);
1055             }
1056 			else
1057 			{
1058 #ifdef DBG_UTIL
1059 				long nCurY = pBand->mnYBottom;
1060 				pBand = pBand->mpNextBand;
1061 
1062                 while(pBand)
1063 				{
1064 					if((pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY))
1065 					{
1066 						DBG_ERROR( "RegionBand::XOr() - Bands not sorted!" );
1067 					}
1068 
1069                     pBand = pBand->mpNextBand;
1070 				}
1071 #endif
1072 				break;
1073 			}
1074 		}
1075 
1076 		pBand = pBand->mpNextBand;
1077 	}
1078 
1079     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
1080 }
1081 
1082 void RegionBand::Intersect(const RegionBand& rSource)
1083 {
1084     // mark all bands as untouched
1085     ImplRegionBand* pBand = mpFirstBand;
1086 
1087     while ( pBand )
1088     {
1089 	    pBand->mbTouched = false;
1090 	    pBand = pBand->mpNextBand;
1091     }
1092 
1093     pBand = rSource.mpFirstBand;
1094 
1095     while ( pBand )
1096     {
1097 	    // insert bands if the boundaries are not allready in the list
1098 	    InsertBands( pBand->mnYTop, pBand->mnYBottom );
1099 
1100 	    // process all elements of the list
1101 	    ImplRegionBandSep* pSep = pBand->mpFirstSep;
1102 
1103         while ( pSep )
1104 	    {
1105 		    // left boundary?
1106 		    if ( pSep == pBand->mpFirstSep )
1107 		    {
1108 			    // process intersection and do not remove untouched bands
1109 			    Exclude( LONG_MIN+1, pBand->mnYTop, pSep->mnXLeft-1, pBand->mnYBottom );
1110 		    }
1111 
1112 		    // right boundary?
1113 		    if ( pSep->mpNextSep == NULL )
1114 		    {
1115 			    // process intersection and do not remove untouched bands
1116 			    Exclude( pSep->mnXRight+1, pBand->mnYTop, LONG_MAX-1, pBand->mnYBottom );
1117 		    }
1118 		    else
1119 		    {
1120 			    // process intersection and do not remove untouched bands
1121 			    Exclude( pSep->mnXRight+1, pBand->mnYTop, pSep->mpNextSep->mnXLeft-1, pBand->mnYBottom );
1122 		    }
1123 
1124 		    pSep = pSep->mpNextSep;
1125 	    }
1126 
1127 	    pBand = pBand->mpNextBand;
1128     }
1129 
1130     // remove all untouched bands if bands allready left
1131     ImplRegionBand* pPrevBand = 0;
1132     pBand = mpFirstBand;
1133 
1134     while ( pBand )
1135     {
1136 	    if ( !pBand->mbTouched )
1137 	    {
1138 		    // save pointer
1139 		    ImplRegionBand* pOldBand = pBand;
1140 
1141 		    // previous element of the list
1142 		    if ( pBand == mpFirstBand )
1143             {
1144 			    mpFirstBand = pBand->mpNextBand;
1145             }
1146 		    else
1147             {
1148 			    pPrevBand->mpNextBand = pBand->mpNextBand;
1149             }
1150 
1151 		    pBand = pBand->mpNextBand;
1152 		    delete pOldBand;
1153 	    }
1154 	    else
1155 	    {
1156 		    pPrevBand = pBand;
1157 		    pBand = pBand->mpNextBand;
1158 	    }
1159     }
1160 
1161     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
1162 }
1163 
1164 bool RegionBand::Exclude(const RegionBand& rSource)
1165 {
1166     // Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden
1167     ImplRegionBand* pBand = rSource.mpFirstBand;
1168 
1169     while ( pBand )
1170     {
1171 	    // insert bands if the boundaries are not allready in the list
1172 	    InsertBands( pBand->mnYTop, pBand->mnYBottom );
1173 
1174 	    // process all elements of the list
1175 	    ImplRegionBandSep* pSep = pBand->mpFirstSep;
1176 
1177         while ( pSep )
1178 	    {
1179 		    Exclude( pSep->mnXLeft, pBand->mnYTop, pSep->mnXRight, pBand->mnYBottom );
1180 		    pSep = pSep->mpNextSep;
1181 	    }
1182 
1183         // to test less bands, already check in the loop
1184 	    if ( !OptimizeBandList() )
1185 	    {
1186             DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
1187             return false;
1188 	    }
1189 
1190 	    pBand = pBand->mpNextBand;
1191     }
1192 
1193     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
1194     return true;
1195 }
1196 
1197 Rectangle RegionBand::GetBoundRect() const
1198 {
1199     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
1200 
1201     // get the boundaries of the first band
1202 	long nYTop(mpFirstBand->mnYTop);
1203 	long nYBottom(mpFirstBand->mnYBottom);
1204 	long nXLeft(mpFirstBand->GetXLeftBoundary());
1205 	long nXRight(mpFirstBand->GetXRightBoundary());
1206 
1207 	// look in the band list (don't test first band again!)
1208 	ImplRegionBand* pBand = mpFirstBand->mpNextBand;
1209 
1210 	while ( pBand )
1211 	{
1212 		nYBottom = pBand->mnYBottom;
1213 		nXLeft = std::min( nXLeft, pBand->GetXLeftBoundary() );
1214 		nXRight = std::max( nXRight, pBand->GetXRightBoundary() );
1215 
1216 		pBand = pBand->mpNextBand;
1217 	}
1218 
1219     return Rectangle( nXLeft, nYTop, nXRight, nYBottom );
1220 }
1221 
1222 void RegionBand::XOr(const RegionBand& rSource)
1223 {
1224     ImplRegionBand* pBand = rSource.mpFirstBand;
1225 
1226     while ( pBand )
1227     {
1228 	    // insert bands if the boundaries are not allready in the list
1229 	    InsertBands( pBand->mnYTop, pBand->mnYBottom );
1230 
1231 	    // process all elements of the list
1232 	    ImplRegionBandSep* pSep = pBand->mpFirstSep;
1233 
1234         while ( pSep )
1235 	    {
1236 		    XOr( pSep->mnXLeft, pBand->mnYTop, pSep->mnXRight, pBand->mnYBottom );
1237 		    pSep = pSep->mpNextSep;
1238 	    }
1239 
1240 	    pBand = pBand->mpNextBand;
1241     }
1242 }
1243 
1244 bool RegionBand::IsInside(const Point& rPoint) const
1245 {
1246     DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
1247 
1248     // search band list
1249     ImplRegionBand* pBand = mpFirstBand;
1250 
1251     while(pBand)
1252     {
1253         // is point within band?
1254         if((pBand->mnYTop <= rPoint.Y()) && (pBand->mnYBottom >= rPoint.Y()))
1255         {
1256             // is point within separation of the band?
1257             DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
1258             if(pBand->IsInside(rPoint.X()))
1259             {
1260                 return true;
1261             }
1262             else
1263             {
1264                 return false;
1265             }
1266         }
1267 
1268         pBand = pBand->mpNextBand;
1269     }
1270 
1271     return false;
1272 }
1273 
1274 void RegionBand::GetRegionRectangles(RectangleVector& rTarget) const
1275 {
1276     // clear result vector
1277     rTarget.clear();
1278     ImplRegionBand* mpCurrRectBand = mpFirstBand;
1279     Rectangle aRectangle;
1280 
1281     while(mpCurrRectBand)
1282     {
1283         ImplRegionBandSep* mpCurrRectBandSep = mpCurrRectBand->mpFirstSep;
1284 
1285         aRectangle.Top() = mpCurrRectBand->mnYTop;
1286         aRectangle.Bottom() = mpCurrRectBand->mnYBottom;
1287 
1288         while(mpCurrRectBandSep)
1289         {
1290             aRectangle.Left() = mpCurrRectBandSep->mnXLeft;
1291             aRectangle.Right() = mpCurrRectBandSep->mnXRight;
1292             rTarget.push_back(aRectangle);
1293             mpCurrRectBandSep = mpCurrRectBandSep->mpNextSep;
1294         }
1295 
1296         mpCurrRectBand = mpCurrRectBand->mpNextBand;
1297     }
1298 }
1299 
1300 sal_uInt32 RegionBand::getRectangleCount() const
1301 {
1302     sal_uInt32 nCount = 0;
1303     const ImplRegionBand* pBand = mpFirstBand;
1304 
1305     while(pBand)
1306     {
1307         ImplRegionBandSep* pSep = pBand->mpFirstSep;
1308 
1309         while(pSep)
1310         {
1311             nCount++;
1312             pSep = pSep->mpNextSep;
1313         }
1314 
1315         pBand = pBand->mpNextBand;
1316     }
1317 
1318     return 0;
1319 }
1320 
1321 #ifdef DBG_UTIL
1322 const char* ImplDbgTestRegionBand(const void* pObj)
1323 {
1324     const RegionBand* pRegionBand = reinterpret_cast< const RegionBand* >(pObj);
1325 
1326     if(pRegionBand)
1327     {
1328         const ImplRegionBand* pBand = pRegionBand->ImplGetFirstRegionBand();
1329 
1330         while(pBand)
1331         {
1332             if(pBand->mnYBottom < pBand->mnYTop)
1333             {
1334                 return "YBottom < YTop";
1335             }
1336 
1337             if(pBand->mpNextBand)
1338             {
1339                 if(pBand->mnYBottom >= pBand->mpNextBand->mnYTop)
1340                 {
1341                     return "overlapping bands in region";
1342                 }
1343             }
1344 
1345             if(pBand->mbTouched)
1346             {
1347                 return "Band-mbTouched overwrite";
1348             }
1349 
1350             ImplRegionBandSep* pSep = pBand->mpFirstSep;
1351 
1352             while(pSep)
1353             {
1354                 if(pSep->mnXRight < pSep->mnXLeft)
1355                 {
1356                     return "XLeft < XRight";
1357                 }
1358 
1359                 if(pSep->mpNextSep)
1360                 {
1361                     if(pSep->mnXRight >= pSep->mpNextSep->mnXLeft)
1362                     {
1363                         return "overlapping separations in region";
1364                     }
1365                 }
1366 
1367                 if ( pSep->mbRemoved > 1 )
1368                 {
1369                     return "Sep-mbRemoved overwrite";
1370                 }
1371 
1372                 pSep = pSep->mpNextSep;
1373             }
1374 
1375             pBand = pBand->mpNextBand;
1376         }
1377     }
1378 
1379     return 0;
1380 }
1381 #endif
1382 
1383 //////////////////////////////////////////////////////////////////////////////
1384 // eof
1385