Bug 1269971 - Part 2. From ClipBackgroundByText to GenerateAndPushTextMask; draft
authorCJKu <cku@mozilla.com>
Fri, 13 May 2016 00:08:41 +0800
changeset 366371 f1193e084e23184819c8781cb3239372758fd091
parent 366370 964a6889c0c2127cd5d470732413643dfda1efc8
child 366372 223dd5b4ee852c5237082dce3e3ed7efba3b7089
push id17966
push usercku@mozilla.com
push dateThu, 12 May 2016 16:10:23 +0000
bugs1269971
milestone49.0a1
Bug 1269971 - Part 2. From ClipBackgroundByText to GenerateAndPushTextMask; MozReview-Commit-ID: 1PK2Huytq3i
layout/base/nsDisplayList.cpp
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrame.h
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -488,40 +488,69 @@ AddAnimationsForProperty(nsIFrame* aFram
     }
 
     AddAnimationForProperty(aFrame, *property, anim, aLayer, aData, aPending);
     effect->SetIsRunningOnCompositor(aProperty, true);
   }
 }
 
 static void
-ClipBackgroundByText(nsIFrame* aFrame, nsRenderingContext* aContext,
-                     const nsRect& aFillRect)
+GenerateAndPushTextMask(nsIFrame* aFrame, nsRenderingContext* aContext,
+                        const nsRect& aFillRect)
 {
   // The main function of enabling background-clip:text property value.
   // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call
   // this function to
-  // 1. Ask every text frame objects in aFrame puts glyph paths into aContext.
-  // 2. Clip aContext.
-  //
-  // Then, nsDisplayBackgroundImage paints bg-images into this clipped region,
-  // so we get images embedded in text shape!
-
-  gfxContext* ctx = aContext->ThebesContext();
-  gfxContextMatrixAutoSaveRestore save(ctx);
-  gfxRect bounds = nsLayoutUtils::RectToGfxRect(aFillRect, aFrame->PresContext()->AppUnitsPerDevPixel());
-  ctx->SetMatrix(ctx->CurrentMatrix().Translate(bounds.TopLeft()));
-  ctx->NewPath();
-
-  nsLayoutUtils::PaintFrame(aContext, aFrame,
+  // 1. Generate a mask by all descendant text frames
+  // 2. Push the generated mask into aContext.
+
+  gfxContext* sourceCtx = aContext->ThebesContext();
+  gfxRect bounds =
+    nsLayoutUtils::RectToGfxRect(aFillRect,
+                                 aFrame->PresContext()->AppUnitsPerDevPixel());
+
+  // Evaluate required surface size.
+  IntRect drawRect;
+  {
+    gfxContextMatrixAutoSaveRestore matRestore(sourceCtx);
+
+    sourceCtx->SetMatrix(gfxMatrix());
+    gfxRect clipRect = sourceCtx->GetClipExtents();
+    drawRect = RoundedOut(ToRect(clipRect));
+  }
+
+  // Create a mask surface.
+  RefPtr<DrawTarget> sourceTarget = sourceCtx->GetDrawTarget();
+  RefPtr<DrawTarget> maskDT =
+    sourceTarget->CreateSimilarDrawTarget(drawRect.Size(),
+                                          SurfaceFormat::A8);
+  if (!maskDT) {
+    NS_ABORT_OOM(drawRect.width * drawRect.height);
+  }
+  RefPtr<gfxContext> maskCtx = gfxContext::ForDrawTargetWithTransform(maskDT);
+  gfxMatrix currentMatrix = sourceCtx->CurrentMatrix();
+  maskCtx->SetMatrix(gfxMatrix::Translation(bounds.TopLeft()) *
+                     currentMatrix *
+                     gfxMatrix::Translation(-drawRect.TopLeft()));
+
+  // Shade text shape into mask A8 surface.
+  nsRenderingContext rc(maskCtx);
+  nsLayoutUtils::PaintFrame(&rc, aFrame,
                             nsRect(nsPoint(0, 0), aFrame->GetSize()),
                             NS_RGB(255, 255, 255),
                             nsDisplayListBuilderMode::GENERATE_GLYPH);
 
-  ctx->Clip();
+  // Push the generated mask into aContext, so that the caller can pop and
+  // blend with it.
+  Matrix maskTransform = ToMatrix(currentMatrix) *
+                         Matrix::Translation(-drawRect.x, -drawRect.y);
+  maskTransform.Invert();
+
+  RefPtr<SourceSurface> maskSurface = maskDT->Snapshot();
+  sourceCtx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 1.0, maskSurface, maskTransform);
 }
 
 /* static */ void
 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer,
                                                          nsDisplayListBuilder* aBuilder,
                                                          nsDisplayItem* aItem,
                                                          nsIFrame* aFrame,
                                                          nsCSSProperty aProperty)
@@ -2907,29 +2936,28 @@ nsDisplayBackgroundImage::PaintInternal(
                                         nsRect* aClipRect) {
   uint32_t flags = aBuilder->GetBackgroundPaintFlags();
   CheckForBorderItem(this, flags);
 
   gfxContext* ctx = aCtx->ThebesContext();
   uint8_t clip = mBackgroundStyle->mImage.mLayers[mLayer].mClip;
 
   if (clip == NS_STYLE_IMAGELAYER_CLIP_TEXT) {
-    ctx->Save();
-    ClipBackgroundByText(mFrame, aCtx, mBackgroundRect);
+    GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect);
   }
 
   image::DrawResult result =
     nsCSSRendering::PaintBackground(mFrame->PresContext(), *aCtx, mFrame,
                                     aBounds,
                                     mBackgroundRect,
                                     flags, aClipRect, mLayer,
                                     CompositionOp::OP_OVER);
 
   if (clip == NS_STYLE_IMAGELAYER_CLIP_TEXT) {
-    ctx->Restore();
+    ctx->PopGroupAndBlend();
   }
 
   nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
 }
 
 void nsDisplayBackgroundImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                          const nsDisplayItemGeometry* aGeometry,
                                                          nsRegion* aInvalidRegion)
@@ -3339,20 +3367,20 @@ nsDisplayBackgroundColor::Paint(nsDispla
   aDrawTarget.FillRect(rect, color);
 #else
   gfxContext* ctx = aCtx->ThebesContext();
 
   uint8_t clip = mBackgroundStyle->mImage.mLayers[0].mClip;
   if (clip == NS_STYLE_IMAGELAYER_CLIP_TEXT) {
     gfxContextAutoSaveRestore save(ctx);
 
-    ClipBackgroundByText(mFrame, aCtx, mBackgroundRect);
+    GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect);
     ctx->SetColor(mColor);
     ctx->Fill();
-
+    ctx->PopGroupAndBlend();
     return;
   }
 
   gfxRect bounds =
     nsLayoutUtils::RectToGfxRect(mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
 
   ctx->SetColor(mColor);
   ctx->NewPath();
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -4793,20 +4793,17 @@ nsDisplayText::Paint(nsDisplayListBuilde
       ctx->SetMatrix(mat);
     }
   }
   nsTextFrame::PaintTextParams params(aCtx->ThebesContext());
   params.framePt = gfxPoint(framePt.x, framePt.y);
 
   params.dirtyRect = extraVisible;
   nsTextFrame::DrawPathCallbacks callbacks;
-  if (aBuilder->IsForGenerateGlyphMask()) {
-    params.callbacks = &callbacks;
-  }
-
+  params.generateTextMask = aBuilder->IsForGenerateGlyphMask();
   f->PaintText(params, *this, mOpacity);
 }
 
 void
 nsTextFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                               const nsRect&           aDirtyRect,
                               const nsDisplayListSet& aLists)
 {
@@ -6590,17 +6587,19 @@ nsTextFrame::PaintText(const PaintTextPa
     params.provider = &provider;
     params.contentRange = contentRange;
     params.textPaintStyle = &textPaintStyle;
     if (PaintTextWithSelection(params, clipEdges)) {
       return;
     }
   }
 
-  nscolor foregroundColor = textPaintStyle.GetTextColor();
+  nscolor foregroundColor = aParams.generateTextMask
+                            ? NS_RGBA(0, 0, 0, 255)
+                            : textPaintStyle.GetTextColor();
   if (aOpacity != 1.0f) {
     gfx::Color gfxColor = gfx::Color::FromABGR(foregroundColor);
     gfxColor.a *= aOpacity;
     foregroundColor = gfxColor.ToABGR();
   }
 
   nscolor textStrokeColor = textPaintStyle.GetWebkitTextStrokeColor();
   if (aOpacity != 1.0f) {
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -387,16 +387,17 @@ public:
 
   struct PaintTextParams
   {
     gfxContext* context;
     gfxPoint framePt;
     LayoutDeviceRect dirtyRect;
     gfxTextContextPaint* contextPaint = nullptr;
     DrawPathCallbacks* callbacks = nullptr;
+    bool generateTextMask = false;
     explicit PaintTextParams(gfxContext* aContext) : context(aContext) {}
   };
 
   struct PaintTextSelectionParams : PaintTextParams
   {
     gfxPoint textBaselinePt;
     PropertyProvider* provider = nullptr;
     Range contentRange;