/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 . */ #include #include #include #include #include #include #include RegionBand::RegionBand() : mpFirstBand(nullptr), mpLastCheckedBand(nullptr) { } RegionBand::RegionBand(const RegionBand& rRef) : mpFirstBand(nullptr), mpLastCheckedBand(nullptr) { *this = rRef; } RegionBand& RegionBand::operator=(const RegionBand& rRef) { if (this != &rRef) { ImplRegionBand* pPrevBand = nullptr; 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; } } return *this; } RegionBand::RegionBand(const tools::Rectangle& rRect) : mpFirstBand(nullptr), mpLastCheckedBand(nullptr) { const tools::Long nTop(std::min(rRect.Top(), rRect.Bottom())); const tools::Long nBottom(std::max(rRect.Top(), rRect.Bottom())); const tools::Long nLeft(std::min(rRect.Left(), rRect.Right())); const tools::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); } void RegionBand::implReset() { ImplRegionBand* pBand = mpFirstBand; while(pBand) { ImplRegionBand* pTempBand = pBand->mpNextBand; delete pBand; pBand = pTempBand; } mpLastCheckedBand = nullptr; mpFirstBand = nullptr; } RegionBand::~RegionBand() { implReset(); } bool RegionBand::operator==( const RegionBand& rRegionBand ) const { // 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 tools::Long nOwnXLeft = pOwnRectBandSep->mnXLeft; tools::Long nSecondXLeft = pSecondRectBandSep->mnXLeft; if ( nOwnXLeft != nSecondXLeft ) { return false; } tools::Long nOwnYTop = pOwnRectBand->mnYTop; tools::Long nSecondYTop = pSecondRectBand->mnYTop; if ( nOwnYTop != nSecondYTop ) { return false; } tools::Long nOwnXRight = pOwnRectBandSep->mnXRight; tools::Long nSecondXRight = pSecondRectBandSep->mnXRight; if ( nOwnXRight != nSecondXRight ) { return false; } tools::Long nOwnYBottom = pOwnRectBand->mnYBottom; tools::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; } namespace { enum StreamEntryType { STREAMENTRY_BANDHEADER, STREAMENTRY_SEPARATION, STREAMENTRY_END }; } bool RegionBand::load(SvStream& rIStrm) { // clear this instance data implReset(); // get all bands ImplRegionBand* pCurrBand = nullptr; // get header from first element sal_uInt16 nTmp16(STREAMENTRY_END); rIStrm.ReadUInt16(nTmp16); if (STREAMENTRY_END == static_cast(nTmp16)) return false; size_t nRecordsPossible = rIStrm.remainingSize() / (2*sizeof(sal_Int32)); if (!nRecordsPossible) { OSL_ENSURE(false, "premature end of region stream" ); implReset(); return false; } do { // insert new band or new separation? if(STREAMENTRY_BANDHEADER == static_cast(nTmp16)) { sal_Int32 nYTop(0); sal_Int32 nYBottom(0); rIStrm.ReadInt32( nYTop ); rIStrm.ReadInt32( 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 { sal_Int32 nXLeft(0); sal_Int32 nXRight(0); rIStrm.ReadInt32( nXLeft ); rIStrm.ReadInt32( nXRight ); // add separation if ( pCurrBand ) { pCurrBand->Union( nXLeft, nXRight ); } } if( rIStrm.eof() ) { OSL_ENSURE(false, "premature end of region stream" ); implReset(); return false; } // get next header rIStrm.ReadUInt16( nTmp16 ); } while (STREAMENTRY_END != static_cast(nTmp16) && rIStrm.good()); if (!CheckConsistency()) { implReset(); return false; } return true; } void RegionBand::save(SvStream& rOStrm) const { ImplRegionBand* pBand = mpFirstBand; while(pBand) { // put boundaries rOStrm.WriteUInt16( STREAMENTRY_BANDHEADER ); rOStrm.WriteInt32( pBand->mnYTop ); rOStrm.WriteInt32( pBand->mnYBottom ); // put separations of current band ImplRegionBandSep* pSep = pBand->mpFirstSep; while(pSep) { // put separation rOStrm.WriteUInt16( STREAMENTRY_SEPARATION ); rOStrm.WriteInt32( pSep->mnXLeft ); rOStrm.WriteInt32( pSep->mnXRight ); // next separation from current band pSep = pSep->mpNextSep; } pBand = pBand->mpNextBand; } // put endmarker rOStrm.WriteUInt16( 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!=nullptr); 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; } } void RegionBand::processPoints() { ImplRegionBand* pRegionBand = mpFirstBand; while(pRegionBand) { // generate separations from the lines and process union pRegionBand->ProcessPoints(); pRegionBand = pRegionBand->mpNextBand; } } /** 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 tools::Long nTop, const tools::Long nBottom) { // Iterate over already existing bands and add missing bands atop the // first and between two bands. ImplRegionBand* pPreviousBand = nullptr; ImplRegionBand* pBand = ImplGetFirstRegionBand(); tools::Long nCurrentTop (nTop); while (pBand != nullptr && nCurrentTopmnYTop) { // 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 interval nTop->nBottom extends past the bottom most band. if (nCurrentTop <= nBottom && (pBand==nullptr || 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)); } } void RegionBand::CreateBandRange(tools::Long nYTop, tools::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 ( tools::Long 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; } } void RegionBand::InsertLine(const Point& rStartPt, const Point& rEndPt, tools::Long nLineId) { tools::Long nX, nY; // lines consisting of a single point do not interest here if ( rStartPt == rEndPt ) { return; } LineType eLineType = (rStartPt.Y() > rEndPt.Y()) ? LineType::Descending : LineType::Ascending; if ( rStartPt.X() == rEndPt.X() ) { // vertical line const tools::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 tools::Long nDX = std::abs( rEndPt.X() - rStartPt.X() ); const tools::Long nDY = std::abs( rEndPt.Y() - rStartPt.Y() ); const tools::Long nStartX = rStartPt.X(); const tools::Long nStartY = rStartPt.Y(); const tools::Long nEndX = rEndPt.X(); const tools::Long nEndY = rEndPt.Y(); const tools::Long nXInc = ( nStartX < nEndX ) ? 1 : -1; const tools::Long nYInc = ( nStartY < nEndY ) ? 1 : -1; if ( nDX >= nDY ) { const tools::Long nDYX = ( nDY - nDX ) * 2; const tools::Long nDY2 = nDY << 1; tools::Long nD = nDY2 - nDX; for ( nX = nStartX, nY = nStartY; nX != nEndX; nX += nXInc ) { InsertPoint( Point( nX, nY ), nLineId, nStartX == nX, eLineType ); if ( nD < 0 ) nD += nDY2; else { nD += nDYX; nY += nYInc; } } } else { const tools::Long nDYX = ( nDX - nDY ) * 2; const tools::Long nDY2 = nDX << 1; tools::Long nD = nDY2 - nDY; for ( nX = nStartX, nY = nStartY; nY != nEndY; nY += nYInc ) { InsertPoint( Point( nX, nY ), nLineId, nStartY == nY, eLineType ); if ( nD < 0 ) nD += nDY2; else { nD += nDYX; nX += nXInc; } } } // last point InsertPoint( Point( nEndX, nEndY ), nLineId, true, eLineType ); } } void RegionBand::InsertPoint(const Point &rPoint, tools::Long nLineID, bool bEndPoint, LineType eLineType) { SAL_WARN_IF( mpFirstBand == nullptr, "vcl", "RegionBand::InsertPoint - no bands available!" ); if ( rPoint.Y() == mpLastCheckedBand->mnYTop ) { mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType ); return; } 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 ); return; } mpLastCheckedBand = mpLastCheckedBand->mpNextBand; } OSL_ENSURE(false, "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 ); return; } mpLastCheckedBand = mpLastCheckedBand->mpPrevBand; } OSL_ENSURE(false, "RegionBand::InsertPoint reached the beginning of the list!" ); } OSL_ENSURE(false, "RegionBand::InsertPoint point not inserted!" ); // reinitialize pointer (should never be reached!) mpLastCheckedBand = mpFirstBand; } bool RegionBand::OptimizeBandList() { ImplRegionBand* pPrevBand = nullptr; 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 ) { SAL_WARN_IF( pBand->mpFirstSep == nullptr, "vcl", "Exiting RegionBand::OptimizeBandList(): empty band in region!" ); if ( pBand->mnYBottom < pBand->mnYTop ) OSL_ENSURE(false, "RegionBand::OptimizeBandList(): YBottomBoundary < YTopBoundary" ); if ( pBand->mpNextBand && pBand->mnYBottom >= pBand->mpNextBand->mnYTop ) OSL_ENSURE(false, "RegionBand::OptimizeBandList(): overlapping bands in region!" ); pBand = pBand->mpNextBand; } #endif return (nullptr != mpFirstBand); } void RegionBand::Move(tools::Long nHorzMove, tools::Long nVertMove) { ImplRegionBand* pBand = mpFirstBand; while(pBand) { // process the vertical move if(nVertMove) { pBand->mnYTop = o3tl::saturating_add(pBand->mnYTop, nVertMove); pBand->mnYBottom = o3tl::saturating_add(pBand->mnYBottom, nVertMove); } // process the horizontal move if(nHorzMove) { pBand->MoveX(nHorzMove); } pBand = pBand->mpNextBand; } } 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; } } void RegionBand::InsertBands(tools::Long nTop, tools::Long nBottom) { // region empty? -> set rectangle as first entry! if ( !mpFirstBand ) { // add band with boundaries of the rectangle mpFirstBand = new ImplRegionBand( nTop, nBottom ); 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 necessary 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; } } bool RegionBand::InsertSingleBand(ImplRegionBand* pBand, tools::Long nYBandPosition) { // boundary already included in band with height 1? -> nothing to do! if ( (pBand->mnYTop == pBand->mnYBottom) && (nYBandPosition == pBand->mnYTop) ) { 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; 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; 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; return true; } if ( nYBandPosition > pBand->mnYBottom ) { // create new band pNewBand = new ImplRegionBand( pBand->mnYBottom + 1, nYBandPosition ); // append band to the list pBand->mpNextBand = pNewBand; return true; } } return false; } void RegionBand::Union(tools::Long nLeft, tools::Long nTop, tools::Long nRight, tools::Long nBottom) { SAL_WARN_IF( nLeft > nRight, "vcl", "RegionBand::Union() - nLeft > nRight" ); SAL_WARN_IF( nTop > nBottom, "vcl", "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 tools::Long nCurY = pBand->mnYBottom; pBand = pBand->mpNextBand; while ( pBand ) { if ( (pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY) ) { OSL_ENSURE(false, "RegionBand::Union() - Bands not sorted!" ); } pBand = pBand->mpNextBand; } #endif break; } } pBand = pBand->mpNextBand; } } void RegionBand::Intersect(tools::Long nLeft, tools::Long nTop, tools::Long nRight, tools::Long nBottom) { // process intersections ImplRegionBand* pPrevBand = nullptr; 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; } } } 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 already 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; } } void RegionBand::Exclude(tools::Long nLeft, tools::Long nTop, tools::Long nRight, tools::Long nBottom) { SAL_WARN_IF( nLeft > nRight, "vcl", "RegionBand::Exclude() - nLeft > nRight" ); SAL_WARN_IF( nTop > nBottom, "vcl", "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 tools::Long nCurY = pBand->mnYBottom; pBand = pBand->mpNextBand; while(pBand) { if((pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY)) { OSL_ENSURE(false, "RegionBand::Exclude() - Bands not sorted!" ); } pBand = pBand->mpNextBand; } #endif break; } } pBand = pBand->mpNextBand; } } void RegionBand::XOr(tools::Long nLeft, tools::Long nTop, tools::Long nRight, tools::Long nBottom) { SAL_WARN_IF( nLeft > nRight, "vcl", "RegionBand::Exclude() - nLeft > nRight" ); SAL_WARN_IF( nTop > nBottom, "vcl", "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 tools::Long nCurY = pBand->mnYBottom; pBand = pBand->mpNextBand; while(pBand) { if((pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY)) { OSL_ENSURE(false, "RegionBand::XOr() - Bands not sorted!" ); } pBand = pBand->mpNextBand; } #endif break; } } pBand = pBand->mpNextBand; } } 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 already 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 == nullptr ) { // 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 already left ImplRegionBand* pPrevBand = nullptr; 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; } } } bool RegionBand::Exclude(const RegionBand& rSource) { // apply all rectangles to the region passed to this region ImplRegionBand* pBand = rSource.mpFirstBand; while ( pBand ) { // insert bands if the boundaries are not already 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() ) { return false; } pBand = pBand->mpNextBand; } return true; } bool RegionBand::CheckConsistency() const { if (!mpFirstBand) return true; // look in the band list (don't test first band again!) const ImplRegionBand* pBand = mpFirstBand->mpNextBand; while (pBand) { if (!pBand->mpFirstSep) return false; pBand = pBand->mpNextBand; } return true; } tools::Rectangle RegionBand::GetBoundRect() const { if (!mpFirstBand) return tools::Rectangle(); // get the boundaries of the first band tools::Long nYTop(mpFirstBand->mnYTop); tools::Long nYBottom(mpFirstBand->mnYBottom); tools::Long nXLeft(mpFirstBand->GetXLeftBoundary()); tools::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 tools::Rectangle( nXLeft, nYTop, nXRight, nYBottom ); } void RegionBand::XOr(const RegionBand& rSource) { ImplRegionBand* pBand = rSource.mpFirstBand; while ( pBand ) { // insert bands if the boundaries are not already 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::Contains(const Point& rPoint) const { // 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? return pBand->Contains(rPoint.X()); } pBand = pBand->mpNextBand; } return false; } void RegionBand::GetRegionRectangles(RectangleVector& rTarget) const { // clear result vector rTarget.clear(); ImplRegionBand* pCurrRectBand = mpFirstBand; tools::Rectangle aRectangle; while(pCurrRectBand) { ImplRegionBandSep* pCurrRectBandSep = pCurrRectBand->mpFirstSep; aRectangle.SetTop( pCurrRectBand->mnYTop ); aRectangle.SetBottom( pCurrRectBand->mnYBottom ); while(pCurrRectBandSep) { aRectangle.SetLeft( pCurrRectBandSep->mnXLeft ); aRectangle.SetRight( pCurrRectBandSep->mnXRight ); rTarget.push_back(aRectangle); pCurrRectBandSep = pCurrRectBandSep->mpNextSep; } pCurrRectBand = pCurrRectBand->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 nCount; } #ifdef DBG_UTIL const char* ImplDbgTestRegionBand(const void* pObj) { const RegionBand* pRegionBand = static_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 nullptr; } #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */