Bug 1153609 - Push layers for cleartype when trying to draw cleartype glyphs to a transparent layer. r=jrmuizel, a=sledru
authorBas Schouten <bschouten@mozilla.com>
Fri, 29 May 2015 14:49:19 +0200
changeset 274999 b71e1e1d26ce4863337a0bfde13c309f5ff9f5dd
parent 274998 c45872216c7bba947d0f2f715859e28132cf91dd
child 275000 bae7437861e7e747d0edafe98d7e0f81e0b0ecd0
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel, sledru
bugs1153609
milestone40.0a2
Bug 1153609 - Push layers for cleartype when trying to draw cleartype glyphs to a transparent layer. r=jrmuizel, a=sledru This patch lets us repush all layers with their backgrounds copied when drawing subpixel AA'ed text to a transparent surface. This can be wasteful since the last layer that was pushed could already have had its pixels where the glyphs will be drawn made opaque, however we have no way of knowing this so we have to always repush the layers.
gfx/2d/DrawTargetD2D1.cpp
gfx/2d/DrawTargetD2D1.h
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -519,20 +519,79 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *a
     mTextRenderingParams = params;
   }
 
   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
 
   AutoDWriteGlyphRun autoRun;
   DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun);
 
+  bool needsRepushedLayers = false;
+  if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && mFormat != SurfaceFormat::B8G8R8X8) {
+    D2D1_RECT_F rect;
+    bool isAligned;
+    needsRepushedLayers = mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
+
+    // If we have a complex clip in our stack and we have a transparent
+    // background, and subpixel AA is permitted, we need to repush our layer
+    // stack limited by the glyph run bounds initializing our layers for
+    // subpixel AA.
+    if (needsRepushedLayers) {
+      mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
+                                  DWRITE_MEASURING_MODE_NATURAL, &rect);
+      rect.left = std::floor(rect.left);
+      rect.right = std::ceil(rect.right);
+      rect.top = std::floor(rect.top);
+      rect.bottom = std::ceil(rect.bottom);
+
+      PopAllClips();
+
+      if (!mTransform.IsRectilinear()) {
+        // We must limit the pixels we touch to the -user space- bounds of
+        // the glyphs being drawn. In order not to get transparent pixels
+        // copied up in our pushed layer stack.
+        D2D1_RECT_F userRect;
+        mDC->SetTransform(D2D1::IdentityMatrix());
+        mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
+                                    DWRITE_MEASURING_MODE_NATURAL, &userRect);
+
+        RefPtr<ID2D1PathGeometry> path;
+        D2DFactory()->CreatePathGeometry(byRef(path));
+        RefPtr<ID2D1GeometrySink> sink;
+        path->Open(byRef(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);
+        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);
+      }
+
+      PushClipsToDC(mDC, true, rect);
+      mDC->SetTransform(D2DMatrix(mTransform));
+    }
+  }
+
   if (brush) {
     mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush);
   }
 
+  if (needsRepushedLayers) {
+    PopClipsFromDC(mDC);
+
+    if (!mTransform.IsRectilinear()) {
+      mDC->PopLayer();
+    }
+  }
+
   FinalizeDrawing(aOptions.mCompositionOp, aPattern);
 }
 
 void
 DrawTargetD2D1::Mask(const Pattern &aSource,
                      const Pattern &aMask,
                      const DrawOptions &aOptions)
 {
@@ -1246,25 +1305,25 @@ DrawTargetD2D1::PushAllClips()
   if (!mClipsArePushed) {
     PushClipsToDC(mDC);
   
     mClipsArePushed = true;
   }
 }
 
 void
-DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC)
+DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
 {
   mDC->SetTransform(D2D1::IdentityMatrix());
   mTransformDirty = true;
 
   for (std::vector<PushedClip>::iterator iter = mPushedClips.begin();
         iter != mPushedClips.end(); iter++) {
     if (iter->mPath) {
-      PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform);
+      PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform, aForceIgnoreAlpha, aMaxRect);
     } else {
       mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
     }
   }
 }
 
 void
 DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC)
@@ -1477,23 +1536,24 @@ DrawTargetD2D1::OptimizeSourceSurface(So
   if (!bitmap) {
     return data.forget();
   }
 
   return new SourceSurfaceD2D1(bitmap.get(), mDC, data->GetFormat(), data->GetSize());
 }
 
 void
-DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform)
+DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
+                             bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
 {
   D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
 
-  if (aDC->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE) {
+  if (aDC->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE || aForceIgnoreAlpha) {
     options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
   }
 
-  mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), aGeometry,
+  mDC->PushLayer(D2D1::LayerParameters1(aMaxRect, aGeometry,
                                         D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform,
                                         1.0, nullptr, options), nullptr);
 }
 
 }
 }
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -174,24 +174,25 @@ private:
   TemporaryRef<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
 
   TemporaryRef<ID2D1Geometry> GetInverseClippedGeometry();
 
   bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned);
 
   void PopAllClips();
   void PushAllClips();
-  void PushClipsToDC(ID2D1DeviceContext *aDC);
+  void PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha = false, const D2D1_RECT_F& aMaxRect = D2D1::InfiniteRect());
   void PopClipsFromDC(ID2D1DeviceContext *aDC);
 
   TemporaryRef<ID2D1Brush> CreateTransparentBlackBrush();
   TemporaryRef<ID2D1SolidColorBrush> GetSolidColorBrush(const D2D_COLOR_F& aColor);
   TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
 
-  void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform);
+  void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
+                    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