Bug 1082530, part 2 - Fix up Moz2D's UserToDevicePixelSnapped to be more like gfxContext::UserToDevicePixelSnapped, and add a temporary variant to aid in porting. r=mattwoodrow
authorJonathan Watt <jwatt@jwatt.org>
Sun, 19 Oct 2014 10:22:47 +0100
changeset 235455 61c3d66c10a14473f69d1895003f9b2a9b00f0d9
parent 235454 9a45f83eeda1f77697897a8f62d5e627f31f9ef3
child 235456 ce005765cf43f6278a37be50dd2a9dfa0cb150b0
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1082530
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1082530, part 2 - Fix up Moz2D's UserToDevicePixelSnapped to be more like gfxContext::UserToDevicePixelSnapped, and add a temporary variant to aid in porting. r=mattwoodrow
gfx/2d/PathHelpers.cpp
gfx/2d/PathHelpers.h
--- a/gfx/2d/PathHelpers.cpp
+++ b/gfx/2d/PathHelpers.cpp
@@ -3,16 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "PathHelpers.h"
 
 namespace mozilla {
 namespace gfx {
 
+UserDataKey sDisablePixelSnapping;
+
 void
 AppendRoundedRectToPath(PathBuilder* aPathBuilder,
                         const Rect& aRect,
                         // paren's needed due to operator precedence:
                         const Size(& aCornerRadii)[4],
                         bool aDrawClockwise)
 {
   // For CW drawing, this looks like:
--- a/gfx/2d/PathHelpers.h
+++ b/gfx/2d/PathHelpers.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_PATHHELPERS_H_
 #define MOZILLA_GFX_PATHHELPERS_H_
 
 #include "2D.h"
 #include "mozilla/Constants.h"
+#include "UserData.h"
 
 namespace mozilla {
 namespace gfx {
 
 template <typename T>
 void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius,
                  float aStartAngle, float aEndAngle, bool aAntiClockwise)
 {
@@ -176,22 +177,49 @@ GFX2D_API bool SnapLineToDevicePixelsFor
  * possible, but also that the ends of stroke dashes start and end on device
  * pixels too.
  */
 GFX2D_API void StrokeSnappedEdgesOfRect(const Rect& aRect,
                                         DrawTarget& aDrawTarget,
                                         const ColorPattern& aColor,
                                         const StrokeOptions& aStrokeOptions);
 
-static inline bool
-UserToDevicePixelSnapped(Rect& aRect, const Matrix& aTransform)
+extern UserDataKey sDisablePixelSnapping;
+
+/**
+ * If aDrawTarget's transform only contains a translation or, if
+ * aAllowScaleOr90DegreeRotate is true, and/or a scale/90 degree rotation, this
+ * function will convert aRect to device space and snap it to device pixels.
+ * This function returns true if aRect is modified, otherwise it returns false.
+ *
+ * Note that the snapping is such that filling the rect using a DrawTarget
+ * which has the identity matrix as its transform will result in crisp edges.
+ * (That is, aRect will have integer values, aligning its edges between pixel
+ * boundaries.)  If on the other hand you stroking the rect with an odd valued
+ * stroke width then the edges of the stroke will be antialiased (assuming an
+ * AntialiasMode that does antialiasing).
+ */
+inline bool UserToDevicePixelSnapped(Rect& aRect, const Matrix& aTransform,
+                                     bool aAllowScaleOr90DegreeRotate = false)
 {
-  Point p1 = aTransform * aRect.TopLeft();
-  Point p2 = aTransform * aRect.TopRight();
-  Point p3 = aTransform * aRect.BottomRight();
+  Matrix mat = aTransform;
+
+  const Float epsilon = 0.0000001f;
+#define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
+  if (!aAllowScaleOr90DegreeRotate &&
+      (!WITHIN_E(mat._11, 1.f) || !WITHIN_E(mat._22, 1.f) ||
+       !WITHIN_E(mat._12, 0.f) || !WITHIN_E(mat._21, 0.f))) {
+    // We have non-translation, but only translation is allowed.
+    return false;
+  }
+#undef WITHIN_E
+
+  Point p1 = mat * aRect.TopLeft();
+  Point p2 = mat * aRect.TopRight();
+  Point p3 = mat * aRect.BottomRight();
 
   // Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
   // two opposite corners define the entire rectangle. So check if
   // the axis-aligned rectangle with opposite corners p1 and p3
   // define an axis-aligned rectangle whose other corners are p2 and p4.
   // We actually only need to check one of p2 and p4, since an affine
   // transform maps parallelograms to parallelograms.
   if (p2 == Point(p1.x, p3.y) || p2 == Point(p3.x, p1.y)) {
@@ -202,12 +230,37 @@ UserToDevicePixelSnapped(Rect& aRect, co
       aRect.SizeTo(Size(std::max(p1.x, p3.x) - aRect.X(),
                         std::max(p1.y, p3.y) - aRect.Y()));
       return true;
   }
 
   return false;
 }
 
+inline bool UserToDevicePixelSnapped(Rect& aRect, const DrawTarget& aDrawTarget,
+                                     bool aIgnoreScale = false)
+{
+  if (aDrawTarget.GetUserData(&sDisablePixelSnapping)) {
+    return false;
+  }
+  return UserToDevicePixelSnapped(aRect, aDrawTarget.GetTransform());
+}
+
+/**
+ * This function has the same behavior as UserToDevicePixelSnapped except that
+ * aRect is not transformed to device space.
+ */
+inline void MaybeSnapToDevicePixels(Rect& aRect, const DrawTarget& aDrawTarget,
+                                    bool aIgnoreScale = false)
+{
+  if (UserToDevicePixelSnapped(aRect, aDrawTarget, aIgnoreScale)) {
+    // Since UserToDevicePixelSnapped returned true we know there is no
+    // rotation/skew in 'mat', so we can just use TransformBounds() here.
+    Matrix mat = aDrawTarget.GetTransform();
+    mat.Invert();
+    aRect = mat.TransformBounds(aRect);
+  }
+}
+
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_PATHHELPERS_H_ */