Title: [197987] trunk
Revision
197987
Author
[email protected]
Date
2016-03-10 20:03:32 -0800 (Thu, 10 Mar 2016)

Log Message

Simple line layout: Add text-align: justify support.
https://bugs.webkit.org/show_bug.cgi?id=155006

Reviewed by Antti Koivisto.

Source/WebCore:

This patch enables text-align: justify; for simple line layout (only latin text for now).
It speeds up/reduced memory consumption for justified text.

PerformanceTests/Layout/line-layout-simple.html
before text-align: justify support -> mean: 86.20513022288704 runs/s
after -> mean: 96.73972475626084 runs/s

Covered by existing text-align: justify tests.

* rendering/SimpleLineLayout.cpp:
(WebCore::SimpleLineLayout::canUseForFontAndText):
(WebCore::SimpleLineLayout::LineState::expansionOpportunityCount):
(WebCore::SimpleLineLayout::LineState::expansionOportunity):
(WebCore::SimpleLineLayout::expansionBehavior):
(WebCore::SimpleLineLayout::justifyRuns):
(WebCore::SimpleLineLayout::closeLineEndingAndAdjustRuns):
(WebCore::SimpleLineLayout::createTextRuns):
(WebCore::SimpleLineLayout::printReason):
(WebCore::SimpleLineLayout::canUseForStyle): Deleted.
* rendering/SimpleLineLayout.h:
* rendering/SimpleLineLayoutFunctions.cpp:
(WebCore::SimpleLineLayout::paintFlow):
* rendering/SimpleLineLayoutResolver.h:
(WebCore::SimpleLineLayout::RunResolver::Run::expansion):
(WebCore::SimpleLineLayout::RunResolver::Run::expansionBehavior):

LayoutTests:

Rebaseline based on simple line layout output.

* fast/text/whitespace/023-expected.txt:
* platform/mac/css2.1/t0905-c5525-fltwidth-00-c-g-expected.txt:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (197986 => 197987)


--- trunk/LayoutTests/ChangeLog	2016-03-11 03:57:52 UTC (rev 197986)
+++ trunk/LayoutTests/ChangeLog	2016-03-11 04:03:32 UTC (rev 197987)
@@ -1,3 +1,15 @@
+2016-03-10  Zalan Bujtas  <[email protected]>
+
+        Simple line layout: Add text-align: justify support.
+        https://bugs.webkit.org/show_bug.cgi?id=155006
+
+        Reviewed by Antti Koivisto.
+
+        Rebaseline based on simple line layout output.
+
+        * fast/text/whitespace/023-expected.txt:
+        * platform/mac/css2.1/t0905-c5525-fltwidth-00-c-g-expected.txt:
+
 2016-03-10  Nan Wang  <[email protected]>
 
         AX: Force allow user zoom

Modified: trunk/LayoutTests/fast/text/whitespace/023-expected.txt (197986 => 197987)


--- trunk/LayoutTests/fast/text/whitespace/023-expected.txt	2016-03-11 03:57:52 UTC (rev 197986)
+++ trunk/LayoutTests/fast/text/whitespace/023-expected.txt	2016-03-11 04:03:32 UTC (rev 197987)
@@ -6,6 +6,4 @@
       RenderBlock {PRE} at (0,0) size 304x34 [border: (2px solid #008000)]
         RenderText {#text} at (2,2) size 94x30
           text run at (2,2) width 86: "This should"
-          text run at (87,2) width 1: " "
           text run at (2,17) width 94: "not justify."
-          text run at (95,17) width 1: " "

Modified: trunk/LayoutTests/platform/mac/css2.1/t0905-c5525-fltwidth-00-c-g-expected.txt (197986 => 197987)


--- trunk/LayoutTests/platform/mac/css2.1/t0905-c5525-fltwidth-00-c-g-expected.txt	2016-03-11 03:57:52 UTC (rev 197986)
+++ trunk/LayoutTests/platform/mac/css2.1/t0905-c5525-fltwidth-00-c-g-expected.txt	2016-03-11 04:03:32 UTC (rev 197987)
@@ -46,7 +46,7 @@
           text run at (233,306) width 152: "ignore this float text"
           text run at (0,324) width 257: "ignore this float text ignore this float"
       RenderBlock {DIV} at (0,154) size 769x468 [color=#C0C0C0]
-        RenderText {#text} at (384,0) size 770x468
+        RenderText {#text} at (384,0) size 769x468
           text run at (384,0) width 385: "this is some dummy text this is some dummy text this is"
           text run at (384,18) width 98: "some dummy "
           text run at (481,18) width 288: "text this is some dummy text this is some"
@@ -88,7 +88,7 @@
           text run at (569,360) width 200: "some dummy text this is some"
           text run at (0,378) width 244: "dummy text this is some dummy text "
           text run at (243,378) width 460: "this is some dummy text this is some dummy text this is some dummy "
-          text run at (702,378) width 68: "text this is"
+          text run at (702,378) width 67: "text this is"
           text run at (0,396) width 365: "some dummy text this is some dummy text this is some "
           text run at (364,396) width 405: "dummy text this is some dummy text this is some dummy text"
           text run at (0,414) width 47: "this is "

Modified: trunk/Source/WebCore/ChangeLog (197986 => 197987)


--- trunk/Source/WebCore/ChangeLog	2016-03-11 03:57:52 UTC (rev 197986)
+++ trunk/Source/WebCore/ChangeLog	2016-03-11 04:03:32 UTC (rev 197987)
@@ -1,3 +1,36 @@
+2016-03-10  Zalan Bujtas  <[email protected]>
+
+        Simple line layout: Add text-align: justify support.
+        https://bugs.webkit.org/show_bug.cgi?id=155006
+
+        Reviewed by Antti Koivisto.
+
+        This patch enables text-align: justify; for simple line layout (only latin text for now).
+        It speeds up/reduced memory consumption for justified text.
+
+        PerformanceTests/Layout/line-layout-simple.html
+        before text-align: justify support -> mean: 86.20513022288704 runs/s
+        after -> mean: 96.73972475626084 runs/s
+
+        Covered by existing text-align: justify tests.
+
+        * rendering/SimpleLineLayout.cpp:
+        (WebCore::SimpleLineLayout::canUseForFontAndText):
+        (WebCore::SimpleLineLayout::LineState::expansionOpportunityCount):
+        (WebCore::SimpleLineLayout::LineState::expansionOportunity):
+        (WebCore::SimpleLineLayout::expansionBehavior):
+        (WebCore::SimpleLineLayout::justifyRuns):
+        (WebCore::SimpleLineLayout::closeLineEndingAndAdjustRuns):
+        (WebCore::SimpleLineLayout::createTextRuns):
+        (WebCore::SimpleLineLayout::printReason):
+        (WebCore::SimpleLineLayout::canUseForStyle): Deleted.
+        * rendering/SimpleLineLayout.h:
+        * rendering/SimpleLineLayoutFunctions.cpp:
+        (WebCore::SimpleLineLayout::paintFlow):
+        * rendering/SimpleLineLayoutResolver.h:
+        (WebCore::SimpleLineLayout::RunResolver::Run::expansion):
+        (WebCore::SimpleLineLayout::RunResolver::Run::expansionBehavior):
+
 2016-03-10  Nan Wang  <[email protected]>
 
         AX: Force allow user zoom

Modified: trunk/Source/WebCore/rendering/SimpleLineLayout.cpp (197986 => 197987)


--- trunk/Source/WebCore/rendering/SimpleLineLayout.cpp	2016-03-11 03:57:52 UTC (rev 197986)
+++ trunk/Source/WebCore/rendering/SimpleLineLayout.cpp	2016-03-11 04:03:32 UTC (rev 197987)
@@ -74,7 +74,7 @@
     FlowHasNonSupportedChild              = 1LLU  << 9,
     FlowHasUnsupportedFloat               = 1LLU  << 10,
     FlowHasUnsupportedUnderlineDecoration = 1LLU  << 11,
-    FlowIsJustifyAligned                  = 1LLU  << 12,
+    FlowHasJustifiedNonLatinText          = 1LLU  << 12,
     FlowHasOverflowVisible                = 1LLU  << 13,
     FlowIsNotLTR                          = 1LLU  << 14,
     FlowHasLineBoxContainProperty         = 1LLU  << 15,
@@ -186,6 +186,8 @@
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasSVGFont, reasons, includeReasons);
 
     for (const auto& textRenderer : childrenOfType<RenderText>(flow)) {
+        if (style.textAlign() == JUSTIFY && !textRenderer.originalText().containsOnlyLatin1())
+            SET_REASON_AND_RETURN_IF_NEEDED(FlowHasJustifiedNonLatinText, reasons, includeReasons);
         if (textRenderer.isCombineText())
             SET_REASON_AND_RETURN_IF_NEEDED(FlowTextIsCombineText, reasons, includeReasons);
         if (textRenderer.isCounter())
@@ -213,8 +215,6 @@
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextOverflow, reasons, includeReasons);
     if ((style.textDecorationsInEffect() & TextDecorationUnderline) && style.textUnderlinePosition() == TextUnderlinePositionUnder)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasUnsupportedUnderlineDecoration, reasons, includeReasons);
-    if (style.textAlign() == JUSTIFY)
-        SET_REASON_AND_RETURN_IF_NEEDED(FlowIsJustifyAligned, reasons, includeReasons);
     // Non-visible overflow should be pretty easy to support.
     if (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasOverflowVisible, reasons, includeReasons);
@@ -409,6 +409,28 @@
     bool fits(float extra) const { return m_availableWidth >= m_runsWidth + extra; }
     bool firstCharacterFits() const { return m_firstCharacterFits; }
     float width() const { return m_runsWidth; }
+    std::pair<unsigned, bool> expansionOpportunityCount(unsigned from, unsigned to) const
+    {
+        // linebreak runs are special.
+        if (from == to)
+            return std::make_pair(0, false);
+        unsigned expansionOpportunityCount = 0;
+        auto previousFragmentType = TextFragmentIterator::TextFragment::ContentEnd;
+        for (const auto& fragment : m_fragments) {
+            if (fragment.end() <= from)
+                continue;
+            auto currentFragmentType = fragment.type();
+            auto expansionOpportunity = this->expansionOpportunity(currentFragmentType, previousFragmentType);
+            if (expansionOpportunity)
+                ++expansionOpportunityCount;
+            previousFragmentType = currentFragmentType;
+            if (fragment.end() >= to)
+                return std::make_pair(expansionOpportunityCount, expansionOpportunity);
+        }
+        ASSERT_NOT_REACHED();
+        return std::make_pair(expansionOpportunityCount, false);
+    }
+
     bool isEmpty() const
     {
         if (!m_fragments.size())
@@ -508,6 +530,12 @@
     }
 
 private:
+    bool expansionOpportunity(TextFragmentIterator::TextFragment::Type currentFragmentType, TextFragmentIterator::TextFragment::Type previousFragmentType) const
+    {
+        return (currentFragmentType == TextFragmentIterator::TextFragment::Whitespace
+            || (currentFragmentType == TextFragmentIterator::TextFragment::NonWhitespace && previousFragmentType == TextFragmentIterator::TextFragment::NonWhitespace));
+    }
+
     float m_availableWidth { 0 };
     float m_logicalLeftOffset { 0 };
     TextFragmentIterator::TextFragment m_overflowedFragment;
@@ -723,20 +751,75 @@
     return (fragment.type() == TextFragmentIterator::TextFragment::ContentEnd && line.overflowedFragment().isEmpty()) || line.overflowedFragment().type() == TextFragmentIterator::TextFragment::ContentEnd;
 }
 
-static void closeLineEndingAndAdjustRuns(LineState& line, Layout::RunVector& runs, unsigned previousRunCount, unsigned& lineCount, const TextFragmentIterator& textFragmentIterator)
+static ExpansionBehavior expansionBehavior(bool isAfterExpansion, bool lastRunOnLine)
 {
-    if (previousRunCount == runs.size())
+    ExpansionBehavior expansionBehavior;
+    expansionBehavior = isAfterExpansion ? ForbidLeadingExpansion : AllowLeadingExpansion;
+    expansionBehavior |= lastRunOnLine ? ForbidTrailingExpansion : AllowTrailingExpansion;
+    return expansionBehavior;
+}
+
+static void justifyRuns(const LineState& line, Layout::RunVector& runs, Optional<unsigned> lastRunIndexOfPreviousLine)
+{
+    ASSERT(runs.size());
+    auto widthToDistribute = line.availableWidth() - line.width();
+    if (widthToDistribute <= 0)
         return;
-    ASSERT(runs.size());
+
+    auto firstRunIndex = lastRunIndexOfPreviousLine ? lastRunIndexOfPreviousLine.value() + 1 : 0;
+    auto lastRunIndex = runs.size() - 1;
+    ASSERT(firstRunIndex <= lastRunIndex);
+    Vector<std::pair<unsigned, ExpansionBehavior>> expansionOpportunityList;
+    unsigned expansionOpportunityCountOnThisLine = 0;
+    auto isAfterExpansion = true;
+    for (auto i = firstRunIndex; i <= lastRunIndex; ++i) {
+        const auto& run = runs.at(i);
+        unsigned opportunityCountInRun = 0;
+        std::tie(opportunityCountInRun, isAfterExpansion) = line.expansionOpportunityCount(run.start, run.end);
+        expansionOpportunityList.append(std::make_pair(opportunityCountInRun, expansionBehavior(isAfterExpansion, i == lastRunIndex)));
+        expansionOpportunityCountOnThisLine += opportunityCountInRun;
+    }
+    if (!expansionOpportunityCountOnThisLine)
+        return;
+
+    ASSERT(expansionOpportunityList.size() == lastRunIndex - firstRunIndex + 1);
+    auto expansion = widthToDistribute / expansionOpportunityCountOnThisLine;
+    float accumulatedExpansion = 0;
+    for (auto i = firstRunIndex; i <= lastRunIndex; ++i) {
+        auto& run = runs.at(i);
+        unsigned opportunityCountInRun;
+        std::tie(opportunityCountInRun, run.expansionBehavior) = expansionOpportunityList.at(i - firstRunIndex);
+        run.expansion = opportunityCountInRun * expansion;
+        run.logicalLeft += accumulatedExpansion;
+        run.logicalRight += (accumulatedExpansion + run.expansion);
+        accumulatedExpansion += run.expansion;
+    }
+}
+
+static void closeLineEndingAndAdjustRuns(LineState& line, Layout::RunVector& runs, Optional<unsigned> lastRunIndexOfPreviousLine, unsigned& lineCount,
+    const TextFragmentIterator& textFragmentIterator, bool lastLine)
+{
+    if (!runs.size() || (lastRunIndexOfPreviousLine && runs.size() - 1 == lastRunIndexOfPreviousLine.value()))
+        return;
     removeTrailingWhitespace(line, runs, textFragmentIterator);
     if (!runs.size())
         return;
     // Adjust runs' position by taking line's alignment into account.
-    if (float lineLogicalLeft = computeLineLeft(textFragmentIterator.style().textAlign, line.availableWidth(), line.width(), line.logicalLeftOffset())) {
-        for (unsigned i = previousRunCount; i < runs.size(); ++i) {
-            runs[i].logicalLeft += lineLogicalLeft;
-            runs[i].logicalRight += lineLogicalLeft;
-        }
+    const auto& style = textFragmentIterator.style();
+    auto textAlign = style.textAlign;
+    // Fallback to LEFT alignment both for non-collapsable content and for the last line.
+    if (textAlign == JUSTIFY && (!style.collapseWhitespace || lastLine))
+        textAlign = LEFT;
+
+    auto lineLogicalLeft = line.logicalLeftOffset();
+    if (textAlign == JUSTIFY)
+        justifyRuns(line, runs, lastRunIndexOfPreviousLine);
+    else
+        lineLogicalLeft = computeLineLeft(textAlign, line.availableWidth(), line.width(), line.logicalLeftOffset());
+    auto firstRunIndex = lastRunIndexOfPreviousLine ? lastRunIndexOfPreviousLine.value() + 1 : 0;
+    for (auto i = firstRunIndex; i < runs.size(); ++i) {
+        runs[i].logicalLeft += lineLogicalLeft;
+        runs[i].logicalRight += lineLogicalLeft;
     }
     runs.last().isEndOfLine = true;
     ++lineCount;
@@ -749,14 +832,16 @@
     LineState line;
     bool isEndOfContent = false;
     TextFragmentIterator textFragmentIterator = TextFragmentIterator(flow);
+    Optional<unsigned> lastRunIndexOfPreviousLine;
     do {
         flow.setLogicalHeight(lineHeight * lineCount + borderAndPaddingBefore);
         LineState previousLine = line;
-        unsigned previousRunCount = runs.size();
         line = LineState();
         updateLineConstrains(flow, line, !lineCount);
         isEndOfContent = createLineRuns(line, previousLine, runs, textFragmentIterator);
-        closeLineEndingAndAdjustRuns(line, runs, previousRunCount, lineCount, textFragmentIterator);
+        closeLineEndingAndAdjustRuns(line, runs, lastRunIndexOfPreviousLine, lineCount, textFragmentIterator, isEndOfContent);
+        if (runs.size())
+            lastRunIndexOfPreviousLine = runs.size() - 1;
     } while (!isEndOfContent);
 }
 
@@ -829,8 +914,8 @@
     case FlowHasUnsupportedUnderlineDecoration:
         stream << "text-underline-position: under";
         break;
-    case FlowIsJustifyAligned:
-        stream << "text-align: justify";
+    case FlowHasJustifiedNonLatinText:
+        stream << "text-align: justify with non-latin text";
         break;
     case FlowHasOverflowVisible:
         stream << "overflow: visible";

Modified: trunk/Source/WebCore/rendering/SimpleLineLayout.h (197986 => 197987)


--- trunk/Source/WebCore/rendering/SimpleLineLayout.h	2016-03-11 03:57:52 UTC (rev 197986)
+++ trunk/Source/WebCore/rendering/SimpleLineLayout.h	2016-03-11 04:03:32 UTC (rev 197987)
@@ -26,6 +26,7 @@
 #ifndef SimpleLineLayout_h
 #define SimpleLineLayout_h
 
+#include "TextFlags.h"
 #include <wtf/Vector.h>
 #include <wtf/text/WTFString.h>
 
@@ -59,6 +60,9 @@
     unsigned isEndOfLine : 1;
     float logicalLeft;
     float logicalRight;
+    // TODO: Move these optional items out of SimpleLineLayout::Run to a supplementary structure.
+    float expansion { 0 };
+    ExpansionBehavior expansionBehavior { ForbidLeadingExpansion | ForbidTrailingExpansion };
 };
 
 class Layout {

Modified: trunk/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp (197986 => 197987)


--- trunk/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp	2016-03-11 03:57:52 UTC (rev 197986)
+++ trunk/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp	2016-03-11 04:03:32 UTC (rev 197987)
@@ -106,10 +106,9 @@
         if (paintRect.y() > visualOverflowRect.maxY() || paintRect.maxY() < visualOverflowRect.y())
             continue;
 
-        TextRun textRun(run.text());
-        textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
         // x position indicates the line offset from the rootbox. It's always 0 in case of simple line layout.
-        textRun.setXPos(0);
+        TextRun textRun(run.text(), 0, run.expansion(), run.expansionBehavior());
+        textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
         FloatPoint textOrigin = FloatPoint(rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor));
         textPainter.paintText(textRun, textRun.length(), rect, textOrigin);
         if (textDecorationPainter) {

Modified: trunk/Source/WebCore/rendering/SimpleLineLayoutResolver.h (197986 => 197987)


--- trunk/Source/WebCore/rendering/SimpleLineLayoutResolver.h	2016-03-11 03:57:52 UTC (rev 197986)
+++ trunk/Source/WebCore/rendering/SimpleLineLayoutResolver.h	2016-03-11 04:03:32 UTC (rev 197987)
@@ -62,6 +62,8 @@
         unsigned end() const;
 
         FloatRect rect() const;
+        float expansion() const;
+        ExpansionBehavior expansionBehavior() const;
         int baselinePosition() const;
         StringView text() const;
         bool isEndOfLine() const;
@@ -170,6 +172,16 @@
     return m_iterator.simpleRun().end;
 }
 
+inline float RunResolver::Run::expansion() const
+{
+    return m_iterator.simpleRun().expansion;
+}
+
+inline ExpansionBehavior RunResolver::Run::expansionBehavior() const
+{
+    return m_iterator.simpleRun().expansionBehavior;
+}
+
 inline int RunResolver::Run::baselinePosition() const
 {
     return roundToInt(computeBaselinePosition());
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to