Bug 1097499 part 5 - Layout text combine upright. r=jfkthame
authorXidorn Quan <quanxunzhen@gmail.com>
Fri, 22 Apr 2016 09:18:41 +1000
changeset 318175 6373a28c2d743636969afe25a215ad66b1e1751c
parent 318174 400f5b753ae00255f18dd2d3f3ea88ca211302d9
child 318176 c582f2934be9efa80e5e0ec8b7ff482277fdf97d
push id9480
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 17:12:58 +0000
treeherdermozilla-aurora@0d6a91c76a9e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1097499
milestone48.0a1
Bug 1097499 part 5 - Layout text combine upright. r=jfkthame MozReview-Commit-ID: 2HnXJ8bEvJH
layout/generic/nsTextFrame.cpp
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -62,16 +62,17 @@
 #include "nsLineBreaker.h"
 #include "nsIWordBreaker.h"
 #include "nsGenericDOMDataNode.h"
 #include "nsIFrameInlines.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 
 #include <algorithm>
+#include <limits>
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 #include "nsAutoPtr.h"
 
 #include "nsPrintfCString.h"
 
 #include "gfxContext.h"
@@ -4704,27 +4705,37 @@ nsDisplayText::ComputeInvalidationRegion
       mVisIEndEdge != geometry->mVisIEndEdge ||
       !oldRect.IsEqualInterior(newRect) ||
       !geometry->mBorderRect.IsEqualInterior(GetBorderRect()) ||
       mOpacity != geometry->mOpacity) {
     aInvalidRegion->Or(oldRect, newRect);
   }
 }
 
+NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(TextCombineScaleFactorProperty, float)
+
+static float
+GetTextCombineScaleFactor(nsTextFrame* aFrame)
+{
+  float factor = aFrame->Properties().Get(TextCombineScaleFactorProperty());
+  return factor ? factor : 1.0f;
+}
+
 void
 nsDisplayText::Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) {
   PROFILER_LABEL("nsDisplayText", "Paint",
     js::ProfileEntry::Category::GRAPHICS);
 
   // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
   // antialiased pixels beyond the measured text extents.
   // This is temporary until we do this in the actual calculation of text extents.
-  LayoutDeviceRect extraVisible = LayoutDeviceRect::FromAppUnits(
-    mVisibleRect, mFrame->PresContext()->AppUnitsPerDevPixel());
+  auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
+  LayoutDeviceRect extraVisible =
+    LayoutDeviceRect::FromAppUnits(mVisibleRect, A2D);
   extraVisible.Inflate(1);
   nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
 
   DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
                                                     mDisableSubpixelAA);
   gfxContext* ctx = aCtx->ThebesContext();
   gfxContextAutoSaveRestore save(ctx);
 
@@ -4738,16 +4749,28 @@ nsDisplayText::Paint(nsDisplayListBuilde
     ctx->Rectangle(pixelVisible);
     ctx->Clip();
   }
 
   NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
   NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
 
   nsPoint framePt = ToReferenceFrame();
+  if (f->StyleContext()->IsTextCombined()) {
+    float scaleFactor = GetTextCombineScaleFactor(f);
+    if (scaleFactor != 1.0f) {
+      // Setup matrix to compress text for text-combine-upright if
+      // necessary. This is done here because we want selection be
+      // compressed at the same time as text.
+      gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
+      gfxMatrix mat = ctx->CurrentMatrix()
+        .Translate(pt).Scale(scaleFactor, 1.0).Translate(-pt);
+      ctx->SetMatrix(mat);
+    }
+  }
   nsTextFrame::PaintTextParams params(aCtx->ThebesContext());
   params.framePt = gfxPoint(framePt.x, framePt.y);
 
   params.dirtyRect = extraVisible;
   nsTextFrame::DrawPathCallbacks callbacks;
   if (aBuilder->IsForGenerateGlyphPath()) {
     params.callbacks = &callbacks;
   }
@@ -6801,16 +6824,19 @@ nsTextFrame::GetCharacterOffsetAtFramePo
     return offsets;
 
   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
   // Trim leading but not trailing whitespace if possible
   provider.InitializeForDisplay(false);
   gfxFloat width = mTextRun->IsVertical()
     ? (mTextRun->IsInlineReversed() ? mRect.height - aPoint.y : aPoint.y)
     : (mTextRun->IsInlineReversed() ? mRect.width - aPoint.x : aPoint.x);
+  if (StyleContext()->IsTextCombined()) {
+    width /= GetTextCombineScaleFactor(this);
+  }
   gfxFloat fitWidth;
   Range skippedRange = ComputeTransformedRange(provider);
 
   uint32_t charsFit = CountCharsFit(mTextRun, skippedRange,
                                     width, &provider, &fitWidth);
 
   int32_t selectedOffset;
   if (charsFit < skippedRange.Length()) {
@@ -7062,16 +7088,19 @@ nsTextFrame::GetPointFromOffset(int32_t 
       outPoint->y = iSize;
     }
   } else {
     if (mTextRun->IsInlineReversed()) {
       outPoint->x = mRect.width - iSize;
     } else {
       outPoint->x = iSize;
     }
+    if (StyleContext()->IsTextCombined()) {
+      outPoint->x *= GetTextCombineScaleFactor(this);
+    }
   }
 
   return NS_OK;
 }
 
 nsresult
 nsTextFrame::GetChildFrameContainingOffset(int32_t   aContentOffset,
                                            bool      aHint,
@@ -7744,16 +7773,26 @@ nsTextFrame::AddInlineMinISizeForFlow(ns
 
   bool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant();
   bool preformatNewlines = textStyle->NewlineIsSignificant(this);
   bool preformatTabs = textStyle->WhiteSpaceIsSignificant();
   gfxFloat tabWidth = -1;
   uint32_t start =
     FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun);
 
+  // text-combine-upright frame is constantly 1em on inline-axis.
+  if (StyleContext()->IsTextCombined()) {
+    if (textRun->CanBreakLineBefore(start)) {
+      aData->OptionallyBreak();
+    }
+    aData->mCurrentLine += provider.GetFontMetrics()->EmHeight();
+    aData->mTrailingWhitespace = 0;
+    return;
+  }
+
   AutoTArray<bool,BIG_TEXT_NODE_SIZE> hyphBuffer;
   bool *hyphBreakBefore = nullptr;
   if (hyphenating) {
     hyphBreakBefore = hyphBuffer.AppendElements(flowEndInTextRun - start,
                                                 fallible);
     if (hyphBreakBefore) {
       provider.GetHyphenationBreaks(Range(start, flowEndInTextRun),
                                     hyphBreakBefore);
@@ -7898,16 +7937,23 @@ nsTextFrame::AddInlinePrefISizeForFlow(n
   // Pass null for the line container. This will disable tab spacing, but that's
   // OK since we can't really handle tabs for intrinsic sizing anyway.
   
   const nsStyleText* textStyle = StyleText();
   const nsTextFragment* frag = mContent->GetText();
   PropertyProvider provider(textRun, textStyle, frag, this,
                             iter, INT32_MAX, nullptr, 0, aTextRunType);
 
+  // text-combine-upright frame is constantly 1em on inline-axis.
+  if (StyleContext()->IsTextCombined()) {
+    aData->mCurrentLine += provider.GetFontMetrics()->EmHeight();
+    aData->mTrailingWhitespace = 0;
+    return;
+  }
+
   bool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant();
   bool preformatNewlines = textStyle->NewlineIsSignificant(this);
   bool preformatTabs = textStyle->TabIsSignificant();
   gfxFloat tabWidth = -1;
   uint32_t start =
     FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun);
 
   // XXX Should we consider hyphenation here?
@@ -8631,16 +8677,21 @@ nsTextFrame::ReflowText(nsLineLayout& aL
     gfxSkipCharsIterator iter(provider.GetStart());
     iter.SetOriginalOffset(offset + limitLength);
     transformedLength = iter.GetSkippedOffset() - transformedOffset;
   }
   uint32_t transformedLastBreak = 0;
   bool usedHyphenation;
   gfxFloat trimmedWidth = 0;
   gfxFloat availWidth = aAvailableWidth;
+  if (StyleContext()->IsTextCombined()) {
+    // If text-combine-upright is 'all', we would compress whatever long
+    // text into ~1em width, so there is no limited on the avail width.
+    availWidth = std::numeric_limits<gfxFloat>::infinity();
+  }
   bool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant() ||
                                    (GetStateBits() & TEXT_IS_IN_TOKEN_MATHML);
   gfxBreakPriority breakPriority = aLineLayout.LastOptionalBreakPriority();
   gfxTextRun::SuppressBreak suppressBreak = gfxTextRun::eNoSuppressBreak;
   bool shouldSuppressLineBreak = ShouldSuppressLineBreak();
   if (shouldSuppressLineBreak) {
     suppressBreak = gfxTextRun::eSuppressAllBreaks;
   } else if (!aLineLayout.LineIsBreakable()) {
@@ -8738,17 +8789,17 @@ nsTextFrame::ReflowText(nsLineLayout& aL
         textMetrics.mBoundingBox.MoveBy(gfxPoint(trimmedWidth, 0));
       }
     }
   }
 
   if (!brokeText && lastBreak >= 0) {
     // Since everything fit and no break was forced,
     // record the last break opportunity
-    NS_ASSERTION(textMetrics.mAdvanceWidth - trimmableWidth <= aAvailableWidth,
+    NS_ASSERTION(textMetrics.mAdvanceWidth - trimmableWidth <= availWidth,
                  "If the text doesn't fit, and we have a break opportunity, why didn't MeasureText use it?");
     MOZ_ASSERT(lastBreak >= offset, "Strange break position");
     aLineLayout.NotifyOptionalBreakPosition(this, lastBreak - offset,
                                             true, breakPriority);
   }
 
   int32_t contentLength = offset + charsFit - GetContentOffset();
 
@@ -8786,21 +8837,41 @@ nsTextFrame::ReflowText(nsLineLayout& aL
     nscoord fontAscent =
       wm.IsLineInverted() ? fm->MaxDescent() : fm->MaxAscent();
     nscoord fontDescent =
       wm.IsLineInverted() ? fm->MaxAscent() : fm->MaxDescent();
     aMetrics.SetBlockStartAscent(std::max(NSToCoordCeil(textMetrics.mAscent), fontAscent));
     nscoord descent = std::max(NSToCoordCeil(textMetrics.mDescent), fontDescent);
     finalSize.BSize(wm) = aMetrics.BlockStartAscent() + descent;
   }
+  if (StyleContext()->IsTextCombined()) {
+    nsFontMetrics* fm = provider.GetFontMetrics();
+    gfxFloat width = finalSize.ISize(wm);
+    gfxFloat em = fm->EmHeight();
+    // Compress the characters in horizontal axis if necessary.
+    if (width <= em) {
+      Properties().Remove(TextCombineScaleFactorProperty());
+    } else {
+      Properties().Set(TextCombineScaleFactorProperty(), em / width);
+      finalSize.ISize(wm) = em;
+    }
+    // Make the characters be in an 1em square.
+    if (finalSize.BSize(wm) != em) {
+      aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() +
+                                   (em - finalSize.BSize(wm)) / 2);
+      finalSize.BSize(wm) = em;
+    }
+  }
   aMetrics.SetSize(wm, finalSize);
 
   NS_ASSERTION(aMetrics.BlockStartAscent() >= 0,
                "Negative ascent???");
-  NS_ASSERTION(aMetrics.BSize(aMetrics.GetWritingMode()) -
+  NS_ASSERTION((StyleContext()->IsTextCombined()
+                ? aMetrics.ISize(aMetrics.GetWritingMode())
+                : aMetrics.BSize(aMetrics.GetWritingMode())) -
                aMetrics.BlockStartAscent() >= 0,
                "Negative descent???");
 
   mAscent = aMetrics.BlockStartAscent();
 
   // Handle text that runs outside its normal bounds.
   nsRect boundingBox = RoundOut(textMetrics.mBoundingBox);
   if (mTextRun->IsVertical()) {