Bug 759568 - Part 2. Render background-clip:text;
MozReview-Commit-ID: IcqiEsMWOEy
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -1811,17 +1811,18 @@ nsCSSRendering::GetImageLayerClip(const
aClipState->mAdditionalBGClipArea.Deflate(padding);
}
// Also clip at a non-scrolling, rounded-corner 'padding-box',
// same as the scrolled content because of the 'overflow' property.
backgroundClip = NS_STYLE_IMAGELAYER_CLIP_PADDING;
}
- if (backgroundClip != NS_STYLE_IMAGELAYER_CLIP_BORDER) {
+ if (backgroundClip != NS_STYLE_IMAGELAYER_CLIP_BORDER &&
+ backgroundClip != NS_STYLE_IMAGELAYER_CLIP_TEXT) {
nsMargin border = aForFrame->GetUsedBorder();
if (backgroundClip == NS_STYLE_IMAGELAYER_CLIP_MOZ_ALMOST_PADDING) {
// Reduce |border| by 1px (device pixels) on all sides, if
// possible, so that we don't get antialiasing seams between the
// background and border.
border.top = std::max(0, border.top - aAppUnitsPerPixel);
border.right = std::max(0, border.right - aAppUnitsPerPixel);
border.bottom = std::max(0, border.bottom - aAppUnitsPerPixel);
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -486,16 +486,56 @@ AddAnimationsForProperty(nsIFrame* aFram
continue;
}
AddAnimationForProperty(aFrame, *property, anim, aLayer, aData, aPending);
effect->SetIsRunningOnCompositor(aProperty, true);
}
}
+static void
+ClipBackgroundByText(nsIFrame* aFrame, nsRenderingContext* aContext,
+ const gfxRect& 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. Then, clip aContext.
+ //
+ // Then, nsDisplayBackgroundImage paints bg-images into this clipped region,
+ // so we get images embedded in text shape!
+
+ nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::GENERATE_GLYPH, false);
+
+ builder.EnterPresShell(aFrame);
+ nsDisplayList list;
+ aFrame->BuildDisplayListForStackingContext(&builder,
+ nsRect(nsPoint(0, 0), aFrame->GetSize()),
+ &list);
+ builder.LeavePresShell(aFrame);
+
+#ifdef DEBUG
+ for (nsDisplayItem* i = list.GetBottom(); i; i = i->GetAbove()) {
+ MOZ_ASSERT(nsDisplayItem::TYPE_TEXT == i->GetType());
+ }
+#endif
+
+ gfxContext* ctx = aContext->ThebesContext();
+ gfxContextMatrixAutoSaveRestore save(ctx);
+ ctx->SetMatrix(ctx->CurrentMatrix().Translate(aFillRect.TopLeft()));
+ ctx->NewPath();
+
+ RefPtr<LayerManager> layerManager =
+ list.PaintRoot(&builder, aContext, nsDisplayList::PAINT_DEFAULT);
+
+ ctx->Clip();
+ list.DeleteAll();
+}
+
/* static */ void
nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer,
nsDisplayListBuilder* aBuilder,
nsDisplayItem* aItem,
nsIFrame* aFrame,
nsCSSProperty aProperty)
{
MOZ_ASSERT(nsCSSProps::PropHasFlags(aProperty,
@@ -2778,16 +2818,17 @@ nsDisplayBackgroundImage::GetInsideClipR
nsRect clipRect;
if (frame->GetType() == nsGkAtoms::canvasFrame) {
nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame();
} else {
switch (aClip) {
case NS_STYLE_IMAGELAYER_CLIP_BORDER:
+ case NS_STYLE_IMAGELAYER_CLIP_TEXT:
clipRect = nsRect(aItem->ToReferenceFrame(), frame->GetSize());
break;
case NS_STYLE_IMAGELAYER_CLIP_PADDING:
clipRect = frame->GetPaddingRect() - frame->GetPosition() + aItem->ToReferenceFrame();
break;
case NS_STYLE_IMAGELAYER_CLIP_CONTENT:
clipRect = frame->GetContentRectRelativeToSelf() + aItem->ToReferenceFrame();
break;
@@ -2815,17 +2856,18 @@ nsDisplayBackgroundImage::GetOpaqueRegio
// this could easily lead to O(N^2) behavior inside InlineBackgroundData,
// which expects frames to be sent to it in content order, not reverse
// content order which we'll produce here.
// Of course, if there's only one frame in the flow, it doesn't matter.
if (mFrame->StyleBorder()->mBoxDecorationBreak ==
NS_STYLE_BOX_DECORATION_BREAK_CLONE ||
(!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
const nsStyleImageLayers::Layer& layer = mBackgroundStyle->mImage.mLayers[mLayer];
- if (layer.mImage.IsOpaque() && layer.mBlendMode == NS_STYLE_BLEND_NORMAL) {
+ if (layer.mImage.IsOpaque() && layer.mBlendMode == NS_STYLE_BLEND_NORMAL &&
+ layer.mClip != NS_STYLE_IMAGELAYER_CLIP_TEXT) {
result = GetInsideClipRegion(this, layer.mClip, mBounds);
}
}
return result;
}
bool
@@ -2889,27 +2931,40 @@ nsDisplayBackgroundImage::Paint(nsDispla
nsRenderingContext* aCtx) {
PaintInternal(aBuilder, aCtx, mVisibleRect, &mBounds);
}
void
nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx, const nsRect& aBounds,
nsRect* aClipRect) {
- nsPoint offset = ToReferenceFrame();
uint32_t flags = aBuilder->GetBackgroundPaintFlags();
CheckForBorderItem(this, flags);
+ nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize());
+ gfxContext* ctx = aCtx->ThebesContext();
+ uint8_t clip = mBackgroundStyle->mImage.mLayers[mLayer].mClip;
+
+ if (clip == NS_STYLE_IMAGELAYER_CLIP_TEXT) {
+ gfxRect bounds = nsLayoutUtils::RectToGfxRect(borderBox, mFrame->PresContext()->AppUnitsPerDevPixel());
+ ctx->Save();
+ ClipBackgroundByText(mFrame, aCtx, bounds);
+ }
+
image::DrawResult result =
nsCSSRendering::PaintBackground(mFrame->PresContext(), *aCtx, mFrame,
aBounds,
- nsRect(offset, mFrame->GetSize()),
+ borderBox,
flags, aClipRect, mLayer,
CompositionOp::OP_OVER);
+ if (clip == NS_STYLE_IMAGELAYER_CLIP_TEXT) {
+ ctx->Restore();
+ }
+
nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
}
void nsDisplayBackgroundImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion* aInvalidRegion)
{
if (!mBackgroundStyle) {
@@ -3313,16 +3368,28 @@ nsDisplayBackgroundColor::Paint(nsDispla
ColorPattern color(ToDeviceColor(mColor));
aDrawTarget.FillRect(rect, color);
#else
gfxContext* ctx = aCtx->ThebesContext();
gfxRect bounds =
nsLayoutUtils::RectToGfxRect(borderBox, mFrame->PresContext()->AppUnitsPerDevPixel());
+
+ uint8_t clip = mBackgroundStyle->mImage.mLayers[0].mClip;
+ if (clip == NS_STYLE_IMAGELAYER_CLIP_TEXT) {
+ gfxContextAutoSaveRestore save(ctx);
+
+ ClipBackgroundByText(mFrame, aCtx, bounds);
+ ctx->SetColor(mColor);
+ ctx->Fill();
+
+ return;
+ }
+
ctx->SetColor(mColor);
ctx->NewPath();
ctx->Rectangle(bounds, true);
ctx->Fill();
#endif
}
nsRegion
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -221,17 +221,18 @@ public:
* @param aBuildCaret whether or not we should include the caret in any
* display lists that we make.
*/
enum Mode {
PAINTING,
EVENT_DELIVERY,
PLUGIN_GEOMETRY,
FRAME_VISIBILITY,
- TRANSFORM_COMPUTATION
+ TRANSFORM_COMPUTATION,
+ GENERATE_GLYPH
};
nsDisplayListBuilder(nsIFrame* aReferenceFrame, Mode aMode, bool aBuildCaret);
~nsDisplayListBuilder();
void SetWillComputePluginGeometry(bool aWillComputePluginGeometry)
{
mWillComputePluginGeometry = aWillComputePluginGeometry;
}
@@ -263,16 +264,24 @@ public:
bool IsForPainting() { return mMode == PAINTING; }
/**
* @return true if the display list is being built for determining frame
* visibility.
*/
bool IsForFrameVisibility() { return mMode == FRAME_VISIBILITY; }
+ /**
+ * @return true if the display list is being built for creating the glyph
+ * path from text items. While painting the display list, all text display
+ * items should only create glyph paths in target context, instead of
+ * drawing text into it.
+ */
+ bool IsForGenerateGlyphPath() { return mMode == GENERATE_GLYPH; }
+
bool WillComputePluginGeometry() { return mWillComputePluginGeometry; }
/**
* @return true if "painting is suppressed" during page load and we
* should paint only the background of the document.
*/
bool IsBackgroundOnly() {
NS_ASSERTION(mPresShellStates.Length() > 0,
"don't call this if we're not in a presshell");
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1852,18 +1852,23 @@ nsFrame::DisplayBackgroundUnconditional(
void
nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists,
bool aForceBackground)
{
// The visibility check belongs here since child elements have the
// opportunity to override the visibility property and display even if
// their parent is hidden.
- if (!IsVisibleForPainting(aBuilder))
+ if (!IsVisibleForPainting(aBuilder)) {
return;
+ }
+
+ if (aBuilder->IsForGenerateGlyphPath()) {
+ return;
+ }
nsCSSShadowArray* shadows = StyleBorder()->mBoxShadow;
if (shadows && shadows->HasShadowWithInset(false)) {
aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
nsDisplayBoxShadowOuter(aBuilder, this));
}
bool bgIsThemed = DisplayBackgroundUnconditional(aBuilder, aLists,
@@ -2543,20 +2548,26 @@ nsIFrame::BuildDisplayListForChild(nsDis
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists,
uint32_t aFlags) {
// If painting is restricted to just the background of the top level frame,
// then we have nothing to do here.
if (aBuilder->IsBackgroundOnly())
return;
+ if (aBuilder->IsForGenerateGlyphPath()) {
+ if (nsGkAtoms::textFrame != aChild->GetType() && aChild->IsLeaf()) {
+ return;
+ }
+ }
+
nsIFrame* child = aChild;
if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
return;
-
+
bool isSVG = (child->GetStateBits() & NS_FRAME_SVG_LAYOUT);
// true if this is a real or pseudo stacking context
bool pseudoStackingContext =
(aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0;
if (!isSVG &&
(aFlags & DISPLAY_CHILD_INLINE) &&
!child->IsFrameOfType(eLineParticipant)) {
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -276,18 +276,16 @@ struct TextRunUserData {
* underline information. The data is computed lazily and cached as necessary.
* These live for just the duration of one paint operation.
*/
class nsTextPaintStyle {
public:
explicit nsTextPaintStyle(nsTextFrame* aFrame);
void SetResolveColors(bool aResolveColors) {
- NS_ASSERTION(mFrame->IsSVGText() || aResolveColors,
- "must resolve colors is frame is not for SVG text");
mResolveColors = aResolveColors;
}
nscolor GetTextColor();
/**
* Compute the colors for normally-selected text. Returns false if
* the normal selection is not being displayed.
*/
@@ -4794,27 +4792,35 @@ nsDisplayText::Paint(nsDisplayListBuilde
gfxContext* ctx = aCtx->ThebesContext();
gfxContextAutoSaveRestore save(ctx);
gfxRect pixelVisible(extraVisible.x, extraVisible.y,
extraVisible.width, extraVisible.height);
pixelVisible.Inflate(2);
pixelVisible.RoundOut();
- ctx->NewPath();
- ctx->Rectangle(pixelVisible);
- ctx->Clip();
+ if (!aBuilder->IsForGenerateGlyphPath()) {
+ ctx->NewPath();
+ ctx->Rectangle(pixelVisible);
+ ctx->Clip();
+ }
NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
nsPoint framePt = ToReferenceFrame();
nsTextFrame::PaintTextParams params(aCtx->ThebesContext());
params.framePt = gfxPoint(framePt.x, framePt.y);
+
params.dirtyRect = extraVisible;
+ nsTextFrame::DrawPathCallbacks callbacks;
+ if (aBuilder->IsForGenerateGlyphPath()) {
+ params.callbacks = &callbacks;
+ }
+
f->PaintText(params, *this, mOpacity);
}
void
nsTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -382,16 +382,18 @@ public:
*/
virtual void NotifyBeforeSelectionDecorationLine(nscolor aColor) { }
/**
* Called just after a path corresponding to a selection decoration line
* has been emitted to the gfxContext.
*/
virtual void NotifySelectionDecorationLinePathEmitted() { }
+
+ virtual void NotifyGlyphPathEmitted() override {}
};
struct PaintTextParams
{
gfxContext* context;
gfxPoint framePt;
LayoutDeviceRect dirtyRect;
gfxTextContextPaint* contextPaint = nullptr;