Bug 1097499 part 13 - Draw decoration line properly for text-combine-upright. r=jfkthame
authorXidorn Quan <quanxunzhen@gmail.com>
Fri, 22 Apr 2016 09:18:41 +1000
changeset 332280 45acf5ee3c4222765919340d3f7abace373bab56
parent 332279 d238e12a7f717b577ecceefa4e8e93951ee9437c
child 332281 660f1684ab37d18791bd03f6121813f6f3b2a449
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1097499
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1097499 part 13 - Draw decoration line properly for text-combine-upright. r=jfkthame MozReview-Commit-ID: AymG09nvxh1
gfx/thebes/gfxContext.h
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrame.h
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -637,16 +637,18 @@ public:
     }
 
     const gfxMatrix& Matrix()
     {
         MOZ_ASSERT(mContext, "mMatrix doesn't contain a useful matrix");
         return mMatrix;
     }
 
+    bool HasMatrix() const { return !!mContext; }
+
 private:
     gfxContext *mContext;
     gfxMatrix   mMatrix;
 };
 
 
 class DrawTargetAutoDisableSubpixelAntialiasing {
 public:
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -4948,27 +4948,30 @@ nsTextFrame::GetTextDecorations(
                     nsTextFrame::TextDecorations& aDecorations)
 {
   const nsCompatibility compatMode = aPresContext->CompatibilityMode();
 
   bool useOverride = false;
   nscolor overrideColor = NS_RGBA(0, 0, 0, 0);
 
   bool nearestBlockFound = false;
-  WritingMode wm = GetWritingMode();
+  // Use writing mode of parent frame for orthogonal text frame to work.
+  // See comment in nsTextFrame::DrawTextRunAndDecorations.
+  WritingMode wm = GetParent()->GetWritingMode();
   bool vertical = wm.IsVertical();
 
+  nscoord ascent = GetLogicalBaseline(wm);
   // physicalBlockStartOffset represents the offset from our baseline
   // to f's physical block start, which is top in horizontal writing
   // mode, and left in vertical writing modes, in our coordinate space.
   // This physical block start is logical block start in most cases,
   // but for vertical-rl, it is logical block end, and consequently in
   // that case, it starts from the descent instead of ascent.
   nscoord physicalBlockStartOffset =
-    wm.IsVerticalRL() ? GetSize().width - mAscent : mAscent;
+    wm.IsVerticalRL() ? GetSize().width - ascent : ascent;
   // baselineOffset represents the offset from our baseline to f's baseline or
   // the nearest block's baseline, in our coordinate space, whichever is closest
   // during the particular iteration
   nscoord baselineOffset = 0;
 
   for (nsIFrame* f = this, *fChild = nullptr;
        f;
        fChild = f,
@@ -5211,20 +5214,19 @@ void
 nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
                                      nsIFrame* aBlock,
                                      PropertyProvider& aProvider,
                                      nsRect* aVisualOverflowRect,
                                      bool aIncludeTextDecorations)
 {
   const WritingMode wm = GetWritingMode();
   bool verticalRun = mTextRun->IsVertical();
-  bool useVerticalMetrics = verticalRun && mTextRun->UseCenterBaseline();
-  bool inverted = wm.IsLineInverted();
 
   if (IsFloatingFirstLetterChild()) {
+    bool inverted = wm.IsLineInverted();
     // The underline/overline drawable area must be contained in the overflow
     // rect when this is in floating first letter frame at *both* modes.
     // In this case, aBlock is the ::first-letter frame.
     uint8_t decorationStyle = aBlock->StyleContext()->
                                 StyleTextReset()->GetDecorationStyle();
     // If the style is none, let's include decoration line rect as solid style
     // since changing the style from none to solid/dotted/dashed doesn't cause
     // reflow.
@@ -5260,43 +5262,50 @@ nsTextFrame::UnionAdditionalOverflow(nsP
     aVisualOverflowRect->UnionRect(*aVisualOverflowRect, underlineRect);
     aVisualOverflowRect->UnionRect(*aVisualOverflowRect, overlineRect);
 
     // XXX If strikeoutSize is much thicker than the underlineSize, it may
     //     cause overflowing from the overflow rect.  However, such case
     //     isn't realistic, we don't need to compute it now.
   }
   if (aIncludeTextDecorations) {
+    // Use writing mode of parent frame for orthogonal text frame to
+    // work. See comment in nsTextFrame::DrawTextRunAndDecorations.
+    WritingMode parentWM = GetParent()->GetWritingMode();
+    bool verticalDec = parentWM.IsVertical();
+    bool useVerticalMetrics = verticalDec != verticalRun
+      ? verticalDec : verticalRun && mTextRun->UseCenterBaseline();
+
     // Since CSS 2.1 requires that text-decoration defined on ancestors maintain
     // style and position, they can be drawn at virtually any y-offset, so
     // maxima and minima are required to reliably generate the rectangle for
     // them
     TextDecorations textDecs;
     GetTextDecorations(aPresContext, eResolvedColors, textDecs);
     if (textDecs.HasDecorationLines()) {
       nscoord inflationMinFontSize =
         nsLayoutUtils::InflationMinFontSizeFor(aBlock);
 
-      const nscoord measure = verticalRun ? GetSize().height : GetSize().width;
-      const gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel(),
-                     gfxWidth = measure / appUnitsPerDevUnit;
-      gfxFloat ascent = gfxFloat(mAscent) / appUnitsPerDevUnit;
+      const nscoord measure = verticalDec ? GetSize().height : GetSize().width;
+      const gfxFloat app = aPresContext->AppUnitsPerDevPixel();
+      gfxFloat gfxWidth = measure / app;
+      gfxFloat ascent = gfxFloat(GetLogicalBaseline(parentWM)) / app;
       nscoord frameBStart = 0;
-      if (wm.IsVerticalRL()) {
+      if (parentWM.IsVerticalRL()) {
         frameBStart = GetSize().width;
         ascent = -ascent;
       }
       // The decoration-line offsets need to be reversed for sideways-lr mode,
       // so we will multiply the values from metrics by this factor.
       gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0;
 
       nsCSSRendering::DecorationRectParams params;
       params.lineSize = Size(gfxWidth, 0);
       params.ascent = ascent;
-      params.vertical = verticalRun;
+      params.vertical = verticalDec;
 
       nscoord topOrLeft(nscoord_MAX), bottomOrRight(nscoord_MIN);
       typedef gfxFont::Metrics Metrics;
       auto accumulateDecorationRect = [&](const LineDecoration& dec,
                                           gfxFloat Metrics::* lineSize,
                                           gfxFloat Metrics::* lineOffset) {
         params.style = dec.mStyle;
         // If the style is solid, let's include decoration line rect of solid
@@ -5311,20 +5320,20 @@ nsTextFrame::UnionAdditionalOverflow(nsP
         const Metrics metrics =
           GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
                               useVerticalMetrics);
 
         params.lineSize.height = metrics.*lineSize;
         params.offset = decorationOffsetDir * metrics.*lineOffset;
         const nsRect decorationRect =
           nsCSSRendering::GetTextDecorationRect(aPresContext, params) +
-          (verticalRun ? nsPoint(frameBStart - dec.mBaselineOffset, 0)
+          (verticalDec ? nsPoint(frameBStart - dec.mBaselineOffset, 0)
                        : nsPoint(0, -dec.mBaselineOffset));
 
-        if (verticalRun) {
+        if (verticalDec) {
           topOrLeft = std::min(decorationRect.x, topOrLeft);
           bottomOrRight = std::max(decorationRect.XMost(), bottomOrRight);
         } else {
           topOrLeft = std::min(decorationRect.y, topOrLeft);
           bottomOrRight = std::max(decorationRect.YMost(), bottomOrRight);
         }
       };
 
@@ -5343,22 +5352,22 @@ nsTextFrame::UnionAdditionalOverflow(nsP
       params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
       for (const LineDecoration& dec : textDecs.mStrikes) {
         accumulateDecorationRect(dec, &Metrics::strikeoutSize,
                                  &Metrics::strikeoutOffset);
       }
 
       aVisualOverflowRect->UnionRect(
         *aVisualOverflowRect,
-        verticalRun ? nsRect(topOrLeft, 0, bottomOrRight - topOrLeft, measure)
+        verticalDec ? nsRect(topOrLeft, 0, bottomOrRight - topOrLeft, measure)
                     : nsRect(0, topOrLeft, measure, bottomOrRight - topOrLeft));
     }
 
     aVisualOverflowRect->UnionRect(*aVisualOverflowRect,
-                                   UpdateTextEmphasis(wm, aProvider));
+                                   UpdateTextEmphasis(parentWM, aProvider));
   }
 
   // Text-shadow overflows
   nsRect shadowRect =
     nsLayoutUtils::GetTextShadowRectsUnion(*aVisualOverflowRect, this);
   aVisualOverflowRect->UnionRect(*aVisualOverflowRect, shadowRect);
 
   // When this frame is not selected, the text-decoration area must be in
@@ -6625,44 +6634,52 @@ nsTextFrame::DrawTextRun(Range aRange, c
 void
 nsTextFrame::DrawTextRunAndDecorations(Range aRange,
                                        const gfxPoint& aTextBaselinePt,
                                        const DrawTextParams& aParams,
                                        const TextDecorations& aDecorations)
 {
     const gfxFloat app =
       aParams.textStyle->PresContext()->AppUnitsPerDevPixel();
+    // Writing mode of parent frame is used because the text frame may
+    // be orthogonal to its parent when text-combine-upright is used or
+    // its parent has "display: contents", and in those cases, we want
+    // to draw the decoration lines according to parents' direction
+    // rather than ours.
+    const WritingMode wm = GetParent()->GetWritingMode();
+    bool verticalDec = wm.IsVertical();
     bool verticalRun = mTextRun->IsVertical();
-    bool useVerticalMetrics = verticalRun && mTextRun->UseCenterBaseline();
+    // If the text run and the decoration is orthogonal, we choose the
+    // metrics for decoration so that decoration line won't be broken.
+    bool useVerticalMetrics = verticalDec != verticalRun
+      ? verticalDec : verticalRun && mTextRun->UseCenterBaseline();
 
     // XXX aFramePt is in AppUnits, shouldn't it be nsFloatPoint?
     nscoord x = NSToCoordRound(aParams.framePt.x);
     nscoord y = NSToCoordRound(aParams.framePt.y);
 
     // 'measure' here is textrun-relative, so for a horizontal run it's the
     // width, while for a vertical run it's the height of the decoration
     const nsSize frameSize = GetSize();
-    nscoord measure = verticalRun ? frameSize.height : frameSize.width;
-
-    if (verticalRun) {
+    nscoord measure = verticalDec ? frameSize.height : frameSize.width;
+
+    if (verticalDec) {
       aParams.clipEdges->Intersect(&y, &measure);
     } else {
       aParams.clipEdges->Intersect(&x, &measure);
     }
 
     // decSize is a textrun-relative size, so its 'width' field is actually
     // the run-relative measure, and 'height' will be the line thickness
-    gfxFloat ascent = gfxFloat(mAscent) / app;
-
+    gfxFloat ascent = gfxFloat(GetLogicalBaseline(wm)) / app;
     // The starting edge of the frame in block direction
-    gfxFloat frameBStart = verticalRun ? aParams.framePt.x : aParams.framePt.y;
-
-    // In vertical-rl mode, block coordinates are measured from the right,
-    // so we need to adjust here.
-    const WritingMode wm = GetWritingMode();
+    gfxFloat frameBStart = verticalDec ? aParams.framePt.x : aParams.framePt.y;
+
+    // In vertical-rl mode, block coordinates are measured from the
+    // right, so we need to adjust here.
     if (wm.IsVerticalRL()) {
       frameBStart += frameSize.width;
       ascent = -ascent;
     }
 
     nscoord inflationMinFontSize =
       nsLayoutUtils::InflationMinFontSizeFor(this);
 
@@ -6673,20 +6690,35 @@ nsTextFrame::DrawTextRunAndDecorations(R
     PaintDecorationLineParams params;
     params.context = aParams.context;
     params.dirtyRect = aParams.dirtyRect;
     params.overrideColor = aParams.decorationOverrideColor;
     params.callbacks = aParams.callbacks;
     // pt is the physical point where the decoration is to be drawn,
     // relative to the frame; one of its coordinates will be updated below.
     params.pt = Point(x / app, y / app);
-    Float& bCoord = verticalRun ? params.pt.x : params.pt.y;
+    Float& bCoord = verticalDec ? params.pt.x : params.pt.y;
     params.lineSize = Size(measure / app, 0);
     params.ascent = ascent;
-    params.vertical = verticalRun;
+    params.vertical = verticalDec;
+
+    // The matrix of the context may have been altered for text-combine-
+    // upright. However, we want to draw decoration lines unscaled, thus
+    // we need to revert the scaling here.
+    gfxContextMatrixAutoSaveRestore scaledRestorer;
+    if (StyleContext()->IsTextCombined()) {
+      float scaleFactor = GetTextCombineScaleFactor(this);
+      if (scaleFactor != 1.0f) {
+        scaledRestorer.SetContext(aParams.context);
+        gfxMatrix unscaled = aParams.context->CurrentMatrix();
+        gfxPoint pt(x / app, y / app);
+        unscaled.Translate(pt).Scale(1.0f / scaleFactor, 1.0f).Translate(-pt);
+        aParams.context->SetMatrix(unscaled);
+      }
+    }
 
     typedef gfxFont::Metrics Metrics;
     auto paintDecorationLine = [&](const LineDecoration& dec,
                                    gfxFloat Metrics::* lineSize,
                                    gfxFloat Metrics::* lineOffset) {
       if (dec.mStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
         return;
       }
@@ -6713,19 +6745,27 @@ nsTextFrame::DrawTextRunAndDecorations(R
                           &Metrics::underlineOffset);
     }
     // Overlines
     params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
     for (const LineDecoration& dec : Reversed(aDecorations.mOverlines)) {
       paintDecorationLine(dec, &Metrics::underlineSize, &Metrics::maxAscent);
     }
 
-    // CSS 2.1 mandates that text be painted after over/underlines, and *then*
-    // line-throughs
-    DrawTextRun(aRange, aTextBaselinePt, aParams);
+    {
+      gfxContextMatrixAutoSaveRestore unscaledRestorer;
+      if (scaledRestorer.HasMatrix()) {
+        unscaledRestorer.SetContext(aParams.context);
+        aParams.context->SetMatrix(scaledRestorer.Matrix());
+      }
+
+      // CSS 2.1 mandates that text be painted after over/underlines,
+      // and *then* line-throughs
+      DrawTextRun(aRange, aTextBaselinePt, aParams);
+    }
 
     // Emphasis marks
     DrawEmphasisMarks(aParams.context, wm, aTextBaselinePt, aRange,
                       aParams.decorationOverrideColor, aParams.provider);
 
     // Line-throughs
     params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
     for (const LineDecoration& dec : Reversed(aDecorations.mStrikes)) {
@@ -9488,19 +9528,33 @@ nsTextFrame::HasSignificantTerminalNewli
 
 bool
 nsTextFrame::IsAtEndOfLine() const
 {
   return (GetStateBits() & TEXT_END_OF_LINE) != 0;
 }
 
 nscoord
-nsTextFrame::GetLogicalBaseline(WritingMode aWritingMode ) const
-{
-  return mAscent;
+nsTextFrame::GetLogicalBaseline(WritingMode aWM) const
+{
+  if (!aWM.IsOrthogonalTo(GetWritingMode())) {
+    return mAscent;
+  }
+
+  // When the text frame has a writing mode orthogonal to the desired
+  // writing mode, return a baseline coincides its parent frame.
+  nsIFrame* parent = GetParent();
+  nsPoint position = GetNormalPosition();
+  nscoord parentAscent = parent->GetLogicalBaseline(aWM);
+  if (aWM.IsVerticalRL()) {
+    nscoord parentDescent = parent->GetSize().width - parentAscent;
+    nscoord descent = parentDescent - position.x;
+    return GetSize().width - descent;
+  }
+  return parentAscent - (aWM.IsVertical() ? position.x : position.y);
 }
 
 bool
 nsTextFrame::HasAnyNoncollapsedCharacters()
 {
   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
   int32_t offset = GetContentOffset(),
           offsetEnd = GetContentEnd();
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -187,17 +187,17 @@ public:
                                                  bool    inHint,
                                                  int32_t* outFrameContentOffset,
                                                  nsIFrame** outChildFrame) override;
   
   virtual bool IsVisibleInSelection(nsISelection* aSelection) override;
   
   virtual bool IsEmpty() override;
   virtual bool IsSelfEmpty() override { return IsEmpty(); }
-  virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
+  nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const final;
   
   virtual bool HasSignificantTerminalNewline() const override;
 
   /**
    * Returns true if this text frame is logically adjacent to the end of the
    * line.
    */
   bool IsAtEndOfLine() const;