Bug 1029705 - allow clipping to a list of device-space rectangles (a region) in DrawTarget via PushDeviceSpaceClipRects. r=bas
authorLee Salzman <lsalzman@mozilla.com>
Wed, 21 Sep 2016 17:03:20 -0400
changeset 357719 3bdcf5e55af2239ec71e86709686b207c4a6d674
parent 357718 db14395dc391abf60fcb652b35d3f6029cb773b4
child 357720 16d9055f9eab4893a73f623ad9a811e0b5ef120f
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas
bugs1029705
milestone52.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 1029705 - allow clipping to a list of device-space rectangles (a region) in DrawTarget via PushDeviceSpaceClipRects. r=bas MozReview-Commit-ID: 8zM116zB0NB
gfx/2d/2D.h
gfx/2d/DrawTarget.cpp
gfx/2d/DrawTargetD2D1.cpp
gfx/2d/DrawTargetD2D1.h
gfx/2d/DrawTargetSkia.cpp
gfx/2d/DrawTargetSkia.h
gfx/thebes/gfxUtils.cpp
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1014,16 +1014,26 @@ public:
   /**
    * Push an axis-aligned rectangular clip to the DrawTarget. This rectangle
    * is specified in user space.
    *
    * @param aRect The rect to clip to
    */
   virtual void PushClipRect(const Rect &aRect) = 0;
 
+  /**
+   * Push a clip region specifed by the union of axis-aligned rectangular
+   * clips to the DrawTarget. These rectangles are specified in device space.
+   * This must be balanced by a corresponding call to PopClip within a layer.
+   *
+   * @param aRects The rects to clip to
+   * @param aCount The number of rectangles
+   */
+  virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount);
+
   /** Pop a clip from the DrawTarget. A pop without a corresponding push will
    * be ignored.
    */
   virtual void PopClip() = 0;
 
   /**
    * Push a 'layer' to the DrawTarget, a layer is a temporary surface that all
    * drawing will be redirected to, this is used for example to support group
--- a/gfx/2d/DrawTarget.cpp
+++ b/gfx/2d/DrawTarget.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * 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 "2D.h"
 #include "Logging.h"
+#include "PathHelpers.h"
 
 #include "DrawTargetCapture.h"
 
 namespace mozilla {
 namespace gfx {
 
 already_AddRefed<DrawTargetCapture>
 DrawTarget::CreateCaptureDT(const IntSize& aSize)
@@ -30,10 +31,26 @@ DrawTarget::DrawCapturedDT(DrawTargetCap
 {
   if (aTransform.HasNonIntegerTranslation()) {
     gfxWarning() << "Non integer translations are not supported for DrawCaptureDT at this time!";
     return;
   }
   static_cast<DrawTargetCaptureImpl*>(aCaptureDT)->ReplayToDrawTarget(this, aTransform);
 }
 
+void
+DrawTarget::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
+{
+  Matrix oldTransform = GetTransform();
+  SetTransform(Matrix());
+
+  RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
+  for (uint32_t i = 0; i < aCount; i++) {
+    AppendRectToPath(pathBuilder, Rect(aRects[i]));
+  }
+  RefPtr<Path> path = pathBuilder->Finish();
+  PushClip(path);
+
+  SetTransform(oldTransform);
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -674,61 +674,63 @@ DrawTargetD2D1::Mask(const Pattern &aSou
   mDC->FillRectangle(D2DRect(mat.TransformBounds(rect)), source);
 
   mDC->PopLayer();
 
   FinalizeDrawing(aOptions.mCompositionOp, aSource);
 }
 
 void
-DrawTargetD2D1::PushClip(const Path *aPath)
+DrawTargetD2D1::PushClipGeometry(ID2D1Geometry* aGeometry,
+                                 const D2D1_MATRIX_3X2_F& aTransform,
+                                 bool aPixelAligned)
 {
-  if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
-    gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
-    return;
-  }
-
   mCurrentClippedGeometry = nullptr;
 
-  RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
+  PushedClip clip;
+  clip.mGeometry = aGeometry;
+  clip.mTransform = aTransform;
+  clip.mIsPixelAligned = aPixelAligned;
 
-  PushedClip clip;
-  clip.mTransform = D2DMatrix(mTransform);
-  clip.mPath = pathD2D;
-  
-  pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds);
+  aGeometry->GetBounds(aTransform, &clip.mBounds);
 
   CurrentLayer().mPushedClips.push_back(clip);
 
   // The transform of clips is relative to the world matrix, since we use the total
   // transform for the clips, make the world matrix identity.
   mDC->SetTransform(D2D1::IdentityMatrix());
   mTransformDirty = true;
 
   if (CurrentLayer().mClipsArePushed) {
-    PushD2DLayer(mDC, pathD2D->mGeometry, clip.mTransform);
+    PushD2DLayer(mDC, clip.mGeometry, clip.mTransform, clip.mIsPixelAligned);
   }
 }
 
 void
+DrawTargetD2D1::PushClip(const Path *aPath)
+{
+  if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
+    gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
+    return;
+  }
+
+  RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
+
+  PushClipGeometry(pathD2D->GetGeometry(), D2DMatrix(mTransform));
+}
+
+void
 DrawTargetD2D1::PushClipRect(const Rect &aRect)
 {
   if (!mTransform.IsRectilinear()) {
     // Whoops, this isn't a rectangle in device space, Direct2D will not deal
     // with this transform the way we want it to.
     // See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx
-
-    RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
-    pathBuilder->MoveTo(aRect.TopLeft());
-    pathBuilder->LineTo(aRect.TopRight());
-    pathBuilder->LineTo(aRect.BottomRight());
-    pathBuilder->LineTo(aRect.BottomLeft());
-    pathBuilder->Close();
-    RefPtr<Path> path = pathBuilder->Finish();
-    return PushClip(path);
+    RefPtr<ID2D1Geometry> geom = ConvertRectToGeometry(D2DRect(aRect));
+    return PushClipGeometry(geom, D2DMatrix(mTransform));
   }
 
   mCurrentClippedGeometry = nullptr;
 
   PushedClip clip;
   Rect rect = mTransform.TransformBounds(aRect);
   IntRect intRect;
   clip.mIsPixelAligned = rect.ToIntRect(&intRect);
@@ -742,26 +744,49 @@ DrawTargetD2D1::PushClipRect(const Rect 
   mTransformDirty = true;
 
   if (CurrentLayer().mClipsArePushed) {
     mDC->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
   }
 }
 
 void
+DrawTargetD2D1::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
+{
+  // Build a path for the union of the rects.
+  RefPtr<ID2D1PathGeometry> path;
+  factory()->CreatePathGeometry(getter_AddRefs(path));
+  RefPtr<ID2D1GeometrySink> sink;
+  path->Open(getter_AddRefs(sink));
+  sink->SetFillMode(D2D1_FILL_MODE_WINDING);
+  for (uint32_t i = 0; i < aCount; i++) {
+    const IntRect& rect = aRects[i];
+    sink->BeginFigure(D2DPoint(rect.TopLeft()), D2D1_FIGURE_BEGIN_FILLED);
+    D2D1_POINT_2F lines[3] = { D2DPoint(rect.TopRight()), D2DPoint(rect.BottomRight()), D2DPoint(rect.BottomLeft()) };
+    sink->AddLines(lines, 3);
+    sink->EndFigure(D2D1_FIGURE_END_CLOSED);
+  }
+  sink->Close();
+
+  // The path is in device-space, so there is no transform needed,
+  // and all rects are pixel aligned.
+  PushClipGeometry(path, D2D1::IdentityMatrix(), true);
+}
+
+void
 DrawTargetD2D1::PopClip()
 {
   mCurrentClippedGeometry = nullptr;
   if (CurrentLayer().mPushedClips.empty()) {
     gfxDevCrash(LogReason::UnbalancedClipStack) << "DrawTargetD2D1::PopClip: No clip to pop.";
     return;
   }
 
   if (CurrentLayer().mClipsArePushed) {
-    if (CurrentLayer().mPushedClips.back().mPath) {
+    if (CurrentLayer().mPushedClips.back().mGeometry) {
       mDC->PopLayer();
     } else {
       mDC->PopAxisAlignedClip();
     }
   }
   CurrentLayer().mPushedClips.pop_back();
 }
 
@@ -1390,17 +1415,17 @@ bool
 DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned)
 {
   if (!CurrentLayer().mPushedClips.size()) {
     return false;
   }
 
   aClipRect = D2D1::RectF(0, 0, mSize.width, mSize.height);
   for (auto iter = CurrentLayer().mPushedClips.begin();iter != CurrentLayer().mPushedClips.end(); iter++) {
-    if (iter->mPath) {
+    if (iter->mGeometry) {
       return false;
     }
     aClipRect = IntersectRect(aClipRect, iter->mBounds);
     if (!iter->mIsPixelAligned) {
       aIsPixelAligned = false;
     }
   }
   return true;
@@ -1470,63 +1495,63 @@ DrawTargetD2D1::GetClippedGeometry(IntRe
   mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize);
 
   // if pathGeom is null then pathRect represents the path.
   RefPtr<ID2D1Geometry> pathGeom;
   D2D1_RECT_F pathRect;
   bool pathRectIsAxisAligned = false;
   auto iter = CurrentLayer().mPushedClips.begin();
 
-  if (iter->mPath) {
-    pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
+  if (iter->mGeometry) {
+    pathGeom = GetTransformedGeometry(iter->mGeometry, iter->mTransform);
   } else {
     pathRect = iter->mBounds;
     pathRectIsAxisAligned = iter->mIsPixelAligned;
   }
 
   iter++;
   for (;iter != CurrentLayer().mPushedClips.end(); iter++) {
     // Do nothing but add it to the current clip bounds.
-    if (!iter->mPath && iter->mIsPixelAligned) {
+    if (!iter->mGeometry && iter->mIsPixelAligned) {
       mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
         IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top),
                 int32_t(iter->mBounds.right - iter->mBounds.left),
                 int32_t(iter->mBounds.bottom - iter->mBounds.top)));
       continue;
     }
 
     if (!pathGeom) {
       if (pathRectIsAxisAligned) {
         mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
           IntRect(int32_t(pathRect.left), int32_t(pathRect.top),
                   int32_t(pathRect.right - pathRect.left),
                   int32_t(pathRect.bottom - pathRect.top)));
       }
-      if (iter->mPath) {
+      if (iter->mGeometry) {
         // See if pathRect needs to go into the path geometry.
         if (!pathRectIsAxisAligned) {
           pathGeom = ConvertRectToGeometry(pathRect);
         } else {
-          pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
+          pathGeom = GetTransformedGeometry(iter->mGeometry, iter->mTransform);
         }
       } else {
         pathRect = IntersectRect(pathRect, iter->mBounds);
         pathRectIsAxisAligned = false;
         continue;
       }
     }
 
     RefPtr<ID2D1PathGeometry> newGeom;
     factory()->CreatePathGeometry(getter_AddRefs(newGeom));
 
     RefPtr<ID2D1GeometrySink> currentSink;
     newGeom->Open(getter_AddRefs(currentSink));
 
-    if (iter->mPath) {
-      pathGeom->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT,
+    if (iter->mGeometry) {
+      pathGeom->CombineWithGeometry(iter->mGeometry, D2D1_COMBINE_MODE_INTERSECT,
                                     iter->mTransform, currentSink);
     } else {
       RefPtr<ID2D1Geometry> rectGeom = ConvertRectToGeometry(iter->mBounds);
       pathGeom->CombineWithGeometry(rectGeom, D2D1_COMBINE_MODE_INTERSECT,
                                     D2D1::IdentityMatrix(), currentSink);
     }
 
     currentSink->Close();
@@ -1587,29 +1612,29 @@ DrawTargetD2D1::PushAllClips()
 
 void
 DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
 {
   mDC->SetTransform(D2D1::IdentityMatrix());
   mTransformDirty = true;
 
   for (auto iter = CurrentLayer().mPushedClips.begin(); iter != CurrentLayer().mPushedClips.end(); iter++) {
-    if (iter->mPath) {
-      PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform, aForceIgnoreAlpha, aMaxRect);
+    if (iter->mGeometry) {
+      PushD2DLayer(aDC, iter->mGeometry, iter->mTransform, iter->mIsPixelAligned, aForceIgnoreAlpha, aMaxRect);
     } else {
       mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
     }
   }
 }
 
 void
 DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC)
 {
   for (int i = CurrentLayer().mPushedClips.size() - 1; i >= 0; i--) {
-    if (CurrentLayer().mPushedClips[i].mPath) {
+    if (CurrentLayer().mPushedClips[i].mGeometry) {
       aDC->PopLayer();
     } else {
       aDC->PopAxisAlignedClip();
     }
   }
 }
 
 already_AddRefed<ID2D1Brush>
@@ -1861,23 +1886,25 @@ DrawTargetD2D1::OptimizeSourceSurface(So
     return data.forget();
   }
 
   return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(), mDC, data->GetFormat(), data->GetSize());
 }
 
 void
 DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
-                             bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
+                             bool aPixelAligned, bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
 {
   D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
 
   if (CurrentLayer().mIsOpaque || aForceIgnoreAlpha) {
     options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
   }
 
-  mDC->PushLayer(D2D1::LayerParameters1(aMaxRect, aGeometry,
-                                        D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform,
+  D2D1_ANTIALIAS_MODE antialias =
+    aPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
+
+  mDC->PushLayer(D2D1::LayerParameters1(aMaxRect, aGeometry, antialias, aTransform,
                                         1.0, nullptr, options), nullptr);
 }
 
 }
 }
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -88,16 +88,18 @@ public:
                           const Pattern &aPattern,
                           const DrawOptions &aOptions = DrawOptions(),
                           const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
   virtual void Mask(const Pattern &aSource,
                     const Pattern &aMask,
                     const DrawOptions &aOptions = DrawOptions()) override;
   virtual void PushClip(const Path *aPath) override;
   virtual void PushClipRect(const Rect &aRect) override;
+  virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override;
+
   virtual void PopClip() override;
   virtual void PushLayer(bool aOpaque, Float aOpacity,
                          SourceSurface* aMask,
                          const Matrix& aMaskTransform,
                          const IntRect& aBounds = IntRect(),
                          bool aCopyBackground = false) override;
   virtual void PopLayer() override;
 
@@ -210,18 +212,21 @@ private:
   void PushAllClips();
   void PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha = false, const D2D1_RECT_F& aMaxRect = D2D1::InfiniteRect());
   void PopClipsFromDC(ID2D1DeviceContext *aDC);
 
   already_AddRefed<ID2D1Brush> CreateTransparentBlackBrush();
   already_AddRefed<ID2D1SolidColorBrush> GetSolidColorBrush(const D2D_COLOR_F& aColor);
   already_AddRefed<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
 
+  void PushClipGeometry(ID2D1Geometry* aGeometry, const D2D1_MATRIX_3X2_F& aTransform, bool aPixelAligned = false);
+
   void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
-                    bool aForceIgnoreAlpha = false, const D2D1_RECT_F& aLayerRect = D2D1::InfiniteRect());
+                    bool aPixelAligned = false, bool aForceIgnoreAlpha = false,
+                    const D2D1_RECT_F& aLayerRect = D2D1::InfiniteRect());
 
   IntSize mSize;
 
   RefPtr<ID3D11Device> mDevice;
   RefPtr<ID3D11Texture2D> mTexture;
   RefPtr<ID2D1Geometry> mCurrentClippedGeometry;
   // This is only valid if mCurrentClippedGeometry is non-null. And will
   // only be the intersection of all pixel-aligned retangular clips. This is in
@@ -235,23 +240,22 @@ private:
 
   // We store this to prevent excessive SetTextRenderingParams calls.
   RefPtr<IDWriteRenderingParams> mTextRenderingParams;
 
   // List of pushed clips.
   struct PushedClip
   {
     D2D1_RECT_F mBounds;
-    union {
-      // If mPath is non-null, the mTransform member will be used, otherwise
-      // the mIsPixelAligned member is valid.
-      D2D1_MATRIX_3X2_F mTransform;
-      bool mIsPixelAligned;
-    };
-    RefPtr<PathD2D> mPath;
+    // If mGeometry is non-null, the mTransform member will be used.
+    D2D1_MATRIX_3X2_F mTransform;
+    RefPtr<ID2D1Geometry> mGeometry;
+    // Indicates if mBounds, and when non-null, mGeometry with mTransform
+    // applied, are pixel-aligned.
+    bool mIsPixelAligned;
   };
 
   // List of pushed layers.
   struct PushedLayer
   {
     PushedLayer() : mClipsArePushed(false), mIsOpaque(false), mOldPermitSubpixelAA(false) {}
 
     std::vector<PushedClip> mPushedClips;
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -1812,16 +1812,32 @@ DrawTargetSkia::PushClip(const Path *aPa
   }
 
   const PathSkia *skiaPath = static_cast<const PathSkia*>(aPath);
   mCanvas->save();
   mCanvas->clipPath(skiaPath->GetPath(), SkRegion::kIntersect_Op, true);
 }
 
 void
+DrawTargetSkia::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
+{
+  // Build a region by unioning all the rects together.
+  SkRegion region;
+  for (uint32_t i = 0; i < aCount; i++) {
+    region.op(IntRectToSkIRect(aRects[i]), SkRegion::kUnion_Op);
+  }
+
+  // Clip with the resulting region. clipRegion does not transform
+  // this region by the current transform, unlike the other SkCanvas
+  // clip methods, so it is just passed through in device-space.
+  mCanvas->save();
+  mCanvas->clipRegion(region, SkRegion::kIntersect_Op);
+}
+
+void
 DrawTargetSkia::PushClipRect(const Rect& aRect)
 {
   SkRect rect = RectToSkRect(aRect);
 
   mCanvas->save();
   mCanvas->clipRect(rect, SkRegion::kIntersect_Op, true);
 }
 
--- a/gfx/2d/DrawTargetSkia.h
+++ b/gfx/2d/DrawTargetSkia.h
@@ -99,16 +99,17 @@ public:
   virtual void MaskSurface(const Pattern &aSource,
                            SourceSurface *aMask,
                            Point aOffset,
                            const DrawOptions &aOptions = DrawOptions()) override;
   virtual bool Draw3DTransformedSurface(SourceSurface* aSurface,
                                         const Matrix4x4& aMatrix) override;
   virtual void PushClip(const Path *aPath) override;
   virtual void PushClipRect(const Rect& aRect) override;
+  virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override;
   virtual void PopClip() override;
   virtual void PushLayer(bool aOpaque, Float aOpacity,
                          SourceSurface* aMask,
                          const Matrix& aMaskTransform,
                          const IntRect& aBounds = IntRect(),
                          bool aCopyBackground = false) override;
   virtual void PopLayer() override;
   virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -15,16 +15,17 @@
 #include "libyuv.h"
 #include "mozilla/Base64.h"
 #include "mozilla/dom/ImageEncoder.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/Vector.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIClipboardHelper.h"
 #include "nsIFile.h"
 #include "nsIGfxInfo.h"
@@ -714,35 +715,51 @@ gfxUtils::ClipToRegion(gfxContext* aCont
     aContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height));
   }
   aContext->Clip();
 }
 
 /*static*/ void
 gfxUtils::ClipToRegion(DrawTarget* aTarget, const nsIntRegion& aRegion)
 {
-  if (!aRegion.IsComplex()) {
-    IntRect rect = aRegion.GetBounds();
-    aTarget->PushClipRect(Rect(rect.x, rect.y, rect.width, rect.height));
+  uint32_t numRects = aRegion.GetNumRects();
+  // If there is only one rect, then the region bounds are equivalent to the
+  // contents. So just use push a single clip rect with the bounds.
+  if (numRects == 1) {
+    aTarget->PushClipRect(Rect(aRegion.GetBounds()));
     return;
   }
 
-  RefPtr<PathBuilder> pb = aTarget->CreatePathBuilder();
-
-  for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
-    const IntRect& r = iter.Get();
-    pb->MoveTo(Point(r.x, r.y));
-    pb->LineTo(Point(r.XMost(), r.y));
-    pb->LineTo(Point(r.XMost(), r.YMost()));
-    pb->LineTo(Point(r.x, r.YMost()));
-    pb->Close();
+  // Check if the target's transform will preserve axis-alignment and
+  // pixel-alignment for each rect. For now, just handle the common case
+  // of integer translations.
+  Matrix transform = aTarget->GetTransform();
+  if (transform.IsIntegerTranslation()) {
+    IntPoint translation = RoundedToInt(transform.GetTranslation());
+    AutoTArray<IntRect, 16> rects;
+    rects.SetLength(numRects);
+    uint32_t i = 0;
+    // Build the list of transformed rects by adding in the translation.
+    for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+      IntRect rect = iter.Get();
+      rect.MoveBy(translation);
+      rects[i++] = rect;
+    }
+    aTarget->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
+  } else {
+    // The transform does not produce axis-aligned rects or a rect was not
+    // pixel-aligned. So just build a path with all the rects and clip to it
+    // instead.
+    RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder();
+    for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+      AppendRectToPath(pathBuilder, Rect(iter.Get()));
+    }
+    RefPtr<Path> path = pathBuilder->Finish();
+    aTarget->PushClip(path);
   }
-  RefPtr<Path> path = pb->Finish();
-
-  aTarget->PushClip(path);
 }
 
 /*static*/ gfxFloat
 gfxUtils::ClampToScaleFactor(gfxFloat aVal)
 {
   // Arbitary scale factor limitation. We can increase this
   // for better scaling performance at the cost of worse
   // quality.