Bug 702878: Extend gfx::2d API and D2D backend with new functionality. r=jrmuizel sr=roc
☠☠ backed out by 0b1a809be3bd ☠ ☠
authorBas Schouten <bschouten@mozilla.com>
Wed, 28 Dec 2011 03:51:38 +0100
changeset 85863 9c78b487233347155c1dd02d8a7f251a24db4aae
parent 85862 35674d1a5efba2e309b58e3302242b8b5b3b6bb3
child 85864 365161a949b534aa978c0e836242d91adcece440
push id674
push userffxbld
push dateTue, 13 Mar 2012 21:17:50 +0000
treeherdermozilla-beta@e3c4c92dec31 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel, roc
bugs702878
milestone12.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 702878: Extend gfx::2d API and D2D backend with new functionality. r=jrmuizel sr=roc
content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
gfx/2d/2D.h
gfx/2d/DrawTargetCairo.h
gfx/2d/DrawTargetD2D.cpp
gfx/2d/DrawTargetD2D.h
gfx/2d/DrawTargetSkia.cpp
gfx/2d/DrawTargetSkia.h
gfx/2d/HelpersD2D.h
gfx/2d/Matrix.h
gfx/2d/ShadersD2D.fx
gfx/2d/SourceSurfaceD2DTarget.cpp
gfx/2d/Types.h
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -804,17 +804,17 @@ protected:
                                                 state.patternStyles[aStyle]->mForceWriteOnly,
                                                 state.patternStyles[aStyle]->mCORSUsed);
         }
 
         ExtendMode mode;
         if (state.patternStyles[aStyle]->mRepeat == nsCanvasPatternAzure::NOREPEAT) {
           mode = EXTEND_CLAMP;
         } else {
-          mode = EXTEND_WRAP;
+          mode = EXTEND_REPEAT;
         }
         mPattern = new (mSurfacePattern.addr())
           SurfacePattern(state.patternStyles[aStyle]->mSurface, mode);
       }
 
       return *mPattern;
     }
 
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -143,23 +143,29 @@ struct StrokeOptions {
   CapStyle mLineCap : 3;
 };
 
 /*
  * This structure supplies additional options for calls to DrawSurface.
  *
  * mFilter - Filter used when resampling source surface region to the
  *           destination region.
+ * aSamplingBounds - This indicates whether the implementation is allowed
+ *                   to sample pixels outside the source rectangle as
+ *                   specified in DrawSurface on the surface.
  */
 struct DrawSurfaceOptions {
-  DrawSurfaceOptions(Filter aFilter = FILTER_LINEAR)
+  DrawSurfaceOptions(Filter aFilter = FILTER_LINEAR,
+                     SamplingBounds aSamplingBounds = SAMPLING_UNBOUNDED)
     : mFilter(aFilter)
+    , mSamplingBounds(aSamplingBounds)
   { }
 
   Filter mFilter : 3;
+  SamplingBounds mSamplingBounds : 1;
 };
 
 /*
  * This class is used to store gradient stops, it can only be used with a
  * matching DrawTarget. Not adhering to this condition will make a draw call
  * fail.
  */
 class GradientStops : public RefCounted<GradientStops>
@@ -207,89 +213,109 @@ public:
  * stored in a separate object and are backend dependent. This class itself
  * may be used on the stack.
  */
 class LinearGradientPattern : public Pattern
 {
 public:
   /*
    * aBegin Start of the linear gradient
-   * aEnd End of the linear gradient
+   * aEnd End of the linear gradient - NOTE: In the case of a zero length
+   *      gradient it will act as the color of the last stop.
    * aStops GradientStops object for this gradient, this should match the
    *        backend type of the draw target this pattern will be used with.
+   * aMatrix A matrix that transforms the pattern into user space
    */
   LinearGradientPattern(const Point &aBegin,
                         const Point &aEnd,
-                        GradientStops *aStops)
+                        GradientStops *aStops,
+                        const Matrix &aMatrix = Matrix())
     : mBegin(aBegin)
     , mEnd(aEnd)
     , mStops(aStops)
+    , mMatrix(aMatrix)
   {
   }
 
   virtual PatternType GetType() const { return PATTERN_LINEAR_GRADIENT; }
 
   Point mBegin;
   Point mEnd;
   RefPtr<GradientStops> mStops;
+  Matrix mMatrix;
 };
 
 /*
  * This class is used for Radial Gradient Patterns, the gradient stops are
  * stored in a separate object and are backend dependent. This class itself
  * may be used on the stack.
  */
 class RadialGradientPattern : public Pattern
 {
 public:
   /*
    * aBegin Start of the linear gradient
    * aEnd End of the linear gradient
    * aStops GradientStops object for this gradient, this should match the
    *        backend type of the draw target this pattern will be used with.
+   * aMatrix A matrix that transforms the pattern into user space
    */
   RadialGradientPattern(const Point &aCenter1,
                         const Point &aCenter2,
                         Float aRadius1,
                         Float aRadius2,
-                        GradientStops *aStops)
+                        GradientStops *aStops,
+                        const Matrix &aMatrix = Matrix())
     : mCenter1(aCenter1)
     , mCenter2(aCenter2)
     , mRadius1(aRadius1)
     , mRadius2(aRadius2)
     , mStops(aStops)
+    , mMatrix(aMatrix)
   {
   }
 
   virtual PatternType GetType() const { return PATTERN_RADIAL_GRADIENT; }
 
   Point mCenter1;
   Point mCenter2;
   Float mRadius1;
   Float mRadius2;
   RefPtr<GradientStops> mStops;
+  Matrix mMatrix;
 };
 
 /*
  * This class is used for Surface Patterns, they wrap a surface and a
  * repetition mode for the surface. This may be used on the stack.
  */
 class SurfacePattern : public Pattern
 {
 public:
-  SurfacePattern(SourceSurface *aSourceSurface, ExtendMode aExtendMode)
+  /*
+   * aSourceSurface Surface to use for drawing
+   * aExtendMode This determines how the image is extended outside the bounds
+   *             of the image.
+   * aMatrix A matrix that transforms the pattern into user space
+   * aFilter Resampling filter used for resampling the image.
+   */
+  SurfacePattern(SourceSurface *aSourceSurface, ExtendMode aExtendMode,
+                 const Matrix &aMatrix = Matrix(), Filter aFilter = FILTER_LINEAR)
     : mSurface(aSourceSurface)
     , mExtendMode(aExtendMode)
+    , mFilter(aFilter)
+    , mMatrix(aMatrix)
   {}
 
   virtual PatternType GetType() const { return PATTERN_SURFACE; }
 
   RefPtr<SourceSurface> mSurface;
   ExtendMode mExtendMode;
   Filter mFilter;
+  Matrix mMatrix;
 };
 
 /*
  * This is the base class for source surfaces. These objects are surfaces
  * which may be used as a source in a SurfacePattern of a DrawSurface call.
  * They cannot be drawn to directly.
  */
 class SourceSurface : public RefCounted<SourceSurface>
@@ -306,24 +332,31 @@ public:
    * DataSourceSurface's data can be accessed directly.
    */
   virtual TemporaryRef<DataSourceSurface> GetDataSurface() = 0;
 };
 
 class DataSourceSurface : public SourceSurface
 {
 public:
+  virtual SurfaceType GetType() const { return SURFACE_DATA; }
   /* Get the raw bitmap data of the surface */
   virtual unsigned char *GetData() = 0;
   /*
    * Stride of the surface, distance in bytes between the start of the image
    * data belonging to row y and row y+1. This may be negative.
    */
   virtual int32_t Stride() = 0;
 
+  /*
+   * This function is called after modifying the data on the source surface
+   * directly through the data pointer.
+   */
+  virtual void MarkDirty() {}
+
   virtual TemporaryRef<DataSourceSurface> GetDataSurface() { RefPtr<DataSourceSurface> temp = this; return temp.forget(); }
 };
 
 /* This is an abstract object that accepts path segments. */
 class PathSink : public RefCounted<PathSink>
 {
 public:
   virtual ~PathSink() {}
@@ -602,37 +635,58 @@ public:
    * Fill a series of clyphs on the draw target with a certain source pattern.
    */
   virtual void FillGlyphs(ScaledFont *aFont,
                           const GlyphBuffer &aBuffer,
                           const Pattern &aPattern,
                           const DrawOptions &aOptions = DrawOptions()) = 0;
 
   /*
+   * This takes a source pattern and a mask, and composites the source pattern
+   * onto the destination surface using the alpha channel of the mask pattern
+   * as a mask for the operation.
+   *
+   * aSource Source pattern
+   * aMask Mask pattern
+   * aOptions Drawing options
+   */
+  virtual void Mask(const Pattern &aSource,
+                    const Pattern &aMask,
+                    const DrawOptions &aOptions = DrawOptions()) = 0;
+
+  /*
    * Push a clip to the DrawTarget.
    *
    * aPath The path to clip to
    */
   virtual void PushClip(const Path *aPath) = 0;
 
+  /*
+   * Push an axis-aligned rectangular clip to the DrawTarget. This rectangle
+   * is specified in user space.
+   *
+   * aRect The rect to clip to
+   */
+  virtual void PushClipRect(const Rect &aRect) = 0;
+
   /* Pop a clip from the DrawTarget. A pop without a corresponding push will
    * be ignored.
    */
   virtual void PopClip() = 0;
 
   /*
    * Create a SourceSurface optimized for use with this DrawTarget from
    * existing bitmap data in memory.
    *
    * The SourceSurface does not take ownership of aData, and may be freed at any time.
    */
   virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
-                                                            const IntSize &aSize,
-                                                            int32_t aStride,
-                                                            SurfaceFormat aFormat) const = 0;
+                                                                  const IntSize &aSize,
+                                                                  int32_t aStride,
+                                                                  SurfaceFormat aFormat) const = 0;
 
   /*
    * Create a SourceSurface optimized for use with this DrawTarget from
    * an arbitrary other SourceSurface. This may return aSourceSurface or some
    * other existing surface.
    */
   virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const = 0;
 
@@ -661,18 +715,23 @@ public:
 
   /*
    * Create a GradientStops object that holds information about a set of
    * gradient stops, this object is required for linear or radial gradient
    * patterns to represent the color stops in the gradient.
    *
    * aStops An array of gradient stops
    * aNumStops Number of stops in the array aStops
+   * aExtendNone This describes how to extend the stop color outside of the
+   *             gradient area.
    */
-  virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops) const = 0;
+  virtual TemporaryRef<GradientStops>
+    CreateGradientStops(GradientStop *aStops,
+                        uint32_t aNumStops,
+                        ExtendMode aExtendMode = EXTEND_CLAMP) const = 0;
 
   const Matrix &GetTransform() const { return mTransform; }
 
   /*
    * Set a transform on the surface, this transform is applied at drawing time
    * to both the mask and source of the operation.
    */
   virtual void SetTransform(const Matrix &aTransform)
@@ -690,22 +749,44 @@ protected:
   bool mTransformDirty : 1;
 
   SurfaceFormat mFormat;
 };
 
 class Factory
 {
 public:
-#ifdef USE_CAIRO
   static TemporaryRef<DrawTarget> CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface);
-#endif
+
+  static TemporaryRef<DrawTarget>
+    CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat);
+  
+  static TemporaryRef<DrawTarget>
+    CreateDrawTargetForData(BackendType aBackend, unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat);
+
+  static TemporaryRef<ScaledFont>
+    CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize);
 
-  static TemporaryRef<DrawTarget> CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat);
-  static TemporaryRef<ScaledFont> CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize);
+  /*
+   * This creates a simple data source surface for a certain size. It allocates
+   * new memory for the surface. This memory is freed when the surface is
+   * destroyed.
+   */
+  static TemporaryRef<DataSourceSurface>
+    CreateDataSourceSurface(const IntSize &aSize, SurfaceFormat aFormat);
+  
+  /*
+   * This creates a simple data source surface for some existing data. It will
+   * wrap this data and the data for this source surface. The caller is
+   * responsible for deallocating the memory only after destruction of the
+   * surface.
+   */
+  static TemporaryRef<DataSourceSurface>
+    CreateDataSourceSurfaceFromData(unsigned char *aData, int32_t aStride,
+                                    const IntSize &aSize, SurfaceFormat aFormat);
 
 #ifdef WIN32
   static TemporaryRef<DrawTarget> CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat);
   static void SetDirect3D10Device(ID3D10Device1 *aDevice);
   static ID3D10Device1 *GetDirect3D10Device();
 
 private:
   static ID3D10Device1 *mD3D10Device;
--- a/gfx/2d/DrawTargetCairo.h
+++ b/gfx/2d/DrawTargetCairo.h
@@ -101,34 +101,39 @@ public:
                     const DrawOptions &aOptions = DrawOptions())
   { return; }
 
   virtual void FillGlyphs(ScaledFont *aFont,
                           const GlyphBuffer &aBuffer,
                           const Pattern &aPattern,
                           const DrawOptions &aOptions)
   { return; }
+  virtual void Mask(const Pattern &aSource,
+                    const Pattern &aMask,
+                    const DrawOptions &aOptions = DrawOptions())
+  { return; }
 
   virtual void PushClip(const Path *aPath) { }
+  virtual void PushClipRect(const Rect &aRect) { }
   virtual void PopClip() { }
 
   virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const { return NULL; }
 
   virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
                                                             const IntSize &aSize,
                                                             int32_t aStride,
                                                             SurfaceFormat aFormat) const;
   virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const;
   virtual TemporaryRef<SourceSurface>
     CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const;
   virtual TemporaryRef<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
   { return NULL; }
 
-  virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops) const
+  virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = EXTEND_CLAMP) const
   { return NULL; }
 
   virtual void *GetNativeSurface(NativeSurfaceType aType)
   { return NULL; }
 
   virtual void SetTransform(const Matrix& aTransform);
 
   bool Init(cairo_surface_t* aSurface);
--- a/gfx/2d/DrawTargetD2D.cpp
+++ b/gfx/2d/DrawTargetD2D.cpp
@@ -263,16 +263,18 @@ DrawTargetD2D::DrawSurface(SourceSurface
                            const DrawOptions &aOptions)
 {
   RefPtr<ID2D1Bitmap> bitmap;
 
   ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()));
   
   PrepareForDrawing(rt);
 
+  rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
   Rect srcRect = aSource;
 
   switch (aSurface->GetType()) {
 
   case SURFACE_D2D1_BITMAP:
     {
       SourceSurfaceD2D *srcSurf = static_cast<SourceSurfaceD2D*>(aSurface);
       bitmap = srcSurf->GetBitmap();
@@ -748,16 +750,18 @@ void
 DrawTargetD2D::FillRect(const Rect &aRect,
                         const Pattern &aPattern,
                         const DrawOptions &aOptions)
 {
   ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
 
   PrepareForDrawing(rt);
 
+  rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
 
   if (brush) {
     rt->FillRectangle(D2DRect(aRect), brush);
   }
 
   FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, aRect);
 }
@@ -767,16 +771,18 @@ DrawTargetD2D::StrokeRect(const Rect &aR
                           const Pattern &aPattern,
                           const StrokeOptions &aStrokeOptions,
                           const DrawOptions &aOptions)
 {
   ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
 
   PrepareForDrawing(rt);
 
+  rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
 
   RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
 
   if (brush && strokeStyle) {
     rt->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle);
   }
 
@@ -789,16 +795,18 @@ DrawTargetD2D::StrokeLine(const Point &a
                           const Pattern &aPattern,
                           const StrokeOptions &aStrokeOptions,
                           const DrawOptions &aOptions)
 {
   ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
 
   PrepareForDrawing(rt);
 
+  rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
 
   RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
 
   if (brush && strokeStyle) {
     rt->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle);
   }
 
@@ -817,16 +825,18 @@ DrawTargetD2D::Stroke(const Path *aPath,
   }
 
   const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
 
   ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
 
   PrepareForDrawing(rt);
 
+  rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
 
   RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
 
   if (brush && strokeStyle) {
     rt->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle);
   }
 
@@ -844,16 +854,18 @@ DrawTargetD2D::Fill(const Path *aPath,
   }
 
   const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
 
   ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
 
   PrepareForDrawing(rt);
 
+  rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
+
   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
 
   if (brush) {
     rt->FillGeometry(d2dPath->mGeometry, brush);
   }
 
   Rect bounds;
   if (aOptions.mCompositionOp != OP_OVER) {
@@ -912,16 +924,46 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aF
   if (brush) {
     rt->DrawGlyphRun(D2D1::Point2F(), &glyphRun, brush);
   }
 
   FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, (Float)mSize.width, (Float)mSize.height));
 }
 
 void
+DrawTargetD2D::Mask(const Pattern &aSource,
+                    const Pattern &aMask,
+                    const DrawOptions &aOptions)
+{
+  ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aSource);
+  
+  PrepareForDrawing(rt);
+
+  RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha);
+  RefPtr<ID2D1Brush> maskBrush = CreateBrushForPattern(aMask, 1.0f);
+
+  RefPtr<ID2D1Layer> layer;
+  rt->CreateLayer(byRef(layer));
+  rt->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), NULL,
+                                      D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+                                      D2D1::IdentityMatrix(),
+                                      1.0f, maskBrush),
+                layer);
+
+  Rect rect(0, 0, mSize.width, mSize.height);
+  Matrix mat = mTransform;
+  mat.Invert();
+  
+  rt->FillRectangle(D2DRect(mat.TransformBounds(rect)), brush);
+  rt->PopLayer();
+
+  FinalizeRTForOperation(aOptions.mCompositionOp, aSource, Rect(0, 0, (Float)mSize.width, (Float)mSize.height));
+}
+
+void
 DrawTargetD2D::PushClip(const Path *aPath)
 {
   if (aPath->GetBackendType() != BACKEND_DIRECT2D) {
     gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
     return;
   }
 
   RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
@@ -953,34 +995,67 @@ DrawTargetD2D::PushClip(const Path *aPat
     mRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), pathD2D->mGeometry,
                                          D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
                                          clip.mTransform, 1.0f, NULL,
                                          options), layer);
   }
 }
 
 void
+DrawTargetD2D::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);
+  }
+
+  PushedClip clip;
+  // Do not store the transform, just store the device space rectangle directly.
+  clip.mBounds = D2DRect(mTransform.TransformBounds(aRect));
+
+  mPushedClips.push_back(clip);
+
+  mRT->SetTransform(D2D1::IdentityMatrix());
+  if (mClipsArePushed) {
+    mRT->PushAxisAlignedClip(clip.mBounds, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+  }
+}
+
+void
 DrawTargetD2D::PopClip()
 {
   if (mClipsArePushed) {
-    mRT->PopLayer();
+    if (mPushedClips.back().mLayer) {
+      mRT->PopLayer();
+    } else {
+      mRT->PopAxisAlignedClip();
+    }
   }
   mPushedClips.pop_back();
 }
 
 TemporaryRef<SourceSurface> 
 DrawTargetD2D::CreateSourceSurfaceFromData(unsigned char *aData,
                                            const IntSize &aSize,
                                            int32_t aStride,
                                            SurfaceFormat aFormat) const
 {
   RefPtr<SourceSurfaceD2D> newSurf = new SourceSurfaceD2D();
 
   if (!newSurf->InitFromData(aData, aSize, aStride, aFormat, mRT)) {
-    gfxDebug() << *this << ": Failure to create source surface from data. Size: " << aSize;
     return NULL;
   }
 
   return newSurf;
 }
 
 TemporaryRef<SourceSurface> 
 DrawTargetD2D::OptimizeSourceSurface(SourceSurface *aSurface) const
@@ -1044,28 +1119,31 @@ DrawTargetD2D::CreatePathBuilder(FillRul
   if (aFillRule == FILL_WINDING) {
     sink->SetFillMode(D2D1_FILL_MODE_WINDING);
   }
 
   return new PathBuilderD2D(sink, path, aFillRule);
 }
 
 TemporaryRef<GradientStops>
-DrawTargetD2D::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops) const
+DrawTargetD2D::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const
 {
-  D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops];
+  vector<D2D1_GRADIENT_STOP> stops(aNumStops);
 
   for (uint32_t i = 0; i < aNumStops; i++) {
     stops[i].position = rawStops[i].offset;
     stops[i].color = D2DColor(rawStops[i].color);
   }
 
   RefPtr<ID2D1GradientStopCollection> stopCollection;
 
-  HRESULT hr = mRT->CreateGradientStopCollection(stops, aNumStops, byRef(stopCollection));
+  HRESULT hr =
+    mRT->CreateGradientStopCollection(&stops.front(), aNumStops,
+                                      D2D1_GAMMA_2_2, D2DExtend(aExtendMode),
+                                      byRef(stopCollection));
 
   if (FAILED(hr)) {
     gfxWarning() << "Failed to create GradientStopCollection. Code: " << hr;
     return NULL;
   }
 
   return new GradientStopsD2D(stopCollection);
 }
@@ -1092,30 +1170,35 @@ DrawTargetD2D::Init(const IntSize &aSize
   mFormat = aFormat;
 
   if (!Factory::GetDirect3D10Device()) {
     gfxDebug() << "Failed to Init Direct2D DrawTarget (No D3D10 Device set.)";
     return false;
   }
   mDevice = Factory::GetDirect3D10Device();
 
-  CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
+  CD3D10_TEXTURE2D_DESC desc(DXGIFormat(aFormat),
                              mSize.width,
                              mSize.height,
                              1, 1);
   desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
 
   hr = mDevice->CreateTexture2D(&desc, NULL, byRef(mTexture));
 
   if (FAILED(hr)) {
     gfxDebug() << "Failed to init Direct2D DrawTarget. Size: " << mSize << " Code: " << hr;
     return false;
   }
 
-  return InitD2DRenderTarget();
+  if (!InitD2DRenderTarget()) {
+    return false;
+  }
+
+  mRT->Clear(D2D1::ColorF(0, 0));
+  return true;
 }
 
 bool
 DrawTargetD2D::Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat)
 {
   HRESULT hr;
 
   mTexture = aTexture;
@@ -1225,49 +1308,54 @@ DrawTargetD2D::InitD2DRenderTarget()
   mRT = CreateRTForTexture(mTexture);
 
   if (!mRT) {
     return false;
   }
 
   mRT->BeginDraw();
 
-  mRT->Clear(D2D1::ColorF(0, 0));
-
   if (mFormat == FORMAT_B8G8R8X8) {
     mRT->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
   }
 
   return InitD3D10Data();
 }
 
 void
 DrawTargetD2D::PrepareForDrawing(ID2D1RenderTarget *aRT)
 {
   if (!mClipsArePushed || aRT == mTempRT) {
     if (mPushedClips.size()) {
       // The transform of clips is relative to the world matrix, since we use the total
       // transform for the clips, make the world matrix identity.
-      mRT->SetTransform(D2D1::IdentityMatrix());
-      mTransformDirty = true;
+      aRT->SetTransform(D2D1::IdentityMatrix());
+      if (aRT == mRT) {
+        mTransformDirty = true;
+      }
       for (std::vector<PushedClip>::iterator iter = mPushedClips.begin();
-           iter != mPushedClips.end(); iter++) {
+            iter != mPushedClips.end(); iter++) {
         D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE;
+        if (iter->mLayer) {
+          D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE;
 
-        if (mFormat == FORMAT_B8G8R8X8) {
-          options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE;
+          if (mFormat == FORMAT_B8G8R8X8) {
+            options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE;
+            if (mFormat == FORMAT_B8G8R8X8) {
+              options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE;
+            }
+
+            aRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), iter->mPath->mGeometry,
+                                                  D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+                                                  iter->mTransform, 1.0f, NULL,
+                                                  options), iter->mLayer);
+          } else {
+            aRT->PushAxisAlignedClip(iter->mBounds, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+          }
         }
-
-        aRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), iter->mPath->mGeometry,
-                                             D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
-                                             iter->mTransform, 1.0f, NULL,
-                                             options), iter->mLayer);
-      }
-      if (aRT == mRT) {
-        mClipsArePushed = true;
       }
     }
   }
   FlushTransformToRT();
   MarkChanged();
 
   if (aRT == mTempRT) {
     mTempRT->SetTransform(D2DMatrix(mTransform));
@@ -1416,18 +1504,22 @@ DrawTargetD2D::FinalizeRTForOperation(Co
   if (aOperator == OP_OVER && !IsPatternSupportedByD2D(aPattern)) {
     return;
   }
 
   if (!mTempRT) {
     return;
   }
 
-  for (unsigned int i = 0; i < mPushedClips.size(); i++) {
-    mTempRT->PopLayer();
+  for (int i = mPushedClips.size() - 1; i >= 0; i--) {
+    if (mPushedClips[i].mLayer) {
+      mTempRT->PopLayer();
+    } else {
+      mTempRT->PopAxisAlignedClip();
+    }
   }
 
   mRT->Flush();
   mTempRT->Flush();
 
   AutoSaveRestoreClippedOut restoreClippedOut(this);
 
   bool needsWriteBack =
@@ -1530,17 +1622,17 @@ DrawTargetD2D::CreateRTForTexture(ID3D10
     return NULL;
   }
 
   D3D10_TEXTURE2D_DESC desc;
   aTexture->GetDesc(&desc);
 
   D2D1_ALPHA_MODE alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
 
-  if (mFormat == FORMAT_B8G8R8X8) {
+  if (mFormat == FORMAT_B8G8R8X8 && aTexture == mTexture) {
     alphaMode = D2D1_ALPHA_MODE_IGNORE;
   }
 
   D2D1_RENDER_TARGET_PROPERTIES props =
     D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(desc.Format, alphaMode));
   hr = factory()->CreateDxgiSurfaceRenderTarget(surface, props, byRef(rt));
 
   if (FAILED(hr)) {
@@ -1587,18 +1679,22 @@ DrawTargetD2D::EnsureViews()
     gfxWarning() << *this << "Failed to create rendertarget view for temp texture. Code: " << hr;
   }
 }
 
 void
 DrawTargetD2D::PopAllClips()
 {
   if (mClipsArePushed) {
-    for (unsigned int i = 0; i < mPushedClips.size(); i++) {
-      mRT->PopLayer();
+    for (int i = mPushedClips.size() - 1; i >= 0; i--) {
+      if (mPushedClips[i].mLayer) {
+        mRT->PopLayer();
+      } else {
+        mRT->PopAxisAlignedClip();
+      }
     }
   
     mClipsArePushed = false;
   }
 }
 
 TemporaryRef<ID2D1Brush>
 DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
@@ -1624,19 +1720,30 @@ DrawTargetD2D::CreateBrushForPattern(con
 
     GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get());
 
     if (!stops) {
       gfxDebug() << "No stops specified for gradient pattern.";
       return NULL;
     }
 
+    if (pat->mBegin == pat->mEnd) {
+      RefPtr<ID2D1SolidColorBrush> colBrush;
+      uint32_t stopCount = stops->mStopCollection->GetGradientStopCount();
+      vector<D2D1_GRADIENT_STOP> d2dStops(stopCount);
+      stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount);
+      mRT->CreateSolidColorBrush(d2dStops.back().color,
+                                 D2D1::BrushProperties(aAlpha),
+                                 byRef(colBrush));
+      return colBrush;
+    }
+
     mRT->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin),
                                                                        D2DPoint(pat->mEnd)),
-                                   D2D1::BrushProperties(aAlpha),
+                                   D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
                                    stops->mStopCollection,
                                    byRef(gradBrush));
     return gradBrush;
   } else if (aPattern.GetType() == PATTERN_RADIAL_GRADIENT) {
     RefPtr<ID2D1RadialGradientBrush> gradBrush;
     const RadialGradientPattern *pat =
       static_cast<const RadialGradientPattern*>(&aPattern);
 
@@ -1647,71 +1754,66 @@ DrawTargetD2D::CreateBrushForPattern(con
       return NULL;
     }
 
     // This will not be a complex radial gradient brush.
     mRT->CreateRadialGradientBrush(
       D2D1::RadialGradientBrushProperties(D2DPoint(pat->mCenter1),
                                           D2D1::Point2F(),
                                           pat->mRadius2, pat->mRadius2),
-      D2D1::BrushProperties(aAlpha),
+      D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
       stops->mStopCollection,
       byRef(gradBrush));
 
     return gradBrush;
   } else if (aPattern.GetType() == PATTERN_SURFACE) {
     RefPtr<ID2D1BitmapBrush> bmBrush;
     const SurfacePattern *pat =
       static_cast<const SurfacePattern*>(&aPattern);
 
     if (!pat->mSurface) {
       gfxDebug() << "No source surface specified for surface pattern";
       return NULL;
     }
 
     RefPtr<ID2D1Bitmap> bitmap;
+
+    Matrix mat = pat->mMatrix;
     
     switch (pat->mSurface->GetType()) {
     case SURFACE_D2D1_BITMAP:
       {
         SourceSurfaceD2D *surf = static_cast<SourceSurfaceD2D*>(pat->mSurface.get());
 
         bitmap = surf->mBitmap;
 
         if (!bitmap) {
-          gfxDebug() << "Source surface used for pattern too large!";
-          return NULL;
+          bitmap = CreatePartialBitmapForSurface(surf, mat);
+
+          if (!bitmap) {
+            return NULL;
+          }
         }
       }
       break;
     case SURFACE_D2D1_DRAWTARGET:
       {
         SourceSurfaceD2DTarget *surf =
           static_cast<SourceSurfaceD2DTarget*>(pat->mSurface.get());
         bitmap = surf->GetBitmap(mRT);
         AddDependencyOnSource(surf);
       }
       break;
     }
-
-    D2D1_EXTEND_MODE extend = D2D1_EXTEND_MODE_CLAMP;
-    switch (pat->mExtendMode) {
-    case EXTEND_WRAP:
-      extend = D2D1_EXTEND_MODE_WRAP;
-      break;
-    case EXTEND_MIRROR:
-      extend = D2D1_EXTEND_MODE_MIRROR;
-      break;
-    }
-
+    
     mRT->CreateBitmapBrush(bitmap,
-                           D2D1::BitmapBrushProperties(extend,
-                                                       extend,
+                           D2D1::BitmapBrushProperties(D2DExtend(pat->mExtendMode),
+                                                       D2DExtend(pat->mExtendMode),
                                                        D2DFilter(pat->mFilter)),
-                           D2D1::BrushProperties(aAlpha),
+                           D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
                            byRef(bmBrush));
 
     return bmBrush;
   }
 
   gfxWarning() << "Invalid pattern type detected.";
   return NULL;
 }
@@ -1850,16 +1952,69 @@ DrawTargetD2D::CreateGradientTexture(con
   data.pSysMem = &textureData.front();
 
   RefPtr<ID3D10Texture1D> tex;
   mDevice->CreateTexture1D(&desc, &data, byRef(tex));
 
   return tex;
 }
 
+TemporaryRef<ID2D1Bitmap>
+DrawTargetD2D::CreatePartialBitmapForSurface(SourceSurfaceD2D *aSurface, Matrix &aMatrix)
+{
+  RefPtr<ID2D1Bitmap> bitmap;
+
+  // This is where things get complicated. The source surface was
+  // created for a surface that was too large to fit in a texture.
+  // We'll need to figure out if we can work with a partial upload
+  // or downsample in software.
+
+  Matrix transform = mTransform;
+  transform = aMatrix * transform;
+  if (!transform.Invert()) {
+    // Singular transform, nothing to be drawn.
+    return NULL;
+  }
+
+  Rect rect(0, 0, mSize.width, mSize.height);
+
+  // Calculate the rectangle of the source mapped to our surface.
+  rect = transform.TransformBounds(rect);
+  rect.RoundOut();
+
+  Rect uploadRect(0, 0, aSurface->mSize.width, aSurface->mSize.height);
+
+  // Calculate the rectangle on the source bitmap that touches our
+  // surface.
+  uploadRect = uploadRect.Intersect(rect);
+
+  if (uploadRect.width <= mRT->GetMaximumBitmapSize() &&
+      uploadRect.height <= mRT->GetMaximumBitmapSize()) {
+            
+    int Bpp = BytesPerPixel(aSurface->mFormat);
+    int stride = Bpp * aSurface->mSize.width;
+
+    // A partial upload will suffice.
+    mRT->CreateBitmap(D2D1::SizeU(uint32_t(uploadRect.width), uint32_t(uploadRect.height)),
+                      &aSurface->mRawData.front() + int(uploadRect.x) + int(uploadRect.y) * stride,
+                      stride,
+                      D2D1::BitmapProperties(D2DPixelFormat(aSurface->mFormat)),
+                      byRef(bitmap));
+
+    aMatrix.Translate(-uploadRect.x, -uploadRect.y);
+
+    return bitmap;
+  } else {
+    // XXX - FIX ME!!
+    MOZ_ASSERT(false);
+    gfxDebug() << "Source surface used for pattern too large!";
+    return NULL;
+  }
+}
+
 void
 DrawTargetD2D::SetupEffectForRadialGradient(const RadialGradientPattern *aPattern)
 {
   mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")->GetPassByIndex(0)->Apply(0);
   mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()->
     SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
 
   float dimensions[] = { float(mSize.width), float(mSize.height), 0, 0 };
@@ -1902,23 +2057,36 @@ DrawTargetD2D::SetupEffectForRadialGradi
                       invTransform._21, invTransform._22, 0, 0,
                       invTransform._31, invTransform._32, 1.0f, 0,
                       0, 0, 0, 1.0f };
 
   mPrivateData->mEffect->GetVariableByName("DeviceSpaceToUserSpace")->
     AsMatrix()->SetMatrix(matrix);
 
   float A = dc.x * dc.x + dc.y * dc.y - dr * dr;
+
+  uint32_t offset = 0;
+  switch (stops->mStopCollection->GetExtendMode()) {
+  case D2D1_EXTEND_MODE_WRAP:
+    offset = 1;
+    break;
+  case D2D1_EXTEND_MODE_MIRROR:
+    offset = 2;
+    break;
+  default:
+    gfxWarning() << "This shouldn't happen! Invalid extend mode for gradient stops.";
+  }
+
   if (A == 0) {
     mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")->
-      GetPassByIndex(1)->Apply(0);
+      GetPassByIndex(offset * 2 + 1)->Apply(0);
   } else {
     mPrivateData->mEffect->GetVariableByName("A")->AsScalar()->SetFloat(A);
     mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")->
-      GetPassByIndex(0)->Apply(0);
+      GetPassByIndex(offset * 2)->Apply(0);
   }
 }
 
 ID2D1Factory*
 DrawTargetD2D::factory()
 {
   if (mFactory) {
     return mFactory;
@@ -1931,17 +2099,17 @@ DrawTargetD2D::factory()
 
   if (!createD2DFactory) {
     gfxWarning() << "Failed to locate D2D1CreateFactory function.";
     return NULL;
   }
 
   D2D1_FACTORY_OPTIONS options;
 #ifdef _DEBUG
-  options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
+  options.debugLevel = D2D1_DEBUG_LEVEL_WARNING;
 #else
   options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
 #endif
 
   HRESULT hr = createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
                                 __uuidof(ID2D1Factory),
                                 &options,
                                 (void**)&mFactory);
--- a/gfx/2d/DrawTargetD2D.h
+++ b/gfx/2d/DrawTargetD2D.h
@@ -51,16 +51,17 @@
 #else
 #include <unordered_set>
 #endif
 
 namespace mozilla {
 namespace gfx {
 
 class SourceSurfaceD2DTarget;
+class SourceSurfaceD2D;
 class GradientStopsD2D;
 
 struct PrivateD3D10DataD2D
 {
   RefPtr<ID3D10Effect> mEffect;
   RefPtr<ID3D10InputLayout> mInputLayout;
   RefPtr<ID3D10Buffer> mVB;
   RefPtr<ID3D10BlendState> mBlendStates[OP_COUNT];
@@ -112,34 +113,41 @@ public:
                       const DrawOptions &aOptions = DrawOptions());
   virtual void Fill(const Path *aPath,
                     const Pattern &aPattern,
                     const DrawOptions &aOptions = DrawOptions());
   virtual void FillGlyphs(ScaledFont *aFont,
                           const GlyphBuffer &aBuffer,
                           const Pattern &aPattern,
                           const DrawOptions &aOptions = DrawOptions());
+  virtual void Mask(const Pattern &aSource,
+                    const Pattern &aMask,
+                    const DrawOptions &aOptions = DrawOptions());
   virtual void PushClip(const Path *aPath);
+  virtual void PushClipRect(const Rect &aRect);
   virtual void PopClip();
 
   virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
                                                             const IntSize &aSize,
                                                             int32_t aStride,
                                                             SurfaceFormat aFormat) const;
   virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const;
 
   virtual TemporaryRef<SourceSurface>
     CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const;
   
   virtual TemporaryRef<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
 
   virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const;
 
-  virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops) const;
+  virtual TemporaryRef<GradientStops>
+    CreateGradientStops(GradientStop *aStops,
+                        uint32_t aNumStops,
+                        ExtendMode aExtendMode = EXTEND_CLAMP) const;
 
   virtual void *GetNativeSurface(NativeSurfaceType aType);
 
   bool Init(const IntSize &aSize, SurfaceFormat aFormat);
   bool Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat);
   bool InitD3D10Data();
 
   static ID2D1Factory *factory();
@@ -181,16 +189,21 @@ private:
 
   TemporaryRef<ID2D1RenderTarget> CreateRTForTexture(ID3D10Texture2D *aTexture);
   TemporaryRef<ID2D1Geometry> GetClippedGeometry();
 
   TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
 
   TemporaryRef<ID3D10Texture1D> CreateGradientTexture(const GradientStopsD2D *aStops);
 
+  // This creates a partially uploaded bitmap for a SourceSurfaceD2D that is
+  // too big to fit in a bitmap. It adjusts the passed Matrix to accomodate the
+  // partial upload.
+  TemporaryRef<ID2D1Bitmap> CreatePartialBitmapForSurface(SourceSurfaceD2D *aSurface, Matrix &aMatrix);
+
   void SetupEffectForRadialGradient(const RadialGradientPattern *aPattern);
 
   static const uint32_t test = 4;
 
   IntSize mSize;
 
   RefPtr<ID3D10Device1> mDevice;
   RefPtr<ID3D10Texture2D> mTexture;
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -194,19 +194,19 @@ DrawTargetSkia::Snapshot()
 
 SkShader::TileMode
 ExtendModeToTileMode(ExtendMode aMode)
 {
   switch (aMode)
   {
     case EXTEND_CLAMP:
       return SkShader::kClamp_TileMode;
-    case EXTEND_WRAP:
+    case EXTEND_REPEAT:
       return SkShader::kRepeat_TileMode;
-    case EXTEND_MIRROR:
+    case EXTEND_REFLECT:
       return SkShader::kMirror_TileMode;
   }
   return SkShader::kClamp_TileMode;
 }
 
 struct AutoPaintSetup {
   AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Pattern& aPattern)
     : mNeedsRestore(false), mAlpha(1.0)
@@ -666,17 +666,17 @@ DrawTargetSkia::PushClip(const Path *aPa
 
 void
 DrawTargetSkia::PopClip()
 {
   mCanvas->restore();
 }
 
 TemporaryRef<GradientStops>
-DrawTargetSkia::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops) const
+DrawTargetSkia::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const
 {
   std::vector<GradientStop> stops;
   stops.resize(aNumStops);
   for (uint32_t i = 0; i < aNumStops; i++) {
     stops[i] = aStops[i];
   }
   std::stable_sort(stops.begin(), stops.end());
   
--- a/gfx/2d/DrawTargetSkia.h
+++ b/gfx/2d/DrawTargetSkia.h
@@ -94,29 +94,34 @@ public:
                       const DrawOptions &aOptions = DrawOptions());
   virtual void Fill(const Path *aPath,
                     const Pattern &aPattern,
                     const DrawOptions &aOptions = DrawOptions());
   virtual void FillGlyphs(ScaledFont *aFont,
                           const GlyphBuffer &aBuffer,
                           const Pattern &aPattern,
                           const DrawOptions &aOptions = DrawOptions());
+  virtual void Mask(const Pattern &aSource,
+                    const Pattern &aMask,
+                    const DrawOptions &aOptions = DrawOptions())
+  { return; }
   virtual void PushClip(const Path *aPath);
+  virtual void PushClipRect(const Rect &aRect) { }
   virtual void PopClip();
   virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
                                                             const IntSize &aSize,
                                                             int32_t aStride,
                                                             SurfaceFormat aFormat) const;
   virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const;
   virtual TemporaryRef<SourceSurface>
     CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const;
   virtual TemporaryRef<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
   virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const;
-  virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops) const;
+  virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = EXTEND_CLAMP) const;
   virtual void SetTransform(const Matrix &aTransform);
 
   bool Init(const IntSize &aSize, SurfaceFormat aFormat);
   
   operator std::string() const {
     std::stringstream stream;
     stream << "DrawTargetSkia(" << this << ")";
     return stream.str();
--- a/gfx/2d/HelpersD2D.h
+++ b/gfx/2d/HelpersD2D.h
@@ -54,31 +54,46 @@ static inline D2D1_SIZE_U D2DIntSize(con
   return D2D1::SizeU(aSize.width, aSize.height);
 }
 
 static inline D2D1_RECT_F D2DRect(const Rect &aRect)
 {
   return D2D1::RectF(aRect.x, aRect.y, aRect.XMost(), aRect.YMost());
 }
 
+static inline D2D1_EXTEND_MODE D2DExtend(ExtendMode aExtendMode)
+{
+  D2D1_EXTEND_MODE extend = D2D1_EXTEND_MODE_CLAMP;
+  switch (aExtendMode) {
+  case EXTEND_REPEAT:
+    extend = D2D1_EXTEND_MODE_WRAP;
+    break;
+  case EXTEND_REFLECT:
+    extend = D2D1_EXTEND_MODE_MIRROR;
+    break;
+  }
+
+  return extend;
+}
+
 static inline D2D1_BITMAP_INTERPOLATION_MODE D2DFilter(const Filter &aFilter)
 {
   switch (aFilter) {
   case FILTER_POINT:
     return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
   }
 
   return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
 }
 
-static inline D2D1_ANTIALIAS_MODE D2DAAMode(const AntialiasMode &aMode)
+static inline D2D1_ANTIALIAS_MODE D2DAAMode(AntialiasMode aMode)
 {
   switch (aMode) {
   case AA_NONE:
-    D2D1_ANTIALIAS_MODE_ALIASED;
+    return D2D1_ANTIALIAS_MODE_ALIASED;
   }
 
   return D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
 }
 
 static inline D2D1_MATRIX_3X2_F D2DMatrix(const Matrix &aTransform)
 {
   return D2D1::Matrix3x2F(aTransform._11, aTransform._12,
@@ -136,16 +151,21 @@ static inline D2D1_ALPHA_MODE AlphaMode(
   switch (aFormat) {
   case FORMAT_B8G8R8X8:
     return D2D1_ALPHA_MODE_IGNORE;
   }
 
   return D2D1_ALPHA_MODE_PREMULTIPLIED;
 }
 
+static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat aFormat)
+{
+  return D2D1::PixelFormat(DXGIFormat(aFormat), AlphaMode(aFormat));
+}
+
 static inline int BytesPerPixel(SurfaceFormat aFormat)
 {
   switch (aFormat) {
   case FORMAT_A8:
     return 1;
   default:
     return 4;
   }
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -138,14 +138,32 @@ public:
     resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22;
     resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21;
     resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22;
     resultMatrix._31 = this->_31 * aMatrix._11 + this->_32 * aMatrix._21 + aMatrix._31;
     resultMatrix._32 = this->_31 * aMatrix._12 + this->_32 * aMatrix._22 + aMatrix._32;
 
     return resultMatrix;
   }
+
+  /* Returns true if the matrix is a rectilinear transformation (i.e.
+   * grid-aligned rectangles are transformed to grid-aligned rectangles)
+   */
+  bool IsRectilinear() {
+    if (FuzzyEqual(_12, 0) && FuzzyEqual(_21, 0)) {
+      return true;
+    } else if (FuzzyEqual(_22, 0) && FuzzyEqual(_11, 0)) {
+      return true;
+    }
+
+    return false;
+  }
+private:
+  static bool FuzzyEqual(Float aV1, Float aV2) {
+    // XXX - Check if fabs does the smart thing and just negates the sign bit.
+    return fabs(aV2 - aV1) < 1e-6;
+  }
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_MATRIX_H_ */
--- a/gfx/2d/ShadersD2D.fx
+++ b/gfx/2d/ShadersD2D.fx
@@ -55,16 +55,30 @@ Texture2D mask;
 
 sampler sSampler = sampler_state {
     Filter = MIN_MAG_MIP_LINEAR;
     Texture = tex;
     AddressU = Clamp;
     AddressV = Clamp;
 };
 
+sampler sWrapSampler = sampler_state {
+    Filter = MIN_MAG_MIP_LINEAR;
+    Texture = tex;
+    AddressU = Wrap;
+    AddressV = Wrap;
+};
+
+sampler sMirrorSampler = sampler_state {
+    Filter = MIN_MAG_MIP_LINEAR;
+    Texture = tex;
+    AddressU = Mirror;
+    AddressV = Mirror;
+};
+
 sampler sMaskSampler = sampler_state {
     Filter = MIN_MAG_MIP_LINEAR;
     Texture = mask;
     AddressU = Clamp;
     AddressV = Clamp;
 };
 
 sampler sShadowSampler = sampler_state {
@@ -137,17 +151,17 @@ float4 SampleTexturePS( VS_OUTPUT In) : 
     return tex.Sample(sSampler, In.TexCoord);
 };
 
 float4 SampleMaskTexturePS( VS_OUTPUT In) : SV_Target
 {
     return tex.Sample(sSampler, In.TexCoord) * mask.Sample(sMaskSampler, In.MaskTexCoord).a;
 };
 
-float4 SampleRadialGradientPS( VS_RADIAL_OUTPUT In) : SV_Target
+float4 SampleRadialGradientPS(VS_RADIAL_OUTPUT In, uniform sampler aSampler) : SV_Target
 {
     // Radial gradient painting is defined as the set of circles whose centers
     // are described by C(t) = (C2 - C1) * t + C1; with radii
     // R(t) = (R2 - R1) * t + R1; for R(t) > 0. This shader solves the
     // quadratic equation that arises when calculating t for pixel (x, y).
     //
     // A more extensive derrivation can be found in the pixman radial gradient
     // code.
@@ -173,25 +187,25 @@ float4 SampleRadialGradientPS( VS_RADIAL
     float2 isValid = step(float2(-radius1, -radius1), t * diff.z);
 
     if (max(isValid.x, isValid.y) <= 0) {
       return float4(0, 0, 0, 0);
     }
 
     float upper_t = lerp(t.y, t.x, isValid.x);
 
-    float4 output = tex.Sample(sSampler, float2(upper_t, 0.5));
+    float4 output = tex.Sample(aSampler, float2(upper_t, 0.5));
     // Premultiply
     output.rgb *= output.a;
     // Multiply the output color by the input mask for the operation.
     output *= mask.Sample(sMaskSampler, In.MaskTexCoord).a;
     return output;
 };
 
-float4 SampleRadialGradientA0PS( VS_RADIAL_OUTPUT In) : SV_Target
+float4 SampleRadialGradientA0PS( VS_RADIAL_OUTPUT In, uniform sampler aSampler ) : SV_Target
 {
     // This simpler shader is used for the degenerate case where A is 0,
     // i.e. we're actually solving a linear equation.
 
     float2 p = In.PixelCoord;
     float3 dp = float3(p - center1, radius1);
 
     // dpx * dcx + dpy * dcy + r * dr
@@ -200,17 +214,17 @@ float4 SampleRadialGradientA0PS( VS_RADI
     float C = pow(dp.x, 2) + pow(dp.y, 2) - pow(radius1, 2);
 
     float t = 0.5 * C / B;
 
     if (-radius1 >= t * diff.z) {
       return float4(0, 0, 0, 0);
     }
 
-    float4 output = tex.Sample(sSampler, float2(t, 0.5));
+    float4 output = tex.Sample(aSampler, float2(t, 0.5));
     // Premultiply
     output.rgb *= output.a;
     // Multiply the output color by the input mask for the operation.
     output *= mask.Sample(sMaskSampler, In.MaskTexCoord).a;
     return output;
 };
 
 float4 SampleShadowHPS( VS_OUTPUT In) : SV_Target
@@ -272,29 +286,57 @@ technique10 SampleTexture
         SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
         SetGeometryShader(NULL);
         SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTexturePS()));
     }
 }
 
 technique10 SampleRadialGradient
 {
-    pass P0
+    pass APos
+    {
+        SetRasterizerState(TextureRast);
+        SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+        SetGeometryShader(NULL);
+        SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientPS( sSampler )));
+    }
+    pass A0
+    {
+        SetRasterizerState(TextureRast);
+        SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+        SetGeometryShader(NULL);
+        SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientA0PS( sSampler )));
+    }
+    pass APosWrap
     {
         SetRasterizerState(TextureRast);
         SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
         SetGeometryShader(NULL);
-        SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientPS()));
+        SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientPS( sWrapSampler )));
     }
-    pass P1
+    pass A0Wrap
     {
         SetRasterizerState(TextureRast);
         SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
         SetGeometryShader(NULL);
-        SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientA0PS()));
+        SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientA0PS( sWrapSampler )));
+    }
+    pass APosMirror
+    {
+        SetRasterizerState(TextureRast);
+        SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+        SetGeometryShader(NULL);
+        SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientPS( sMirrorSampler )));
+    }
+    pass A0Mirror
+    {
+        SetRasterizerState(TextureRast);
+        SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleRadialVS()));
+        SetGeometryShader(NULL);
+        SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleRadialGradientA0PS( sMirrorSampler )));
     }
 }
 
 technique10 SampleMaskedTexture
 {
     pass P0
     {
         SetRasterizerState(TextureRast);
--- a/gfx/2d/SourceSurfaceD2DTarget.cpp
+++ b/gfx/2d/SourceSurfaceD2DTarget.cpp
@@ -155,16 +155,26 @@ SourceSurfaceD2DTarget::GetBitmap(ID2D1R
     return NULL;
   }
 
   D2D1_BITMAP_PROPERTIES props =
     D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(mFormat), AlphaMode(mFormat)));
   hr = aRT->CreateSharedBitmap(IID_IDXGISurface, surf, &props, byRef(mBitmap));
 
   if (FAILED(hr)) {
+    // This seems to happen for FORMAT_A8 sometimes...
+    aRT->CreateBitmap(D2D1::SizeU(desc.Width, desc.Height),
+                      D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(mFormat),
+                                             AlphaMode(mFormat))),
+                      byRef(mBitmap));
+
+    if (mDrawTarget) {
+      mBitmap->CopyFromRenderTarget(NULL, mDrawTarget->mRT, NULL);
+      return mBitmap;
+    }
     gfxWarning() << "Failed to create shared bitmap for DrawTarget snapshot. Code: " << hr;
     return NULL;
   }
 
   return mBitmap;
 }
 
 void
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -48,16 +48,17 @@ namespace gfx {
 typedef float Float;
 
 enum SurfaceType
 {
   SURFACE_DATA, /* Data surface - bitmap in memory */
   SURFACE_D2D1_BITMAP, /* Surface wrapping a ID2D1Bitmap */
   SURFACE_D2D1_DRAWTARGET, /* Surface made from a D2D draw target */
   SURFACE_CAIRO, /* Surface wrapping a cairo surface */
+  SURFACE_CAIRO_IMAGE, /* Data surface wrapping a cairo image surface */
   SURFACE_COREGRAPHICS_IMAGE, /* Surface wrapping a CoreGraphics Image */
   SURFACE_SKIA /* Surface wrapping a Skia bitmap */
 };
 
 enum SurfaceFormat
 {
   FORMAT_B8G8R8A8,
   FORMAT_B8G8R8X8,
@@ -90,24 +91,25 @@ enum NativeFontType
 {
   NATIVE_FONT_DWRITE_FONT_FACE,
   NATIVE_FONT_GDI_FONT_FACE,
   NATIVE_FONT_MAC_FONT_FACE,
   NATIVE_FONT_SKIA_FONT_FACE
 };
 
 enum CompositionOp { OP_OVER, OP_ADD, OP_ATOP, OP_OUT, OP_IN, OP_SOURCE, OP_DEST_IN, OP_DEST_OUT, OP_DEST_OVER, OP_DEST_ATOP, OP_XOR, OP_COUNT };
-enum ExtendMode { EXTEND_CLAMP, EXTEND_WRAP, EXTEND_MIRROR };
+enum ExtendMode { EXTEND_CLAMP, EXTEND_REPEAT, EXTEND_REFLECT };
 enum FillRule { FILL_WINDING, FILL_EVEN_ODD };
 enum AntialiasMode { AA_NONE, AA_GRAY, AA_SUBPIXEL };
 enum Snapping { SNAP_NONE, SNAP_ALIGNED };
 enum Filter { FILTER_LINEAR, FILTER_POINT };
 enum PatternType { PATTERN_COLOR, PATTERN_SURFACE, PATTERN_LINEAR_GRADIENT, PATTERN_RADIAL_GRADIENT };
 enum JoinStyle { JOIN_BEVEL, JOIN_ROUND, JOIN_MITER, JOIN_MITER_OR_BEVEL };
 enum CapStyle { CAP_BUTT, CAP_ROUND, CAP_SQUARE };
+enum SamplingBounds { SAMPLING_UNBOUNDED, SAMPLING_BOUNDED };
 
 /* Color is stored in non-premultiplied form */
 struct Color
 {
 public:
   Color()
     : r(0.0f), g(0.0f), b(0.0f), a(0.0f)
   {}