Bug 1220629 - Part 5: Implement PushLayer/PopLayer API for Direct2D 1.1. r=jrmuizel draft
authorBas Schouten <bschouten@mozilla.com>
Wed, 06 Jan 2016 00:23:29 +0100
changeset 319146 ec545e1bde6741e9bc461b1a6ab966e7b6c9236a
parent 319145 78f439fd806576f9c4cd55ea49f12a2994e46fc3
child 319147 5a421568fa20b23d7ceef71eb58014e29dbc1c76
push id8986
push userbschouten@mozilla.com
push dateTue, 05 Jan 2016 23:23:40 +0000
reviewersjrmuizel
bugs1220629
milestone46.0a1
Bug 1220629 - Part 5: Implement PushLayer/PopLayer API for Direct2D 1.1. r=jrmuizel
gfx/2d/DrawTargetD2D1.cpp
gfx/2d/DrawTargetD2D1.h
gfx/2d/HelpersD2D.h
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -582,21 +582,17 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *a
         mDC->SetTransform(D2D1::IdentityMatrix());
         mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
                                     DWRITE_MEASURING_MODE_NATURAL, &userRect);
 
         RefPtr<ID2D1PathGeometry> path;
         D2DFactory()->CreatePathGeometry(getter_AddRefs(path));
         RefPtr<ID2D1GeometrySink> sink;
         path->Open(getter_AddRefs(sink));
-        sink->BeginFigure(D2D1::Point2F(userRect.left, userRect.top), D2D1_FIGURE_BEGIN_FILLED);
-        sink->AddLine(D2D1::Point2F(userRect.right, userRect.top));
-        sink->AddLine(D2D1::Point2F(userRect.right, userRect.bottom));
-        sink->AddLine(D2D1::Point2F(userRect.left, userRect.bottom));
-        sink->EndFigure(D2D1_FIGURE_END_CLOSED);
+        AddRectToSink(sink, userRect);
         sink->Close();
 
         mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), path, D2D1_ANTIALIAS_MODE_ALIASED,
                                               D2DMatrix(mTransform), 1.0f, nullptr,
                                               D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND |
                                               D2D1_LAYER_OPTIONS1_IGNORE_ALPHA), nullptr);
       }
 
@@ -724,16 +720,92 @@ DrawTargetD2D1::PopClip()
       mDC->PopLayer();
     } else {
       mDC->PopAxisAlignedClip();
     }
   }
   CurrentLayer().mPushedClips.pop_back();
 }
 
+void
+DrawTargetD2D1::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+                          const Matrix& aMaskTransform, const IntRect& aBounds,
+                          bool aCopyBackground)
+{
+  D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
+
+  if (aOpaque) {
+    options |= D2D1_LAYER_OPTIONS1_IGNORE_ALPHA;
+  }
+  if (aCopyBackground) {
+    options |= D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
+  }
+
+  RefPtr<ID2D1BitmapBrush> mask;
+
+  Matrix maskTransform = aMaskTransform;
+
+  RefPtr<ID2D1PathGeometry> clip;
+  if (aMask) {
+    mDC->SetTransform(D2D1::IdentityMatrix());
+    mTransformDirty = true;
+
+    RefPtr<ID2D1Image> image = GetImageForSurface(aMask, maskTransform, ExtendMode::CLAMP);
+
+    // The mask is given in user space. Our layer will apply it in device space.
+    maskTransform = maskTransform * mTransform;
+
+    if (image) {
+      RefPtr<ID2D1Bitmap> bitmap;
+      image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+
+      mDC->CreateBitmapBrush(bitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(1.0f, D2DMatrix(maskTransform)), getter_AddRefs(mask));
+      MOZ_ASSERT(bitmap); // This should always be true since it was created for a surface.
+
+      factory()->CreatePathGeometry(getter_AddRefs(clip));
+      RefPtr<ID2D1GeometrySink> sink;
+      clip->Open(getter_AddRefs(sink));
+      AddRectToSink(sink, D2D1::RectF(0, 0, aMask->GetSize().width, aMask->GetSize().height));
+      sink->Close();
+    } else {
+      gfxCriticalError() << "Failed to get image for mask surface!";
+    }
+  }
+
+  PushAllClips();
+
+  mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), clip, D2D1_ANTIALIAS_MODE_ALIASED, D2DMatrix(maskTransform), aOpacity, mask, options), nullptr);
+  PushedLayer pushedLayer;
+  pushedLayer.mClipsArePushed = false;
+  pushedLayer.mIsOpaque = aOpaque;
+  mDC->CreateCommandList(getter_AddRefs(pushedLayer.mCurrentList));
+  mPushedLayers.push_back(pushedLayer);
+
+  mDC->SetTarget(CurrentTarget());
+}
+
+void
+DrawTargetD2D1::PopLayer()
+{
+  MOZ_ASSERT(CurrentLayer().mPushedClips.size() == 0);
+
+  RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
+  mPushedLayers.pop_back();
+  mDC->SetTarget(CurrentTarget());
+
+  list->Close();
+  mDC->SetTransform(D2D1::IdentityMatrix());
+  mTransformDirty = true;
+
+  DCCommandSink sink(mDC);
+  list->Stream(&sink);
+
+  mDC->PopLayer();
+}
+
 already_AddRefed<SourceSurface>
 DrawTargetD2D1::CreateSourceSurfaceFromData(unsigned char *aData,
                                             const IntSize &aSize,
                                             int32_t aStride,
                                             SurfaceFormat aFormat) const
 {
   RefPtr<ID2D1Bitmap1> bitmap;
 
@@ -1060,80 +1132,58 @@ DrawTargetD2D1::FinalizeDrawing(Composit
 
   mDC->SetTransform(D2D1::IdentityMatrix());
   mTransformDirty = true;
 
   if (patternSupported) {
     if (D2DSupportsCompositeMode(aOp)) {
       D2D1_RECT_F rect;
       bool isAligned;
-      RefPtr<ID2D1Bitmap> tmpBitmap;
+      RefPtr<ID2D1Image> tmpImage;
       bool clipIsComplex = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
 
       if (clipIsComplex) {
         if (!IsOperatorBoundByMask(aOp)) {
-          HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap));
-          if (FAILED(hr)) {
-            gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat;
-            // For now, crash in this scenario; this should happen because tmpBitmap is
-            // null and CopyFromBitmap call below dereferences it.
-            // return;
-          }
-          mDC->Flush();
-
-          tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
+          tmpImage = GetImageForLayerContent();
         }
       } else {
         PushAllClips();
       }
       mDC->DrawImage(source, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
 
-      if (tmpBitmap) {
-        RefPtr<ID2D1BitmapBrush> brush;
+      if (tmpImage) {
+        RefPtr<ID2D1ImageBrush> brush;
         RefPtr<ID2D1Geometry> inverseGeom = GetInverseClippedGeometry();
-        mDC->CreateBitmapBrush(tmpBitmap, getter_AddRefs(brush));
+        mDC->CreateImageBrush(tmpImage, D2D1::ImageBrushProperties(D2D1::RectF(0, 0, mSize.width, mSize.height)),
+                              getter_AddRefs(brush));
 
         mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
         mDC->FillGeometry(inverseGeom, brush);
         mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
       }
       return;
     }
 
-    if (!mBlendEffect) {
-      HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(mBlendEffect));
+    RefPtr<ID2D1Effect> blendEffect;
+    HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(blendEffect));
 
-      if (FAILED(hr) || !mBlendEffect) {
-        gfxWarning() << "Failed to create blend effect!";
-        return;
-      }
-    }
-
-    RefPtr<ID2D1Bitmap> tmpBitmap;
-    HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap));
-    if (FAILED(hr)) {
-      gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 5CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat;
+    if (FAILED(hr) || !blendEffect) {
+      gfxWarning() << "Failed to create blend effect!";
       return;
     }
 
-    // This flush is important since the copy method will not know about the context drawing to the surface.
-    // We also need to pop all the clips to make sure any drawn content will have made it to the final bitmap.
-    mDC->Flush();
+    RefPtr<ID2D1Image> tmpImage = GetImageForLayerContent();
 
-    // We need to use a copy here because affects don't accept a surface on
-    // both their in- and outputs.
-    tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
-
-    mBlendEffect->SetInput(0, tmpBitmap);
-    mBlendEffect->SetInput(1, source);
-    mBlendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
+    blendEffect->SetInput(0, tmpImage);
+    blendEffect->SetInput(1, source);
+    blendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
 
     PushAllClips();
 
-    mDC->DrawImage(mBlendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
+    mDC->DrawImage(blendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
     return;
   }
 
   const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern);
   if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) {
     // Draw nothing!
     return;
   }
@@ -1200,16 +1250,45 @@ DrawTargetD2D1::GetDeviceSpaceClipRect(D
     aClipRect = IntersectRect(aClipRect, iter->mBounds);
     if (!iter->mIsPixelAligned) {
       aIsPixelAligned = false;
     }
   }
   return true;
 }
 
+already_AddRefed<ID2D1Image>
+DrawTargetD2D1::GetImageForLayerContent()
+{
+  if (!CurrentLayer().mCurrentList) {
+    RefPtr<ID2D1Bitmap> tmpBitmap;
+    HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap));
+    if (FAILED(hr)) {
+      gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat;
+      // For now, crash in this scenario; this should happen because tmpBitmap is
+      // null and CopyFromBitmap call below dereferences it.
+      // return;
+    }
+    mDC->Flush();
+
+    tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
+    return tmpBitmap.forget();
+  } else {
+    RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
+    mDC->CreateCommandList(getter_AddRefs(CurrentLayer().mCurrentList));
+    mDC->SetTarget(CurrentTarget());
+    list->Close();
+
+    DCCommandSink sink(mDC);
+    list->Stream(&sink);
+
+    return list.forget();
+  }
+}
+
 already_AddRefed<ID2D1Geometry>
 DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
 {
   if (mCurrentClippedGeometry) {
     *aClipBounds = mCurrentClipBounds;
     RefPtr<ID2D1Geometry> clippedGeometry(mCurrentClippedGeometry);
     return clippedGeometry.forget();
   }
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -89,16 +89,22 @@ public:
                           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 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,
                                                                   const IntSize &aSize,
                                                                   int32_t aStride,
                                                                   SurfaceFormat aFormat) const override;
   virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
 
   virtual already_AddRefed<SourceSurface>
@@ -112,16 +118,17 @@ public:
   virtual already_AddRefed<GradientStops>
     CreateGradientStops(GradientStop *aStops,
                         uint32_t aNumStops,
                         ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
 
   virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
 
   virtual bool SupportsRegionClipping() const override { return false; }
+  virtual bool IsCurrentGroupOpaque() override { return CurrentLayer().mIsOpaque; }
 
   virtual void *GetNativeSurface(NativeSurfaceType aType) override { return nullptr; }
 
   bool Init(const IntSize &aSize, SurfaceFormat aFormat);
   bool Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat);
   uint32_t GetByteSize() const;
 
   already_AddRefed<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
@@ -162,16 +169,19 @@ private:
   void FlushTransformToDC() {
     if (mTransformDirty) {
       mDC->SetTransform(D2DMatrix(mTransform));
       mTransformDirty = false;
     }
   }
   void AddDependencyOnSource(SourceSurfaceD2D1* aSource);
 
+  // Must be called with all clips popped and an identity matrix set.
+  already_AddRefed<ID2D1Image> GetImageForLayerContent();
+
   ID2D1Image* CurrentTarget()
   {
     if (CurrentLayer().mCurrentList) {
       return CurrentLayer().mCurrentList;
     }
     return mBitmap;
   }
 
@@ -206,17 +216,16 @@ private:
   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
   // device space.
   IntRect mCurrentClipBounds;
   mutable RefPtr<ID2D1DeviceContext> mDC;
   RefPtr<ID2D1Bitmap1> mBitmap;
   RefPtr<ID2D1CommandList> mCommandList;
-  RefPtr<ID2D1Effect> mBlendEffect;
 
   RefPtr<ID2D1SolidColorBrush> mSolidColorBrush;
 
   // We store this to prevent excessive SetTextRenderingParams calls.
   RefPtr<IDWriteRenderingParams> mTextRenderingParams;
 
   // List of pushed clips.
   struct PushedClip
--- a/gfx/2d/HelpersD2D.h
+++ b/gfx/2d/HelpersD2D.h
@@ -691,12 +691,273 @@ CreatePartialBitmapForSurface(DataSource
 
       aSourceTransform.PreScale(Float(size.width) / newSize.width,
                                 Float(size.height) / newSize.height);
     }
     return bitmap.forget();
   }
 }
 
+static inline void AddRectToSink(ID2D1GeometrySink* aSink, const D2D1_RECT_F& aRect)
+{
+  aSink->BeginFigure(D2D1::Point2F(aRect.left, aRect.top), D2D1_FIGURE_BEGIN_FILLED);
+  aSink->AddLine(D2D1::Point2F(aRect.right, aRect.top));
+  aSink->AddLine(D2D1::Point2F(aRect.right, aRect.bottom));
+  aSink->AddLine(D2D1::Point2F(aRect.left, aRect.bottom));
+  aSink->EndFigure(D2D1_FIGURE_END_CLOSED);
+}
+
+class DCCommandSink : public ID2D1CommandSink
+{
+public:
+  DCCommandSink(ID2D1DeviceContext* aCtx) : mCtx(aCtx)
+  {
+  }
+
+  HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr)
+  {
+    if (!aPtr) {
+      return E_POINTER;
+    }
+
+    if (aIID == IID_IUnknown) {
+      *aPtr = static_cast<IUnknown*>(this);
+      return S_OK;
+    } else if (aIID == IID_ID2D1CommandSink) {
+      *aPtr = static_cast<ID2D1CommandSink*>(this);
+      return S_OK;
+    }
+
+    return E_NOINTERFACE;
+  }
+
+  ULONG STDMETHODCALLTYPE AddRef()
+  {
+    return 1;
+  }
+
+  ULONG STDMETHODCALLTYPE Release()
+  {
+    return 1;
+  }
+
+  STDMETHODIMP BeginDraw()
+  {
+    // We don't want to do anything here!
+    return S_OK;
+  }
+  STDMETHODIMP EndDraw()
+  {
+    // We don't want to do anything here!
+    return S_OK;
+  }
+
+  STDMETHODIMP SetAntialiasMode(
+    D2D1_ANTIALIAS_MODE antialiasMode
+    )
+  {
+    mCtx->SetAntialiasMode(antialiasMode);
+    return S_OK;
+  }
+
+  STDMETHODIMP SetTags(D2D1_TAG tag1, D2D1_TAG tag2)
+  {
+    mCtx->SetTags(tag1, tag2);
+    return S_OK;
+  }
+
+  STDMETHODIMP SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode)
+  {
+    mCtx->SetTextAntialiasMode(textAntialiasMode);
+    return S_OK;
+  }
+
+  STDMETHODIMP SetTextRenderingParams(_In_opt_ IDWriteRenderingParams *textRenderingParams)
+  {
+    mCtx->SetTextRenderingParams(textRenderingParams);
+    return S_OK;
+  }
+
+  STDMETHODIMP  SetTransform(_In_ CONST D2D1_MATRIX_3X2_F *transform)
+  {
+    mCtx->SetTransform(transform);
+    return S_OK;
+  }
+
+  STDMETHODIMP SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND primitiveBlend)
+  {
+    mCtx->SetPrimitiveBlend(primitiveBlend);
+    return S_OK;
+  }
+
+  STDMETHODIMP SetUnitMode(D2D1_UNIT_MODE unitMode)
+  {
+    mCtx->SetUnitMode(unitMode);
+    return S_OK;
+  }
+
+  STDMETHODIMP Clear(_In_opt_ CONST D2D1_COLOR_F *color)
+  {
+    mCtx->Clear(color);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawGlyphRun(
+      D2D1_POINT_2F baselineOrigin,
+      _In_ CONST DWRITE_GLYPH_RUN *glyphRun,
+      _In_opt_ CONST DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription,
+      _In_ ID2D1Brush *foregroundBrush,
+      DWRITE_MEASURING_MODE measuringMode
+    )
+  {
+    mCtx->DrawGlyphRun(baselineOrigin, glyphRun, glyphRunDescription,
+                       foregroundBrush, measuringMode);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawLine(
+      D2D1_POINT_2F point0,
+      D2D1_POINT_2F point1,
+      _In_ ID2D1Brush *brush,
+      FLOAT strokeWidth,
+      _In_opt_ ID2D1StrokeStyle *strokeStyle
+    )
+  {
+    mCtx->DrawLine(point0, point1, brush, strokeWidth, strokeStyle);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawGeometry(
+      _In_ ID2D1Geometry *geometry,
+      _In_ ID2D1Brush *brush,
+      FLOAT strokeWidth,
+      _In_opt_ ID2D1StrokeStyle *strokeStyle
+    )
+  {
+    mCtx->DrawGeometry(geometry, brush, strokeWidth, strokeStyle);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawRectangle(
+      _In_ CONST D2D1_RECT_F *rect,
+      _In_ ID2D1Brush *brush,
+      FLOAT strokeWidth,
+      _In_opt_ ID2D1StrokeStyle *strokeStyle
+    )
+  {
+    mCtx->DrawRectangle(rect, brush, strokeWidth, strokeStyle);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawBitmap(
+      _In_ ID2D1Bitmap *bitmap,
+      _In_opt_ CONST D2D1_RECT_F *destinationRectangle,
+      FLOAT opacity,
+      D2D1_INTERPOLATION_MODE interpolationMode,
+      _In_opt_ CONST D2D1_RECT_F *sourceRectangle,
+      _In_opt_ CONST D2D1_MATRIX_4X4_F *perspectiveTransform
+    )
+  {
+    mCtx->DrawBitmap(bitmap, destinationRectangle, opacity,
+                     interpolationMode, sourceRectangle,
+                     perspectiveTransform);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawImage(
+      _In_ ID2D1Image *image,
+      _In_opt_ CONST D2D1_POINT_2F *targetOffset,
+      _In_opt_ CONST D2D1_RECT_F *imageRectangle,
+      D2D1_INTERPOLATION_MODE interpolationMode,
+      D2D1_COMPOSITE_MODE compositeMode
+    )
+  {
+    mCtx->DrawImage(image, targetOffset, imageRectangle,
+                    interpolationMode, compositeMode);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawGdiMetafile(
+      _In_ ID2D1GdiMetafile *gdiMetafile,
+      _In_opt_ CONST D2D1_POINT_2F *targetOffset
+    )
+  {
+    mCtx->DrawGdiMetafile(gdiMetafile, targetOffset);
+    return S_OK;
+  }
+
+  STDMETHODIMP FillMesh(
+      _In_ ID2D1Mesh *mesh,
+      _In_ ID2D1Brush *brush
+    )
+  {
+    mCtx->FillMesh(mesh, brush);
+    return S_OK;
+  }
+
+  STDMETHODIMP FillOpacityMask(
+      _In_ ID2D1Bitmap *opacityMask,
+      _In_ ID2D1Brush *brush,
+      _In_opt_ CONST D2D1_RECT_F *destinationRectangle,
+      _In_opt_ CONST D2D1_RECT_F *sourceRectangle
+    )
+  {
+    mCtx->FillOpacityMask(opacityMask, brush, destinationRectangle,
+                          sourceRectangle);
+    return S_OK;
+  }
+
+  STDMETHODIMP FillGeometry(
+      _In_ ID2D1Geometry *geometry,
+      _In_ ID2D1Brush *brush,
+      _In_opt_ ID2D1Brush *opacityBrush
+    )
+  {
+    mCtx->FillGeometry(geometry, brush, opacityBrush);
+    return S_OK;
+  }
+
+  STDMETHODIMP FillRectangle(
+      _In_ CONST D2D1_RECT_F *rect,
+      _In_ ID2D1Brush *brush
+    )
+  {
+    mCtx->FillRectangle(rect, brush);
+    return S_OK;
+  }
+
+  STDMETHODIMP PushAxisAlignedClip(
+      _In_ CONST D2D1_RECT_F *clipRect,
+      D2D1_ANTIALIAS_MODE antialiasMode
+    )
+  {
+    mCtx->PushAxisAlignedClip(clipRect, antialiasMode);
+    return S_OK;
+  }
+
+  STDMETHODIMP PushLayer(
+      _In_ CONST D2D1_LAYER_PARAMETERS1 *layerParameters1,
+      _In_opt_ ID2D1Layer *layer
+    )
+  {
+    mCtx->PushLayer(layerParameters1, layer);
+    return S_OK;
+  }
+
+  STDMETHODIMP PopAxisAlignedClip()
+  {
+    mCtx->PopAxisAlignedClip();
+    return S_OK;
+  }
+
+  STDMETHODIMP PopLayer()
+  {
+    mCtx->PopLayer();
+    return S_OK;
+  }
+
+  ID2D1DeviceContext* mCtx;
+};
+
 }
 }
 
 #endif /* MOZILLA_GFX_HELPERSD2D_H_ */