/* -*- 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 #include #include #include #include namespace tools { PolyPolygon::PolyPolygon( sal_uInt16 nInitSize ) : mpImplPolyPolygon( ImplPolyPolygon( nInitSize ) ) { } PolyPolygon::PolyPolygon( const tools::Polygon& rPoly ) : mpImplPolyPolygon( rPoly ) { } PolyPolygon::PolyPolygon( const tools::PolyPolygon& rPolyPoly ) : mpImplPolyPolygon( rPolyPoly.mpImplPolyPolygon ) { } PolyPolygon::PolyPolygon( tools::PolyPolygon&& rPolyPoly ) noexcept : mpImplPolyPolygon( std::move(rPolyPoly.mpImplPolyPolygon) ) { } PolyPolygon::~PolyPolygon() { } void PolyPolygon::Insert( const tools::Polygon& rPoly, sal_uInt16 nPos ) { assert ( mpImplPolyPolygon->mvPolyAry.size() < MAX_POLYGONS ); if ( nPos > mpImplPolyPolygon->mvPolyAry.size() ) nPos = mpImplPolyPolygon->mvPolyAry.size(); mpImplPolyPolygon->mvPolyAry.insert(mpImplPolyPolygon->mvPolyAry.begin() + nPos, rPoly); } void PolyPolygon::Remove( sal_uInt16 nPos ) { assert(nPos < Count() && "PolyPolygon::Remove(): nPos >= nSize"); mpImplPolyPolygon->mvPolyAry.erase(mpImplPolyPolygon->mvPolyAry.begin() + nPos); } void PolyPolygon::Replace( const tools::Polygon& rPoly, sal_uInt16 nPos ) { assert(nPos < Count() && "PolyPolygon::Replace(): nPos >= nSize"); mpImplPolyPolygon->mvPolyAry[nPos] = rPoly; } const tools::Polygon& PolyPolygon::GetObject( sal_uInt16 nPos ) const { assert(nPos < Count() && "PolyPolygon::GetObject(): nPos >= nSize"); return mpImplPolyPolygon->mvPolyAry[nPos]; } bool PolyPolygon::IsRect() const { bool bIsRect = false; if ( Count() == 1 ) bIsRect = mpImplPolyPolygon->mvPolyAry[ 0 ].IsRect(); return bIsRect; } void PolyPolygon::Clear() { mpImplPolyPolygon->mvPolyAry.clear(); } void PolyPolygon::Optimize( PolyOptimizeFlags nOptimizeFlags ) { if(!(bool(nOptimizeFlags) && Count())) return; // #115630# ImplDrawHatch does not work with beziers included in the polypolygon, take care of that bool bIsCurve(false); for(sal_uInt16 a(0); !bIsCurve && a < Count(); a++) { if((*this)[a].HasFlags()) { bIsCurve = true; } } if(bIsCurve) { OSL_ENSURE(false, "Optimize does *not* support curves, falling back to AdaptiveSubdivide()..."); tools::PolyPolygon aPolyPoly; AdaptiveSubdivide(aPolyPoly); aPolyPoly.Optimize(nOptimizeFlags); *this = aPolyPoly; } else { double fArea; const bool bEdges = ( nOptimizeFlags & PolyOptimizeFlags::EDGES ) == PolyOptimizeFlags::EDGES; sal_uInt16 nPercent = 0; if( bEdges ) { const tools::Rectangle aBound( GetBoundRect() ); fArea = ( aBound.GetWidth() + aBound.GetHeight() ) * 0.5; nPercent = 50; nOptimizeFlags &= ~PolyOptimizeFlags::EDGES; } // Optimize polygons for( sal_uInt16 i = 0, nPolyCount = mpImplPolyPolygon->mvPolyAry.size(); i < nPolyCount; i++ ) { if( bEdges ) { mpImplPolyPolygon->mvPolyAry[ i ].Optimize( PolyOptimizeFlags::NO_SAME ); tools::Polygon::ImplReduceEdges( mpImplPolyPolygon->mvPolyAry[ i ], fArea, nPercent ); } if( bool(nOptimizeFlags) ) mpImplPolyPolygon->mvPolyAry[ i ].Optimize( nOptimizeFlags ); } } } void PolyPolygon::AdaptiveSubdivide( tools::PolyPolygon& rResult ) const { rResult.Clear(); tools::Polygon aPolygon; for( size_t i = 0; i < mpImplPolyPolygon->mvPolyAry.size(); i++ ) { mpImplPolyPolygon->mvPolyAry[ i ].AdaptiveSubdivide( aPolygon, 1.0 ); rResult.Insert( aPolygon ); } } tools::PolyPolygon PolyPolygon::SubdivideBezier( const tools::PolyPolygon& rPolyPoly ) { sal_uInt16 i, nPolys = rPolyPoly.Count(); tools::PolyPolygon aPolyPoly( nPolys ); for( i=0; imvPolyAry.size(); } void PolyPolygon::Move( tools::Long nHorzMove, tools::Long nVertMove ) { // Required for DrawEngine if( nHorzMove || nVertMove ) { // move points sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size(); for ( sal_uInt16 i = 0; i < nPolyCount; i++ ) mpImplPolyPolygon->mvPolyAry[i].Move( nHorzMove, nVertMove ); } } void PolyPolygon::Translate( const Point& rTrans ) { // move points for ( sal_uInt16 i = 0, nCount = mpImplPolyPolygon->mvPolyAry.size(); i < nCount; i++ ) mpImplPolyPolygon->mvPolyAry[ i ].Translate( rTrans ); } void PolyPolygon::Scale( double fScaleX, double fScaleY ) { // Move points for ( sal_uInt16 i = 0, nCount = mpImplPolyPolygon->mvPolyAry.size(); i < nCount; i++ ) mpImplPolyPolygon->mvPolyAry[ i ].Scale( fScaleX, fScaleY ); } void PolyPolygon::Rotate( const Point& rCenter, Degree10 nAngle10 ) { nAngle10 %= 3600_deg10; if( nAngle10 ) { const double fAngle = toRadians(nAngle10); Rotate( rCenter, sin( fAngle ), cos( fAngle ) ); } } void PolyPolygon::Rotate( const Point& rCenter, double fSin, double fCos ) { // move points for ( sal_uInt16 i = 0, nCount = mpImplPolyPolygon->mvPolyAry.size(); i < nCount; i++ ) mpImplPolyPolygon->mvPolyAry[ i ].Rotate( rCenter, fSin, fCos ); } void PolyPolygon::Clip( const tools::Rectangle& rRect ) { sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size(); sal_uInt16 i; if ( !nPolyCount ) return; // If there are bezier curves involved, Polygon::Clip() is broken. // Use a temporary B2DPolyPolygon for the clipping. for ( i = 0; i < nPolyCount; i++ ) { if(mpImplPolyPolygon->mvPolyAry[i].HasFlags()) { const basegfx::B2DPolyPolygon aPoly( basegfx::utils::clipPolyPolygonOnRange( getB2DPolyPolygon(), basegfx::B2DRange( rRect.Left(), rRect.Top(), rRect.Right() + 1, rRect.Bottom() + 1), true, false)); *this = PolyPolygon( aPoly ); return; } } // Clip every polygon, deleting the empty ones for ( i = 0; i < nPolyCount; i++ ) mpImplPolyPolygon->mvPolyAry[i].Clip( rRect ); while ( nPolyCount ) { if ( GetObject( nPolyCount-1 ).GetSize() <= 2 ) Remove( nPolyCount-1 ); nPolyCount--; } } tools::Rectangle PolyPolygon::GetBoundRect() const { tools::Long nXMin=0, nXMax=0, nYMin=0, nYMax=0; bool bFirst = true; sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size(); for ( sal_uInt16 n = 0; n < nPolyCount; n++ ) { const tools::Polygon* pPoly = &mpImplPolyPolygon->mvPolyAry[n]; const Point* pAry = pPoly->GetConstPointAry(); sal_uInt16 nPointCount = pPoly->GetSize(); for ( sal_uInt16 i = 0; i < nPointCount; i++ ) { const Point* pPt = &pAry[ i ]; if ( bFirst ) { nXMin = nXMax = pPt->X(); nYMin = nYMax = pPt->Y(); bFirst = false; } else { if ( pPt->X() < nXMin ) nXMin = pPt->X(); if ( pPt->X() > nXMax ) nXMax = pPt->X(); if ( pPt->Y() < nYMin ) nYMin = pPt->Y(); if ( pPt->Y() > nYMax ) nYMax = pPt->Y(); } } } if ( !bFirst ) return tools::Rectangle( nXMin, nYMin, nXMax, nYMax ); else return tools::Rectangle(); } Polygon& PolyPolygon::operator[]( sal_uInt16 nPos ) { assert(nPos < Count() && "PolyPolygon::[](): nPos >= nSize"); return mpImplPolyPolygon->mvPolyAry[nPos]; } PolyPolygon& PolyPolygon::operator=( const tools::PolyPolygon& rPolyPoly ) { mpImplPolyPolygon = rPolyPoly.mpImplPolyPolygon; return *this; } PolyPolygon& PolyPolygon::operator=( tools::PolyPolygon&& rPolyPoly ) noexcept { mpImplPolyPolygon = std::move(rPolyPoly.mpImplPolyPolygon); return *this; } bool PolyPolygon::operator==( const tools::PolyPolygon& rPolyPoly ) const { return rPolyPoly.mpImplPolyPolygon == mpImplPolyPolygon; } SvStream& ReadPolyPolygon( SvStream& rIStream, tools::PolyPolygon& rPolyPoly ) { sal_uInt16 nPolyCount(0); // Read number of polygons rIStream.ReadUInt16( nPolyCount ); const size_t nMinRecordSize = sizeof(sal_uInt16); const size_t nMaxRecords = rIStream.remainingSize() / nMinRecordSize; if (nPolyCount > nMaxRecords) { SAL_WARN("tools", "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"); nPolyCount = nMaxRecords; } if( nPolyCount ) { rPolyPoly.mpImplPolyPolygon->mvPolyAry.resize(nPolyCount); tools::Polygon aTempPoly; for ( sal_uInt16 i = 0; i < nPolyCount; i++ ) { ReadPolygon( rIStream, aTempPoly ); rPolyPoly.mpImplPolyPolygon->mvPolyAry[i] = aTempPoly; } } else rPolyPoly = tools::PolyPolygon(); return rIStream; } SvStream& WritePolyPolygon( SvStream& rOStream, const tools::PolyPolygon& rPolyPoly ) { // Write number of polygons sal_uInt16 nPolyCount = rPolyPoly.mpImplPolyPolygon->mvPolyAry.size(); rOStream.WriteUInt16( nPolyCount ); // output polygons for ( sal_uInt16 i = 0; i < nPolyCount; i++ ) WritePolygon( rOStream, rPolyPoly.mpImplPolyPolygon->mvPolyAry[i] ); return rOStream; } void PolyPolygon::Read( SvStream& rIStream ) { VersionCompatRead aCompat(rIStream); sal_uInt16 nPolyCount(0); // Read number of polygons rIStream.ReadUInt16( nPolyCount ); const size_t nMinRecordSize = sizeof(sal_uInt16); const size_t nMaxRecords = rIStream.remainingSize() / nMinRecordSize; if (nPolyCount > nMaxRecords) { SAL_WARN("tools", "Parsing error: " << nMaxRecords << " max possible entries, but " << nPolyCount << " claimed, truncating"); nPolyCount = nMaxRecords; } if( nPolyCount ) { mpImplPolyPolygon->mvPolyAry.clear(); for ( sal_uInt16 i = 0; i < nPolyCount; i++ ) { tools::Polygon aTempPoly; aTempPoly.ImplRead( rIStream ); mpImplPolyPolygon->mvPolyAry.emplace_back( aTempPoly ); } } else *this = tools::PolyPolygon(); } void PolyPolygon::Write( SvStream& rOStream ) const { VersionCompatWrite aCompat(rOStream, 1); // Write number of polygons sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size(); rOStream.WriteUInt16( nPolyCount ); // Output polygons for ( sal_uInt16 i = 0; i < nPolyCount; i++ ) mpImplPolyPolygon->mvPolyAry[i].ImplWrite( rOStream ); } // convert to basegfx::B2DPolyPolygon and return basegfx::B2DPolyPolygon PolyPolygon::getB2DPolyPolygon() const { basegfx::B2DPolyPolygon aRetval; for(size_t a(0); a < mpImplPolyPolygon->mvPolyAry.size(); a++) { tools::Polygon const & rCandidate = mpImplPolyPolygon->mvPolyAry[a]; aRetval.append(rCandidate.getB2DPolygon()); } return aRetval; } // constructor to convert from basegfx::B2DPolyPolygon PolyPolygon::PolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon) : mpImplPolyPolygon(rPolyPolygon) { } } /* namespace tools */ ImplPolyPolygon::ImplPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon) { const sal_uInt16 nCount(sal_uInt16(rPolyPolygon.count())); DBG_ASSERT(sal_uInt32(nCount) == rPolyPolygon.count(), "PolyPolygon::PolyPolygon: Too many sub-polygons in given basegfx::B2DPolyPolygon (!)"); if ( nCount ) { mvPolyAry.resize( nCount ); for(sal_uInt16 a(0); a < nCount; a++) { const basegfx::B2DPolygon& aCandidate(rPolyPolygon.getB2DPolygon(sal_uInt32(a))); mvPolyAry[a] = tools::Polygon( aCandidate ); } } else mvPolyAry.reserve(16); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */