зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
d93a690a00
Коммит
16361eebae
|
@ -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).
|
||||
*/
|
||||
|
|
Загрузка…
Ссылка в новой задаче