Bug 1626113 - Use mozilla::Span in TransformAndClipRect and do some refactoring. r=kip

Differential Revision: https://phabricator.services.mozilla.com/D68929

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Markus Stange 2020-04-02 04:01:01 +00:00
Родитель 6b7047492f
Коммит d00525a3a9
2 изменённых файлов: 98 добавлений и 75 удалений

Просмотреть файл

@ -114,5 +114,66 @@ Matrix4x4 MatrixDouble::operator*(const Matrix4x4& aMatrix) const {
return resultMatrix;
}
// Intersect the polygon given by aPoints with the half space induced by
// aPlaneNormal and return the resulting polygon. The returned points are
// stored in aDestBuffer, and its meaningful subspan is returned.
template <typename F>
Span<Point4DTyped<UnknownUnits, F>> IntersectPolygon(
Span<Point4DTyped<UnknownUnits, F>> aPoints,
const Point4DTyped<UnknownUnits, F>& aPlaneNormal,
Span<Point4DTyped<UnknownUnits, F>> aDestBuffer) {
if (aPoints.Length() < 3 || aDestBuffer.Length() < 3) {
return {};
}
size_t nextIndex = 0; // aDestBuffer[nextIndex] is the next emitted point.
// Iterate over the polygon edges. In each iteration the current edge
// is the edge from *prevPoint to point. If the two end points lie on
// different sides of the plane, we have an intersection. Otherwise,
// the edge is either completely "inside" the half-space created by
// the clipping plane, and we add curPoint, or it is completely
// "outside", and we discard curPoint. This loop can create duplicated
// points in the polygon.
const auto* prevPoint = &aPoints[aPoints.Length() - 1];
F prevDot = aPlaneNormal.DotProduct(*prevPoint);
for (const auto& curPoint : aPoints) {
F curDot = aPlaneNormal.DotProduct(curPoint);
if ((curDot >= 0.0) != (prevDot >= 0.0)) {
// An intersection with the clipping plane has been detected.
// Interpolate to find the intersecting curPoint and emit it.
F t = -prevDot / (curDot - prevDot);
aDestBuffer[nextIndex++] = curPoint * t + *prevPoint * (1.0 - t);
if (nextIndex >= aDestBuffer.Length()) {
break;
}
}
if (curDot >= 0.0) {
// Emit any source points that are on the positive side of the
// clipping plane.
aDestBuffer[nextIndex++] = curPoint;
if (nextIndex >= aDestBuffer.Length()) {
break;
}
}
prevPoint = &curPoint;
prevDot = curDot;
}
return aDestBuffer.To(nextIndex);
}
template Span<Point4DTyped<UnknownUnits, Float>> IntersectPolygon(
Span<Point4DTyped<UnknownUnits, Float>> aPoints,
const Point4DTyped<UnknownUnits, Float>& aPlaneNormal,
Span<Point4DTyped<UnknownUnits, Float>> aDestBuffer);
template Span<Point4DTyped<UnknownUnits, Double>> IntersectPolygon(
Span<Point4DTyped<UnknownUnits, Double>> aPoints,
const Point4DTyped<UnknownUnits, Double>& aPlaneNormal,
Span<Point4DTyped<UnknownUnits, Double>> aDestBuffer);
} // namespace gfx
} // namespace mozilla

Просмотреть файл

@ -17,6 +17,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Span.h"
namespace mozilla {
namespace gfx {
@ -26,6 +27,12 @@ static inline bool FuzzyEqual(Float aV1, Float aV2) {
return fabs(aV2 - aV1) < 1e-6;
}
template <typename F>
Span<Point4DTyped<UnknownUnits, F>> IntersectPolygon(
Span<Point4DTyped<UnknownUnits, F>> aPoints,
const Point4DTyped<UnknownUnits, F>& aPlaneNormal,
Span<Point4DTyped<UnknownUnits, F>> aDestBuffer);
template <class T>
class BaseMatrix {
// Alias that maps to either Point or PointDouble depending on whether T is a
@ -834,99 +841,54 @@ class Matrix4x4Typed {
return 0;
}
// Initialize a double-buffered array of points in homogenous space with
// the input rectangle, aRect.
Point4DTyped<UnknownUnits, F> points[2][kTransformAndClipRectMaxVerts];
Point4DTyped<UnknownUnits, F>* dstPointStart = points[0];
Point4DTyped<UnknownUnits, F>* dstPoint = dstPointStart;
typedef Point4DTyped<UnknownUnits, F> P4D;
*dstPoint++ = TransformPoint(
Point4DTyped<UnknownUnits, F>(aRect.X(), aRect.Y(), 0, 1));
*dstPoint++ = TransformPoint(
Point4DTyped<UnknownUnits, F>(aRect.XMost(), aRect.Y(), 0, 1));
*dstPoint++ = TransformPoint(
Point4DTyped<UnknownUnits, F>(aRect.XMost(), aRect.YMost(), 0, 1));
*dstPoint++ = TransformPoint(
Point4DTyped<UnknownUnits, F>(aRect.X(), aRect.YMost(), 0, 1));
// The initial polygon is made up by the corners of aRect in homogenous
// space, mapped into the destination space of this transform.
P4D rectCorners[] = {
TransformPoint(P4D(aRect.X(), aRect.Y(), 0, 1)),
TransformPoint(P4D(aRect.XMost(), aRect.Y(), 0, 1)),
TransformPoint(P4D(aRect.XMost(), aRect.YMost(), 0, 1)),
TransformPoint(P4D(aRect.X(), aRect.YMost(), 0, 1)),
};
// Cut off pieces of the polygon that are outside of aClip (the "view
// frustrum"), by consecutively intersecting the polygon with the half space
// induced by the clipping plane for each side of aClip.
// View frustum clipping planes are described as normals originating from
// the 0,0,0,0 origin.
Point4DTyped<UnknownUnits, F> planeNormals[4];
planeNormals[0] = Point4DTyped<UnknownUnits, F>(1.0, 0.0, 0.0, -aClip.X());
planeNormals[1] =
Point4DTyped<UnknownUnits, F>(-1.0, 0.0, 0.0, aClip.XMost());
planeNormals[2] = Point4DTyped<UnknownUnits, F>(0.0, 1.0, 0.0, -aClip.Y());
planeNormals[3] =
Point4DTyped<UnknownUnits, F>(0.0, -1.0, 0.0, aClip.YMost());
// Iterate through each clipping plane and clip the polygon.
// For each clipping plane, we intersect the plane with all polygon edges.
// Each pass can increase or decrease the number of points that make up the
// current clipped polygon. We double buffer that set of points, alternating
// between points[0] and points[1].
for (int plane = 0; plane < 4; plane++) {
auto planeNormal = planeNormals[plane];
Point4DTyped<UnknownUnits, F>* srcPoint = dstPointStart;
Point4DTyped<UnknownUnits, F>* srcPointEnd = dstPoint;
// current clipped polygon. We double buffer the set of points, alternating
// between polygonBufA and polygonBufB. Duplicated points in the polygons
// are kept around until all clipping is done. The loop at the end filters
// out any consecutive duplicates.
P4D polygonBufA[kTransformAndClipRectMaxVerts];
P4D polygonBufB[kTransformAndClipRectMaxVerts];
dstPointStart = points[~plane & 1];
dstPoint = dstPointStart;
Span<P4D> polygon(rectCorners);
polygon = IntersectPolygon<F>(polygon, P4D(1.0, 0.0, 0.0, -aClip.X()),
polygonBufA);
polygon = IntersectPolygon<F>(polygon, P4D(-1.0, 0.0, 0.0, aClip.XMost()),
polygonBufB);
polygon = IntersectPolygon<F>(polygon, P4D(0.0, 1.0, 0.0, -aClip.Y()),
polygonBufA);
polygon = IntersectPolygon<F>(polygon, P4D(0.0, -1.0, 0.0, aClip.YMost()),
polygonBufB);
// Iterate over the polygon edges. In each iteration the current edge is
// the edge from prevPoint to srcPoint. If the two end points lie on
// different sides of the plane, we have an intersection. Otherwise, the
// edge is either completely "inside" the half-space created by the
// clipping plane, and we add srcPoint, or it is completely "outside", and
// we discard srcPoint.
// We may create duplicated points in the polygon. We keep those around
// until all clipping is done and then filter out duplicates at the end.
Point4DTyped<UnknownUnits, F>* prevPoint = srcPointEnd - 1;
F prevDot = planeNormal.DotProduct(*prevPoint);
while (srcPoint < srcPointEnd &&
((dstPoint - dstPointStart) < kTransformAndClipRectMaxVerts)) {
F nextDot = planeNormal.DotProduct(*srcPoint);
if ((nextDot >= 0.0) != (prevDot >= 0.0)) {
// An intersection with the clipping plane has been detected.
// Interpolate to find the intersecting point and emit it.
F t = -prevDot / (nextDot - prevDot);
*dstPoint++ = *srcPoint * t + *prevPoint * (1.0 - t);
}
if (nextDot >= 0.0) {
// Emit any source points that are on the positive side of the
// clipping plane.
*dstPoint++ = *srcPoint;
}
prevPoint = srcPoint++;
prevDot = nextDot;
}
if (dstPoint == dstPointStart) {
// No polygon points were produced, so the polygon has been
// completely clipped away by the current clipping plane. Exit.
break;
}
}
Point4DTyped<UnknownUnits, F>* srcPoint = dstPointStart;
Point4DTyped<UnknownUnits, F>* srcPointEnd = dstPoint;
size_t vertCount = 0;
while (srcPoint < srcPointEnd) {
for (const auto& srcPoint : polygon) {
PointTyped<TargetUnits, F> p;
if (srcPoint->w == 0.0) {
if (srcPoint.w == 0.0) {
// If a point lies on the intersection of the clipping planes at
// (0,0,0,0), we must avoid a division by zero w component.
p = PointTyped<TargetUnits, F>(0.0, 0.0);
} else {
p = srcPoint->As2DPoint();
p = srcPoint.As2DPoint();
}
// Emit only unique points
if (vertCount == 0 || p != aVerts[vertCount - 1]) {
aVerts[vertCount++] = p;
}
srcPoint++;
}
return vertCount;