diff options
-rw-r--r-- | basegfx/inc/basegfx/range/b2dpolyrange.hxx | 6 | ||||
-rw-r--r-- | basegfx/inc/basegfx/tools/b2dclipstate.hxx | 119 | ||||
-rw-r--r-- | basegfx/prj/d.lst | 3 | ||||
-rw-r--r-- | basegfx/source/range/b2dpolyrange.cxx | 40 | ||||
-rw-r--r-- | basegfx/source/tools/b2dclipstate.cxx | 662 | ||||
-rwxr-xr-x | basegfx/source/tools/makefile.mk | 3 | ||||
-rw-r--r-- | basegfx/test/basegfx2d.cxx | 6 | ||||
-rw-r--r-- | basegfx/test/clipstate.cxx | 187 | ||||
-rw-r--r-- | basegfx/test/genericclipper.cxx | 168 | ||||
-rw-r--r-- | basegfx/test/makefile.mk | 11 |
10 files changed, 1199 insertions, 6 deletions
diff --git a/basegfx/inc/basegfx/range/b2dpolyrange.hxx b/basegfx/inc/basegfx/range/b2dpolyrange.hxx index f31f8319b159..bd10bc15b7b5 100644 --- a/basegfx/inc/basegfx/range/b2dpolyrange.hxx +++ b/basegfx/inc/basegfx/range/b2dpolyrange.hxx @@ -131,6 +131,12 @@ namespace basegfx */ B2DPolyPolygon solveCrossovers() const; + // element iterators + const B2DRange* begin() const; + const B2DRange* end() const; + B2DRange* begin(); + B2DRange* end(); + private: o3tl::cow_wrapper< ImplB2DPolyRange > mpImpl; }; diff --git a/basegfx/inc/basegfx/tools/b2dclipstate.hxx b/basegfx/inc/basegfx/tools/b2dclipstate.hxx new file mode 100644 index 000000000000..7d336d8cb48e --- /dev/null +++ b/basegfx/inc/basegfx/tools/b2dclipstate.hxx @@ -0,0 +1,119 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: rectcliptools.hxx,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _BGFX_TOOLS_CLIPSTATE_HXX +#define _BGFX_TOOLS_CLIPSTATE_HXX + +#include <sal/types.h> +#include <o3tl/cow_wrapper.hxx> + +////////////////////////////////////////////////////////////////////////////// + +namespace basegfx +{ + class B2DRange; + class B2DPolyRange; + class B2DPolygon; + class B2DPolyPolygon; + + namespace tools + { + class ImplB2DClipState; + + /** This class provides an optimized, symbolic clip state for graphical output + + Having a current 'clip' state is a common attribute of + almost all graphic output APIs, most of which internally + represent it via a list of rectangular bands. In contrast, + this implementation purely uses symbolic clips, but in a + quite efficient manner, deferring actual evaluation until + a clip representation is requested, and using faster code + paths for common special cases (like all-rectangle clips) + */ + class B2DClipState + { + public: + typedef o3tl::cow_wrapper< ImplB2DClipState > ImplType; + + private: + ImplType mpImpl; + + public: + /// Init clip, in 'cleared' state - everything is visible + B2DClipState(); + ~B2DClipState(); + B2DClipState( const B2DClipState& ); + explicit B2DClipState( const B2DRange& ); + explicit B2DClipState( const B2DPolygon& ); + explicit B2DClipState( const B2DPolyPolygon& ); + B2DClipState& operator=( const B2DClipState& ); + + /// unshare this poly-range with all internally shared instances + void makeUnique(); + + /// Set clip to 'null' - nothing is visible + void makeNull(); + /// returns true when clip is 'null' - nothing is visible + bool isNull() const; + + /// Set clip 'cleared' - everything is visible + void makeClear(); + /// returns true when clip is 'cleared' - everything is visible + bool isCleared() const; + + bool operator==(const B2DClipState&) const; + bool operator!=(const B2DClipState&) const; + + void unionRange(const B2DRange& ); + void unionPolygon(const B2DPolygon& ); + void unionPolyPolygon(const B2DPolyPolygon& ); + void unionClipState(const B2DClipState& ); + + void intersectRange(const B2DRange& ); + void intersectPolygon(const B2DPolygon& ); + void intersectPolyPolygon(const B2DPolyPolygon& ); + void intersectClipState(const B2DClipState& ); + + void subtractRange(const B2DRange& ); + void subtractPolygon(const B2DPolygon& ); + void subtractPolyPolygon(const B2DPolyPolygon& ); + void subtractClipState(const B2DClipState& ); + + void xorRange(const B2DRange& ); + void xorPolygon(const B2DPolygon& ); + void xorPolyPolygon(const B2DPolyPolygon& ); + void xorClipState(const B2DClipState& ); + + B2DPolyPolygon getClipPoly() const; + }; + } +} + +#endif // _BGFX_TOOLS_CLIPSTATE_HXX diff --git a/basegfx/prj/d.lst b/basegfx/prj/d.lst index 170796920945..86535375fb8c 100644 --- a/basegfx/prj/d.lst +++ b/basegfx/prj/d.lst @@ -27,7 +27,7 @@ mkdir: %_DEST%\inc%_EXT%\basegfx\range ..\inc\basegfx\range\basicrange.hxx %_DEST%\inc%_EXT%\basegfx\range\basicrange.hxx ..\inc\basegfx\range\basicbox.hxx %_DEST%\inc%_EXT%\basegfx\range\basicbox.hxx ..\inc\basegfx\range\b1drange.hxx %_DEST%\inc%_EXT%\basegfx\range\b1drange.hxx -..\inc\basegfx\range\b2dmultirange.hxx %_DEST%\inc%_EXT%\basegfx\range\b2dmultirange.hxx +..\inc\basegfx\range\b2dpolyrange.hxx %_DEST%\inc%_EXT%\basegfx\range\b2dpolyrange.hxx ..\inc\basegfx\range\b2drange.hxx %_DEST%\inc%_EXT%\basegfx\range\b2drange.hxx ..\inc\basegfx\range\b2drectangle.hxx %_DEST%\inc%_EXT%\basegfx\range\b2drectangle.hxx ..\inc\basegfx\range\b2dconnectedranges.hxx %_DEST%\inc%_EXT%\basegfx\range\b2dconnectedranges.hxx @@ -92,6 +92,7 @@ mkdir: %_DEST%\inc%_EXT%\basegfx\tools ..\inc\basegfx\tools\keystoplerp.hxx %_DEST%\inc%_EXT%\basegfx\tools\keystoplerp.hxx ..\inc\basegfx\tools\lerp.hxx %_DEST%\inc%_EXT%\basegfx\tools\lerp.hxx ..\inc\basegfx\tools\unopolypolygon.hxx %_DEST%\inc%_EXT%\basegfx\tools\unopolypolygon.hxx +..\inc\basegfx\tools\b2dclipstate.hxx %_DEST%\inc%_EXT%\basegfx\tools\b2dclipstate.hxx ..\inc\basegfx\tools\rectcliptools.hxx %_DEST%\inc%_EXT%\basegfx\tools\rectcliptools.hxx ..\inc\basegfx\tools\tools.hxx %_DEST%\inc%_EXT%\basegfx\tools\tools.hxx ..\inc\basegfx\tools\gradienttools.hxx %_DEST%\inc%_EXT%\basegfx\tools\gradienttools.hxx diff --git a/basegfx/source/range/b2dpolyrange.cxx b/basegfx/source/range/b2dpolyrange.cxx index c4969b38712c..d35af8f5dd0c 100644 --- a/basegfx/source/range/b2dpolyrange.cxx +++ b/basegfx/source/range/b2dpolyrange.cxx @@ -227,6 +227,26 @@ namespace basegfx return tools::solveCrossovers(maRanges,maOrient); } + const B2DRange* begin() const + { + return &maRanges.front(); + } + + const B2DRange* end() const + { + return &maRanges[maRanges.size()]; + } + + B2DRange* begin() + { + return &maRanges.front(); + } + + B2DRange* end() + { + return &maRanges[maRanges.size()]; + } + private: B2DRange maBounds; std::vector<B2DRange> maRanges; @@ -366,6 +386,26 @@ namespace basegfx return mpImpl->solveCrossovers(); } + const B2DRange* B2DPolyRange::begin() const + { + return mpImpl->begin(); + } + + const B2DRange* B2DPolyRange::end() const + { + return mpImpl->end(); + } + + B2DRange* B2DPolyRange::begin() + { + return mpImpl->begin(); + } + + B2DRange* B2DPolyRange::end() + { + return mpImpl->end(); + } + } // end of namespace basegfx // eof diff --git a/basegfx/source/tools/b2dclipstate.cxx b/basegfx/source/tools/b2dclipstate.cxx new file mode 100644 index 000000000000..005dca1aa66a --- /dev/null +++ b/basegfx/source/tools/b2dclipstate.cxx @@ -0,0 +1,662 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: b2dmultirange.cxx,v $ + * $Revision: 1.8 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basegfx.hxx" +#include <basegfx/tools/b2dclipstate.hxx> + +#include <basegfx/range/b2drange.hxx> +#include <basegfx/range/b2dpolyrange.hxx> +#include <basegfx/range/b2drangeclipper.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygoncutter.hxx> + +namespace basegfx +{ +namespace tools +{ + struct ImplB2DClipState + { + public: + enum Operation {UNION, INTERSECT, XOR, SUBTRACT}; + + ImplB2DClipState() : + maPendingPolygons(), + maPendingRanges(), + maClipPoly(), + mePendingOps(UNION) + {} + + explicit ImplB2DClipState( const B2DRange& rRange ) : + maPendingPolygons(), + maPendingRanges(), + maClipPoly( + tools::createPolygonFromRect(rRange)), + mePendingOps(UNION) + {} + + explicit ImplB2DClipState( const B2DPolygon& rPoly ) : + maPendingPolygons(), + maPendingRanges(), + maClipPoly(rPoly), + mePendingOps(UNION) + {} + + explicit ImplB2DClipState( const B2DPolyPolygon& rPoly ) : + maPendingPolygons(), + maPendingRanges(), + maClipPoly(rPoly), + mePendingOps(UNION) + {} + + bool isCleared() const + { + return !maClipPoly.count() + && !maPendingPolygons.count() + && !maPendingRanges.count(); + } + + void makeClear() + { + maPendingPolygons.clear(); + maPendingRanges.clear(); + maClipPoly.clear(); + mePendingOps = UNION; + } + + bool isNullClipPoly() const + { + return maClipPoly.count() == 1 + && !maClipPoly.getB2DPolygon(0).count(); + } + + bool isNull() const + { + return !maPendingPolygons.count() + && !maPendingRanges.count() + && isNullClipPoly(); + } + + void makeNull() + { + maPendingPolygons.clear(); + maPendingRanges.clear(); + maClipPoly.clear(); + maClipPoly.append(B2DPolygon()); + mePendingOps = UNION; + } + + bool operator==(const ImplB2DClipState& rRHS) const + { + return maPendingPolygons == rRHS.maPendingPolygons + && maPendingRanges == rRHS.maPendingRanges + && maClipPoly == rRHS.maClipPoly + && mePendingOps == rRHS.mePendingOps; + } + + void addRange(const B2DRange& rRange, Operation eOp) + { + if( rRange.isEmpty() ) + return; + + commitPendingPolygons(); + if( mePendingOps != eOp ) + commitPendingRanges(); + + mePendingOps = eOp; + maPendingRanges.appendElement( + rRange, + ORIENTATION_POSITIVE); + } + + void addPolygon(B2DPolygon aPoly, Operation eOp) + { + commitPendingRanges(); + if( mePendingOps != eOp ) + commitPendingPolygons(); + + mePendingOps = eOp; + maPendingPolygons.append(aPoly); + } + + void addPolyPolygon(B2DPolyPolygon aPoly, Operation eOp) + { + commitPendingRanges(); + if( mePendingOps != eOp ) + commitPendingPolygons(); + + mePendingOps = eOp; + maPendingPolygons.append(aPoly); + } + + void addClipState(const ImplB2DClipState& rOther, Operation eOp) + { + if( rOther.mePendingOps == mePendingOps + && !rOther.maClipPoly.count() + && !rOther.maPendingPolygons.count() ) + { + maPendingRanges.appendPolyRange( rOther.maPendingRanges ); + } + else + { + commitPendingRanges(); + commitPendingPolygons(); + rOther.commitPendingRanges(); + rOther.commitPendingPolygons(); + + maPendingPolygons = rOther.maClipPoly; + mePendingOps = eOp; + } + } + + void unionRange(const B2DRange& rRange) + { + if( isCleared() ) + return; + + addRange(rRange,UNION); + } + + void unionPolygon(const B2DPolygon& rPoly) + { + if( isCleared() ) + return; + + addPolygon(rPoly,UNION); + } + + void unionPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + if( isCleared() ) + return; + + addPolyPolygon(rPolyPoly,UNION); + } + + void unionClipState(const ImplB2DClipState& rOther) + { + if( isCleared() ) + return; + + addClipState(rOther, UNION); + } + + void intersectRange(const B2DRange& rRange) + { + if( isNull() ) + return; + + addRange(rRange,INTERSECT); + } + + void intersectPolygon(const B2DPolygon& rPoly) + { + if( isNull() ) + return; + + addPolygon(rPoly,INTERSECT); + } + + void intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + if( isNull() ) + return; + + addPolyPolygon(rPolyPoly,INTERSECT); + } + + void intersectClipState(const ImplB2DClipState& rOther) + { + if( isNull() ) + return; + + addClipState(rOther, INTERSECT); + } + + void subtractRange(const B2DRange& rRange ) + { + if( isNull() ) + return; + + addRange(rRange,SUBTRACT); + } + + void subtractPolygon(const B2DPolygon& rPoly) + { + if( isNull() ) + return; + + addPolygon(rPoly,SUBTRACT); + } + + void subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + if( isNull() ) + return; + + addPolyPolygon(rPolyPoly,SUBTRACT); + } + + void subtractClipState(const ImplB2DClipState& rOther) + { + if( isNull() ) + return; + + addClipState(rOther, SUBTRACT); + } + + void xorRange(const B2DRange& rRange) + { + addRange(rRange,XOR); + } + + void xorPolygon(const B2DPolygon& rPoly) + { + addPolygon(rPoly,XOR); + } + + void xorPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + addPolyPolygon(rPolyPoly,XOR); + } + + void xorClipState(const ImplB2DClipState& rOther) + { + addClipState(rOther, XOR); + } + + B2DPolyPolygon getClipPoly() const + { + commitPendingRanges(); + commitPendingPolygons(); + + return maClipPoly; + } + + private: + void commitPendingPolygons() const + { + if( !maPendingPolygons.count() ) + return; + + // assumption: maClipPoly has kept polygons prepared for + // clipping; i.e. no neutral polygons & correct + // orientation + maPendingPolygons = tools::prepareForPolygonOperation(maPendingPolygons); + const bool bIsEmpty=isNullClipPoly(); + const bool bIsCleared=!maClipPoly.count(); + switch(mePendingOps) + { + case UNION: + OSL_ASSERT( !bIsCleared ); + + if( bIsEmpty ) + maClipPoly = maPendingPolygons; + else + maClipPoly = tools::solvePolygonOperationOr( + maClipPoly, + maPendingPolygons); + break; + case INTERSECT: + OSL_ASSERT( !bIsEmpty ); + + if( bIsCleared ) + maClipPoly = maPendingPolygons; + else + maClipPoly = tools::solvePolygonOperationAnd( + maClipPoly, + maPendingPolygons); + break; + case XOR: + if( bIsEmpty ) + maClipPoly = maPendingPolygons; + else if( bIsCleared ) + { + // not representable, strictly speaking, + // using polygons with the common even/odd + // or nonzero winding number fill rule. If + // we'd want to represent it, fill rule + // would need to be "non-negative winding + // number" (and we then would return + // 'holes' here) + + // going for an ugly hack meanwhile + maClipPoly = tools::solvePolygonOperationXor( + B2DPolyPolygon( + tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))), + maPendingPolygons); + } + else + maClipPoly = tools::solvePolygonOperationXor( + maClipPoly, + maPendingPolygons); + break; + case SUBTRACT: + OSL_ASSERT( !bIsEmpty ); + + // first union all pending ones, subtract en bloc then + maPendingPolygons = solveCrossovers(maPendingPolygons); + maPendingPolygons = stripNeutralPolygons(maPendingPolygons); + maPendingPolygons = stripDispensablePolygons(maPendingPolygons, false); + + if( bIsCleared ) + { + // not representable, strictly speaking, + // using polygons with the common even/odd + // or nonzero winding number fill rule. If + // we'd want to represent it, fill rule + // would need to be "non-negative winding + // number" (and we then would return + // 'holes' here) + + // going for an ugly hack meanwhile + maClipPoly = tools::solvePolygonOperationDiff( + B2DPolyPolygon( + tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))), + maPendingPolygons); + } + else + maClipPoly = tools::solvePolygonOperationDiff( + maClipPoly, + maPendingPolygons); + break; + } + + maPendingPolygons.clear(); + mePendingOps = UNION; + } + + void commitPendingRanges() const + { + if( !maPendingRanges.count() ) + return; + + // use the specialized range clipper for the win + B2DPolyPolygon aCollectedRanges; + const bool bIsEmpty=isNullClipPoly(); + const bool bIsCleared=!maClipPoly.count(); + switch(mePendingOps) + { + case UNION: + OSL_ASSERT( !bIsCleared ); + + aCollectedRanges = maPendingRanges.solveCrossovers(); + aCollectedRanges = stripNeutralPolygons(aCollectedRanges); + aCollectedRanges = stripDispensablePolygons(aCollectedRanges, false); + if( bIsEmpty ) + maClipPoly = aCollectedRanges; + else + maClipPoly = tools::solvePolygonOperationOr( + maClipPoly, + aCollectedRanges); + break; + case INTERSECT: + OSL_ASSERT( !bIsEmpty ); + + aCollectedRanges = maPendingRanges.solveCrossovers(); + aCollectedRanges = stripNeutralPolygons(aCollectedRanges); + if( maPendingRanges.count() > 1 ) + aCollectedRanges = stripDispensablePolygons(aCollectedRanges, true); + + if( bIsCleared ) + maClipPoly = aCollectedRanges; + else + maClipPoly = tools::solvePolygonOperationAnd( + maClipPoly, + aCollectedRanges); + break; + case XOR: + aCollectedRanges = maPendingRanges.solveCrossovers(); + aCollectedRanges = stripNeutralPolygons(aCollectedRanges); + aCollectedRanges = correctOrientations(aCollectedRanges); + + if( bIsEmpty ) + maClipPoly = aCollectedRanges; + else if( bIsCleared ) + { + // not representable, strictly speaking, + // using polygons with the common even/odd + // or nonzero winding number fill rule. If + // we'd want to represent it, fill rule + // would need to be "non-negative winding + // number" (and we then would return + // 'holes' here) + + // going for an ugly hack meanwhile + maClipPoly = tools::solvePolygonOperationXor( + B2DPolyPolygon( + tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))), + aCollectedRanges); + } + else + maClipPoly = tools::solvePolygonOperationXor( + maClipPoly, + aCollectedRanges); + break; + case SUBTRACT: + OSL_ASSERT( !bIsEmpty ); + + // first union all pending ranges, subtract en bloc then + aCollectedRanges = maPendingRanges.solveCrossovers(); + aCollectedRanges = stripNeutralPolygons(aCollectedRanges); + aCollectedRanges = stripDispensablePolygons(aCollectedRanges, false); + + if( bIsCleared ) + { + // not representable, strictly speaking, + // using polygons with the common even/odd + // or nonzero winding number fill rule. If + // we'd want to represent it, fill rule + // would need to be "non-negative winding + // number" (and we then would return + // 'holes' here) + + // going for an ugly hack meanwhile + maClipPoly = tools::solvePolygonOperationDiff( + B2DPolyPolygon( + tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))), + aCollectedRanges); + } + else + maClipPoly = tools::solvePolygonOperationDiff( + maClipPoly, + aCollectedRanges); + break; + } + + maPendingRanges.clear(); + mePendingOps = UNION; + } + + mutable B2DPolyPolygon maPendingPolygons; + mutable B2DPolyRange maPendingRanges; + mutable B2DPolyPolygon maClipPoly; + mutable Operation mePendingOps; + }; + + B2DClipState::B2DClipState() : + mpImpl() + {} + + B2DClipState::~B2DClipState() + {} + + B2DClipState::B2DClipState( const B2DClipState& rOrig ) : + mpImpl(rOrig.mpImpl) + {} + + B2DClipState::B2DClipState( const B2DRange& rRange ) : + mpImpl( ImplB2DClipState(rRange) ) + {} + + B2DClipState::B2DClipState( const B2DPolygon& rPoly ) : + mpImpl( ImplB2DClipState(rPoly) ) + {} + + B2DClipState::B2DClipState( const B2DPolyPolygon& rPolyPoly ) : + mpImpl( ImplB2DClipState(rPolyPoly) ) + {} + + B2DClipState& B2DClipState::operator=( const B2DClipState& rRHS ) + { + mpImpl = rRHS.mpImpl; + return *this; + } + + void B2DClipState::makeUnique() + { + mpImpl.make_unique(); + } + + void B2DClipState::makeNull() + { + mpImpl->makeNull(); + } + + bool B2DClipState::isNull() const + { + return mpImpl->isNull(); + } + + void B2DClipState::makeClear() + { + mpImpl->makeClear(); + } + + bool B2DClipState::isCleared() const + { + return mpImpl->isCleared(); + } + + bool B2DClipState::operator==(const B2DClipState& rRHS) const + { + if(mpImpl.same_object(rRHS.mpImpl)) + return true; + + return ((*mpImpl) == (*rRHS.mpImpl)); + } + + bool B2DClipState::operator!=(const B2DClipState& rRHS) const + { + return !(*this == rRHS); + } + + void B2DClipState::unionRange(const B2DRange& rRange) + { + mpImpl->unionRange(rRange); + } + + void B2DClipState::unionPolygon(const B2DPolygon& rPoly) + { + mpImpl->unionPolygon(rPoly); + } + + void B2DClipState::unionPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + mpImpl->unionPolyPolygon(rPolyPoly); + } + + void B2DClipState::unionClipState(const B2DClipState& rState) + { + mpImpl->unionClipState(*rState.mpImpl); + } + + void B2DClipState::intersectRange(const B2DRange& rRange) + { + mpImpl->intersectRange(rRange); + } + + void B2DClipState::intersectPolygon(const B2DPolygon& rPoly) + { + mpImpl->intersectPolygon(rPoly); + } + + void B2DClipState::intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + mpImpl->intersectPolyPolygon(rPolyPoly); + } + + void B2DClipState::intersectClipState(const B2DClipState& rState) + { + mpImpl->intersectClipState(*rState.mpImpl); + } + + void B2DClipState::subtractRange(const B2DRange& rRange) + { + mpImpl->subtractRange(rRange); + } + + void B2DClipState::subtractPolygon(const B2DPolygon& rPoly) + { + mpImpl->subtractPolygon(rPoly); + } + + void B2DClipState::subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + mpImpl->subtractPolyPolygon(rPolyPoly); + } + + void B2DClipState::subtractClipState(const B2DClipState& rState) + { + mpImpl->subtractClipState(*rState.mpImpl); + } + + void B2DClipState::xorRange(const B2DRange& rRange) + { + mpImpl->xorRange(rRange); + } + + void B2DClipState::xorPolygon(const B2DPolygon& rPoly) + { + mpImpl->xorPolygon(rPoly); + } + + void B2DClipState::xorPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + mpImpl->xorPolyPolygon(rPolyPoly); + } + + void B2DClipState::xorClipState(const B2DClipState& rState) + { + mpImpl->xorClipState(*rState.mpImpl); + } + + B2DPolyPolygon B2DClipState::getClipPoly() const + { + return mpImpl->getClipPoly(); + } + +} // end of namespace tools +} // end of namespace basegfx + +// eof diff --git a/basegfx/source/tools/makefile.mk b/basegfx/source/tools/makefile.mk index d55bbae12f8f..1b19505bd331 100755 --- a/basegfx/source/tools/makefile.mk +++ b/basegfx/source/tools/makefile.mk @@ -41,7 +41,8 @@ ENABLE_EXCEPTIONS=TRUE # --- Files ------------------------------------- -SLOFILES= $(SLO)$/canvastools.obj \ +SLOFILES= $(SLO)$/b2dclipstate.obj \ + $(SLO)$/canvastools.obj \ $(SLO)$/gradienttools.obj \ $(SLO)$/debugplotter.obj \ $(SLO)$/keystoplerp.obj \ diff --git a/basegfx/test/basegfx2d.cxx b/basegfx/test/basegfx2d.cxx index 32128b927049..e30e6bf561c1 100644 --- a/basegfx/test/basegfx2d.cxx +++ b/basegfx/test/basegfx2d.cxx @@ -321,6 +321,12 @@ public: aRange.getElement(0).head == B2DRange(0,0,1,1)); CPPUNIT_ASSERT_MESSAGE("simple poly range - second element", aRange.getElement(1).head == B2DRange(2,2,3,3)); + + // B2DPolyRange relies on correctly orientated rects + const B2DRange aRect(0,0,1,1); + CPPUNIT_ASSERT_MESSAGE("createPolygonFromRect - correct orientation", + tools::getOrientation( + tools::createPolygonFromRect(aRect)) == ORIENTATION_POSITIVE ); } // Change the following lines only, if you add, remove or rename diff --git a/basegfx/test/clipstate.cxx b/basegfx/test/clipstate.cxx new file mode 100644 index 000000000000..38b18d57003f --- /dev/null +++ b/basegfx/test/clipstate.cxx @@ -0,0 +1,187 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: basegfx2d.cxx,v $ + * $Revision: 1.14 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basegfx.hxx" +// autogenerated file with codegen.pl + +#include <cppunit/simpleheader.hxx> + +#include <basegfx/tools/b2dclipstate.hxx> +#include <basegfx/range/b2dpolyrange.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/numeric/ftools.hxx> + +#include <boost/bind.hpp> + +using namespace ::basegfx; + + +namespace basegfx2d +{ + +class clipstate : public CppUnit::TestFixture +{ +private: + tools::B2DClipState aUnion1; + tools::B2DClipState aUnion2; + tools::B2DClipState aIntersect; + tools::B2DClipState aXor; + tools::B2DClipState aSubtract; + +public: + void setUp() + { + B2DRange aCenter(100, 100, -100, -100); + B2DRange aNorth(-10, -110, 10, -90); + B2DRange aWest(-110, -10, -90, 10); + B2DRange aSouth(-10, 110, 10, 90); + B2DRange aEast(110, -10, 90, 10); + + aUnion1.unionRange(aCenter); + aUnion1.unionRange(aNorth); + aUnion1.unionRange(aWest); + aUnion1.unionRange(aSouth); + aUnion1.unionRange(aEast); + + aUnion2.makeNull(); + aUnion2.unionRange(aCenter); + aUnion2.unionRange(aNorth); + aUnion2.unionRange(aWest); + aUnion2.unionRange(aSouth); + aUnion2.unionRange(aEast); + + aIntersect.intersectRange(aCenter); + aIntersect.intersectRange(aNorth); + aIntersect.intersectRange(aWest); + aIntersect.intersectRange(aSouth); + aIntersect.intersectRange(aEast); + + aXor.makeNull(); + aXor.xorRange(aCenter); + aXor.xorRange(aNorth); + aXor.xorRange(aWest); + aXor.xorRange(aSouth); + aXor.xorRange(aEast); + + aSubtract.intersectRange(aCenter); + aSubtract.subtractRange(aNorth); + aSubtract.subtractRange(aWest); + aSubtract.subtractRange(aSouth); + aSubtract.subtractRange(aEast); + } + + void tearDown() + {} + + void verifyPoly(const char* sName, const char* sSvg, const tools::B2DClipState& toTest) + { +#if defined(VERBOSE) + fprintf(stderr, "%s - svg:d=\"%s\"\n", + sName, rtl::OUStringToOString( + basegfx::tools::exportToSvgD(toTest.getClipPoly()), + RTL_TEXTENCODING_UTF8).getStr() ); +#endif + + B2DPolyPolygon aTmp1; + CPPUNIT_ASSERT_MESSAGE(sName, + tools::importFromSvgD( + aTmp1, + rtl::OUString::createFromAscii(sSvg))); + + const rtl::OUString aSvg= + tools::exportToSvgD(toTest.getClipPoly()); + B2DPolyPolygon aTmp2; + CPPUNIT_ASSERT_MESSAGE(sName, + tools::importFromSvgD( + aTmp2, + aSvg)); + + CPPUNIT_ASSERT_MESSAGE( + sName, + aTmp2 == aTmp1); + } + + void verifySimpleRange() + { + const char* unionSvg="m100 10v90h-90v10h-20v-10h-90v-90h-10v-20h10v-90h90v-10h20v10h90v90h10v20z"; + const char* intersectSvg="m-100 10v-20h10v20zm80 90v-10h20v10zm-20-190v-10h20v10zm80 100v-20h10v20z"; + const char* xorSvg="m-100 10h10v-20h-10zm90 110h20v-10h-20zm0-180h20v-10h-20zm100 110h10v-20h-10zm10 20v90h-90v10h-20v-10h-90v-90h-10v-20h10v-90h90v-10h20v10h90v90h10v20z"; + const char* subtractSvg="m-90 10v-20h-10v-90h90v10h20v-10h90v90h-10v20h10v90h-90v-10h-20v10h-90v-90z"; + + CPPUNIT_ASSERT_MESSAGE("cleared clip stays empty under union operation", + aUnion1.isCleared()); + verifyPoly("union", unionSvg, aUnion2); + verifyPoly("intersect", intersectSvg, aIntersect); + verifyPoly("xor", xorSvg, aXor); + verifyPoly("subtract", subtractSvg, aSubtract); + } + + void verifyMixedClips() + { + tools::B2DClipState aMixedClip; + + const char* unionSvg="m100 10v90h-90v10h-20v-10h-90v-90h-10v-20h10v-90h90v-10h20v10h90v90h10v20z"; + + B2DPolyPolygon aTmp1; + tools::importFromSvgD( + aTmp1, + rtl::OUString::createFromAscii(unionSvg)); + + aMixedClip.intersectPolyPolygon(aTmp1); + aMixedClip.subtractRange(B2DRange(-20,-150,20,0)); + aMixedClip.subtractRange(B2DRange(-150,-20,0,20)); + aMixedClip.xorRange(B2DRange(-150,-150,150,150)); + + const char* mixedClipSvg="m0 0v20h-100v80h90v10h20v-10h90v-90h10v-20h-10v-90h-80v100zm-40-20v-80h-80v80zm-50 170v-300h300v300z"; + verifyPoly("mixed clip", mixedClipSvg, aMixedClip); + } + + CPPUNIT_TEST_SUITE(clipstate); + CPPUNIT_TEST(verifySimpleRange); + CPPUNIT_TEST(verifyMixedClips); + CPPUNIT_TEST_SUITE_END(); +}; + +// ----------------------------------------------------------------------------- +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::clipstate, "clipstate"); +} // namespace basegfx2d + + +// ----------------------------------------------------------------------------- + +// this macro creates an empty function, which will called by the RegisterAllFunctions() +// to let the user the possibility to also register some functions by hand. +// NOADDITIONAL; + diff --git a/basegfx/test/genericclipper.cxx b/basegfx/test/genericclipper.cxx new file mode 100644 index 000000000000..9ab903ca1541 --- /dev/null +++ b/basegfx/test/genericclipper.cxx @@ -0,0 +1,168 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: basegfx2d.cxx,v $ + * $Revision: 1.14 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basegfx.hxx" +// autogenerated file with codegen.pl + +#include <cppunit/simpleheader.hxx> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/curve/b2dcubicbezier.hxx> +#include <basegfx/curve/b2dbeziertools.hxx> +#include <basegfx/range/b2dpolyrange.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygoncutter.hxx> +#include <basegfx/polygon/b2dpolygonclipper.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/numeric/ftools.hxx> + +#include <boost/bind.hpp> + +using namespace ::basegfx; + + +namespace basegfx2d +{ + +class genericclipper : public CppUnit::TestFixture +{ +private: + B2DPolygon aSelfIntersecting; + B2DPolygon aShiftedRectangle; + +public: + // initialise your test code values here. + void setUp() + { + aSelfIntersecting.append(B2DPoint(0, 0)); + aSelfIntersecting.append(B2DPoint(0, 100)); + aSelfIntersecting.append(B2DPoint(75, 100)); + aSelfIntersecting.append(B2DPoint(75, 50)); + aSelfIntersecting.append(B2DPoint(25, 50)); + aSelfIntersecting.append(B2DPoint(25, 150)); + aSelfIntersecting.append(B2DPoint(100,150)); + aSelfIntersecting.append(B2DPoint(100,0)); + aSelfIntersecting.setClosed(true); + + aShiftedRectangle = tools::createPolygonFromRect( + B2DRange(0,90,20,150)); + } + + void tearDown() + {} + + void validate(const char* pName, + const char* pValidSvgD, + B2DPolyPolygon (*pFunc)(const B2DPolyPolygon&, const B2DPolyPolygon&)) + { + const B2DPolyPolygon aSelfIntersect( + tools::prepareForPolygonOperation(aSelfIntersecting)); + const B2DPolyPolygon aRect( + tools::prepareForPolygonOperation(aShiftedRectangle)); +#if defined(VERBOSE) + fprintf(stderr, "%s input LHS - svg:d=\"%s\"\n", + pName, rtl::OUStringToOString( + basegfx::tools::exportToSvgD( + aSelfIntersect), + RTL_TEXTENCODING_UTF8).getStr() ); + fprintf(stderr, "%s input RHS - svg:d=\"%s\"\n", + pName, rtl::OUStringToOString( + basegfx::tools::exportToSvgD( + aRect), + RTL_TEXTENCODING_UTF8).getStr() ); +#endif + + const B2DPolyPolygon aRes= + pFunc(aSelfIntersect, aRect); + +#if defined(VERBOSE) + fprintf(stderr, "%s - svg:d=\"%s\"\n", + pName, rtl::OUStringToOString( + basegfx::tools::exportToSvgD(aRes), + RTL_TEXTENCODING_UTF8).getStr() ); +#endif + + rtl::OUString aValid=rtl::OUString::createFromAscii(pValidSvgD); + + CPPUNIT_ASSERT_MESSAGE(pName, + basegfx::tools::exportToSvgD(aRes) == aValid); + } + + void validateOr() + { + const char* pValid="m0 0h100v150h-75v-50h-5v50h-20v-50-10zm75 10v-50h-50v50z"; + validate("validateOr", pValid, &tools::solvePolygonOperationOr); + } + + void validateXor() + { + const char* pValid="m0 0h100v150h-75v-50h-5v50h-20v-50-10zm0 10h20v-10h-20zm75 10v-50h-50v50z"; + validate("validateXor", pValid, &tools::solvePolygonOperationXor); + } + + void validateAnd() + { + const char* pValid="m0 100v-10h20v10z"; + validate("validateAnd", pValid, &tools::solvePolygonOperationAnd); + } + + void validateDiff() + { + const char* pValid="m0 90v-90h100v150h-75v-50h-5v-10zm55 10v-50h-50v50z"; + validate("validateDiff", pValid, &tools::solvePolygonOperationDiff); + } + + // Change the following lines only, if you add, remove or rename + // member functions of the current class, + // because these macros are need by auto register mechanism. + + CPPUNIT_TEST_SUITE(genericclipper); + CPPUNIT_TEST(validateOr); + CPPUNIT_TEST(validateXor); + CPPUNIT_TEST(validateAnd); + CPPUNIT_TEST(validateDiff); + CPPUNIT_TEST_SUITE_END(); +}; + +// ----------------------------------------------------------------------------- +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::genericclipper, "genericclipper"); +} // namespace basegfx2d + + +// ----------------------------------------------------------------------------- + +// this macro creates an empty function, which will called by the RegisterAllFunctions() +// to let the user the possibility to also register some functions by hand. +// NOADDITIONAL; + diff --git a/basegfx/test/makefile.mk b/basegfx/test/makefile.mk index cf6c96d4d850..631eb32edc0e 100644 --- a/basegfx/test/makefile.mk +++ b/basegfx/test/makefile.mk @@ -43,10 +43,13 @@ ENABLE_EXCEPTIONS=TRUE # --- Common ---------------------------------------------------------- SHL1OBJS= \ - $(SLO)$/basegfx1d.obj \ - $(SLO)$/basegfx2d.obj \ - $(SLO)$/basegfx3d.obj \ - $(SLO)$/basegfxtools.obj \ + $(SLO)$/basegfx1d.obj \ + $(SLO)$/basegfx2d.obj \ + $(SLO)$/basegfx3d.obj \ + $(SLO)$/boxclipper.obj \ + $(SLO)$/basegfxtools.obj \ + $(SLO)$/clipstate.obj \ + $(SLO)$/genericclipper.obj \ $(SLO)$/testtools.obj # linking statically against basegfx parts |