Title: [126763] trunk/Source/WebCore
Revision
126763
Author
[email protected]
Date
2012-08-27 08:31:56 -0700 (Mon, 27 Aug 2012)

Log Message

Improve line breaking performance for complex text
https://bugs.webkit.org/show_bug.cgi?id=83045

Patch by Ned Holbrook <[email protected]> on 2012-08-27
Reviewed by Darin Adler.

Currently RenderBlock::LineBreaker::nextLineBreak assumes that measuring individual words is as cheap
as free. This is not the case when dealing with complex text, which benefits from laying out as much
text as possible and by reusing that layout when feasible: by doing so this patch improves line
breaking by 25% as measured with a simple test app.

The bulk of this change is modifying ComplexTextController::advance, which previously required the
text offset to be strictly increasing and assumed unidirectional text; now it supports random seeking
in a naive fashion (by restarting to the beginning) and traverses glyphs in logical order. In the
latter case, the presence of any non-LTR runs triggers the population of a mapping from logical to
visual run indices. Finally, a new flag has been added which inhibits glyph advances from being split
across ligatures (thus causing spurious line breaks).

A ComplexTextController and its associated TextRun and Font are encapsulated in a TextLayout object,
which is instantiated as an opaque object via functions that are no-ops unless building for Mac. A
static member function (isNeeded) checks to see whether a TextRun is complex, in order to avoid
needless instantiation. It also bails if tabs would be enabled since positional effects are not yet
handled in this mode.

No behavioral changes are expected due to this change, so no new tests.

* platform/graphics/Font.cpp:
(WTF): Define deleteOwnedPtr for TextLayout as calling through destroyLayout; relying on operator delete is not workable as the class does not exist on all platforms.
(WTF::WebCore::TextLayout):
(WebCore): Implement no-op TextLayout wrappers for non-Mac platforms.
(WebCore::Font::createLayout):
(WebCore::Font::deleteLayout):
(WebCore::Font::width):
* platform/graphics/Font.h:
(WebCore): Add forward declarations for RenderText and TextLayout.
(Font): Add functions for dealing with pointer to TextLayout implementation.
(WTF): Declare deleteOwnedPtr for TextLayout.
* platform/graphics/mac/ComplexTextController.cpp:
(TextLayout): An instance of this class corresponds to a ComplexTextController for a particular TextRun.
(WebCore::TextLayout::isNeeded): Used by wrapper to avoid instantiation when complex layout is not required.
(WebCore::TextLayout::TextLayout):
(WebCore::TextLayout::width):
(WebCore::TextLayout::fontWithNoWordSpacing): Helper function to allow initialization of member variable.
(WebCore::TextLayout::constructTextRun): Ditto.
(WebCore): Implement real TextLayout wrappers for Mac.
(WebCore::Font::createLayout):
(WebCore::Font::deleteLayout):
(WebCore::Font::width):
(WebCore::ComplexTextController::ComplexTextController): Initialize m_ltrOnly and reserve initial capacity for m_runIndices.
(WebCore::ComplexTextController::indexOfCurrentRun): Return (visual) m_complexTextRuns index corresponding to (logical) m_currentRun, lazily constructing m_runIndices if needed.
(WebCore::ComplexTextController::incrementCurrentRun): Return next m_complexTextRuns index in logical order.
(WebCore::ComplexTextController::advance): Allow restarting, support for bidi reordering, and option to measure only whole glyphs rather than dividing advances.
(WebCore::ComplexTextController::adjustGlyphsAndAdvances): Clear m_ltrOnly on detecting a RTL run.
* platform/graphics/mac/ComplexTextController.h:
(ComplexTextController): Add m_ltrOnly indicating no bidi reordering, and m_runIndices mapping from runs (logical order) to m_complexTextRuns (visual order).
(WebCore::ComplexTextController::ComplexTextRun::indexBegin):
(WebCore::ComplexTextController::ComplexTextRun::isLTR):
(ComplexTextRun): Add helper functions returning values pertinent to individual runs as opposed to the entire containing line.
(WebCore::ComplexTextController::stringBegin): Return first string index.
(WebCore::ComplexTextController::stringEnd): Return one past last string index.
* platform/graphics/mac/ComplexTextControllerCoreText.mm: Initialize m_indexBegin and m_ltr.
(WebCore::ComplexTextController::ComplexTextRun::ComplexTextRun): Initialize m_indexBegin and m_ltr.
* rendering/RenderBlock.h:
(RenderTextInfo): Add single mapping from RenderText to LazyLineBreakIterator and (possibly null) TextLayout since they are recreated under the same circumstances.
* rendering/RenderBlockLineLayout.cpp:
(WebCore::RenderBlock::RenderTextInfo::RenderTextInfo): Make non-inline to avoid compilation errors.
(WebCore::RenderBlock::RenderTextInfo::~RenderTextInfo): Ditto.
(WebCore::RenderBlock::layoutRunsAndFloatsInRange): Allow RenderTextInfo to be reused across calls to nextLineBreak.
(WebCore::textWidth): Use TextLayout when supplied for measuring.
(WebCore::RenderBlock::LineBreaker::nextLineBreak):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (126762 => 126763)


--- trunk/Source/WebCore/ChangeLog	2012-08-27 14:52:32 UTC (rev 126762)
+++ trunk/Source/WebCore/ChangeLog	2012-08-27 15:31:56 UTC (rev 126763)
@@ -1,3 +1,75 @@
+2012-08-27  Ned Holbrook  <[email protected]>
+
+        Improve line breaking performance for complex text
+        https://bugs.webkit.org/show_bug.cgi?id=83045
+
+        Reviewed by Darin Adler.
+
+        Currently RenderBlock::LineBreaker::nextLineBreak assumes that measuring individual words is as cheap
+        as free. This is not the case when dealing with complex text, which benefits from laying out as much
+        text as possible and by reusing that layout when feasible: by doing so this patch improves line
+        breaking by 25% as measured with a simple test app.
+
+        The bulk of this change is modifying ComplexTextController::advance, which previously required the
+        text offset to be strictly increasing and assumed unidirectional text; now it supports random seeking
+        in a naive fashion (by restarting to the beginning) and traverses glyphs in logical order. In the
+        latter case, the presence of any non-LTR runs triggers the population of a mapping from logical to
+        visual run indices. Finally, a new flag has been added which inhibits glyph advances from being split
+        across ligatures (thus causing spurious line breaks).
+
+        A ComplexTextController and its associated TextRun and Font are encapsulated in a TextLayout object,
+        which is instantiated as an opaque object via functions that are no-ops unless building for Mac. A
+        static member function (isNeeded) checks to see whether a TextRun is complex, in order to avoid
+        needless instantiation. It also bails if tabs would be enabled since positional effects are not yet
+        handled in this mode.
+
+        No behavioral changes are expected due to this change, so no new tests.
+
+        * platform/graphics/Font.cpp:
+        (WTF): Define deleteOwnedPtr for TextLayout as calling through destroyLayout; relying on operator delete is not workable as the class does not exist on all platforms.
+        (WTF::WebCore::TextLayout):
+        (WebCore): Implement no-op TextLayout wrappers for non-Mac platforms.
+        (WebCore::Font::createLayout):
+        (WebCore::Font::deleteLayout):
+        (WebCore::Font::width):
+        * platform/graphics/Font.h:
+        (WebCore): Add forward declarations for RenderText and TextLayout.
+        (Font): Add functions for dealing with pointer to TextLayout implementation.
+        (WTF): Declare deleteOwnedPtr for TextLayout.
+        * platform/graphics/mac/ComplexTextController.cpp:
+        (TextLayout): An instance of this class corresponds to a ComplexTextController for a particular TextRun.
+        (WebCore::TextLayout::isNeeded): Used by wrapper to avoid instantiation when complex layout is not required.
+        (WebCore::TextLayout::TextLayout):
+        (WebCore::TextLayout::width):
+        (WebCore::TextLayout::fontWithNoWordSpacing): Helper function to allow initialization of member variable.
+        (WebCore::TextLayout::constructTextRun): Ditto.
+        (WebCore): Implement real TextLayout wrappers for Mac.
+        (WebCore::Font::createLayout):
+        (WebCore::Font::deleteLayout):
+        (WebCore::Font::width):
+        (WebCore::ComplexTextController::ComplexTextController): Initialize m_ltrOnly and reserve initial capacity for m_runIndices.
+        (WebCore::ComplexTextController::indexOfCurrentRun): Return (visual) m_complexTextRuns index corresponding to (logical) m_currentRun, lazily constructing m_runIndices if needed.
+        (WebCore::ComplexTextController::incrementCurrentRun): Return next m_complexTextRuns index in logical order.
+        (WebCore::ComplexTextController::advance): Allow restarting, support for bidi reordering, and option to measure only whole glyphs rather than dividing advances.
+        (WebCore::ComplexTextController::adjustGlyphsAndAdvances): Clear m_ltrOnly on detecting a RTL run.
+        * platform/graphics/mac/ComplexTextController.h:
+        (ComplexTextController): Add m_ltrOnly indicating no bidi reordering, and m_runIndices mapping from runs (logical order) to m_complexTextRuns (visual order).
+        (WebCore::ComplexTextController::ComplexTextRun::indexBegin):
+        (WebCore::ComplexTextController::ComplexTextRun::isLTR):
+        (ComplexTextRun): Add helper functions returning values pertinent to individual runs as opposed to the entire containing line.
+        (WebCore::ComplexTextController::stringBegin): Return first string index.
+        (WebCore::ComplexTextController::stringEnd): Return one past last string index.
+        * platform/graphics/mac/ComplexTextControllerCoreText.mm: Initialize m_indexBegin and m_ltr.
+        (WebCore::ComplexTextController::ComplexTextRun::ComplexTextRun): Initialize m_indexBegin and m_ltr.
+        * rendering/RenderBlock.h:
+        (RenderTextInfo): Add single mapping from RenderText to LazyLineBreakIterator and (possibly null) TextLayout since they are recreated under the same circumstances.
+        * rendering/RenderBlockLineLayout.cpp:
+        (WebCore::RenderBlock::RenderTextInfo::RenderTextInfo): Make non-inline to avoid compilation errors.
+        (WebCore::RenderBlock::RenderTextInfo::~RenderTextInfo): Ditto.
+        (WebCore::RenderBlock::layoutRunsAndFloatsInRange): Allow RenderTextInfo to be reused across calls to nextLineBreak.
+        (WebCore::textWidth): Use TextLayout when supplied for measuring.
+        (WebCore::RenderBlock::LineBreaker::nextLineBreak):
+
 2012-08-27  Nico Weber  <[email protected]>
 
         Add two missing variable initializers to RenderFlowThread

Modified: trunk/Source/WebCore/platform/graphics/Font.cpp (126762 => 126763)


--- trunk/Source/WebCore/platform/graphics/Font.cpp	2012-08-27 14:52:32 UTC (rev 126762)
+++ trunk/Source/WebCore/platform/graphics/Font.cpp	2012-08-27 15:31:56 UTC (rev 126763)
@@ -39,6 +39,16 @@
 using namespace WTF;
 using namespace Unicode;
 
+namespace WTF {
+
+// allow compilation of OwnPtr<TextLayout> in source files that don't have access to the TextLayout class definition
+template <> void deleteOwnedPtr<WebCore::TextLayout>(WebCore::TextLayout* ptr)
+{
+    WebCore::Font::deleteLayout(ptr);
+}
+
+}
+
 namespace WebCore {
 
 const uint8_t Font::s_roundingHackCharacterTable[256] = {
@@ -197,6 +207,25 @@
     return floatWidthForComplexText(run);
 }
 
+#if !PLATFORM(MAC)
+
+PassOwnPtr<TextLayout> Font::createLayout(RenderText*, float, bool) const
+{
+    return nullptr;
+}
+
+void Font::deleteLayout(TextLayout*)
+{
+}
+
+float Font::width(TextLayout&, unsigned, unsigned)
+{
+    ASSERT_NOT_REACHED();
+    return 0;
+}
+
+#endif
+
 FloatRect Font::selectionRectForText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const
 {
     to = (to == -1 ? run.length() : to);

Modified: trunk/Source/WebCore/platform/graphics/Font.h (126762 => 126763)


--- trunk/Source/WebCore/platform/graphics/Font.h	2012-08-27 14:52:32 UTC (rev 126762)
+++ trunk/Source/WebCore/platform/graphics/Font.h	2012-08-27 15:31:56 UTC (rev 126763)
@@ -53,6 +53,8 @@
 class GlyphBuffer;
 class GlyphPageTreeNode;
 class GraphicsContext;
+class RenderText;
+class TextLayout;
 class TextRun;
 
 struct GlyphData;
@@ -102,6 +104,10 @@
     float width(const TextRun&, HashSet<const SimpleFontData*>* fallbackFonts = 0, GlyphOverflow* = 0) const;
     float width(const TextRun&, int& charsConsumed, String& glyphName) const;
 
+    PassOwnPtr<TextLayout> createLayout(RenderText*, float xPos, bool collapseWhiteSpace) const;
+    static void deleteLayout(TextLayout*);
+    static float width(TextLayout&, unsigned from, unsigned len);
+
     int offsetForPosition(const TextRun&, float position, bool includePartialGlyphs) const;
     FloatRect selectionRectForText(const TextRun&, const FloatPoint&, int h, int from = 0, int to = -1) const;
 
@@ -310,4 +316,10 @@
 
 }
 
+namespace WTF {
+
+template <> void deleteOwnedPtr<WebCore::TextLayout>(WebCore::TextLayout*);
+
+}
+
 #endif

Modified: trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp (126762 => 126763)


--- trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp	2012-08-27 14:52:32 UTC (rev 126762)
+++ trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp	2012-08-27 15:31:56 UTC (rev 126763)
@@ -27,6 +27,8 @@
 
 #include "FloatSize.h"
 #include "Font.h"
+#include "RenderBlock.h"
+#include "RenderText.h"
 #include "TextBreakIterator.h"
 #include "TextRun.h"
 #include <ApplicationServices/ApplicationServices.h>
@@ -37,6 +39,71 @@
 
 namespace WebCore {
 
+class TextLayout {
+public:
+    static bool isNeeded(RenderText* text, const Font& font)
+    {
+        TextRun run = RenderBlock::constructTextRun(text, font, text->characters(), text->textLength(), text->style());
+        return font.codePath(run) == Font::Complex;
+    }
+
+    TextLayout(RenderText* text, const Font& font, float xPos)
+        : m_font(fontWithNoWordSpacing(font))
+        , m_run(constructTextRun(text, font, xPos))
+        , m_controller(adoptPtr(new ComplexTextController(&m_font, m_run, true)))
+    {
+    }
+
+    float width(unsigned from, unsigned len)
+    {
+        m_controller->advance(from, 0, ByWholeGlyphs);
+        float beforeWidth = m_controller->runWidthSoFar();
+        m_controller->advance(from + len, 0, ByWholeGlyphs);
+        float afterWidth = m_controller->runWidthSoFar();
+        return afterWidth - beforeWidth;
+    }
+
+private:
+    static Font fontWithNoWordSpacing(const Font& originalFont)
+    {
+        Font font(originalFont);
+        font.setWordSpacing(0);
+        return font;
+    }
+
+    static TextRun constructTextRun(RenderText* text, const Font& font, float xPos)
+    {
+        TextRun run = RenderBlock::constructTextRun(text, font, text->characters(), text->textLength(), text->style());
+        run.setCharactersLength(text->textLength());
+        ASSERT(run.charactersLength() >= run.length());
+
+        run.setXPos(xPos);
+        return run;
+    }
+
+    // ComplexTextController has only references to its Font and TextRun so they must be kept alive here.
+    Font m_font;
+    TextRun m_run;
+    OwnPtr<ComplexTextController> m_controller;
+};
+
+PassOwnPtr<TextLayout> Font::createLayout(RenderText* text, float xPos, bool collapseWhiteSpace) const
+{
+    if (!collapseWhiteSpace || !TextLayout::isNeeded(text, *this))
+        return nullptr;
+    return adoptPtr(new TextLayout(text, *this, xPos));
+}
+
+void Font::deleteLayout(TextLayout* layout)
+{
+    delete layout;
+}
+
+float Font::width(TextLayout& layout, unsigned from, unsigned len)
+{
+    return layout.width(from, len);
+}
+
 static inline CGFloat roundCGFloat(CGFloat f)
 {
     if (sizeof(CGFloat) == sizeof(float))
@@ -54,6 +121,7 @@
 ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis)
     : m_font(*font)
     , m_run(run)
+    , m_isLTROnly(true)
     , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
     , m_forTextEmphasis(forTextEmphasis)
     , m_currentCharacter(0)
@@ -92,6 +160,9 @@
     collectComplexTextRuns();
     adjustGlyphsAndAdvances();
 
+    if (!m_isLTROnly)
+        m_runIndices.reserveInitialCapacity(m_complexTextRuns.size());
+
     m_runWidthSoFar = m_leadingExpansion;
 }
 
@@ -340,25 +411,75 @@
     }
 }
 
-void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer)
+unsigned ComplexTextController::indexOfCurrentRun(unsigned& leftmostGlyph)
 {
+    leftmostGlyph = 0;
+    
+    size_t runCount = m_complexTextRuns.size();
+    if (m_currentRun >= runCount)
+        return runCount;
+
+    if (m_isLTROnly) {
+        for (unsigned i = 0; i < m_currentRun; ++i)
+            leftmostGlyph += m_complexTextRuns[i]->glyphCount();
+        return m_currentRun;
+    }
+
+    while (m_runIndices.size() <= m_currentRun) {
+        unsigned offset = m_runIndices.isEmpty() ? 0 : stringEnd(*m_complexTextRuns[m_runIndices.last()]);
+
+        for (unsigned i = 0; i < runCount; ++i) {
+            if (offset == stringBegin(*m_complexTextRuns[i])) {
+                m_runIndices.uncheckedAppend(i);
+                break;
+            }
+        }
+    }
+
+    unsigned currentRunIndex = m_runIndices[m_currentRun];
+    for (unsigned i = 0; i < currentRunIndex; ++i)
+        leftmostGlyph += m_complexTextRuns[i]->glyphCount();
+    return currentRunIndex;
+}
+
+unsigned ComplexTextController::incrementCurrentRun(unsigned& leftmostGlyph)
+{
+    if (m_isLTROnly) {
+        leftmostGlyph += m_complexTextRuns[m_currentRun++]->glyphCount();
+        return m_currentRun;
+    }
+
+    m_currentRun++;
+    leftmostGlyph = 0;
+    return indexOfCurrentRun(leftmostGlyph);
+}
+
+void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, GlyphIterationStyle iterationStyle)
+{
     if (static_cast<int>(offset) > m_end)
         offset = m_end;
 
-    if (offset <= m_currentCharacter)
-        return;
+    if (offset <= m_currentCharacter) {
+        m_runWidthSoFar = m_leadingExpansion;
+        m_numGlyphsSoFar = 0;
+        m_currentRun = 0;
+        m_glyphInCurrentRun = 0;
+        m_characterInCurrentGlyph = 0;
+    }
 
     m_currentCharacter = offset;
 
     size_t runCount = m_complexTextRuns.size();
 
-    bool ltr = m_run.ltr();
-
-    unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar;
+    unsigned leftmostGlyph = 0;
+    unsigned currentRunIndex = indexOfCurrentRun(leftmostGlyph);
     while (m_currentRun < runCount) {
-        const ComplexTextRun& complexTextRun = *m_complexTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun];
+        const ComplexTextRun& complexTextRun = *m_complexTextRuns[currentRunIndex];
+        bool ltr = complexTextRun.isLTR();
         size_t glyphCount = complexTextRun.glyphCount();
         unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
+        unsigned k = leftmostGlyph + g;
+
         while (m_glyphInCurrentRun < glyphCount) {
             unsigned glyphStartOffset = complexTextRun.indexAt(g);
             unsigned glyphEndOffset;
@@ -387,6 +508,9 @@
                 // When there are multiple glyphs per character we need to advance by the full width of the glyph.
                 ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
                 m_runWidthSoFar += adjustedAdvance.width;
+            } else if (iterationStyle == ByWholeGlyphs) {
+                if (!oldCharacterInCurrentGlyph)
+                    m_runWidthSoFar += adjustedAdvance.width;
             } else
                 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
 
@@ -404,10 +528,10 @@
                 k--;
             }
         }
-        m_currentRun++;
+        currentRunIndex = incrementCurrentRun(leftmostGlyph);
         m_glyphInCurrentRun = 0;
     }
-    if (!ltr && m_numGlyphsSoFar == m_adjustedAdvances.size())
+    if (!m_run.ltr() && m_numGlyphsSoFar == m_adjustedAdvances.size())
         m_runWidthSoFar += m_finalRoundingWidth;
 }
 
@@ -421,6 +545,9 @@
         unsigned glyphCount = complexTextRun.glyphCount();
         const SimpleFontData* fontData = complexTextRun.fontData();
 
+        if (!complexTextRun.isLTR())
+            m_isLTROnly = false;
+
         const CGGlyph* glyphs = complexTextRun.glyphs();
         const CGSize* advances = complexTextRun.advances();
 

Modified: trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.h (126762 => 126763)


--- trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.h	2012-08-27 14:52:32 UTC (rev 126762)
+++ trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.h	2012-08-27 15:31:56 UTC (rev 126763)
@@ -44,6 +44,8 @@
 class SimpleFontData;
 class TextRun;
 
+enum GlyphIterationStyle { IncludePartialGlyphs, ByWholeGlyphs };
+
 // ComplexTextController is responsible for rendering and measuring glyphs for
 // complex scripts on OS X.
 class ComplexTextController {
@@ -51,7 +53,7 @@
     ComplexTextController(const Font*, const TextRun&, bool mayUseNaturalWritingDirection = false, HashSet<const SimpleFontData*>* fallbackFonts = 0, bool forTextEmphasis = false);
 
     // Advance and emit glyphs up to the specified character.
-    void advance(unsigned to, GlyphBuffer* = 0);
+    void advance(unsigned to, GlyphBuffer* = 0, GlyphIterationStyle = IncludePartialGlyphs);
 
     // Compute the character offset for a given x coordinate.
     int offsetForPosition(float x, bool includePartialGlyphs);
@@ -89,10 +91,12 @@
         unsigned stringLocation() const { return m_stringLocation; }
         size_t stringLength() const { return m_stringLength; }
         ALWAYS_INLINE CFIndex indexAt(size_t i) const;
+        CFIndex indexBegin() const { return m_indexBegin; }
         CFIndex indexEnd() const { return m_indexEnd; }
         CFIndex endOffsetAt(size_t i) const { ASSERT(!m_isMonotonic); return m_glyphEndOffsets[i]; }
         const CGGlyph* glyphs() const { return m_glyphs; }
         const CGSize* advances() const { return m_advances; }
+        bool isLTR() const { return m_isLTR; }
         bool isMonotonic() const { return m_isMonotonic; }
         void setIsNonMonotonic();
 
@@ -107,22 +111,35 @@
         size_t m_stringLength;
         Vector<CFIndex, 64> m_coreTextIndicesVector;
         const CFIndex* m_coreTextIndices;
+        CFIndex m_indexBegin;
         CFIndex m_indexEnd;
         Vector<CFIndex, 64> m_glyphEndOffsets;
         Vector<CGGlyph, 64> m_glyphsVector;
         const CGGlyph* m_glyphs;
         Vector<CGSize, 64> m_advancesVector;
         const CGSize* m_advances;
+        bool m_isLTR;
         bool m_isMonotonic;
     };
+    
+    static unsigned stringBegin(const ComplexTextRun& run) { return run.stringLocation() + run.indexBegin(); }
+    static unsigned stringEnd(const ComplexTextRun& run) { return run.stringLocation() + run.indexEnd(); }
 
     void collectComplexTextRuns();
 
     void collectComplexTextRunsForCharacters(const UChar*, unsigned length, unsigned stringLocation, const SimpleFontData*);
     void adjustGlyphsAndAdvances();
 
+    unsigned indexOfCurrentRun(unsigned& leftmostGlyph);
+    unsigned incrementCurrentRun(unsigned& leftmostGlyph);
+
+    // The default size of this vector was selected as being the smallest power of two greater than
+    // the average (3.5) plus one standard deviation (7.5) of nonzero sizes used on Arabic Wikipedia.
+    Vector<unsigned, 16> m_runIndices;
+
     const Font& m_font;
     const TextRun& m_run;
+    bool m_isLTROnly;
     bool m_mayUseNaturalWritingDirection;
     bool m_forTextEmphasis;
 

Modified: trunk/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.mm (126762 => 126763)


--- trunk/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.mm	2012-08-27 14:52:32 UTC (rev 126762)
+++ trunk/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.mm	2012-08-27 15:31:56 UTC (rev 126763)
@@ -105,7 +105,9 @@
     , m_characters(characters)
     , m_stringLocation(stringLocation)
     , m_stringLength(stringLength)
+    , m_indexBegin(runRange.location)
     , m_indexEnd(runRange.location + runRange.length)
+    , m_isLTR(!(CTRunGetStatus(ctRun) & kCTRunStatusRightToLeft))
     , m_isMonotonic(true)
 {
     m_glyphCount = CTRunGetGlyphCount(ctRun);
@@ -138,7 +140,9 @@
     , m_characters(characters)
     , m_stringLocation(stringLocation)
     , m_stringLength(stringLength)
+    , m_indexBegin(0)
     , m_indexEnd(stringLength)
+    , m_isLTR(ltr)
     , m_isMonotonic(true)
 {
     m_coreTextIndicesVector.reserveInitialCapacity(m_stringLength);

Modified: trunk/Source/WebCore/rendering/RenderBlock.h (126762 => 126763)


--- trunk/Source/WebCore/rendering/RenderBlock.h	2012-08-27 14:52:32 UTC (rev 126762)
+++ trunk/Source/WebCore/rendering/RenderBlock.h	2012-08-27 15:31:56 UTC (rev 126763)
@@ -29,6 +29,7 @@
 #include "RenderBox.h"
 #include "RenderLineBoxList.h"
 #include "RootInlineBox.h"
+#include "TextBreakIterator.h"
 #include "TextRun.h"
 #include <wtf/OwnPtr.h>
 #include <wtf/ListHashSet.h>
@@ -42,7 +43,6 @@
 class BidiContext;
 class InlineIterator;
 class LayoutStateMaintainer;
-class LazyLineBreakIterator;
 class LineLayoutState;
 class LineWidth;
 class RenderInline;
@@ -52,6 +52,7 @@
 struct PaintInfo;
 class LineInfo;
 class RenderRubyRun;
+class TextLayout;
 
 template <class Iterator, class Run> class BidiResolver;
 template <class Run> class BidiRunList;
@@ -706,7 +707,16 @@
     LayoutPoint computeLogicalLocationForFloat(const FloatingObject*, LayoutUnit logicalTopOffset) const;
 
     // The following functions' implementations are in RenderBlockLineLayout.cpp.
-    typedef std::pair<RenderText*, LazyLineBreakIterator> LineBreakIteratorInfo;
+    struct RenderTextInfo {
+        // Destruction of m_layout requires TextLayout to be a complete type, so the constructor and destructor are made non-inline to avoid compilation errors.
+        RenderTextInfo();
+        ~RenderTextInfo();
+
+        RenderText* m_text;
+        OwnPtr<TextLayout> m_layout;
+        LazyLineBreakIterator m_lineBreakIterator;
+    };
+
     class LineBreaker {
     public:
         LineBreaker(RenderBlock* block)
@@ -715,7 +725,7 @@
             reset();
         }
 
-        InlineIterator nextLineBreak(InlineBidiResolver&, LineInfo&, LineBreakIteratorInfo&, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines);
+        InlineIterator nextLineBreak(InlineBidiResolver&, LineInfo&, RenderTextInfo&, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines);
 
         bool lineWasHyphenated() { return m_hyphenated; }
         const Vector<RenderBox*>& positionedObjects() { return m_positionedObjects; }

Modified: trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp (126762 => 126763)


--- trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp	2012-08-27 14:52:32 UTC (rev 126762)
+++ trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp	2012-08-27 15:31:56 UTC (rev 126763)
@@ -36,7 +36,6 @@
 #include "RenderRubyRun.h"
 #include "RenderView.h"
 #include "Settings.h"
-#include "TextBreakIterator.h"
 #include "TrailingFloatsRootInlineBox.h"
 #include "VerticalPositionCache.h"
 #include "break_lines.h"
@@ -1274,6 +1273,15 @@
     repaintDirtyFloats(layoutState.floats());
 }
 
+RenderBlock::RenderTextInfo::RenderTextInfo()
+    : m_text(0)
+{
+}
+
+RenderBlock::RenderTextInfo::~RenderTextInfo()
+{
+}
+
 void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, InlineBidiResolver& resolver, const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus, unsigned consecutiveHyphenatedLines)
 {
     RenderStyle* styleToUse = style();
@@ -1281,7 +1289,7 @@
     LineMidpointState& lineMidpointState = resolver.midpointState();
     InlineIterator end = resolver.position();
     bool checkForEndLineMatch = layoutState.endLine();
-    LineBreakIteratorInfo lineBreakIteratorInfo;
+    RenderTextInfo renderTextInfo;
     VerticalPositionCache verticalPositionCache;
 
     LineBreaker lineBreaker(this);
@@ -1317,7 +1325,7 @@
         if (wrapShapeInfo)
             wrapShapeInfo->computeSegmentsForLine(logicalHeight());
 #endif
-        end = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), lineBreakIteratorInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines);
+        end = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines);
         if (resolver.position().atEnd()) {
             // FIXME: We shouldn't be creating any runs in nextLineBreak to begin with!
             // Once BidiRunList is separated from BidiResolver this will not be needed.
@@ -2017,11 +2025,14 @@
     return false;
 }
 
-static inline float textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, float xPos, bool isFixedPitch, bool collapseWhiteSpace)
+static inline float textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, float xPos, bool isFixedPitch, bool collapseWhiteSpace, TextLayout* layout = 0)
 {
     if (isFixedPitch || (!from && len == text->textLength()) || text->style()->hasTextCombine())
         return text->width(from, len, font, xPos);
 
+    if (layout)
+        return Font::width(*layout, from, len);
+
     TextRun run = RenderBlock::constructTextRun(text, font, text->characters() + from, len, text->style());
     run.setCharactersLength(text->textLength() - from);
     ASSERT(run.charactersLength() >= run.length());
@@ -2183,8 +2194,7 @@
     m_clear = CNONE;
 }
 
-InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resolver, LineInfo& lineInfo,
-    LineBreakIteratorInfo& lineBreakIteratorInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines)
+InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines)
 {
     reset();
 
@@ -2423,6 +2433,13 @@
                 ASSERT(current.m_pos == t->textLength());
             }
 
+            if (renderTextInfo.m_text != t || renderTextInfo.m_lineBreakIterator.string() != t->characters()) {
+                renderTextInfo.m_text = t;
+                renderTextInfo.m_layout = f.createLayout(t, width.currentWidth(), collapseWhiteSpace);
+                renderTextInfo.m_lineBreakIterator.reset(t->characters(), t->textLength(), style->locale());
+            }
+            TextLayout* textLayout = renderTextInfo.m_layout.get();
+
             for (; current.m_pos < t->textLength(); current.fastIncrementInTextNode()) {
                 bool previousCharacterIsSpace = currentCharacterIsSpace;
                 bool previousCharacterIsWS = currentCharacterIsWS;
@@ -2444,16 +2461,11 @@
                 if ((breakAll || breakWords) && !midWordBreak) {
                     wrapW += charWidth;
                     bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && current.m_pos + 1 < t->textLength() && U16_IS_TRAIL(t->characters()[current.m_pos + 1]);
-                    charWidth = textWidth(t, current.m_pos, midWordBreakIsBeforeSurrogatePair ? 2 : 1, f, width.committedWidth() + wrapW, isFixedPitch, collapseWhiteSpace);
+                    charWidth = textWidth(t, current.m_pos, midWordBreakIsBeforeSurrogatePair ? 2 : 1, f, width.committedWidth() + wrapW, isFixedPitch, collapseWhiteSpace, textLayout);
                     midWordBreak = width.committedWidth() + wrapW + charWidth > width.availableWidth();
                 }
 
-                if ((lineBreakIteratorInfo.first != t) || (lineBreakIteratorInfo.second.string() != t->characters())) {
-                    lineBreakIteratorInfo.first = t;
-                    lineBreakIteratorInfo.second.reset(t->characters(), t->textLength(), style->locale());
-                }
-
-                bool betweenWords = c == '\n' || (currWS != PRE && !atStart && isBreakable(lineBreakIteratorInfo.second, current.m_pos, current.m_nextBreakablePosition, breakNBSP)
+                bool betweenWords = c == '\n' || (currWS != PRE && !atStart && isBreakable(renderTextInfo.m_lineBreakIterator, current.m_pos, current.m_nextBreakablePosition, breakNBSP)
                     && (style->hyphens() != HyphensNone || (current.previousInSameNode() != softHyphen)));
 
                 if (betweenWords || midWordBreak) {
@@ -2475,9 +2487,9 @@
 
                     float additionalTmpW;
                     if (wordTrailingSpaceWidth && currentCharacterIsSpace)
-                        additionalTmpW = textWidth(t, lastSpace, current.m_pos + 1 - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace) - wordTrailingSpaceWidth + lastSpaceWordSpacing;
+                        additionalTmpW = textWidth(t, lastSpace, current.m_pos + 1 - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, textLayout) - wordTrailingSpaceWidth + lastSpaceWordSpacing;
                     else
-                        additionalTmpW = textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing;
+                        additionalTmpW = textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, textLayout) + lastSpaceWordSpacing;
                     width.addUncommittedWidth(additionalTmpW);
                     if (!appliedStartWidth) {
                         width.addUncommittedWidth(inlineLogicalWidth(current.m_obj, true, false));
@@ -2494,7 +2506,7 @@
                         // as candidate width for this line.
                         bool lineWasTooWide = false;
                         if (width.fitsOnLine() && currentCharacterIsWS && currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) {
-                            float charWidth = textWidth(t, current.m_pos, 1, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace) + (applyWordSpacing ? wordSpacing : 0);
+                            float charWidth = textWidth(t, current.m_pos, 1, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, textLayout) + (applyWordSpacing ? wordSpacing : 0);
                             // Check if line is too big even without the extra space
                             // at the end of the line. If it is not, do nothing.
                             // If the line needs the extra whitespace to be too long,
@@ -2620,7 +2632,7 @@
             }
 
             // IMPORTANT: current.m_pos is > length here!
-            float additionalTmpW = ignoringSpaces ? 0 : textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing;
+            float additionalTmpW = ignoringSpaces ? 0 : textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, textLayout) + lastSpaceWordSpacing;
             width.addUncommittedWidth(additionalTmpW + inlineLogicalWidth(current.m_obj, !appliedStartWidth, includeEndWidth));
             includeEndWidth = false;
 
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to