basegfx: optimize B2DRange and B2DPolygon transform
Use the Arvo method for B2DRange::transform(), which computes the new AABB directly from matrix element signs instead of transforming all 4 corners. This reduces multiplications from 16 to 8 and eliminates temporary B2DPoint construction and expand() branching. Benchmark shows about 2.8x speedup. For polygon transform, extract matrix values once before the loop instead of calling rMatrix.get() per point. Applies to both the simple point path and the Bezier control vector path. Benchmark shows about 1.45x speedup. Change-Id: I56c1b415fd59dc24a5bec515d952064dee46403e Signed-off-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/2870 Reviewed-by: Noel Grandin <noel.grandin@collabora.com> Tested-by: Jenkins CPCI <releng@collaboraoffice.com> Reviewed-by: Miklos Vajna <vmiklos@collabora.com> (cherry picked from commit 6d682b4c9ae3e177a97be68ebeb7383c0148601d) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/205802 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
committed by
Xisco Fauli
parent
c4c4127727
commit
46ab3f7681
@@ -172,9 +172,19 @@ public:
|
||||
|
||||
void transform(const basegfx::B2DHomMatrix& rMatrix)
|
||||
{
|
||||
const double m00 = rMatrix.get(0, 0);
|
||||
const double m01 = rMatrix.get(0, 1);
|
||||
const double m02 = rMatrix.get(0, 2);
|
||||
const double m10 = rMatrix.get(1, 0);
|
||||
const double m11 = rMatrix.get(1, 1);
|
||||
const double m12 = rMatrix.get(1, 2);
|
||||
|
||||
for (auto& point : maVector)
|
||||
{
|
||||
point *= rMatrix;
|
||||
const double x = point.getX();
|
||||
const double y = point.getY();
|
||||
point.setX(m00 * x + m01 * y + m02);
|
||||
point.setY(m10 * x + m11 * y + m12);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1066,6 +1076,13 @@ public:
|
||||
|
||||
if(moControlVector)
|
||||
{
|
||||
const double m00 = rMatrix.get(0, 0);
|
||||
const double m01 = rMatrix.get(0, 1);
|
||||
const double m02 = rMatrix.get(0, 2);
|
||||
const double m10 = rMatrix.get(1, 0);
|
||||
const double m11 = rMatrix.get(1, 1);
|
||||
const double m12 = rMatrix.get(1, 2);
|
||||
|
||||
for(sal_uInt32 a(0); a < maPoints.count(); a++)
|
||||
{
|
||||
basegfx::B2DPoint aCandidate = maPoints.getCoordinate(a);
|
||||
@@ -1077,19 +1094,22 @@ public:
|
||||
|
||||
if(!rPrevVector.equalZero())
|
||||
{
|
||||
basegfx::B2DVector aPrevVector(rMatrix * rPrevVector);
|
||||
moControlVector->setPrevVector(a, aPrevVector);
|
||||
const double x = rPrevVector.getX();
|
||||
const double y = rPrevVector.getY();
|
||||
moControlVector->setPrevVector(a, basegfx::B2DVector(m00 * x + m01 * y, m10 * x + m11 * y));
|
||||
}
|
||||
|
||||
if(!rNextVector.equalZero())
|
||||
{
|
||||
basegfx::B2DVector aNextVector(rMatrix * rNextVector);
|
||||
moControlVector->setNextVector(a, aNextVector);
|
||||
const double x = rNextVector.getX();
|
||||
const double y = rNextVector.getY();
|
||||
moControlVector->setNextVector(a, basegfx::B2DVector(m00 * x + m01 * y, m10 * x + m11 * y));
|
||||
}
|
||||
}
|
||||
|
||||
aCandidate *= rMatrix;
|
||||
maPoints.setCoordinate(a, aCandidate);
|
||||
const double x = aCandidate.getX();
|
||||
const double y = aCandidate.getY();
|
||||
maPoints.setCoordinate(a, basegfx::B2DPoint(m00 * x + m01 * y + m02, m10 * x + m11 * y + m12));
|
||||
}
|
||||
|
||||
if(!moControlVector->isUsed())
|
||||
|
||||
@@ -37,15 +37,54 @@ namespace basegfx
|
||||
|
||||
void B2DRange::transform(const B2DHomMatrix& rMatrix)
|
||||
{
|
||||
if(!isEmpty() && !rMatrix.isIdentity())
|
||||
if(isEmpty() || rMatrix.isIdentity())
|
||||
return;
|
||||
|
||||
// Compute the AABB of the transformed box directly from matrix
|
||||
// coefficients, instead of transforming all four corners and
|
||||
// expanding. For an affine transform the extremes of each output
|
||||
// coordinate are reached at corners chosen per coefficient: a
|
||||
// non-negative coefficient pairs the input min with the output min,
|
||||
// a negative coefficient pairs the input max with the output min.
|
||||
// Translation is the same constant offset for min and max, so it
|
||||
// is folded into the initial value below.
|
||||
// (Arvo, Graphics Gems I, 1990.)
|
||||
|
||||
const double oldMinX = getMinX();
|
||||
const double oldMaxX = getMaxX();
|
||||
const double oldMinY = getMinY();
|
||||
const double oldMaxY = getMaxY();
|
||||
|
||||
const double m00 = rMatrix.get(0, 0);
|
||||
const double m01 = rMatrix.get(0, 1);
|
||||
const double m10 = rMatrix.get(1, 0);
|
||||
const double m11 = rMatrix.get(1, 1);
|
||||
|
||||
double newMinX = rMatrix.get(0, 2);
|
||||
double newMaxX = newMinX;
|
||||
double newMinY = rMatrix.get(1, 2);
|
||||
double newMaxY = newMinY;
|
||||
|
||||
auto axis = [](const double matrixValue, const double inLow, const double inHigh, double& outLow, double& outHigh)
|
||||
{
|
||||
const B2DRange aSource(*this);
|
||||
reset();
|
||||
expand(rMatrix * B2DPoint(aSource.getMinX(), aSource.getMinY()));
|
||||
expand(rMatrix * B2DPoint(aSource.getMaxX(), aSource.getMinY()));
|
||||
expand(rMatrix * B2DPoint(aSource.getMinX(), aSource.getMaxY()));
|
||||
expand(rMatrix * B2DPoint(aSource.getMaxX(), aSource.getMaxY()));
|
||||
}
|
||||
if (matrixValue >= 0.0)
|
||||
{
|
||||
outLow += matrixValue * inLow;
|
||||
outHigh += matrixValue * inHigh;
|
||||
}
|
||||
else
|
||||
{
|
||||
outLow += matrixValue * inHigh;
|
||||
outHigh += matrixValue * inLow;
|
||||
}
|
||||
};
|
||||
|
||||
axis(m00, oldMinX, oldMaxX, newMinX, newMaxX);
|
||||
axis(m01, oldMinY, oldMaxY, newMinX, newMaxX);
|
||||
axis(m10, oldMinX, oldMaxX, newMinY, newMaxY);
|
||||
axis(m11, oldMinY, oldMaxY, newMinY, newMaxY);
|
||||
|
||||
*this = B2DRange(newMinX, newMinY, newMaxX, newMaxY);
|
||||
}
|
||||
|
||||
void B2DRange::translate(double fTranslateX, double fTranslateY)
|
||||
|
||||
Reference in New Issue
Block a user