Bug 1724839 - Move transform snapping code into gfxUtils. r=jrmuizel

Depends on D122173

Differential Revision: https://phabricator.services.mozilla.com/D122174
This commit is contained in:
Matt Woodrow 2021-08-10 03:38:41 +00:00
Родитель d93a690a00
Коммит 16361eebae
3 изменённых файлов: 177 добавлений и 105 удалений

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

@ -202,87 +202,12 @@ Matrix4x4 Layer::SnapTransformTranslation(const Matrix4x4& aTransform,
return aTransform;
}
Matrix matrix2D;
if (aTransform.CanDraw2D(&matrix2D) && !matrix2D.HasNonTranslation() &&
matrix2D.HasNonIntegerTranslation()) {
auto snappedTranslation = IntPoint::Round(matrix2D.GetTranslation());
Matrix snappedMatrix =
Matrix::Translation(snappedTranslation.x, snappedTranslation.y);
Matrix4x4 result = Matrix4x4::From2D(snappedMatrix);
if (aResidualTransform) {
// set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
// (I.e., appying snappedMatrix after aResidualTransform gives the
// ideal transform.)
*aResidualTransform =
Matrix::Translation(matrix2D._31 - snappedTranslation.x,
matrix2D._32 - snappedTranslation.y);
}
return result;
}
return SnapTransformTranslation3D(aTransform, aResidualTransform);
return gfxUtils::SnapTransformTranslation(aTransform, aResidualTransform);
}
Matrix4x4 Layer::SnapTransformTranslation3D(const Matrix4x4& aTransform,
Matrix* aResidualTransform) {
if (aTransform.IsSingular() || aTransform.HasPerspectiveComponent() ||
aTransform.HasNonTranslation() ||
!aTransform.HasNonIntegerTranslation()) {
// For a singular transform, there is no reversed matrix, so we
// don't snap it.
// For a perspective transform, the content is transformed in
// non-linear, so we don't snap it too.
return aTransform;
}
// Snap for 3D Transforms
Point3D transformedOrigin = aTransform.TransformPoint(Point3D());
// Compute the transformed snap by rounding the values of
// transformed origin.
auto transformedSnapXY =
IntPoint::Round(transformedOrigin.x, transformedOrigin.y);
Matrix4x4 inverse = aTransform;
inverse.Invert();
// see Matrix4x4::ProjectPoint()
Float transformedSnapZ =
inverse._33 == 0 ? 0
: (-(transformedSnapXY.x * inverse._13 +
transformedSnapXY.y * inverse._23 + inverse._43) /
inverse._33);
Point3D transformedSnap =
Point3D(transformedSnapXY.x, transformedSnapXY.y, transformedSnapZ);
if (transformedOrigin == transformedSnap) {
return aTransform;
}
// Compute the snap from the transformed snap.
Point3D snap = inverse.TransformPoint(transformedSnap);
if (snap.z > 0.001 || snap.z < -0.001) {
// Allow some level of accumulated computation error.
MOZ_ASSERT(inverse._33 == 0.0);
return aTransform;
}
// The difference between the origin and snap is the residual transform.
if (aResidualTransform) {
// The residual transform is to translate the snap to the origin
// of the content buffer.
*aResidualTransform = Matrix::Translation(-snap.x, -snap.y);
}
// Translate transformed origin to transformed snap since the
// residual transform would trnslate the snap to the origin.
Point3D transformedShift = transformedSnap - transformedOrigin;
Matrix4x4 result = aTransform;
result.PostTranslate(transformedShift.x, transformedShift.y,
transformedShift.z);
// For non-2d transform, residual translation could be more than
// 0.5 pixels for every axis.
return result;
return gfxUtils::SnapTransformTranslation3D(aTransform, aResidualTransform);
}
Matrix4x4 Layer::SnapTransform(const Matrix4x4& aTransform,
@ -292,35 +217,11 @@ Matrix4x4 Layer::SnapTransform(const Matrix4x4& aTransform,
*aResidualTransform = Matrix();
}
Matrix matrix2D;
Matrix4x4 result;
if (mManager->IsSnappingEffectiveTransforms() && aTransform.Is2D(&matrix2D) &&
gfxSize(1.0, 1.0) <= aSnapRect.Size() &&
matrix2D.PreservesAxisAlignedRectangles()) {
auto transformedTopLeft =
IntPoint::Round(matrix2D.TransformPoint(ToPoint(aSnapRect.TopLeft())));
auto transformedTopRight =
IntPoint::Round(matrix2D.TransformPoint(ToPoint(aSnapRect.TopRight())));
auto transformedBottomRight = IntPoint::Round(
matrix2D.TransformPoint(ToPoint(aSnapRect.BottomRight())));
Matrix snappedMatrix = gfxUtils::TransformRectToRect(
aSnapRect, transformedTopLeft, transformedTopRight,
transformedBottomRight);
result = Matrix4x4::From2D(snappedMatrix);
if (aResidualTransform && !snappedMatrix.IsSingular()) {
// set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
// (i.e., appying snappedMatrix after aResidualTransform gives the
// ideal transform.
Matrix snappedMatrixInverse = snappedMatrix;
snappedMatrixInverse.Invert();
*aResidualTransform = matrix2D * snappedMatrixInverse;
}
} else {
result = aTransform;
if (!mManager->IsSnappingEffectiveTransforms()) {
return aTransform;
}
return result;
return gfxUtils::SnapTransform(aTransform, aSnapRect, aResidualTransform);
}
static bool AncestorLayerMayChangeTransform(Layer* aLayer) {

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

@ -798,6 +798,132 @@ gfxQuad gfxUtils::TransformToQuad(const gfxRect& aRect,
return gfxQuad(points[0], points[1], points[2], points[3]);
}
Matrix4x4 gfxUtils::SnapTransformTranslation(const Matrix4x4& aTransform,
Matrix* aResidualTransform) {
if (aResidualTransform) {
*aResidualTransform = Matrix();
}
Matrix matrix2D;
if (aTransform.CanDraw2D(&matrix2D) && !matrix2D.HasNonTranslation() &&
matrix2D.HasNonIntegerTranslation()) {
auto snappedTranslation = IntPoint::Round(matrix2D.GetTranslation());
Matrix snappedMatrix =
Matrix::Translation(snappedTranslation.x, snappedTranslation.y);
Matrix4x4 result = Matrix4x4::From2D(snappedMatrix);
if (aResidualTransform) {
// set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
// (I.e., appying snappedMatrix after aResidualTransform gives the
// ideal transform.)
*aResidualTransform =
Matrix::Translation(matrix2D._31 - snappedTranslation.x,
matrix2D._32 - snappedTranslation.y);
}
return result;
}
return SnapTransformTranslation3D(aTransform, aResidualTransform);
}
Matrix4x4 gfxUtils::SnapTransformTranslation3D(const Matrix4x4& aTransform,
Matrix* aResidualTransform) {
if (aTransform.IsSingular() || aTransform.HasPerspectiveComponent() ||
aTransform.HasNonTranslation() ||
!aTransform.HasNonIntegerTranslation()) {
// For a singular transform, there is no reversed matrix, so we
// don't snap it.
// For a perspective transform, the content is transformed in
// non-linear, so we don't snap it too.
return aTransform;
}
// Snap for 3D Transforms
Point3D transformedOrigin = aTransform.TransformPoint(Point3D());
// Compute the transformed snap by rounding the values of
// transformed origin.
auto transformedSnapXY =
IntPoint::Round(transformedOrigin.x, transformedOrigin.y);
Matrix4x4 inverse = aTransform;
inverse.Invert();
// see Matrix4x4::ProjectPoint()
Float transformedSnapZ =
inverse._33 == 0 ? 0
: (-(transformedSnapXY.x * inverse._13 +
transformedSnapXY.y * inverse._23 + inverse._43) /
inverse._33);
Point3D transformedSnap =
Point3D(transformedSnapXY.x, transformedSnapXY.y, transformedSnapZ);
if (transformedOrigin == transformedSnap) {
return aTransform;
}
// Compute the snap from the transformed snap.
Point3D snap = inverse.TransformPoint(transformedSnap);
if (snap.z > 0.001 || snap.z < -0.001) {
// Allow some level of accumulated computation error.
MOZ_ASSERT(inverse._33 == 0.0);
return aTransform;
}
// The difference between the origin and snap is the residual transform.
if (aResidualTransform) {
// The residual transform is to translate the snap to the origin
// of the content buffer.
*aResidualTransform = Matrix::Translation(-snap.x, -snap.y);
}
// Translate transformed origin to transformed snap since the
// residual transform would trnslate the snap to the origin.
Point3D transformedShift = transformedSnap - transformedOrigin;
Matrix4x4 result = aTransform;
result.PostTranslate(transformedShift.x, transformedShift.y,
transformedShift.z);
// For non-2d transform, residual translation could be more than
// 0.5 pixels for every axis.
return result;
}
Matrix4x4 gfxUtils::SnapTransform(const Matrix4x4& aTransform,
const gfxRect& aSnapRect,
Matrix* aResidualTransform) {
if (aResidualTransform) {
*aResidualTransform = Matrix();
}
Matrix matrix2D;
Matrix4x4 result;
if (aTransform.Is2D(&matrix2D) && gfxSize(1.0, 1.0) <= aSnapRect.Size() &&
matrix2D.PreservesAxisAlignedRectangles()) {
auto transformedTopLeft =
IntPoint::Round(matrix2D.TransformPoint(ToPoint(aSnapRect.TopLeft())));
auto transformedTopRight =
IntPoint::Round(matrix2D.TransformPoint(ToPoint(aSnapRect.TopRight())));
auto transformedBottomRight = IntPoint::Round(
matrix2D.TransformPoint(ToPoint(aSnapRect.BottomRight())));
Matrix snappedMatrix = gfxUtils::TransformRectToRect(
aSnapRect, transformedTopLeft, transformedTopRight,
transformedBottomRight);
result = Matrix4x4::From2D(snappedMatrix);
if (aResidualTransform && !snappedMatrix.IsSingular()) {
// set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
// (i.e., appying snappedMatrix after aResidualTransform gives the
// ideal transform.
Matrix snappedMatrixInverse = snappedMatrix;
snappedMatrixInverse.Invert();
*aResidualTransform = matrix2D * snappedMatrixInverse;
}
} else {
result = aTransform;
}
return result;
}
/* static */
void gfxUtils::ClearThebesSurface(gfxASurface* aSurface) {
if (aSurface->CairoStatus()) {

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

@ -60,6 +60,7 @@ class gfxUtils {
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::gfx::IntPoint IntPoint;
typedef mozilla::gfx::Matrix Matrix;
typedef mozilla::gfx::Matrix4x4 Matrix4x4;
typedef mozilla::gfx::SourceSurface SourceSurface;
typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
typedef mozilla::image::ImageRegion ImageRegion;
@ -165,6 +166,50 @@ class gfxUtils {
*/
static float ClampToScaleFactor(float aVal, bool aRoundDown = false);
/**
* We can snap layer transforms for two reasons:
* 1) To avoid unnecessary resampling when a transform is a translation
* by a non-integer number of pixels.
* Snapping the translation to an integer number of pixels avoids
* blurring the layer and can be faster to composite.
* 2) When a layer is used to render a rectangular object, we need to
* emulate the rendering of rectangular inactive content and snap the
* edges of the rectangle to pixel boundaries. This is both to ensure
* layer rendering is consistent with inactive content rendering, and to
* avoid seams.
* This function implements type 1 snapping. If aTransform is a 2D
* translation, and this layer's layer manager has enabled snapping
* (which is the default), return aTransform with the translation snapped
* to nearest pixels. Otherwise just return aTransform. Call this when the
* layer does not correspond to a single rectangular content object.
* This function does not try to snap if aTransform has a scale, because in
* that case resampling is inevitable and there's no point in trying to
* avoid it. In fact snapping can cause problems because pixel edges in the
* layer's content can be rendered unpredictably (jiggling) as the scale
* interacts with the snapping of the translation, especially with animated
* transforms.
* @param aResidualTransform a transform to apply before the result transform
* in order to get the results to completely match aTransform.
*/
static Matrix4x4 SnapTransformTranslation(const Matrix4x4& aTransform,
Matrix* aResidualTransform);
static Matrix4x4 SnapTransformTranslation3D(const Matrix4x4& aTransform,
Matrix* aResidualTransform);
/**
* See comment for SnapTransformTranslation.
* This function implements type 2 snapping. If aTransform is a translation
* and/or scale, transform aSnapRect by aTransform, snap to pixel boundaries,
* and return the transform that maps aSnapRect to that rect. Otherwise
* just return aTransform.
* @param aSnapRect a rectangle whose edges should be snapped to pixel
* boundaries in the destination surface.
* @param aResidualTransform a transform to apply before the result transform
* in order to get the results to completely match aTransform.
*/
static Matrix4x4 SnapTransform(const Matrix4x4& aTransform,
const gfxRect& aSnapRect,
Matrix* aResidualTransform);
/**
* Clears surface to aColor (which defaults to transparent black).
*/