Rect Matrix4x4::ProjectRectBounds(const Rect& aRect) const { Point4D points[4]; points[0] = ProjectPoint(aRect.TopLeft()); points[1] = ProjectPoint(aRect.TopRight()); points[2] = ProjectPoint(aRect.BottomRight()); points[3] = ProjectPoint(aRect.BottomLeft()); Float min_x = std::numeric_limits<Float>::max(); Float min_y = std::numeric_limits<Float>::max(); Float max_x = -std::numeric_limits<Float>::max(); Float max_y = -std::numeric_limits<Float>::max(); bool foundPoint = false; for (int i=0; i<4; i++) { // Only use points that exist above the w=0 plane if (points[i].HasPositiveWCoord()) { foundPoint = true; Point point2d = points[i].As2DPoint(); min_x = min<Float>(point2d.x, min_x); max_x = max<Float>(point2d.x, max_x); min_y = min<Float>(point2d.y, min_y); max_y = max<Float>(point2d.y, max_y); } int next = (i == 3) ? 0 : i + 1; if (points[i].HasPositiveWCoord() != points[next].HasPositiveWCoord()) { // If the line between two points crosses the w=0 plane, then interpolate a point // as close to the w=0 plane as possible and use that instead. Point4D intercept = ComputePerspectivePlaneIntercept(points[i], points[next]); Point point2d = intercept.As2DPoint(); min_x = min<Float>(point2d.x, min_x); max_x = max<Float>(point2d.x, max_x); min_y = min<Float>(point2d.y, min_y); max_y = max<Float>(point2d.y, max_y); } } if (!foundPoint) { return Rect(0, 0, 0, 0); } return Rect(min_x, min_y, max_x - min_x, max_y - min_y); }
Rect Matrix4x4::ProjectRectBounds(const Rect& aRect, const Rect &aClip) const { // This function must never return std::numeric_limits<Float>::max() or any // other arbitrary large value in place of inifinity. This often occurs when // aRect is an inversed projection matrix or when aRect is transformed to be // partly behind and in front of the camera (w=0 plane in homogenous // coordinates) - See Bug 1035611 // Some call-sites will call RoundGfxRectToAppRect which clips both the // extents and dimensions of the rect to be bounded by nscoord_MAX. // If we return a Rect that, when converted to nscoords, has a width or height // greater than nscoord_MAX, RoundGfxRectToAppRect will clip the overflow // off both the min and max end of the rect after clipping the extents of the // rect, resulting in a translation of the rect towards the infinite end. // The bounds returned by ProjectRectBounds are expected to be clipped only on // the edges beyond the bounds of the coordinate system; otherwise, the // clipped bounding box would be smaller than the correct one and result // bugs such as incorrect culling (eg. Bug 1073056) // To address this without requiring all code to work in homogenous // coordinates or interpret infinite values correctly, a specialized // clipping function is integrated into ProjectRectBounds. // Callers should pass an aClip value that represents the extents to clip // the result to, in the same coordinate system as aRect. Point4D points[4]; points[0] = ProjectPoint(aRect.TopLeft()); points[1] = ProjectPoint(aRect.TopRight()); points[2] = ProjectPoint(aRect.BottomRight()); points[3] = ProjectPoint(aRect.BottomLeft()); Float min_x = std::numeric_limits<Float>::max(); Float min_y = std::numeric_limits<Float>::max(); Float max_x = -std::numeric_limits<Float>::max(); Float max_y = -std::numeric_limits<Float>::max(); for (int i=0; i<4; i++) { // Only use points that exist above the w=0 plane if (points[i].HasPositiveWCoord()) { Point point2d = aClip.ClampPoint(points[i].As2DPoint()); min_x = std::min<Float>(point2d.x, min_x); max_x = std::max<Float>(point2d.x, max_x); min_y = std::min<Float>(point2d.y, min_y); max_y = std::max<Float>(point2d.y, max_y); } int next = (i == 3) ? 0 : i + 1; if (points[i].HasPositiveWCoord() != points[next].HasPositiveWCoord()) { // If the line between two points crosses the w=0 plane, then interpolate // to find the point of intersection with the w=0 plane and use that // instead. Point4D intercept = ComputePerspectivePlaneIntercept(points[i], points[next]); // Since intercept.w will always be 0 here, we interpret x,y,z as a // direction towards an infinite vanishing point. if (intercept.x < 0.0f) { min_x = aClip.x; } else if (intercept.x > 0.0f) { max_x = aClip.XMost(); } if (intercept.y < 0.0f) { min_y = aClip.y; } else if (intercept.y > 0.0f) { max_y = aClip.YMost(); } } } if (max_x < min_x || max_y < min_y) { return Rect(0, 0, 0, 0); } return Rect(min_x, min_y, max_x - min_x, max_y - min_y); }