diff options
Diffstat (limited to 'tools/source/generic')
-rw-r--r-- | tools/source/generic/b3dtrans.cxx | 1014 | ||||
-rw-r--r-- | tools/source/generic/bigint.cxx | 1141 | ||||
-rw-r--r-- | tools/source/generic/color.cxx | 510 | ||||
-rw-r--r-- | tools/source/generic/config.cxx | 1304 | ||||
-rw-r--r-- | tools/source/generic/fract.cxx | 736 | ||||
-rw-r--r-- | tools/source/generic/gen.cxx | 661 | ||||
-rw-r--r-- | tools/source/generic/line.cxx | 363 | ||||
-rw-r--r-- | tools/source/generic/link.cxx | 58 | ||||
-rw-r--r-- | tools/source/generic/makefile.mk | 70 | ||||
-rw-r--r-- | tools/source/generic/poly.cxx | 2375 | ||||
-rw-r--r-- | tools/source/generic/poly2.cxx | 891 | ||||
-rw-r--r-- | tools/source/generic/svborder.cxx | 77 | ||||
-rw-r--r-- | tools/source/generic/toolsin.cxx | 95 |
13 files changed, 9295 insertions, 0 deletions
diff --git a/tools/source/generic/b3dtrans.cxx b/tools/source/generic/b3dtrans.cxx new file mode 100644 index 000000000000..9ed887457035 --- /dev/null +++ b/tools/source/generic/b3dtrans.cxx @@ -0,0 +1,1014 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_tools.hxx" +#include <tools/b3dtrans.hxx> +#include <tools/debug.hxx> + +/************************************************************************* +|* +|* Transformationen fuer alle 3D Ausgaben +|* +\************************************************************************/ + +B3dTransformationSet::B3dTransformationSet() +{ + Reset(); +} + +B3dTransformationSet::~B3dTransformationSet() +{ +} + +void B3dTransformationSet::Orientation(basegfx::B3DHomMatrix& rTarget, basegfx::B3DPoint aVRP, basegfx::B3DVector aVPN, basegfx::B3DVector aVUP) +{ + rTarget.translate( -aVRP.getX(), -aVRP.getY(), -aVRP.getZ()); + aVUP.normalize(); + aVPN.normalize(); + basegfx::B3DVector aRx(aVUP); + basegfx::B3DVector aRy(aVPN); + aRx = aRx.getPerpendicular(aRy); + aRx.normalize(); + aRy = aRy.getPerpendicular(aRx); + aRy.normalize(); + basegfx::B3DHomMatrix aTemp; + aTemp.set(0, 0, aRx.getX()); + aTemp.set(0, 1, aRx.getY()); + aTemp.set(0, 2, aRx.getZ()); + aTemp.set(1, 0, aRy.getX()); + aTemp.set(1, 1, aRy.getY()); + aTemp.set(1, 2, aRy.getZ()); + aTemp.set(2, 0, aVPN.getX()); + aTemp.set(2, 1, aVPN.getY()); + aTemp.set(2, 2, aVPN.getZ()); + rTarget *= aTemp; +} + +void B3dTransformationSet::Frustum(basegfx::B3DHomMatrix& rTarget, double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar) +{ + if(!(fNear > 0.0)) + { + fNear = 0.001; + } + if(!(fFar > 0.0)) + { + fFar = 1.0; + } + if(fNear == fFar) + { + fFar = fNear + 1.0; + } + if(fLeft == fRight) + { + fLeft -= 1.0; + fRight += 1.0; + } + if(fTop == fBottom) + { + fBottom -= 1.0; + fTop += 1.0; + } + basegfx::B3DHomMatrix aTemp; + + aTemp.set(0, 0, 2.0 * fNear / (fRight - fLeft)); + aTemp.set(1, 1, 2.0 * fNear / (fTop - fBottom)); + aTemp.set(0, 2, (fRight + fLeft) / (fRight - fLeft)); + aTemp.set(1, 2, (fTop + fBottom) / (fTop - fBottom)); + aTemp.set(2, 2, -1.0 * ((fFar + fNear) / (fFar - fNear))); + aTemp.set(3, 2, -1.0); + aTemp.set(2, 3, -1.0 * ((2.0 * fFar * fNear) / (fFar - fNear))); + aTemp.set(3, 3, 0.0); + + rTarget *= aTemp; +} + +void B3dTransformationSet::Ortho(basegfx::B3DHomMatrix& rTarget, double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar) +{ + if(fNear == fFar) + { + DBG_ERROR("Near and far clipping plane in Ortho definition are identical"); + fFar = fNear + 1.0; + } + if(fLeft == fRight) + { + DBG_ERROR("Left and right in Ortho definition are identical"); + fLeft -= 1.0; + fRight += 1.0; + } + if(fTop == fBottom) + { + DBG_ERROR("Top and bottom in Ortho definition are identical"); + fBottom -= 1.0; + fTop += 1.0; + } + basegfx::B3DHomMatrix aTemp; + + aTemp.set(0, 0, 2.0 / (fRight - fLeft)); + aTemp.set(1, 1, 2.0 / (fTop - fBottom)); + aTemp.set(2, 2, -1.0 * (2.0 / (fFar - fNear))); + aTemp.set(0, 3, -1.0 * ((fRight + fLeft) / (fRight - fLeft))); + aTemp.set(1, 3, -1.0 * ((fTop + fBottom) / (fTop - fBottom))); + aTemp.set(2, 3, -1.0 * ((fFar + fNear) / (fFar - fNear))); + + rTarget *= aTemp; +} + +/************************************************************************* +|* +|* Reset der Werte +|* +\************************************************************************/ + +void B3dTransformationSet::Reset() +{ + // Matritzen auf Einheitsmatritzen + maObjectTrans.identity(); + PostSetObjectTrans(); + + Orientation(maOrientation); + PostSetOrientation(); + + maTexture.identity(); + + mfLeftBound = mfBottomBound = -1.0; + mfRightBound = mfTopBound = 1.0; + mfNearBound = 0.001; + mfFarBound = 1.001; + + meRatio = Base3DRatioGrow; + mfRatio = 0.0; + + maViewportRectangle = Rectangle(-1, -1, 2, 2); + maVisibleRectangle = maViewportRectangle; + + mbPerspective = sal_True; + + mbProjectionValid = sal_False; + mbObjectToDeviceValid = sal_False; + mbWorldToViewValid = sal_False; + + CalcViewport(); +} + +/************************************************************************* +|* +|* Objekttransformation +|* +\************************************************************************/ + +void B3dTransformationSet::SetObjectTrans(const basegfx::B3DHomMatrix& rObj) +{ + maObjectTrans = rObj; + + mbObjectToDeviceValid = sal_False; + mbInvTransObjectToEyeValid = sal_False; + + PostSetObjectTrans(); +} + +void B3dTransformationSet::PostSetObjectTrans() +{ + // Zuweisen und Inverse bestimmen + maInvObjectTrans = maObjectTrans; + maInvObjectTrans.invert(); +} + +/************************************************************************* +|* +|* Orientierungstransformation +|* +\************************************************************************/ + +void B3dTransformationSet::SetOrientation( basegfx::B3DPoint aVRP, basegfx::B3DVector aVPN, basegfx::B3DVector aVUP) +{ + maOrientation.identity(); + Orientation(maOrientation, aVRP, aVPN, aVUP); + + mbInvTransObjectToEyeValid = sal_False; + mbObjectToDeviceValid = sal_False; + mbWorldToViewValid = sal_False; + + PostSetOrientation(); +} + +void B3dTransformationSet::SetOrientation(basegfx::B3DHomMatrix& mOrient) +{ + maOrientation = mOrient; + + mbInvTransObjectToEyeValid = sal_False; + mbObjectToDeviceValid = sal_False; + mbWorldToViewValid = sal_False; + + PostSetOrientation(); +} + +void B3dTransformationSet::PostSetOrientation() +{ + // Zuweisen und Inverse bestimmen + maInvOrientation = maOrientation; + maInvOrientation.invert(); +} + +/************************************************************************* +|* +|* Projektionstransformation +|* +\************************************************************************/ + +void B3dTransformationSet::SetProjection(const basegfx::B3DHomMatrix& mProject) +{ + maProjection = mProject; + PostSetProjection(); +} + +const basegfx::B3DHomMatrix& B3dTransformationSet::GetProjection() +{ + if(!mbProjectionValid) + CalcViewport(); + return maProjection; +} + +const basegfx::B3DHomMatrix& B3dTransformationSet::GetInvProjection() +{ + if(!mbProjectionValid) + CalcViewport(); + return maInvProjection; +} + +void B3dTransformationSet::PostSetProjection() +{ + // Zuweisen und Inverse bestimmen + maInvProjection = GetProjection(); + maInvProjection.invert(); + + // Abhaengige Matritzen invalidieren + mbObjectToDeviceValid = sal_False; + mbWorldToViewValid = sal_False; +} + +/************************************************************************* +|* +|* Texturtransformation +|* +\************************************************************************/ + +void B3dTransformationSet::SetTexture(const basegfx::B2DHomMatrix& rTxt) +{ + maTexture = rTxt; + PostSetTexture(); +} + +void B3dTransformationSet::PostSetTexture() +{ +} + +/************************************************************************* +|* +|* Viewport-Transformation +|* +\************************************************************************/ + +void B3dTransformationSet::CalcViewport() +{ + // Faktoren fuer die Projektion + double fLeft(mfLeftBound); + double fRight(mfRightBound); + double fBottom(mfBottomBound); + double fTop(mfTopBound); + + // Soll das Seitenverhaeltnis Beachtung finden? + // Falls ja, Bereich der Projektion an Seitenverhaeltnis anpassen + if(GetRatio() != 0.0) + { + // Berechne aktuelles Seitenverhaeltnis der Bounds + double fBoundWidth = (double)(maViewportRectangle.GetWidth() + 1); + double fBoundHeight = (double)(maViewportRectangle.GetHeight() + 1); + double fActRatio = 1; + double fFactor; + + if(fBoundWidth != 0.0) + fActRatio = fBoundHeight / fBoundWidth; + // FIXME else in this case has a lot of problems, should this return. + + switch(meRatio) + { + case Base3DRatioShrink : + { + // Kleineren Teil vergroessern + if(fActRatio > mfRatio) + { + // X vergroessern + fFactor = 1.0 / fActRatio; + fRight *= fFactor; + fLeft *= fFactor; + } + else + { + // Y vergroessern + fFactor = fActRatio; + fTop *= fFactor; + fBottom *= fFactor; + } + break; + } + case Base3DRatioGrow : + { + // GroesserenTeil verkleinern + if(fActRatio > mfRatio) + { + // Y verkleinern + fFactor = fActRatio; + fTop *= fFactor; + fBottom *= fFactor; + } + else + { + // X verkleinern + fFactor = 1.0 / fActRatio; + fRight *= fFactor; + fLeft *= fFactor; + } + break; + } + case Base3DRatioMiddle : + { + // Mitteln + fFactor = ((1.0 / fActRatio) + 1.0) / 2.0; + fRight *= fFactor; + fLeft *= fFactor; + fFactor = (fActRatio + 1.0) / 2.0; + fTop *= fFactor; + fBottom *= fFactor; + break; + } + } + } + + // Ueberschneiden sich Darstellungsflaeche und Objektflaeche? + maSetBound = maViewportRectangle; + + // Mit den neuen Werten Projektion und ViewPort setzen + basegfx::B3DHomMatrix aNewProjection; + + // #i36281# + // OpenGL needs a little more rough additional size to not let + // the front face vanish. Changed from SMALL_DVALUE to 0.000001, + // which is 1/10000th, comared with 1/tenth of a million from SMALL_DVALUE. + const double fDistPart((mfFarBound - mfNearBound) * 0.0001); + + // Near, Far etwas grosszuegiger setzen, um falsches, + // zu kritisches clippen zu verhindern + if(mbPerspective) + { + Frustum(aNewProjection, fLeft, fRight, fBottom, fTop, mfNearBound - fDistPart, mfFarBound + fDistPart); + } + else + { + Ortho(aNewProjection, fLeft, fRight, fBottom, fTop, mfNearBound - fDistPart, mfFarBound + fDistPart); + } + + // jetzt schon auf gueltig setzen um Endlosschleife zu vermeiden + mbProjectionValid = sal_True; + + // Neue Projektion setzen + SetProjection(aNewProjection); + + // fill parameters for ViewportTransformation + // Translation + maTranslate.setX((double)maSetBound.Left() + ((maSetBound.GetWidth() - 1L) / 2.0)); + maTranslate.setY((double)maSetBound.Top() + ((maSetBound.GetHeight() - 1L) / 2.0)); + maTranslate.setZ(ZBUFFER_DEPTH_RANGE / 2.0); + + // Skalierung + maScale.setX((maSetBound.GetWidth() - 1L) / 2.0); + maScale.setY((maSetBound.GetHeight() - 1L) / -2.0); + maScale.setZ(ZBUFFER_DEPTH_RANGE / 2.0); + + // Auf Veraenderung des ViewPorts reagieren + PostSetViewport(); +} + +void B3dTransformationSet::SetRatio(double fNew) +{ + if(mfRatio != fNew) + { + mfRatio = fNew; + mbProjectionValid = sal_False; + mbObjectToDeviceValid = sal_False; + mbWorldToViewValid = sal_False; + } +} + +void B3dTransformationSet::SetRatioMode(Base3DRatio eNew) +{ + if(meRatio != eNew) + { + meRatio = eNew; + mbProjectionValid = sal_False; + mbObjectToDeviceValid = sal_False; + mbWorldToViewValid = sal_False; + } +} + +void B3dTransformationSet::SetDeviceRectangle(double fL, double fR, double fB, double fT, + sal_Bool bBroadCastChange) +{ + if(fL != mfLeftBound || fR != mfRightBound || fB != mfBottomBound || fT != mfTopBound) + { + mfLeftBound = fL; + mfRightBound = fR; + mfBottomBound = fB; + mfTopBound = fT; + + mbProjectionValid = sal_False; + mbObjectToDeviceValid = sal_False; + mbWorldToViewValid = sal_False; + + // Aenderung bekanntmachen + if(bBroadCastChange) + DeviceRectangleChange(); + } +} + +void B3dTransformationSet::SetDeviceVolume(const basegfx::B3DRange& rVol, sal_Bool bBroadCastChange) +{ + SetDeviceRectangle(rVol.getMinX(), rVol.getMaxX(), rVol.getMinY(), rVol.getMaxY(), bBroadCastChange); + SetFrontClippingPlane(rVol.getMinZ()); + SetBackClippingPlane(rVol.getMaxZ()); +} + +void B3dTransformationSet::DeviceRectangleChange() +{ +} + +void B3dTransformationSet::GetDeviceRectangle(double &fL, double &fR, double& fB, double& fT) +{ + fL = mfLeftBound; + fR = mfRightBound; + fB = mfBottomBound; + fT = mfTopBound; + + mbProjectionValid = sal_False; + mbObjectToDeviceValid = sal_False; + mbWorldToViewValid = sal_False; +} + +basegfx::B3DRange B3dTransformationSet::GetDeviceVolume() +{ + basegfx::B3DRange aRet; + + aRet.expand(basegfx::B3DTuple(mfLeftBound, mfBottomBound, mfNearBound)); + aRet.expand(basegfx::B3DTuple(mfRightBound, mfTopBound, mfFarBound)); + + return aRet; +} + +void B3dTransformationSet::SetFrontClippingPlane(double fF) +{ + if(mfNearBound != fF) + { + mfNearBound = fF; + mbProjectionValid = sal_False; + mbObjectToDeviceValid = sal_False; + mbWorldToViewValid = sal_False; + } +} + +void B3dTransformationSet::SetBackClippingPlane(double fB) +{ + if(mfFarBound != fB) + { + mfFarBound = fB; + mbProjectionValid = sal_False; + mbObjectToDeviceValid = sal_False; + mbWorldToViewValid = sal_False; + } +} + +void B3dTransformationSet::SetPerspective(sal_Bool bNew) +{ + if(mbPerspective != bNew) + { + mbPerspective = bNew; + mbProjectionValid = sal_False; + mbObjectToDeviceValid = sal_False; + mbWorldToViewValid = sal_False; + } +} + +void B3dTransformationSet::SetViewportRectangle(Rectangle& rRect, Rectangle& rVisible) +{ + if(rRect != maViewportRectangle || rVisible != maVisibleRectangle) + { + maViewportRectangle = rRect; + maVisibleRectangle = rVisible; + + mbProjectionValid = sal_False; + mbObjectToDeviceValid = sal_False; + mbWorldToViewValid = sal_False; + } +} + +void B3dTransformationSet::PostSetViewport() +{ +} + +const Rectangle& B3dTransformationSet::GetLogicalViewportBounds() +{ + if(!mbProjectionValid) + CalcViewport(); + return maSetBound; +} + +const basegfx::B3DVector& B3dTransformationSet::GetScale() +{ + if(!mbProjectionValid) + CalcViewport(); + return maScale; +} + +const basegfx::B3DVector& B3dTransformationSet::GetTranslate() +{ + if(!mbProjectionValid) + CalcViewport(); + return maTranslate; +} + +/************************************************************************* +|* +|* Hilfsmatrixberechnungsroutinen +|* +\************************************************************************/ + +void B3dTransformationSet::CalcMatObjectToDevice() +{ + // ObjectToDevice berechnen (Orientation * Projection * Object) + maObjectToDevice = maObjectTrans; + maObjectToDevice *= maOrientation; + maObjectToDevice *= GetProjection(); + + // auf gueltig setzen + mbObjectToDeviceValid = sal_True; +} + +const basegfx::B3DHomMatrix& B3dTransformationSet::GetObjectToDevice() +{ + if(!mbObjectToDeviceValid) + CalcMatObjectToDevice(); + return maObjectToDevice; +} + +void B3dTransformationSet::CalcMatInvTransObjectToEye() +{ + maInvTransObjectToEye = maObjectTrans; + maInvTransObjectToEye *= maOrientation; + maInvTransObjectToEye.invert(); + maInvTransObjectToEye.transpose(); + + // eventuelle Translationen rausschmeissen, da diese + // Matrix nur zur Transformation von Vektoren gedacht ist + maInvTransObjectToEye.set(3, 0, 0.0); + maInvTransObjectToEye.set(3, 1, 0.0); + maInvTransObjectToEye.set(3, 2, 0.0); + maInvTransObjectToEye.set(3, 3, 1.0); + + // auf gueltig setzen + mbInvTransObjectToEyeValid = sal_True; +} + +const basegfx::B3DHomMatrix& B3dTransformationSet::GetInvTransObjectToEye() +{ + if(!mbInvTransObjectToEyeValid) + CalcMatInvTransObjectToEye(); + return maInvTransObjectToEye; +} + +basegfx::B3DHomMatrix B3dTransformationSet::GetMatFromObjectToView() +{ + basegfx::B3DHomMatrix aFromObjectToView = GetObjectToDevice(); + + const basegfx::B3DVector& rScale(GetScale()); + aFromObjectToView.scale(rScale.getX(), rScale.getY(), rScale.getZ()); + const basegfx::B3DVector& rTranslate(GetTranslate()); + aFromObjectToView.translate(rTranslate.getX(), rTranslate.getY(), rTranslate.getZ()); + + return aFromObjectToView; +} + +void B3dTransformationSet::CalcMatFromWorldToView() +{ + maMatFromWorldToView = maOrientation; + maMatFromWorldToView *= GetProjection(); + const basegfx::B3DVector& rScale(GetScale()); + maMatFromWorldToView.scale(rScale.getX(), rScale.getY(), rScale.getZ()); + const basegfx::B3DVector& rTranslate(GetTranslate()); + maMatFromWorldToView.translate(rTranslate.getX(), rTranslate.getY(), rTranslate.getZ()); + maInvMatFromWorldToView = maMatFromWorldToView; + maInvMatFromWorldToView.invert(); + + // gueltig setzen + mbWorldToViewValid = sal_True; +} + +const basegfx::B3DHomMatrix& B3dTransformationSet::GetMatFromWorldToView() +{ + if(!mbWorldToViewValid) + CalcMatFromWorldToView(); + return maMatFromWorldToView; +} + +const basegfx::B3DHomMatrix& B3dTransformationSet::GetInvMatFromWorldToView() +{ + if(!mbWorldToViewValid) + CalcMatFromWorldToView(); + return maInvMatFromWorldToView; +} + +/************************************************************************* +|* +|* Direkter Zugriff auf verschiedene Transformationen +|* +\************************************************************************/ + +const basegfx::B3DPoint B3dTransformationSet::WorldToEyeCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetOrientation(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::EyeToWorldCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetInvOrientation(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::EyeToViewCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetProjection(); + aVec *= GetScale(); + aVec += GetTranslate(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::ViewToEyeCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec -= GetTranslate(); + aVec = aVec / GetScale(); + aVec *= GetInvProjection(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::WorldToViewCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetMatFromWorldToView(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::ViewToWorldCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetInvMatFromWorldToView(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::DeviceToViewCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetScale(); + aVec += GetTranslate(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::ViewToDeviceCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec -= GetTranslate(); + aVec = aVec / GetScale(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::ObjectToWorldCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetObjectTrans(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::WorldToObjectCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetInvObjectTrans(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::ObjectToViewCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetObjectTrans(); + aVec *= GetMatFromWorldToView(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::ViewToObjectCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetInvMatFromWorldToView(); + aVec *= GetInvObjectTrans(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::ObjectToEyeCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetObjectTrans(); + aVec *= GetOrientation(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::EyeToObjectCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetInvOrientation(); + aVec *= GetInvObjectTrans(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::DeviceToEyeCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetInvProjection(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::EyeToDeviceCoor(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetProjection(); + return aVec; +} + +const basegfx::B3DPoint B3dTransformationSet::InvTransObjectToEye(const basegfx::B3DPoint& rVec) +{ + basegfx::B3DPoint aVec(rVec); + aVec *= GetInvTransObjectToEye(); + return aVec; +} + +const basegfx::B2DPoint B3dTransformationSet::TransTextureCoor(const basegfx::B2DPoint& rVec) +{ + basegfx::B2DPoint aVec(rVec); + aVec *= GetTexture(); + return aVec; +} + +/************************************************************************* +|* +|* Konstruktor B3dViewport +|* +\************************************************************************/ + +B3dViewport::B3dViewport() +: B3dTransformationSet(), + aVRP(0, 0, 0), + aVPN(0, 0, 1), + aVUV(0, 1, 0) +{ + CalcOrientation(); +} + +B3dViewport::~B3dViewport() +{ +} + +void B3dViewport::SetVRP(const basegfx::B3DPoint& rNewVRP) +{ + aVRP = rNewVRP; + CalcOrientation(); +} + +void B3dViewport::SetVPN(const basegfx::B3DVector& rNewVPN) +{ + aVPN = rNewVPN; + CalcOrientation(); +} + +void B3dViewport::SetVUV(const basegfx::B3DVector& rNewVUV) +{ + aVUV = rNewVUV; + CalcOrientation(); +} + +void B3dViewport::SetViewportValues( + const basegfx::B3DPoint& rNewVRP, + const basegfx::B3DVector& rNewVPN, + const basegfx::B3DVector& rNewVUV) +{ + aVRP = rNewVRP; + aVPN = rNewVPN; + aVUV = rNewVUV; + CalcOrientation(); +} + +void B3dViewport::CalcOrientation() +{ + SetOrientation(aVRP, aVPN, aVUV); +} + +/************************************************************************* +|* +|* Konstruktor B3dViewport +|* +\************************************************************************/ + +B3dCamera::B3dCamera( + const basegfx::B3DPoint& rPos, const basegfx::B3DVector& rLkAt, + double fFocLen, double fBnkAng, sal_Bool bUseFocLen) +: B3dViewport(), + aPosition(rPos), + aCorrectedPosition(rPos), + aLookAt(rLkAt), + fFocalLength(fFocLen), + fBankAngle(fBnkAng), + bUseFocalLength(bUseFocLen) +{ + CalcNewViewportValues(); +} + +B3dCamera::~B3dCamera() +{ +} + +void B3dCamera::SetPosition(const basegfx::B3DPoint& rNewPos) +{ + if(rNewPos != aPosition) + { + // Zuweisen + aCorrectedPosition = aPosition = rNewPos; + + // Neuberechnung + CalcNewViewportValues(); + } +} + +void B3dCamera::SetLookAt(const basegfx::B3DVector& rNewLookAt) +{ + if(rNewLookAt != aLookAt) + { + // Zuweisen + aLookAt = rNewLookAt; + + // Neuberechnung + CalcNewViewportValues(); + } +} + +void B3dCamera::SetPositionAndLookAt(const basegfx::B3DPoint& rNewPos, const basegfx::B3DVector& rNewLookAt) +{ + if(rNewPos != aPosition || rNewLookAt != aLookAt) + { + // Zuweisen + aPosition = rNewPos; + aLookAt = rNewLookAt; + + // Neuberechnung + CalcNewViewportValues(); + } +} + +void B3dCamera::SetFocalLength(double fLen) +{ + if(fLen != fFocalLength) + { + // Zuweisen + if(fLen < 5.0) + fLen = 5.0; + fFocalLength = fLen; + + // Neuberechnung + CalcNewViewportValues(); + } +} + +void B3dCamera::SetBankAngle(double fAngle) +{ + if(fAngle != fBankAngle) + { + // Zuweisen + fBankAngle = fAngle; + + // Neuberechnung + CalcNewViewportValues(); + } +} + +void B3dCamera::SetUseFocalLength(sal_Bool bNew) +{ + if(bNew != (sal_Bool)bUseFocalLength) + { + // Zuweisen + bUseFocalLength = bNew; + + // Neuberechnung + CalcNewViewportValues(); + } +} + +void B3dCamera::DeviceRectangleChange() +{ + // call parent + B3dViewport::DeviceRectangleChange(); + + // Auf Aenderung reagieren + CalcNewViewportValues(); +} + +void B3dCamera::CalcNewViewportValues() +{ + basegfx::B3DVector aViewVector(aPosition - aLookAt); + basegfx::B3DVector aNewVPN(aViewVector); + + basegfx::B3DVector aNewVUV(0.0, 1.0, 0.0); + if(aNewVPN.getLength() < aNewVPN.getY()) + aNewVUV.setX(0.5); + + aNewVUV.normalize(); + aNewVPN.normalize(); + + basegfx::B3DVector aNewToTheRight = aNewVPN; + aNewToTheRight = aNewToTheRight.getPerpendicular(aNewVUV); + aNewToTheRight.normalize(); + aNewVUV = aNewToTheRight.getPerpendicular(aNewVPN); + aNewVUV.normalize(); + + SetViewportValues(aPosition, aNewVPN, aNewVUV); + if(CalcFocalLength()) + SetViewportValues(aCorrectedPosition, aNewVPN, aNewVUV); + + if(fBankAngle != 0.0) + { + basegfx::B3DHomMatrix aRotMat; + aRotMat.rotate(0.0, 0.0, fBankAngle); + basegfx::B3DVector aUp(0.0, 1.0, 0.0); + aUp *= aRotMat; + aUp = EyeToWorldCoor(aUp); + aUp.normalize(); + SetVUV(aUp); + } +} + +sal_Bool B3dCamera::CalcFocalLength() +{ + double fWidth = GetDeviceRectangleWidth(); + sal_Bool bRetval = sal_False; + + if(bUseFocalLength) + { + // Position aufgrund der FocalLength korrigieren + aCorrectedPosition = basegfx::B3DPoint(0.0, 0.0, fFocalLength * fWidth / 35.0); + aCorrectedPosition = EyeToWorldCoor(aCorrectedPosition); + bRetval = sal_True; + } + else + { + // FocalLength anhand der Position anpassen + basegfx::B3DPoint aOldPosition; + aOldPosition = WorldToEyeCoor(aOldPosition); + if(fWidth != 0.0) + fFocalLength = aOldPosition.getZ() / fWidth * 35.0; + if(fFocalLength < 5.0) + fFocalLength = 5.0; + } + return bRetval; +} + +// eof diff --git a/tools/source/generic/bigint.cxx b/tools/source/generic/bigint.cxx new file mode 100644 index 000000000000..7b10f31d733f --- /dev/null +++ b/tools/source/generic/bigint.cxx @@ -0,0 +1,1141 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_tools.hxx" + +#include <math.h> +#include <tools/tools.h> + +#include <tools/bigint.hxx> +#include <tools/string.hxx> +#include <tools/debug.hxx> + +#include <string.h> +#include <ctype.h> + +static const long MY_MAXLONG = 0x3fffffff; +static const long MY_MINLONG = -MY_MAXLONG; +static const long MY_MAXSHORT = 0x00007fff; +static const long MY_MINSHORT = -MY_MAXSHORT; + +/* Die ganzen Algorithmen zur Addition, Subtraktion, Multiplikation und + * Division von langen Zahlen stammen aus SEMINUMERICAL ALGORITHMS von + * DONALD E. KNUTH aus der Reihe The Art of Computer Programming. Zu finden + * sind diese Algorithmen im Kapitel 4.3.1. The Classical Algorithms. + */ + +// Muss auf UINT16/INT16/UINT32/INT32 umgestellt werden !!! W.P. + +// ----------------------------------------------------------------------- + +void BigInt::MakeBigInt( const BigInt& rVal ) +{ + if ( rVal.bIsBig ) + { + memcpy( (void*)this, (const void*)&rVal, sizeof( BigInt ) ); + while ( nLen > 1 && nNum[nLen-1] == 0 ) + nLen--; + } + else + { + long nTmp = rVal.nVal; + + nVal = rVal.nVal; + bIsBig = sal_True; + if ( nTmp < 0 ) + { + bIsNeg = sal_True; + nTmp = -nTmp; + } + else + bIsNeg = sal_False; + + nNum[0] = (sal_uInt16)(nTmp & 0xffffL); + nNum[1] = (sal_uInt16)(nTmp >> 16); +#ifndef _WIN16 + if ( nTmp & 0xffff0000L ) +#else + long l = 0xffff0000L; + if ( nTmp & l ) +#endif + nLen = 2; + else + nLen = 1; + } +} + +// ----------------------------------------------------------------------- + +void BigInt::Normalize() +{ + if ( bIsBig ) + { + while ( nLen > 1 && nNum[nLen-1] == 0 ) + nLen--; + + if ( nLen < 3 ) + { + if ( nLen < 2 ) + nVal = nNum[0]; + else if ( nNum[1] & 0x8000 ) + return; + else + nVal = ((long)nNum[1] << 16) + nNum[0]; + + bIsBig = sal_False; + + if ( bIsNeg ) + nVal = -nVal; + } + // else ist nVal undefiniert !!! W.P. + } + // wozu, nLen ist doch undefiniert ??? W.P. + else if ( nVal & 0xFFFF0000L ) + nLen = 2; + else + nLen = 1; +} + +// ----------------------------------------------------------------------- + +void BigInt::Mult( const BigInt &rVal, sal_uInt16 nMul ) +{ + sal_uInt16 nK = 0; + for ( int i = 0; i < rVal.nLen; i++ ) + { + sal_uInt32 nTmp = (sal_uInt32)rVal.nNum[i] * (sal_uInt32)nMul + nK; + nK = (sal_uInt16)(nTmp >> 16); + nNum[i] = (sal_uInt16)nTmp; + } + + if ( nK ) + { + nNum[rVal.nLen] = nK; + nLen = rVal.nLen + 1; + } + else + nLen = rVal.nLen; + + bIsBig = sal_True; + bIsNeg = rVal.bIsNeg; +} + +// ----------------------------------------------------------------------- + +void BigInt::Div( sal_uInt16 nDiv, sal_uInt16& rRem ) +{ + sal_uInt32 nK = 0; + for ( int i = nLen - 1; i >= 0; i-- ) + { + sal_uInt32 nTmp = (sal_uInt32)nNum[i] + (nK << 16); + nNum[i] = (sal_uInt16)(nTmp / nDiv); + nK = nTmp % nDiv; + } + rRem = (sal_uInt16)nK; + + if ( nNum[nLen-1] == 0 ) + nLen -= 1; +} + +// ----------------------------------------------------------------------- + +sal_Bool BigInt::IsLess( const BigInt& rVal ) const +{ + if ( rVal.nLen < nLen) + return sal_True; + if ( rVal.nLen > nLen ) + return sal_False; + + int i; + for ( i = nLen - 1; i > 0 && nNum[i] == rVal.nNum[i]; i-- ) + { + } + return rVal.nNum[i] < nNum[i]; +} + +// ----------------------------------------------------------------------- + +void BigInt::AddLong( BigInt& rB, BigInt& rErg ) +{ + if ( bIsNeg == rB.bIsNeg ) + { + int i; + char len; + + // wenn die Zahlen unterschiedlich lang sind, sollte zunaechst bei + // der kleineren Zahl die fehlenden Ziffern mit 0 initialisert werden + if (nLen >= rB.nLen) + { + len = nLen; + for (i = rB.nLen; i < len; i++) + rB.nNum[i] = 0; + } + else + { + len = rB.nLen; + for (i = nLen; i < len; i++) + nNum[i] = 0; + } + + // Die Ziffern werden von hinten nach vorne addiert + long k; + long nZ = 0; + for (i = 0, k = 0; i < len; i++) { + nZ = (long)nNum[i] + (long)rB.nNum[i] + k; + if (nZ & 0xff0000L) + k = 1; + else + k = 0; + rErg.nNum[i] = (sal_uInt16)(nZ & 0xffffL); + } + // Trat nach der letzten Addition ein Ueberlauf auf, muss dieser + // noch ins Ergebis uebernommen werden + if (nZ & 0xff0000L) // oder if(k) + { + rErg.nNum[i] = 1; + len++; + } + // Die Laenge und das Vorzeichen setzen + rErg.nLen = len; + rErg.bIsNeg = bIsNeg && rB.bIsNeg; + rErg.bIsBig = sal_True; + } + // Wenn nur einer der beiden Operanten negativ ist, wird aus der + // Addition eine Subtaktion + else if (bIsNeg) + { + bIsNeg = sal_False; + rB.SubLong(*this, rErg); + bIsNeg = sal_True; + } + else + { + rB.bIsNeg = sal_False; + SubLong(rB, rErg); + rB.bIsNeg = sal_True; + } +} + +// ----------------------------------------------------------------------- + +void BigInt::SubLong( BigInt& rB, BigInt& rErg ) +{ + if ( bIsNeg == rB.bIsNeg ) + { + int i; + char len; + long nZ, k; + + // wenn die Zahlen unterschiedlich lang sind, sollte zunaechst bei + // der kleineren Zahl die fehlenden Ziffern mit 0 initialisert werden + if (nLen >= rB.nLen) + { + len = nLen; + for (i = rB.nLen; i < len; i++) + rB.nNum[i] = 0; + } + else + { + len = rB.nLen; + for (i = nLen; i < len; i++) + nNum[i] = 0; + } + + if ( IsLess(rB) ) + { + for (i = 0, k = 0; i < len; i++) + { + nZ = (long)nNum[i] - (long)rB.nNum[i] + k; + if (nZ < 0) + k = -1; + else + k = 0; + rErg.nNum[i] = (sal_uInt16)(nZ & 0xffffL); + } + rErg.bIsNeg = bIsNeg; + } + else + { + for (i = 0, k = 0; i < len; i++) + { + nZ = (long)rB.nNum[i] - (long)nNum[i] + k; + if (nZ < 0) + k = -1; + else + k = 0; + rErg.nNum[i] = (sal_uInt16)(nZ & 0xffffL); + } + // wenn a < b, dann Vorzeichen vom Ergebnis umdrehen + rErg.bIsNeg = !bIsNeg; + } + rErg.nLen = len; + rErg.bIsBig = sal_True; + } + // Wenn nur einer der beiden Operanten negativ ist, wird aus der + // Subtaktion eine Addition + else if (bIsNeg) + { + bIsNeg = sal_False; + AddLong(rB, rErg); + bIsNeg = sal_True; + rErg.bIsNeg = sal_True; + } + else + { + rB.bIsNeg = sal_False; + AddLong(rB, rErg); + rB.bIsNeg = sal_True; + rErg.bIsNeg = sal_False; + } +} + +// ----------------------------------------------------------------------- + +void BigInt::MultLong( const BigInt& rB, BigInt& rErg ) const +{ + int i, j; + sal_uInt32 nZ, k; + + rErg.bIsNeg = bIsNeg != rB.bIsNeg; + rErg.bIsBig = sal_True; + rErg.nLen = nLen + rB.nLen; + + for (i = 0; i < rErg.nLen; i++) + rErg.nNum[i] = 0; + + for (j = 0; j < rB.nLen; j++) + { + for (i = 0, k = 0; i < nLen; i++) + { + nZ = (sal_uInt32)nNum[i] * (sal_uInt32)rB.nNum[j] + + (sal_uInt32)rErg.nNum[i + j] + k; + rErg.nNum[i + j] = (sal_uInt16)(nZ & 0xffffUL); + k = nZ >> 16; + } + rErg.nNum[i + j] = (sal_uInt16)k; + } +} + +// ----------------------------------------------------------------------- + +void BigInt::DivLong( const BigInt& rB, BigInt& rErg ) const +{ + int i, j; + long nTmp; + sal_uInt16 nK, nQ, nMult; + short nLenB = rB.nLen; + short nLenB1 = rB.nLen - 1; + BigInt aTmpA, aTmpB; + + nMult = (sal_uInt16)(0x10000L / ((long)rB.nNum[nLenB1] + 1)); + + aTmpA.Mult( *this, nMult ); + if ( aTmpA.nLen == nLen ) + { + aTmpA.nNum[aTmpA.nLen] = 0; + aTmpA.nLen++; + } + + aTmpB.Mult( rB, nMult ); + + for (j = aTmpA.nLen - 1; j >= nLenB; j--) + { // Raten des Divisors + nTmp = ( (long)aTmpA.nNum[j] << 16 ) + aTmpA.nNum[j - 1]; + if (aTmpA.nNum[j] == aTmpB.nNum[nLenB1]) + nQ = 0xFFFF; + else + nQ = (sal_uInt16)(((sal_uInt32)nTmp) / aTmpB.nNum[nLenB1]); + + if ( ((sal_uInt32)aTmpB.nNum[nLenB1 - 1] * nQ) > + ((((sal_uInt32)nTmp) - aTmpB.nNum[nLenB1] * nQ) << 16) + aTmpA.nNum[j - 2]) + nQ--; + // Und hier faengt das Teilen an + nK = 0; + nTmp = 0; + for (i = 0; i < nLenB; i++) + { + nTmp = (long)aTmpA.nNum[j - nLenB + i] + - ((long)aTmpB.nNum[i] * nQ) + - nK; + aTmpA.nNum[j - nLenB + i] = (sal_uInt16)nTmp; + nK = (sal_uInt16) (nTmp >> 16); + if ( nK ) + nK = (sal_uInt16)(0x10000UL - nK); + } + unsigned short& rNum( aTmpA.nNum[j - nLenB + i] ); + rNum = rNum - nK; // MSVC yields a warning on -= here, so don't use it + if (aTmpA.nNum[j - nLenB + i] == 0) + rErg.nNum[j - nLenB] = nQ; + else + { + rErg.nNum[j - nLenB] = nQ - 1; + nK = 0; + for (i = 0; i < nLenB; i++) + { + nTmp = aTmpA.nNum[j - nLenB + i] + aTmpB.nNum[i] + nK; + aTmpA.nNum[j - nLenB + i] = (sal_uInt16)(nTmp & 0xFFFFL); + if (nTmp & 0xFFFF0000L) + nK = 1; + else + nK = 0; + } + } + } + + rErg.bIsNeg = bIsNeg != rB.bIsNeg; + rErg.bIsBig = sal_True; + rErg.nLen = nLen - rB.nLen + 1; +} + +// ----------------------------------------------------------------------- + +void BigInt::ModLong( const BigInt& rB, BigInt& rErg ) const +{ + short i, j; + long nTmp; + sal_uInt16 nK, nQ, nMult; + short nLenB = rB.nLen; + short nLenB1 = rB.nLen - 1; + BigInt aTmpA, aTmpB; + + nMult = (sal_uInt16)(0x10000L / ((long)rB.nNum[nLenB1] + 1)); + + aTmpA.Mult( *this, nMult); + if ( aTmpA.nLen == nLen ) + { + aTmpA.nNum[aTmpA.nLen] = 0; + aTmpA.nLen++; + } + + aTmpB.Mult( rB, nMult); + + for (j = aTmpA.nLen - 1; j >= nLenB; j--) + { // Raten des Divisors + nTmp = ( (long)aTmpA.nNum[j] << 16 ) + aTmpA.nNum[j - 1]; + if (aTmpA.nNum[j] == aTmpB.nNum[nLenB1]) + nQ = 0xFFFF; + else + nQ = (sal_uInt16)(((sal_uInt32)nTmp) / aTmpB.nNum[nLenB1]); + + if ( ((sal_uInt32)aTmpB.nNum[nLenB1 - 1] * nQ) > + ((((sal_uInt32)nTmp) - aTmpB.nNum[nLenB1] * nQ) << 16) + aTmpA.nNum[j - 2]) + nQ--; + // Und hier faengt das Teilen an + nK = 0; + nTmp = 0; + for (i = 0; i < nLenB; i++) + { + nTmp = (long)aTmpA.nNum[j - nLenB + i] + - ((long)aTmpB.nNum[i] * nQ) + - nK; + aTmpA.nNum[j - nLenB + i] = (sal_uInt16)nTmp; + nK = (sal_uInt16) (nTmp >> 16); + if ( nK ) + nK = (sal_uInt16)(0x10000UL - nK); + } + unsigned short& rNum( aTmpA.nNum[j - nLenB + i] ); + rNum = rNum - nK; + if (aTmpA.nNum[j - nLenB + i] == 0) + rErg.nNum[j - nLenB] = nQ; + else + { + rErg.nNum[j - nLenB] = nQ - 1; + nK = 0; + for (i = 0; i < nLenB; i++) { + nTmp = aTmpA.nNum[j - nLenB + i] + aTmpB.nNum[i] + nK; + aTmpA.nNum[j - nLenB + i] = (sal_uInt16)(nTmp & 0xFFFFL); + if (nTmp & 0xFFFF0000L) + nK = 1; + else + nK = 0; + } + } + } + + rErg = aTmpA; + rErg.Div( nMult, nQ ); +} + +// ----------------------------------------------------------------------- + +sal_Bool BigInt::ABS_IsLess( const BigInt& rB ) const +{ + if (bIsBig || rB.bIsBig) + { + BigInt nA, nB; + nA.MakeBigInt( *this ); + nB.MakeBigInt( rB ); + if (nA.nLen == nB.nLen) + { + int i; + for (i = nA.nLen - 1; i > 0 && nA.nNum[i] == nB.nNum[i]; i--) + { + } + return nA.nNum[i] < nB.nNum[i]; + } + else + return nA.nLen < nB.nLen; + } + if ( nVal < 0 ) + if ( rB.nVal < 0 ) + return nVal > rB.nVal; + else + return nVal > -rB.nVal; + else + if ( rB.nVal < 0 ) + return nVal < -rB.nVal; + else + return nVal < rB.nVal; +} + +// ----------------------------------------------------------------------- + +BigInt::BigInt( const BigInt& rBigInt ) +{ + if ( rBigInt.bIsBig ) + memcpy( (void*)this, (const void*)&rBigInt, sizeof( BigInt ) ); + else + { + bIsSet = rBigInt.bIsSet; + bIsBig = sal_False; + nVal = rBigInt.nVal; + } +} + +// ----------------------------------------------------------------------- + +BigInt::BigInt( const ByteString& rString ) +{ + bIsSet = sal_True; + bIsNeg = sal_False; + bIsBig = sal_False; + nVal = 0; + + sal_Bool bNeg = sal_False; + const sal_Char* p = rString.GetBuffer(); + if ( *p == '-' ) + { + bNeg = sal_True; + p++; + } + while( *p >= '0' && *p <= '9' ) + { + *this *= 10; + *this += *p - '0'; + p++; + } + if ( bIsBig ) + bIsNeg = bNeg; + else if( bNeg ) + nVal = -nVal; +} + +// ----------------------------------------------------------------------- + +BigInt::BigInt( const UniString& rString ) +{ + bIsSet = sal_True; + bIsNeg = sal_False; + bIsBig = sal_False; + nVal = 0; + + sal_Bool bNeg = sal_False; + const sal_Unicode* p = rString.GetBuffer(); + if ( *p == '-' ) + { + bNeg = sal_True; + p++; + } + while( *p >= '0' && *p <= '9' ) + { + *this *= 10; + *this += *p - '0'; + p++; + } + if ( bIsBig ) + bIsNeg = bNeg; + else if( bNeg ) + nVal = -nVal; +} + +// ----------------------------------------------------------------------- + +BigInt::BigInt( double nValue ) +{ + bIsSet = sal_True; + + if ( nValue < 0 ) + { + nValue *= -1; + bIsNeg = sal_True; + } + else + { + bIsNeg = sal_False; + } + + if ( nValue < 1 ) + { + bIsBig = sal_False; + nVal = 0; + } + else + { + bIsBig = sal_True; + + int i=0; + + while ( ( nValue > 65536.0 ) && ( i < MAX_DIGITS ) ) + { + nNum[i] = (sal_uInt16) fmod( nValue, 65536.0 ); + nValue -= nNum[i]; + nValue /= 65536.0; + i++; + } + if ( i < MAX_DIGITS ) + nNum[i++] = (sal_uInt16) nValue; + + nLen = i; + + if ( i < 3 ) + Normalize(); + } +} + +// ----------------------------------------------------------------------- + +BigInt::BigInt( sal_uInt32 nValue ) +{ + bIsSet = sal_True; + if ( nValue & 0x80000000UL ) + { + bIsBig = sal_True; + bIsNeg = sal_False; + nNum[0] = (sal_uInt16)(nValue & 0xffffUL); + nNum[1] = (sal_uInt16)(nValue >> 16); + nLen = 2; + } + else + { + bIsBig = sal_False; + nVal = nValue; + } +} + +// ----------------------------------------------------------------------- + +BigInt::operator ULONG() const +{ + if ( !bIsBig ) + return (sal_uInt32)nVal; + else if ( nLen == 2 ) + { + sal_uInt32 nRet; + nRet = ((sal_uInt32)nNum[1]) << 16; + nRet += nNum[0]; + return nRet; + } + return 0; +} + +// ----------------------------------------------------------------------- + +BigInt::operator double() const +{ + if ( !bIsBig ) + return (double) nVal; + else + { + int i = nLen-1; + double nRet = (double) ((sal_uInt32)nNum[i]); + + while ( i ) + { + nRet *= 65536.0; + i--; + nRet += (double) ((sal_uInt32)nNum[i]); + } + + if ( bIsNeg ) + nRet *= -1; + + return nRet; + } +} + +// ----------------------------------------------------------------------- + +ByteString BigInt::GetByteString() const +{ + ByteString aString; + + if ( !bIsBig ) + aString = ByteString::CreateFromInt32( nVal ); + else + { + BigInt aTmp( *this ); + BigInt a1000000000( 1000000000L ); + aTmp.Abs(); + + do + { + BigInt a = aTmp; + a %= a1000000000; + aTmp /= a1000000000; + + ByteString aStr = aString; + if ( a.nVal < 100000000L ) + { // leading 0s + aString = ByteString::CreateFromInt32( a.nVal + 1000000000L ); + aString.Erase( 0, 1 ); + } + else + aString = ByteString::CreateFromInt32( a.nVal ); + aString += aStr; + } + while( aTmp.bIsBig ); + + ByteString aStr = aString; + if ( bIsNeg ) + aString = ByteString::CreateFromInt32( -aTmp.nVal ); + else + aString = ByteString::CreateFromInt32( aTmp.nVal ); + aString += aStr; + } + + return aString; +} + +// ----------------------------------------------------------------------- + +UniString BigInt::GetString() const +{ + UniString aString; + + if ( !bIsBig ) + aString = UniString::CreateFromInt32( nVal ); + else + { + BigInt aTmp( *this ); + BigInt a1000000000( 1000000000L ); + aTmp.Abs(); + + do + { + BigInt a = aTmp; + a %= a1000000000; + aTmp /= a1000000000; + + UniString aStr = aString; + if ( a.nVal < 100000000L ) + { // leading 0s + aString = UniString::CreateFromInt32( a.nVal + 1000000000L ); + aString.Erase(0,1); + } + else + aString = UniString::CreateFromInt32( a.nVal ); + aString += aStr; + } + while( aTmp.bIsBig ); + + UniString aStr = aString; + if ( bIsNeg ) + aString = UniString::CreateFromInt32( -aTmp.nVal ); + else + aString = UniString::CreateFromInt32( aTmp.nVal ); + aString += aStr; + } + + return aString; +} + +// ----------------------------------------------------------------------- + +BigInt& BigInt::operator=( const BigInt& rBigInt ) +{ + if ( rBigInt.bIsBig ) + memcpy( (void*)this, (const void*)&rBigInt, sizeof( BigInt ) ); + else + { + bIsSet = rBigInt.bIsSet; + bIsBig = sal_False; + nVal = rBigInt.nVal; + } + return *this; +} + +// ----------------------------------------------------------------------- + +BigInt& BigInt::operator+=( const BigInt& rVal ) +{ + if ( !bIsBig && !rVal.bIsBig ) + { + if( nVal <= MY_MAXLONG && rVal.nVal <= MY_MAXLONG + && nVal >= MY_MINLONG && rVal.nVal >= MY_MINLONG ) + { // wir bewegen uns im ungefaehrlichem Bereich + nVal += rVal.nVal; + return *this; + } + + if( (nVal < 0) != (rVal.nVal < 0) ) + { // wir bewegen uns im ungefaehrlichem Bereich + nVal += rVal.nVal; + return *this; + } + } + + BigInt aTmp1, aTmp2; + aTmp1.MakeBigInt( *this ); + aTmp2.MakeBigInt( rVal ); + aTmp1.AddLong( aTmp2, *this ); + Normalize(); + return *this; +} + +// ----------------------------------------------------------------------- + +BigInt& BigInt::operator-=( const BigInt& rVal ) +{ + if ( !bIsBig && !rVal.bIsBig ) + { + if ( nVal <= MY_MAXLONG && rVal.nVal <= MY_MAXLONG && + nVal >= MY_MINLONG && rVal.nVal >= MY_MINLONG ) + { // wir bewegen uns im ungefaehrlichem Bereich + nVal -= rVal.nVal; + return *this; + } + + if ( (nVal < 0) == (rVal.nVal < 0) ) + { // wir bewegen uns im ungefaehrlichem Bereich + nVal -= rVal.nVal; + return *this; + } + } + + BigInt aTmp1, aTmp2; + aTmp1.MakeBigInt( *this ); + aTmp2.MakeBigInt( rVal ); + aTmp1.SubLong( aTmp2, *this ); + Normalize(); + return *this; +} + +// ----------------------------------------------------------------------- + +BigInt& BigInt::operator*=( const BigInt& rVal ) +{ + if ( !bIsBig && !rVal.bIsBig + && nVal <= MY_MAXSHORT && rVal.nVal <= MY_MAXSHORT + && nVal >= MY_MINSHORT && rVal.nVal >= MY_MINSHORT ) + // nicht optimal !!! W.P. + { // wir bewegen uns im ungefaehrlichem Bereich + nVal *= rVal.nVal; + } + else + { + BigInt aTmp1, aTmp2; + aTmp1.MakeBigInt( rVal ); + aTmp2.MakeBigInt( *this ); + aTmp1.MultLong(aTmp2, *this); + Normalize(); + } + return *this; +} + +// ----------------------------------------------------------------------- + +BigInt& BigInt::operator/=( const BigInt& rVal ) +{ + if ( !rVal.bIsBig ) + { + if ( rVal.nVal == 0 ) + { + DBG_ERROR( "BigInt::operator/ --> divide by zero" ); + return *this; + } + + if ( !bIsBig ) + { + // wir bewegen uns im ungefaehrlichem Bereich + nVal /= rVal.nVal; + return *this; + } + + if ( rVal.nVal == 1 ) + return *this; + + if ( rVal.nVal == -1 ) + { + bIsNeg = !bIsNeg; + return *this; + } + + if ( rVal.nVal <= (long)0xFFFF && rVal.nVal >= -(long)0xFFFF ) + { + // ein BigInt durch ein sal_uInt16 teilen + sal_uInt16 nTmp; + if ( rVal.nVal < 0 ) + { + nTmp = (sal_uInt16) -rVal.nVal; + bIsNeg = !bIsNeg; + } + else + nTmp = (sal_uInt16) rVal.nVal; + + Div( nTmp, nTmp ); + Normalize(); + return *this; + } + } + + if ( ABS_IsLess( rVal ) ) + { + *this = BigInt( (long)0 ); + return *this; + } + + // BigInt durch BigInt teilen + BigInt aTmp1, aTmp2; + aTmp1.MakeBigInt( *this ); + aTmp2.MakeBigInt( rVal ); + aTmp1.DivLong(aTmp2, *this); + Normalize(); + return *this; +} + +// ----------------------------------------------------------------------- + +void BigInt::DivMod( const BigInt& rVal, BigInt& rMod ) +{ + if ( !rVal.bIsBig ) + { + if ( rVal.nVal == 0 ) + { + DBG_ERROR( "BigInt::operator/ --> divide by zero" ); + return; + } + + if ( !bIsBig ) + { + // wir bewegen uns im ungefaehrlichem Bereich + rMod = BigInt( nVal % rVal.nVal ); + nVal /= rVal.nVal; + return; + } + + if ( rVal.nVal == 1 ) + { + rMod = BigInt( (long)0 ); + return; + } + + if ( rVal.nVal == -1 ) + { + rMod = BigInt( (long)0 ); + bIsNeg = !bIsNeg; + return; + } + + if ( rVal.nVal <= (long)0xFFFF && rVal.nVal >= -(long)0xFFFF ) + { + // ein BigInt durch ein sal_uInt16 teilen + sal_uInt16 nTmp; + if ( rVal.nVal < 0 ) + { + nTmp = (sal_uInt16) -rVal.nVal; + bIsNeg = !bIsNeg; + } + else + nTmp = (sal_uInt16) rVal.nVal; + + Div( nTmp, nTmp ); + rMod = BigInt( (long)nTmp ); + Normalize(); + return; + } + } + + if ( ABS_IsLess( rVal ) ) + { + rMod = *this; + *this = BigInt( (long)0 ); + return; + } + + // BigInt durch BigInt teilen + BigInt aTmp1, aTmp2; + aTmp1.MakeBigInt( *this ); + aTmp2.MakeBigInt( rVal ); + aTmp1.DivLong(aTmp2, *this); + Normalize(); + aTmp1.ModLong(aTmp2, rMod); // nicht optimal + rMod.Normalize(); +} + +// ----------------------------------------------------------------------- + +BigInt& BigInt::operator%=( const BigInt& rVal ) +{ + if ( !rVal.bIsBig ) + { + if ( rVal.nVal == 0 ) + { + DBG_ERROR( "BigInt::operator/ --> divide by zero" ); + return *this; + } + + if ( !bIsBig ) + { + // wir bewegen uns im ungefaehrlichem Bereich + nVal %= rVal.nVal; + return *this; + } + + if ( rVal.nVal <= (long)0xFFFF && rVal.nVal >= -(long)0xFFFF ) + { + // ein BigInt durch ein short teilen + sal_uInt16 nTmp; + if ( rVal.nVal < 0 ) + { + nTmp = (sal_uInt16) -rVal.nVal; + bIsNeg = !bIsNeg; + } + else + nTmp = (sal_uInt16) rVal.nVal; + + Div( nTmp, nTmp ); + *this = BigInt( (long)nTmp ); + return *this; + } + } + + if ( ABS_IsLess( rVal ) ) + return *this; + + // BigInt durch BigInt teilen + BigInt aTmp1, aTmp2; + aTmp1.MakeBigInt( *this ); + aTmp2.MakeBigInt( rVal ); + aTmp1.ModLong(aTmp2, *this); + Normalize(); + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool operator==( const BigInt& rVal1, const BigInt& rVal2 ) +{ + if ( rVal1.bIsBig || rVal2.bIsBig ) + { + BigInt nA, nB; + nA.MakeBigInt( rVal1 ); + nB.MakeBigInt( rVal2 ); + if ( nA.bIsNeg == nB.bIsNeg ) + { + if ( nA.nLen == nB.nLen ) + { + int i; + for ( i = nA.nLen - 1; i > 0 && nA.nNum[i] == nB.nNum[i]; i-- ) + { + } + + return nA.nNum[i] == nB.nNum[i]; + } + return sal_False; + } + return sal_False; + } + return rVal1.nVal == rVal2.nVal; +} + +// ----------------------------------------------------------------------- + +sal_Bool operator<( const BigInt& rVal1, const BigInt& rVal2 ) +{ + if ( rVal1.bIsBig || rVal2.bIsBig ) + { + BigInt nA, nB; + nA.MakeBigInt( rVal1 ); + nB.MakeBigInt( rVal2 ); + if ( nA.bIsNeg == nB.bIsNeg ) + { + if ( nA.nLen == nB.nLen ) + { + int i; + for ( i = nA.nLen - 1; i > 0 && nA.nNum[i] == nB.nNum[i]; i-- ) + { + } + + if ( nA.bIsNeg ) + return nA.nNum[i] > nB.nNum[i]; + else + return nA.nNum[i] < nB.nNum[i]; + } + if ( nA.bIsNeg ) + return nA.nLen > nB.nLen; + else + return nA.nLen < nB.nLen; + } + return !nB.bIsNeg; + } + return rVal1.nVal < rVal2.nVal; +} + +// ----------------------------------------------------------------------- + +sal_Bool operator >(const BigInt& rVal1, const BigInt& rVal2 ) +{ + if ( rVal1.bIsBig || rVal2.bIsBig ) + { + BigInt nA, nB; + nA.MakeBigInt( rVal1 ); + nB.MakeBigInt( rVal2 ); + if ( nA.bIsNeg == nB.bIsNeg ) + { + if ( nA.nLen == nB.nLen ) + { + int i; + for ( i = nA.nLen - 1; i > 0 && nA.nNum[i] == nB.nNum[i]; i-- ) + { + } + + if ( nA.bIsNeg ) + return nA.nNum[i] < nB.nNum[i]; + else + return nA.nNum[i] > nB.nNum[i]; + } + if ( nA.bIsNeg ) + return nA.nLen < nB.nLen; + else + return nA.nLen > nB.nLen; + } + return !nA.bIsNeg; + } + + return rVal1.nVal > rVal2.nVal; +} diff --git a/tools/source/generic/color.cxx b/tools/source/generic/color.cxx new file mode 100644 index 000000000000..37e9dedf9259 --- /dev/null +++ b/tools/source/generic/color.cxx @@ -0,0 +1,510 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_tools.hxx" + +#include <stdlib.h> +#include <vos/macros.hxx> +#include <tools/color.hxx> +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <tools/rc.hxx> +#include <tools/rcid.h> +#include <tools/resid.hxx> +#ifndef _SV_RC_H +#include <tools/rc.h> +#endif + +// ----------- +// - Inlines - +// ----------- + +static inline long _FRound( double fVal ) +{ + return( fVal > 0.0 ? (long) ( fVal + 0.5 ) : -(long) ( -fVal + 0.5 ) ); +} + +// --------- +// - Color - +// --------- + +Color::Color( const ResId& rResId ) +{ + rResId.SetRT( RSC_COLOR ); + ResMgr* pResMgr = rResId.GetResMgr(); + if ( pResMgr && pResMgr->GetResource( rResId ) ) + { + // Header ueberspringen + pResMgr->Increment( sizeof( RSHEADER_TYPE ) ); + + // Daten laden + USHORT nRed = pResMgr->ReadShort(); + USHORT nGreen = pResMgr->ReadShort(); + USHORT nBlue = pResMgr->ReadShort(); + // one more historical ULONG + pResMgr->ReadLong(); + + // RGB-Farbe + mnColor = RGB_COLORDATA( nRed>>8, nGreen>>8, nBlue>>8 ); + } + else + { + mnColor = RGB_COLORDATA( 0, 0, 0 ); + } +} +UINT8 Color::GetColorError( const Color& rCompareColor ) const +{ + const long nErrAbs = labs( (long) rCompareColor.GetRed() - GetRed() ) + + labs( (long) rCompareColor.GetGreen() - GetGreen() ) + + labs( (long) rCompareColor.GetBlue() - GetBlue() ); + + return (UINT8) _FRound( nErrAbs * 0.3333333333 ); +} + +// ----------------------------------------------------------------------- + +void Color::IncreaseLuminance( UINT8 cLumInc ) +{ + SetRed( (UINT8) VOS_BOUND( (long) COLORDATA_RED( mnColor ) + cLumInc, 0L, 255L ) ); + SetGreen( (UINT8) VOS_BOUND( (long) COLORDATA_GREEN( mnColor ) + cLumInc, 0L, 255L ) ); + SetBlue( (UINT8) VOS_BOUND( (long) COLORDATA_BLUE( mnColor ) + cLumInc, 0L, 255L ) ); +} + +// ----------------------------------------------------------------------- + +void Color::DecreaseLuminance( UINT8 cLumDec ) +{ + SetRed( (UINT8) VOS_BOUND( (long) COLORDATA_RED( mnColor ) - cLumDec, 0L, 255L ) ); + SetGreen( (UINT8) VOS_BOUND( (long) COLORDATA_GREEN( mnColor ) - cLumDec, 0L, 255L ) ); + SetBlue( (UINT8) VOS_BOUND( (long) COLORDATA_BLUE( mnColor ) - cLumDec, 0L, 255L ) ); +} + +// ----------------------------------------------------------------------- + +void Color::IncreaseContrast( UINT8 cContInc ) +{ + if( cContInc) + { + const double fM = 128.0 / ( 128.0 - 0.4985 * cContInc ); + const double fOff = 128.0 - fM * 128.0; + + SetRed( (UINT8) VOS_BOUND( _FRound( COLORDATA_RED( mnColor ) * fM + fOff ), 0L, 255L ) ); + SetGreen( (UINT8) VOS_BOUND( _FRound( COLORDATA_GREEN( mnColor ) * fM + fOff ), 0L, 255L ) ); + SetBlue( (UINT8) VOS_BOUND( _FRound( COLORDATA_BLUE( mnColor ) * fM + fOff ), 0L, 255L ) ); + } +} + +// ----------------------------------------------------------------------- + +void Color::DecreaseContrast( UINT8 cContDec ) +{ + if( cContDec ) + { + const double fM = ( 128.0 - 0.4985 * cContDec ) / 128.0; + const double fOff = 128.0 - fM * 128.0; + + SetRed( (UINT8) VOS_BOUND( _FRound( COLORDATA_RED( mnColor ) * fM + fOff ), 0L, 255L ) ); + SetGreen( (UINT8) VOS_BOUND( _FRound( COLORDATA_GREEN( mnColor ) * fM + fOff ), 0L, 255L ) ); + SetBlue( (UINT8) VOS_BOUND( _FRound( COLORDATA_BLUE( mnColor ) * fM + fOff ), 0L, 255L ) ); + } +} + +// ----------------------------------------------------------------------- + +void Color::Invert() +{ + SetRed( ~COLORDATA_RED( mnColor ) ); + SetGreen( ~COLORDATA_GREEN( mnColor ) ); + SetBlue( ~COLORDATA_BLUE( mnColor ) ); +} + +// ----------------------------------------------------------------------- + +BOOL Color::IsDark() const +{ + return GetLuminance() <= 38; +} + +// ----------------------------------------------------------------------- + +BOOL Color::IsBright() const +{ + return GetLuminance() >= 245; +} + +// ----------------------------------------------------------------------- +// color space conversion +// ----------------------------------------------------------------------- + +void Color::RGBtoHSB( USHORT& nHue, USHORT& nSat, USHORT& nBri ) const +{ + UINT8 c[3]; + UINT8 cMax, cMin; + + c[0] = GetRed(); + c[1] = GetGreen(); + c[2] = GetBlue(); + + cMax = c[0]; + if( c[1] > cMax ) + cMax = c[1]; + if( c[2] > cMax ) + cMax = c[2]; + + // Brightness = max(R, G, B); + nBri = cMax * 100 / 255; + + cMin = c[0]; + if( c[1] < cMin ) + cMin = c[1]; + if( c[2] < cMin ) + cMin = c[2]; + + UINT8 cDelta = cMax - cMin; + + // Saturation = max - min / max + if( nBri > 0 ) + nSat = cDelta * 100 / cMax; + else + nSat = 0; + + if( nSat == 0 ) + nHue = 0; // Default = undefined + else + { + double dHue = 0.0; + + if( c[0] == cMax ) + { + dHue = (double)( c[1] - c[2] ) / (double)cDelta; + } + else if( c[1] == cMax ) + { + dHue = 2.0 + (double)( c[2] - c[0] ) / (double)cDelta; + } + else if ( c[2] == cMax ) + { + dHue = 4.0 + (double)( c[0] - c[1] ) / (double)cDelta; + } + dHue *= 60.0; + + if( dHue < 0.0 ) + dHue += 360.0; + + nHue = (UINT16) dHue; + } +} + +ColorData Color::HSBtoRGB( USHORT nHue, USHORT nSat, USHORT nBri ) +{ + UINT8 cR=0,cG=0,cB=0; + UINT8 nB = (UINT8) ( nBri * 255 / 100 ); + + if( nSat == 0 ) + { + cR = nB; + cG = nB; + cB = nB; + } + else + { + double dH = nHue; + double f; + UINT16 n; + if( dH == 360.0 ) + dH = 0.0; + + dH /= 60.0; + n = (UINT16) dH; + f = dH - n; + + UINT8 a = (UINT8) ( nB * ( 100 - nSat ) / 100 ); + UINT8 b = (UINT8) ( nB * ( 100 - ( (double)nSat * f + 0.5 ) ) / 100 ); + UINT8 c = (UINT8) ( nB * ( 100 - ( (double)nSat * ( 1.0 - f ) + 0.5 ) ) / 100 ); + + switch( n ) + { + case 0: cR = nB; cG = c; cB = a; break; + case 1: cR = b; cG = nB; cB = a; break; + case 2: cR = a; cG = nB; cB = c; break; + case 3: cR = a; cG = b; cB = nB; break; + case 4: cR = c; cG = a; cB = nB; break; + case 5: cR = nB; cG = a; cB = b; break; + } + } + + return RGB_COLORDATA( cR, cG, cB ); +} + +// ----------------------------------------------------------------------- + +SvStream& Color::Read( SvStream& rIStm, BOOL bNewFormat ) +{ + if ( bNewFormat ) + rIStm >> mnColor; + else + rIStm >> *this; + + return rIStm; +} + +// ----------------------------------------------------------------------- + +SvStream& Color::Write( SvStream& rOStm, BOOL bNewFormat ) +{ + if ( bNewFormat ) + rOStm << mnColor; + else + rOStm << *this; + + return rOStm; +} + +// ----------------------------------------------------------------------- + +#define COL_NAME_USER ((USHORT)0x8000) +#define COL_RED_1B ((USHORT)0x0001) +#define COL_RED_2B ((USHORT)0x0002) +#define COL_GREEN_1B ((USHORT)0x0010) +#define COL_GREEN_2B ((USHORT)0x0020) +#define COL_BLUE_1B ((USHORT)0x0100) +#define COL_BLUE_2B ((USHORT)0x0200) + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStream, Color& rColor ) +{ + DBG_ASSERTWARNING( rIStream.GetVersion(), "Color::>> - Solar-Version not set on rIStream" ); + + USHORT nColorName; + USHORT nRed; + USHORT nGreen; + USHORT nBlue; + + rIStream >> nColorName; + + if ( nColorName & COL_NAME_USER ) + { + if ( rIStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + unsigned char cAry[6]; + USHORT i = 0; + + nRed = 0; + nGreen = 0; + nBlue = 0; + + if ( nColorName & COL_RED_2B ) + i += 2; + else if ( nColorName & COL_RED_1B ) + i++; + if ( nColorName & COL_GREEN_2B ) + i += 2; + else if ( nColorName & COL_GREEN_1B ) + i++; + if ( nColorName & COL_BLUE_2B ) + i += 2; + else if ( nColorName & COL_BLUE_1B ) + i++; + + rIStream.Read( cAry, i ); + i = 0; + + if ( nColorName & COL_RED_2B ) + { + nRed = cAry[i]; + nRed <<= 8; + i++; + nRed |= cAry[i]; + i++; + } + else if ( nColorName & COL_RED_1B ) + { + nRed = cAry[i]; + nRed <<= 8; + i++; + } + if ( nColorName & COL_GREEN_2B ) + { + nGreen = cAry[i]; + nGreen <<= 8; + i++; + nGreen |= cAry[i]; + i++; + } + else if ( nColorName & COL_GREEN_1B ) + { + nGreen = cAry[i]; + nGreen <<= 8; + i++; + } + if ( nColorName & COL_BLUE_2B ) + { + nBlue = cAry[i]; + nBlue <<= 8; + i++; + nBlue |= cAry[i]; + i++; + } + else if ( nColorName & COL_BLUE_1B ) + { + nBlue = cAry[i]; + nBlue <<= 8; + i++; + } + } + else + { + rIStream >> nRed; + rIStream >> nGreen; + rIStream >> nBlue; + } + + rColor.mnColor = RGB_COLORDATA( nRed>>8, nGreen>>8, nBlue>>8 ); + } + else + { + static ColorData aColAry[] = + { + COL_BLACK, // COL_BLACK + COL_BLUE, // COL_BLUE + COL_GREEN, // COL_GREEN + COL_CYAN, // COL_CYAN + COL_RED, // COL_RED + COL_MAGENTA, // COL_MAGENTA + COL_BROWN, // COL_BROWN + COL_GRAY, // COL_GRAY + COL_LIGHTGRAY, // COL_LIGHTGRAY + COL_LIGHTBLUE, // COL_LIGHTBLUE + COL_LIGHTGREEN, // COL_LIGHTGREEN + COL_LIGHTCYAN, // COL_LIGHTCYAN + COL_LIGHTRED, // COL_LIGHTRED + COL_LIGHTMAGENTA, // COL_LIGHTMAGENTA + COL_YELLOW, // COL_YELLOW + COL_WHITE, // COL_WHITE + COL_WHITE, // COL_MENUBAR + COL_BLACK, // COL_MENUBARTEXT + COL_WHITE, // COL_POPUPMENU + COL_BLACK, // COL_POPUPMENUTEXT + COL_BLACK, // COL_WINDOWTEXT + COL_WHITE, // COL_WINDOWWORKSPACE + COL_BLACK, // COL_HIGHLIGHT + COL_WHITE, // COL_HIGHLIGHTTEXT + COL_BLACK, // COL_3DTEXT + COL_LIGHTGRAY, // COL_3DFACE + COL_WHITE, // COL_3DLIGHT + COL_GRAY, // COL_3DSHADOW + COL_LIGHTGRAY, // COL_SCROLLBAR + COL_WHITE, // COL_FIELD + COL_BLACK // COL_FIELDTEXT + }; + + if ( nColorName < (sizeof( aColAry )/sizeof(ColorData)) ) + rColor.mnColor = aColAry[nColorName]; + else + rColor.mnColor = COL_BLACK; + } + + return rIStream; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStream, const Color& rColor ) +{ + DBG_ASSERTWARNING( rOStream.GetVersion(), "Color::<< - Solar-Version not set on rOStream" ); + + USHORT nColorName = COL_NAME_USER; + USHORT nRed = rColor.GetRed(); + USHORT nGreen = rColor.GetGreen(); + USHORT nBlue = rColor.GetBlue(); + nRed = (nRed<<8) + nRed; + nGreen = (nGreen<<8) + nGreen; + nBlue = (nBlue<<8) + nBlue; + + if ( rOStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + unsigned char cAry[6]; + USHORT i = 0; + + if ( nRed & 0x00FF ) + { + nColorName |= COL_RED_2B; + cAry[i] = (unsigned char)(nRed & 0xFF); + i++; + cAry[i] = (unsigned char)((nRed >> 8) & 0xFF); + i++; + } + else if ( nRed & 0xFF00 ) + { + nColorName |= COL_RED_1B; + cAry[i] = (unsigned char)((nRed >> 8) & 0xFF); + i++; + } + if ( nGreen & 0x00FF ) + { + nColorName |= COL_GREEN_2B; + cAry[i] = (unsigned char)(nGreen & 0xFF); + i++; + cAry[i] = (unsigned char)((nGreen >> 8) & 0xFF); + i++; + } + else if ( nGreen & 0xFF00 ) + { + nColorName |= COL_GREEN_1B; + cAry[i] = (unsigned char)((nGreen >> 8) & 0xFF); + i++; + } + if ( nBlue & 0x00FF ) + { + nColorName |= COL_BLUE_2B; + cAry[i] = (unsigned char)(nBlue & 0xFF); + i++; + cAry[i] = (unsigned char)((nBlue >> 8) & 0xFF); + i++; + } + else if ( nBlue & 0xFF00 ) + { + nColorName |= COL_BLUE_1B; + cAry[i] = (unsigned char)((nBlue >> 8) & 0xFF); + i++; + } + + rOStream << nColorName; + rOStream.Write( cAry, i ); + } + else + { + rOStream << nColorName; + rOStream << nRed; + rOStream << nGreen; + rOStream << nBlue; + } + + return rOStream; +} diff --git a/tools/source/generic/config.cxx b/tools/source/generic/config.cxx new file mode 100644 index 000000000000..1a94c2b11198 --- /dev/null +++ b/tools/source/generic/config.cxx @@ -0,0 +1,1304 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_tools.hxx" + +#define _CONFIG_CXX + +#include <cstddef> +#include <cstdlib> +#include <limits> +#include <new> +#include <string.h> + +#ifdef WNT +#include "stdlib.h" +#endif +#include <osl/file.hxx> +#include <tools/stream.hxx> +#include <tools/debug.hxx> +#include <tools/config.hxx> +#include <osl/security.h> + +#define MAXBUFLEN 1024 // Fuer Buffer bei VOS-Funktionen + +// ----------------- +// - ImplConfigData - +// ----------------- + +struct ImplKeyData +{ + ImplKeyData* mpNext; + ByteString maKey; + ByteString maValue; + BOOL mbIsComment; +}; + +struct ImplGroupData +{ + ImplGroupData* mpNext; + ImplKeyData* mpFirstKey; + ByteString maGroupName; + USHORT mnEmptyLines; +}; + +struct ImplConfigData +{ + ImplGroupData* mpFirstGroup; + XubString maFileName; + ULONG mnDataUpdateId; + ULONG mnTimeStamp; + LineEnd meLineEnd; + USHORT mnRefCount; + BOOL mbModified; + BOOL mbRead; + BOOL mbIsUTF8BOM; +}; + +// ======================================================================= + +static ByteString& getEmptyByteString() +{ + static ByteString aEmpty; + return aEmpty; +} + +// ======================================================================= + +static String toUncPath( const String& rPath ) +{ + ::rtl::OUString aFileURL; + + // check if rFileName is already a URL; if not make it so + if( rPath.CompareToAscii( "file://", 7 ) == COMPARE_EQUAL ) + aFileURL = rPath; + else if( ::osl::FileBase::getFileURLFromSystemPath( rPath, aFileURL ) != ::osl::FileBase::E_None ) + aFileURL = rPath; + + return aFileURL; +} + +static ULONG ImplSysGetConfigTimeStamp( const XubString& rFileName ) +{ + ULONG nTimeStamp = 0; + ::osl::DirectoryItem aItem; + ::osl::FileStatus aStatus( osl_FileStatus_Mask_ModifyTime ); + + int nError = 0; + if( ( nError = ::osl::DirectoryItem::get( rFileName, aItem ) ) == ::osl::FileBase::E_None && + aItem.getFileStatus( aStatus ) == ::osl::FileBase::E_None ) + { + nTimeStamp = aStatus.getModifyTime().Seconds; + } + + return nTimeStamp; +} + +// ----------------------------------------------------------------------- + +static BYTE* ImplSysReadConfig( const XubString& rFileName, + sal_uInt64& rRead, BOOL& rbRead, BOOL& rbIsUTF8BOM, ULONG& rTimeStamp ) +{ + BYTE* pBuf = NULL; + ::osl::File aFile( rFileName ); + + if( aFile.open( osl_File_OpenFlag_Read ) == ::osl::FileBase::E_None ) + { + sal_uInt64 nPos = 0, nRead = 0; + if( aFile.getSize( nPos ) == ::osl::FileBase::E_None ) + { + if (nPos > std::numeric_limits< std::size_t >::max()) { + aFile.close(); + return 0; + } + pBuf = new BYTE[static_cast< std::size_t >(nPos)]; + if( aFile.read( pBuf, nPos, nRead ) == ::osl::FileBase::E_None && nRead == nPos ) + { + //skip the byte-order-mark 0xEF 0xBB 0xBF, if it was UTF8 files + unsigned char BOM[3] = {0xEF, 0xBB, 0xBF}; + if (nRead > 2 && memcmp(pBuf, BOM, 3) == 0) + { + nRead -= 3; + rtl_moveMemory(pBuf, pBuf + 3, sal::static_int_cast<sal_Size>(nRead * sizeof(BYTE)) ); + rbIsUTF8BOM = TRUE; + } + + rTimeStamp = ImplSysGetConfigTimeStamp( rFileName ); + rbRead = TRUE; + rRead = nRead; + } + else + { + delete[] pBuf; + pBuf = NULL; + } + } + aFile.close(); + } + + return pBuf; +} + +// ----------------------------------------------------------------------- + +static BOOL ImplSysWriteConfig( const XubString& rFileName, + const BYTE* pBuf, ULONG nBufLen, BOOL rbIsUTF8BOM, ULONG& rTimeStamp ) +{ + BOOL bSuccess = FALSE; + BOOL bUTF8BOMSuccess = FALSE; + + ::osl::File aFile( rFileName ); + ::osl::FileBase::RC eError = aFile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ); + if( eError != ::osl::FileBase::E_None ) + eError = aFile.open( osl_File_OpenFlag_Write ); + if( eError == ::osl::FileBase::E_None ) + { + // truncate + aFile.setSize( 0 ); + sal_uInt64 nWritten; + + //write the the byte-order-mark 0xEF 0xBB 0xBF first , if it was UTF8 files + if ( rbIsUTF8BOM ) + { + unsigned char BOM[3] = {0xEF, 0xBB, 0xBF}; + sal_uInt64 nUTF8BOMWritten; + if( aFile.write( BOM, 3, nUTF8BOMWritten ) == ::osl::FileBase::E_None && 3 == nUTF8BOMWritten ) + { + bUTF8BOMSuccess = TRUE; + } + } + + if( aFile.write( pBuf, nBufLen, nWritten ) == ::osl::FileBase::E_None && nWritten == nBufLen ) + { + bSuccess = TRUE; + } + if ( rbIsUTF8BOM ? bSuccess && bUTF8BOMSuccess : bSuccess ) + { + rTimeStamp = ImplSysGetConfigTimeStamp( rFileName ); + } + } + + return rbIsUTF8BOM ? bSuccess && bUTF8BOMSuccess : bSuccess; +} + +// ----------------------------------------------------------------------- + +static String ImplMakeConfigName( const XubString* pFileName, + const XubString* pPathName ) +{ + ::rtl::OUString aFileName; + ::rtl::OUString aPathName; + if ( pFileName ) + { +#ifdef UNX + aFileName = ::rtl::OUString::createFromAscii( "." ); + aFileName += *pFileName; + aFileName += ::rtl::OUString::createFromAscii( "rc" ); +#else + aFileName = *pFileName; + aFileName += ::rtl::OUString::createFromAscii( ".ini" ); +#endif + } + else + { +#ifdef UNX + aFileName = ::rtl::OUString::createFromAscii( ".sversionrc" ); +#else + aFileName = ::rtl::OUString::createFromAscii( "sversion.ini" ); +#endif + } + + // #88208# in case pPathName is set but empty and pFileName is set + // and not empty just return the filename; on the default case + // prepend default path as usual + if ( pPathName && pPathName->Len() ) + aPathName = toUncPath( *pPathName ); + else if( pPathName && pFileName && pFileName->Len() ) + return aFileName; + else + { + oslSecurity aSec = osl_getCurrentSecurity(); + osl_getConfigDir( aSec, &aPathName.pData ); + osl_freeSecurityHandle( aSec ); + } + + ::rtl::OUString aName( aPathName ); + aName += ::rtl::OUString::createFromAscii( "/" ); + aName += aFileName; + + return aName; +} + +// ----------------------------------------------------------------------- + +namespace { + +ByteString makeByteString(BYTE const * p, sal_uInt64 n) { + if (n > STRING_MAXLEN) { + #ifdef WNT + abort(); + #else + ::std::abort(); //TODO: handle this gracefully + #endif + } + return ByteString( + reinterpret_cast< char const * >(p), + sal::static_int_cast< xub_StrLen >(n)); +} + +} + +static void ImplMakeConfigList( ImplConfigData* pData, + const BYTE* pBuf, sal_uInt64 nLen ) +{ + // kein Buffer, keine Daten + if ( !nLen ) + return; + + // Buffer parsen und Liste zusammenbauen + sal_uInt64 nStart; + sal_uInt64 nLineLen; + xub_StrLen nNameLen; + xub_StrLen nKeyLen; + sal_uInt64 i; + const BYTE* pLine; + ImplKeyData* pPrevKey = NULL; + ImplKeyData* pKey; + ImplGroupData* pPrevGroup = NULL; + ImplGroupData* pGroup = NULL; + i = 0; + while ( i < nLen ) + { + // Ctrl+Z + if ( pBuf[i] == 0x1A ) + break; + + // Spaces und Tabs entfernen + while ( (pBuf[i] == ' ') || (pBuf[i] == '\t') ) + i++; + + // Zeilenanfang merken + nStart = i; + pLine = pBuf+i; + + // Zeilenende suchen + while ( (i < nLen) && pBuf[i] && (pBuf[i] != '\r') && (pBuf[i] != '\n') && + (pBuf[i] != 0x1A) ) + i++; + + nLineLen = i-nStart; + + // Wenn Zeilenende (CR/LF), dann noch einen weiterschalten + if ( (i+1 < nLen) && + (pBuf[i] != pBuf[i+1]) && + ((pBuf[i+1] == '\r') || (pBuf[i+1] == '\n')) ) + i++; + i++; + + // Zeile auswerten + if ( *pLine == '[' ) + { + pGroup = new ImplGroupData; + pGroup->mpNext = NULL; + pGroup->mpFirstKey = NULL; + pGroup->mnEmptyLines = 0; + if ( pPrevGroup ) + pPrevGroup->mpNext = pGroup; + else + pData->mpFirstGroup = pGroup; + pPrevGroup = pGroup; + pPrevKey = NULL; + pKey = NULL; + + // Gruppennamen rausfiltern + pLine++; + nLineLen--; + // Spaces und Tabs entfernen + while ( (*pLine == ' ') || (*pLine == '\t') ) + { + nLineLen--; + pLine++; + } + nNameLen = 0; + while ( (nNameLen < nLineLen) && (pLine[nNameLen] != ']') ) + nNameLen++; + if ( nNameLen ) + { + while ( (pLine[nNameLen-1] == ' ') || (pLine[nNameLen-1] == '\t') ) + nNameLen--; + } + pGroup->maGroupName = ByteString( (const sal_Char*)pLine, nNameLen ); + } + else + { + if ( nLineLen ) + { + // Wenn noch keine Gruppe existiert, dann alle Keys in die + // Default-Gruppe + if ( !pGroup ) + { + pGroup = new ImplGroupData; + pGroup->mpNext = NULL; + pGroup->mpFirstKey = NULL; + pGroup->mnEmptyLines = 0; + if ( pPrevGroup ) + pPrevGroup->mpNext = pGroup; + else + pData->mpFirstGroup = pGroup; + pPrevGroup = pGroup; + pPrevKey = NULL; + } + + // Falls Leerzeile vorhanden, dann anhaengen + if ( pPrevKey ) + { + while ( pGroup->mnEmptyLines ) + { + pKey = new ImplKeyData; + pKey->mbIsComment = TRUE; + pPrevKey->mpNext = pKey; + pPrevKey = pKey; + pGroup->mnEmptyLines--; + } + } + + // Neuen Key erzeugen + pKey = new ImplKeyData; + pKey->mpNext = NULL; + if ( pPrevKey ) + pPrevKey->mpNext = pKey; + else + pGroup->mpFirstKey = pKey; + pPrevKey = pKey; + if ( pLine[0] == ';' ) + { + pKey->maValue = makeByteString(pLine, nLineLen); + pKey->mbIsComment = TRUE; + } + else + { + pKey->mbIsComment = FALSE; + nNameLen = 0; + while ( (nNameLen < nLineLen) && (pLine[nNameLen] != '=') ) + nNameLen++; + nKeyLen = nNameLen; + // Spaces und Tabs entfernen + if ( nNameLen ) + { + while ( (pLine[nNameLen-1] == ' ') || (pLine[nNameLen-1] == '\t') ) + nNameLen--; + } + pKey->maKey = ByteString( (const sal_Char*)pLine, nNameLen ); + nKeyLen++; + if ( nKeyLen < nLineLen ) + { + pLine += nKeyLen; + nLineLen -= nKeyLen; + // Spaces und Tabs entfernen + while ( (*pLine == ' ') || (*pLine == '\t') ) + { + nLineLen--; + pLine++; + } + if ( nLineLen ) + { + while ( (pLine[nLineLen-1] == ' ') || (pLine[nLineLen-1] == '\t') ) + nLineLen--; + pKey->maValue = makeByteString(pLine, nLineLen); + } + } + } + } + else + { + // Leerzeilen werden nur gezaehlt und beim Erzeugen des + // naechsten Keys angehaengt, da wir Leerzeilen am Ende + // einer Gruppe auch nach hinzufuegen von neuen Keys nur + // am Ende der Gruppe wieder speichern wollen + if ( pGroup ) + pGroup->mnEmptyLines++; + } + } + } +} + +// ----------------------------------------------------------------------- + +static BYTE* ImplGetConfigBuffer( const ImplConfigData* pData, ULONG& rLen ) +{ + BYTE* pWriteBuf; + BYTE* pBuf; + BYTE aLineEndBuf[2] = {0, 0}; + ImplKeyData* pKey; + ImplGroupData* pGroup; + unsigned int nBufLen; + USHORT nValueLen; + USHORT nKeyLen; + USHORT nLineEndLen; + + if ( pData->meLineEnd == LINEEND_CR ) + { + aLineEndBuf[0] = _CR; + nLineEndLen = 1; + } + else if ( pData->meLineEnd == LINEEND_LF ) + { + aLineEndBuf[0] = _LF; + nLineEndLen = 1; + } + else + { + aLineEndBuf[0] = _CR; + aLineEndBuf[1] = _LF; + nLineEndLen = 2; + } + + // Buffergroesse ermitteln + nBufLen = 0; + pGroup = pData->mpFirstGroup; + while ( pGroup ) + { + // Leere Gruppen werden nicht geschrieben + if ( pGroup->mpFirstKey ) + { + nBufLen += pGroup->maGroupName.Len() + nLineEndLen + 2; + pKey = pGroup->mpFirstKey; + while ( pKey ) + { + nValueLen = pKey->maValue.Len(); + if ( pKey->mbIsComment ) + nBufLen += nValueLen + nLineEndLen; + else + nBufLen += pKey->maKey.Len() + nValueLen + nLineEndLen + 1; + + pKey = pKey->mpNext; + } + + // Leerzeile nach jeder Gruppe auch wieder speichern + if ( !pGroup->mnEmptyLines ) + pGroup->mnEmptyLines = 1; + nBufLen += nLineEndLen * pGroup->mnEmptyLines; + } + + pGroup = pGroup->mpNext; + } + + // Laenge dem Aufrufer mitteilen + rLen = nBufLen; + if ( !nBufLen ) + { + pWriteBuf = new BYTE[nLineEndLen]; + if ( pWriteBuf ) + { + pWriteBuf[0] = aLineEndBuf[0]; + if ( nLineEndLen == 2 ) + pWriteBuf[1] = aLineEndBuf[1]; + return pWriteBuf; + } + else + return 0; + } + + // Schreibbuffer anlegen (wird vom Aufrufer zerstoert) + pWriteBuf = new BYTE[nBufLen]; + if ( !pWriteBuf ) + return 0; + + // Buffer fuellen + pBuf = pWriteBuf; + pGroup = pData->mpFirstGroup; + while ( pGroup ) + { + // Leere Gruppen werden nicht geschrieben + if ( pGroup->mpFirstKey ) + { + *pBuf = '['; pBuf++; + memcpy( pBuf, pGroup->maGroupName.GetBuffer(), pGroup->maGroupName.Len() ); + pBuf += pGroup->maGroupName.Len(); + *pBuf = ']'; pBuf++; + *pBuf = aLineEndBuf[0]; pBuf++; + if ( nLineEndLen == 2 ) + { + *pBuf = aLineEndBuf[1]; pBuf++; + } + pKey = pGroup->mpFirstKey; + while ( pKey ) + { + nValueLen = pKey->maValue.Len(); + if ( pKey->mbIsComment ) + { + if ( nValueLen ) + { + memcpy( pBuf, pKey->maValue.GetBuffer(), nValueLen ); + pBuf += nValueLen; + } + *pBuf = aLineEndBuf[0]; pBuf++; + if ( nLineEndLen == 2 ) + { + *pBuf = aLineEndBuf[1]; pBuf++; + } + } + else + { + nKeyLen = pKey->maKey.Len(); + memcpy( pBuf, pKey->maKey.GetBuffer(), nKeyLen ); + pBuf += nKeyLen; + *pBuf = '='; pBuf++; + memcpy( pBuf, pKey->maValue.GetBuffer(), nValueLen ); + pBuf += nValueLen; + *pBuf = aLineEndBuf[0]; pBuf++; + if ( nLineEndLen == 2 ) + { + *pBuf = aLineEndBuf[1]; pBuf++; + } + } + + pKey = pKey->mpNext; + } + + // Leerzeile nach jeder Gruppe auch wieder speichern + USHORT nEmptyLines = pGroup->mnEmptyLines; + while ( nEmptyLines ) + { + *pBuf = aLineEndBuf[0]; pBuf++; + if ( nLineEndLen == 2 ) + { + *pBuf = aLineEndBuf[1]; pBuf++; + } + nEmptyLines--; + } + } + + pGroup = pGroup->mpNext; + } + + return pWriteBuf; +} + +// ----------------------------------------------------------------------- + +static void ImplReadConfig( ImplConfigData* pData ) +{ + ULONG nTimeStamp = 0; + sal_uInt64 nRead = 0; + BOOL bRead = FALSE; + BOOL bIsUTF8BOM =FALSE; + BYTE* pBuf = ImplSysReadConfig( pData->maFileName, nRead, bRead, bIsUTF8BOM, nTimeStamp ); + + // Aus dem Buffer die Config-Verwaltungsliste aufbauen + if ( pBuf ) + { + ImplMakeConfigList( pData, pBuf, nRead ); + delete[] pBuf; + } + pData->mnTimeStamp = nTimeStamp; + pData->mbModified = FALSE; + if ( bRead ) + pData->mbRead = TRUE; + if ( bIsUTF8BOM ) + pData->mbIsUTF8BOM = TRUE; +} + +// ----------------------------------------------------------------------- + +static void ImplWriteConfig( ImplConfigData* pData ) +{ +#ifdef DBG_UTIL + if ( DbgIsAssert() ) + { + if ( pData->mnTimeStamp != ImplSysGetConfigTimeStamp( pData->maFileName ) ) + { + DBG_ERROR1( "Config overwrites modified configfile:\n %s", ByteString( pData->maFileName, RTL_TEXTENCODING_UTF8 ).GetBuffer() ); + } + } +#endif + + // Aus der Config-Liste einen Buffer zusammenbauen + ULONG nBufLen; + BYTE* pBuf = ImplGetConfigBuffer( pData, nBufLen ); + if ( pBuf ) + { + if ( ImplSysWriteConfig( pData->maFileName, pBuf, nBufLen, pData->mbIsUTF8BOM, pData->mnTimeStamp ) ) + pData->mbModified = FALSE; + delete[] pBuf; + } + else + pData->mbModified = FALSE; +} + +// ----------------------------------------------------------------------- + +static void ImplDeleteConfigData( ImplConfigData* pData ) +{ + ImplKeyData* pTempKey; + ImplKeyData* pKey; + ImplGroupData* pTempGroup; + ImplGroupData* pGroup = pData->mpFirstGroup; + while ( pGroup ) + { + pTempGroup = pGroup->mpNext; + + // Alle Keys loeschen + pKey = pGroup->mpFirstKey; + while ( pKey ) + { + pTempKey = pKey->mpNext; + delete pKey; + pKey = pTempKey; + } + + // Gruppe loeschen und weiterschalten + delete pGroup; + pGroup = pTempGroup; + } + + pData->mpFirstGroup = NULL; +} + +// ======================================================================= + +static ImplConfigData* ImplGetConfigData( const XubString& rFileName ) +{ + ImplConfigData* pData; + + pData = new ImplConfigData; + pData->maFileName = rFileName; + pData->mpFirstGroup = NULL; + pData->mnDataUpdateId = 0; + pData->meLineEnd = LINEEND_CRLF; + pData->mnRefCount = 0; + pData->mbRead = FALSE; + pData->mbIsUTF8BOM = FALSE; + ImplReadConfig( pData ); + + return pData; +} + +// ----------------------------------------------------------------------- + +static void ImplFreeConfigData( ImplConfigData* pDelData ) +{ + ImplDeleteConfigData( pDelData ); + delete pDelData; +} + +// ======================================================================= + +BOOL Config::ImplUpdateConfig() const +{ + // Wenn sich TimeStamp unterscheidet, dann Datei neu einlesen + if ( mpData->mnTimeStamp != ImplSysGetConfigTimeStamp( maFileName ) ) + { + ImplDeleteConfigData( mpData ); + ImplReadConfig( mpData ); + mpData->mnDataUpdateId++; + return TRUE; + } + else + return FALSE; +} + +// ----------------------------------------------------------------------- + +ImplGroupData* Config::ImplGetGroup() const +{ + if ( !mpActGroup || (mnDataUpdateId != mpData->mnDataUpdateId) ) + { + ImplGroupData* pPrevGroup = NULL; + ImplGroupData* pGroup = mpData->mpFirstGroup; + while ( pGroup ) + { + if ( pGroup->maGroupName.EqualsIgnoreCaseAscii( maGroupName ) ) + break; + + pPrevGroup = pGroup; + pGroup = pGroup->mpNext; + } + + // Falls Gruppe noch nicht existiert, dann dazufuegen + if ( !pGroup ) + { + pGroup = new ImplGroupData; + pGroup->mpNext = NULL; + pGroup->mpFirstKey = NULL; + pGroup->mnEmptyLines = 1; + if ( pPrevGroup ) + pPrevGroup->mpNext = pGroup; + else + mpData->mpFirstGroup = pGroup; + } + + // Gruppenname immer uebernehmen, da er auch in dieser Form + // geschrieben werden soll. Ausserdem die Cache-Members der + // Config-Klasse updaten + pGroup->maGroupName = maGroupName; + ((Config*)this)->mnDataUpdateId = mpData->mnDataUpdateId; + ((Config*)this)->mpActGroup = pGroup; + } + + return mpActGroup; +} + +// ======================================================================= + +Config::Config() +{ + // Daten initialisieren und einlesen + maFileName = ImplMakeConfigName( NULL, NULL ); + mpData = ImplGetConfigData( maFileName ); + mpActGroup = NULL; + mnDataUpdateId = 0; + mnLockCount = 1; + mbPersistence = TRUE; + +#ifdef DBG_UTIL + DBG_TRACE( "Config::Config()" ); +#endif +} + +// ----------------------------------------------------------------------- + +Config::Config( const XubString& rFileName ) +{ + // Daten initialisieren und einlesen + maFileName = toUncPath( rFileName ); + mpData = ImplGetConfigData( maFileName ); + mpActGroup = NULL; + mnDataUpdateId = 0; + mnLockCount = 1; + mbPersistence = TRUE; + +#ifdef DBG_UTIL + ByteString aTraceStr( "Config::Config( " ); + aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 ); + aTraceStr += " )"; + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif +} + +// ----------------------------------------------------------------------- + +Config::~Config() +{ +#ifdef DBG_UTIL + DBG_TRACE( "Config::~Config()" ); +#endif + + Flush(); + ImplFreeConfigData( mpData ); +} + +// ----------------------------------------------------------------------- + +String Config::GetDefDirectory() +{ + ::rtl::OUString aDefConfig; + oslSecurity aSec = osl_getCurrentSecurity(); + osl_getConfigDir( aSec, &aDefConfig.pData ); + osl_freeSecurityHandle( aSec ); + + return aDefConfig; +} + +// ----------------------------------------------------------------------- + +XubString Config::GetConfigName( const XubString& rPath, + const XubString& rBaseName ) +{ + return ImplMakeConfigName( &rBaseName, &rPath ); +} + +// ----------------------------------------------------------------------- + +void Config::SetGroup( const ByteString& rGroup ) +{ + // Wenn neue Gruppe gesetzt wird, muss beim naechsten mal die + // Gruppe neu ermittelt werden + if ( maGroupName != rGroup ) + { + maGroupName = rGroup; + mnDataUpdateId = mpData->mnDataUpdateId-1; + } +} + +// ----------------------------------------------------------------------- + +void Config::DeleteGroup( const ByteString& rGroup ) +{ + // Config-Daten evt. updaten + if ( !mnLockCount || !mpData->mbRead ) + { + ImplUpdateConfig(); + mpData->mbRead = TRUE; + } + + ImplGroupData* pPrevGroup = NULL; + ImplGroupData* pGroup = mpData->mpFirstGroup; + while ( pGroup ) + { + if ( pGroup->maGroupName.EqualsIgnoreCaseAscii( rGroup ) ) + break; + + pPrevGroup = pGroup; + pGroup = pGroup->mpNext; + } + + if ( pGroup ) + { + // Alle Keys loeschen + ImplKeyData* pTempKey; + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + pTempKey = pKey->mpNext; + delete pKey; + pKey = pTempKey; + } + + // Gruppe weiterschalten und loeschen + if ( pPrevGroup ) + pPrevGroup->mpNext = pGroup->mpNext; + else + mpData->mpFirstGroup = pGroup->mpNext; + delete pGroup; + + // Config-Datei neu schreiben + if ( !mnLockCount && mbPersistence ) + ImplWriteConfig( mpData ); + else + { + mpData->mbModified = TRUE; + } + + // Gruppen auf ungluetig setzen + mnDataUpdateId = mpData->mnDataUpdateId; + mpData->mnDataUpdateId++; + } +} + +// ----------------------------------------------------------------------- + +ByteString Config::GetGroupName( USHORT nGroup ) const +{ + // Config-Daten evt. updaten + if ( !mnLockCount ) + ImplUpdateConfig(); + + ImplGroupData* pGroup = mpData->mpFirstGroup; + USHORT nGroupCount = 0; + ByteString aGroupName; + while ( pGroup ) + { + if ( nGroup == nGroupCount ) + { + aGroupName = pGroup->maGroupName; + break; + } + + nGroupCount++; + pGroup = pGroup->mpNext; + } + + return aGroupName; +} + +// ----------------------------------------------------------------------- + +USHORT Config::GetGroupCount() const +{ + // Config-Daten evt. updaten + if ( !mnLockCount ) + ImplUpdateConfig(); + + ImplGroupData* pGroup = mpData->mpFirstGroup; + USHORT nGroupCount = 0; + while ( pGroup ) + { + nGroupCount++; + pGroup = pGroup->mpNext; + } + + return nGroupCount; +} + +// ----------------------------------------------------------------------- + +BOOL Config::HasGroup( const ByteString& rGroup ) const +{ + // Config-Daten evt. updaten + if ( !mnLockCount ) + ImplUpdateConfig(); + + ImplGroupData* pGroup = mpData->mpFirstGroup; + BOOL bRet = FALSE; + + while( pGroup ) + { + if( pGroup->maGroupName.EqualsIgnoreCaseAscii( rGroup ) ) + { + bRet = TRUE; + break; + } + + pGroup = pGroup->mpNext; + } + + return bRet; +} + +// ----------------------------------------------------------------------- + +ByteString Config::ReadKey( const ByteString& rKey ) const +{ + return ReadKey( rKey, getEmptyByteString() ); +} + +// ----------------------------------------------------------------------- + +UniString Config::ReadKey( const ByteString& rKey, rtl_TextEncoding eEncoding ) const +{ + if ( mpData->mbIsUTF8BOM ) + eEncoding = RTL_TEXTENCODING_UTF8; + return UniString( ReadKey( rKey ), eEncoding ); +} + +// ----------------------------------------------------------------------- + +ByteString Config::ReadKey( const ByteString& rKey, const ByteString& rDefault ) const +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "Config::ReadKey( " ); + aTraceStr += rKey; + aTraceStr += " ) from "; + aTraceStr += GetGroup(); + aTraceStr += " in "; + aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 ); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + // Config-Daten evt. updaten + if ( !mnLockCount ) + ImplUpdateConfig(); + + // Key suchen und Value zurueckgeben + ImplGroupData* pGroup = ImplGetGroup(); + if ( pGroup ) + { + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + if ( !pKey->mbIsComment && pKey->maKey.EqualsIgnoreCaseAscii( rKey ) ) + return pKey->maValue; + + pKey = pKey->mpNext; + } + } + + return rDefault; +} + +// ----------------------------------------------------------------------- + +void Config::WriteKey( const ByteString& rKey, const ByteString& rStr ) +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "Config::WriteKey( " ); + aTraceStr += rKey; + aTraceStr += ", "; + aTraceStr += rStr; + aTraceStr += " ) to "; + aTraceStr += GetGroup(); + aTraceStr += " in "; + aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 ); + DBG_TRACE( aTraceStr.GetBuffer() ); + DBG_ASSERTWARNING( rStr != ReadKey( rKey ), "Config::WriteKey() with the same Value" ); +#endif + + // Config-Daten evt. updaten + if ( !mnLockCount || !mpData->mbRead ) + { + ImplUpdateConfig(); + mpData->mbRead = TRUE; + } + + // Key suchen und Value setzen + ImplGroupData* pGroup = ImplGetGroup(); + if ( pGroup ) + { + ImplKeyData* pPrevKey = NULL; + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + if ( !pKey->mbIsComment && pKey->maKey.EqualsIgnoreCaseAscii( rKey ) ) + break; + + pPrevKey = pKey; + pKey = pKey->mpNext; + } + + BOOL bNewValue; + if ( !pKey ) + { + pKey = new ImplKeyData; + pKey->mpNext = NULL; + pKey->maKey = rKey; + pKey->mbIsComment = FALSE; + if ( pPrevKey ) + pPrevKey->mpNext = pKey; + else + pGroup->mpFirstKey = pKey; + bNewValue = TRUE; + } + else + bNewValue = pKey->maValue != rStr; + + if ( bNewValue ) + { + pKey->maValue = rStr; + + if ( !mnLockCount && mbPersistence ) + ImplWriteConfig( mpData ); + else + { + mpData->mbModified = TRUE; + } + } + } +} + +// ----------------------------------------------------------------------- + +void Config::WriteKey( const ByteString& rKey, const UniString& rValue, rtl_TextEncoding eEncoding ) +{ + if ( mpData->mbIsUTF8BOM ) + eEncoding = RTL_TEXTENCODING_UTF8; + WriteKey( rKey, ByteString( rValue, eEncoding ) ); +} + +// ----------------------------------------------------------------------- + +void Config::DeleteKey( const ByteString& rKey ) +{ + // Config-Daten evt. updaten + if ( !mnLockCount || !mpData->mbRead ) + { + ImplUpdateConfig(); + mpData->mbRead = TRUE; + } + + // Key suchen und Value setzen + ImplGroupData* pGroup = ImplGetGroup(); + if ( pGroup ) + { + ImplKeyData* pPrevKey = NULL; + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + if ( !pKey->mbIsComment && pKey->maKey.EqualsIgnoreCaseAscii( rKey ) ) + break; + + pPrevKey = pKey; + pKey = pKey->mpNext; + } + + if ( pKey ) + { + // Gruppe weiterschalten und loeschen + if ( pPrevKey ) + pPrevKey->mpNext = pKey->mpNext; + else + pGroup->mpFirstKey = pKey->mpNext; + delete pKey; + + // Config-Datei neu schreiben + if ( !mnLockCount && mbPersistence ) + ImplWriteConfig( mpData ); + else + { + mpData->mbModified = TRUE; + } + } + } +} + +// ----------------------------------------------------------------------- + +USHORT Config::GetKeyCount() const +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "Config::GetKeyCount()" ); + aTraceStr += " from "; + aTraceStr += GetGroup(); + aTraceStr += " in "; + aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 ); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + // Config-Daten evt. updaten + if ( !mnLockCount ) + ImplUpdateConfig(); + + // Key suchen und Value zurueckgeben + USHORT nCount = 0; + ImplGroupData* pGroup = ImplGetGroup(); + if ( pGroup ) + { + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + if ( !pKey->mbIsComment ) + nCount++; + + pKey = pKey->mpNext; + } + } + + return nCount; +} + +// ----------------------------------------------------------------------- + +ByteString Config::GetKeyName( USHORT nKey ) const +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "Config::GetKeyName( " ); + aTraceStr += ByteString::CreateFromInt32(nKey); + aTraceStr += " ) from "; + aTraceStr += GetGroup(); + aTraceStr += " in "; + aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 ); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + // Key suchen und Name zurueckgeben + ImplGroupData* pGroup = ImplGetGroup(); + if ( pGroup ) + { + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + if ( !pKey->mbIsComment ) + { + if ( !nKey ) + return pKey->maKey; + nKey--; + } + + pKey = pKey->mpNext; + } + } + + return getEmptyByteString(); +} + +// ----------------------------------------------------------------------- + +ByteString Config::ReadKey( USHORT nKey ) const +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "Config::ReadKey( " ); + aTraceStr += ByteString::CreateFromInt32( nKey ); + aTraceStr += " ) from "; + aTraceStr += GetGroup(); + aTraceStr += " in "; + aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 ); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + // Key suchen und Value zurueckgeben + ImplGroupData* pGroup = ImplGetGroup(); + if ( pGroup ) + { + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + if ( !pKey->mbIsComment ) + { + if ( !nKey ) + return pKey->maValue; + nKey--; + } + + pKey = pKey->mpNext; + } + } + + return getEmptyByteString(); +} + +// ----------------------------------------------------------------------- + +void Config::EnterLock() +{ + // Config-Daten evt. updaten + if ( !mnLockCount ) + ImplUpdateConfig(); + + mnLockCount++; +} + +// ----------------------------------------------------------------------- + +void Config::LeaveLock() +{ + DBG_ASSERT( mnLockCount, "Config::LeaveLook() without Config::EnterLook()" ); + mnLockCount--; + + if ( (mnLockCount == 0) && mpData->mbModified && mbPersistence ) + ImplWriteConfig( mpData ); +} + +// ----------------------------------------------------------------------- + +BOOL Config::Update() +{ + return ImplUpdateConfig(); +} + +// ----------------------------------------------------------------------- + +void Config::Flush() +{ + if ( mpData->mbModified && mbPersistence ) + ImplWriteConfig( mpData ); +} + +// ----------------------------------------------------------------------- + +void Config::SetLineEnd( LineEnd eLineEnd ) +{ + mpData->meLineEnd = eLineEnd; +} + +// ----------------------------------------------------------------------- + +LineEnd Config::GetLineEnd() const +{ + return mpData->meLineEnd; +} + diff --git a/tools/source/generic/fract.cxx b/tools/source/generic/fract.cxx new file mode 100644 index 000000000000..0b8231d620ce --- /dev/null +++ b/tools/source/generic/fract.cxx @@ -0,0 +1,736 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_tools.hxx" + +#ifndef _LIMITS_H +#include <limits.h> +#endif +#include <tools/debug.hxx> +#include <tools/fract.hxx> +#include <tools/stream.hxx> + +#include <tools/bigint.hxx> + +/************************************************************************* +|* +|* GetGGT() +|* +|* Beschreibung Berechnet den groessten gemeinsamen Teiler von +|* nVal1 und nVal2 +|* Parameter long nVal1, long nVal2 +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Die Funktion GetGGT berechnet den groessten gemeinsamen Teiler der +// beiden als Parameter uebergebenen Werte nVal1 und nVal2 nach dem +// Algorithmus von Euklid. Hat einer der beiden Parameter den Wert 0 oder +// 1, so wird als Ergebnis der Wert 1 zurŸckgegeben. Da der Algorithmus +// nur mit positiven Zahlen arbeitet, werden die beiden Parameter +// entsprechend umgewandelt. +// Zum Algorithmus: die beiden Parameter werden solange ducheinander +// geteilt, bis sie beide gleich sind oder bis bei der Division +// kein Rest bleibt. Der kleinere der beiden Werte ist dann der +// GGT. + +static long GetGGT( long nVal1, long nVal2 ) +{ + nVal1 = Abs( nVal1 ); + nVal2 = Abs( nVal2 ); + + if ( nVal1 <= 1 || nVal2 <= 1 ) + return 1; + + while ( nVal1 != nVal2 ) + { + if ( nVal1 > nVal2 ) + { + nVal1 %= nVal2; + if ( nVal1 == 0 ) + return nVal2; + } + else + { + nVal2 %= nVal1; + if ( nVal2 == 0 ) + return nVal1; + } + } + + return nVal1; +} + +static void Reduce( BigInt &rVal1, BigInt &rVal2 ) +{ + BigInt nA( rVal1 ); + BigInt nB( rVal2 ); + nA.Abs(); + nB.Abs(); + + if ( nA.IsOne() || nB.IsOne() || nA.IsZero() || nB.IsZero() ) + return; + + while ( nA != nB ) + { + if ( nA > nB ) + { + nA %= nB; + if ( nA.IsZero() ) + { + rVal1 /= nB; + rVal2 /= nB; + return; + } + } + else + { + nB %= nA; + if ( nB.IsZero() ) + { + rVal1 /= nA; + rVal2 /= nA; + return; + } + } + } + + rVal1 /= nA; + rVal2 /= nB; +} + +/************************************************************************* +|* +|* Fraction::Fraction() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung WP 07.03.97 +|* Letzte Aenderung +|* +*************************************************************************/ + +Fraction::Fraction( long nN1, long nN2, long nD1, long nD2 ) +{ + long n; + int i = 1; + + if( nN1 < 0 ) { i = -i; nN1 = -nN1; } + if( nN2 < 0 ) { i = -i; nN2 = -nN2; } + if( nD1 < 0 ) { i = -i; nD1 = -nD1; } + if( nD2 < 0 ) { i = -i; nD2 = -nD2; } + + n = GetGGT( nN1, nD1 ); if( n > 1 ) { nN1 /= n; nD1 /= n; } + n = GetGGT( nN1, nD2 ); if( n > 1 ) { nN1 /= n; nD2 /= n; } + n = GetGGT( nN2, nD1 ); if( n > 1 ) { nN2 /= n; nD1 /= n; } + n = GetGGT( nN2, nD2 ); if( n > 1 ) { nN2 /= n; nD2 /= n; } + + BigInt nN( nN1 ); + nN *= BigInt( nN2 ); + + BigInt nD( nD1 ); + nD *= BigInt( nD2 ); + + while ( nN.bIsBig || nD.bIsBig ) + { + BigInt n1 = 1; + BigInt n2 = 2; + + nN += n1; + nN /= n2; + nD += n1; + nD /= n2; + + // Kuerzen ueber Groesste Gemeinsame Teiler + Reduce( nN, nD ); + } + + nNumerator = i * (long)nN; + nDenominator = (long)nD; +} + +/************************************************************************* +|* +|* Fraction::Fraction() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Zur Initialisierung eines Bruches wird nNum dem Zaehler und nDen dem +// Nenner zugewiesen. Da negative Werte des Nenners einen Bruch als +// ungueltig kennzeichnen, wird bei der Eingabe eines negativen Nenners +// sowohl das Vorzeichen des Nenners und des Zaehlers invertiert um wieder +// einen gueltigen Wert fuer den Bruch zu erhalten. + +Fraction::Fraction( long nNum, long nDen ) +{ + nNumerator = nNum; + nDenominator = nDen; + if ( nDenominator < 0 ) + { + nDenominator = -nDenominator; + nNumerator = -nNumerator; + } + + // Kuerzen ueber Groesste Gemeinsame Teiler + long n = GetGGT( nNumerator, nDenominator ); + nNumerator /= n; + nDenominator /= n; +} + +/************************************************************************* +|* +|* Fraction::Fraction() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Wenn der Wert von dVal groesser ist als LONG_MAX, dann wird der Bruch +// auf den Wert ungueltig gesetzt, ansonsten werden dVal und der Nenner +// solange mit 10 multipliziert, bis entweder der Zaehler oder der Nenner +// groesser als LONG_MAX / 10 ist. Zum Schluss wird der so entstandene Bruch +// gekuerzt. + +Fraction::Fraction( double dVal ) +{ + long nDen = 1; + long nMAX = LONG_MAX / 10; + + if ( dVal > LONG_MAX || dVal < LONG_MIN ) + { + nNumerator = 0; + nDenominator = -1; + return; + } + + while ( Abs( (long)dVal ) < nMAX && nDen < nMAX ) + { + dVal *= 10; + nDen *= 10; + } + nNumerator = (long)dVal; + nDenominator = nDen; + + // Kuerzen ueber Groesste Gemeinsame Teiler + long n = GetGGT( nNumerator, nDenominator ); + nNumerator /= n; + nDenominator /= n; +} + +/************************************************************************* +|* +|* Fraction::operator double() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 14.05.91 +|* +*************************************************************************/ + +Fraction::operator double() const +{ + if ( nDenominator > 0 ) + return (double)nNumerator / (double)nDenominator; + else + return (double)0; +} + +/************************************************************************* +|* +|* Fraction::operator+=() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Zunaechst werden die beiden Parameter auf ihre Gueltigkeit ueberprueft. +// Ist einer der Parameter ungueltig, dann ist auch des Ergebnis +// ungueltig. Zur Addition werden die beiden Brueche erst durch +// Erweiterung mit den Nenner des jeweils anderen Bruches auf einen +// gemeinsamen Nenner gebracht. Anschliessend werden die beiden Zaehler +// addiert und das Ergebnis gekuerzt (durch Division von Zaehler und +// Nenner mit nGGT). Innerhalb der Funktion wird mit dem Datentyp SLong +// gerechnet, um einen Moeglichen Ueberlauf erkennen zu koennen. Bei +// einem Ueberlauf wird das Ergebnis auf den Wert ungueltig gesetzt. + +Fraction& Fraction::operator += ( const Fraction& rVal ) +{ + if ( !rVal.IsValid() ) + { + nNumerator = 0; + nDenominator = -1; + } + if ( !IsValid() ) + return *this; + + // (a/b) + (c/d) = ( (a*d) + (c*b) ) / (b*d) + BigInt nN( nNumerator ); + nN *= BigInt( rVal.nDenominator ); + BigInt nW1Temp( nDenominator ); + nW1Temp *= BigInt( rVal.nNumerator ); + nN += nW1Temp; + + BigInt nD( nDenominator ); + nD *= BigInt( rVal.nDenominator ); + + Reduce( nN, nD ); + + if ( nN.bIsBig || nD.bIsBig ) + { + nNumerator = 0; + nDenominator = -1; + } + else + { + nNumerator = (long)nN, + nDenominator = (long)nD; + } + + return *this; +} + +/************************************************************************* +|* +|* Fraction::operator-=() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Zunaechst werden die beiden Parameter auf ihre Gueltigkeit ueberprueft. +// Ist einer der Parameter ungueltig, dann ist auch des Ergebnis +// ungueltig. Zur Subtraktion werden die beiden Brueche erst durch +// Erweiterung mit den Nenner des jeweils anderen Bruches auf einen +// gemeinsamen Nenner gebracht. Anschliessend werden die beiden Zaehler +// subtrahiert und das Ergebnis gekuerzt (durch Division von Zaehler und +// Nenner mit nGGT). Innerhalb der Funktion wird mit dem Datentyp BigInt +// gerechnet, um einen Moeglichen Ueberlauf erkennen zu koennen. Bei +// einem Ueberlauf wird das Ergebnis auf den Wert ungueltig gesetzt. + +Fraction& Fraction::operator -= ( const Fraction& rVal ) +{ + if ( !rVal.IsValid() ) + { + nNumerator = 0; + nDenominator = -1; + } + if ( !IsValid() ) + return *this; + + // (a/b) - (c/d) = ( (a*d) - (c*b) ) / (b*d) + BigInt nN( nNumerator ); + nN *= BigInt( rVal.nDenominator ); + BigInt nW1Temp( nDenominator ); + nW1Temp *= BigInt( rVal.nNumerator ); + nN -= nW1Temp; + + BigInt nD( nDenominator ); + nD *= BigInt( rVal.nDenominator ); + + Reduce( nN, nD ); + + if ( nN.bIsBig || nD.bIsBig ) + { + nNumerator = 0; + nDenominator = -1; + } + else + { + nNumerator = (long)nN, + nDenominator = (long)nD; + } + + return *this; +} + +/************************************************************************* +|* +|* Fraction::operator*=() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung TH 19.08.92 +|* +*************************************************************************/ + +// Zunaechst werden die beiden Parameter auf ihre Gueltigkeit ueberprueft. +// Ist einer der Parameter ungueltig, dann ist auch des Ergebnis +// ungueltig. Zur Multiplikation werden jeweils die beiden Zaehler und +// Nenner miteinander multipliziert. Um Ueberlaufe zu vermeiden, werden +// vorher jeweils der GGT zwischen dem Zaehler des einen und dem Nenner +// des anderen Bruches bestimmt und bei der Multiplikation Zaehler und +// Nenner durch die entsprechenden Werte geteilt. +// Innerhalb der Funktion wird mit dem Datentyp BigInt gerechnet, um +// einen Moeglichen Ueberlauf erkennen zu koennen. Bei einem Ueberlauf +// wird das Ergebnis auf den Wert ungueltig gesetzt. + +Fraction& Fraction::operator *= ( const Fraction& rVal ) +{ + if ( !rVal.IsValid() ) + { + nNumerator = 0; + nDenominator = -1; + } + if ( !IsValid() ) + return *this; + + long nGGT1 = GetGGT( nNumerator, rVal.nDenominator ); + long nGGT2 = GetGGT( rVal.nNumerator, nDenominator ); + BigInt nN( nNumerator / nGGT1 ); + nN *= BigInt( rVal.nNumerator / nGGT2 ); + BigInt nD( nDenominator / nGGT2 ); + nD *= BigInt( rVal.nDenominator / nGGT1 ); + + if ( nN.bIsBig || nD.bIsBig ) + { + nNumerator = 0; + nDenominator = -1; + } + else + { + nNumerator = (long)nN, + nDenominator = (long)nD; + } + + return *this; +} + +/************************************************************************* +|* +|* Fraction::operator/=() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Zunaechst werden die beiden Parameter auf ihre Gueltigkeit ueberprueft. +// Ist einer der Parameter ungueltig, dann ist auch des Ergebnis +// ungueltig. +// Um den Bruch a durch b zu teilen, wird a mit dem Kehrwert von b +// multipliziert. Analog zu Multiplikation wird jezt jeweils der Zaehler +// des einen Bruches mit dem Nenner des anderen multipliziert. +// Um Ueberlaufe zu vermeiden, werden vorher jeweils der GGT zwischen den +// beiden Zaehlern und den beiden Nennern bestimmt und bei der +// Multiplikation Zaehler und Nenner durch die entsprechenden Werte +// geteilt. +// Innerhalb der Funktion wird mit dem Datentyp BigInt gerechnet, um +// einen Moeglichen Ueberlauf erkennen zu koennen. Bei einem Ueberlauf +// wird das Ergebnis auf den Wert ungueltig gesetzt. + +Fraction& Fraction::operator /= ( const Fraction& rVal ) +{ + if ( !rVal.IsValid() ) + { + nNumerator = 0; + nDenominator = -1; + } + if ( !IsValid() ) + return *this; + + long nGGT1 = GetGGT( nNumerator, rVal.nNumerator ); + long nGGT2 = GetGGT( rVal.nDenominator, nDenominator ); + BigInt nN( nNumerator / nGGT1 ); + nN *= BigInt( rVal.nDenominator / nGGT2 ); + BigInt nD( nDenominator / nGGT2 ); + nD *= BigInt( rVal.nNumerator / nGGT1 ); + + if ( nN.bIsBig || nD.bIsBig ) + { + nNumerator = 0; + nDenominator = -1; + } + else + { + nNumerator = (long)nN, + nDenominator = (long)nD; + if ( nDenominator < 0 ) + { + nDenominator = -nDenominator; + nNumerator = -nNumerator; + } + } + + return *this; +} + +/************************************************************************* +|* +|* Fraction::ReduceInaccurate() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung JOE 17.09.95 +|* Letzte Aenderung kendy 2007-06-13 +|* +*************************************************************************/ + + +// Similar to clz_table that can be googled +const char nbits_table[32] = +{ + 32, 1, 23, 2, 29, 24, 14, 3, + 30, 27, 25, 18, 20, 15, 10, 4, + 31, 22, 28, 13, 26, 17, 19, 9, + 21, 12, 16, 8, 11, 7, 6, 5 +}; + +static int impl_NumberOfBits( unsigned long nNum ) +{ + // http://en.wikipedia.org/wiki/De_Bruijn_sequence + // + // background paper: Using de Bruijn Sequences to Index a 1 in a + // Computer Word (1998) Charles E. Leiserson, + // Harald Prokop, Keith H. Randall + // (e.g. http://citeseer.ist.psu.edu/leiserson98using.html) + const sal_uInt32 nDeBruijn = 0x7DCD629; + + if ( nNum == 0 ) + return 0; + + // Get it to form like 0000001111111111b + nNum |= ( nNum >> 1 ); + nNum |= ( nNum >> 2 ); + nNum |= ( nNum >> 4 ); + nNum |= ( nNum >> 8 ); + nNum |= ( nNum >> 16 ); + + sal_uInt32 nNumber; + int nBonus = 0; + +#if SAL_TYPES_SIZEOFLONG == 4 + nNumber = nNum; +#elif SAL_TYPES_SIZEOFLONG == 8 + nNum |= ( nNum >> 32 ); + + if ( nNum & 0x80000000 ) + { + nNumber = sal_uInt32( nNum >> 32 ); + nBonus = 32; + + if ( nNumber == 0 ) + return 32; + } + else + nNumber = sal_uInt32( nNum & 0xFFFFFFFF ); +#else +#error "Unknown size of long!" +#endif + + // De facto shift left of nDeBruijn using multiplication (nNumber + // is all ones from topmost bit, thus nDeBruijn + (nDeBruijn * + // nNumber) => nDeBruijn * (nNumber+1) clears all those bits to + // zero, sets the next bit to one, and thus effectively shift-left + // nDeBruijn by lg2(nNumber+1). This generates a distinct 5bit + // sequence in the msb for each distinct position of the last + // leading 0 bit - that's the property of a de Bruijn number. + nNumber = nDeBruijn + ( nDeBruijn * nNumber ); + + // 5-bit window indexes the result + return ( nbits_table[nNumber >> 27] ) + nBonus; +} + +/** Inaccurate cancellation for a fraction. + + Clip both nominator and denominator to said number of bits. If + either of those already have equal or less number of bits used, + this method does nothing. + + @param nSignificantBits denotes, how many significant binary + digits to maintain, in both nominator and denominator. + + @example ReduceInaccurate(8) has an error <1% [1/2^(8-1)] - the + largest error occurs with the following pair of values: + + binary 1000000011111111111111111111111b/1000000000000000000000000000000b + = 1082130431/1073741824 + = approx. 1.007812499 + + A ReduceInaccurate(8) yields 1/1. +*/ +void Fraction::ReduceInaccurate( unsigned nSignificantBits ) +{ + if ( !nNumerator || !nDenominator ) + return; + + // Count with unsigned longs only + const bool bNeg = ( nNumerator < 0 ); + unsigned long nMul = (unsigned long)( bNeg? -nNumerator: nNumerator ); + unsigned long nDiv = (unsigned long)( nDenominator ); + + DBG_ASSERT(nSignificantBits<65, "More than 64 bit of significance is overkill!"); + + // How much bits can we lose? + const int nMulBitsToLose = Max( ( impl_NumberOfBits( nMul ) - int( nSignificantBits ) ), 0 ); + const int nDivBitsToLose = Max( ( impl_NumberOfBits( nDiv ) - int( nSignificantBits ) ), 0 ); + + const int nToLose = Min( nMulBitsToLose, nDivBitsToLose ); + + // Remove the bits + nMul >>= nToLose; + nDiv >>= nToLose; + + if ( !nMul || !nDiv ) + { + // Return without reduction + DBG_ERROR( "Oops, we reduced too much..." ); + return; + } + + // Reduce + long n1 = GetGGT( nMul, nDiv ); + if ( n1 != 1 ) + { + nMul /= n1; + nDiv /= n1; + } + + nNumerator = bNeg? -long( nMul ): long( nMul ); + nDenominator = nDiv; +} + +/************************************************************************* +|* +|* Fraction::operator ==() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung TH 19.08.92 +|* +*************************************************************************/ + +BOOL operator == ( const Fraction& rVal1, const Fraction& rVal2 ) +{ + if ( !rVal1.IsValid() || !rVal2.IsValid() ) + return FALSE; + + return rVal1.nNumerator == rVal2.nNumerator + && rVal1.nDenominator == rVal2.nDenominator; +} + +/************************************************************************* +|* +|* Fraction::operator <() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Beide Operanden werden zunaechst auf ihre Gueltigkeit ueberprueft und +// anschliessend zur Sicherheit noch einmal gekuerzt. Um die Brueche +// (a/b) und (c/d) zu vergleichen, werden sie zunaechst auf einen +// gemeinsamen Nenner gebracht (b*d), um dann die beiden Zaehler (a*d) +// und (c*b) zu vergleichen. Das Ergebnis dieses Vergleichs wird +// zurueckgegeben. + +BOOL operator < ( const Fraction& rVal1, const Fraction& rVal2 ) +{ + if ( !rVal1.IsValid() || !rVal2.IsValid() ) + return FALSE; + + BigInt nN( rVal1.nNumerator ); + nN *= BigInt( rVal2.nDenominator ); + BigInt nD( rVal1.nDenominator ); + nD *= BigInt( rVal2.nNumerator ); + + return nN < nD; +} + +/************************************************************************* +|* +|* Fraction::operator >() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung TH 19.08.92 +|* +*************************************************************************/ + +// Beide Operanden werden zunaechst auf ihre Gueltigkeit ueberprueft und +// anschliessend zur Sicherheit noch einmal gekuerzt. Um die Brueche +// (a/b) und (c/d) zu vergleichen, werden sie zunaechst auf einen +// gemeinsamen Nenner gebracht (b*d), um dann die beiden Zaehler (a*d) +// und (c*b) zu vergleichen. Das Ergebnis dieses Vergleichs wird +// zurueckgegeben. + +BOOL operator > ( const Fraction& rVal1, const Fraction& rVal2 ) +{ + if ( !rVal1.IsValid() || !rVal2.IsValid() ) + return FALSE; + + BigInt nN( rVal1.nNumerator ); + nN *= BigInt( rVal2.nDenominator ); + BigInt nD( rVal1.nDenominator); + nD *= BigInt( rVal2.nNumerator ); + + return nN > nD; +} + +/************************************************************************* +|* +|* SvStream& operator>>( SvStream& rIStream, Fraction& rFract ) +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung MM 08.01.96 +|* Letzte Aenderung MM 08.01.96 +|* +*************************************************************************/ +SvStream& operator >> ( SvStream& rIStream, Fraction& rFract ) +{ + rIStream >> rFract.nNumerator; + rIStream >> rFract.nDenominator; + return rIStream; +} + +/************************************************************************* +|* +|* SvStream& operator<<( SvStream& rIStream, Fraction& rFract ) +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung MM 08.01.96 +|* Letzte Aenderung MM 08.01.96 +|* +*************************************************************************/ +SvStream& operator << ( SvStream& rOStream, const Fraction& rFract ) +{ + rOStream << rFract.nNumerator; + rOStream << rFract.nDenominator; + return rOStream; +} diff --git a/tools/source/generic/gen.cxx b/tools/source/generic/gen.cxx new file mode 100644 index 000000000000..b0eb57f1ab73 --- /dev/null +++ b/tools/source/generic/gen.cxx @@ -0,0 +1,661 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_tools.hxx" +#include <tools/debug.hxx> +#include <tools/gen.hxx> +#include <tools/stream.hxx> + +// ======================================================================= + +SvStream& operator>>( SvStream& rIStream, Pair& rPair ) +{ + DBG_ASSERTWARNING( rIStream.GetVersion(), "Pair::>> - Solar-Version not set on rIStream" ); + + if ( rIStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + unsigned char cId; + unsigned char cAry[8]; + int i; + int i1; + int i2; + UINT32 nNum; + + rIStream >> cId; + i1 = (cId & 0x70) >> 4; + i2 = cId & 0x07; + rIStream.Read( cAry, i1+i2 ); + + nNum = 0; + i = i1; + while ( i ) + { + i--; + nNum <<= 8; + nNum |= cAry[i]; + } + if ( cId & 0x80 ) + nNum ^= 0xFFFFFFFF; + rPair.nA = (INT32)nNum; + + nNum = 0; + i = i1+i2; + while ( i > i1 ) + { + i--; + nNum <<= 8; + nNum |= cAry[i]; + } + if ( cId & 0x08 ) + nNum ^= 0xFFFFFFFF; + rPair.nB = (INT32)nNum; + } + else + { + rIStream >> rPair.nA >> rPair.nB; + } + + return rIStream; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStream, const Pair& rPair ) +{ + DBG_ASSERTWARNING( rOStream.GetVersion(), "Pair::<< - Solar-Version not set on rOStream" ); + + if ( rOStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + unsigned char cAry[9]; + int i = 1; + UINT32 nNum; + + cAry[0] = 0; + + nNum = (UINT32)(INT32)rPair.nA; + if ( rPair.nA < 0 ) + { + cAry[0] |= 0x80; + nNum ^= 0xFFFFFFFF; + } + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + cAry[0] |= 0x40; + } + else + cAry[0] |= 0x30; + } + else + cAry[0] |= 0x20; + } + else + cAry[0] |= 0x10; + } + + nNum = (UINT32)(INT32)rPair.nB; + if ( rPair.nB < 0 ) + { + cAry[0] |= 0x08; + nNum ^= 0xFFFFFFFF; + } + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + cAry[0] |= 0x04; + } + else + cAry[0] |= 0x03; + } + else + cAry[0] |= 0x02; + } + else + cAry[0] |= 0x01; + } + + rOStream.Write( cAry, i ); + } + else + { + rOStream << rPair.nA << rPair.nB; + } + + return rOStream; +} + +/************************************************************************* +|* +|* Rectangle::SetSize() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung DV 29.10.91 +|* Letzte Aenderung MM 21.04.94 +|* +*************************************************************************/ + +void Rectangle::SetSize( const Size& rSize ) +{ + if ( rSize.Width() < 0 ) + nRight = nLeft + rSize.Width() +1; + else if ( rSize.Width() > 0 ) + nRight = nLeft + rSize.Width() -1; + else + nRight = RECT_EMPTY; + + if ( rSize.Height() < 0 ) + nBottom = nTop + rSize.Height() +1; + else if ( rSize.Height() > 0 ) + nBottom = nTop + rSize.Height() -1; + else + nBottom = RECT_EMPTY; +} + +/************************************************************************* +|* +|* Rectangle::Union() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung TH 20.10.92 +|* Letzte Aenderung MM 21.04.94 +|* +*************************************************************************/ + +Rectangle& Rectangle::Union( const Rectangle& rRect ) +{ + if ( rRect.IsEmpty() ) + return *this; + + if ( IsEmpty() ) + *this = rRect; + else + { + nLeft = Min( Min( nLeft, rRect.nLeft ), Min( nRight, rRect.nRight ) ); + nRight = Max( Max( nLeft, rRect.nLeft ), Max( nRight, rRect.nRight ) ); + nTop = Min( Min( nTop, rRect.nTop ), Min( nBottom, rRect.nBottom ) ); + nBottom = Max( Max( nTop, rRect.nTop ), Max( nBottom, rRect.nBottom ) ); + } + + return *this; +} + +/************************************************************************* +|* +|* Rectangle::Intersection() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung TH 20.10.92 +|* Letzte Aenderung MM 21.04.94 +|* +*************************************************************************/ + +Rectangle& Rectangle::Intersection( const Rectangle& rRect ) +{ + if ( IsEmpty() ) + return *this; + if ( rRect.IsEmpty() ) + { + *this = Rectangle(); + return *this; + } + + // nicht mit umgedrehten Rechtecken arbeiten + Rectangle aTmpRect( rRect ); + Justify(); + aTmpRect.Justify(); + + // Schnitt bilden + nLeft = Max( nLeft, aTmpRect.nLeft ); + nRight = Min( nRight, aTmpRect.nRight ); + nTop = Max( nTop, aTmpRect.nTop ); + nBottom= Min( nBottom, aTmpRect.nBottom ); + + // Feststellen ob Schnitt leer + if ( nRight < nLeft || nBottom < nTop ) + *this = Rectangle(); + + return *this; +} + +/************************************************************************* +|* +|* Rectangle::Justify() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung DV 07.03.91 +|* Letzte Aenderung DV 07.03.91 +|* +*************************************************************************/ + +void Rectangle::Justify() +{ + long nHelp; + + // Abfrage, ob Right kleiner Left + if ( (nRight < nLeft) && (nRight != RECT_EMPTY) ) + { + nHelp = nLeft; + nLeft = nRight; + nRight = nHelp; + } + + // Abfrage, ob Bottom kleiner Top + if ( (nBottom < nTop) && (nBottom != RECT_EMPTY) ) + { + nHelp = nBottom; + nBottom = nTop; + nTop = nHelp; + } +} + +/************************************************************************* +|* +|* Rectangle::IsInside() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung TH 19.03.90 +|* Letzte Aenderung MM 21.04.94 +|* +*************************************************************************/ + +BOOL Rectangle::IsInside( const Point& rPoint ) const +{ + if ( IsEmpty() ) + return FALSE; + + BOOL bRet = TRUE; + if ( nLeft <= nRight ) + { + if ( (rPoint.X() < nLeft) || (rPoint.X() > nRight) ) + bRet = FALSE; + } + else + { + if ( (rPoint.X() > nLeft) || (rPoint.X() < nRight) ) + bRet = FALSE; + } + if ( nTop <= nBottom ) + { + if ( (rPoint.Y() < nTop) || (rPoint.Y() > nBottom) ) + bRet = FALSE; + } + else + { + if ( (rPoint.Y() > nTop) || (rPoint.Y() < nBottom) ) + bRet = FALSE; + } + return bRet; +} + +/************************************************************************* +|* +|* Rectangle::IsInside() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung TH 19.03.90 +|* Letzte Aenderung MM 21.04.94 +|* +*************************************************************************/ + +BOOL Rectangle::IsInside( const Rectangle& rRect ) const +{ + if ( IsInside( rRect.TopLeft() ) && IsInside( rRect.BottomRight() ) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************* +|* +|* Rectangle::IsOver() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung TH 19.03.90 +|* Letzte Aenderung MM 21.04.94 +|* +*************************************************************************/ + +BOOL Rectangle::IsOver( const Rectangle& rRect ) const +{ + // Wenn sie sich nicht schneiden, ueberlappen sie auch nicht + return !GetIntersection( rRect ).IsEmpty(); +} + +// ======================================================================= + +SvStream& operator>>( SvStream& rIStream, Rectangle& rRect ) +{ + DBG_ASSERTWARNING( rIStream.GetVersion(), "Rectangle::>> - Solar-Version not set on rIStream" ); + + if ( rIStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + unsigned char cIdAry[2]; + unsigned char cAry[16]; + int i; + int iLast; + int i1; + int i2; + int i3; + int i4; + UINT32 nNum; + + rIStream.Read( cIdAry, 2 ); + i1 = (cIdAry[0] & 0x70) >> 4; + i2 = cIdAry[0] & 0x07; + i3 = (cIdAry[1] & 0x70) >> 4; + i4 = cIdAry[1] & 0x07; + rIStream.Read( cAry, i1+i2+i3+i4 ); + + nNum = 0; + i = i1; + iLast = i; + while ( i ) + { + i--; + nNum <<= 8; + nNum |= cAry[i]; + } + iLast = i1; + if ( cIdAry[0] & 0x80 ) + nNum ^= 0xFFFFFFFF; + rRect.nLeft = (INT32)nNum; + + nNum = 0; + i = iLast+i2; + while ( i > iLast ) + { + i--; + nNum <<= 8; + nNum |= cAry[i]; + } + iLast += i2; + if ( cIdAry[0] & 0x08 ) + nNum ^= 0xFFFFFFFF; + rRect.nTop = (INT32)nNum; + + nNum = 0; + i = iLast+i3; + while ( i > iLast ) + { + i--; + nNum <<= 8; + nNum |= cAry[i]; + } + iLast += i3; + if ( cIdAry[1] & 0x80 ) + nNum ^= 0xFFFFFFFF; + rRect.nRight = (INT32)nNum; + + nNum = 0; + i = iLast+i4; + while ( i > iLast ) + { + i--; + nNum <<= 8; + nNum |= cAry[i]; + } + if ( cIdAry[1] & 0x08 ) + nNum ^= 0xFFFFFFFF; + rRect.nBottom = (INT32)nNum; + } + else + { + rIStream >> rRect.nLeft >> rRect.nTop >> rRect.nRight >> rRect.nBottom; + } + + return rIStream; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStream, const Rectangle& rRect ) +{ + DBG_ASSERTWARNING( rOStream.GetVersion(), "Rectangle::<< - Solar-Version not set on rOStream" ); + + if ( rOStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + unsigned char cAry[18]; + int i = 2; + UINT32 nNum; + + cAry[0] = 0; + cAry[1] = 0; + + nNum = (UINT32)(INT32)rRect.nLeft; + if ( rRect.nLeft < 0 ) + { + cAry[0] |= 0x80; + nNum ^= 0xFFFFFFFF; + } + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + cAry[0] |= 0x40; + } + else + cAry[0] |= 0x30; + } + else + cAry[0] |= 0x20; + } + else + cAry[0] |= 0x10; + } + + nNum = (UINT32)(INT32)rRect.nTop; + if ( rRect.nTop < 0 ) + { + cAry[0] |= 0x08; + nNum ^= 0xFFFFFFFF; + } + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + cAry[0] |= 0x04; + } + else + cAry[0] |= 0x03; + } + else + cAry[0] |= 0x02; + } + else + cAry[0] |= 0x01; + } + + nNum = (UINT32)(INT32)rRect.nRight; + if ( rRect.nRight < 0 ) + { + cAry[1] |= 0x80; + nNum ^= 0xFFFFFFFF; + } + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + cAry[1] |= 0x40; + } + else + cAry[1] |= 0x30; + } + else + cAry[1] |= 0x20; + } + else + cAry[1] |= 0x10; + } + + nNum = (UINT32)(INT32)rRect.nBottom; + if ( rRect.nBottom < 0 ) + { + cAry[1] |= 0x08; + nNum ^= 0xFFFFFFFF; + } + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + cAry[1] |= 0x04; + } + else + cAry[1] |= 0x03; + } + else + cAry[1] |= 0x02; + } + else + cAry[1] |= 0x01; + } + + rOStream.Write( cAry, i ); + } + else + { + rOStream << rRect.nLeft << rRect.nTop << rRect.nRight << rRect.nBottom; + } + + return rOStream; +} diff --git a/tools/source/generic/line.cxx b/tools/source/generic/line.cxx new file mode 100644 index 000000000000..0c2de98d843d --- /dev/null +++ b/tools/source/generic/line.cxx @@ -0,0 +1,363 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_tools.hxx" + +#define _LINE_CXX +#include <tools/link.hxx> +#include <tools/line.hxx> +#include <tools/debug.hxx> + +#include <cstdlib> +#include <math.h> + +// -------- +// - Line - +// -------- + +double Line::GetLength() const +{ + return hypot( maStart.X() - maEnd.X(), maStart.Y() - maEnd.Y() ); +} + +// ------------------------------------------------------------------------ + +BOOL Line::Intersection( const Line& rLine, Point& rIntersection ) const +{ + double fX, fY; + BOOL bRet; + + if( Intersection( rLine, fX, fY ) ) + { + rIntersection.X() = FRound( fX ); + rIntersection.Y() = FRound( fY ); + bRet = TRUE; + } + else + bRet = FALSE; + + return bRet; +} + +// ------------------------------------------------------------------------ + +BOOL Line::Intersection( const Line& rLine, double& rIntersectionX, double& rIntersectionY ) const +{ + const double fAx = maEnd.X() - maStart.X(); + const double fAy = maEnd.Y() - maStart.Y(); + const double fBx = rLine.maStart.X() - rLine.maEnd.X(); + const double fBy = rLine.maStart.Y() - rLine.maEnd.Y(); + const double fDen = fAy * fBx - fAx * fBy; + BOOL bOk = FALSE; + + if( fDen != 0. ) + { + const double fCx = maStart.X() - rLine.maStart.X(); + const double fCy = maStart.Y() - rLine.maStart.Y(); + const double fA = fBy * fCx - fBx * fCy; + const BOOL bGreater = ( fDen > 0. ); + + bOk = TRUE; + + if ( bGreater ) + { + if ( ( fA < 0. ) || ( fA > fDen ) ) + bOk = FALSE; + } + else if ( ( fA > 0. ) || ( fA < fDen ) ) + bOk = FALSE; + + if ( bOk ) + { + const double fB = fAx * fCy - fAy * fCx; + + if ( bGreater ) + { + if ( ( fB < 0. ) || ( fB > fDen ) ) + bOk = FALSE; + } + else if ( ( fB > 0. ) || ( fB < fDen ) ) + bOk = FALSE; + + if( bOk ) + { + const double fAlpha = fA / fDen; + + rIntersectionX = ( maStart.X() + fAlpha * fAx ); + rIntersectionY = ( maStart.Y() + fAlpha * fAy ); + } + } + } + + return bOk; +} + +// ------------------------------------------------------------------------ + +BOOL Line::Intersection( const Rectangle& rRect, Line& rIntersection ) const +{ + const BOOL bStartInside = rRect.IsInside( maStart ); + const BOOL bEndInside = rRect.IsInside( maEnd ); + BOOL bRet = TRUE; + + if( bStartInside && bEndInside ) + { + // line completely inside rect + rIntersection.maStart = maStart; + rIntersection.maEnd = maEnd; + } + else + { + // calculate intersections + const Point aTL( rRect.TopLeft() ), aTR( rRect.TopRight() ); + const Point aBR( rRect.BottomRight() ), aBL( rRect.BottomLeft() ); + Point aIntersect1, aIntersect2; + Point* pCurIntersection = &aIntersect1; + + if( Intersection( Line( aTL, aTR ), *pCurIntersection ) ) + pCurIntersection = &aIntersect2; + + if( Intersection( Line( aTR, aBR ), *pCurIntersection ) ) + pCurIntersection = ( pCurIntersection == &aIntersect1 ) ? &aIntersect2 : NULL; + + if( pCurIntersection && Intersection( Line( aBR, aBL ), *pCurIntersection ) ) + pCurIntersection = ( pCurIntersection == &aIntersect1 ) ? &aIntersect2 : NULL; + + if( pCurIntersection && Intersection( Line( aBL, aTL ), *pCurIntersection ) ) + pCurIntersection = ( pCurIntersection == &aIntersect1 ) ? &aIntersect2 : NULL; + + if( !pCurIntersection ) + { + // two intersections + rIntersection.maStart = aIntersect1; + rIntersection.maEnd = aIntersect2; + } + else if( pCurIntersection == &aIntersect2 ) + { + // one intersection + rIntersection.maStart = aIntersect1; + + if( ( maStart != aIntersect1 ) && bStartInside ) + rIntersection.maEnd = maStart; + else if( ( maEnd != aIntersect1 ) && bEndInside ) + rIntersection.maEnd = maEnd; + else + rIntersection.maEnd = rIntersection.maStart; + } + else + bRet = FALSE; + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +Point Line::NearestPoint( const Point& rPoint ) const +{ + Point aRetPt; + + if ( maStart != maEnd ) + { + const double fDistX = maEnd.X() - maStart.X(); + const double fDistY = maStart.Y() - maEnd.Y(); + const double fTau = ( ( maStart.Y() - rPoint.Y() ) * fDistY - + ( maStart.X() - rPoint.X() ) * fDistX ) / + ( fDistX * fDistX + fDistY * fDistY ); + + if( fTau < 0.0 ) + aRetPt = maStart; + else if( fTau <= 1.0 ) + { + aRetPt.X() = FRound( maStart.X() + fTau * fDistX ); + aRetPt.Y() = FRound( maStart.Y() - fTau * fDistY ); + } + else + aRetPt = maEnd; + } + else + aRetPt = maStart; + + return aRetPt; +} + +// ------------------------------------------------------------------------ + +double Line::GetDistance( const double& rPtX, const double& rPtY ) const +{ + double fDist; + + if( maStart != maEnd ) + { + const double fDistX = maEnd.X() - maStart.X(); + const double fDistY = maEnd.Y() - maStart.Y(); + const double fACX = maStart.X() - rPtX; + const double fACY = maStart.Y() - rPtY; + const double fL2 = fDistX * fDistX + fDistY * fDistY; + const double fR = ( fACY * -fDistY - fACX * fDistX ) / fL2; + const double fS = ( fACY * fDistX - fACX * fDistY ) / fL2; + + if( fR < 0.0 ) + { + fDist = hypot( maStart.X() - rPtX, maStart.Y() - rPtY ); + + if( fS < 0.0 ) + fDist *= -1.0; + } + else if( fR <= 1.0 ) + fDist = fS * sqrt( fL2 ); + else + { + fDist = hypot( maEnd.X() - rPtX, maEnd.Y() - rPtY ); + + if( fS < 0.0 ) + fDist *= -1.0; + } + } + else + fDist = hypot( maStart.X() - rPtX, maStart.Y() - rPtY ); + + return fDist; +} + +// ------------------------------------------------------------------------ + +void Line::Enum( const Link& rEnumLink ) +{ + DBG_ASSERT( rEnumLink.IsSet(), "This call doesn't make any sense with !rEnumLink.IsSet()" ); + + Point aEnum; + long nX; + long nY; + + if( maStart.X() == maEnd.X() ) + { + const long nEndY = maEnd.Y(); + + nX = maStart.X(); + nY = maStart.Y(); + + if( nEndY > nY ) + { + while( nY <= nEndY ) + { + aEnum.X() = nX; + aEnum.Y() = nY++; + rEnumLink.Call( &aEnum ); + } + } + else + { + while( nY >= nEndY ) + { + aEnum.X() = nX; + aEnum.Y() = nY--; + rEnumLink.Call( &aEnum ); + } + } + } + else if( maStart.Y() == maEnd.Y() ) + { + const long nEndX = maEnd.X(); + + nX = maStart.X(); + nY = maStart.Y(); + + if( nEndX > nX ) + { + while( nX <= nEndX ) + { + aEnum.X() = nX++; + aEnum.Y() = nY; + rEnumLink.Call( &aEnum ); + } + } + else + { + while( nX >= nEndX ) + { + aEnum.X() = nX--; + aEnum.Y() = nY; + rEnumLink.Call( &aEnum ); + } + } + } + else + { + const long nDX = labs( maEnd.X() - maStart.X() ); + const long nDY = labs( maEnd.Y() - maStart.Y() ); + const long nStartX = maStart.X(); + const long nStartY = maStart.Y(); + const long nEndX = maEnd.X(); + const long nEndY = maEnd.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 ) + { + aEnum.X() = nX; + aEnum.Y() = nY; + rEnumLink.Call( &aEnum ); + + 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 ) + { + aEnum.X() = nX; + aEnum.Y() = nY; + rEnumLink.Call( &aEnum ); + + if( nD < 0L ) + nD += nDY2; + else + nD += nDYX, nX += nXInc; + } + } + + // last point + aEnum.X() = nEndX; + aEnum.Y() = nEndY; + rEnumLink.Call( &aEnum ); + } +} diff --git a/tools/source/generic/link.cxx b/tools/source/generic/link.cxx new file mode 100644 index 000000000000..928251306901 --- /dev/null +++ b/tools/source/generic/link.cxx @@ -0,0 +1,58 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_tools.hxx" +#include <tools/link.hxx> + +/************************************************************************* +|* +|* Link::operator==() +|* +|* Beschreibung LINK.SDW +|* Ersterstellung AM 14.02.91 +|* Letzte Aenderung TH 07.11.95 +|* +*************************************************************************/ + +BOOL Link::operator==( const Link& rLink ) const +{ + if ( pFunc == rLink.pFunc ) + { + if ( pFunc ) + { + if ( pInst == rLink.pInst ) + return TRUE; + else + return FALSE; + } + else + return TRUE; + } + else + return FALSE; +} diff --git a/tools/source/generic/makefile.mk b/tools/source/generic/makefile.mk new file mode 100644 index 000000000000..5cdaa02065ae --- /dev/null +++ b/tools/source/generic/makefile.mk @@ -0,0 +1,70 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# 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. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=tools +TARGET=gen + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +EXCEPTIONSFILES = $(SLO)$/poly.obj $(OBJ)$/poly.obj + +SLOFILES= $(SLO)$/toolsin.obj \ + $(SLO)$/b3dtrans.obj \ + $(SLO)$/link.obj \ + $(SLO)$/bigint.obj \ + $(SLO)$/fract.obj \ + $(SLO)$/color.obj \ + $(SLO)$/gen.obj \ + $(SLO)$/config.obj \ + $(SLO)$/poly.obj \ + $(SLO)$/poly2.obj \ + $(SLO)$/svborder.obj \ + $(SLO)$/line.obj + +OBJFILES= $(OBJ)$/toolsin.obj \ + $(OBJ)$/b3dtrans.obj \ + $(OBJ)$/link.obj \ + $(OBJ)$/bigint.obj \ + $(OBJ)$/fract.obj \ + $(OBJ)$/color.obj \ + $(OBJ)$/gen.obj \ + $(OBJ)$/config.obj \ + $(OBJ)$/poly.obj \ + $(OBJ)$/poly2.obj \ + $(OBJ)$/svborder.obj \ + $(OBJ)$/line.obj + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/tools/source/generic/poly.cxx b/tools/source/generic/poly.cxx new file mode 100644 index 000000000000..2290cfdbe7c2 --- /dev/null +++ b/tools/source/generic/poly.cxx @@ -0,0 +1,2375 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_tools.hxx" + +#define _SV_POLY_CXX +#include <osl/endian.h> +#include <tools/bigint.hxx> +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <poly.h> +#include <tools/line.hxx> +#ifndef _VECTOR2D_H +#include <tools/vector2d.hxx> +#endif +#ifndef _POLY_HXX +#include <tools/poly.hxx> +#endif +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/vector/b2dvector.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/curve/b2dcubicbezier.hxx> + +#include <vector> +#include <iterator> +#include <algorithm> +#include <cstring> +#include <limits.h> +#include <cmath> + + +// ======================================================================= + +DBG_NAME( Polygon ) + +// ----------------------------------------------------------------------- + +#define EDGE_LEFT 1 +#define EDGE_TOP 2 +#define EDGE_RIGHT 4 +#define EDGE_BOTTOM 8 +#define EDGE_HORZ (EDGE_RIGHT | EDGE_LEFT) +#define EDGE_VERT (EDGE_TOP | EDGE_BOTTOM) +#define SMALL_DVALUE 0.0000001 +#define FSQRT2 1.4142135623730950488016887242097 + +// ----------------------------------------------------------------------- + +static ImplPolygonData aStaticImplPolygon = +{ + NULL, NULL, 0, 0 +}; + +// ======================================================================= + +ImplPolygon::ImplPolygon( USHORT nInitSize, BOOL bFlags ) +{ + if ( nInitSize ) + { + mpPointAry = (Point*)new char[(ULONG)nInitSize*sizeof(Point)]; + memset( mpPointAry, 0, (ULONG)nInitSize*sizeof(Point) ); + } + else + mpPointAry = NULL; + + if( bFlags ) + { + mpFlagAry = new BYTE[ nInitSize ]; + memset( mpPointAry, 0, nInitSize ); + } + else + mpFlagAry = NULL; + + mnRefCount = 1; + mnPoints = nInitSize; +} + +// ----------------------------------------------------------------------- + +ImplPolygon::ImplPolygon( const ImplPolygon& rImpPoly ) +{ + if ( rImpPoly.mnPoints ) + { + mpPointAry = (Point*)new char[(ULONG)rImpPoly.mnPoints*sizeof(Point)]; + memcpy( mpPointAry, rImpPoly.mpPointAry, (ULONG)rImpPoly.mnPoints*sizeof(Point) ); + + if( rImpPoly.mpFlagAry ) + { + mpFlagAry = new BYTE[ rImpPoly.mnPoints ]; + memcpy( mpFlagAry, rImpPoly.mpFlagAry, rImpPoly.mnPoints ); + } + else + mpFlagAry = NULL; + } + else + { + mpPointAry = NULL; + mpFlagAry = NULL; + } + + mnRefCount = 1; + mnPoints = rImpPoly.mnPoints; +} + +// ----------------------------------------------------------------------- + +ImplPolygon::ImplPolygon( USHORT nInitSize, const Point* pInitAry, const BYTE* pInitFlags ) +{ + if ( nInitSize ) + { + mpPointAry = (Point*)new char[(ULONG)nInitSize*sizeof(Point)]; + memcpy( mpPointAry, pInitAry, (ULONG)nInitSize*sizeof( Point ) ); + + if( pInitFlags ) + { + mpFlagAry = new BYTE[ nInitSize ]; + memcpy( mpFlagAry, pInitFlags, nInitSize ); + } + else + mpFlagAry = NULL; + } + else + { + mpPointAry = NULL; + mpFlagAry = NULL; + } + + mnRefCount = 1; + mnPoints = nInitSize; +} + +// ----------------------------------------------------------------------- + +ImplPolygon::~ImplPolygon() +{ + if ( mpPointAry ) + { + delete[] (char*) mpPointAry; + } + + if( mpFlagAry ) + delete[] mpFlagAry; +} + +// ----------------------------------------------------------------------- + +void ImplPolygon::ImplSetSize( USHORT nNewSize, BOOL bResize ) +{ + if( mnPoints == nNewSize ) + return; + + Point* pNewAry; + + if ( nNewSize ) + { + pNewAry = (Point*)new char[(ULONG)nNewSize*sizeof(Point)]; + + if ( bResize ) + { + // Alte Punkte kopieren + if ( mnPoints < nNewSize ) + { + // Neue Punkte mit 0 initialisieren + memset( pNewAry+mnPoints, 0, (ULONG)(nNewSize-mnPoints)*sizeof(Point) ); + if ( mpPointAry ) + memcpy( pNewAry, mpPointAry, mnPoints*sizeof(Point) ); + } + else + { + if ( mpPointAry ) + memcpy( pNewAry, mpPointAry, (ULONG)nNewSize*sizeof(Point) ); + } + } + } + else + pNewAry = NULL; + + if ( mpPointAry ) + delete[] (char*) mpPointAry; + + // ggf. FlagArray beruecksichtigen + if( mpFlagAry ) + { + BYTE* pNewFlagAry; + + if( nNewSize ) + { + pNewFlagAry = new BYTE[ nNewSize ]; + + if( bResize ) + { + // Alte Flags kopieren + if ( mnPoints < nNewSize ) + { + // Neue Punkte mit 0 initialisieren + memset( pNewFlagAry+mnPoints, 0, nNewSize-mnPoints ); + memcpy( pNewFlagAry, mpFlagAry, mnPoints ); + } + else + memcpy( pNewFlagAry, mpFlagAry, nNewSize ); + } + } + else + pNewFlagAry = NULL; + + delete[] mpFlagAry; + mpFlagAry = pNewFlagAry; + } + + mpPointAry = pNewAry; + mnPoints = nNewSize; +} + +// ----------------------------------------------------------------------- + +void ImplPolygon::ImplSplit( USHORT nPos, USHORT nSpace, ImplPolygon* pInitPoly ) +{ + const ULONG nSpaceSize = nSpace * sizeof( Point ); + const USHORT nNewSize = mnPoints + nSpace; + + if( nPos >= mnPoints ) + { + // Hinten anhaengen + nPos = mnPoints; + ImplSetSize( nNewSize, TRUE ); + + if( pInitPoly ) + { + memcpy( mpPointAry + nPos, pInitPoly->mpPointAry, nSpaceSize ); + + if( pInitPoly->mpFlagAry ) + memcpy( mpFlagAry + nPos, pInitPoly->mpFlagAry, nSpace ); + } + } + else + { + // PointArray ist in diesem Zweig immer vorhanden + const USHORT nSecPos = nPos + nSpace; + const USHORT nRest = mnPoints - nPos; + + Point* pNewAry = (Point*) new char[ (ULONG) nNewSize * sizeof( Point ) ]; + + memcpy( pNewAry, mpPointAry, nPos * sizeof( Point ) ); + + if( pInitPoly ) + memcpy( pNewAry + nPos, pInitPoly->mpPointAry, nSpaceSize ); + else + memset( pNewAry + nPos, 0, nSpaceSize ); + + memcpy( pNewAry + nSecPos, mpPointAry + nPos, nRest * sizeof( Point ) ); + delete[] (char*) mpPointAry; + + // ggf. FlagArray beruecksichtigen + if( mpFlagAry ) + { + BYTE* pNewFlagAry = new BYTE[ nNewSize ]; + + memcpy( pNewFlagAry, mpFlagAry, nPos ); + + if( pInitPoly && pInitPoly->mpFlagAry ) + memcpy( pNewFlagAry + nPos, pInitPoly->mpFlagAry, nSpace ); + else + memset( pNewFlagAry + nPos, 0, nSpace ); + + memcpy( pNewFlagAry + nSecPos, mpFlagAry + nPos, nRest ); + delete[] mpFlagAry; + mpFlagAry = pNewFlagAry; + } + + mpPointAry = pNewAry; + mnPoints = nNewSize; + } +} + +// ----------------------------------------------------------------------- + +void ImplPolygon::ImplRemove( USHORT nPos, USHORT nCount ) +{ + const USHORT nRemoveCount = Min( (USHORT) ( mnPoints - nPos ), (USHORT) nCount ); + + if( nRemoveCount ) + { + const USHORT nNewSize = mnPoints - nRemoveCount; + const USHORT nSecPos = nPos + nRemoveCount; + const USHORT nRest = mnPoints - nSecPos; + + Point* pNewAry = (Point*) new char[ (ULONG) nNewSize * sizeof( Point ) ]; + + memcpy( pNewAry, mpPointAry, nPos * sizeof( Point ) ); + memcpy( pNewAry + nPos, mpPointAry + nSecPos, nRest * sizeof( Point ) ); + + delete[] (char*) mpPointAry; + + // ggf. FlagArray beruecksichtigen + if( mpFlagAry ) + { + BYTE* pNewFlagAry = new BYTE[ nNewSize ]; + + memcpy( pNewFlagAry, mpFlagAry, nPos ); + memcpy( pNewFlagAry + nPos, mpFlagAry + nSecPos, nRest ); + delete[] mpFlagAry; + mpFlagAry = pNewFlagAry; + } + + mpPointAry = pNewAry; + mnPoints = nNewSize; + } +} + +// ----------------------------------------------------------------------- + +void ImplPolygon::ImplCreateFlagArray() +{ + if( !mpFlagAry ) + { + mpFlagAry = new BYTE[ mnPoints ]; + memset( mpFlagAry, 0, mnPoints ); + } +} + +// ======================================================================= + +inline void Polygon::ImplMakeUnique() +{ + // Falls noch andere Referenzen bestehen, dann kopieren + if ( mpImplPolygon->mnRefCount != 1 ) + { + if ( mpImplPolygon->mnRefCount ) + mpImplPolygon->mnRefCount--; + mpImplPolygon = new ImplPolygon( *mpImplPolygon ); + } +} + +// ----------------------------------------------------------------------- + +inline double ImplGetAngle( const Point& rCenter, const Point& rPt ) +{ + const long nDX = rPt.X() - rCenter.X(); + return( atan2( -rPt.Y() + rCenter.Y(), ( ( nDX == 0L ) ? 0.000000001 : nDX ) ) ); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon() +{ + DBG_CTOR( Polygon, NULL ); + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( USHORT nSize ) +{ + DBG_CTOR( Polygon, NULL ); + + if ( nSize ) + mpImplPolygon = new ImplPolygon( nSize ); + else + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( USHORT nPoints, const Point* pPtAry, const BYTE* pFlagAry ) +{ + DBG_CTOR( Polygon, NULL ); + + if( nPoints ) + mpImplPolygon = new ImplPolygon( nPoints, pPtAry, pFlagAry ); + else + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Polygon& rPoly ) +{ + DBG_CTOR( Polygon, NULL ); + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + DBG_ASSERT( rPoly.mpImplPolygon->mnRefCount < 0xFFFFFFFE, "Polygon: RefCount overflow" ); + + mpImplPolygon = rPoly.mpImplPolygon; + if ( mpImplPolygon->mnRefCount ) + mpImplPolygon->mnRefCount++; +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Rectangle& rRect ) +{ + DBG_CTOR( Polygon, NULL ); + + if ( rRect.IsEmpty() ) + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); + else + { + mpImplPolygon = new ImplPolygon( 5 ); + mpImplPolygon->mpPointAry[0] = rRect.TopLeft(); + mpImplPolygon->mpPointAry[1] = rRect.TopRight(); + mpImplPolygon->mpPointAry[2] = rRect.BottomRight(); + mpImplPolygon->mpPointAry[3] = rRect.BottomLeft(); + mpImplPolygon->mpPointAry[4] = rRect.TopLeft(); + } +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Rectangle& rRect, ULONG nHorzRound, ULONG nVertRound ) +{ + DBG_CTOR( Polygon, NULL ); + + if ( rRect.IsEmpty() ) + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); + else + { + Rectangle aRect( rRect ); + aRect.Justify(); // SJ: i9140 + + nHorzRound = Min( nHorzRound, (ULONG) labs( aRect.GetWidth() >> 1 ) ); + nVertRound = Min( nVertRound, (ULONG) labs( aRect.GetHeight() >> 1 ) ); + + if( !nHorzRound && !nVertRound ) + { + mpImplPolygon = new ImplPolygon( 5 ); + mpImplPolygon->mpPointAry[0] = aRect.TopLeft(); + mpImplPolygon->mpPointAry[1] = aRect.TopRight(); + mpImplPolygon->mpPointAry[2] = aRect.BottomRight(); + mpImplPolygon->mpPointAry[3] = aRect.BottomLeft(); + mpImplPolygon->mpPointAry[4] = aRect.TopLeft(); + } + else + { + const Point aTL( aRect.Left() + nHorzRound, aRect.Top() + nVertRound ); + const Point aTR( aRect.Right() - nHorzRound, aRect.Top() + nVertRound ); + const Point aBR( aRect.Right() - nHorzRound, aRect.Bottom() - nVertRound ); + const Point aBL( aRect.Left() + nHorzRound, aRect.Bottom() - nVertRound ); + Polygon* pEllipsePoly = new Polygon( Point(), nHorzRound, nVertRound ); + USHORT i, nEnd, nSize4 = pEllipsePoly->GetSize() >> 2; + + mpImplPolygon = new ImplPolygon( pEllipsePoly->GetSize() + 1 ); + + const Point* pSrcAry = pEllipsePoly->GetConstPointAry(); + Point* pDstAry = mpImplPolygon->mpPointAry; + + for( i = 0, nEnd = nSize4; i < nEnd; i++ ) + ( pDstAry[ i ] = pSrcAry[ i ] ) += aTR; + + for( nEnd = nEnd + nSize4; i < nEnd; i++ ) + ( pDstAry[ i ] = pSrcAry[ i ] ) += aTL; + + for( nEnd = nEnd + nSize4; i < nEnd; i++ ) + ( pDstAry[ i ] = pSrcAry[ i ] ) += aBL; + + for( nEnd = nEnd + nSize4; i < nEnd; i++ ) + ( pDstAry[ i ] = pSrcAry[ i ] ) += aBR; + + pDstAry[ nEnd ] = pDstAry[ 0 ]; + delete pEllipsePoly; + } + } +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Point& rCenter, long nRadX, long nRadY, USHORT nPoints ) +{ + DBG_CTOR( Polygon, NULL ); + + if( nRadX && nRadY ) + { + // Default berechnen (abhaengig von Groesse) + if( !nPoints ) + { + nPoints = (USHORT) ( F_PI * ( 1.5 * ( nRadX + nRadY ) - + sqrt( (double) labs( nRadX * nRadY ) ) ) ); + + nPoints = (USHORT) MinMax( nPoints, 32, 256 ); + + if( ( nRadX > 32 ) && ( nRadY > 32 ) && ( nRadX + nRadY ) < 8192 ) + nPoints >>= 1; + } + + // Anzahl der Punkte auf durch 4 teilbare Zahl aufrunden + mpImplPolygon = new ImplPolygon( nPoints = (nPoints + 3) & ~3 ); + + Point* pPt; + USHORT i; + USHORT nPoints2 = nPoints >> 1; + USHORT nPoints4 = nPoints >> 2; + double nAngle; + double nAngleStep = F_PI2 / ( nPoints4 - 1 ); + + for( i=0, nAngle = 0.0; i < nPoints4; i++, nAngle += nAngleStep ) + { + long nX = FRound( nRadX * cos( nAngle ) ); + long nY = FRound( -nRadY * sin( nAngle ) ); + + pPt = &(mpImplPolygon->mpPointAry[i]); + pPt->X() = nX + rCenter.X(); + pPt->Y() = nY + rCenter.Y(); + pPt = &(mpImplPolygon->mpPointAry[nPoints2-i-1]); + pPt->X() = -nX + rCenter.X(); + pPt->Y() = nY + rCenter.Y(); + pPt = &(mpImplPolygon->mpPointAry[i+nPoints2]); + pPt->X() = -nX + rCenter.X(); + pPt->Y() = -nY + rCenter.Y(); + pPt = &(mpImplPolygon->mpPointAry[nPoints-i-1]); + pPt->X() = nX + rCenter.X(); + pPt->Y() = -nY + rCenter.Y(); + } + } + else + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Rectangle& rBound, + const Point& rStart, const Point& rEnd, PolyStyle eStyle ) +{ + DBG_CTOR( Polygon, NULL ); + + const long nWidth = rBound.GetWidth(); + const long nHeight = rBound.GetHeight(); + + if( ( nWidth > 1 ) && ( nHeight > 1 ) ) + { + const Point aCenter( rBound.Center() ); + const long nRadX = aCenter.X() - rBound.Left(); + const long nRadY = aCenter.Y() - rBound.Top(); + USHORT nPoints; + + nPoints = (USHORT) ( F_PI * ( 1.5 * ( nRadX + nRadY ) - + sqrt( (double) labs( nRadX * nRadY ) ) ) ); + + nPoints = (USHORT) MinMax( nPoints, 32, 256 ); + + if( ( nRadX > 32 ) && ( nRadY > 32 ) && ( nRadX + nRadY ) < 8192 ) + nPoints >>= 1; + + // Winkel berechnen + const double fRadX = nRadX; + const double fRadY = nRadY; + const double fCenterX = aCenter.X(); + const double fCenterY = aCenter.Y(); + double fStart = ImplGetAngle( aCenter, rStart ); + double fEnd = ImplGetAngle( aCenter, rEnd ); + double fDiff = fEnd - fStart; + double fStep; + USHORT nStart; + USHORT nEnd; + + if( fDiff < 0. ) + fDiff += F_2PI; + + // Punktanzahl proportional verkleinern ( fDiff / (2PI) ); + // ist eingentlich nur fuer einen Kreis richtig; wir + // machen es hier aber trotzdem + nPoints = Max( (USHORT) ( ( fDiff * 0.1591549 ) * nPoints ), (USHORT) 16 ); + fStep = fDiff / ( nPoints - 1 ); + + if( POLY_PIE == eStyle ) + { + const Point aCenter2( FRound( fCenterX ), FRound( fCenterY ) ); + + nStart = 1; + nEnd = nPoints + 1; + mpImplPolygon = new ImplPolygon( nPoints + 2 ); + mpImplPolygon->mpPointAry[ 0 ] = aCenter2; + mpImplPolygon->mpPointAry[ nEnd ] = aCenter2; + } + else + { + mpImplPolygon = new ImplPolygon( ( POLY_CHORD == eStyle ) ? ( nPoints + 1 ) : nPoints ); + nStart = 0; + nEnd = nPoints; + } + + for(; nStart < nEnd; nStart++, fStart += fStep ) + { + Point& rPt = mpImplPolygon->mpPointAry[ nStart ]; + + rPt.X() = FRound( fCenterX + fRadX * cos( fStart ) ); + rPt.Y() = FRound( fCenterY - fRadY * sin( fStart ) ); + } + + if( POLY_CHORD == eStyle ) + mpImplPolygon->mpPointAry[ nPoints ] = mpImplPolygon->mpPointAry[ 0 ]; + } + else + mpImplPolygon = (ImplPolygon*) &aStaticImplPolygon; +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Point& rBezPt1, const Point& rCtrlPt1, + const Point& rBezPt2, const Point& rCtrlPt2, + USHORT nPoints ) +{ + DBG_CTOR( Polygon, NULL ); + + nPoints = ( 0 == nPoints ) ? 25 : ( ( nPoints < 2 ) ? 2 : nPoints ); + + const double fInc = 1.0 / ( nPoints - 1 ); + double fK_1 = 0.0, fK1_1 = 1.0; + double fK_2, fK_3, fK1_2, fK1_3, fK12, fK21; + const double fX0 = rBezPt1.X(); + const double fY0 = rBezPt1.Y(); + const double fX1 = 3.0 * rCtrlPt1.X(); + const double fY1 = 3.0 * rCtrlPt1.Y(); + const double fX2 = 3.0 * rCtrlPt2.X();; + const double fY2 = 3.0 * rCtrlPt2.Y();; + const double fX3 = rBezPt2.X(); + const double fY3 = rBezPt2.Y(); + + mpImplPolygon = new ImplPolygon( nPoints ); + + for( USHORT i = 0; i < nPoints; i++, fK_1 += fInc, fK1_1 -= fInc ) + { + Point& rPt = mpImplPolygon->mpPointAry[ i ]; + + fK_2 = fK_1, fK_3 = ( fK_2 *= fK_1 ), fK_3 *= fK_1; + fK1_2 = fK1_1, fK1_3 = ( fK1_2 *= fK1_1 ), fK1_3 *= fK1_1; + fK12 = fK_1 * fK1_2, fK21 = fK_2 * fK1_1; + + rPt.X() = FRound( fK1_3 * fX0 + fK12 * fX1 + fK21 * fX2 + fK_3 * fX3 ); + rPt.Y() = FRound( fK1_3 * fY0 + fK12 * fY1 + fK21 * fY2 + fK_3 * fY3 ); + } +} + +// ----------------------------------------------------------------------- + +Polygon::~Polygon() +{ + DBG_DTOR( Polygon, NULL ); + + // Wenn es keine statischen ImpDaten sind, dann loeschen, wenn es + // die letzte Referenz ist, sonst Referenzcounter decrementieren + if ( mpImplPolygon->mnRefCount ) + { + if ( mpImplPolygon->mnRefCount > 1 ) + mpImplPolygon->mnRefCount--; + else + delete mpImplPolygon; + } +} + +// ----------------------------------------------------------------------- + +Point* Polygon::ImplGetPointAry() +{ + DBG_CHKTHIS( Polygon, NULL ); + + ImplMakeUnique(); + return (Point*)mpImplPolygon->mpPointAry; +} + +// ----------------------------------------------------------------------- + +BYTE* Polygon::ImplGetFlagAry() +{ + DBG_CHKTHIS( Polygon, NULL ); + + ImplMakeUnique(); + mpImplPolygon->ImplCreateFlagArray(); + return mpImplPolygon->mpFlagAry; +} + +// ----------------------------------------------------------------------- + +const Point* Polygon::GetConstPointAry() const +{ + DBG_CHKTHIS( Polygon, NULL ); + return (Point*)mpImplPolygon->mpPointAry; +} + +// ----------------------------------------------------------------------- + +const BYTE* Polygon::GetConstFlagAry() const +{ + DBG_CHKTHIS( Polygon, NULL ); + return mpImplPolygon->mpFlagAry; +} + +// ----------------------------------------------------------------------- + +void Polygon::SetPoint( const Point& rPt, USHORT nPos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::SetPoint(): nPos >= nPoints" ); + + ImplMakeUnique(); + mpImplPolygon->mpPointAry[nPos] = rPt; +} + +// ----------------------------------------------------------------------- + +void Polygon::SetFlags( USHORT nPos, PolyFlags eFlags ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::SetFlags(): nPos >= nPoints" ); + + // we do only want to create the flag array if there + // is at least one flag different to POLY_NORMAL + if ( mpImplPolygon || ( eFlags != POLY_NORMAL ) ) + { + ImplMakeUnique(); + mpImplPolygon->ImplCreateFlagArray(); + mpImplPolygon->mpFlagAry[ nPos ] = (BYTE) eFlags; + } +} + +// ----------------------------------------------------------------------- + +const Point& Polygon::GetPoint( USHORT nPos ) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::GetPoint(): nPos >= nPoints" ); + + return mpImplPolygon->mpPointAry[nPos]; +} + +// ----------------------------------------------------------------------- + +PolyFlags Polygon::GetFlags( USHORT nPos ) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::GetFlags(): nPos >= nPoints" ); + return( mpImplPolygon->mpFlagAry ? + (PolyFlags) mpImplPolygon->mpFlagAry[ nPos ] : + POLY_NORMAL ); +} + +// ----------------------------------------------------------------------- + +sal_Bool Polygon::HasFlags() const +{ + return mpImplPolygon->mpFlagAry != NULL; +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsControl(USHORT nPos) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::GetFlags(): nPos >= nPoints" ); + PolyFlags eFlags = mpImplPolygon->mpFlagAry ? + (PolyFlags) mpImplPolygon->mpFlagAry[ nPos ] : POLY_NORMAL; + + return( POLY_CONTROL == eFlags ); +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsSmooth(USHORT nPos) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::GetFlags(): nPos >= nPoints" ); + PolyFlags eFlags = mpImplPolygon->mpFlagAry ? + (PolyFlags) mpImplPolygon->mpFlagAry[ nPos ] : POLY_NORMAL; + + return( ( POLY_SMOOTH == eFlags ) || ( POLY_SYMMTR == eFlags ) ); +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsRect() const +{ + BOOL bIsRect = FALSE; + if ( mpImplPolygon->mpFlagAry == NULL ) + { + if ( ( ( mpImplPolygon->mnPoints == 5 ) && ( mpImplPolygon->mpPointAry[ 0 ] == mpImplPolygon->mpPointAry[ 4 ] ) ) || + ( mpImplPolygon->mnPoints == 4 ) ) + { + if ( ( mpImplPolygon->mpPointAry[ 0 ].X() == mpImplPolygon->mpPointAry[ 3 ].X() ) && + ( mpImplPolygon->mpPointAry[ 0 ].Y() == mpImplPolygon->mpPointAry[ 1 ].Y() ) && + ( mpImplPolygon->mpPointAry[ 1 ].X() == mpImplPolygon->mpPointAry[ 2 ].X() ) && + ( mpImplPolygon->mpPointAry[ 2 ].Y() == mpImplPolygon->mpPointAry[ 3 ].Y() ) ) + bIsRect = TRUE; + } + } + return bIsRect; +} + +// ----------------------------------------------------------------------- + +void Polygon::SetSize( USHORT nNewSize ) +{ + DBG_CHKTHIS( Polygon, NULL ); + + if( nNewSize != mpImplPolygon->mnPoints ) + { + ImplMakeUnique(); + mpImplPolygon->ImplSetSize( nNewSize ); + } +} + +// ----------------------------------------------------------------------- + +USHORT Polygon::GetSize() const +{ + DBG_CHKTHIS( Polygon, NULL ); + + return mpImplPolygon->mnPoints; +} + +// ----------------------------------------------------------------------- + +void Polygon::Clear() +{ + DBG_CHKTHIS( Polygon, NULL ); + + if ( mpImplPolygon->mnRefCount ) + { + if ( mpImplPolygon->mnRefCount > 1 ) + mpImplPolygon->mnRefCount--; + else + delete mpImplPolygon; + } + + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +double Polygon::CalcDistance( USHORT nP1, USHORT nP2 ) +{ + DBG_ASSERT( nP1 < mpImplPolygon->mnPoints, + "Polygon::CalcDistance(): nPos1 >= nPoints" ); + DBG_ASSERT( nP2 < mpImplPolygon->mnPoints, + "Polygon::CalcDistance(): nPos2 >= nPoints" ); + + const Point& rP1 = mpImplPolygon->mpPointAry[ nP1 ]; + const Point& rP2 = mpImplPolygon->mpPointAry[ nP2 ]; + const double fDx = rP2.X() - rP1.X(); + const double fDy = rP2.Y() - rP1.Y(); + + return sqrt( fDx * fDx + fDy * fDy ); +} + +// ----------------------------------------------------------------------- + +void Polygon::Optimize( ULONG nOptimizeFlags, const PolyOptimizeData* pData ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( !mpImplPolygon->mpFlagAry, "Optimizing could fail with beziers!" ); + + USHORT nSize = mpImplPolygon->mnPoints; + + if( nOptimizeFlags && nSize ) + { + if( nOptimizeFlags & POLY_OPTIMIZE_EDGES ) + { + const Rectangle aBound( GetBoundRect() ); + const double fArea = ( aBound.GetWidth() + aBound.GetHeight() ) * 0.5; + const USHORT nPercent = pData ? pData->GetPercentValue() : 50; + + Optimize( POLY_OPTIMIZE_NO_SAME ); + ImplReduceEdges( *this, fArea, nPercent ); + } + else if( nOptimizeFlags & ( POLY_OPTIMIZE_REDUCE | POLY_OPTIMIZE_NO_SAME ) ) + { + Polygon aNewPoly; + const Point& rFirst = mpImplPolygon->mpPointAry[ 0 ]; + ULONG nReduce; + + if( nOptimizeFlags & ( POLY_OPTIMIZE_REDUCE ) ) + nReduce = pData ? pData->GetAbsValue() : 4UL; + else + nReduce = 0UL; + + while( nSize && ( mpImplPolygon->mpPointAry[ nSize - 1 ] == rFirst ) ) + nSize--; + + if( nSize > 1 ) + { + USHORT nLast = 0, nNewCount = 1; + + aNewPoly.SetSize( nSize ); + aNewPoly[ 0 ] = rFirst; + + for( USHORT i = 1; i < nSize; i++ ) + { + if( ( mpImplPolygon->mpPointAry[ i ] != mpImplPolygon->mpPointAry[ nLast ] ) && + ( !nReduce || ( nReduce < (ULONG) FRound( CalcDistance( nLast, i ) ) ) ) ) + { + aNewPoly[ nNewCount++ ] = mpImplPolygon->mpPointAry[ nLast = i ]; + } + } + + if( nNewCount == 1 ) + aNewPoly.Clear(); + else + aNewPoly.SetSize( nNewCount ); + } + + *this = aNewPoly; + } + + nSize = mpImplPolygon->mnPoints; + + if( nSize > 1 ) + { + if( ( nOptimizeFlags & POLY_OPTIMIZE_CLOSE ) && + ( mpImplPolygon->mpPointAry[ 0 ] != mpImplPolygon->mpPointAry[ nSize - 1 ] ) ) + { + SetSize( mpImplPolygon->mnPoints + 1 ); + mpImplPolygon->mpPointAry[ mpImplPolygon->mnPoints - 1 ] = mpImplPolygon->mpPointAry[ 0 ]; + } + else if( ( nOptimizeFlags & POLY_OPTIMIZE_OPEN ) && + ( mpImplPolygon->mpPointAry[ 0 ] == mpImplPolygon->mpPointAry[ nSize - 1 ] ) ) + { + const Point& rFirst = mpImplPolygon->mpPointAry[ 0 ]; + + while( nSize && ( mpImplPolygon->mpPointAry[ nSize - 1 ] == rFirst ) ) + nSize--; + + SetSize( nSize ); + } + } + } +} + +// ======================================================================= + +/* Recursively subdivide cubic bezier curve via deCasteljau. + + @param rPointIter + Output iterator, where the subdivided polylines are written to. + + @param d + Squared difference of curve to a straight line + + @param P* + Exactly four points, interpreted as support and control points of + a cubic bezier curve. Must be in device coordinates, since stop + criterion is based on the following assumption: the device has a + finite resolution, it is thus sufficient to stop subdivision if the + curve does not deviate more than one pixel from a straight line. + +*/ +static void ImplAdaptiveSubdivide( ::std::back_insert_iterator< ::std::vector< Point > >& rPointIter, + const double old_d2, + int recursionDepth, + const double d2, + const double P1x, const double P1y, + const double P2x, const double P2y, + const double P3x, const double P3y, + const double P4x, const double P4y ) +{ + // Hard limit on recursion depth, empiric number. + enum {maxRecursionDepth=128}; + + // Perform bezier flatness test (lecture notes from R. Schaback, + // Mathematics of Computer-Aided Design, Uni Goettingen, 2000) + // + // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)|| + // 0<=j<=n + // + // What is calculated here is an upper bound to the distance from + // a line through b_0 and b_3 (P1 and P4 in our notation) and the + // curve. We can drop 0 and n from the running indices, since the + // argument of max becomes zero for those cases. + const double fJ1x( P2x - P1x - 1.0/3.0*(P4x - P1x) ); + const double fJ1y( P2y - P1y - 1.0/3.0*(P4y - P1y) ); + const double fJ2x( P3x - P1x - 2.0/3.0*(P4x - P1x) ); + const double fJ2y( P3y - P1y - 2.0/3.0*(P4y - P1y) ); + const double distance2( ::std::max( fJ1x*fJ1x + fJ1y*fJ1y, + fJ2x*fJ2x + fJ2y*fJ2y) ); + + // stop if error measure does not improve anymore. This is a + // safety guard against floating point inaccuracies. + // stop at recursion level 128. This is a safety guard against + // floating point inaccuracies. + // stop if distance from line is guaranteed to be bounded by d + if( old_d2 > d2 && + recursionDepth < maxRecursionDepth && + distance2 >= d2 ) + { + // deCasteljau bezier arc, split at t=0.5 + // Foley/vanDam, p. 508 + const double L1x( P1x ), L1y( P1y ); + const double L2x( (P1x + P2x)*0.5 ), L2y( (P1y + P2y)*0.5 ); + const double Hx ( (P2x + P3x)*0.5 ), Hy ( (P2y + P3y)*0.5 ); + const double L3x( (L2x + Hx)*0.5 ), L3y( (L2y + Hy)*0.5 ); + const double R4x( P4x ), R4y( P4y ); + const double R3x( (P3x + P4x)*0.5 ), R3y( (P3y + P4y)*0.5 ); + const double R2x( (Hx + R3x)*0.5 ), R2y( (Hy + R3y)*0.5 ); + const double R1x( (L3x + R2x)*0.5 ), R1y( (L3y + R2y)*0.5 ); + const double L4x( R1x ), L4y( R1y ); + + // subdivide further + ++recursionDepth; + ImplAdaptiveSubdivide(rPointIter, distance2, recursionDepth, d2, L1x, L1y, L2x, L2y, L3x, L3y, L4x, L4y); + ImplAdaptiveSubdivide(rPointIter, distance2, recursionDepth, d2, R1x, R1y, R2x, R2y, R3x, R3y, R4x, R4y); + } + else + { + // requested resolution reached. + // Add end points to output iterator. + // order is preserved, since this is so to say depth first traversal. + *rPointIter++ = Point( FRound(P1x), FRound(P1y) ); + } +} + +// ======================================================================= + +void Polygon::AdaptiveSubdivide( Polygon& rResult, const double d ) const +{ + if( !mpImplPolygon->mpFlagAry ) + { + rResult = *this; + } + else + { + USHORT i; + USHORT nPts( GetSize() ); + ::std::vector< Point > aPoints; + aPoints.reserve( nPts ); + ::std::back_insert_iterator< ::std::vector< Point > > aPointIter( aPoints ); + + for(i=0; i<nPts;) + { + if( ( i + 3 ) < nPts ) + { + BYTE P1( mpImplPolygon->mpFlagAry[ i ] ); + BYTE P4( mpImplPolygon->mpFlagAry[ i + 3 ] ); + + if( ( POLY_NORMAL == P1 || POLY_SMOOTH == P1 || POLY_SYMMTR == P1 ) && + ( POLY_CONTROL == mpImplPolygon->mpFlagAry[ i + 1 ] ) && + ( POLY_CONTROL == mpImplPolygon->mpFlagAry[ i + 2 ] ) && + ( POLY_NORMAL == P4 || POLY_SMOOTH == P4 || POLY_SYMMTR == P4 ) ) + { + ImplAdaptiveSubdivide( aPointIter, d*d+1.0, 0, d*d, + mpImplPolygon->mpPointAry[ i ].X(), mpImplPolygon->mpPointAry[ i ].Y(), + mpImplPolygon->mpPointAry[ i+1 ].X(), mpImplPolygon->mpPointAry[ i+1 ].Y(), + mpImplPolygon->mpPointAry[ i+2 ].X(), mpImplPolygon->mpPointAry[ i+2 ].Y(), + mpImplPolygon->mpPointAry[ i+3 ].X(), mpImplPolygon->mpPointAry[ i+3 ].Y() ); + i += 3; + continue; + } + } + + *aPointIter++ = mpImplPolygon->mpPointAry[ i++ ]; + } + + // fill result polygon + rResult = Polygon( (USHORT)aPoints.size() ); // ensure sufficient size for copy + ::std::copy(aPoints.begin(), aPoints.end(), rResult.mpImplPolygon->mpPointAry); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::GetIntersection( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + const PolyPolygon aTmp( *this ); + aTmp.GetIntersection( rPolyPoly, rResult ); +} + +// ----------------------------------------------------------------------- + +void Polygon::GetUnion( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + const PolyPolygon aTmp( *this ); + aTmp.GetUnion( rPolyPoly, rResult ); +} + +// ----------------------------------------------------------------------- + +void Polygon::GetDifference( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + const PolyPolygon aTmp( *this ); + aTmp.GetDifference( rPolyPoly, rResult ); +} + +// ----------------------------------------------------------------------- + +void Polygon::GetXOR( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + const PolyPolygon aTmp( *this ); + aTmp.GetXOR( rPolyPoly, rResult ); +} + +// ----------------------------------------------------------------------- + +void Polygon::ImplReduceEdges( Polygon& rPoly, const double& rArea, USHORT nPercent ) +{ + const double fBound = 2000.0 * ( 100 - nPercent ) * 0.01; + USHORT nNumNoChange = 0, nNumRuns = 0; + + while( nNumNoChange < 2 ) + { + USHORT nPntCnt = rPoly.GetSize(), nNewPos = 0; + Polygon aNewPoly( nPntCnt ); + BOOL bChangeInThisRun = FALSE; + + for( USHORT n = 0; n < nPntCnt; n++ ) + { + BOOL bDeletePoint = FALSE; + + if( ( n + nNumRuns ) % 2 ) + { + USHORT nIndPrev = !n ? nPntCnt - 1 : n - 1; + USHORT nIndPrevPrev = !nIndPrev ? nPntCnt - 1 : nIndPrev - 1; + USHORT nIndNext = ( n == nPntCnt-1 ) ? 0 : n + 1; + USHORT nIndNextNext = ( nIndNext == nPntCnt - 1 ) ? 0 : nIndNext + 1; + Vector2D aVec1( rPoly[ nIndPrev ] ); aVec1 -= rPoly[ nIndPrevPrev ]; + Vector2D aVec2( rPoly[ n ] ); aVec2 -= rPoly[ nIndPrev ]; + Vector2D aVec3( rPoly[ nIndNext ] ); aVec3 -= rPoly[ n ]; + Vector2D aVec4( rPoly[ nIndNextNext ] ); aVec4 -= rPoly[ nIndNext ]; + double fDist1 = aVec1.GetLength(), fDist2 = aVec2.GetLength(); + double fDist3 = aVec3.GetLength(), fDist4 = aVec4.GetLength(); + double fTurnB = aVec2.Normalize().Scalar( aVec3.Normalize() ); + + if( fabs( fTurnB ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnB ) > ( 1.0 - SMALL_DVALUE ) ) + bDeletePoint = TRUE; + else + { + Vector2D aVecB( rPoly[ nIndNext ] ); + double fDistB = ( aVecB -= rPoly[ nIndPrev ] ).GetLength(); + double fLenWithB = fDist2 + fDist3; + double fLenFact = ( fDistB != 0.0 ) ? fLenWithB / fDistB : 1.0; + double fTurnPrev = aVec1.Normalize().Scalar( aVec2 ); + double fTurnNext = aVec3.Scalar( aVec4.Normalize() ); + double fGradPrev, fGradB, fGradNext; + + if( fabs( fTurnPrev ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnPrev ) > ( 1.0 - SMALL_DVALUE ) ) + fGradPrev = 0.0; + else + fGradPrev = acos( fTurnPrev ) / ( aVec1.IsNegative( aVec2 ) ? -F_PI180 : F_PI180 ); + + fGradB = acos( fTurnB ) / ( aVec2.IsNegative( aVec3 ) ? -F_PI180 : F_PI180 ); + + if( fabs( fTurnNext ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnNext ) > ( 1.0 - SMALL_DVALUE ) ) + fGradNext = 0.0; + else + fGradNext = acos( fTurnNext ) / ( aVec3.IsNegative( aVec4 ) ? -F_PI180 : F_PI180 ); + + if( ( fGradPrev > 0.0 && fGradB < 0.0 && fGradNext > 0.0 ) || + ( fGradPrev < 0.0 && fGradB > 0.0 && fGradNext < 0.0 ) ) + { + if( ( fLenFact < ( FSQRT2 + SMALL_DVALUE ) ) && + ( ( ( fDist1 + fDist4 ) / ( fDist2 + fDist3 ) ) * 2000.0 ) > fBound ) + { + bDeletePoint = TRUE; + } + } + else + { + double fRelLen = 1.0 - sqrt( fDistB / rArea ); + + if( fRelLen < 0.0 ) + fRelLen = 0.0; + else if( fRelLen > 1.0 ) + fRelLen = 1.0; + + if( ( (UINT32) ( ( ( fLenFact - 1.0 ) * 1000000.0 ) + 0.5 ) < fBound ) && + ( fabs( fGradB ) <= ( fRelLen * fBound * 0.01 ) ) ) + { + bDeletePoint = TRUE; + } + } + } + } + + if( !bDeletePoint ) + aNewPoly[ nNewPos++ ] = rPoly[ n ]; + else + bChangeInThisRun = TRUE; + } + + if( bChangeInThisRun && nNewPos ) + { + aNewPoly.SetSize( nNewPos ); + rPoly = aNewPoly; + nNumNoChange = 0; + } + else + nNumNoChange++; + + nNumRuns++; + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Move( long nHorzMove, long nVertMove ) +{ + DBG_CHKTHIS( Polygon, NULL ); + + // Diese Abfrage sollte man fuer die DrawEngine durchfuehren + if ( !nHorzMove && !nVertMove ) + return; + + ImplMakeUnique(); + + // Punkte verschieben + USHORT nCount = mpImplPolygon->mnPoints; + for ( USHORT i = 0; i < nCount; i++ ) + { + Point* pPt = &(mpImplPolygon->mpPointAry[i]); + pPt->X() += nHorzMove; + pPt->Y() += nVertMove; + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Translate(const Point& rTrans) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + for ( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + mpImplPolygon->mpPointAry[ i ] += rTrans; +} + +// ----------------------------------------------------------------------- + +void Polygon::Scale( double fScaleX, double fScaleY ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + for ( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPnt = mpImplPolygon->mpPointAry[i]; + rPnt.X() = (long) ( fScaleX * rPnt.X() ); + rPnt.Y() = (long) ( fScaleY * rPnt.Y() ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Rotate( const Point& rCenter, USHORT nAngle10 ) +{ + DBG_CHKTHIS( Polygon, NULL ); + nAngle10 %= 3600; + + if( nAngle10 ) + { + const double fAngle = F_PI1800 * nAngle10; + Rotate( rCenter, sin( fAngle ), cos( fAngle ) ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Rotate( const Point& rCenter, double fSin, double fCos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + long nX, nY; + long nCenterX = rCenter.X(); + long nCenterY = rCenter.Y(); + + for( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPt = mpImplPolygon->mpPointAry[ i ]; + + nX = rPt.X() - nCenterX; + nY = rPt.Y() - nCenterY; + rPt.X() = (long) FRound( fCos * nX + fSin * nY ) + nCenterX; + rPt.Y() = -(long) FRound( fSin * nX - fCos * nY ) + nCenterY; + } +} + +// ----------------------------------------------------------------------- + +void Polygon::SlantX( long nYRef, double fSin, double fCos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + for( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPnt = mpImplPolygon->mpPointAry[ i ]; + const long nDy = rPnt.Y() - nYRef; + + rPnt.X() += (long)( fSin * nDy ); + rPnt.Y() = nYRef + (long)( fCos * nDy ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::SlantY( long nXRef, double fSin, double fCos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + for( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPnt = mpImplPolygon->mpPointAry[ i ]; + const long nDx = rPnt.X() - nXRef; + + rPnt.X() = nXRef + (long)( fCos * nDx ); + rPnt.Y() -= (long)( fSin * nDx ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Distort( const Rectangle& rRefRect, const Polygon& rDistortedRect ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + long Xr, Wr, X1, X2, X3, X4; + long Yr, Hr, Y1, Y2, Y3, Y4; + double fTx, fTy, fUx, fUy; + + Xr = rRefRect.Left(); + Yr = rRefRect.Top(); + Wr = rRefRect.GetWidth(); + Hr = rRefRect.GetHeight(); + + if( Wr && Hr ) + { + DBG_ASSERT( rDistortedRect.mpImplPolygon->mnPoints >= 4, "Distort rect too small!" ); + + X1 = rDistortedRect[0].X(); + Y1 = rDistortedRect[0].Y(); + X2 = rDistortedRect[1].X(); + Y2 = rDistortedRect[1].Y(); + X3 = rDistortedRect[3].X(); + Y3 = rDistortedRect[3].Y(); + X4 = rDistortedRect[2].X(); + Y4 = rDistortedRect[2].Y(); + + for( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPnt = mpImplPolygon->mpPointAry[ i ]; + + fTx = (double)( rPnt.X() - Xr) / Wr; + fTy = (double)( rPnt.Y() - Yr) / Hr; + fUx = 1.0 - fTx; + fUy = 1.0 - fTy; + + rPnt.X() = (long) ( fUy * (fUx * X1 + fTx * X2) + fTy * (fUx * X3 + fTx * X4) ); + rPnt.Y() = (long) ( fUx * (fUy * Y1 + fTy * Y3) + fTx * (fUy * Y2 + fTy * Y4) ); + } + } +} + +// ----------------------------------------------------------------------- + +class ImplPointFilter +{ +public: + virtual void LastPoint() = 0; + virtual void Input( const Point& rPoint ) = 0; +}; + +class ImplPolygonPointFilter : public ImplPointFilter +{ +public: + ImplPolygon* mpPoly; // Nicht loeschen, wird dem Polygon zugewiesen + USHORT mnSize; + + ImplPolygonPointFilter( USHORT nDestSize ) : + mnSize( 0 ) + { + mpPoly = new ImplPolygon( nDestSize ); + } + + virtual void LastPoint(); + virtual void Input( const Point& rPoint ); +}; + +void ImplPolygonPointFilter::Input( const Point& rPoint ) +{ + if ( !mnSize || (rPoint != mpPoly->mpPointAry[mnSize-1]) ) + { + mnSize++; + if ( mnSize > mpPoly->mnPoints ) + mpPoly->ImplSetSize( mnSize ); + mpPoly->mpPointAry[mnSize-1] = rPoint; + } +} + +void ImplPolygonPointFilter::LastPoint() +{ + if ( mnSize < mpPoly->mnPoints ) + mpPoly->ImplSetSize( mnSize ); +}; + +class ImplEdgePointFilter : public ImplPointFilter +{ + Point maFirstPoint; + Point maLastPoint; + ImplPointFilter& mrNextFilter; + const long mnLow; + const long mnHigh; + const int mnEdge; + int mnLastOutside; + BOOL mbFirst; + +public: + ImplEdgePointFilter( int nEdge, long nLow, long nHigh, + ImplPointFilter& rNextFilter ) : + mrNextFilter( rNextFilter ), + mnLow( nLow ), + mnHigh( nHigh ), + mnEdge( nEdge ), + mbFirst( TRUE ) + { + } + + Point EdgeSection( const Point& rPoint, int nEdge ) const; + int VisibleSide( const Point& rPoint ) const; + int IsPolygon() const + { return maFirstPoint == maLastPoint; } + + virtual void Input( const Point& rPoint ); + virtual void LastPoint(); +}; + +inline int ImplEdgePointFilter::VisibleSide( const Point& rPoint ) const +{ + if ( mnEdge & EDGE_HORZ ) + { + return rPoint.X() < mnLow ? EDGE_LEFT : + rPoint.X() > mnHigh ? EDGE_RIGHT : 0; + } + else + { + return rPoint.Y() < mnLow ? EDGE_TOP : + rPoint.Y() > mnHigh ? EDGE_BOTTOM : 0; + } +} + +Point ImplEdgePointFilter::EdgeSection( const Point& rPoint, int nEdge ) const +{ + long lx = maLastPoint.X(); + long ly = maLastPoint.Y(); + long md = rPoint.X() - lx; + long mn = rPoint.Y() - ly; + long nNewX; + long nNewY; + + if ( nEdge & EDGE_VERT ) + { + nNewY = (nEdge == EDGE_TOP) ? mnLow : mnHigh; + long dy = nNewY - ly; + if ( !md ) + nNewX = lx; + else if ( (LONG_MAX / Abs(md)) >= Abs(dy) ) + nNewX = (dy * md) / mn + lx; + else + { + BigInt ady = dy; + ady *= md; + if( ady.IsNeg() ) + if( mn < 0 ) + ady += mn/2; + else + ady -= (mn-1)/2; + else + if( mn < 0 ) + ady -= (mn+1)/2; + else + ady += mn/2; + ady /= mn; + nNewX = (long)ady + lx; + } + } + else + { + nNewX = (nEdge == EDGE_LEFT) ? mnLow : mnHigh; + long dx = nNewX - lx; + if ( !mn ) + nNewY = ly; + else if ( (LONG_MAX / Abs(mn)) >= Abs(dx) ) + nNewY = (dx * mn) / md + ly; + else + { + BigInt adx = dx; + adx *= mn; + if( adx.IsNeg() ) + if( md < 0 ) + adx += md/2; + else + adx -= (md-1)/2; + else + if( md < 0 ) + adx -= (md+1)/2; + else + adx += md/2; + adx /= md; + nNewY = (long)adx + ly; + } + } + + return Point( nNewX, nNewY ); +} + +void ImplEdgePointFilter::Input( const Point& rPoint ) +{ + int nOutside = VisibleSide( rPoint ); + + if ( mbFirst ) + { + maFirstPoint = rPoint; + mbFirst = FALSE; + if ( !nOutside ) + mrNextFilter.Input( rPoint ); + } + else if ( rPoint == maLastPoint ) + return; + else if ( !nOutside ) + { + if ( mnLastOutside ) + mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) ); + mrNextFilter.Input( rPoint ); + } + else if ( !mnLastOutside ) + mrNextFilter.Input( EdgeSection( rPoint, nOutside ) ); + else if ( nOutside != mnLastOutside ) + { + mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) ); + mrNextFilter.Input( EdgeSection( rPoint, nOutside ) ); + } + + maLastPoint = rPoint; + mnLastOutside = nOutside; +} + +void ImplEdgePointFilter::LastPoint() +{ + if ( !mbFirst ) + { + int nOutside = VisibleSide( maFirstPoint ); + + if ( nOutside != mnLastOutside ) + Input( maFirstPoint ); + mrNextFilter.LastPoint(); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Clip( const Rectangle& rRect, BOOL bPolygon ) +{ + // #105251# Justify rect befor edge filtering + Rectangle aJustifiedRect( rRect ); + aJustifiedRect.Justify(); + + USHORT nSourceSize = mpImplPolygon->mnPoints; + ImplPolygonPointFilter aPolygon( nSourceSize ); + ImplEdgePointFilter aHorzFilter( EDGE_HORZ, aJustifiedRect.Left(), aJustifiedRect.Right(), + aPolygon ); + ImplEdgePointFilter aVertFilter( EDGE_VERT, aJustifiedRect.Top(), aJustifiedRect.Bottom(), + aHorzFilter ); + + for ( USHORT i = 0; i < nSourceSize; i++ ) + aVertFilter.Input( mpImplPolygon->mpPointAry[i] ); + if ( bPolygon || aVertFilter.IsPolygon() ) + aVertFilter.LastPoint(); + else + aPolygon.LastPoint(); + + // Alte ImpPolygon-Daten loeschen und die vom ImpPolygonPointFilter + // zuweisen + if ( mpImplPolygon->mnRefCount ) + { + if ( mpImplPolygon->mnRefCount > 1 ) + mpImplPolygon->mnRefCount--; + else + delete mpImplPolygon; + } + mpImplPolygon = aPolygon.mpPoly; +} + +// ----------------------------------------------------------------------- + +Rectangle Polygon::GetBoundRect() const +{ + DBG_CHKTHIS( Polygon, NULL ); + // Removing the assert. Bezier curves have the attribute that each single + // curve segment defined by four points can not exit the four-point polygon + // defined by that points. This allows to say that the curve segment can also + // never leave the Range of it's defining points. + // The result is that Polygon::GetBoundRect() may not create the minimal + // BoundRect of the Polygon (to get that, use basegfx::B2DPolygon classes), + // but will always create a valid BoundRect, at least as long as this method + // 'blindly' travels over all points, including control points. + // + // DBG_ASSERT( !mpImplPolygon->mpFlagAry, "GetBoundRect could fail with beziers!" ); + + USHORT nCount = mpImplPolygon->mnPoints; + if( ! nCount ) + return Rectangle(); + + long nXMin, nXMax, nYMin, nYMax; + + const Point* pPt = &(mpImplPolygon->mpPointAry[0]); + nXMin = nXMax = pPt->X(); + nYMin = nYMax = pPt->Y(); + + for ( USHORT i = 0; i < nCount; i++ ) + { + pPt = &(mpImplPolygon->mpPointAry[i]); + + 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(); + } + + return Rectangle( nXMin, nYMin, nXMax, nYMax ); +} + +// ----------------------------------------------------------------------- + +double Polygon::GetArea() const +{ + const double fArea = GetSignedArea(); + return( ( fArea < 0.0 ) ? -fArea : fArea ); +} + +// ----------------------------------------------------------------------- + +double Polygon::GetSignedArea() const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( !mpImplPolygon->mpFlagAry, "GetArea could fail with beziers!" ); + + double fArea = 0.0; + + if( mpImplPolygon->mnPoints > 2 ) + { + const USHORT nCount1 = mpImplPolygon->mnPoints - 1; + + for( USHORT i = 0; i < nCount1; ) + { + const Point& rPt = mpImplPolygon->mpPointAry[ i ]; + const Point& rPt1 = mpImplPolygon->mpPointAry[ ++i ]; + fArea += ( rPt.X() - rPt1.X() ) * ( rPt.Y() + rPt1.Y() ); + } + + const Point& rPt = mpImplPolygon->mpPointAry[ nCount1 ]; + const Point& rPt0 = mpImplPolygon->mpPointAry[ 0 ]; + fArea += ( rPt.X() - rPt0.X() ) * ( rPt.Y() + rPt0.Y() ); + } + + return fArea; +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsInside( const Point& rPoint ) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( !mpImplPolygon->mpFlagAry, "IsInside could fail with beziers!" ); + + const Rectangle aBound( GetBoundRect() ); + const Line aLine( rPoint, Point( aBound.Right() + 100L, rPoint.Y() ) ); + USHORT nCount = mpImplPolygon->mnPoints; + USHORT nPCounter = 0; + + if ( ( nCount > 2 ) && aBound.IsInside( rPoint ) ) + { + Point aPt1( mpImplPolygon->mpPointAry[ 0 ] ); + Point aIntersection; + Point aLastIntersection; + + while ( ( aPt1 == mpImplPolygon->mpPointAry[ nCount - 1 ] ) && ( nCount > 3 ) ) + nCount--; + + for ( USHORT i = 1; i <= nCount; i++ ) + { + const Point& rPt2 = mpImplPolygon->mpPointAry[ ( i < nCount ) ? i : 0 ]; + + if ( aLine.Intersection( Line( aPt1, rPt2 ), aIntersection ) ) + { + // Hiermit verhindern wir das Einfuegen von + // doppelten Intersections, die gleich hintereinander folgen + if ( nPCounter ) + { + if ( aIntersection != aLastIntersection ) + { + aLastIntersection = aIntersection; + nPCounter++; + } + } + else + { + aLastIntersection = aIntersection; + nPCounter++; + } + } + + aPt1 = rPt2; + } + } + + // innerhalb, wenn die Anzahl der Schnittpunkte ungerade ist + return ( ( nPCounter & 1 ) == 1 ); +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsRightOrientated() const +{ + DBG_CHKTHIS( Polygon, NULL ); + return GetSignedArea() >= 0.0; +} + +// ----------------------------------------------------------------------- + +void Polygon::Insert( USHORT nPos, const Point& rPt, PolyFlags eFlags ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + if( nPos >= mpImplPolygon->mnPoints ) + nPos = mpImplPolygon->mnPoints; + + mpImplPolygon->ImplSplit( nPos, 1 ); + mpImplPolygon->mpPointAry[ nPos ] = rPt; + + if( POLY_NORMAL != eFlags ) + { + mpImplPolygon->ImplCreateFlagArray(); + mpImplPolygon->mpFlagAry[ nPos ] = (BYTE) eFlags; + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Insert( USHORT nPos, const Polygon& rPoly ) +{ + DBG_CHKTHIS( Polygon, NULL ); + const USHORT nInsertCount = rPoly.mpImplPolygon->mnPoints; + + if( nInsertCount ) + { + ImplMakeUnique(); + + if( nPos >= mpImplPolygon->mnPoints ) + nPos = mpImplPolygon->mnPoints; + + if( rPoly.mpImplPolygon->mpFlagAry ) + mpImplPolygon->ImplCreateFlagArray(); + + mpImplPolygon->ImplSplit( nPos, nInsertCount, rPoly.mpImplPolygon ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Remove( USHORT nPos, USHORT nCount ) +{ + DBG_CHKTHIS( Polygon, NULL ); + if( nCount && ( nPos < mpImplPolygon->mnPoints ) ) + { + ImplMakeUnique(); + mpImplPolygon->ImplRemove( nPos, nCount ); + } +} + +// ----------------------------------------------------------------------- + +Point& Polygon::operator[]( USHORT nPos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, "Polygon::[]: nPos >= nPoints" ); + + ImplMakeUnique(); + return mpImplPolygon->mpPointAry[nPos]; +} + +// ----------------------------------------------------------------------- + +Polygon& Polygon::operator=( const Polygon& rPoly ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + DBG_ASSERT( rPoly.mpImplPolygon->mnRefCount < 0xFFFFFFFE, "Polygon: RefCount overflow" ); + + // Zuerst Referenzcounter erhoehen, damit man sich selbst zuweisen kann + // RefCount == 0 fuer statische Objekte + if ( rPoly.mpImplPolygon->mnRefCount ) + rPoly.mpImplPolygon->mnRefCount++; + + // Wenn es keine statischen ImpDaten sind, dann loeschen, wenn es + // die letzte Referenz ist, sonst Referenzcounter decrementieren + if ( mpImplPolygon->mnRefCount ) + { + if ( mpImplPolygon->mnRefCount > 1 ) + mpImplPolygon->mnRefCount--; + else + delete mpImplPolygon; + } + + mpImplPolygon = rPoly.mpImplPolygon; + return *this; +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::operator==( const Polygon& rPoly ) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + + if ( (rPoly.mpImplPolygon == mpImplPolygon) ) + return TRUE; + else + return FALSE; +} + +// ----------------------------------------------------------------------- + +sal_Bool Polygon::IsEqual( const Polygon& rPoly ) const +{ + sal_Bool bIsEqual = sal_True;; + sal_uInt16 i; + if ( GetSize() != rPoly.GetSize() ) + bIsEqual = sal_False; + else + { + for ( i = 0; i < GetSize(); i++ ) + { + if ( ( GetPoint( i ) != rPoly.GetPoint( i ) ) || + ( GetFlags( i ) != rPoly.GetFlags( i ) ) ) + { + bIsEqual = sal_False; + break; + } + } + } + return bIsEqual; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStream, Polygon& rPoly ) +{ + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + DBG_ASSERTWARNING( rIStream.GetVersion(), "Polygon::>> - Solar-Version not set on rIStream" ); + + USHORT i; + USHORT nStart; + USHORT nCurPoints; + USHORT nPoints; + unsigned char bShort; + short nShortX; + short nShortY; + long nLongX; + long nLongY; + + // Anzahl der Punkte einlesen und Array erzeugen + rIStream >> nPoints; + if ( rPoly.mpImplPolygon->mnRefCount != 1 ) + { + if ( rPoly.mpImplPolygon->mnRefCount ) + rPoly.mpImplPolygon->mnRefCount--; + rPoly.mpImplPolygon = new ImplPolygon( nPoints ); + } + else + rPoly.mpImplPolygon->ImplSetSize( nPoints, FALSE ); + + // Je nach CompressMode das Polygon einlesen + if ( rIStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + i = 0; + while ( i < nPoints ) + { + rIStream >> bShort >> nCurPoints; + + if ( bShort ) + { + for ( nStart = i; i < nStart+nCurPoints; i++ ) + { + rIStream >> nShortX >> nShortY; + rPoly.mpImplPolygon->mpPointAry[i].X() = nShortX; + rPoly.mpImplPolygon->mpPointAry[i].Y() = nShortY; + } + } + else + { + for ( nStart = i; i < nStart+nCurPoints; i++ ) + { + rIStream >> nLongX >> nLongY; + rPoly.mpImplPolygon->mpPointAry[i].X() = nLongX; + rPoly.mpImplPolygon->mpPointAry[i].Y() = nLongY; + } + } + } + } + else + { + // Feststellen, ob ueber die Operatoren geschrieben werden muss +#if (SAL_TYPES_SIZEOFLONG) != 4 + if ( 1 ) +#else +#ifdef OSL_BIGENDIAN + if ( rIStream.GetNumberFormatInt() != NUMBERFORMAT_INT_BIGENDIAN ) +#else + if ( rIStream.GetNumberFormatInt() != NUMBERFORMAT_INT_LITTLEENDIAN ) +#endif +#endif + { + for( i = 0; i < nPoints; i++ ) + { + rIStream >> rPoly.mpImplPolygon->mpPointAry[i].X() + >> rPoly.mpImplPolygon->mpPointAry[i].Y(); + } + } + else + rIStream.Read( rPoly.mpImplPolygon->mpPointAry, nPoints*sizeof(Point) ); + } + + return rIStream; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStream, const Polygon& rPoly ) +{ + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + DBG_ASSERTWARNING( rOStream.GetVersion(), "Polygon::<< - Solar-Version not set on rOStream" ); + + unsigned char bShort; + unsigned char bCurShort; + USHORT nStart; + USHORT i; + USHORT nPoints = rPoly.GetSize(); + + // Anzahl der Punkte rausschreiben + rOStream << nPoints; + + // Je nach CompressMode das Polygon rausschreiben + if ( rOStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + i = 0; + while ( i < nPoints ) + { + nStart = i; + + // Feststellen, welcher Typ geschrieben werden soll + if ( ((rPoly.mpImplPolygon->mpPointAry[nStart].X() >= SHRT_MIN) && + (rPoly.mpImplPolygon->mpPointAry[nStart].X() <= SHRT_MAX)) && + ((rPoly.mpImplPolygon->mpPointAry[nStart].Y() >= SHRT_MIN) && + (rPoly.mpImplPolygon->mpPointAry[nStart].Y() <= SHRT_MAX)) ) + bShort = TRUE; + else + bShort = FALSE; + while ( i < nPoints ) + { + // Feststellen, welcher Typ geschrieben werden soll + if ( ((rPoly.mpImplPolygon->mpPointAry[nStart].X() >= SHRT_MIN) && + (rPoly.mpImplPolygon->mpPointAry[nStart].X() <= SHRT_MAX)) && + ((rPoly.mpImplPolygon->mpPointAry[nStart].Y() >= SHRT_MIN) && + (rPoly.mpImplPolygon->mpPointAry[nStart].Y() <= SHRT_MAX)) ) + bCurShort = TRUE; + else + bCurShort = FALSE; + + // Wenn sich die Werte in einen anderen Bereich begeben, + // muessen wir neu rausschreiben + if ( bCurShort != bShort ) + { + bShort = bCurShort; + break; + } + + i++; + } + + rOStream << bShort << (USHORT)(i-nStart); + + if ( bShort ) + { + for( ; nStart < i; nStart++ ) + { + rOStream << (short)rPoly.mpImplPolygon->mpPointAry[nStart].X() + << (short)rPoly.mpImplPolygon->mpPointAry[nStart].Y(); + } + } + else + { + for( ; nStart < i; nStart++ ) + { + rOStream << rPoly.mpImplPolygon->mpPointAry[nStart].X() + << rPoly.mpImplPolygon->mpPointAry[nStart].Y(); + } + } + } + } + else + { + // Feststellen, ob ueber die Operatoren geschrieben werden muss +#if (SAL_TYPES_SIZEOFLONG) != 4 + if ( 1 ) +#else +#ifdef OSL_BIGENDIAN + if ( rOStream.GetNumberFormatInt() != NUMBERFORMAT_INT_BIGENDIAN ) +#else + if ( rOStream.GetNumberFormatInt() != NUMBERFORMAT_INT_LITTLEENDIAN ) +#endif +#endif + { + for( i = 0; i < nPoints; i++ ) + { + rOStream << rPoly.mpImplPolygon->mpPointAry[i].X() + << rPoly.mpImplPolygon->mpPointAry[i].Y(); + } + } + else + { + if ( nPoints ) + rOStream.Write( rPoly.mpImplPolygon->mpPointAry, nPoints*sizeof(Point) ); + } + } + + return rOStream; +} + +// ----------------------------------------------------------------------- + +void Polygon::ImplRead( SvStream& rIStream ) +{ + sal_uInt8 bHasPolyFlags; + + rIStream >> *this + >> bHasPolyFlags; + + if ( bHasPolyFlags ) + { + mpImplPolygon->mpFlagAry = new sal_uInt8[ mpImplPolygon->mnPoints ]; + rIStream.Read( mpImplPolygon->mpFlagAry, mpImplPolygon->mnPoints ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Read( SvStream& rIStream ) +{ + VersionCompat aCompat( rIStream, STREAM_READ ); + + ImplRead( rIStream ); +} + +// ----------------------------------------------------------------------- + +void Polygon::ImplWrite( SvStream& rOStream ) const +{ + sal_uInt8 bHasPolyFlags = mpImplPolygon->mpFlagAry != NULL; + rOStream << *this + << bHasPolyFlags; + + if ( bHasPolyFlags ) + rOStream.Write( mpImplPolygon->mpFlagAry, mpImplPolygon->mnPoints ); +} + +// ----------------------------------------------------------------------- + +void Polygon::Write( SvStream& rOStream ) const +{ + VersionCompat aCompat( rOStream, STREAM_WRITE, 1 ); + + ImplWrite( rOStream ); +} + +// ----------------------------------------------------------------------- +// #i74631# numerical correction method for B2DPolygon +void impCorrectContinuity(basegfx::B2DPolygon& roPolygon, sal_uInt32 nIndex, BYTE nCFlag) +{ + const sal_uInt32 nPointCount(roPolygon.count()); + OSL_ENSURE(nIndex < nPointCount, "impCorrectContinuity: index access out of range (!)"); + + if(nIndex < nPointCount && (POLY_SMOOTH == nCFlag || POLY_SYMMTR == nCFlag)) + { + if(roPolygon.isPrevControlPointUsed(nIndex) && roPolygon.isNextControlPointUsed(nIndex)) + { + const basegfx::B2DPoint aPoint(roPolygon.getB2DPoint(nIndex)); + + if(POLY_SMOOTH == nCFlag) + { + // C1: apply inverse direction of prev to next, keep length of next + const basegfx::B2DVector aOriginalNext(roPolygon.getNextControlPoint(nIndex) - aPoint); + basegfx::B2DVector aNewNext(aPoint - roPolygon.getPrevControlPoint(nIndex)); + + aNewNext.setLength(aOriginalNext.getLength()); + roPolygon.setNextControlPoint(nIndex, basegfx::B2DPoint(aPoint + aNewNext)); + } + else // POLY_SYMMTR + { + // C2: apply inverse control point to next + roPolygon.setNextControlPoint(nIndex, (2.0 * aPoint) - roPolygon.getPrevControlPoint(nIndex)); + } + } + } +} + +// ----------------------------------------------------------------------- +// convert to basegfx::B2DPolygon and return +basegfx::B2DPolygon Polygon::getB2DPolygon() const +{ + basegfx::B2DPolygon aRetval; + const sal_uInt16 nCount(mpImplPolygon->mnPoints); + + if(nCount) + { + if(mpImplPolygon->mpFlagAry) + { + // handling for curves. Add start point + const Point aStartPoint(mpImplPolygon->mpPointAry[0]); + BYTE nPointFlag(mpImplPolygon->mpFlagAry[0]); + aRetval.append(basegfx::B2DPoint(aStartPoint.X(), aStartPoint.Y())); + Point aControlA, aControlB; + + for(sal_uInt16 a(1); a < nCount;) + { + bool bControlA(false); + bool bControlB(false); + + if(POLY_CONTROL == mpImplPolygon->mpFlagAry[a]) + { + aControlA = mpImplPolygon->mpPointAry[a++]; + bControlA = true; + } + + if(a < nCount && POLY_CONTROL == mpImplPolygon->mpFlagAry[a]) + { + aControlB = mpImplPolygon->mpPointAry[a++]; + bControlB = true; + } + + // assert invalid polygons + OSL_ENSURE(bControlA == bControlB, "Polygon::getB2DPolygon: Invalid source polygon (!)"); + + if(a < nCount) + { + const Point aEndPoint(mpImplPolygon->mpPointAry[a]); + + if(bControlA) + { + // bezier edge, add + aRetval.appendBezierSegment( + basegfx::B2DPoint(aControlA.X(), aControlA.Y()), + basegfx::B2DPoint(aControlB.X(), aControlB.Y()), + basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y())); + + impCorrectContinuity(aRetval, aRetval.count() - 2, nPointFlag); + } + else + { + // no bezier edge, add end point + aRetval.append(basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y())); + } + + nPointFlag = mpImplPolygon->mpFlagAry[a++]; + } + } + + // if exist, remove double first/last points, set closed and correct control points + basegfx::tools::checkClosed(aRetval); + + if(aRetval.isClosed()) + { + // closeWithGeometryChange did really close, so last point(s) were removed. + // Correct the continuity in the changed point + impCorrectContinuity(aRetval, 0, mpImplPolygon->mpFlagAry[0]); + } + } + else + { + // extra handling for non-curves (most-used case) for speedup + for(sal_uInt16 a(0); a < nCount; a++) + { + // get point and add + const Point aPoint(mpImplPolygon->mpPointAry[a]); + aRetval.append(basegfx::B2DPoint(aPoint.X(), aPoint.Y())); + } + + // set closed flag + basegfx::tools::checkClosed(aRetval); + } + } + + return aRetval; +} + +// ----------------------------------------------------------------------- +// constructor to convert from basegfx::B2DPolygon +// #i76891# Needed to change from adding all control points (even for unused +// edges) and creating a fixed-size Polygon in the first run to creating the +// minimal Polygon. This requires a temporary Point- and Flag-Array for curves +// and a memcopy at ImplPolygon creation, but contains no zero-controlpoints +// for straight edges. +Polygon::Polygon(const basegfx::B2DPolygon& rPolygon) +: mpImplPolygon(0) +{ + DBG_CTOR( Polygon, NULL ); + + const bool bCurve(rPolygon.areControlPointsUsed()); + const bool bClosed(rPolygon.isClosed()); + sal_uInt32 nB2DLocalCount(rPolygon.count()); + + if(bCurve) + { + // #127979# Reduce source point count hard to the limit of the tools Polygon + if(nB2DLocalCount > ((0x0000ffff / 3L) - 1L)) + { + DBG_ERROR("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)"); + nB2DLocalCount = ((0x0000ffff / 3L) - 1L); + } + + // calculate target point count + const sal_uInt32 nLoopCount(bClosed ? nB2DLocalCount : (nB2DLocalCount ? nB2DLocalCount - 1L : 0L )); + + if(nLoopCount) + { + // calculate maximum array size and allocate; prepare insert index + const sal_uInt32 nMaxTargetCount((nLoopCount * 3) + 1); + mpImplPolygon = new ImplPolygon(static_cast< sal_uInt16 >(nMaxTargetCount), true); + + // prepare insert index and current point + sal_uInt32 nArrayInsert(0); + basegfx::B2DCubicBezier aBezier; + aBezier.setStartPoint(rPolygon.getB2DPoint(0)); + + for(sal_uInt32 a(0L); a < nLoopCount; a++) + { + // add current point (always) and remember StartPointIndex for evtl. later corrections + const Point aStartPoint(FRound(aBezier.getStartPoint().getX()), FRound(aBezier.getStartPoint().getY())); + const sal_uInt32 nStartPointIndex(nArrayInsert); + mpImplPolygon->mpPointAry[nStartPointIndex] = aStartPoint; + mpImplPolygon->mpFlagAry[nStartPointIndex] = (BYTE)POLY_NORMAL; + nArrayInsert++; + + // prepare next segment + const sal_uInt32 nNextIndex((a + 1) % nB2DLocalCount); + aBezier.setEndPoint(rPolygon.getB2DPoint(nNextIndex)); + aBezier.setControlPointA(rPolygon.getNextControlPoint(a)); + aBezier.setControlPointB(rPolygon.getPrevControlPoint(nNextIndex)); + + if(aBezier.isBezier()) + { + // if one is used, add always two control points due to the old schema + mpImplPolygon->mpPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointA().getX()), FRound(aBezier.getControlPointA().getY())); + mpImplPolygon->mpFlagAry[nArrayInsert] = (BYTE)POLY_CONTROL; + nArrayInsert++; + + mpImplPolygon->mpPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointB().getX()), FRound(aBezier.getControlPointB().getY())); + mpImplPolygon->mpFlagAry[nArrayInsert] = (BYTE)POLY_CONTROL; + nArrayInsert++; + } + + // test continuity with previous control point to set flag value + if(aBezier.getControlPointA() != aBezier.getStartPoint() && (bClosed || a)) + { + const basegfx::B2VectorContinuity eCont(rPolygon.getContinuityInPoint(a)); + + if(basegfx::CONTINUITY_C1 == eCont) + { + mpImplPolygon->mpFlagAry[nStartPointIndex] = (BYTE)POLY_SMOOTH; + } + else if(basegfx::CONTINUITY_C2 == eCont) + { + mpImplPolygon->mpFlagAry[nStartPointIndex] = (BYTE)POLY_SYMMTR; + } + } + + // prepare next polygon step + aBezier.setStartPoint(aBezier.getEndPoint()); + } + + if(bClosed) + { + // add first point again as closing point due to old definition + mpImplPolygon->mpPointAry[nArrayInsert] = mpImplPolygon->mpPointAry[0]; + mpImplPolygon->mpFlagAry[nArrayInsert] = (BYTE)POLY_NORMAL; + nArrayInsert++; + } + else + { + // add last point as closing point + const basegfx::B2DPoint aClosingPoint(rPolygon.getB2DPoint(nB2DLocalCount - 1L)); + const Point aEnd(FRound(aClosingPoint.getX()), FRound(aClosingPoint.getY())); + mpImplPolygon->mpPointAry[nArrayInsert] = aEnd; + mpImplPolygon->mpFlagAry[nArrayInsert] = (BYTE)POLY_NORMAL; + nArrayInsert++; + } + + DBG_ASSERT(nArrayInsert <= nMaxTargetCount, "Polygon::Polygon from basegfx::B2DPolygon: wrong max point count estimation (!)"); + + if(nArrayInsert != nMaxTargetCount) + { + mpImplPolygon->ImplSetSize(static_cast< sal_uInt16 >(nArrayInsert), true); + } + } + } + else + { + // #127979# Reduce source point count hard to the limit of the tools Polygon + if(nB2DLocalCount > (0x0000ffff - 1L)) + { + DBG_ERROR("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)"); + nB2DLocalCount = (0x0000ffff - 1L); + } + + if(nB2DLocalCount) + { + // point list creation + const sal_uInt32 nTargetCount(nB2DLocalCount + (bClosed ? 1L : 0L)); + mpImplPolygon = new ImplPolygon( static_cast< sal_uInt16 >(nTargetCount) ); + sal_uInt16 nIndex(0); + + for(sal_uInt32 a(0L); a < nB2DLocalCount; a++) + { + basegfx::B2DPoint aB2DPoint(rPolygon.getB2DPoint(a)); + Point aPoint(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY())); + mpImplPolygon->mpPointAry[nIndex++] = aPoint; + } + + if(bClosed) + { + // add first point as closing point + mpImplPolygon->mpPointAry[nIndex] = mpImplPolygon->mpPointAry[0]; + } + } + } + + if(!mpImplPolygon) + { + // no content yet, create empty polygon + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); + } +} + +// eof diff --git a/tools/source/generic/poly2.cxx b/tools/source/generic/poly2.cxx new file mode 100644 index 000000000000..46459353fa35 --- /dev/null +++ b/tools/source/generic/poly2.cxx @@ -0,0 +1,891 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_tools.hxx" + +#define _SV_POLY2_CXX + +#define POLY_CLIP_INT 0 +#define POLY_CLIP_UNION 1 +#define POLY_CLIP_DIFF 2 +#define POLY_CLIP_XOR 3 + +#include <rtl/math.hxx> +#include <poly.h> +#include <tools/poly.hxx> +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygoncutter.hxx> + +// --------------- +// - PolyPolygon - +// --------------- + +DBG_NAME( PolyPolygon ) + +// ----------------------------------------------------------------------- + +ImplPolyPolygon::ImplPolyPolygon( USHORT nInitSize ) +{ + mnRefCount = 1; + mnCount = nInitSize; + mnSize = nInitSize; + mnResize = 16; + mpPolyAry = new SVPPOLYGON[ nInitSize ]; +} + +// ----------------------------------------------------------------------- + +ImplPolyPolygon::ImplPolyPolygon( const ImplPolyPolygon& rImplPolyPoly ) +{ + mnRefCount = 1; + mnCount = rImplPolyPoly.mnCount; + mnSize = rImplPolyPoly.mnSize; + mnResize = rImplPolyPoly.mnResize; + + if ( rImplPolyPoly.mpPolyAry ) + { + mpPolyAry = new SVPPOLYGON[mnSize]; + for ( USHORT i = 0; i < mnCount; i++ ) + mpPolyAry[i] = new Polygon( *rImplPolyPoly.mpPolyAry[i] ); + } + else + mpPolyAry = NULL; +} + +// ----------------------------------------------------------------------- + +ImplPolyPolygon::~ImplPolyPolygon() +{ + if ( mpPolyAry ) + { + for ( USHORT i = 0; i < mnCount; i++ ) + delete mpPolyAry[i]; + delete[] mpPolyAry; + } +} + +// ======================================================================= + +PolyPolygon::PolyPolygon( USHORT nInitSize, USHORT nResize ) +{ + DBG_CTOR( PolyPolygon, NULL ); + + if ( nInitSize > MAX_POLYGONS ) + nInitSize = MAX_POLYGONS; + else if ( !nInitSize ) + nInitSize = 1; + if ( nResize > MAX_POLYGONS ) + nResize = MAX_POLYGONS; + else if ( !nResize ) + nResize = 1; + mpImplPolyPolygon = new ImplPolyPolygon( nInitSize, nResize ); +} + +// ----------------------------------------------------------------------- + +PolyPolygon::PolyPolygon( const Polygon& rPoly ) +{ + DBG_CTOR( PolyPolygon, NULL ); + + if ( rPoly.GetSize() ) + { + mpImplPolyPolygon = new ImplPolyPolygon( 1 ); + mpImplPolyPolygon->mpPolyAry[0] = new Polygon( rPoly ); + } + else + mpImplPolyPolygon = new ImplPolyPolygon( 16, 16 ); +} + +// ----------------------------------------------------------------------- + +PolyPolygon::PolyPolygon( USHORT nPoly, const USHORT* pPointCountAry, + const Point* pPtAry ) +{ + DBG_CTOR( PolyPolygon, NULL ); + + if ( nPoly > MAX_POLYGONS ) + nPoly = MAX_POLYGONS; + + mpImplPolyPolygon = new ImplPolyPolygon( nPoly ); + for ( USHORT i = 0; i < nPoly; i++ ) + { + mpImplPolyPolygon->mpPolyAry[i] = new Polygon( *pPointCountAry, pPtAry ); + pPtAry += *pPointCountAry; + pPointCountAry++; + } +} + +// ----------------------------------------------------------------------- + +PolyPolygon::PolyPolygon( const PolyPolygon& rPolyPoly ) +{ + DBG_CTOR( PolyPolygon, NULL ); + DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); + DBG_ASSERT( rPolyPoly.mpImplPolyPolygon->mnRefCount < 0xFFFFFFFE, "PolyPolygon: RefCount overflow" ); + + mpImplPolyPolygon = rPolyPoly.mpImplPolyPolygon; + mpImplPolyPolygon->mnRefCount++; +} + +// ----------------------------------------------------------------------- + +PolyPolygon::~PolyPolygon() +{ + DBG_DTOR( PolyPolygon, NULL ); + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + mpImplPolyPolygon->mnRefCount--; + else + delete mpImplPolyPolygon; +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Insert( const Polygon& rPoly, USHORT nPos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + if ( mpImplPolyPolygon->mnCount >= MAX_POLYGONS ) + return; + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + if ( nPos > mpImplPolyPolygon->mnCount ) + nPos = mpImplPolyPolygon->mnCount; + + if ( !mpImplPolyPolygon->mpPolyAry ) + mpImplPolyPolygon->mpPolyAry = new SVPPOLYGON[mpImplPolyPolygon->mnSize]; + else if ( mpImplPolyPolygon->mnCount == mpImplPolyPolygon->mnSize ) + { + USHORT nOldSize = mpImplPolyPolygon->mnSize; + USHORT nNewSize = nOldSize + mpImplPolyPolygon->mnResize; + SVPPOLYGON* pNewAry; + + if ( nNewSize >= MAX_POLYGONS ) + nNewSize = MAX_POLYGONS; + pNewAry = new SVPPOLYGON[nNewSize]; + memcpy( pNewAry, mpImplPolyPolygon->mpPolyAry, nPos*sizeof(SVPPOLYGON) ); + memcpy( pNewAry+nPos+1, mpImplPolyPolygon->mpPolyAry+nPos, + (nOldSize-nPos)*sizeof(SVPPOLYGON) ); + delete[] mpImplPolyPolygon->mpPolyAry; + mpImplPolyPolygon->mpPolyAry = pNewAry; + mpImplPolyPolygon->mnSize = nNewSize; + } + else if ( nPos < mpImplPolyPolygon->mnCount ) + { + memmove( mpImplPolyPolygon->mpPolyAry+nPos+1, + mpImplPolyPolygon->mpPolyAry+nPos, + (mpImplPolyPolygon->mnCount-nPos)*sizeof(SVPPOLYGON) ); + } + + mpImplPolyPolygon->mpPolyAry[nPos] = new Polygon( rPoly ); + mpImplPolyPolygon->mnCount++; +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Remove( USHORT nPos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_ASSERT( nPos < Count(), "PolyPolygon::Remove(): nPos >= nSize" ); + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + delete mpImplPolyPolygon->mpPolyAry[nPos]; + mpImplPolyPolygon->mnCount--; + memmove( mpImplPolyPolygon->mpPolyAry+nPos, + mpImplPolyPolygon->mpPolyAry+nPos+1, + (mpImplPolyPolygon->mnCount-nPos)*sizeof(SVPPOLYGON) ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Replace( const Polygon& rPoly, USHORT nPos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_ASSERT( nPos < Count(), "PolyPolygon::Replace(): nPos >= nSize" ); + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + delete mpImplPolyPolygon->mpPolyAry[nPos]; + mpImplPolyPolygon->mpPolyAry[nPos] = new Polygon( rPoly ); +} + +// ----------------------------------------------------------------------- + +const Polygon& PolyPolygon::GetObject( USHORT nPos ) const +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_ASSERT( nPos < Count(), "PolyPolygon::GetObject(): nPos >= nSize" ); + + return *(mpImplPolyPolygon->mpPolyAry[nPos]); +} + +// ----------------------------------------------------------------------- + +BOOL PolyPolygon::IsRect() const +{ + BOOL bIsRect = FALSE; + if ( Count() == 1 ) + bIsRect = mpImplPolyPolygon->mpPolyAry[ 0 ]->IsRect(); + return bIsRect; +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Clear() +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( mpImplPolyPolygon->mnResize, + mpImplPolyPolygon->mnResize ); + } + else + { + if ( mpImplPolyPolygon->mpPolyAry ) + { + for ( USHORT i = 0; i < mpImplPolyPolygon->mnCount; i++ ) + delete mpImplPolyPolygon->mpPolyAry[i]; + delete[] mpImplPolyPolygon->mpPolyAry; + mpImplPolyPolygon->mpPolyAry = NULL; + mpImplPolyPolygon->mnCount = 0; + mpImplPolyPolygon->mnSize = mpImplPolyPolygon->mnResize; + } + } +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Optimize( ULONG nOptimizeFlags, const PolyOptimizeData* pData ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + if( nOptimizeFlags ) + { + double fArea; + const BOOL bEdges = ( nOptimizeFlags & POLY_OPTIMIZE_EDGES ) == POLY_OPTIMIZE_EDGES; + USHORT nPercent = 0; + + if( bEdges ) + { + const Rectangle aBound( GetBoundRect() ); + + fArea = ( aBound.GetWidth() + aBound.GetHeight() ) * 0.5; + nPercent = pData ? pData->GetPercentValue() : 50; + nOptimizeFlags &= ~POLY_OPTIMIZE_EDGES; + } + + // watch for ref counter + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Optimize polygons + for( USHORT i = 0, nPolyCount = mpImplPolyPolygon->mnCount; i < nPolyCount; i++ ) + { + if( bEdges ) + { + mpImplPolyPolygon->mpPolyAry[ i ]->Optimize( POLY_OPTIMIZE_NO_SAME ); + Polygon::ImplReduceEdges( *( mpImplPolyPolygon->mpPolyAry[ i ] ), fArea, nPercent ); + } + + if( nOptimizeFlags ) + mpImplPolyPolygon->mpPolyAry[ i ]->Optimize( nOptimizeFlags, pData ); + } + } +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::AdaptiveSubdivide( PolyPolygon& rResult, const double d ) const +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + rResult.Clear(); + + Polygon aPolygon; + + for( USHORT i = 0; i < mpImplPolyPolygon->mnCount; i++ ) + { + mpImplPolyPolygon->mpPolyAry[ i ]->AdaptiveSubdivide( aPolygon, d ); + rResult.Insert( aPolygon ); + } +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::GetIntersection( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + ImplDoOperation( rPolyPoly, rResult, POLY_CLIP_INT ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::GetUnion( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + ImplDoOperation( rPolyPoly, rResult, POLY_CLIP_UNION ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::GetDifference( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + ImplDoOperation( rPolyPoly, rResult, POLY_CLIP_DIFF ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::GetXOR( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + ImplDoOperation( rPolyPoly, rResult, POLY_CLIP_XOR ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::ImplDoOperation( const PolyPolygon& rPolyPoly, PolyPolygon& rResult, ULONG nOperation ) const +{ + // Convert to B2DPolyPolygon, temporarily. It might be + // advantageous in the future, to have a PolyPolygon adaptor that + // just simulates a B2DPolyPolygon here... + basegfx::B2DPolyPolygon aMergePolyPolygonA( getB2DPolyPolygon() ); + basegfx::B2DPolyPolygon aMergePolyPolygonB( rPolyPoly.getB2DPolyPolygon() ); + + // normalize the two polypolygons before. Force properly oriented + // polygons. + aMergePolyPolygonA = basegfx::tools::prepareForPolygonOperation( aMergePolyPolygonA ); + aMergePolyPolygonB = basegfx::tools::prepareForPolygonOperation( aMergePolyPolygonB ); + + switch( nOperation ) + { + // All code extracted from svx/source/svdraw/svedtv2.cxx + // ----------------------------------------------------- + + case POLY_CLIP_UNION: + { + // merge A and B (OR) + aMergePolyPolygonA = basegfx::tools::solvePolygonOperationOr(aMergePolyPolygonA, aMergePolyPolygonB); + break; + } + + case POLY_CLIP_DIFF: + { + // substract B from A (DIFF) + aMergePolyPolygonA = basegfx::tools::solvePolygonOperationDiff(aMergePolyPolygonA, aMergePolyPolygonB); + break; + } + + case POLY_CLIP_XOR: + { + // compute XOR between poly A and B + aMergePolyPolygonA = basegfx::tools::solvePolygonOperationXor(aMergePolyPolygonA, aMergePolyPolygonB); + break; + } + + default: + case POLY_CLIP_INT: + { + // cut poly 1 against polys 2..n (AND) + aMergePolyPolygonA = basegfx::tools::solvePolygonOperationAnd(aMergePolyPolygonA, aMergePolyPolygonB); + break; + } + } + + rResult = PolyPolygon( aMergePolyPolygonA ); +} + +// ----------------------------------------------------------------------- + +USHORT PolyPolygon::Count() const +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + return mpImplPolyPolygon->mnCount; +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Move( long nHorzMove, long nVertMove ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Diese Abfrage sollte man fuer die DrawEngine durchfuehren + if( nHorzMove || nVertMove ) + { + // Referenzcounter beruecksichtigen + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + USHORT nPolyCount = mpImplPolyPolygon->mnCount; + for ( USHORT i = 0; i < nPolyCount; i++ ) + mpImplPolyPolygon->mpPolyAry[i]->Move( nHorzMove, nVertMove ); + } +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Translate( const Point& rTrans ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Referenzcounter beruecksichtigen + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + for ( USHORT i = 0, nCount = mpImplPolyPolygon->mnCount; i < nCount; i++ ) + mpImplPolyPolygon->mpPolyAry[ i ]->Translate( rTrans ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Scale( double fScaleX, double fScaleY ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Referenzcounter beruecksichtigen + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + for ( USHORT i = 0, nCount = mpImplPolyPolygon->mnCount; i < nCount; i++ ) + mpImplPolyPolygon->mpPolyAry[ i ]->Scale( fScaleX, fScaleY ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Rotate( const Point& rCenter, USHORT nAngle10 ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + nAngle10 %= 3600; + + if( nAngle10 ) + { + const double fAngle = F_PI1800 * nAngle10; + Rotate( rCenter, sin( fAngle ), cos( fAngle ) ); + } +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Rotate( const Point& rCenter, double fSin, double fCos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Referenzcounter beruecksichtigen + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + for ( USHORT i = 0, nCount = mpImplPolyPolygon->mnCount; i < nCount; i++ ) + mpImplPolyPolygon->mpPolyAry[ i ]->Rotate( rCenter, fSin, fCos ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::SlantX( long nYRef, double fSin, double fCos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Referenzcounter beruecksichtigen + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + for ( USHORT i = 0, nCount = mpImplPolyPolygon->mnCount; i < nCount; i++ ) + mpImplPolyPolygon->mpPolyAry[ i ]->SlantX( nYRef, fSin, fCos ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::SlantY( long nXRef, double fSin, double fCos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Referenzcounter beruecksichtigen + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + for ( USHORT i = 0, nCount = mpImplPolyPolygon->mnCount; i < nCount; i++ ) + mpImplPolyPolygon->mpPolyAry[ i ]->SlantY( nXRef, fSin, fCos ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Distort( const Rectangle& rRefRect, const Polygon& rDistortedRect ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Referenzcounter beruecksichtigen + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + for ( USHORT i = 0, nCount = mpImplPolyPolygon->mnCount; i < nCount; i++ ) + mpImplPolyPolygon->mpPolyAry[ i ]->Distort( rRefRect, rDistortedRect ); +} + + +// ----------------------------------------------------------------------- + +void PolyPolygon::Clip( const Rectangle& rRect ) +{ + // Polygon-Clippen + USHORT nPolyCount = mpImplPolyPolygon->mnCount; + USHORT i; + + if ( !nPolyCount ) + return; + + // Referenzcounter beruecksichtigen + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Erst jedes Polygon Clippen und dann die leeren entfernen + for ( i = 0; i < nPolyCount; i++ ) + mpImplPolyPolygon->mpPolyAry[i]->Clip( rRect ); + while ( nPolyCount ) + { + if ( GetObject( nPolyCount-1 ).GetSize() <= 2 ) + Remove( nPolyCount-1 ); + nPolyCount--; + } +} + +// ----------------------------------------------------------------------- + +Rectangle PolyPolygon::GetBoundRect() const +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + long nXMin=0, nXMax=0, nYMin=0, nYMax=0; + BOOL bFirst = TRUE; + USHORT nPolyCount = mpImplPolyPolygon->mnCount; + + for ( USHORT n = 0; n < nPolyCount; n++ ) + { + const Polygon* pPoly = mpImplPolyPolygon->mpPolyAry[n]; + const Point* pAry = pPoly->GetConstPointAry(); + USHORT nPointCount = pPoly->GetSize(); + + for ( USHORT 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 Rectangle( nXMin, nYMin, nXMax, nYMax ); + else + return Rectangle(); +} + +// ----------------------------------------------------------------------- + +Polygon& PolyPolygon::operator[]( USHORT nPos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_ASSERT( nPos < Count(), "PolyPolygon::[](): nPos >= nSize" ); + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + return *(mpImplPolyPolygon->mpPolyAry[nPos]); +} + +// ----------------------------------------------------------------------- + +PolyPolygon& PolyPolygon::operator=( const PolyPolygon& rPolyPoly ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); + DBG_ASSERT( rPolyPoly.mpImplPolyPolygon->mnRefCount < 0xFFFFFFFE, "PolyPolygon: RefCount overflow" ); + + rPolyPoly.mpImplPolyPolygon->mnRefCount++; + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + mpImplPolyPolygon->mnRefCount--; + else + delete mpImplPolyPolygon; + + mpImplPolyPolygon = rPolyPoly.mpImplPolyPolygon; + return *this; +} + +// ----------------------------------------------------------------------- + +BOOL PolyPolygon::operator==( const PolyPolygon& rPolyPoly ) const +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); + + if ( rPolyPoly.mpImplPolyPolygon == mpImplPolyPolygon ) + return TRUE; + else + return FALSE; +} + +// ----------------------------------------------------------------------- + +sal_Bool PolyPolygon::IsEqual( const PolyPolygon& rPolyPoly ) const +{ + sal_Bool bIsEqual = sal_True; + if ( Count() != rPolyPoly.Count() ) + bIsEqual = sal_False; + else + { + sal_uInt16 i; + for ( i = 0; i < Count(); i++ ) + { + if (!GetObject( i ).IsEqual( rPolyPoly.GetObject( i ) ) ) + { + bIsEqual = sal_False; + break; + } + } + } + return bIsEqual; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStream, PolyPolygon& rPolyPoly ) +{ + DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); + DBG_ASSERTWARNING( rIStream.GetVersion(), "PolyPolygon::>> - Solar-Version not set on rIStream" ); + + Polygon* pPoly; + USHORT nPolyCount; + + // Anzahl der Polygone einlesen + rIStream >> nPolyCount; + + // Daten anlegen + if( nPolyCount ) + { + // Referenzcounter beruecksichtigen + if ( rPolyPoly.mpImplPolyPolygon->mnRefCount > 1 ) + rPolyPoly.mpImplPolyPolygon->mnRefCount--; + else + delete rPolyPoly.mpImplPolyPolygon; + + rPolyPoly.mpImplPolyPolygon = new ImplPolyPolygon( nPolyCount ); + + for ( USHORT i = 0; i < nPolyCount; i++ ) + { + pPoly = new Polygon; + rIStream >> *pPoly; + rPolyPoly.mpImplPolyPolygon->mpPolyAry[i] = pPoly; + } + } + else + rPolyPoly = PolyPolygon(); + + return rIStream; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStream, const PolyPolygon& rPolyPoly ) +{ + DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); + DBG_ASSERTWARNING( rOStream.GetVersion(), "PolyPolygon::<< - Solar-Version not set on rOStream" ); + + // Anzahl der Polygone rausschreiben + USHORT nPolyCount = rPolyPoly.mpImplPolyPolygon->mnCount; + rOStream << nPolyCount; + + // Die einzelnen Polygone ausgeben + for ( USHORT i = 0; i < nPolyCount; i++ ) + rOStream << *(rPolyPoly.mpImplPolyPolygon->mpPolyAry[i]); + + return rOStream; +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Read( SvStream& rIStream ) +{ + VersionCompat aCompat( rIStream, STREAM_READ ); + + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_ASSERTWARNING( rIStream.GetVersion(), "PolyPolygon::>> - Solar-Version not set on rIStream" ); + + Polygon* pPoly; + USHORT nPolyCount; + + // Anzahl der Polygone einlesen + rIStream >> nPolyCount; + + // Daten anlegen + if( nPolyCount ) + { + // Referenzcounter beruecksichtigen + if ( mpImplPolyPolygon->mnRefCount > 1 ) + mpImplPolyPolygon->mnRefCount--; + else + delete mpImplPolyPolygon; + + mpImplPolyPolygon = new ImplPolyPolygon( nPolyCount ); + + for ( USHORT i = 0; i < nPolyCount; i++ ) + { + pPoly = new Polygon; + pPoly->ImplRead( rIStream ); + mpImplPolyPolygon->mpPolyAry[i] = pPoly; + } + } + else + *this = PolyPolygon(); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Write( SvStream& rOStream ) const +{ + VersionCompat aCompat( rOStream, STREAM_WRITE, 1 ); + + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_ASSERTWARNING( rOStream.GetVersion(), "PolyPolygon::<< - Solar-Version not set on rOStream" ); + + // Anzahl der Polygone rausschreiben + USHORT nPolyCount = mpImplPolyPolygon->mnCount; + rOStream << nPolyCount; + + // Die einzelnen Polygone ausgeben + for ( USHORT i = 0; i < nPolyCount; i++ ) + mpImplPolyPolygon->mpPolyAry[i]->ImplWrite( rOStream );; +} + +// ----------------------------------------------------------------------- +// convert to basegfx::B2DPolyPolygon and return +basegfx::B2DPolyPolygon PolyPolygon::getB2DPolyPolygon() const +{ + basegfx::B2DPolyPolygon aRetval; + + for(sal_uInt16 a(0); a < mpImplPolyPolygon->mnCount; a++) + { + Polygon* pCandidate = mpImplPolyPolygon->mpPolyAry[a]; + aRetval.append(pCandidate->getB2DPolygon()); + } + + return aRetval; +} + +// ----------------------------------------------------------------------- +// constructor to convert from basegfx::B2DPolyPolygon +PolyPolygon::PolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon) +{ + DBG_CTOR( PolyPolygon, NULL ); + 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 ) + { + mpImplPolyPolygon = new ImplPolyPolygon( nCount ); + + for(sal_uInt16 a(0); a < nCount; a++) + { + basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(sal_uInt32(a))); + mpImplPolyPolygon->mpPolyAry[a] = new Polygon( aCandidate ); + } + } + else + { + mpImplPolyPolygon = new ImplPolyPolygon( 16, 16 ); + } +} + +// eof diff --git a/tools/source/generic/svborder.cxx b/tools/source/generic/svborder.cxx new file mode 100644 index 000000000000..eb254faf2310 --- /dev/null +++ b/tools/source/generic/svborder.cxx @@ -0,0 +1,77 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_tools.hxx" + +#include <tools/svborder.hxx> +#include <osl/diagnose.h> + +SvBorder::SvBorder( const Rectangle & rOuter, const Rectangle & rInner ) +{ + Rectangle aOuter( rOuter ); + aOuter.Justify(); + Rectangle aInner( rInner ); + if( aInner.IsEmpty() ) + aInner = Rectangle( aOuter.Center(), aOuter.Center() ); + else + aInner.Justify(); + + OSL_ENSURE( aOuter.IsInside( aInner ), + "SvBorder::SvBorder: FALSE == aOuter.IsInside( aInner )" ); + nTop = aInner.Top() - aOuter.Top(); + nRight = aOuter.Right() - aInner.Right(); + nBottom = aOuter.Bottom() - aInner.Bottom(); + nLeft = aInner.Left() - aOuter.Left(); +} + +Rectangle & operator += ( Rectangle & rRect, const SvBorder & rBorder ) +{ + // wegen Empty-Rect, GetSize muss als erstes gerufen werden + Size aS( rRect.GetSize() ); + aS.Width() += rBorder.Left() + rBorder.Right(); + aS.Height() += rBorder.Top() + rBorder.Bottom(); + + rRect.Left() -= rBorder.Left(); + rRect.Top() -= rBorder.Top(); + rRect.SetSize( aS ); + return rRect; +} + +Rectangle & operator -= ( Rectangle & rRect, const SvBorder & rBorder ) +{ + // wegen Empty-Rect, GetSize muss als erstes gerufen werden + Size aS( rRect.GetSize() ); + aS.Width() -= rBorder.Left() + rBorder.Right(); + aS.Height() -= rBorder.Top() + rBorder.Bottom(); + + rRect.Left() += rBorder.Left(); + rRect.Top() += rBorder.Top(); + rRect.SetSize( aS ); + return rRect; +} + diff --git a/tools/source/generic/toolsin.cxx b/tools/source/generic/toolsin.cxx new file mode 100644 index 000000000000..50c9c0187b16 --- /dev/null +++ b/tools/source/generic/toolsin.cxx @@ -0,0 +1,95 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_tools.hxx" + +#define _TOOLS_TOOLSIN_CXX + +#include <string.h> +#include <tools/shl.hxx> +#include <tools/debug.hxx> +#include <toolsin.hxx> + +#if defined WNT || defined OS2 +#include <dll.hxx> +#endif + +void ImplDeleteCharTabData(); + +// ======================================================================= + +TOOLSINDATA* ImplGetToolsInData() +{ + TOOLSINDATA** ppData = (TOOLSINDATA**)GetAppData( SHL_TOOLS ); + if ( !(*ppData) ) + { + TOOLSINDATA* pData = new TOOLSINDATA; + memset( pData, 0, sizeof( TOOLSINDATA ) ); + *ppData = pData; + } + + return *ppData; +} + +// ======================================================================= + +void InitTools() +{ + DBG_DEBUGSTART(); +} + +// ----------------------------------------------------------------------- + +void DeInitTools() +{ + TOOLSINDATA** ppData = (TOOLSINDATA**)GetAppData( SHL_TOOLS ); + TOOLSINDATA* pData = *ppData; + + if ( pData ) + { + ImplDeleteCharTabData(); + delete pData; + *ppData = NULL; + } + + DBG_DEBUGEND(); +} + +// ----------------------------------------------------------------------- + +void GlobalDeInitTools() +{ + DBG_GLOBALDEBUGEND(); + +#if defined WNT + ImpDeInitWinTools(); +#endif +#ifdef OS2 + ImpDeInitOS2Tools(); +#endif +} |