blob: 067bd8a9a57f6652c388467f283ee4053bb2c0c9 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"
#include <tools/stream.hxx>
#include <tools/debug.hxx>
#include <regionband.hxx>
//////////////////////////////////////////////////////////////////////////////
DBG_NAME( RegionBand )
DBG_NAMEEX( Polygon )
DBG_NAMEEX( PolyPolygon )
//////////////////////////////////////////////////////////////////////////////
RegionBand::RegionBand()
: mpFirstBand(0),
mpLastCheckedBand(0)
{
DBG_CTOR(RegionBand, ImplDbgTestRegionBand);
}
RegionBand::RegionBand(const RegionBand& rRef)
: mpFirstBand(0),
mpLastCheckedBand(0)
{
*this = rRef;
DBG_CTOR(RegionBand, ImplDbgTestRegionBand);
}
RegionBand& RegionBand::operator=(const RegionBand& rRef)
{
ImplRegionBand* pPrevBand = 0;
ImplRegionBand* pBand = rRef.mpFirstBand;
while(pBand)
{
ImplRegionBand* pNewBand = new ImplRegionBand(*pBand);
// first element? -> set as first into the list
if(pBand == rRef.mpFirstBand)
{
mpFirstBand = pNewBand;
}
else
{
pPrevBand->mpNextBand = pNewBand;
}
pPrevBand = pNewBand;
pBand = pBand->mpNextBand;
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
DBG_CHKOBJ(&rRef, RegionBand, ImplDbgTestRegionBand);
return *this;
}
RegionBand::RegionBand(const Rectangle& rRect)
: mpFirstBand(0),
mpLastCheckedBand(0)
{
const long nTop(std::min(rRect.Top(), rRect.Bottom()));
const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
const long nLeft(std::min(rRect.Left(), rRect.Right()));
const long nRight(std::max(rRect.Left(), rRect.Right()));
// add band with boundaries of the rectangle
mpFirstBand = new ImplRegionBand(nTop, nBottom);
// Set left and right boundaries of the band
mpFirstBand->Union(nLeft, nRight);
DBG_CTOR(RegionBand, ImplDbgTestRegionBand);
}
void RegionBand::implReset()
{
ImplRegionBand* pBand = mpFirstBand;
while(pBand)
{
ImplRegionBand* pTempBand = pBand->mpNextBand;
delete pBand;
pBand = pTempBand;
}
mpLastCheckedBand = 0;
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
RegionBand::~RegionBand()
{
implReset();
DBG_DTOR(RegionBand, ImplDbgTestRegionBand);
}
bool RegionBand::operator==( const RegionBand& rRegionBand ) const
{
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
DBG_CHKOBJ(&rRegionBand, RegionBand, ImplDbgTestRegionBand);
// initialise pointers
ImplRegionBand* pOwnRectBand = mpFirstBand;
ImplRegionBandSep* pOwnRectBandSep = pOwnRectBand->mpFirstSep;
ImplRegionBand* pSecondRectBand = rRegionBand.mpFirstBand;
ImplRegionBandSep* pSecondRectBandSep = pSecondRectBand->mpFirstSep;
while ( pOwnRectBandSep && pSecondRectBandSep )
{
// get boundaries of current rectangle
long nOwnXLeft = pOwnRectBandSep->mnXLeft;
long nSecondXLeft = pSecondRectBandSep->mnXLeft;
if ( nOwnXLeft != nSecondXLeft )
{
return false;
}
long nOwnYTop = pOwnRectBand->mnYTop;
long nSecondYTop = pSecondRectBand->mnYTop;
if ( nOwnYTop != nSecondYTop )
{
return false;
}
long nOwnXRight = pOwnRectBandSep->mnXRight;
long nSecondXRight = pSecondRectBandSep->mnXRight;
if ( nOwnXRight != nSecondXRight )
{
return false;
}
long nOwnYBottom = pOwnRectBand->mnYBottom;
long nSecondYBottom = pSecondRectBand->mnYBottom;
if ( nOwnYBottom != nSecondYBottom )
{
return false;
}
// get next separation from current band
pOwnRectBandSep = pOwnRectBandSep->mpNextSep;
// no separation found? -> go to next band!
if ( !pOwnRectBandSep )
{
// get next band
pOwnRectBand = pOwnRectBand->mpNextBand;
// get first separation in current band
if( pOwnRectBand )
{
pOwnRectBandSep = pOwnRectBand->mpFirstSep;
}
}
// get next separation from current band
pSecondRectBandSep = pSecondRectBandSep->mpNextSep;
// no separation found? -> go to next band!
if ( !pSecondRectBandSep )
{
// get next band
pSecondRectBand = pSecondRectBand->mpNextBand;
// get first separation in current band
if( pSecondRectBand )
{
pSecondRectBandSep = pSecondRectBand->mpFirstSep;
}
}
if ( pOwnRectBandSep && !pSecondRectBandSep )
{
return false;
}
if ( !pOwnRectBandSep && pSecondRectBandSep )
{
return false;
}
}
return true;
}
enum StreamEntryType { STREAMENTRY_BANDHEADER, STREAMENTRY_SEPARATION, STREAMENTRY_END };
void RegionBand::load(SvStream& rIStrm)
{
// clear this nstance's data
implReset();
// get all bands
ImplRegionBand* pCurrBand = 0;
// get header from first element
sal_uInt16 nTmp16(0);
rIStrm >> nTmp16;
while(STREAMENTRY_END != (StreamEntryType)nTmp16)
{
// insert new band or new separation?
if(STREAMENTRY_BANDHEADER == (StreamEntryType)nTmp16)
{
long nYTop;
long nYBottom;
rIStrm >> nYTop;
rIStrm >> nYBottom;
// create band
ImplRegionBand* pNewBand = new ImplRegionBand( nYTop, nYBottom );
// first element? -> set as first into the list
if ( !pCurrBand )
{
mpFirstBand = pNewBand;
}
else
{
pCurrBand->mpNextBand = pNewBand;
}
// save pointer for next creation
pCurrBand = pNewBand;
}
else
{
long nXLeft;
long nXRight;
rIStrm >> nXLeft;
rIStrm >> nXRight;
// add separation
if ( pCurrBand )
{
pCurrBand->Union( nXLeft, nXRight );
}
}
if( rIStrm.IsEof() )
{
DBG_ERROR( "premature end of region stream" );
implReset();
return;
}
// get next header
rIStrm >> nTmp16;
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
void RegionBand::save(SvStream& rOStrm) const
{
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
ImplRegionBand* pBand = mpFirstBand;
while(pBand)
{
// put boundaries
rOStrm << (sal_uInt16)STREAMENTRY_BANDHEADER;
rOStrm << pBand->mnYTop;
rOStrm << pBand->mnYBottom;
// put separations of current band
ImplRegionBandSep* pSep = pBand->mpFirstSep;
while(pSep)
{
// put separation
rOStrm << (sal_uInt16)STREAMENTRY_SEPARATION;
rOStrm << pSep->mnXLeft;
rOStrm << pSep->mnXRight;
// next separation from current band
pSep = pSep->mpNextSep;
}
pBand = pBand->mpNextBand;
}
// put endmarker
rOStrm << (sal_uInt16)STREAMENTRY_END;
}
bool RegionBand::isSingleRectangle() const
{
// just one band?
if(mpFirstBand && !mpFirstBand->mpNextBand)
{
// just one sep?
if(mpFirstBand->mpFirstSep && !mpFirstBand->mpFirstSep->mpNextSep)
{
return true;
}
}
return false;
}
void RegionBand::InsertBand(ImplRegionBand* pPreviousBand, ImplRegionBand* pBandToInsert)
{
OSL_ASSERT(pBandToInsert!=NULL);
if(!pPreviousBand)
{
// Insert band before all others.
if(mpFirstBand)
{
mpFirstBand->mpPrevBand = pBandToInsert;
}
pBandToInsert->mpNextBand = mpFirstBand;
mpFirstBand = pBandToInsert;
}
else
{
// Insert band directly after pPreviousBand.
pBandToInsert->mpNextBand = pPreviousBand->mpNextBand;
pPreviousBand->mpNextBand = pBandToInsert;
pBandToInsert->mpPrevBand = pPreviousBand;
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
void RegionBand::processPoints()
{
ImplRegionBand* pRegionBand = mpFirstBand;
while(pRegionBand)
{
// generate separations from the lines and process union
pRegionBand->ProcessPoints();
pRegionBand = pRegionBand->mpNextBand;
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
/** This function is similar to the RegionBand::InsertBands() method.
It creates a minimal set of missing bands so that the entire vertical
interval from nTop to nBottom is covered by bands.
*/
void RegionBand::ImplAddMissingBands(const long nTop, const long nBottom)
{
// Iterate over already existing bands and add missing bands atop the
// first and between two bands.
ImplRegionBand* pPreviousBand = NULL;
ImplRegionBand* pBand = ImplGetFirstRegionBand();
long nCurrentTop (nTop);
while (pBand != NULL && nCurrentTop<nBottom)
{
if (nCurrentTop < pBand->mnYTop)
{
// Create new band above the current band.
ImplRegionBand* pAboveBand = new ImplRegionBand(
nCurrentTop,
::std::min(nBottom,pBand->mnYTop-1));
InsertBand(pPreviousBand, pAboveBand);
}
// Adapt the top of the interval to prevent overlapping bands.
nCurrentTop = ::std::max(nTop, pBand->mnYBottom+1);
// Advance to next band.
pPreviousBand = pBand;
pBand = pBand->mpNextBand;
}
// We still have to cover two cases:
// 1. The region does not yet contain any bands.
// 2. The intervall nTop->nBottom extends past the bottom most band.
if (nCurrentTop <= nBottom
&& (pBand==NULL || nBottom>pBand->mnYBottom))
{
// When there is no previous band then the new one will be the
// first. Otherwise the new band is inserted behind the last band.
InsertBand(
pPreviousBand,
new ImplRegionBand(
nCurrentTop,
nBottom));
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
void RegionBand::CreateBandRange(long nYTop, long nYBottom)
{
// add top band
mpFirstBand = new ImplRegionBand( nYTop-1, nYTop-1 );
// begin first search from the first element
mpLastCheckedBand = mpFirstBand;
ImplRegionBand* pBand = mpFirstBand;
for ( int i = nYTop; i <= nYBottom+1; i++ )
{
// create new band
ImplRegionBand* pNewBand = new ImplRegionBand( i, i );
pBand->mpNextBand = pNewBand;
if ( pBand != mpFirstBand )
{
pNewBand->mpPrevBand = pBand;
}
pBand = pBand->mpNextBand;
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
bool RegionBand::InsertLine(const Point& rStartPt, const Point& rEndPt, long nLineId)
{
long nX, nY;
// lines consisting of a single point do not interest here
if ( rStartPt == rEndPt )
{
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return true;
}
LineType eLineType = (rStartPt.Y() > rEndPt.Y()) ? LINE_DESCENDING : LINE_ASCENDING;
if ( rStartPt.X() == rEndPt.X() )
{
// vertical line
const long nEndY = rEndPt.Y();
nX = rStartPt.X();
nY = rStartPt.Y();
if( nEndY > nY )
{
for ( ; nY <= nEndY; nY++ )
{
Point aNewPoint( nX, nY );
InsertPoint( aNewPoint, nLineId,
(aNewPoint == rEndPt) || (aNewPoint == rStartPt),
eLineType );
}
}
else
{
for ( ; nY >= nEndY; nY-- )
{
Point aNewPoint( nX, nY );
InsertPoint( aNewPoint, nLineId,
(aNewPoint == rEndPt) || (aNewPoint == rStartPt),
eLineType );
}
}
}
else if ( rStartPt.Y() != rEndPt.Y() )
{
const long nDX = labs( rEndPt.X() - rStartPt.X() );
const long nDY = labs( rEndPt.Y() - rStartPt.Y() );
const long nStartX = rStartPt.X();
const long nStartY = rStartPt.Y();
const long nEndX = rEndPt.X();
const long nEndY = rEndPt.Y();
const long nXInc = ( nStartX < nEndX ) ? 1L : -1L;
const long nYInc = ( nStartY < nEndY ) ? 1L : -1L;
if ( nDX >= nDY )
{
const long nDYX = ( nDY - nDX ) << 1;
const long nDY2 = nDY << 1;
long nD = nDY2 - nDX;
for ( nX = nStartX, nY = nStartY; nX != nEndX; nX += nXInc )
{
InsertPoint( Point( nX, nY ), nLineId, nStartX == nX, eLineType );
if ( nD < 0L )
nD += nDY2;
else
nD += nDYX, nY += nYInc;
}
}
else
{
const long nDYX = ( nDX - nDY ) << 1;
const long nDY2 = nDX << 1;
long nD = nDY2 - nDY;
for ( nX = nStartX, nY = nStartY; nY != nEndY; nY += nYInc )
{
InsertPoint( Point( nX, nY ), nLineId, nStartY == nY, eLineType );
if ( nD < 0L )
nD += nDY2;
else
nD += nDYX, nX += nXInc;
}
}
// last point
InsertPoint( Point( nEndX, nEndY ), nLineId, true, eLineType );
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return true;
}
bool RegionBand::InsertPoint(const Point &rPoint, long nLineID, bool bEndPoint, LineType eLineType)
{
DBG_ASSERT( mpFirstBand != NULL, "RegionBand::InsertPoint - no bands available!" );
if ( rPoint.Y() == mpLastCheckedBand->mnYTop )
{
mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType );
return true;
}
if ( rPoint.Y() > mpLastCheckedBand->mnYTop )
{
// Search ascending
while ( mpLastCheckedBand )
{
// Insert point if possible
if ( rPoint.Y() == mpLastCheckedBand->mnYTop )
{
mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType );
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return true;
}
mpLastCheckedBand = mpLastCheckedBand->mpNextBand;
}
DBG_ERROR( "RegionBand::InsertPoint reached the end of the list!" );
}
else
{
// Search descending
while ( mpLastCheckedBand )
{
// Insert point if possible
if ( rPoint.Y() == mpLastCheckedBand->mnYTop )
{
mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType );
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return true;
}
mpLastCheckedBand = mpLastCheckedBand->mpPrevBand;
}
DBG_ERROR( "RegionBand::InsertPoint reached the beginning of the list!" );
}
DBG_ERROR( "RegionBand::InsertPoint point not inserted!" );
// reinitialize pointer (should never be reached!)
mpLastCheckedBand = mpFirstBand;
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return false;
}
bool RegionBand::OptimizeBandList()
{
ImplRegionBand* pPrevBand = 0;
ImplRegionBand* pBand = mpFirstBand;
while ( pBand )
{
const bool bBTEqual = pBand->mpNextBand && (pBand->mnYBottom == pBand->mpNextBand->mnYTop);
// no separation? -> remove!
if ( pBand->IsEmpty() || (bBTEqual && (pBand->mnYBottom == pBand->mnYTop)) )
{
// save pointer
ImplRegionBand* pOldBand = pBand;
// previous element of the list
if ( pBand == mpFirstBand )
mpFirstBand = pBand->mpNextBand;
else
pPrevBand->mpNextBand = pBand->mpNextBand;
pBand = pBand->mpNextBand;
delete pOldBand;
}
else
{
// fixup
if ( bBTEqual )
pBand->mnYBottom = pBand->mpNextBand->mnYTop-1;
// this and next band with equal separations? -> combine!
if ( pBand->mpNextBand &&
((pBand->mnYBottom+1) == pBand->mpNextBand->mnYTop) &&
(*pBand == *pBand->mpNextBand) )
{
// expand current height
pBand->mnYBottom = pBand->mpNextBand->mnYBottom;
// remove next band from list
ImplRegionBand* pDeletedBand = pBand->mpNextBand;
pBand->mpNextBand = pDeletedBand->mpNextBand;
delete pDeletedBand;
// check band again!
}
else
{
// count rectangles within band
ImplRegionBandSep* pSep = pBand->mpFirstSep;
while ( pSep )
{
pSep = pSep->mpNextSep;
}
pPrevBand = pBand;
pBand = pBand->mpNextBand;
}
}
}
#ifdef DBG_UTIL
pBand = mpFirstBand;
while ( pBand )
{
DBG_ASSERT( pBand->mpFirstSep != NULL, "Exiting RegionBand::OptimizeBandList(): empty band in region!" );
if ( pBand->mnYBottom < pBand->mnYTop )
DBG_ERROR( "RegionBand::OptimizeBandList(): YBottomBoundary < YTopBoundary" );
if ( pBand->mpNextBand )
{
if ( pBand->mnYBottom >= pBand->mpNextBand->mnYTop )
DBG_ERROR( "RegionBand::OptimizeBandList(): overlapping bands in region!" );
}
pBand = pBand->mpNextBand;
}
#endif
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return (0 != mpFirstBand);
}
void RegionBand::Move(long nHorzMove, long nVertMove)
{
ImplRegionBand* pBand = mpFirstBand;
while(pBand)
{
// process the vertical move
if(nVertMove)
{
pBand->mnYTop = pBand->mnYTop + nVertMove;
pBand->mnYBottom = pBand->mnYBottom + nVertMove;
}
// process the horizontal move
if(nHorzMove)
{
pBand->MoveX(nHorzMove);
}
pBand = pBand->mpNextBand;
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
void RegionBand::Scale(double fScaleX, double fScaleY)
{
ImplRegionBand* pBand = mpFirstBand;
while(pBand)
{
// process the vertical move
if(0.0 != fScaleY)
{
pBand->mnYTop = basegfx::fround(pBand->mnYTop * fScaleY);
pBand->mnYBottom = basegfx::fround(pBand->mnYBottom * fScaleY);
}
// process the horizontal move
if(0.0 != fScaleX)
{
pBand->ScaleX(fScaleX);
}
pBand = pBand->mpNextBand;
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
void RegionBand::InsertBands(long nTop, long nBottom)
{
// region empty? -> set rectagle as first entry!
if ( !mpFirstBand )
{
// add band with boundaries of the rectangle
mpFirstBand = new ImplRegionBand( nTop, nBottom );
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return;
}
// find/insert bands for the boundaries of the rectangle
bool bTopBoundaryInserted = false;
bool bTop2BoundaryInserted = false;
bool bBottomBoundaryInserted = false;
// special case: top boundary is above the first band
ImplRegionBand* pNewBand;
if ( nTop < mpFirstBand->mnYTop )
{
// create new band above the first in the list
pNewBand = new ImplRegionBand( nTop, mpFirstBand->mnYTop );
if ( nBottom < mpFirstBand->mnYTop )
{
pNewBand->mnYBottom = nBottom;
}
// insert band into the list
pNewBand->mpNextBand = mpFirstBand;
mpFirstBand = pNewBand;
bTopBoundaryInserted = true;
}
// insert band(s) into the list
ImplRegionBand* pBand = mpFirstBand;
while ( pBand )
{
// Insert Bands if possible
if ( !bTopBoundaryInserted )
{
bTopBoundaryInserted = InsertSingleBand( pBand, nTop - 1 );
}
if ( !bTop2BoundaryInserted )
{
bTop2BoundaryInserted = InsertSingleBand( pBand, nTop );
}
if ( !bBottomBoundaryInserted && (nTop != nBottom) )
{
bBottomBoundaryInserted = InsertSingleBand( pBand, nBottom );
}
// both boundaries inserted? -> nothing more to do
if ( bTopBoundaryInserted && bTop2BoundaryInserted && bBottomBoundaryInserted )
{
break;
}
// insert bands between two bands if neccessary
if ( pBand->mpNextBand )
{
if ( (pBand->mnYBottom + 1) < pBand->mpNextBand->mnYTop )
{
// copy band with list and set new boundary
pNewBand = new ImplRegionBand( pBand->mnYBottom+1, pBand->mpNextBand->mnYTop-1 );
// insert band into the list
pNewBand->mpNextBand = pBand->mpNextBand;
pBand->mpNextBand = pNewBand;
}
}
pBand = pBand->mpNextBand;
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
bool RegionBand::InsertSingleBand(ImplRegionBand* pBand, long nYBandPosition)
{
// boundary already included in band with height 1? -> nothing to do!
if ( (pBand->mnYTop == pBand->mnYBottom) && (nYBandPosition == pBand->mnYTop) )
{
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return true;
}
// insert single height band on top?
ImplRegionBand* pNewBand;
if ( nYBandPosition == pBand->mnYTop )
{
// copy band with list and set new boundary
pNewBand = new ImplRegionBand( *pBand );
pNewBand->mnYTop = nYBandPosition+1;
// insert band into the list
pNewBand->mpNextBand = pBand->mpNextBand;
pBand->mnYBottom = nYBandPosition;
pBand->mpNextBand = pNewBand;
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return true;
}
// top of new rectangle within the current band? -> insert new band and copy data
if ( (nYBandPosition > pBand->mnYTop) && (nYBandPosition < pBand->mnYBottom) )
{
// copy band with list and set new boundary
pNewBand = new ImplRegionBand( *pBand );
pNewBand->mnYTop = nYBandPosition;
// insert band into the list
pNewBand->mpNextBand = pBand->mpNextBand;
pBand->mnYBottom = nYBandPosition;
pBand->mpNextBand = pNewBand;
// copy band with list and set new boundary
pNewBand = new ImplRegionBand( *pBand );
pNewBand->mnYTop = nYBandPosition;
// insert band into the list
pBand->mpNextBand->mnYTop = nYBandPosition+1;
pNewBand->mpNextBand = pBand->mpNextBand;
pBand->mnYBottom = nYBandPosition - 1;
pBand->mpNextBand = pNewBand;
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return true;
}
// create new band behind the current in the list
if ( !pBand->mpNextBand )
{
if ( nYBandPosition == pBand->mnYBottom )
{
// copy band with list and set new boundary
pNewBand = new ImplRegionBand( *pBand );
pNewBand->mnYTop = pBand->mnYBottom;
pNewBand->mnYBottom = nYBandPosition;
pBand->mnYBottom = nYBandPosition-1;
// append band to the list
pBand->mpNextBand = pNewBand;
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return true;
}
if ( nYBandPosition > pBand->mnYBottom )
{
// create new band
pNewBand = new ImplRegionBand( pBand->mnYBottom + 1, nYBandPosition );
// append band to the list
pBand->mpNextBand = pNewBand;
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return true;
}
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return false;
}
void RegionBand::Union(long nLeft, long nTop, long nRight, long nBottom)
{
DBG_ASSERT( nLeft <= nRight, "RegionBand::Union() - nLeft > nRight" );
DBG_ASSERT( nTop <= nBottom, "RegionBand::Union() - nTop > nBottom" );
// process union
ImplRegionBand* pBand = mpFirstBand;
while ( pBand )
{
if ( pBand->mnYTop >= nTop )
{
if ( pBand->mnYBottom <= nBottom )
pBand->Union( nLeft, nRight );
else
{
#ifdef DBG_UTIL
long nCurY = pBand->mnYBottom;
pBand = pBand->mpNextBand;
while ( pBand )
{
if ( (pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY) )
{
DBG_ERROR( "RegionBand::Union() - Bands not sorted!" );
}
pBand = pBand->mpNextBand;
}
#endif
break;
}
}
pBand = pBand->mpNextBand;
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
void RegionBand::Intersect(long nLeft, long nTop, long nRight, long nBottom)
{
// process intersections
ImplRegionBand* pPrevBand = 0;
ImplRegionBand* pBand = mpFirstBand;
while(pBand)
{
// band within intersection boundary? -> process. otherwise remove
if((pBand->mnYTop >= nTop) && (pBand->mnYBottom <= nBottom))
{
// process intersection
pBand->Intersect(nLeft, nRight);
pPrevBand = pBand;
pBand = pBand->mpNextBand;
}
else
{
ImplRegionBand* pOldBand = pBand;
if(pBand == mpFirstBand)
{
mpFirstBand = pBand->mpNextBand;
}
else
{
pPrevBand->mpNextBand = pBand->mpNextBand;
}
pBand = pBand->mpNextBand;
delete pOldBand;
}
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
void RegionBand::Union(const RegionBand& rSource)
{
// apply all rectangles from rSource to this
ImplRegionBand* pBand = rSource.mpFirstBand;
while ( pBand )
{
// insert bands if the boundaries are not allready in the list
InsertBands(pBand->mnYTop, pBand->mnYBottom);
// process all elements of the list
ImplRegionBandSep* pSep = pBand->mpFirstSep;
while(pSep)
{
Union(pSep->mnXLeft, pBand->mnYTop, pSep->mnXRight, pBand->mnYBottom);
pSep = pSep->mpNextSep;
}
pBand = pBand->mpNextBand;
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
void RegionBand::Exclude(long nLeft, long nTop, long nRight, long nBottom)
{
DBG_ASSERT( nLeft <= nRight, "RegionBand::Exclude() - nLeft > nRight" );
DBG_ASSERT( nTop <= nBottom, "RegionBand::Exclude() - nTop > nBottom" );
// process exclude
ImplRegionBand* pBand = mpFirstBand;
while(pBand)
{
if(pBand->mnYTop >= nTop)
{
if(pBand->mnYBottom <= nBottom)
{
pBand->Exclude(nLeft, nRight);
}
else
{
#ifdef DBG_UTIL
long nCurY = pBand->mnYBottom;
pBand = pBand->mpNextBand;
while(pBand)
{
if((pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY))
{
DBG_ERROR( "RegionBand::Exclude() - Bands not sorted!" );
}
pBand = pBand->mpNextBand;
}
#endif
break;
}
}
pBand = pBand->mpNextBand;
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
void RegionBand::XOr(long nLeft, long nTop, long nRight, long nBottom)
{
DBG_ASSERT( nLeft <= nRight, "RegionBand::Exclude() - nLeft > nRight" );
DBG_ASSERT( nTop <= nBottom, "RegionBand::Exclude() - nTop > nBottom" );
// process xor
ImplRegionBand* pBand = mpFirstBand;
while(pBand)
{
if(pBand->mnYTop >= nTop)
{
if(pBand->mnYBottom <= nBottom)
{
pBand->XOr(nLeft, nRight);
}
else
{
#ifdef DBG_UTIL
long nCurY = pBand->mnYBottom;
pBand = pBand->mpNextBand;
while(pBand)
{
if((pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY))
{
DBG_ERROR( "RegionBand::XOr() - Bands not sorted!" );
}
pBand = pBand->mpNextBand;
}
#endif
break;
}
}
pBand = pBand->mpNextBand;
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
void RegionBand::Intersect(const RegionBand& rSource)
{
// mark all bands as untouched
ImplRegionBand* pBand = mpFirstBand;
while ( pBand )
{
pBand->mbTouched = false;
pBand = pBand->mpNextBand;
}
pBand = rSource.mpFirstBand;
while ( pBand )
{
// insert bands if the boundaries are not allready in the list
InsertBands( pBand->mnYTop, pBand->mnYBottom );
// process all elements of the list
ImplRegionBandSep* pSep = pBand->mpFirstSep;
while ( pSep )
{
// left boundary?
if ( pSep == pBand->mpFirstSep )
{
// process intersection and do not remove untouched bands
Exclude( LONG_MIN+1, pBand->mnYTop, pSep->mnXLeft-1, pBand->mnYBottom );
}
// right boundary?
if ( pSep->mpNextSep == NULL )
{
// process intersection and do not remove untouched bands
Exclude( pSep->mnXRight+1, pBand->mnYTop, LONG_MAX-1, pBand->mnYBottom );
}
else
{
// process intersection and do not remove untouched bands
Exclude( pSep->mnXRight+1, pBand->mnYTop, pSep->mpNextSep->mnXLeft-1, pBand->mnYBottom );
}
pSep = pSep->mpNextSep;
}
pBand = pBand->mpNextBand;
}
// remove all untouched bands if bands allready left
ImplRegionBand* pPrevBand = 0;
pBand = mpFirstBand;
while ( pBand )
{
if ( !pBand->mbTouched )
{
// save pointer
ImplRegionBand* pOldBand = pBand;
// previous element of the list
if ( pBand == mpFirstBand )
{
mpFirstBand = pBand->mpNextBand;
}
else
{
pPrevBand->mpNextBand = pBand->mpNextBand;
}
pBand = pBand->mpNextBand;
delete pOldBand;
}
else
{
pPrevBand = pBand;
pBand = pBand->mpNextBand;
}
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
}
bool RegionBand::Exclude(const RegionBand& rSource)
{
// Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden
ImplRegionBand* pBand = rSource.mpFirstBand;
while ( pBand )
{
// insert bands if the boundaries are not allready in the list
InsertBands( pBand->mnYTop, pBand->mnYBottom );
// process all elements of the list
ImplRegionBandSep* pSep = pBand->mpFirstSep;
while ( pSep )
{
Exclude( pSep->mnXLeft, pBand->mnYTop, pSep->mnXRight, pBand->mnYBottom );
pSep = pSep->mpNextSep;
}
// to test less bands, already check in the loop
if ( !OptimizeBandList() )
{
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return false;
}
pBand = pBand->mpNextBand;
}
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
return true;
}
Rectangle RegionBand::GetBoundRect() const
{
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
// get the boundaries of the first band
long nYTop(mpFirstBand->mnYTop);
long nYBottom(mpFirstBand->mnYBottom);
long nXLeft(mpFirstBand->GetXLeftBoundary());
long nXRight(mpFirstBand->GetXRightBoundary());
// look in the band list (don't test first band again!)
ImplRegionBand* pBand = mpFirstBand->mpNextBand;
while ( pBand )
{
nYBottom = pBand->mnYBottom;
nXLeft = std::min( nXLeft, pBand->GetXLeftBoundary() );
nXRight = std::max( nXRight, pBand->GetXRightBoundary() );
pBand = pBand->mpNextBand;
}
return Rectangle( nXLeft, nYTop, nXRight, nYBottom );
}
void RegionBand::XOr(const RegionBand& rSource)
{
ImplRegionBand* pBand = rSource.mpFirstBand;
while ( pBand )
{
// insert bands if the boundaries are not allready in the list
InsertBands( pBand->mnYTop, pBand->mnYBottom );
// process all elements of the list
ImplRegionBandSep* pSep = pBand->mpFirstSep;
while ( pSep )
{
XOr( pSep->mnXLeft, pBand->mnYTop, pSep->mnXRight, pBand->mnYBottom );
pSep = pSep->mpNextSep;
}
pBand = pBand->mpNextBand;
}
}
bool RegionBand::IsInside(const Point& rPoint) const
{
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
// search band list
ImplRegionBand* pBand = mpFirstBand;
while(pBand)
{
// is point within band?
if((pBand->mnYTop <= rPoint.Y()) && (pBand->mnYBottom >= rPoint.Y()))
{
// is point within separation of the band?
DBG_CHKTHIS(RegionBand, ImplDbgTestRegionBand);
if(pBand->IsInside(rPoint.X()))
{
return true;
}
else
{
return false;
}
}
pBand = pBand->mpNextBand;
}
return false;
}
void RegionBand::GetRegionRectangles(RectangleVector& rTarget) const
{
// clear result vector
rTarget.clear();
ImplRegionBand* mpCurrRectBand = mpFirstBand;
Rectangle aRectangle;
while(mpCurrRectBand)
{
ImplRegionBandSep* mpCurrRectBandSep = mpCurrRectBand->mpFirstSep;
aRectangle.Top() = mpCurrRectBand->mnYTop;
aRectangle.Bottom() = mpCurrRectBand->mnYBottom;
while(mpCurrRectBandSep)
{
aRectangle.Left() = mpCurrRectBandSep->mnXLeft;
aRectangle.Right() = mpCurrRectBandSep->mnXRight;
rTarget.push_back(aRectangle);
mpCurrRectBandSep = mpCurrRectBandSep->mpNextSep;
}
mpCurrRectBand = mpCurrRectBand->mpNextBand;
}
}
sal_uInt32 RegionBand::getRectangleCount() const
{
sal_uInt32 nCount = 0;
const ImplRegionBand* pBand = mpFirstBand;
while(pBand)
{
ImplRegionBandSep* pSep = pBand->mpFirstSep;
while(pSep)
{
nCount++;
pSep = pSep->mpNextSep;
}
pBand = pBand->mpNextBand;
}
return 0;
}
#ifdef DBG_UTIL
const char* ImplDbgTestRegionBand(const void* pObj)
{
const RegionBand* pRegionBand = reinterpret_cast< const RegionBand* >(pObj);
if(pRegionBand)
{
const ImplRegionBand* pBand = pRegionBand->ImplGetFirstRegionBand();
while(pBand)
{
if(pBand->mnYBottom < pBand->mnYTop)
{
return "YBottom < YTop";
}
if(pBand->mpNextBand)
{
if(pBand->mnYBottom >= pBand->mpNextBand->mnYTop)
{
return "overlapping bands in region";
}
}
if(pBand->mbTouched)
{
return "Band-mbTouched overwrite";
}
ImplRegionBandSep* pSep = pBand->mpFirstSep;
while(pSep)
{
if(pSep->mnXRight < pSep->mnXLeft)
{
return "XLeft < XRight";
}
if(pSep->mpNextSep)
{
if(pSep->mnXRight >= pSep->mpNextSep->mnXLeft)
{
return "overlapping separations in region";
}
}
if ( pSep->mbRemoved )
{
return "Sep-mbRemoved overwrite";
}
pSep = pSep->mpNextSep;
}
pBand = pBand->mpNextBand;
}
}
return 0;
}
#endif
//////////////////////////////////////////////////////////////////////////////
// eof