Bug 586683 - Part 1 - Add resolution handling to BasicLayers. r=roc a=blocking2.0
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 09 Feb 2011 09:35:54 +1300
changeset 62161 5ed6c4935486d1aee52f9edcd96043b0fab86fb8
parent 62160 ffe65159d361f313c2f7dbf83e498acf112b0edd
child 62162 b0512b9a8c11ae974391a714753aca994857a391
push id18625
push usermwoodrow@mozilla.com
push dateTue, 08 Feb 2011 20:45:21 +0000
treeherdermozilla-central@0b4c13e87e5a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, blocking2
bugs586683
milestone2.0b12pre
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 586683 - Part 1 - Add resolution handling to BasicLayers. r=roc a=blocking2.0
gfx/layers/basic/BasicLayers.cpp
gfx/src/nsRect.cpp
gfx/src/nsRect.h
gfx/src/nsRegion.cpp
gfx/src/nsRegion.h
gfx/thebes/gfxUtils.cpp
gfx/thebes/gfxUtils.h
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -549,28 +549,30 @@ BasicThebesLayer::Paint(gfxContext* aCon
       }
 
       aContext->Restore();
     }
     return;
   }
 
   {
-    float paintXRes = BasicManager()->XResolution();
-    float paintYRes = BasicManager()->YResolution();
+    gfxSize scale = aContext->CurrentMatrix().ScaleFactors(PR_TRUE);
+    float paintXRes = gfxUtils::ClampToScaleFactor(BasicManager()->XResolution() * scale.width);
+    float paintYRes = gfxUtils::ClampToScaleFactor(BasicManager()->YResolution() * scale.height);
     Buffer::PaintState state =
       mBuffer.BeginPaint(this, contentType, paintXRes, paintYRes);
     mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
 
     if (state.mContext) {
       // The area that became invalid and is visible needs to be repainted
       // (this could be the whole visible area if our buffer switched
       // from RGB to RGBA, because we might need to repaint with
       // subpixel AA)
       state.mRegionToInvalidate.And(state.mRegionToInvalidate, mVisibleRegion);
+      state.mRegionToDraw.ExtendForScaling(paintXRes, paintYRes);
       mXResolution = paintXRes;
       mYResolution = paintYRes;
       SetAntialiasingFlags(this, state.mContext);
       PaintBuffer(state.mContext,
                   state.mRegionToDraw, state.mRegionToInvalidate,
                   aCallback, aCallbackData);
       Mutated();
     } else {
--- a/gfx/src/nsRect.cpp
+++ b/gfx/src/nsRect.cpp
@@ -200,16 +200,42 @@ nsRect& nsRect::ScaleRoundOut(float aXSc
   nscoord bottom = NSToCoordCeil(float(YMost()) * aYScale);
   x = NSToCoordFloor(float(x) * aXScale);
   y = NSToCoordFloor(float(y) * aYScale);
   width = (right - x);
   height = (bottom - y);
   return *this;
 }
 
+static bool IsFloatInteger(float aFloat)
+{
+  return fabs(aFloat - NS_round(aFloat)) < 1e-6;
+}
+
+nsRect& nsRect::ExtendForScaling(float aXMult, float aYMult)
+{
+  NS_ASSERTION((IsFloatInteger(aXMult) || IsFloatInteger(1/aXMult)) &&
+               (IsFloatInteger(aYMult) || IsFloatInteger(1/aYMult)),
+               "Multiplication factors must be integers or 1/integer");
+               
+  // Scale rect by multiplier, snap outwards to integers and then unscale.
+  // We round the results to the nearest integer to prevent floating point errors.
+  if (aXMult < 1) {
+    nscoord right = NSToCoordRound(ceil(float(XMost()) * aXMult) / aXMult);
+    x = NSToCoordRound(floor(float(x) * aXMult) / aXMult);
+    width = right - x;
+  }
+  if (aYMult < 1) {
+    nscoord bottom = NSToCoordRound(ceil(float(YMost()) * aYMult) / aYMult);
+    y = NSToCoordRound(floor(float(y) * aYMult) / aYMult);
+    height = bottom - y;
+  }
+  return *this;
+}
+
 #ifdef DEBUG
 // Diagnostics
 
 FILE* operator<<(FILE* out, const nsRect& rect)
 {
   nsAutoString tmp;
 
   // Output the coordinates in fractional pixels so they're easier to read
--- a/gfx/src/nsRect.h
+++ b/gfx/src/nsRect.h
@@ -178,16 +178,21 @@ struct NS_GFX nsRect {
   nsRect  operator+(const nsMargin& aMargin) const { return nsRect(*this) += aMargin; }
   nsRect  operator-(const nsMargin& aMargin) const { return nsRect(*this) -= aMargin; }
 
   // Scale by aScale, converting coordinates to integers so that the result is
   // the smallest integer-coordinate rectangle containing the unrounded result.
   nsRect& ScaleRoundOut(float aScale) { return ScaleRoundOut(aScale, aScale); }
   nsRect& ScaleRoundOut(float aXScale, float aYScale);
 
+  // Extend the rect outwards such that the edges are on integer boundaries
+  // and the edges scaled by aXMult/aYMult are also on integer boundaries.
+  // aXMult/aYMult must be N or 1/N for integer N.
+  nsRect& ExtendForScaling(float aXMult, float aYMult);
+
   // Converts this rect from aFromAPP, an appunits per pixel ratio, to aToAPP.
   // In the RoundOut version we make the rect the smallest rect containing the
   // unrounded result. In the RoundIn version we make the rect the largest rect
   // contained in the unrounded result.
   inline nsRect ConvertAppUnitsRoundOut(PRInt32 aFromAPP, PRInt32 aToAPP) const;
   inline nsRect ConvertAppUnitsRoundIn(PRInt32 aFromAPP, PRInt32 aToAPP) const;
 
   // Helpers for accessing the vertices
--- a/gfx/src/nsRegion.cpp
+++ b/gfx/src/nsRegion.cpp
@@ -1296,16 +1296,32 @@ void nsRegion::MoveBy (nsPoint aPt)
       pRect->MoveBy (aPt.x, aPt.y);
       pRect = pRect->next;
     }
 
     mBoundRect.MoveBy (aPt.x, aPt.y);
   }
 }
 
+nsRegion& nsRegion::ExtendForScaling (float aXMult, float aYMult)
+{
+  nsRegion region;
+  nsRegionRectIterator iter(*this);
+  for (;;) {
+    const nsRect* r = iter.Next();
+    if (!r)
+      break;
+    nsRect rect = *r;
+    rect.ExtendForScaling(aXMult, aYMult);
+    region.Or(region, rect);
+  }
+  *this = region;
+  return *this;
+}
+
 nsRegion nsRegion::ConvertAppUnitsRoundOut (PRInt32 aFromAPP, PRInt32 aToAPP) const
 {
   if (aFromAPP == aToAPP) {
     return *this;
   }
   // Do it in a simplistic and slow way to avoid any weird behaviour with
   // rounding causing rects to overlap. Should be fast enough for what we need.
   nsRegion region;
--- a/gfx/src/nsRegion.h
+++ b/gfx/src/nsRegion.h
@@ -180,16 +180,18 @@ public:
   PRUint32 GetNumRects () const { return mRectCount; }
   const nsRect& GetBounds () const { return mBoundRect; }
   // Converts this region from aFromAPP, an appunits per pixel ratio, to
   // aToAPP. This applies nsRect::ConvertAppUnitsRoundOut/In to each rect of
   // the region.
   nsRegion ConvertAppUnitsRoundOut (PRInt32 aFromAPP, PRInt32 aToAPP) const;
   nsRegion ConvertAppUnitsRoundIn (PRInt32 aFromAPP, PRInt32 aToAPP) const;
   nsIntRegion ToOutsidePixels (nscoord aAppUnitsPerPixel) const;
+  nsRegion& ExtendForScaling (float aXMult, float aYMult);
+
   /**
    * Gets the largest rectangle contained in the region.
    * @param aContainingRect if non-empty, we choose a rectangle that
    * maximizes the area intersecting with aContainingRect (and break ties by
    * then choosing the largest rectangle overall)
    */
   nsRect GetLargestRectangle (const nsRect& aContainingRect = nsRect()) const;
 
@@ -432,16 +434,22 @@ public:
   PRUint32 GetNumRects () const { return mImpl.GetNumRects (); }
   nsIntRect GetBounds () const { return FromRect (mImpl.GetBounds ()); }
   nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const;
   nsIntRect GetLargestRectangle (const nsIntRect& aContainingRect = nsIntRect()) const
   {
     return FromRect (mImpl.GetLargestRectangle( ToRect(aContainingRect) ));
   }
 
+  nsIntRegion& ExtendForScaling (float aXMult, float aYMult)
+  {
+    mImpl.ExtendForScaling(aXMult, aYMult);
+    return *this;
+  }
+
   /**
    * Make sure the region has at most aMaxRects by adding area to it
    * if necessary. The simplified region will be a superset of the
    * original region. The simplified region's bounding box will be
    * the same as for the current region.
    */
   void SimplifyOutward (PRUint32 aMaxRects)
   {
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -462,16 +462,45 @@ gfxUtils::ClipToRegion(gfxContext* aCont
 }
 
 /*static*/ void
 gfxUtils::ClipToRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion)
 {
   ClipToRegionInternal(aContext, aRegion, PR_TRUE);
 }
 
+/*static*/ gfxFloat
+gfxUtils::ClampToScaleFactor(gfxFloat aVal)
+{
+  // Arbitary scale factor limitation. We can increase this
+  // for better scaling performance at the cost of worse
+  // quality.
+  static const gfxFloat kScaleResolution = 2;
+
+  // Negative scaling is just a flip and irrelevant to
+  // our resolution calculation.
+  if (aVal < 0.0) {
+    aVal = -aVal;
+  }
+
+  gfxFloat power = log(aVal)/log(kScaleResolution);
+
+  // If power is within 1e-6 of an integer, round to nearest to
+  // prevent floating point errors, otherwise round up to the
+  // next integer value.
+  if (fabs(power - NS_round(power)) < 1e-6) {
+    power = NS_round(power);
+  } else {
+    power = NS_ceil(power);
+  }
+
+  return pow(kScaleResolution, power);
+}
+
+
 /*static*/ void
 gfxUtils::PathFromRegion(gfxContext* aContext, const nsIntRegion& aRegion)
 {
   PathFromRegionInternal(aContext, aRegion, PR_FALSE);
 }
 
 /*static*/ void
 gfxUtils::PathFromRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion)
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -112,11 +112,16 @@ public:
     static int ImageFormatToDepth(gfxASurface::gfxImageFormat aFormat);
 
     /**
      * If aIn can be represented exactly using an nsIntRect (i.e.
      * integer-aligned edges and coordinates in the PRInt32 range) then we
      * set aOut to that rectangle, otherwise return failure.
     */
     static PRBool GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut);
+
+    /**
+     * Clamp aVal to a power of kScaleResolution.
+     */
+    static gfxFloat ClampToScaleFactor(gfxFloat aVal);
 };
 
 #endif