From dc06f6d670a286109e7ef1f585853823720c9b76 Mon Sep 17 00:00:00 2001 From: Robert Longson Date: Mon, 13 Jan 2014 12:21:42 +0000 Subject: [PATCH] Bug 889235 - Make text-shadow work on SVG text. r=cam --- layout/base/nsLayoutUtils.cpp | 4 +- layout/generic/nsTextFrame.cpp | 7 +--- .../reftests/svg/text/dynamic-text-shadow.svg | 15 +++++++ .../reftests/svg/text/ignore-text-shadow.svg | 7 ---- layout/reftests/svg/text/reftest.list | 5 ++- layout/reftests/svg/text/text-shadow-ref.svg | 8 ++++ layout/reftests/svg/text/text-shadow.svg | 7 ++++ layout/style/nsStyleStruct.h | 4 +- layout/style/nsStyleStructInlines.h | 11 ++--- layout/svg/SVGTextFrame.cpp | 41 +++++++++++++++---- 10 files changed, 75 insertions(+), 34 deletions(-) create mode 100644 layout/reftests/svg/text/dynamic-text-shadow.svg delete mode 100644 layout/reftests/svg/text/ignore-text-shadow.svg create mode 100644 layout/reftests/svg/text/text-shadow-ref.svg create mode 100644 layout/reftests/svg/text/text-shadow.svg diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 5e96578252c1..63e1bb5f905f 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -2599,7 +2599,7 @@ nsLayoutUtils::GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect, uint32_t aFlags) { const nsStyleText* textStyle = aFrame->StyleText(); - if (!textStyle->HasTextShadow(aFrame)) + if (!textStyle->HasTextShadow()) return aTextAndDecorationsRect; nsRect resultRect = aTextAndDecorationsRect; @@ -3814,7 +3814,7 @@ nsLayoutUtils::PaintTextShadow(const nsIFrame* aFrame, void* aCallbackData) { const nsStyleText* textStyle = aFrame->StyleText(); - if (!textStyle->HasTextShadow(aFrame)) + if (!textStyle->HasTextShadow()) return; // Text shadow happens with the last value being painted at the back, diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index ce015b44ac13..ca47f5c4c2b3 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -5211,9 +5211,6 @@ static bool GetSelectionTextShadow(nsIFrame* aFrame, nsTextPaintStyle& aTextPaintStyle, nsCSSShadowArray** aShadow) { - if (aFrame->IsSVGText()) { - return false; - } switch (aType) { case nsISelectionController::SELECTION_NORMAL: return aTextPaintStyle.GetSelectionShadow(aShadow); @@ -5511,7 +5508,7 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx, // Determine what shadow, if any, to draw - either from textStyle // or from the ::-moz-selection pseudo-class if specified there - nsCSSShadowArray *shadow = textStyle->GetTextShadow(this); + nsCSSShadowArray* shadow = textStyle->GetTextShadow(); GetSelectionTextShadow(this, type, aTextPaintStyle, &shadow); // Draw shadows, if any @@ -5897,7 +5894,7 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, nscolor foregroundColor = textPaintStyle.GetTextColor(); if (!aCallbacks) { const nsStyleText* textStyle = StyleText(); - if (textStyle->HasTextShadow(this)) { + if (textStyle->HasTextShadow()) { // Text shadow happens with the last value being painted at the back, // ie. it is painted first. gfxTextRun::Metrics shadowMetrics = diff --git a/layout/reftests/svg/text/dynamic-text-shadow.svg b/layout/reftests/svg/text/dynamic-text-shadow.svg new file mode 100644 index 000000000000..469eaa6023ef --- /dev/null +++ b/layout/reftests/svg/text/dynamic-text-shadow.svg @@ -0,0 +1,15 @@ + + + Hello + + diff --git a/layout/reftests/svg/text/ignore-text-shadow.svg b/layout/reftests/svg/text/ignore-text-shadow.svg deleted file mode 100644 index bdafea436043..000000000000 --- a/layout/reftests/svg/text/ignore-text-shadow.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - hello there everyone - diff --git a/layout/reftests/svg/text/reftest.list b/layout/reftests/svg/text/reftest.list index 4d9682aa2b87..478bf0db86d0 100644 --- a/layout/reftests/svg/text/reftest.list +++ b/layout/reftests/svg/text/reftest.list @@ -127,6 +127,9 @@ fuzzy-if(/^Windows\x20NT\x206\.[12]/.test(http.oscpu),4,15) == textLength-3.svg == textLength-5.svg textLength-5-ref.svg == textLength-6.svg textLength-6-ref.svg +# text-shadow +== text-shadow.svg text-shadow-ref.svg + # tests for ignoring various properties == ignore-border.svg ignore-prop-ref.svg == ignore-display.svg ignore-display-ref.svg @@ -135,7 +138,6 @@ fuzzy-if(/^Windows\x20NT\x206\.[12]/.test(http.oscpu),4,15) == textLength-3.svg == ignore-position.svg ignore-position-ref.svg == ignore-margin.svg ignore-prop-ref.svg == ignore-padding.svg ignore-prop-ref.svg -== ignore-text-shadow.svg ignore-prop-ref.svg == ignore-vertical-align.svg ignore-vertical-align-ref.svg == ignore-overflow-scroll.svg ignore-prop-ref.svg == ignore-text-align.svg ignore-prop-ref.svg @@ -163,6 +165,7 @@ fuzzy-if(/^Windows\x20NT\x206\.[12]/.test(http.oscpu),4,15) == textLength-3.svg == dynamic-dominant-baseline.svg dynamic-dominant-baseline-ref.svg == dynamic-multiple-x.svg dynamic-multiple-x-ref.svg fuzzy-if(!d2d,14,2) fuzzy-if(azureQuartz,1,6) == dynamic-non-scaling-stroke.svg dynamic-non-scaling-stroke-ref.svg #Bug 885316 +== dynamic-text-shadow.svg text-shadow-ref.svg # text and masks HTTP(../..) == mask-applied.svg mask-applied-ref.svg diff --git a/layout/reftests/svg/text/text-shadow-ref.svg b/layout/reftests/svg/text/text-shadow-ref.svg new file mode 100644 index 000000000000..0fff19f14436 --- /dev/null +++ b/layout/reftests/svg/text/text-shadow-ref.svg @@ -0,0 +1,8 @@ + + + Hello + Hello + diff --git a/layout/reftests/svg/text/text-shadow.svg b/layout/reftests/svg/text/text-shadow.svg new file mode 100644 index 000000000000..35875f51df07 --- /dev/null +++ b/layout/reftests/svg/text/text-shadow.svg @@ -0,0 +1,7 @@ + + + Hello + diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 451fed8027f7..e4fe33747f13 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1431,13 +1431,13 @@ struct nsStyleText { } // These are defined in nsStyleStructInlines.h. + inline bool HasTextShadow() const; + inline nsCSSShadowArray* GetTextShadow() const; // The aContextFrame argument on each of these is the frame this // style struct is for. If the frame is for SVG text, the return // value will be massaged to be something that makes sense for // SVG text. - inline bool HasTextShadow(const nsIFrame* aContextFrame) const; - inline nsCSSShadowArray* GetTextShadow(const nsIFrame* aContextFrame) const; inline bool WhiteSpaceCanWrap(const nsIFrame* aContextFrame) const; inline bool WordCanWrap(const nsIFrame* aContextFrame) const; }; diff --git a/layout/style/nsStyleStructInlines.h b/layout/style/nsStyleStructInlines.h index 53d2e461f013..942b24bd863b 100644 --- a/layout/style/nsStyleStructInlines.h +++ b/layout/style/nsStyleStructInlines.h @@ -57,19 +57,14 @@ nsStyleBorder::GetSubImage(uint8_t aIndex) const } bool -nsStyleText::HasTextShadow(const nsIFrame* aContextFrame) const +nsStyleText::HasTextShadow() const { - NS_ASSERTION(aContextFrame->StyleText() == this, "unexpected aContextFrame"); - return mTextShadow && !aContextFrame->IsSVGText(); + return mTextShadow; } nsCSSShadowArray* -nsStyleText::GetTextShadow(const nsIFrame* aContextFrame) const +nsStyleText::GetTextShadow() const { - NS_ASSERTION(aContextFrame->StyleText() == this, "unexpected aContextFrame"); - if (aContextFrame->IsSVGText()) { - return nullptr; - } return mTextShadow; } diff --git a/layout/svg/SVGTextFrame.cpp b/layout/svg/SVGTextFrame.cpp index 295cf93b5f96..ca43e4938301 100644 --- a/layout/svg/SVGTextFrame.cpp +++ b/layout/svg/SVGTextFrame.cpp @@ -632,8 +632,10 @@ struct TextRenderedRun eIncludeFill = 1, // Includes the stroke geometry of the text in the returned rectangle. eIncludeStroke = 2, + // Includes any text shadow in the returned rectangle. + eIncludeTextShadow = 4, // Don't include any horizontal glyph overflow in the returned rectangle. - eNoHorizontalOverflow = 4 + eNoHorizontalOverflow = 8 }; /** @@ -910,11 +912,21 @@ TextRenderedRun::GetRunUserSpaceRect(nsPresContext* aContext, x = metrics.mBoundingBox.x; width = metrics.mBoundingBox.width; } - gfxRect fillInAppUnits(x, baseline - above, - width, metrics.mBoundingBox.height + above + below); + nsRect fillInAppUnits(x, baseline - above, + width, metrics.mBoundingBox.height + above + below); + + // Account for text-shadow. + if (aFlags & eIncludeTextShadow) { + fillInAppUnits = + nsLayoutUtils::GetTextShadowRectsUnion(fillInAppUnits, mFrame); + } // Convert the app units rectangle to user units. - gfxRect fill = AppUnitsToFloatCSSPixels(fillInAppUnits, aContext); + gfxRect fill = AppUnitsToFloatCSSPixels(gfxRect(fillInAppUnits.x, + fillInAppUnits.y, + fillInAppUnits.width, + fillInAppUnits.height), + aContext); // Scale the rectangle up due to any mFontSizeScaleFactor. We scale // it around the text's origin. @@ -3738,14 +3750,25 @@ SVGTextFrame::ReflowSVG() TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames); for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) { uint32_t runFlags = 0; + if (run.mFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None) { + runFlags |= TextRenderedRun::eIncludeFill | + TextRenderedRun::eIncludeTextShadow; + } + if (nsSVGUtils::HasStroke(run.mFrame)) { + runFlags |= TextRenderedRun::eIncludeFill | + TextRenderedRun::eIncludeTextShadow; + } + // Our "visual" overflow rect needs to be valid for building display lists + // for hit testing, which means that for certain values of 'pointer-events' + // it needs to include the geometry of the fill or stroke even when the fill/ + // stroke don't actually render (e.g. when stroke="none" or + // stroke-opacity="0"). GetGeometryHitTestFlags accounts for 'pointer-events'. + // The text-shadow is not part of the hit-test area. uint16_t hitTestFlags = nsSVGUtils::GetGeometryHitTestFlags(run.mFrame); - - if ((hitTestFlags & SVG_HIT_TEST_FILL) || - run.mFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None) { + if (hitTestFlags & SVG_HIT_TEST_FILL) { runFlags |= TextRenderedRun::eIncludeFill; } - if ((hitTestFlags & SVG_HIT_TEST_STROKE) || - nsSVGUtils::HasStroke(run.mFrame)) { + if (hitTestFlags & SVG_HIT_TEST_STROKE) { runFlags |= TextRenderedRun::eIncludeStroke; }