Large SVG text layout performance regression in r81168
authorzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 Jan 2012 13:08:29 +0000 (13:08 +0000)
committerzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 Jan 2012 13:08:29 +0000 (13:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=65711

Reviewed by Zoltan Herczeg.

Change the way we store x/y/dx/dy/rotate values for a <text> element and its ancestors.

SVGTextLayoutAttributesBuilder used to hold a Vector<float> for x/y/dx/dy/rotate, each as big
as the whole text subtree length. For each character, that has an absolute position, or rotation
the value was stored in this list. For all other characters a special empty value marker was stored.

This is highly inefficient, and is now replaced with a HashMap<unsigned, SVGCharacterData> where
SVGCharacterData is a struct, holding float x/y/dx/dy/rotate. The code is now optimized for the
common case, where only a few characters actually specify such values, eg:
<text x="50" y="90">looooong text<tspan x="50" y="30">abc</tspan></text>.

NOTE: There are still some inefficiencies in this patch (especially the copying of SVGTextLayoutAttributes).
To keep the patch size smaller, I decided to fix this in another patch, and only fix the memory issue with this patch.

This reduces the memory consumption from 35MB to 10MB on the attached testcase in bug 65711.

Doesn't affect any test, yet. A follow-up commit will enable the usage of the simple code path, using WidthIterator,
in SVGTextMetricsBuilder, which will result in several layout test changes, because of geometry changes.

* rendering/svg/RenderSVGInlineText.cpp:
(WebCore::RenderSVGInlineText::RenderSVGInlineText):
(WebCore::RenderSVGInlineText::characterStartsNewTextChunk):
* rendering/svg/RenderSVGInlineText.h:
(WebCore::RenderSVGInlineText::layoutAttributes):
* rendering/svg/RenderSVGText.cpp:
(WebCore::RenderSVGText::layout):
(WebCore::recursiveCollectLayoutAttributes):
(WebCore::RenderSVGText::rebuildLayoutAttributes):
* rendering/svg/RenderSVGText.h:
* rendering/svg/SVGRootInlineBox.cpp:
(WebCore::SVGRootInlineBox::computePerCharacterLayoutInformation):
(WebCore::SVGRootInlineBox::layoutCharactersInTextBoxes):
(WebCore::SVGRootInlineBox::closestLeafChildForPosition):
(WebCore::swapItemsInLayoutAttributes):
(WebCore::findFirstAndLastAttributesInVector):
(WebCore::reverseInlineBoxRangeAndValueListsIfNeeded):
* rendering/svg/SVGTextLayoutAttributes.cpp:
(WebCore::SVGTextLayoutAttributes::clear):
(WebCore::dumpSVGCharacterDataMapValue):
(WebCore::SVGTextLayoutAttributes::dump):
* rendering/svg/SVGTextLayoutAttributes.h:
(WebCore::SVGTextLayoutAttributes::characterDataMap):
* rendering/svg/SVGTextLayoutAttributesBuilder.cpp:
(WebCore::SVGTextLayoutAttributesBuilder::SVGTextLayoutAttributesBuilder):
(WebCore::SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextRenderer):
(WebCore::SVGTextLayoutAttributesBuilder::buildLayoutAttributesForWholeTree):
(WebCore::SVGTextLayoutAttributesBuilder::rebuildMetricsForTextRenderer):
(WebCore::SVGTextLayoutAttributesBuilder::rebuildMetricsForWholeTree):
(WebCore::SVGTextLayoutAttributesBuilder::buildLayoutAttributesIfNeeded):
(WebCore::processRenderSVGInlineText):
(WebCore::SVGTextLayoutAttributesBuilder::collectTextPositioningElements):
(WebCore::SVGTextLayoutAttributesBuilder::buildLayoutAttributes):
(WebCore::updateCharacterData):
(WebCore::SVGTextLayoutAttributesBuilder::fillCharacterDataMap):
* rendering/svg/SVGTextLayoutAttributesBuilder.h:
(WebCore::SVGTextLayoutAttributesBuilder::clearTextPositioningElements):
* rendering/svg/SVGTextLayoutEngine.cpp:
(WebCore::SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded):
(WebCore::SVGTextLayoutEngine::currentLogicalCharacterAttributes):
(WebCore::SVGTextLayoutEngine::layoutTextOnLineOrPath):
* rendering/svg/SVGTextLayoutEngine.h:
(WebCore::SVGTextLayoutEngine::layoutAttributes):
* rendering/svg/SVGTextMetricsBuilder.cpp:
(WebCore::SVGTextMetricsBuilder::advance):
(WebCore::SVGTextMetricsBuilder::advanceComplexText):
(WebCore::SVGTextMetricsBuilder::measureTextRenderer):
* rendering/svg/SVGTextQuery.cpp:
(WebCore::SVGTextQuery::modifyStartEndPositionsRespectingLigatures):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@105057 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
Source/WebCore/ChangeLog
Source/WebCore/rendering/svg/RenderSVGInlineText.cpp
Source/WebCore/rendering/svg/RenderSVGInlineText.h
Source/WebCore/rendering/svg/RenderSVGText.cpp
Source/WebCore/rendering/svg/RenderSVGText.h
Source/WebCore/rendering/svg/SVGRootInlineBox.cpp
Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp
Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h
Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp
Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h
Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp
Source/WebCore/rendering/svg/SVGTextLayoutEngine.h
Source/WebCore/rendering/svg/SVGTextMetricsBuilder.cpp
Source/WebCore/rendering/svg/SVGTextMetricsBuilder.h
Source/WebCore/rendering/svg/SVGTextQuery.cpp

index 0f823d4..f67dfc8 100644 (file)
@@ -1,3 +1,79 @@
+2012-01-16  Nikolas Zimmermann  <nzimmermann@rim.com>
+
+        Large SVG text layout performance regression in r81168
+        https://bugs.webkit.org/show_bug.cgi?id=65711
+
+        Reviewed by Zoltan Herczeg.
+
+        Change the way we store x/y/dx/dy/rotate values for a <text> element and its ancestors.
+
+        SVGTextLayoutAttributesBuilder used to hold a Vector<float> for x/y/dx/dy/rotate, each as big
+        as the whole text subtree length. For each character, that has an absolute position, or rotation
+        the value was stored in this list. For all other characters a special empty value marker was stored.
+
+        This is highly inefficient, and is now replaced with a HashMap<unsigned, SVGCharacterData> where
+        SVGCharacterData is a struct, holding float x/y/dx/dy/rotate. The code is now optimized for the
+        common case, where only a few characters actually specify such values, eg:
+        <text x="50" y="90">looooong text<tspan x="50" y="30">abc</tspan></text>.
+
+        NOTE: There are still some inefficiencies in this patch (especially the copying of SVGTextLayoutAttributes).
+        To keep the patch size smaller, I decided to fix this in another patch, and only fix the memory issue with this patch.
+
+        This reduces the memory consumption from 35MB to 10MB on the attached testcase in bug 65711.
+
+        Doesn't affect any test, yet. A follow-up commit will enable the usage of the simple code path, using WidthIterator,
+        in SVGTextMetricsBuilder, which will result in several layout test changes, because of geometry changes.
+
+        * rendering/svg/RenderSVGInlineText.cpp:
+        (WebCore::RenderSVGInlineText::RenderSVGInlineText):
+        (WebCore::RenderSVGInlineText::characterStartsNewTextChunk):
+        * rendering/svg/RenderSVGInlineText.h:
+        (WebCore::RenderSVGInlineText::layoutAttributes):
+        * rendering/svg/RenderSVGText.cpp:
+        (WebCore::RenderSVGText::layout):
+        (WebCore::recursiveCollectLayoutAttributes):
+        (WebCore::RenderSVGText::rebuildLayoutAttributes):
+        * rendering/svg/RenderSVGText.h:
+        * rendering/svg/SVGRootInlineBox.cpp:
+        (WebCore::SVGRootInlineBox::computePerCharacterLayoutInformation):
+        (WebCore::SVGRootInlineBox::layoutCharactersInTextBoxes):
+        (WebCore::SVGRootInlineBox::closestLeafChildForPosition):
+        (WebCore::swapItemsInLayoutAttributes):
+        (WebCore::findFirstAndLastAttributesInVector):
+        (WebCore::reverseInlineBoxRangeAndValueListsIfNeeded):
+        * rendering/svg/SVGTextLayoutAttributes.cpp:
+        (WebCore::SVGTextLayoutAttributes::clear):
+        (WebCore::dumpSVGCharacterDataMapValue):
+        (WebCore::SVGTextLayoutAttributes::dump):
+        * rendering/svg/SVGTextLayoutAttributes.h:
+        (WebCore::SVGTextLayoutAttributes::characterDataMap):
+        * rendering/svg/SVGTextLayoutAttributesBuilder.cpp:
+        (WebCore::SVGTextLayoutAttributesBuilder::SVGTextLayoutAttributesBuilder):
+        (WebCore::SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextRenderer):
+        (WebCore::SVGTextLayoutAttributesBuilder::buildLayoutAttributesForWholeTree):
+        (WebCore::SVGTextLayoutAttributesBuilder::rebuildMetricsForTextRenderer):
+        (WebCore::SVGTextLayoutAttributesBuilder::rebuildMetricsForWholeTree):
+        (WebCore::SVGTextLayoutAttributesBuilder::buildLayoutAttributesIfNeeded):
+        (WebCore::processRenderSVGInlineText):
+        (WebCore::SVGTextLayoutAttributesBuilder::collectTextPositioningElements):
+        (WebCore::SVGTextLayoutAttributesBuilder::buildLayoutAttributes):
+        (WebCore::updateCharacterData):
+        (WebCore::SVGTextLayoutAttributesBuilder::fillCharacterDataMap):
+        * rendering/svg/SVGTextLayoutAttributesBuilder.h:
+        (WebCore::SVGTextLayoutAttributesBuilder::clearTextPositioningElements):
+        * rendering/svg/SVGTextLayoutEngine.cpp:
+        (WebCore::SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded):
+        (WebCore::SVGTextLayoutEngine::currentLogicalCharacterAttributes):
+        (WebCore::SVGTextLayoutEngine::layoutTextOnLineOrPath):
+        * rendering/svg/SVGTextLayoutEngine.h:
+        (WebCore::SVGTextLayoutEngine::layoutAttributes):
+        * rendering/svg/SVGTextMetricsBuilder.cpp:
+        (WebCore::SVGTextMetricsBuilder::advance):
+        (WebCore::SVGTextMetricsBuilder::advanceComplexText):
+        (WebCore::SVGTextMetricsBuilder::measureTextRenderer):
+        * rendering/svg/SVGTextQuery.cpp:
+        (WebCore::SVGTextQuery::modifyStartEndPositionsRespectingLigatures):
+
 2012-01-15  Andreas Kling  <awesomekling@apple.com>
 
         CSSCanvasValue can't be renamed, enforce this at compile-time.
index 4057e05..a60dd6a 100644 (file)
@@ -68,6 +68,7 @@ static PassRefPtr<StringImpl> applySVGWhitespaceRules(PassRefPtr<StringImpl> str
 RenderSVGInlineText::RenderSVGInlineText(Node* n, PassRefPtr<StringImpl> string)
     : RenderText(n, applySVGWhitespaceRules(string, false))
     , m_scalingFactor(1)
+    , m_layoutAttributes(this)
 {
 }
 
@@ -140,8 +141,6 @@ LayoutRect RenderSVGInlineText::linesBoundingBox() const
 
 bool RenderSVGInlineText::characterStartsNewTextChunk(int position) const
 {
-    ASSERT(m_attributes.xValues().size() == textLength());
-    ASSERT(m_attributes.yValues().size() == textLength());
     ASSERT(position >= 0);
     ASSERT(position < static_cast<int>(textLength()));
 
@@ -149,27 +148,11 @@ bool RenderSVGInlineText::characterStartsNewTextChunk(int position) const
     if (!position && parent()->isSVGTextPath() && !previousSibling())
         return true;
 
-    int currentPosition = 0;
-    unsigned size = m_attributes.textMetricsValues().size();
-    for (unsigned i = 0; i < size; ++i) {
-        const SVGTextMetrics& metrics = m_attributes.textMetricsValues().at(i);
+    const SVGCharacterDataMap::const_iterator it = m_layoutAttributes.characterDataMap().find(static_cast<unsigned>(position + 1));
+    if (it == m_layoutAttributes.characterDataMap().end())
+        return false;
 
-        // We found the desired character.
-        if (currentPosition == position) {
-            return m_attributes.xValues().at(position) != SVGTextLayoutAttributes::emptyValue()
-                || m_attributes.yValues().at(position) != SVGTextLayoutAttributes::emptyValue();
-        }
-
-        currentPosition += metrics.length();
-        if (currentPosition > position)
-            break;
-    }
-
-    // The desired position is available in the x/y list, but not in the character data values list.
-    // That means the previous character data described a single glyph, consisting of multiple unicode characters.
-    // The consequence is that the desired character does not define a new absolute x/y position, even if present in the x/y test.
-    // This code is tested by svg/W3C-SVG-1.1/text-text-06-t.svg (and described in detail, why this influences chunk detection).
-    return false;
+    return it->second.x != SVGTextLayoutAttributes::emptyValue() || it->second.y != SVGTextLayoutAttributes::emptyValue();
 }
 
 VisiblePosition RenderSVGInlineText::positionForPoint(const LayoutPoint& point)
index 6e2c4f4..f0c0042 100644 (file)
@@ -35,10 +35,7 @@ public:
     RenderSVGInlineText(Node*, PassRefPtr<StringImpl>);
 
     bool characterStartsNewTextChunk(int position) const;
-
-    SVGTextLayoutAttributes& layoutAttributes() { return m_attributes; }
-    const SVGTextLayoutAttributes& layoutAttributes() const { return m_attributes; }
-    void storeLayoutAttributes(const SVGTextLayoutAttributes& attributes) { m_attributes = attributes; }
+    SVGTextLayoutAttributes& layoutAttributes() { return m_layoutAttributes; }
 
     float scalingFactor() const { return m_scalingFactor; }
     const Font& scaledFont() const { return m_scaledFont; }
@@ -68,7 +65,7 @@ private:
 
     float m_scalingFactor;
     Font m_scaledFont;
-    SVGTextLayoutAttributes m_attributes;
+    SVGTextLayoutAttributes m_layoutAttributes;
 };
 
 inline RenderSVGInlineText* toRenderSVGInlineText(RenderObject* object)
index 612203c..b5f6f0d 100644 (file)
@@ -141,8 +141,7 @@ void RenderSVGText::layout()
 
     if (m_needsPositioningValuesUpdate) {
         // Perform SVG text layout phase one (see SVGTextLayoutAttributesBuilder for details).
-        SVGTextLayoutAttributesBuilder layoutAttributesBuilder;
-        layoutAttributesBuilder.buildLayoutAttributesForTextSubtree(this);
+        m_layoutAttributesBuilder.buildLayoutAttributesForWholeTree(this);
         m_needsReordering = true;
         m_needsPositioningValuesUpdate = false;
         updateCachedBoundariesInParents = true;
@@ -293,6 +292,40 @@ void RenderSVGText::updateFirstLetter()
 {
 }
 
+static inline void recursiveCollectLayoutAttributes(RenderObject* start, Vector<SVGTextLayoutAttributes>& attributes)
+{
+    for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
+        if (child->isSVGInlineText()) {
+            attributes.append(toRenderSVGInlineText(child)->layoutAttributes());
+            continue;
+        }
+
+        recursiveCollectLayoutAttributes(child, attributes);
+    }
+}
+
+void RenderSVGText::rebuildLayoutAttributes(bool performFullRebuild)
+{
+    // FIXME: For now we always rebuild the whole tree, as it used to be.
+    performFullRebuild = true;
+    if (performFullRebuild)
+        m_layoutAttributes.clear();
+
+    if (m_layoutAttributes.isEmpty()) {
+        recursiveCollectLayoutAttributes(this, m_layoutAttributes);
+        if (m_layoutAttributes.isEmpty() || !performFullRebuild)
+            return;
+
+        m_layoutAttributesBuilder.rebuildMetricsForWholeTree(this);
+        return;
+    }
+
+    /* FIXME: Enable this once we rebuild subtrees, instead of the full tree
+    Vector<SVGTextLayoutAttributes*> affectedAttributes;
+    rebuildLayoutAttributes(affectedAttributes);
+    */
+}
+
 }
 
 #endif // ENABLE(SVG)
index 98b6053..d635649 100644 (file)
@@ -26,7 +26,7 @@
 
 #include "AffineTransform.h"
 #include "RenderSVGBlock.h"
-#include "SVGTextLayoutAttributes.h"
+#include "SVGTextLayoutAttributesBuilder.h"
 
 namespace WebCore {
 
@@ -49,6 +49,8 @@ public:
     Vector<SVGTextLayoutAttributes>& layoutAttributes() { return m_layoutAttributes; }
     bool needsReordering() const { return m_needsReordering; }
 
+    void rebuildLayoutAttributes(bool performFullRebuild = false);
+
 private:
     virtual const char* renderName() const { return "RenderSVGText"; }
     virtual bool isSVGText() const { return true; }
@@ -83,6 +85,7 @@ private:
     bool m_needsPositioningValuesUpdate : 1;
     bool m_needsTransformUpdate : 1;
     AffineTransform m_localTransform;
+    SVGTextLayoutAttributesBuilder m_layoutAttributesBuilder;
     Vector<SVGTextLayoutAttributes> m_layoutAttributes;
 };
 
index d5f5d5c..1251715 100644 (file)
@@ -73,18 +73,19 @@ void SVGRootInlineBox::paint(PaintInfo& paintInfo, const LayoutPoint&, LayoutUni
 
 void SVGRootInlineBox::computePerCharacterLayoutInformation()
 {
-    RenderSVGText* parentBlock = toRenderSVGText(block());
-    ASSERT(parentBlock);
+    RenderSVGText* textRoot = toRenderSVGText(block());
+    ASSERT(textRoot);
 
-    Vector<SVGTextLayoutAttributes>& attributes = parentBlock->layoutAttributes();
-    if (attributes.isEmpty())
+    textRoot->rebuildLayoutAttributes();
+    Vector<SVGTextLayoutAttributes>& layoutAttributes = textRoot->layoutAttributes();
+    if (layoutAttributes.isEmpty())
         return;
 
-    if (parentBlock->needsReordering())
-        reorderValueLists(attributes);
+    if (textRoot->needsReordering())
+        reorderValueLists(layoutAttributes);
 
     // Perform SVG text layout phase two (see SVGTextLayoutEngine for details).
-    SVGTextLayoutEngine characterLayout(attributes);
+    SVGTextLayoutEngine characterLayout(layoutAttributes);
     layoutCharactersInTextBoxes(this, characterLayout);
 
     // Perform SVG text layout phase three (see SVGTextChunkBuilder for details).
@@ -119,10 +120,7 @@ void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGText
             if (isTextPath) {
                 // Build text chunks for all <textPath> children, using the line layout algorithm.
                 // This is needeed as text-anchor is just an additional startOffset for text paths.
-                RenderSVGText* parentBlock = toRenderSVGText(block());
-                ASSERT(parentBlock);
-
-                SVGTextLayoutEngine lineLayout(parentBlock->layoutAttributes());
+                SVGTextLayoutEngine lineLayout(characterLayout.layoutAttributes());
                 layoutCharactersInTextBoxes(flowBox, lineLayout);
 
                 characterLayout.beginTextPathLayout(child->renderer(), lineLayout);
@@ -221,21 +219,28 @@ InlineBox* SVGRootInlineBox::closestLeafChildForPosition(const LayoutPoint& poin
 
     return closestLeaf ? closestLeaf : lastLeaf;
 }
-static inline void swapItemsInVector(Vector<float>& firstVector, Vector<float>& lastVector, unsigned first, unsigned last)
-{
-    float temp = firstVector.at(first);
-    firstVector.at(first) = lastVector.at(last);
-    lastVector.at(last) = temp;
-}
 
-static inline void swapItemsInLayoutAttributes(SVGTextLayoutAttributes& firstAttributes, SVGTextLayoutAttributes& lastAttributes, unsigned firstPosition, unsigned lastPosition)
+static inline void swapItemsInLayoutAttributes(SVGTextLayoutAttributes* firstAttributes, SVGTextLayoutAttributes* lastAttributes, unsigned firstPosition, unsigned lastPosition)
 {
-    swapItemsInVector(firstAttributes.xValues(), lastAttributes.xValues(), firstPosition, lastPosition);
-    swapItemsInVector(firstAttributes.yValues(), lastAttributes.yValues(), firstPosition, lastPosition);
-    swapItemsInVector(firstAttributes.dxValues(), lastAttributes.dxValues(), firstPosition, lastPosition);
-    swapItemsInVector(firstAttributes.dyValues(), lastAttributes.dyValues(), firstPosition, lastPosition);
-    swapItemsInVector(firstAttributes.rotateValues(), lastAttributes.rotateValues(), firstPosition, lastPosition);
+    SVGCharacterDataMap::iterator itFirst = firstAttributes->characterDataMap().find(firstPosition + 1);
+    SVGCharacterDataMap::iterator itLast = lastAttributes->characterDataMap().find(lastPosition + 1);
+    bool firstPresent = itFirst != firstAttributes->characterDataMap().end();
+    bool lastPresent = itLast != lastAttributes->characterDataMap().end();
+    if (!firstPresent && !lastPresent)
+        return;
+
+    if (firstPresent && lastPresent) {
+        std::swap(itFirst->second, itLast->second);
+        return;
+    }
+
+    if (firstPresent && !lastPresent) {
+        lastAttributes->characterDataMap().set(lastPosition + 1, itFirst->second);
+        return;
+    }
+
+    // !firstPresent && lastPresent
+    firstAttributes->characterDataMap().set(firstPosition + 1, itLast->second);
 }
 
 static inline void findFirstAndLastAttributesInVector(Vector<SVGTextLayoutAttributes>& attributes, RenderSVGInlineText* firstContext, RenderSVGInlineText* lastContext,
@@ -246,7 +251,7 @@ static inline void findFirstAndLastAttributesInVector(Vector<SVGTextLayoutAttrib
 
     unsigned attributesSize = attributes.size();
     for (unsigned i = 0; i < attributesSize; ++i) {
-        SVGTextLayoutAttributes& current = attributes.at(i);
+        SVGTextLayoutAttributes& current = attributes[i];
         if (!first && firstContext == current.context())
             first = &current;
         if (!last && lastContext == current.context())
@@ -264,7 +269,7 @@ static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Ve
     ASSERT(userData);
     Vector<SVGTextLayoutAttributes>& attributes = *reinterpret_cast<Vector<SVGTextLayoutAttributes>*>(userData);
 
-    // This is a copy of std::reverse(first, last). It additionally assure that the value lists within the InlineBoxes are reordered as well.
+    // This is a copy of std::reverse(first, last). It additionally assures that the metrics map within the renderers belonging to the InlineBoxes are reordered as well.
     while (true)  {
         if (first == last || first == --last)
             return;
@@ -288,14 +293,7 @@ static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Ve
             SVGTextLayoutAttributes* firstAttributes = 0;
             SVGTextLayoutAttributes* lastAttributes = 0;
             findFirstAndLastAttributesInVector(attributes, firstContext, lastContext, firstAttributes, lastAttributes);
-
-            unsigned firstBoxPosition = firstTextBox->start();
-            unsigned firstBoxEnd = firstTextBox->end();
-
-            unsigned lastBoxPosition = lastTextBox->start();
-            unsigned lastBoxEnd = lastTextBox->end();
-            for (; firstBoxPosition <= firstBoxEnd && lastBoxPosition <= lastBoxEnd; ++lastBoxPosition, ++firstBoxPosition)
-                swapItemsInLayoutAttributes(*firstAttributes, *lastAttributes, firstBoxPosition, lastBoxPosition);
+            swapItemsInLayoutAttributes(firstAttributes, lastAttributes, firstTextBox->start(), lastTextBox->start());
         }
 
         InlineBox* temp = *first;
index df2cffe..a92596c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2010-11. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -32,46 +32,10 @@ SVGTextLayoutAttributes::SVGTextLayoutAttributes(RenderSVGInlineText* context)
 {
 }
 
-void SVGTextLayoutAttributes::reserveCapacity(unsigned length)
+void SVGTextLayoutAttributes::clear()
 {
-    m_positioningLists.xValues.reserveCapacity(length);
-    m_positioningLists.yValues.reserveCapacity(length);
-    m_positioningLists.dxValues.reserveCapacity(length);
-    m_positioningLists.dyValues.reserveCapacity(length);
-    m_positioningLists.rotateValues.reserveCapacity(length);
-    m_textMetricsValues.reserveCapacity(length);
-}
-
-void SVGTextLayoutAttributes::PositioningLists::fillWithEmptyValues(unsigned length)
-{
-    xValues.fill(SVGTextLayoutAttributes::emptyValue(), length);
-    yValues.fill(SVGTextLayoutAttributes::emptyValue(), length);
-    dxValues.fill(SVGTextLayoutAttributes::emptyValue(), length);
-    dyValues.fill(SVGTextLayoutAttributes::emptyValue(), length);
-    rotateValues.fill(SVGTextLayoutAttributes::emptyValue(), length);
-}
-
-void SVGTextLayoutAttributes::PositioningLists::appendEmptyValues()
-{
-    xValues.append(SVGTextLayoutAttributes::emptyValue());
-    yValues.append(SVGTextLayoutAttributes::emptyValue());
-    dxValues.append(SVGTextLayoutAttributes::emptyValue());
-    dyValues.append(SVGTextLayoutAttributes::emptyValue());
-    rotateValues.append(SVGTextLayoutAttributes::emptyValue());
-}
-
-static inline float safeValueAtPosition(const Vector<float>& values, unsigned position)
-{
-    return position < values.size() ? values[position] : SVGTextLayoutAttributes::emptyValue();
-}
-
-void SVGTextLayoutAttributes::PositioningLists::appendValuesFromPosition(const PositioningLists& source, unsigned position)
-{
-    xValues.append(safeValueAtPosition(source.xValues, position));
-    yValues.append(safeValueAtPosition(source.yValues, position));
-    dxValues.append(safeValueAtPosition(source.dxValues, position));
-    dyValues.append(safeValueAtPosition(source.dyValues, position));
-    rotateValues.append(safeValueAtPosition(source.rotateValues, position));
+    m_characterDataMap.clear();
+    m_textMetricsValues.clear();
 }
 
 float SVGTextLayoutAttributes::emptyValue()
@@ -80,55 +44,33 @@ float SVGTextLayoutAttributes::emptyValue()
     return s_emptyValue;
 }
 
-static inline void dumpLayoutVector(const Vector<float>& values)
+static inline void dumpSVGCharacterDataMapValue(const char* identifier, float value, bool appendSpace = true)
 {
-    if (values.isEmpty()) {
-        fprintf(stderr, "empty");
+    if (value == SVGTextLayoutAttributes::emptyValue()) {
+        fprintf(stderr, "%s=x", identifier);
+        if (appendSpace)
+            fprintf(stderr, " ");
         return;
     }
-
-    unsigned size = values.size();
-    for (unsigned i = 0; i < size; ++i) {
-        float value = values.at(i);
-        if (value == SVGTextLayoutAttributes::emptyValue())
-            fprintf(stderr, "x ");
-        else
-            fprintf(stderr, "%lf ", value);
-    }
+    fprintf(stderr, "%s=%lf", identifier, value);
+    if (appendSpace)
+        fprintf(stderr, " ");
 }
 
 void SVGTextLayoutAttributes::dump() const
 {
     fprintf(stderr, "context: %p\n", m_context);
-
-    fprintf(stderr, "x values: ");
-    dumpLayoutVector(m_positioningLists.xValues);
-    fprintf(stderr, "\n");
-
-    fprintf(stderr, "y values: ");
-    dumpLayoutVector(m_positioningLists.yValues);
-    fprintf(stderr, "\n");
-
-    fprintf(stderr, "dx values: ");
-    dumpLayoutVector(m_positioningLists.dxValues);
-    fprintf(stderr, "\n");
-
-    fprintf(stderr, "dy values: ");
-    dumpLayoutVector(m_positioningLists.dyValues);
-    fprintf(stderr, "\n");
-
-    fprintf(stderr, "rotate values: ");
-    dumpLayoutVector(m_positioningLists.rotateValues);
-    fprintf(stderr, "\n");
-
-    fprintf(stderr, "character data values:\n");
-    unsigned textMetricsSize = m_textMetricsValues.size();
-    for (unsigned i = 0; i < textMetricsSize; ++i) {
-        const SVGTextMetrics& metrics = m_textMetricsValues.at(i);
-        fprintf(stderr, "| {length=%i, glyphName='%s', unicodeString='%s', width=%lf, height=%lf}\n",
-                metrics.length(), metrics.glyph().name.utf8().data(), metrics.glyph().unicodeString.utf8().data(), metrics.width(), metrics.height());
+    const SVGCharacterDataMap::const_iterator end = m_characterDataMap.end();
+    for (SVGCharacterDataMap::const_iterator it = m_characterDataMap.begin(); it != end; ++it) {
+        const SVGCharacterData& data = it->second;
+        fprintf(stderr, " ---> pos=%i, data={", it->first);
+        dumpSVGCharacterDataMapValue("x", data.x);
+        dumpSVGCharacterDataMapValue("y", data.y);
+        dumpSVGCharacterDataMapValue("dx", data.dx);
+        dumpSVGCharacterDataMapValue("dy", data.dy);
+        dumpSVGCharacterDataMapValue("rotate", data.rotate, false);
+        fprintf(stderr, "}\n");
     }
-    fprintf(stderr, "\n");
 }
 
 }
index 14e6f28..eb67cb8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -43,54 +43,22 @@ typedef HashMap<unsigned, SVGCharacterData> SVGCharacterDataMap;
 
 class SVGTextLayoutAttributes {
 public:
-    // FIXME: This struct will be replaced by SVGCharacterData in a follow up patch.
-    struct PositioningLists {
-        void fillWithEmptyValues(unsigned length);
-        void appendEmptyValues();
-        void appendValuesFromPosition(const PositioningLists&, unsigned position);
+    SVGTextLayoutAttributes(RenderSVGInlineText*);
 
-        Vector<float> xValues;
-        Vector<float> yValues;
-        Vector<float> dxValues;
-        Vector<float> dyValues;
-        Vector<float> rotateValues;
-    };
-
-    SVGTextLayoutAttributes(RenderSVGInlineText* context = 0);
-
-    // FIXME: Still a no-op, we'll need this once we switch to SVGCharacterDataMap.
-    void clear() { }
-    void reserveCapacity(unsigned length);
+    void clear();
     void dump() const;
-
     static float emptyValue();
 
     RenderSVGInlineText* context() const { return m_context; }
-
-    PositioningLists& positioningLists() { return m_positioningLists; }
-    const PositioningLists& positioningLists() const { return m_positioningLists; }
-
-    Vector<float>& xValues() { return m_positioningLists.xValues; }
-    const Vector<float>& xValues() const { return m_positioningLists.xValues; }
-
-    Vector<float>& yValues() { return m_positioningLists.yValues; }
-    const Vector<float>& yValues() const { return m_positioningLists.yValues; }
-
-    Vector<float>& dxValues() { return m_positioningLists.dxValues; }
-    const Vector<float>& dxValues() const { return m_positioningLists.dxValues; }
-
-    Vector<float>& dyValues() { return m_positioningLists.dyValues; }
-    const Vector<float>& dyValues() const { return m_positioningLists.dyValues; }
-
-    Vector<float>& rotateValues() { return m_positioningLists.rotateValues; }
-    const Vector<float>& rotateValues() const { return m_positioningLists.rotateValues; }
+    
+    SVGCharacterDataMap& characterDataMap() { return m_characterDataMap; }
+    const SVGCharacterDataMap& characterDataMap() const { return m_characterDataMap; }
 
     Vector<SVGTextMetrics>& textMetricsValues() { return m_textMetricsValues; }
-    const Vector<SVGTextMetrics>& textMetricsValues() const { return m_textMetricsValues; }
 
 private:
     RenderSVGInlineText* m_context;
-    PositioningLists m_positioningLists;
+    SVGCharacterDataMap m_characterDataMap;
     Vector<SVGTextMetrics> m_textMetricsValues;
 };
 
index 243a5ae..4797edc 100644 (file)
 namespace WebCore {
 
 SVGTextLayoutAttributesBuilder::SVGTextLayoutAttributesBuilder()
+    : m_textLength(0)
 {
 }
 
-void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextSubtree(RenderSVGText* textRoot)
+void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextRenderer(RenderSVGInlineText* text)
 {
-    ASSERT(textRoot);
-
-    // We always clear our current attribute as we don't want to keep any stale ones that could survive DOM modification.
-    Vector<SVGTextLayoutAttributes>& allAttributes = textRoot->layoutAttributes();
-    allAttributes.clear();
-
-     // Build list of x/y/dx/dy/rotate values for each subtree element that may define these values (tspan/textPath etc).
-    unsigned atCharacter = 0;
-    UChar lastCharacter = '\0';
-    collectTextPositioningElements(textRoot, atCharacter, lastCharacter);
+    ASSERT(text);
 
-    if (!atCharacter)
+    RenderSVGText* textRoot = RenderSVGText::locateRenderSVGTextAncestor(text);
+    if (!textRoot)
         return;
 
-    // Collect x/y/dx/dy/rotate values for each character, stored in the m_positioningLists.xValues()/etc. lists.
-    buildLayoutAttributesForAllCharacters(textRoot, atCharacter);
+    if (!buildLayoutAttributesIfNeeded(textRoot))
+        return;
 
-    // Propagate layout attributes to each RenderSVGInlineText object, and the whole list to the RenderSVGText root.
-    atCharacter = 0;
-    lastCharacter = '\0';
-    propagateLayoutAttributes(textRoot, allAttributes, atCharacter, lastCharacter);
+    m_metricsBuilder.buildMetricsAndLayoutAttributes(textRoot, text, m_characterDataMap);
 }
 
-static inline void extractFloatValuesFromSVGLengthList(SVGElement* contextElement, const SVGLengthList& list, Vector<float>& floatValues, unsigned textContentLength)
+void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForWholeTree(RenderSVGText* textRoot)
 {
-    ASSERT(contextElement);
+    ASSERT(textRoot);
 
-    unsigned length = list.size();
-    if (length > textContentLength)
-        length = textContentLength;
-    floatValues.reserveCapacity(length);
+    if (!buildLayoutAttributesIfNeeded(textRoot))
+        return;
 
-    SVGLengthContext lengthContext(contextElement);
-    for (unsigned i = 0; i < length; ++i) {
-        const SVGLength& length = list.at(i);
-        floatValues.append(length.value(lengthContext));
-    }
+    m_metricsBuilder.buildMetricsAndLayoutAttributes(textRoot, 0, m_characterDataMap);
 }
 
-static inline void extractFloatValuesFromSVGNumberList(const SVGNumberList& list, Vector<float>& floatValues, unsigned textContentLength)
+void SVGTextLayoutAttributesBuilder::rebuildMetricsForTextRenderer(RenderSVGInlineText* text)
 {
-    unsigned length = list.size();
-    if (length > textContentLength)
-        length = textContentLength;
-    floatValues.reserveCapacity(length);
-
-    for (unsigned i = 0; i < length; ++i)
-        floatValues.append(list.at(i));
+    ASSERT(text);
+    m_metricsBuilder.measureTextRenderer(text);
 }
 
-
-static inline bool characterIsSpace(const UChar& character)
+void SVGTextLayoutAttributesBuilder::rebuildMetricsForWholeTree(RenderSVGText* textRoot)
 {
-    return character == ' ';
-}
+    ASSERT(textRoot);
+    Vector<SVGTextLayoutAttributes>& layoutAttributes = textRoot->layoutAttributes();
 
-static inline bool characterIsSpaceOrNull(const UChar& character)
-{
-    return character == ' ' || character == '\0';
+    size_t layoutAttributesSize = layoutAttributes.size();
+    for (size_t i = 0; i < layoutAttributesSize; ++i)
+        m_metricsBuilder.measureTextRenderer(layoutAttributes[i].context());
 }
 
-static inline bool shouldPreserveAllWhiteSpace(RenderStyle* style)
+bool SVGTextLayoutAttributesBuilder::buildLayoutAttributesIfNeeded(RenderSVGText* textRoot)
 {
-    ASSERT(style);
-    return style->whiteSpace() == PRE;
+    ASSERT(textRoot);
+    if (!m_textPositions.isEmpty())
+        return m_textLength;
+
+    m_textLength = 0;
+    const UChar* lastCharacter = 0;
+    collectTextPositioningElements(textRoot, lastCharacter);
+
+    m_characterDataMap.clear();
+    if (!m_textLength)
+        return false;
+
+    buildLayoutAttributes(textRoot);
+    return true;
 }
-static inline void processRenderSVGInlineText(RenderSVGInlineText* text, unsigned& atCharacter, UChar& lastCharacter)
+
+static inline void processRenderSVGInlineText(RenderSVGInlineText* text, unsigned& atCharacter, const UChar*& lastCharacter)
 {
-    if (shouldPreserveAllWhiteSpace(text->style())) {
+    if (text->style()->whiteSpace() == PRE) {
         atCharacter += text->textLength();
         return;
     }
@@ -114,8 +104,8 @@ static inline void processRenderSVGInlineText(RenderSVGInlineText* text, unsigne
     const UChar* characters = text->characters();
     unsigned textLength = text->textLength();    
     for (unsigned textPosition = 0; textPosition < textLength; ++textPosition) {
-        const UChar& currentCharacter = characters[textPosition];
-        if (characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter))
+        const UChar* currentCharacter = characters + textPosition;
+        if (*currentCharacter == ' ' && (!lastCharacter || *lastCharacter == ' '))
             continue;
 
         lastCharacter = currentCharacter;
@@ -123,13 +113,13 @@ static inline void processRenderSVGInlineText(RenderSVGInlineText* text, unsigne
     }
 }
 
-void SVGTextLayoutAttributesBuilder::collectTextPositioningElements(RenderObject* start, unsigned& atCharacter, UChar& lastCharacter)
+void SVGTextLayoutAttributesBuilder::collectTextPositioningElements(RenderObject* start, const UChar*& lastCharacter)
 {
     ASSERT(!start->isSVGText() || m_textPositions.isEmpty());
 
     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { 
         if (child->isSVGInlineText()) {
-            processRenderSVGInlineText(toRenderSVGInlineText(child), atCharacter, lastCharacter);
+            processRenderSVGInlineText(toRenderSVGInlineText(child), m_textLength, lastCharacter);
             continue;
         }
 
@@ -139,9 +129,9 @@ void SVGTextLayoutAttributesBuilder::collectTextPositioningElements(RenderObject
         SVGTextPositioningElement* element = SVGTextPositioningElement::elementFromRenderer(child);
         unsigned atPosition = m_textPositions.size();
         if (element)
-            m_textPositions.append(TextPosition(element, atCharacter));
+            m_textPositions.append(TextPosition(element, m_textLength));
 
-        collectTextPositioningElements(child, atCharacter, lastCharacter);
+        collectTextPositioningElements(child, lastCharacter);
 
         if (!element)
             continue;
@@ -149,166 +139,116 @@ void SVGTextLayoutAttributesBuilder::collectTextPositioningElements(RenderObject
         // Update text position, after we're back from recursion.
         TextPosition& position = m_textPositions[atPosition];
         ASSERT(!position.length);
-        position.length = atCharacter - position.start;
+        position.length = m_textLength - position.start;
     }
 }
 
-void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForAllCharacters(RenderSVGText* textRoot, unsigned textLength)
+void SVGTextLayoutAttributesBuilder::buildLayoutAttributes(RenderSVGText* textRoot)
 {
-    ASSERT(textLength);
+    ASSERT(m_textLength);
 
     SVGTextPositioningElement* outermostTextElement = SVGTextPositioningElement::elementFromRenderer(textRoot);
     ASSERT(outermostTextElement);
 
-    // Fill the lists with the special emptyValue marker.
-    m_positioningLists.fillWithEmptyValues(textLength);
-
-    // Grab outermost <text> element value lists and insert them in the m_positioningLists.
-    TextPosition wholeTextPosition(outermostTextElement, 0, textLength);
-    fillAttributesAtPosition(wholeTextPosition);
+    // Grab outermost <text> element value lists and insert them in the character data map.
+    TextPosition wholeTextPosition(outermostTextElement, 0, m_textLength);
+    fillCharacterDataMap(wholeTextPosition);
 
     // Handle x/y default attributes.
-    float& xFirst = m_positioningLists.xValues.first();
-    if (xFirst == SVGTextLayoutAttributes::emptyValue())
-        xFirst = 0;
-
-    float& yFirst = m_positioningLists.yValues.first();
-    if (yFirst == SVGTextLayoutAttributes::emptyValue())
-        yFirst = 0;
+    SVGCharacterDataMap::iterator it = m_characterDataMap.find(1);
+    if (it == m_characterDataMap.end()) {
+        SVGCharacterData data;
+        data.x = 0;
+        data.y = 0;
+        m_characterDataMap.set(1, data);
+    } else {
+        SVGCharacterData& data = it->second;
+        if (data.x == SVGTextLayoutAttributes::emptyValue())
+            data.x = 0;
+        if (data.y == SVGTextLayoutAttributes::emptyValue())
+            data.y = 0;
+    }
 
-    // Fill m_positioningLists using child text positioning elements in top-down order. 
+    // Fill character data map using child text positioning elements in top-down order. 
     unsigned size = m_textPositions.size();
     for (unsigned i = 0; i < size; ++i)
-        fillAttributesAtPosition(m_textPositions[i]);
-
-    // Now m_positioningLists.contains a x/y/dx/dy/rotate value for each character in the <text> subtree.
-}
-
-void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* start, Vector<SVGTextLayoutAttributes>& allAttributes, unsigned& atCharacter, UChar& lastCharacter) const
-{
-    for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { 
-        if (child->isSVGInlineText()) {
-            RenderSVGInlineText* text = toRenderSVGInlineText(child);
-            const UChar* characters = text->characters();
-            unsigned textLength = text->textLength();
-            bool preserveWhiteSpace = shouldPreserveAllWhiteSpace(text->style());
-
-            SVGTextLayoutAttributes attributes(text);
-            attributes.reserveCapacity(textLength);
-    
-            unsigned valueListPosition = atCharacter;
-            unsigned metricsLength = 1;
-            SVGTextMetrics lastMetrics(SVGTextMetrics::SkippedSpaceMetrics);
-
-            for (unsigned textPosition = 0; textPosition < textLength; textPosition += metricsLength) {
-                const UChar& currentCharacter = characters[textPosition];
-
-                SVGTextMetrics startToCurrentMetrics;
-                SVGTextMetrics currentMetrics;
-                unsigned valueListAdvance = 0;
-
-                if (U16_IS_LEAD(currentCharacter) && (textPosition + 1) < textLength && U16_IS_TRAIL(characters[textPosition + 1])) {
-                    // Handle surrogate pairs.
-                    startToCurrentMetrics = SVGTextMetrics::measureCharacterRange(text, 0, textPosition + 2);
-                    currentMetrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 2);
-                    metricsLength = currentMetrics.length();
-                    valueListAdvance = 1;
-                } else {
-                    // Handle BMP characters.
-                    startToCurrentMetrics = SVGTextMetrics::measureCharacterRange(text, 0, textPosition + 1);
-                    currentMetrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1);
-                    metricsLength = currentMetrics.length();
-                    valueListAdvance = metricsLength;
-                }
-
-                if (!metricsLength)
-                    break;
-                
-                // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken
-                // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping.
-                // So whenever runWidthAdvance != currentMetrics.width(), we are processing a text run whose length is
-                // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated.
-                float runWidthAdvance = startToCurrentMetrics.width() - lastMetrics.width();
-                if (runWidthAdvance != currentMetrics.width())
-                    currentMetrics.setWidth(runWidthAdvance);
-
-                lastMetrics = startToCurrentMetrics;
-
-                if (!preserveWhiteSpace && characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) {
-                    attributes.positioningLists().appendEmptyValues();
-                    attributes.textMetricsValues().append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics));
-                    continue;
-                }
-
-                SVGTextLayoutAttributes::PositioningLists& positioningLists = attributes.positioningLists();
-                positioningLists.appendValuesFromPosition(m_positioningLists, valueListPosition);
-                attributes.textMetricsValues().append(currentMetrics);
-
-                // Pad x/y/dx/dy/rotate value lists with empty values, if the metrics span more than one character.
-                if (metricsLength > 1) {
-                    for (unsigned i = 0; i < metricsLength - 1; ++i)
-                        positioningLists.appendEmptyValues();
-                }
-
-                lastCharacter = currentCharacter;
-                valueListPosition += valueListAdvance;
-            }
+        fillCharacterDataMap(m_textPositions[i]);
 
 #if DUMP_TEXT_LAYOUT_ATTRIBUTES > 0
-            fprintf(stderr, "\nDumping layout attributes for RenderSVGInlineText, renderer=%p, node=%p (atCharacter: %i)\n", text, text->node(), atCharacter);
-            fprintf(stderr, "BiDi properties: unicode-bidi=%i, block direction=%i\n", text->style()->unicodeBidi(), text->style()->direction());
-            attributes.dump();
+    fprintf(stderr, "\nDumping ALL layout attributes for RenderSVGText, renderer=%p, node=%p (m_textLength: %i)\n", textRoot, textRoot->node(), m_textLength);
+    m_characterDataMap.dump();
 #endif
-
-            text->storeLayoutAttributes(attributes);
-            allAttributes.append(attributes);
-            atCharacter = valueListPosition;
-            continue;
-        }
-
-        if (!child->isSVGInline())
-            continue;
-
-        propagateLayoutAttributes(child, allAttributes, atCharacter, lastCharacter);
-    }
 }
 
-static inline void fillListAtPosition(Vector<float>& allValues, Vector<float>& values, unsigned start)
+static inline void updateCharacterData(unsigned i, float& lastRotation, SVGCharacterData& data, const SVGLengthContext& lengthContext, const SVGLengthList* xList, const SVGLengthList* yList, const SVGLengthList* dxList, const SVGLengthList* dyList, const SVGNumberList* rotateList)
 {
-    unsigned valuesSize = values.size();
-    for (unsigned i = 0; i < valuesSize; ++i)
-        allValues[start + i] = values[i];
+    if (xList)
+        data.x = xList->at(i).value(lengthContext);
+    if (yList)
+        data.y = yList->at(i).value(lengthContext);
+    if (dxList)
+        data.dx = dxList->at(i).value(lengthContext);
+    if (dyList)
+        data.dy = dyList->at(i).value(lengthContext);
+    if (rotateList) {
+        data.rotate = rotateList->at(i);
+        lastRotation = data.rotate;
+    }
 }
 
-void SVGTextLayoutAttributesBuilder::fillAttributesAtPosition(const TextPosition& position)
+void SVGTextLayoutAttributesBuilder::fillCharacterDataMap(const TextPosition& position)
 {
-    Vector<float> values;
-    extractFloatValuesFromSVGLengthList(position.element, position.element->x(), values, position.length);
-    fillListAtPosition(m_positioningLists.xValues, values, position.start);
-
-    values.clear();
-    extractFloatValuesFromSVGLengthList(position.element, position.element->y(), values, position.length);
-    fillListAtPosition(m_positioningLists.yValues, values, position.start);
-
-    values.clear();
-    extractFloatValuesFromSVGLengthList(position.element, position.element->dx(), values, position.length);
-    fillListAtPosition(m_positioningLists.dxValues, values, position.start);
+    const SVGLengthList& xList = position.element->x();
+    const SVGLengthList& yList = position.element->y();
+    const SVGLengthList& dxList = position.element->dx();
+    const SVGLengthList& dyList = position.element->dy();
+    const SVGNumberList& rotateList = position.element->rotate();
+
+    unsigned xListSize = xList.size();
+    unsigned yListSize = yList.size();
+    unsigned dxListSize = dxList.size();
+    unsigned dyListSize = dyList.size();
+    unsigned rotateListSize = rotateList.size();
+    if (!xListSize && !yListSize && !dxListSize && !dyListSize && !rotateListSize)
+        return;
 
-    values.clear();
-    extractFloatValuesFromSVGLengthList(position.element, position.element->dy(), values, position.length);
-    fillListAtPosition(m_positioningLists.dyValues, values, position.start);
+    float lastRotation = SVGTextLayoutAttributes::emptyValue();
+    SVGLengthContext lengthContext(position.element);
+    for (unsigned i = 0; i < position.length; ++i) {
+        const SVGLengthList* xListPtr = i < xListSize ? &xList : 0;
+        const SVGLengthList* yListPtr = i < yListSize ? &yList : 0;
+        const SVGLengthList* dxListPtr = i < dxListSize ? &dxList : 0;
+        const SVGLengthList* dyListPtr = i < dyListSize ? &dyList : 0;
+        const SVGNumberList* rotateListPtr = i < rotateListSize ? &rotateList : 0;
+        if (!xListPtr && !yListPtr && !dxListPtr && !dyListPtr && !rotateListPtr)
+            break;
+
+        SVGCharacterDataMap::iterator it = m_characterDataMap.find(position.start + i + 1);
+        if (it == m_characterDataMap.end()) {
+            SVGCharacterData data;
+            updateCharacterData(i, lastRotation, data, lengthContext, xListPtr, yListPtr, dxListPtr, dyListPtr, rotateListPtr);
+            m_characterDataMap.set(position.start + i + 1, data);
+            continue;
+        }
 
-    values.clear();
-    extractFloatValuesFromSVGNumberList(position.element->rotate(), values, position.length);
-    fillListAtPosition(m_positioningLists.rotateValues, values, position.start);
+        updateCharacterData(i, lastRotation, it->second, lengthContext, xListPtr, yListPtr, dxListPtr, dyListPtr, rotateListPtr);
+    }
 
     // The last rotation value always spans the whole scope.
-    if (values.isEmpty())
+    if (lastRotation == SVGTextLayoutAttributes::emptyValue())
         return;
 
-    float lastValue = values.last();
-    for (unsigned i = values.size(); i < position.length; ++i)
-        m_positioningLists.rotateValues[position.start + i] = lastValue;
+    for (unsigned i = rotateList.size(); i < position.length; ++i) {
+        SVGCharacterDataMap::iterator it = m_characterDataMap.find(position.start + i + 1);
+        if (it == m_characterDataMap.end()) {
+            SVGCharacterData data;
+            data.rotate = lastRotation;
+            m_characterDataMap.set(position.start + i + 1, data);
+            continue;
+        }
+
+        it->second.rotate = lastRotation;
+    }
 }
 
 }
index a5995c0..45fad24 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
 #define SVGTextLayoutAttributesBuilder_h
 
 #if ENABLE(SVG)
-#include "SVGTextLayoutAttributes.h"
+#include "SVGTextMetricsBuilder.h"
 #include <wtf/Vector.h>
 
 namespace WebCore {
 
 class RenderObject;
+class RenderSVGInlineText;
 class RenderSVGText;
 class SVGTextPositioningElement;
 
 // SVGTextLayoutAttributesBuilder performs the first layout phase for SVG text.
 //
-// It extracts the x/y/dx/dy/rotate values from the SVGTextPositioningElements in the DOM,
-// measures all characters in the RenderSVGText subtree and extracts kerning/ligature information.
+// It extracts the x/y/dx/dy/rotate values from the SVGTextPositioningElements in the DOM.
 // These values are propagated to the corresponding RenderSVGInlineText renderers.
 // The first layout phase only extracts the relevant information needed in RenderBlockLineLayout
 // to create the InlineBox tree based on text chunk boundaries & BiDi information.
@@ -43,7 +43,14 @@ class SVGTextLayoutAttributesBuilder {
     WTF_MAKE_NONCOPYABLE(SVGTextLayoutAttributesBuilder);
 public:
     SVGTextLayoutAttributesBuilder();
-    void buildLayoutAttributesForTextSubtree(RenderSVGText*);
+    void buildLayoutAttributesForWholeTree(RenderSVGText*);
+    void buildLayoutAttributesForTextRenderer(RenderSVGInlineText*);
+
+    void rebuildMetricsForWholeTree(RenderSVGText*);
+    void rebuildMetricsForTextRenderer(RenderSVGInlineText*);
+
+    // Invoked whenever the underlying DOM tree changes, so that m_textPositions is rebuild.
+    void clearTextPositioningElements() { m_textPositions.clear(); }
 
 private:
     struct TextPosition {
@@ -59,14 +66,16 @@ private:
         unsigned length;
     };
 
-    void collectTextPositioningElements(RenderObject*, unsigned& atCharacter, UChar& lastCharacter);
-    void buildLayoutAttributesForAllCharacters(RenderSVGText*, unsigned textLength);
-    void propagateLayoutAttributes(RenderObject*, Vector<SVGTextLayoutAttributes>& allAttributes, unsigned& atCharacter, UChar& lastCharacter) const;
-    void fillAttributesAtPosition(const TextPosition&);
+    bool buildLayoutAttributesIfNeeded(RenderSVGText*);
+    void collectTextPositioningElements(RenderObject*, const UChar*& lastCharacter);
+    void buildLayoutAttributes(RenderSVGText*);
+    void fillCharacterDataMap(const TextPosition&);
 
 private:
+    unsigned m_textLength;
     Vector<TextPosition> m_textPositions;
-    SVGTextLayoutAttributes::PositioningLists m_positioningLists;
+    SVGCharacterDataMap m_characterDataMap;
+    SVGTextMetricsBuilder m_metricsBuilder;
 };
 
 } // namespace WebCore
index f2de892..f4a2880 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -84,25 +84,16 @@ void SVGTextLayoutEngine::updateCurrentTextPosition(float x, float y, float glyp
     }
 }
 
-void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(Vector<float>& dxValues, Vector<float>& dyValues)
+void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(float dx, float dy)
 {
     // Update relative positioning information.
-    if (dxValues.isEmpty() && dyValues.isEmpty())
+    if (dx == SVGTextLayoutAttributes::emptyValue() && dy == SVGTextLayoutAttributes::emptyValue())
         return;
 
-    float dx = 0;
-    if (!dxValues.isEmpty()) {
-        float& dxCurrent = dxValues.at(m_logicalCharacterOffset);
-        if (dxCurrent != SVGTextLayoutAttributes::emptyValue())
-            dx = dxCurrent;
-    }
-
-    float dy = 0;
-    if (!dyValues.isEmpty()) {
-        float& dyCurrent = dyValues.at(m_logicalCharacterOffset);
-        if (dyCurrent != SVGTextLayoutAttributes::emptyValue())
-            dy = dyCurrent;
-    }
+    if (dx == SVGTextLayoutAttributes::emptyValue())
+        dx = 0;
+    if (dy == SVGTextLayoutAttributes::emptyValue())
+        dy = 0;
 
     if (m_inPathLayout) {
         if (m_isVerticalText) {
@@ -344,7 +335,7 @@ bool SVGTextLayoutEngine::currentLogicalCharacterAttributes(SVGTextLayoutAttribu
         return false;
 
     logicalAttributes = m_layoutAttributes.first();
-    if (m_logicalCharacterOffset != logicalAttributes.xValues().size())
+    if (m_logicalCharacterOffset != logicalAttributes.context()->textLength())
         return true;
 
     m_layoutAttributes.remove(0);
@@ -472,7 +463,7 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend
             continue;
         }
 
-        SVGTextLayoutAttributes logicalAttributes;
+        SVGTextLayoutAttributes logicalAttributes(0);
         if (!currentLogicalCharacterAttributes(logicalAttributes))
             break;
 
@@ -480,26 +471,21 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend
         if (!currentLogicalCharacterMetrics(logicalAttributes, logicalMetrics))
             break;
 
-        Vector<float>& xValues = logicalAttributes.xValues();
-        Vector<float>& yValues = logicalAttributes.yValues();
-        Vector<float>& dxValues = logicalAttributes.dxValues();
-        Vector<float>& dyValues = logicalAttributes.dyValues();
-        Vector<float>& rotateValues = logicalAttributes.rotateValues();
+        SVGCharacterDataMap& characterDataMap = logicalAttributes.characterDataMap();
+        SVGCharacterData data;
+        SVGCharacterDataMap::iterator it = characterDataMap.find(m_logicalCharacterOffset + 1);
+        if (it != characterDataMap.end())
+            data = it->second;
 
-        float x = xValues.at(m_logicalCharacterOffset);
-        float y = yValues.at(m_logicalCharacterOffset);
+        float x = data.x;
+        float y = data.y;
 
         // When we've advanced to the box start offset, determine using the original x/y values,
         // whether this character starts a new text chunk, before doing any further processing.
         if (m_visualCharacterOffset == textBox->start())
             textBox->setStartsNewTextChunk(logicalAttributes.context()->characterStartsNewTextChunk(m_logicalCharacterOffset));
 
-        float angle = 0;
-        if (!rotateValues.isEmpty()) {
-            float newAngle = rotateValues.at(m_logicalCharacterOffset);
-            if (newAngle != SVGTextLayoutAttributes::emptyValue())
-                angle = newAngle;
-        }
+        float angle = data.rotate == SVGTextLayoutAttributes::emptyValue() ? 0 : data.rotate;
 
         // Calculate glyph orientation angle.
         const UChar* currentCharacter = characters + m_visualCharacterOffset;
@@ -514,7 +500,7 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend
         updateCharacerPositionIfNeeded(x, y);
 
         // Apply dx/dy value adjustments to current text position, if needed.
-        updateRelativePositionAdjustmentsIfNeeded(dxValues, dyValues);
+        updateRelativePositionAdjustmentsIfNeeded(data.dx, data.dy);
 
         // Calculate SVG Fonts kerning, if needed.
         float kerning = spacingLayout.calculateSVGKerning(m_isVerticalText, visualMetrics.glyph());
index 22dd59b..0e32101 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -57,10 +57,12 @@ public:
     void layoutInlineTextBox(SVGInlineTextBox*);
     void finishLayout();
 
+    Vector<SVGTextLayoutAttributes>& layoutAttributes() { return m_layoutAttributes; }
+
 private:
     void updateCharacerPositionIfNeeded(float& x, float& y);
     void updateCurrentTextPosition(float x, float y, float glyphAdvance);
-    void updateRelativePositionAdjustmentsIfNeeded(Vector<float>& dxValues, Vector<float>& dyValues);
+    void updateRelativePositionAdjustmentsIfNeeded(float dx, float dy);
 
     void recordTextFragment(SVGInlineTextBox*, Vector<SVGTextMetrics>& textMetricValues);
     bool parentDefinesTextLength(RenderObject*) const;
index 2fe445e..be758f2 100644 (file)
@@ -25,7 +25,6 @@
 #include "RenderSVGInlineText.h"
 #include "RenderSVGText.h"
 #include "SVGTextRunRenderingContext.h"
-#include "WidthIterator.h"
 
 namespace WebCore {
 
@@ -52,6 +51,8 @@ bool SVGTextMetricsBuilder::advance()
 #if PLATFORM(QT)
     advanceComplexText();
 #else
+    // FIXME: Enabling the simple code path, affects some layout test results, so this will be landed seperated.
+    m_isComplexText = true;
     if (m_isComplexText)
         advanceComplexText();
     else
@@ -91,7 +92,9 @@ void SVGTextMetricsBuilder::advanceComplexText()
     unsigned metricsLength = currentCharacterStartsSurrogatePair() ? 2 : 1;
     m_currentMetrics = SVGTextMetrics::measureCharacterRange(m_text, m_textPosition, metricsLength);
     m_complexStartToCurrentMetrics = SVGTextMetrics::measureCharacterRange(m_text, 0, m_textPosition + metricsLength);
-    ASSERT(m_currentMetrics.length() == metricsLength);
+
+    // FIXME: Re-enable this assertion, once SVG Fonts stop using this code path.
+    // ASSERT(m_currentMetrics.length() == metricsLength);
 
     // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken
     // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping.
@@ -170,11 +173,8 @@ void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText* text, Measu
         if (data->processRenderer) {
             if (data->allCharactersMap) {
                 const SVGCharacterDataMap::const_iterator it = data->allCharactersMap->find(data->valueListPosition + m_textPosition - data->skippedCharacters + 1);
-                if (it != data->allCharactersMap->end()) {
-                    // FIXME: Yes this is nonsense for now. This will use attributes->characterDataMap(), as soon as its available, in a follow-up commit.
-                    SVGCharacterDataMap map;
-                    map.set(m_textPosition + 1, it->second);
-                }
+                if (it != data->allCharactersMap->end())
+                    attributes->characterDataMap().set(m_textPosition + 1, it->second);
             }
             textMetricsValues->append(m_currentMetrics);
         }
index 1f5b217..3ce5e9f 100644 (file)
@@ -24,6 +24,7 @@
 #include "SVGTextLayoutAttributes.h"
 #include "SVGTextMetrics.h"
 #include "TextRun.h"
+#include "WidthIterator.h"
 #include <wtf/Vector.h>
 
 namespace WebCore {
@@ -32,7 +33,6 @@ class RenderObject;
 class RenderSVGInlineText;
 class RenderSVGText;
 struct MeasureTextData;
-struct WidthIterator;
 
 class SVGTextMetricsBuilder {
     WTF_MAKE_NONCOPYABLE(SVGTextMetricsBuilder);
index c141c11..d3abc56 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -158,10 +158,8 @@ bool SVGTextQuery::mapStartEndPositionsIntoFragmentCoordinates(Data* queryData,
 
 void SVGTextQuery::modifyStartEndPositionsRespectingLigatures(Data* queryData, int& startPosition, int& endPosition) const
 {
-    const SVGTextLayoutAttributes& layoutAttributes = queryData->textRenderer->layoutAttributes();
-    const Vector<float>& xValues = layoutAttributes.xValues();
-    const Vector<SVGTextMetrics>& textMetricsValues = layoutAttributes.textMetricsValues();
-
+    SVGTextLayoutAttributes& layoutAttributes = queryData->textRenderer->layoutAttributes();
+    Vector<SVGTextMetrics>& textMetricsValues = layoutAttributes.textMetricsValues();
     unsigned boxStart = queryData->textBox->start();
     unsigned boxLength = queryData->textBox->len();
 
@@ -169,14 +167,14 @@ void SVGTextQuery::modifyStartEndPositionsRespectingLigatures(Data* queryData, i
     unsigned textMetricsSize = textMetricsValues.size();
 
     unsigned positionOffset = 0;
-    unsigned positionSize = xValues.size();
+    unsigned positionSize = layoutAttributes.context()->textLength();
 
     bool alterStartPosition = true;
     bool alterEndPosition = true;
 
     int lastPositionOffset = -1;
     for (; textMetricsOffset < textMetricsSize && positionOffset < positionSize; ++textMetricsOffset) {
-        const SVGTextMetrics& metrics = textMetricsValues.at(textMetricsOffset);
+        SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset];
 
         // Advance to text box start location.
         if (positionOffset < boxStart) {