зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1351426 - Part 4: Refactor gfx::Polygon to avoid unnecessary work and memory allocations r=kip
MozReview-Commit-ID: ASusoTqZxuY --HG-- extra : rebase_source : f9968b6993fdfd0ff62afcae35ec2ad805d37042
This commit is contained in:
Родитель
6bb26910a0
Коммит
e0b243a92d
232
gfx/2d/Polygon.h
232
gfx/2d/Polygon.h
|
@ -27,11 +27,15 @@ CalculateEdgeIntersect(const Point4DTyped<Units>& aFirst,
|
|||
return aFirst + (aSecond - aFirst) * t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clips the polygon defined by |aPoints| so that there are no points with
|
||||
* w <= 0.
|
||||
*/
|
||||
template<class Units>
|
||||
nsTArray<Point4DTyped<Units>>
|
||||
ClipHomogeneous(const nsTArray<Point4DTyped<Units>>& aPoints)
|
||||
ClipPointsAtInfinity(const nsTArray<Point4DTyped<Units>>& aPoints)
|
||||
{
|
||||
nsTArray<Point4DTyped<Units>> outPoints;
|
||||
nsTArray<Point4DTyped<Units>> outPoints(aPoints.Length());
|
||||
|
||||
const size_t pointCount = aPoints.Length();
|
||||
for (size_t i = 0; i < pointCount; ++i) {
|
||||
|
@ -56,16 +60,90 @@ ClipHomogeneous(const nsTArray<Point4DTyped<Units>>& aPoints)
|
|||
}
|
||||
|
||||
template<class Units>
|
||||
nsTArray<Point4DTyped<Units>>
|
||||
ToPoints4D(const nsTArray<Point3DTyped<Units>>& aPoints)
|
||||
nsTArray<float>
|
||||
CalculatePointPlaneDistances(const nsTArray<Point4DTyped<Units>>& aPoints,
|
||||
const Point4DTyped<Units>& aPlaneNormal,
|
||||
const Point4DTyped<Units>& aPlanePoint,
|
||||
size_t& aPos, size_t& aNeg)
|
||||
{
|
||||
nsTArray<Point4DTyped<Units>> points;
|
||||
// Point classification might produce incorrect results due to numerical
|
||||
// inaccuracies. Using an epsilon value makes the splitting plane "thicker".
|
||||
const float epsilon = 0.05f;
|
||||
|
||||
for (const Point3DTyped<Units>& point : aPoints) {
|
||||
points.AppendElement(Point4DTyped<Units>(point));
|
||||
aPos = aNeg = 0;
|
||||
nsTArray<float> distances(aPoints.Length());
|
||||
|
||||
for (const Point4DTyped<Units>& point : aPoints) {
|
||||
float dot = (point - aPlanePoint).DotProduct(aPlaneNormal);
|
||||
|
||||
if (dot > epsilon) {
|
||||
aPos++;
|
||||
} else if (dot < -epsilon) {
|
||||
aNeg++;
|
||||
} else {
|
||||
// The point is within the thick plane.
|
||||
dot = 0.0f;
|
||||
}
|
||||
|
||||
distances.AppendElement(dot);
|
||||
}
|
||||
|
||||
return points;
|
||||
return distances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clips the polygon defined by |aPoints|. The clipping uses previously
|
||||
* calculated plane to point distances and the plane normal |aNormal|.
|
||||
* The result of clipping is stored in |aBackPoints| and |aFrontPoints|.
|
||||
*/
|
||||
template<class Units>
|
||||
void
|
||||
ClipPointsWithPlane(const nsTArray<Point4DTyped<Units>>& aPoints,
|
||||
const Point4DTyped<Units>& aNormal,
|
||||
const nsTArray<float>& aDots,
|
||||
nsTArray<Point4DTyped<Units>>& aBackPoints,
|
||||
nsTArray<Point4DTyped<Units>>& aFrontPoints)
|
||||
{
|
||||
static const auto Sign = [](const float& f) {
|
||||
if (f > 0.0f) return 1;
|
||||
if (f < 0.0f) return -1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
const size_t pointCount = aPoints.Length();
|
||||
for (size_t i = 0; i < pointCount; ++i) {
|
||||
size_t j = (i + 1) % pointCount;
|
||||
|
||||
const Point4DTyped<Units>& a = aPoints[i];
|
||||
const Point4DTyped<Units>& b = aPoints[j];
|
||||
const float dotA = aDots[i];
|
||||
const float dotB = aDots[j];
|
||||
|
||||
// The point is in front of or on the plane.
|
||||
if (dotA >= 0) {
|
||||
aFrontPoints.AppendElement(a);
|
||||
}
|
||||
|
||||
// The point is behind or on the plane.
|
||||
if (dotA <= 0) {
|
||||
aBackPoints.AppendElement(a);
|
||||
}
|
||||
|
||||
// If the sign of the dot products changes between two consecutive
|
||||
// vertices, then the plane intersects with the polygon edge.
|
||||
// The case where the polygon edge is within the plane is handled above.
|
||||
if (Sign(dotA) && Sign(dotB) && Sign(dotA) != Sign(dotB)) {
|
||||
// Calculate the line segment and plane intersection point.
|
||||
const Point4DTyped<Units> ab = b - a;
|
||||
const float dotAB = ab.DotProduct(aNormal);
|
||||
const float t = -dotA / dotAB;
|
||||
const Point4DTyped<Units> p = a + (ab * t);
|
||||
|
||||
// Add the intersection point to both polygons.
|
||||
aBackPoints.AppendElement(p);
|
||||
aFrontPoints.AppendElement(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PolygonTyped stores the points of a convex planar polygon.
|
||||
|
@ -89,9 +167,9 @@ public:
|
|||
const Point4DType& aNormal = DefaultNormal())
|
||||
: mNormal(aNormal), mPoints(aPoints)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG
|
||||
EnsurePlanarPolygon();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
RectTyped<Units> BoundingBox() const
|
||||
|
@ -115,38 +193,6 @@ public:
|
|||
return RectTyped<Units>(minX, minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
|
||||
nsTArray<float> CalculateDotProducts(const PolygonTyped<Units>& aPlane,
|
||||
size_t& aPos, size_t& aNeg) const
|
||||
{
|
||||
// Point classification might produce incorrect results due to numerical
|
||||
// inaccuracies. Using an epsilon value makes the splitting plane "thicker".
|
||||
const float epsilon = 0.05f;
|
||||
|
||||
MOZ_ASSERT(!aPlane.GetPoints().IsEmpty());
|
||||
const Point4DType& planeNormal = aPlane.GetNormal();
|
||||
const Point4DType& planePoint = aPlane[0];
|
||||
|
||||
aPos = aNeg = 0;
|
||||
nsTArray<float> dotProducts;
|
||||
|
||||
for (const Point4DType& point : mPoints) {
|
||||
float dot = (point - planePoint).DotProduct(planeNormal);
|
||||
|
||||
if (dot > epsilon) {
|
||||
aPos++;
|
||||
} else if (dot < -epsilon) {
|
||||
aNeg++;
|
||||
} else {
|
||||
// The point is within the thick plane.
|
||||
dot = 0.0f;
|
||||
}
|
||||
|
||||
dotProducts.AppendElement(dot);
|
||||
}
|
||||
|
||||
return dotProducts;
|
||||
}
|
||||
|
||||
// Clips the polygon against the given 2D rectangle.
|
||||
PolygonTyped<Units> ClipPolygon(const RectTyped<Units>& aRect) const
|
||||
{
|
||||
|
@ -157,7 +203,7 @@ public:
|
|||
return ClipPolygon(FromRect(aRect));
|
||||
}
|
||||
|
||||
// Clips the polygon against the given polygon in 2D.
|
||||
// Clips this polygon against the given polygon in 2D.
|
||||
PolygonTyped<Units> ClipPolygon(const PolygonTyped<Units>& aPolygon) const
|
||||
{
|
||||
const nsTArray<Point4DType>& points = aPolygon.GetPoints();
|
||||
|
@ -166,25 +212,43 @@ public:
|
|||
return PolygonTyped<Units>();
|
||||
}
|
||||
|
||||
PolygonTyped<Units> polygon(mPoints, mNormal);
|
||||
nsTArray<Point4DType> clippedPoints(mPoints);
|
||||
|
||||
size_t pos, neg;
|
||||
nsTArray<Point4DType> backPoints(4), frontPoints(4);
|
||||
|
||||
// Iterate over all the edges of the clipping polygon |aPolygon| and clip
|
||||
// this polygon against the edges.
|
||||
const size_t pointCount = points.Length();
|
||||
for (size_t i = 0; i < pointCount; ++i) {
|
||||
const Point4DType p1 = points[(i + 1) % pointCount];
|
||||
const Point4DType p2 = points[i];
|
||||
|
||||
// Calculate the normal for the edge defined by |p1| and |p2|.
|
||||
const Point4DType normal(p2.y - p1.y, p1.x - p2.x, 0.0f, 0.0f);
|
||||
const PolygonTyped<Units> plane({p1, p2}, normal);
|
||||
|
||||
ClipPolygonWithPlane(polygon, plane);
|
||||
// Calculate the distances between the points of the polygon and the
|
||||
// plane defined by |aPolygon|.
|
||||
const nsTArray<float> distances =
|
||||
CalculatePointPlaneDistances(clippedPoints, normal, p1, pos, neg);
|
||||
|
||||
if (polygon.IsEmpty()) {
|
||||
backPoints.ClearAndRetainStorage();
|
||||
frontPoints.ClearAndRetainStorage();
|
||||
|
||||
// Clip the polygon points using the previously calculated distances.
|
||||
ClipPointsWithPlane(clippedPoints, normal, distances,
|
||||
backPoints, frontPoints);
|
||||
|
||||
// Only use the points behind the clipping plane.
|
||||
clippedPoints = Move(backPoints);
|
||||
|
||||
if (clippedPoints.Length() < 3) {
|
||||
// The clipping created a polygon with no area.
|
||||
return PolygonTyped<Units>();
|
||||
}
|
||||
}
|
||||
|
||||
return polygon;
|
||||
return PolygonTyped<Units>(Move(clippedPoints), mNormal);
|
||||
}
|
||||
|
||||
static PolygonTyped<Units> FromRect(const RectTyped<Units>& aRect)
|
||||
|
@ -209,65 +273,12 @@ public:
|
|||
return mPoints;
|
||||
}
|
||||
|
||||
const Point4DType& operator[](size_t aIndex) const
|
||||
{
|
||||
MOZ_ASSERT(mPoints.Length() > aIndex);
|
||||
return mPoints[aIndex];
|
||||
}
|
||||
|
||||
bool IsEmpty() const
|
||||
{
|
||||
// If the polygon has less than three points, it has no visible area.
|
||||
return mPoints.Length() < 3;
|
||||
}
|
||||
|
||||
void SplitPolygon(const Point4DType& aNormal,
|
||||
const nsTArray<float>& aDots,
|
||||
nsTArray<Point4DType>& aBackPoints,
|
||||
nsTArray<Point4DType>& aFrontPoints) const
|
||||
{
|
||||
static const auto Sign = [](const float& f) {
|
||||
if (f > 0.0f) return 1;
|
||||
if (f < 0.0f) return -1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
const size_t pointCount = mPoints.Length();
|
||||
for (size_t i = 0; i < pointCount; ++i) {
|
||||
size_t j = (i + 1) % pointCount;
|
||||
|
||||
const Point4DType& a = mPoints[i];
|
||||
const Point4DType& b = mPoints[j];
|
||||
const float dotA = aDots[i];
|
||||
const float dotB = aDots[j];
|
||||
|
||||
// The point is in front of or on the plane.
|
||||
if (dotA >= 0) {
|
||||
aFrontPoints.AppendElement(a);
|
||||
}
|
||||
|
||||
// The point is behind or on the plane.
|
||||
if (dotA <= 0) {
|
||||
aBackPoints.AppendElement(a);
|
||||
}
|
||||
|
||||
// If the sign of the dot products changes between two consecutive
|
||||
// vertices, then the plane intersects with the polygon edge.
|
||||
// The case where the polygon edge is within the plane is handled above.
|
||||
if (Sign(dotA) && Sign(dotB) && Sign(dotA) != Sign(dotB)) {
|
||||
// Calculate the line segment and plane intersection point.
|
||||
const Point4DType ab = b - a;
|
||||
const float dotAB = ab.DotProduct(aNormal);
|
||||
const float t = -dotA / dotAB;
|
||||
const Point4DType p = a + (ab * t);
|
||||
|
||||
// Add the intersection point to both polygons.
|
||||
aBackPoints.AppendElement(p);
|
||||
aFrontPoints.AppendElement(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<TriangleTyped<Units>> ToTriangles() const
|
||||
{
|
||||
nsTArray<TriangleTyped<Units>> triangles;
|
||||
|
@ -297,27 +308,16 @@ public:
|
|||
MOZ_ASSERT(!aTransform.IsSingular());
|
||||
|
||||
TransformPoints(aTransform, false);
|
||||
mPoints = ClipHomogeneous(mPoints);
|
||||
|
||||
// Perspective projection transformation might produce points with w <= 0,
|
||||
// so we need to clip these points.
|
||||
mPoints = ClipPointsAtInfinity(mPoints);
|
||||
|
||||
// Normal vectors should be transformed using inverse transpose.
|
||||
mNormal = aTransform.Inverse().Transpose().TransformPoint(mNormal);
|
||||
}
|
||||
|
||||
private:
|
||||
void ClipPolygonWithPlane(PolygonTyped<Units>& aPolygon,
|
||||
const PolygonTyped<Units>& aPlane) const
|
||||
{
|
||||
size_t pos, neg;
|
||||
const nsTArray<float> dots =
|
||||
aPolygon.CalculateDotProducts(aPlane, pos, neg);
|
||||
|
||||
nsTArray<Point4DType> backPoints, frontPoints;
|
||||
aPolygon.SplitPolygon(aPlane.GetNormal(), dots, backPoints, frontPoints);
|
||||
|
||||
// Only use the points that are behind the clipping plane.
|
||||
aPolygon = PolygonTyped<Units>(Move(backPoints), aPolygon.GetNormal());
|
||||
}
|
||||
|
||||
static Point4DType DefaultNormal()
|
||||
{
|
||||
return Point4DType(0.0f, 0.0f, 1.0f, 0.0f);
|
||||
|
|
|
@ -57,13 +57,19 @@ BSPTree::BuildTree(BSPTreeNode* aRoot,
|
|||
}
|
||||
|
||||
const gfx::Polygon& plane = aRoot->First();
|
||||
MOZ_ASSERT(!plane.IsEmpty());
|
||||
|
||||
const gfx::Point4D& planeNormal = plane.GetNormal();
|
||||
const gfx::Point4D& planePoint = plane.GetPoints()[0];
|
||||
|
||||
std::list<LayerPolygon> backLayers, frontLayers;
|
||||
for (LayerPolygon& layerPolygon : aLayers) {
|
||||
const Maybe<gfx::Polygon>& geometry = layerPolygon.geometry;
|
||||
const nsTArray<gfx::Point4D>& geometry = layerPolygon.geometry->GetPoints();
|
||||
|
||||
// Calculate the plane-point distances for the polygon classification.
|
||||
size_t pos = 0, neg = 0;
|
||||
nsTArray<float> dots = geometry->CalculateDotProducts(plane, pos, neg);
|
||||
nsTArray<float> distances =
|
||||
CalculatePointPlaneDistances(geometry, planeNormal, planePoint, pos, neg);
|
||||
|
||||
// Back polygon
|
||||
if (pos == 0 && neg > 0) {
|
||||
|
@ -80,10 +86,13 @@ BSPTree::BuildTree(BSPTreeNode* aRoot,
|
|||
// Polygon intersects with the splitting plane.
|
||||
else if (pos > 0 && neg > 0) {
|
||||
nsTArray<gfx::Point4D> backPoints, frontPoints;
|
||||
geometry->SplitPolygon(plane.GetNormal(), dots, backPoints, frontPoints);
|
||||
// Clip the polygon against the plane. We reuse the previously calculated
|
||||
// distances to find the plane-edge intersections.
|
||||
ClipPointsWithPlane(geometry, planeNormal, distances,
|
||||
backPoints, frontPoints);
|
||||
|
||||
const gfx::Point4D& normal = geometry->GetNormal();
|
||||
Layer *layer = layerPolygon.layer;
|
||||
const gfx::Point4D& normal = layerPolygon.geometry->GetNormal();
|
||||
Layer* layer = layerPolygon.layer;
|
||||
|
||||
if (backPoints.Length() >= 3) {
|
||||
backLayers.emplace_back(layer, Move(backPoints), normal);
|
||||
|
|
Загрузка…
Ссылка в новой задаче