Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(707)

Side by Side Diff: third_party/WebKit/Source/core/layout/svg/SVGTextMetricsBuilder.cpp

Issue 1773403002: Update SVG text layout to use shaped glyph data & go fast (O(n^2)->O(n)) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@1773353003
Patch Set: Minor cleanup of comments and tests Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved. 2 * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved.
3 * 3 *
4 * This library is free software; you can redistribute it and/or 4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public 5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either 6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version. 7 * version 2 of the License, or (at your option) any later version.
8 * 8 *
9 * This library is distributed in the hope that it will be useful, 9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details. 12 * Library General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU Library General Public License 14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to 15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA. 17 * Boston, MA 02110-1301, USA.
18 */ 18 */
19 19
20 #include "core/layout/svg/SVGTextMetricsBuilder.h" 20 #include "core/layout/svg/SVGTextMetricsBuilder.h"
21 21
22 #include "core/layout/api/LineLayoutSVGInlineText.h" 22 #include "core/layout/api/LineLayoutSVGInlineText.h"
23 #include "core/layout/svg/LayoutSVGInline.h" 23 #include "core/layout/svg/LayoutSVGInline.h"
24 #include "core/layout/svg/LayoutSVGInlineText.h" 24 #include "core/layout/svg/LayoutSVGInlineText.h"
25 #include "core/layout/svg/LayoutSVGText.h" 25 #include "core/layout/svg/LayoutSVGText.h"
26 #include "core/layout/svg/SVGTextMetrics.h" 26 #include "core/layout/svg/SVGTextMetrics.h"
27 #include "platform/fonts/GlyphBuffer.h" 27 #include "platform/fonts/CharacterRange.h"
28 #include "platform/fonts/shaping/SimpleShaper.h"
29 #include "platform/text/BidiCharacterRun.h" 28 #include "platform/text/BidiCharacterRun.h"
30 #include "platform/text/BidiResolver.h" 29 #include "platform/text/BidiResolver.h"
31 #include "platform/text/TextDirection.h" 30 #include "platform/text/TextDirection.h"
32 #include "platform/text/TextPath.h" 31 #include "platform/text/TextPath.h"
33 #include "platform/text/TextRun.h" 32 #include "platform/text/TextRun.h"
34 #include "platform/text/TextRunIterator.h" 33 #include "platform/text/TextRunIterator.h"
35 #include "wtf/Vector.h" 34 #include "wtf/Vector.h"
36 35
37 namespace blink { 36 namespace blink {
38 37
39 class SVGTextMetricsCalculator { 38 class SVGTextMetricsCalculator {
40 public: 39 public:
41 SVGTextMetricsCalculator(LayoutSVGInlineText*); 40 SVGTextMetricsCalculator(LayoutSVGInlineText*);
42 ~SVGTextMetricsCalculator(); 41 ~SVGTextMetricsCalculator();
43 42
44 SVGTextMetrics computeMetricsForCharacter(unsigned textPosition); 43 bool advancePosition();
45 unsigned textLength() const { return static_cast<unsigned>(m_run.charactersL ength()); } 44 unsigned currentPosition() const { return m_currentPosition; }
46 45
47 bool characterStartsSurrogatePair(unsigned textPosition) const 46 SVGTextMetrics currentCharacterMetrics();
47
48 // TODO(pdr): The surrogate pair concept should be refactored to a single
fs 2016/03/09 11:31:44 I think we should rather strive to move in the dir
pdr. 2016/03/10 04:55:15 Nice! I didn't know the name for this. I've rewrit
49 // place to hide this complexity.
50 bool currentCharacterStartsSurrogatePair() const
48 { 51 {
49 return U16_IS_LEAD(m_run[textPosition]) && textPosition + 1 < textLength () && U16_IS_TRAIL(m_run[textPosition + 1]); 52 return U16_IS_LEAD(m_run[m_currentPosition]) && m_currentPosition + 1 < characterCount() && U16_IS_TRAIL(m_run[m_currentPosition + 1]);
50 } 53 }
51 bool characterIsWhiteSpace(unsigned textPosition) const 54 bool currentCharacterIsWhiteSpace() const
52 { 55 {
53 return m_run[textPosition] == ' '; 56 return m_run[m_currentPosition] == ' ';
57 }
58 unsigned characterCount() const
59 {
60 return static_cast<unsigned>(m_run.charactersLength());
54 } 61 }
55 62
56 private: 63 private:
57 void setupBidiRuns(); 64 void setupBidiRuns();
58 65
66 // Ensure |m_subrunRanges| is updated for the current bidi run, or the
67 // complete m_run if no bidi runs are present. Returns the current position
68 // in the subrun which can be used to index into |m_subrunRanges|.
69 unsigned updateSubrunRangesForCurrentPosition();
70
71 // Current character position in m_text.
72 unsigned m_currentPosition;
73
59 LineLayoutSVGInlineText m_text; 74 LineLayoutSVGInlineText m_text;
75 TextRun m_run;
76
60 BidiCharacterRun* m_bidiRun; 77 BidiCharacterRun* m_bidiRun;
61 TextRun m_run;
62 BidiResolver<TextRunIterator, BidiCharacterRun> m_bidiResolver; 78 BidiResolver<TextRunIterator, BidiCharacterRun> m_bidiResolver;
63 float m_totalWidth; 79
64 TextDirection m_textDirection; 80 // Ranges for the current bidi run if present, or the entire run otherwise.
81 Vector<CharacterRange> m_subrunRanges;
65 }; 82 };
66 83
67 SVGTextMetricsCalculator::SVGTextMetricsCalculator(LayoutSVGInlineText* text) 84 SVGTextMetricsCalculator::SVGTextMetricsCalculator(LayoutSVGInlineText* text)
68 : m_text(LineLayoutSVGInlineText(text)) 85 : m_currentPosition(0)
86 , m_text(LineLayoutSVGInlineText(text))
87 , m_run(SVGTextMetrics::constructTextRun(m_text, 0, m_text.textLength(), m_t ext.styleRef().direction()))
69 , m_bidiRun(nullptr) 88 , m_bidiRun(nullptr)
70 , m_run(SVGTextMetrics::constructTextRun(m_text, 0, m_text.textLength(), m_t ext.styleRef().direction()))
71 , m_totalWidth(0)
72 { 89 {
73 setupBidiRuns(); 90 setupBidiRuns();
74 } 91 }
75 92
76 SVGTextMetricsCalculator::~SVGTextMetricsCalculator() 93 SVGTextMetricsCalculator::~SVGTextMetricsCalculator()
77 { 94 {
78 if (m_bidiRun) 95 if (m_bidiRun)
79 m_bidiResolver.runs().deleteRuns(); 96 m_bidiResolver.runs().deleteRuns();
80 } 97 }
81 98
82 void SVGTextMetricsCalculator::setupBidiRuns() 99 void SVGTextMetricsCalculator::setupBidiRuns()
83 { 100 {
84 const ComputedStyle& style = m_text.styleRef(); 101 if (isOverride(m_text.styleRef().unicodeBidi()))
85 m_textDirection = style.direction();
86 if (isOverride(style.unicodeBidi()))
87 return; 102 return;
88
89 BidiStatus status(LTR, false); 103 BidiStatus status(LTR, false);
90 status.last = status.lastStrong = WTF::Unicode::OtherNeutral; 104 status.last = status.lastStrong = WTF::Unicode::OtherNeutral;
91 m_bidiResolver.setStatus(status); 105 m_bidiResolver.setStatus(status);
92 m_bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&m_run, 0)) ; 106 m_bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&m_run, 0)) ;
93 const bool hardLineBreak = false; 107 const bool hardLineBreak = false;
94 const bool reorderRuns = false; 108 const bool reorderRuns = false;
95 m_bidiResolver.createBidiRunsForLine(TextRunIterator(&m_run, m_run.length()) , NoVisualOverride, hardLineBreak, reorderRuns); 109 m_bidiResolver.createBidiRunsForLine(TextRunIterator(&m_run, m_run.length()) , NoVisualOverride, hardLineBreak, reorderRuns);
96 BidiRunList<BidiCharacterRun>& bidiRuns = m_bidiResolver.runs(); 110 BidiRunList<BidiCharacterRun>& bidiRuns = m_bidiResolver.runs();
97 m_bidiRun = bidiRuns.firstRun(); 111 m_bidiRun = bidiRuns.firstRun();
98 } 112 }
99 113
100 SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacter(unsigned tex tPosition) 114 bool SVGTextMetricsCalculator::advancePosition()
115 {
116 m_currentPosition += currentCharacterStartsSurrogatePair() ? 2 : 1;
117 return m_currentPosition < characterCount();
118 }
119
120 unsigned SVGTextMetricsCalculator::updateSubrunRangesForCurrentPosition()
101 { 121 {
102 if (m_bidiRun) { 122 if (m_bidiRun) {
103 if (textPosition >= static_cast<unsigned>(m_bidiRun->stop())) { 123 if (m_currentPosition >= static_cast<unsigned>(m_bidiRun->stop())) {
104 m_bidiRun = m_bidiRun->next(); 124 m_bidiRun = m_bidiRun->next();
105 // New BiDi run means new reference position for measurements, so re set |m_totalWidth|. 125 // Ensure new subrange ranges are computed below.
106 m_totalWidth = 0; 126 m_subrunRanges.clear();
107 } 127 }
108 ASSERT(m_bidiRun); 128 ASSERT(m_bidiRun);
109 ASSERT(static_cast<int>(textPosition) < m_bidiRun->stop()); 129 ASSERT(static_cast<int>(m_currentPosition) < m_bidiRun->stop());
110 m_textDirection = m_bidiRun->direction();
111 } 130 }
112 131
113 unsigned metricsLength = characterStartsSurrogatePair(textPosition) ? 2 : 1; 132 unsigned positionInRun = m_bidiRun ? m_currentPosition - m_bidiRun->start() : m_currentPosition;
114 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(m_text, textP osition, metricsLength, m_textDirection); 133 if (positionInRun >= m_subrunRanges.size()) {
115 ASSERT(metrics.length() == metricsLength); 134 unsigned subrunStart = m_bidiRun ? m_bidiRun->start() : 0;
135 unsigned subrunEnd = m_bidiRun ? m_bidiRun->stop() : m_run.charactersLen gth();
136 TextDirection subrunDirection = m_bidiRun ? m_bidiRun->direction() : m_t ext.styleRef().direction();
137 TextRun subRun = SVGTextMetrics::constructTextRun(m_text, subrunStart, s ubrunEnd - subrunStart, subrunDirection);
138 m_subrunRanges = m_text.scaledFont().individualCharacterRanges(subRun, 0 , subRun.length() - 1);
116 139
117 unsigned startPosition = m_bidiRun ? m_bidiRun->start() : 0; 140 // TODO(pdr): Our font APIs currently only have per-glyph data so we
118 ASSERT(startPosition <= textPosition); 141 // need to synthesize per-grapheme data. For example, if 'fi' is shaped
119 SVGTextMetrics complexStartToCurrentMetrics = SVGTextMetrics::measureCharact erRange(m_text, startPosition, textPosition - startPosition + metricsLength, m_t extDirection); 142 // into a single glyph, we do not have access to the 'i' position. The
120 // Frequent case for Arabic text: when measuring a single character the arab ic isolated form is taken 143 // code below synthesizes a position when two characters share the same
fs 2016/03/09 11:31:44 Well technically "two or more" (i.e ligatures like
pdr. 2016/03/10 04:55:15 +1, good catch. I've ffixed this loop and added a
pdr. 2016/03/10 07:21:48 I looked at this some more and we're violating the
fs 2016/03/10 09:55:57 Yes, it more common for us weird people with diacr
121 // when laying out the glyph "in context" (with it's surrounding characters) it changes due to shaping. 144 // position. See: https://crbug.com/473476.
122 // So whenever currentWidth != currentMetrics.width(), we are processing a t ext run whose length is 145 for (unsigned rangeIndex = m_subrunRanges.size() - 1; rangeIndex > 0; -- rangeIndex) {
123 // not equal to the sum of the individual lengths of the glyphs, when measur ing them isolated. 146 CharacterRange& currentRange = m_subrunRanges[rangeIndex];
124 float currentWidth = complexStartToCurrentMetrics.width() - m_totalWidth; 147 CharacterRange& previousRange = m_subrunRanges[rangeIndex - 1];
125 if (currentWidth != metrics.width()) 148 if (currentRange.width() == 0 && previousRange.end == currentRange.s tart) {
126 metrics.setWidth(currentWidth); 149 float midpoint = (previousRange.end - previousRange.start) / 2;
150 previousRange.end = previousRange.start + midpoint;
fs 2016/03/09 11:31:44 (previousRange.start + previousRange.end) / 2 for
pdr. 2016/03/10 04:55:15 The new loop now requires a divide 😭
151 currentRange.start = previousRange.end;
152 }
153 }
154 }
127 155
128 m_totalWidth = complexStartToCurrentMetrics.width(); 156 return positionInRun;
129 return metrics; 157 }
158
159 SVGTextMetrics SVGTextMetricsCalculator::currentCharacterMetrics()
160 {
161 unsigned currentSubrunPosition = updateSubrunRangesForCurrentPosition();
162 unsigned length = currentCharacterStartsSurrogatePair() ? 2 : 1;
163 float width = m_subrunRanges[currentSubrunPosition].width();
164 return SVGTextMetrics(m_text, length, width);
130 } 165 }
131 166
132 struct MeasureTextData { 167 struct MeasureTextData {
133 MeasureTextData(SVGCharacterDataMap* characterDataMap) 168 MeasureTextData(SVGCharacterDataMap* characterDataMap)
134 : allCharactersMap(characterDataMap) 169 : allCharactersMap(characterDataMap)
135 , lastCharacterWasWhiteSpace(true) 170 , lastCharacterWasWhiteSpace(true)
136 , valueListPosition(0) 171 , valueListPosition(0)
137 { 172 {
138 } 173 }
139 174
140 SVGCharacterDataMap* allCharactersMap; 175 SVGCharacterDataMap* allCharactersMap;
141 bool lastCharacterWasWhiteSpace; 176 bool lastCharacterWasWhiteSpace;
142 unsigned valueListPosition; 177 unsigned valueListPosition;
143 }; 178 };
144 179
145 static void measureTextLayoutObject(LayoutSVGInlineText* text, MeasureTextData* data, bool processLayoutObject) 180 static void measureTextLayoutObject(LayoutSVGInlineText* text, MeasureTextData* data, bool processLayoutObject)
146 { 181 {
147 ASSERT(text); 182 ASSERT(text);
148 183
149 SVGTextLayoutAttributes* attributes = text->layoutAttributes(); 184 SVGTextLayoutAttributes* attributes = text->layoutAttributes();
150 Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues() ; 185 Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues() ;
151 if (processLayoutObject) { 186 if (processLayoutObject) {
152 if (data->allCharactersMap) 187 if (data->allCharactersMap)
153 attributes->clear(); 188 attributes->clear();
154 else 189 else
155 textMetricsValues->clear(); 190 textMetricsValues->clear();
156 } 191 }
157 192
193 // TODO(pdr): This loop is too tightly coupled to SVGTextMetricsCalculator.
fs 2016/03/09 11:31:44 Should we file a (separate) bug for this, or will
pdr. 2016/03/10 04:55:15 Yeah, I plan to do this one in a followup but want
fs 2016/03/10 09:55:57 Roger that.
194 // We should refactor SVGTextMetricsCalculator to be a simple bidi run
195 // iterator and move all subrun logic to a single function.
158 SVGTextMetricsCalculator calculator(text); 196 SVGTextMetricsCalculator calculator(text);
159 bool preserveWhiteSpace = text->style()->whiteSpace() == PRE; 197 bool preserveWhiteSpace = text->style()->whiteSpace() == PRE;
160 unsigned surrogatePairCharacters = 0; 198 unsigned surrogatePairCharacters = 0;
161 unsigned skippedCharacters = 0; 199 unsigned skippedCharacters = 0;
162 unsigned textPosition = 0; 200 do {
163 unsigned textLength = calculator.textLength(); 201 SVGTextMetrics metrics = calculator.currentCharacterMetrics();
164 202 if (!metrics.length())
165 SVGTextMetrics currentMetrics;
166 for (; textPosition < textLength; textPosition += currentMetrics.length()) {
167 currentMetrics = calculator.computeMetricsForCharacter(textPosition);
168 if (!currentMetrics.length())
169 break; 203 break;
170 204
171 bool characterIsWhiteSpace = calculator.characterIsWhiteSpace(textPositi on); 205 bool characterIsWhiteSpace = calculator.currentCharacterIsWhiteSpace();
172 if (characterIsWhiteSpace && !preserveWhiteSpace && data->lastCharacterW asWhiteSpace) { 206 if (characterIsWhiteSpace && !preserveWhiteSpace && data->lastCharacterW asWhiteSpace) {
173 if (processLayoutObject) 207 if (processLayoutObject)
174 textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::Skipped SpaceMetrics)); 208 textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::Skipped SpaceMetrics));
175 if (data->allCharactersMap) 209 if (data->allCharactersMap)
176 skippedCharacters += currentMetrics.length(); 210 skippedCharacters += metrics.length();
177 continue; 211 continue;
178 } 212 }
179 213
180 if (processLayoutObject) { 214 if (processLayoutObject) {
181 if (data->allCharactersMap) { 215 if (data->allCharactersMap) {
182 const SVGCharacterDataMap::const_iterator it = data->allCharacte rsMap->find(data->valueListPosition + textPosition - skippedCharacters - surroga tePairCharacters + 1); 216 const SVGCharacterDataMap::const_iterator it = data->allCharacte rsMap->find(data->valueListPosition + calculator.currentPosition() - skippedChar acters - surrogatePairCharacters + 1);
183 if (it != data->allCharactersMap->end()) 217 if (it != data->allCharactersMap->end())
184 attributes->characterDataMap().set(textPosition + 1, it->val ue); 218 attributes->characterDataMap().set(calculator.currentPositio n() + 1, it->value);
185 } 219 }
186 textMetricsValues->append(currentMetrics); 220 textMetricsValues->append(metrics);
187 } 221 }
188 222
189 if (data->allCharactersMap && calculator.characterStartsSurrogatePair(te xtPosition)) 223 if (data->allCharactersMap && calculator.currentCharacterStartsSurrogate Pair())
190 surrogatePairCharacters++; 224 surrogatePairCharacters++;
191 225
192 data->lastCharacterWasWhiteSpace = characterIsWhiteSpace; 226 data->lastCharacterWasWhiteSpace = characterIsWhiteSpace;
193 } 227 } while (calculator.advancePosition());
194 228
195 if (!data->allCharactersMap) 229 if (!data->allCharactersMap)
196 return; 230 return;
197 231
198 data->valueListPosition += textPosition - skippedCharacters; 232 data->valueListPosition += calculator.currentPosition() - skippedCharacters;
199 } 233 }
200 234
201 static void walkTree(LayoutSVGText* start, LayoutSVGInlineText* stopAtLeaf, Meas ureTextData* data) 235 static void walkTree(LayoutSVGText* start, LayoutSVGInlineText* stopAtLeaf, Meas ureTextData* data)
202 { 236 {
203 LayoutObject* child = start->firstChild(); 237 LayoutObject* child = start->firstChild();
204 while (child) { 238 while (child) {
205 if (child->isSVGInlineText()) { 239 if (child->isSVGInlineText()) {
206 LayoutSVGInlineText* text = toLayoutSVGInlineText(child); 240 LayoutSVGInlineText* text = toLayoutSVGInlineText(child);
207 measureTextLayoutObject(text, data, !stopAtLeaf || stopAtLeaf == tex t); 241 measureTextLayoutObject(text, data, !stopAtLeaf || stopAtLeaf == tex t);
208 if (stopAtLeaf && stopAtLeaf == text) 242 if (stopAtLeaf && stopAtLeaf == text)
(...skipping 22 matching lines...) Expand all
231 } 265 }
232 266
233 void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(LayoutSVGText* textR oot, LayoutSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap) 267 void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(LayoutSVGText* textR oot, LayoutSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap)
234 { 268 {
235 ASSERT(textRoot); 269 ASSERT(textRoot);
236 MeasureTextData data(&allCharactersMap); 270 MeasureTextData data(&allCharactersMap);
237 walkTree(textRoot, stopAtLeaf, &data); 271 walkTree(textRoot, stopAtLeaf, &data);
238 } 272 }
239 273
240 } // namespace blink 274 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/layout/svg/SVGTextMetrics.cpp ('k') | third_party/WebKit/Source/platform/fonts/Font.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698