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:
Tomaž Vajngerl
2026-06-09 11:46:22 +02:00
committed by Xisco Fauli
parent c4c4127727
commit 46ab3f7681
2 changed files with 74 additions and 15 deletions
+27 -7
View File
@@ -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())
+47 -8
View File
@@ -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)