| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 Google Inc. All rights reserved. | 2 * Copyright (c) 2012 Google Inc. All rights reserved. |
| 3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. | 3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are | 6 * modification, are permitted provided that the following conditions are |
| 7 * met: | 7 * met: |
| 8 * | 8 * |
| 9 * * Redistributions of source code must retain the above copyright | 9 * * Redistributions of source code must retain the above copyright |
| 10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 47 | 47 |
| 48 #include <list> | 48 #include <list> |
| 49 #include <map> | 49 #include <map> |
| 50 #include <string> | 50 #include <string> |
| 51 #include <unicode/normlzr.h> | 51 #include <unicode/normlzr.h> |
| 52 #include <unicode/uchar.h> | 52 #include <unicode/uchar.h> |
| 53 #include <unicode/uscript.h> | 53 #include <unicode/uscript.h> |
| 54 | 54 |
| 55 namespace blink { | 55 namespace blink { |
| 56 | 56 |
| 57 struct HarfBuzzRunGlyphData { |
| 58 uint16_t glyph; |
| 59 uint16_t characterIndex; |
| 60 float advance; |
| 61 FloatSize offset; |
| 62 }; |
| 63 |
| 64 struct ShapeResult::RunInfo { |
| 65 RunInfo(const SimpleFontData* font, hb_direction_t dir, hb_script_t script, |
| 66 unsigned startIndex, unsigned numGlyphs, unsigned numCharacters) |
| 67 : m_fontData(font), m_direction(dir), m_script(script) |
| 68 , m_startIndex(startIndex), m_numCharacters(numCharacters) |
| 69 , m_numGlyphs(numGlyphs) |
| 70 { |
| 71 m_glyphData.resize(m_numGlyphs); |
| 72 } |
| 73 |
| 74 float xPositionForOffset(unsigned) const; |
| 75 int characterIndexForXPosition(float) const; |
| 76 void setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, |
| 77 float offsetX, float offsetY); |
| 78 |
| 79 void addAdvance(unsigned index, float advance) |
| 80 { |
| 81 m_glyphData[index].advance += advance; |
| 82 } |
| 83 |
| 84 size_t glyphToCharacterIndex(size_t i) const |
| 85 { |
| 86 return m_startIndex + m_glyphData[i].characterIndex; |
| 87 } |
| 88 |
| 89 const SimpleFontData* m_fontData; |
| 90 hb_direction_t m_direction; |
| 91 hb_script_t m_script; |
| 92 Vector<HarfBuzzRunGlyphData> m_glyphData; |
| 93 unsigned m_startIndex; |
| 94 unsigned m_numCharacters; |
| 95 unsigned m_numGlyphs; |
| 96 float m_width; |
| 97 }; |
| 98 |
| 99 float ShapeResult::RunInfo::xPositionForOffset(unsigned offset) const |
| 100 { |
| 101 ASSERT(offset < m_numCharacters); |
| 102 unsigned glyphIndex = 0; |
| 103 float position = 0; |
| 104 if (m_direction == HB_DIRECTION_RTL) { |
| 105 while (glyphIndex < m_numGlyphs && m_glyphData[glyphIndex].characterInde
x > offset) { |
| 106 position += m_glyphData[glyphIndex].advance; |
| 107 ++glyphIndex; |
| 108 } |
| 109 // For RTL, we need to return the right side boundary of the character. |
| 110 // Add advance of glyphs which are part of the character. |
| 111 while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].character
Index == m_glyphData[glyphIndex + 1].characterIndex) { |
| 112 position += m_glyphData[glyphIndex].advance; |
| 113 ++glyphIndex; |
| 114 } |
| 115 position += m_glyphData[glyphIndex].advance; |
| 116 } else { |
| 117 while (glyphIndex < m_numGlyphs && m_glyphData[glyphIndex].characterInde
x < offset) { |
| 118 position += m_glyphData[glyphIndex].advance; |
| 119 ++glyphIndex; |
| 120 } |
| 121 } |
| 122 return position; |
| 123 } |
| 124 |
| 125 int ShapeResult::RunInfo::characterIndexForXPosition(float targetX) const |
| 126 { |
| 127 ASSERT(targetX <= m_width); |
| 128 float currentX = 0; |
| 129 float currentAdvance = m_glyphData[0].advance; |
| 130 unsigned glyphIndex = 0; |
| 131 |
| 132 // Sum up advances that belong to the first character. |
| 133 while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].characterInde
x == m_glyphData[glyphIndex + 1].characterIndex) |
| 134 currentAdvance += m_glyphData[++glyphIndex].advance; |
| 135 currentAdvance = currentAdvance / 2.0; |
| 136 if (targetX <= currentAdvance) |
| 137 return m_direction == HB_DIRECTION_RTL ? m_numCharacters : 0; |
| 138 |
| 139 currentX = currentAdvance; |
| 140 ++glyphIndex; |
| 141 while (glyphIndex < m_numGlyphs) { |
| 142 unsigned prevCharacterIndex = m_glyphData[glyphIndex - 1].characterIndex
; |
| 143 float prevAdvance = currentAdvance; |
| 144 currentAdvance = m_glyphData[glyphIndex].advance; |
| 145 while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].character
Index == m_glyphData[glyphIndex + 1].characterIndex) |
| 146 currentAdvance += m_glyphData[++glyphIndex].advance; |
| 147 currentAdvance = currentAdvance / 2.0; |
| 148 float nextX = currentX + prevAdvance + currentAdvance; |
| 149 if (currentX <= targetX && targetX <= nextX) |
| 150 return m_direction == HB_DIRECTION_RTL ? prevCharacterIndex : m_glyp
hData[glyphIndex].characterIndex; |
| 151 currentX = nextX; |
| 152 ++glyphIndex; |
| 153 } |
| 154 |
| 155 return m_direction == HB_DIRECTION_RTL ? 0 : m_numCharacters; |
| 156 } |
| 157 |
| 158 void ShapeResult::RunInfo::setGlyphAndPositions(unsigned index, |
| 159 uint16_t glyphId, float advance, float offsetX, float offsetY) |
| 160 { |
| 161 HarfBuzzRunGlyphData& data = m_glyphData[index]; |
| 162 data.glyph = glyphId; |
| 163 data.advance = advance; |
| 164 data.offset = FloatSize(offsetX, offsetY); |
| 165 } |
| 166 |
| 167 ShapeResult::~ShapeResult() |
| 168 { |
| 169 unsigned destroyed = 0; |
| 170 for (unsigned i = 0; i < m_runs.size(); i++) { |
| 171 if (m_runs[i]) { |
| 172 delete m_runs[i]; |
| 173 destroyed++; |
| 174 } |
| 175 } |
| 176 } |
| 177 |
| 178 static inline void addGlyphToBuffer(GlyphBuffer* glyphBuffer, float advance, |
| 179 hb_direction_t direction, const SimpleFontData* fontData, |
| 180 const HarfBuzzRunGlyphData& glyphData) |
| 181 { |
| 182 FloatPoint startOffset = HB_DIRECTION_IS_HORIZONTAL(direction) |
| 183 ? FloatPoint(advance, 0) |
| 184 : FloatPoint(0, advance); |
| 185 glyphBuffer->add(glyphData.glyph, fontData, startOffset + glyphData.offset); |
| 186 } |
| 187 |
| 188 template<TextDirection direction> |
| 189 float ShapeResult::fillGlyphBufferForRun(GlyphBuffer* glyphBuffer, |
| 190 const RunInfo* run, float initialAdvance, unsigned from, unsigned to, |
| 191 unsigned runOffset) |
| 192 { |
| 193 float advanceSoFar = initialAdvance; |
| 194 unsigned numGlyphs = run->m_numGlyphs; |
| 195 for (unsigned i = 0; i < numGlyphs; ++i) { |
| 196 const HarfBuzzRunGlyphData& glyphData = run->m_glyphData[i]; |
| 197 uint16_t currentCharacterIndex = run->m_startIndex + |
| 198 glyphData.characterIndex + runOffset; |
| 199 if ((direction == RTL && currentCharacterIndex >= to) |
| 200 || (direction == LTR && currentCharacterIndex < from)) { |
| 201 advanceSoFar += glyphData.advance; |
| 202 } else if ((direction == RTL && currentCharacterIndex >= from) |
| 203 || (direction == LTR && currentCharacterIndex < to)) { |
| 204 addGlyphToBuffer(glyphBuffer, advanceSoFar, run->m_direction, |
| 205 run->m_fontData, glyphData); |
| 206 advanceSoFar += glyphData.advance; |
| 207 } |
| 208 } |
| 209 return advanceSoFar - initialAdvance; |
| 210 } |
| 211 |
| 212 static inline unsigned countGraphemesInCluster(const UChar* str, |
| 213 unsigned strLength, uint16_t startIndex, uint16_t endIndex) |
| 214 { |
| 215 if (startIndex > endIndex) { |
| 216 uint16_t tempIndex = startIndex; |
| 217 startIndex = endIndex; |
| 218 endIndex = tempIndex; |
| 219 } |
| 220 uint16_t length = endIndex - startIndex; |
| 221 ASSERT(static_cast<unsigned>(startIndex + length) <= strLength); |
| 222 TextBreakIterator* cursorPosIterator = cursorMovementIterator(&str[startInde
x], length); |
| 223 |
| 224 int cursorPos = cursorPosIterator->current(); |
| 225 int numGraphemes = -1; |
| 226 while (0 <= cursorPos) { |
| 227 cursorPos = cursorPosIterator->next(); |
| 228 numGraphemes++; |
| 229 } |
| 230 return numGraphemes < 0 ? 0 : numGraphemes; |
| 231 } |
| 232 |
| 233 static inline void addEmphasisMark(GlyphBuffer* buffer, |
| 234 const GlyphData* emphasisData, FloatPoint glyphCenter, |
| 235 float midGlyphOffset) |
| 236 { |
| 237 ASSERT(buffer); |
| 238 ASSERT(emphasisData); |
| 239 |
| 240 const SimpleFontData* emphasisFontData = emphasisData->fontData; |
| 241 ASSERT(emphasisFontData); |
| 242 |
| 243 bool isVertical = emphasisFontData->platformData().isVerticalAnyUpright() |
| 244 && emphasisFontData->verticalData(); |
| 245 |
| 246 if (!isVertical) { |
| 247 buffer->add(emphasisData->glyph, emphasisFontData, |
| 248 midGlyphOffset - glyphCenter.x()); |
| 249 } else { |
| 250 buffer->add(emphasisData->glyph, emphasisFontData, |
| 251 FloatPoint(-glyphCenter.x(), midGlyphOffset - glyphCenter.y())); |
| 252 } |
| 253 } |
| 254 |
| 255 float ShapeResult::fillGlyphBufferForTextEmphasisRun(GlyphBuffer* glyphBuffer, |
| 256 const RunInfo* run, const TextRun& textRun, const GlyphData* emphasisData, |
| 257 float initialAdvance, unsigned from, unsigned to, unsigned runOffset) |
| 258 { |
| 259 unsigned graphemesInCluster = 1; |
| 260 float clusterAdvance = 0; |
| 261 |
| 262 FloatPoint glyphCenter = emphasisData->fontData-> |
| 263 boundsForGlyph(emphasisData->glyph).center(); |
| 264 |
| 265 TextDirection direction = textRun.direction(); |
| 266 |
| 267 // A "cluster" in this context means a cluster as it is used by HarfBuzz: |
| 268 // The minimal group of characters and corresponding glyphs, that cannot be
broken |
| 269 // down further from a text shaping point of view. |
| 270 // A cluster can contain multiple glyphs and grapheme clusters, with mutuall
y |
| 271 // overlapping boundaries. Below we count grapheme clusters per HarfBuzz clu
sters, |
| 272 // then linearly split the sum of corresponding glyph advances by the number
of |
| 273 // grapheme clusters in order to find positions for emphasis mark drawing. |
| 274 uint16_t clusterStart = direction == RTL |
| 275 ? run->m_startIndex + run->m_numCharacters + runOffset |
| 276 : run->glyphToCharacterIndex(0) + runOffset; |
| 277 |
| 278 float advanceSoFar = initialAdvance; |
| 279 unsigned numGlyphs = run->m_numGlyphs; |
| 280 for (unsigned i = 0; i < numGlyphs; ++i) { |
| 281 const HarfBuzzRunGlyphData& glyphData = run->m_glyphData[i]; |
| 282 uint16_t currentCharacterIndex = run->m_startIndex + glyphData.character
Index + runOffset; |
| 283 bool isRunEnd = (i + 1 == numGlyphs); |
| 284 bool isClusterEnd = isRunEnd || (run->glyphToCharacterIndex(i + 1) + ru
nOffset != currentCharacterIndex); |
| 285 |
| 286 if ((direction == RTL && currentCharacterIndex >= to) || (direction != R
TL && currentCharacterIndex < from)) { |
| 287 advanceSoFar += glyphData.advance; |
| 288 direction == RTL ? --clusterStart : ++clusterStart; |
| 289 continue; |
| 290 } |
| 291 |
| 292 clusterAdvance += glyphData.advance; |
| 293 |
| 294 if (textRun.is8Bit()) { |
| 295 float glyphAdvanceX = glyphData.advance; |
| 296 if (Character::canReceiveTextEmphasis(textRun[currentCharacterIndex]
)) { |
| 297 addEmphasisMark(glyphBuffer, emphasisData, glyphCenter, advanceS
oFar + glyphAdvanceX / 2); |
| 298 } |
| 299 advanceSoFar += glyphAdvanceX; |
| 300 } else if (isClusterEnd) { |
| 301 uint16_t clusterEnd; |
| 302 if (direction == RTL) |
| 303 clusterEnd = currentCharacterIndex; |
| 304 else |
| 305 clusterEnd = isRunEnd ? run->m_startIndex + run->m_numCharacters
+ runOffset : run->glyphToCharacterIndex(i + 1) + runOffset; |
| 306 |
| 307 graphemesInCluster = countGraphemesInCluster(textRun.characters16(),
textRun.charactersLength(), clusterStart, clusterEnd); |
| 308 if (!graphemesInCluster || !clusterAdvance) |
| 309 continue; |
| 310 |
| 311 float glyphAdvanceX = clusterAdvance / graphemesInCluster; |
| 312 for (unsigned j = 0; j < graphemesInCluster; ++j) { |
| 313 // Do not put emphasis marks on space, separator, and control ch
aracters. |
| 314 if (Character::canReceiveTextEmphasis(textRun[currentCharacterIn
dex])) |
| 315 addEmphasisMark(glyphBuffer, emphasisData, glyphCenter, adva
nceSoFar + glyphAdvanceX / 2); |
| 316 advanceSoFar += glyphAdvanceX; |
| 317 } |
| 318 clusterStart = clusterEnd; |
| 319 clusterAdvance = 0; |
| 320 } |
| 321 } |
| 322 return advanceSoFar - initialAdvance; |
| 323 } |
| 324 |
| 325 float ShapeResult::fillGlyphBuffer(Vector<RefPtr<ShapeResult>>& results, |
| 326 GlyphBuffer* glyphBuffer, const TextRun& textRun, |
| 327 unsigned from, unsigned to) |
| 328 { |
| 329 float advance = 0; |
| 330 if (textRun.rtl()) { |
| 331 unsigned wordOffset = textRun.length(); |
| 332 for (unsigned j = 0; j < results.size(); j++) { |
| 333 unsigned resolvedIndex = results.size() - 1 - j; |
| 334 RefPtr<ShapeResult>& wordResult = results[resolvedIndex]; |
| 335 for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { |
| 336 advance += wordResult->fillGlyphBufferForRun<RTL>(glyphBuffer, |
| 337 wordResult->m_runs[i], advance, from, to, |
| 338 wordOffset - wordResult->numCharacters()); |
| 339 } |
| 340 wordOffset -= wordResult->numCharacters(); |
| 341 } |
| 342 } else { |
| 343 unsigned wordOffset = 0; |
| 344 for (unsigned j = 0; j < results.size(); j++) { |
| 345 RefPtr<ShapeResult>& wordResult = results[j]; |
| 346 for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { |
| 347 advance += wordResult->fillGlyphBufferForRun<LTR>(glyphBuffer, |
| 348 wordResult->m_runs[i], advance, from, to, wordOffset); |
| 349 } |
| 350 wordOffset += wordResult->numCharacters(); |
| 351 } |
| 352 } |
| 353 |
| 354 return advance; |
| 355 } |
| 356 |
| 357 float ShapeResult::fillGlyphBufferForTextEmphasis( |
| 358 Vector<RefPtr<ShapeResult>>& results, GlyphBuffer* glyphBuffer, |
| 359 const TextRun& textRun, const GlyphData* emphasisData, |
| 360 unsigned from, unsigned to) |
| 361 { |
| 362 float advance = 0; |
| 363 unsigned wordOffset = textRun.rtl() ? textRun.length() : 0; |
| 364 for (unsigned j = 0; j < results.size(); j++) { |
| 365 unsigned resolvedIndex = textRun.rtl() ? results.size() - 1 - j : j; |
| 366 RefPtr<ShapeResult>& wordResult = results[resolvedIndex]; |
| 367 for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { |
| 368 unsigned resolvedOffset = wordOffset - |
| 369 (textRun.rtl() ? wordResult->numCharacters() : 0); |
| 370 advance += wordResult->fillGlyphBufferForTextEmphasisRun( |
| 371 glyphBuffer, wordResult->m_runs[i], textRun, emphasisData, |
| 372 advance, from, to, resolvedOffset); |
| 373 } |
| 374 wordOffset += wordResult->numCharacters() * (textRun.rtl() ? -1 : 1); |
| 375 } |
| 376 |
| 377 return advance; |
| 378 } |
| 379 |
| 380 FloatRect ShapeResult::selectionRect(Vector<RefPtr<ShapeResult>>& results, |
| 381 TextDirection direction, float totalWidth, const FloatPoint& point, |
| 382 int height, unsigned absoluteFrom, unsigned absoluteTo) |
| 383 { |
| 384 float currentX = 0; |
| 385 float fromX = 0; |
| 386 float toX = 0; |
| 387 bool foundFromX = false; |
| 388 bool foundToX = false; |
| 389 |
| 390 if (direction == RTL) |
| 391 currentX = totalWidth; |
| 392 |
| 393 // The absoluteFrom and absoluteTo arguments represent the start/end offset |
| 394 // for the entire run, from/to are continuously updated to be relative to |
| 395 // the current word (ShapeResult instance). |
| 396 int from = absoluteFrom; |
| 397 int to = absoluteTo; |
| 398 |
| 399 unsigned wordOffset = 0; |
| 400 for (unsigned j = 0; j < results.size(); j++) { |
| 401 RefPtr<ShapeResult> result = results[j]; |
| 402 for (unsigned i = 0; i < result->m_runs.size(); i++) { |
| 403 if (direction == RTL) |
| 404 currentX -= result->m_runs[i]->m_width; |
| 405 int numCharacters = result->m_runs[i]->m_numCharacters; |
| 406 if (!foundFromX && from >= 0 && from < numCharacters) { |
| 407 fromX = result->m_runs[i]->xPositionForOffset(from) + currentX; |
| 408 foundFromX = true; |
| 409 } else { |
| 410 from -= numCharacters; |
| 411 } |
| 412 |
| 413 if (!foundToX && to >= 0 && to < numCharacters) { |
| 414 toX = result->m_runs[i]->xPositionForOffset(to) + currentX; |
| 415 foundToX = true; |
| 416 } else { |
| 417 to -= numCharacters; |
| 418 } |
| 419 |
| 420 if (foundFromX && foundToX) |
| 421 break; |
| 422 if (direction != RTL) |
| 423 currentX += result->m_runs[i]->m_width; |
| 424 } |
| 425 wordOffset += result->numCharacters(); |
| 426 } |
| 427 |
| 428 // The position in question might be just after the text. |
| 429 if (!foundFromX) |
| 430 fromX = 0; |
| 431 if (!foundToX) |
| 432 toX = direction == RTL ? 0 : totalWidth; |
| 433 |
| 434 // None of our runs is part of the selection, possibly invalid arguments. |
| 435 if (!foundToX && !foundFromX) |
| 436 fromX = toX = 0; |
| 437 if (fromX < toX) |
| 438 return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); |
| 439 return FloatRect(point.x() + toX, point.y(), fromX - toX, height); |
| 440 } |
| 441 |
| 442 int ShapeResult::offsetForPosition(float targetX) |
| 443 { |
| 444 int charactersSoFar = 0; |
| 445 float currentX = 0; |
| 446 |
| 447 if (m_direction == RTL) { |
| 448 charactersSoFar = m_numCharacters; |
| 449 for (unsigned i = 0; i < m_runs.size(); ++i) { |
| 450 charactersSoFar -= m_runs[i]->m_numCharacters; |
| 451 float nextX = currentX + m_runs[i]->m_width; |
| 452 float offsetForRun = targetX - currentX; |
| 453 if (offsetForRun >= 0 && offsetForRun <= m_runs[i]->m_width) { |
| 454 // The x value in question is within this script run. |
| 455 const unsigned index = m_runs[i]->characterIndexForXPosition(off
setForRun); |
| 456 return charactersSoFar + index; |
| 457 } |
| 458 currentX = nextX; |
| 459 } |
| 460 } else { |
| 461 for (unsigned i = 0; i < m_runs.size(); ++i) { |
| 462 float nextX = currentX + m_runs[i]->m_width; |
| 463 float offsetForRun = targetX - currentX; |
| 464 if (offsetForRun >= 0 && offsetForRun <= m_runs[i]->m_width) { |
| 465 const unsigned index = m_runs[i]->characterIndexForXPosition(off
setForRun); |
| 466 return charactersSoFar + index; |
| 467 } |
| 468 charactersSoFar += m_runs[i]->m_numCharacters; |
| 469 currentX = nextX; |
| 470 } |
| 471 } |
| 472 |
| 473 return charactersSoFar; |
| 474 } |
| 475 |
| 476 unsigned ShapeResult::numberOfRunsForTesting() const |
| 477 { |
| 478 return m_runs.size(); |
| 479 } |
| 480 |
| 481 bool ShapeResult::runInfoForTesting(unsigned runIndex, unsigned& startIndex, |
| 482 unsigned& numGlyphs, hb_script_t& script) |
| 483 { |
| 484 if (runIndex < m_runs.size() && m_runs[runIndex]) { |
| 485 startIndex = m_runs[runIndex]->m_startIndex; |
| 486 numGlyphs = m_runs[runIndex]->m_numGlyphs; |
| 487 script = m_runs[runIndex]->m_script; |
| 488 return true; |
| 489 } |
| 490 return false; |
| 491 } |
| 492 |
| 493 uint16_t ShapeResult::glyphForTesting(unsigned runIndex, size_t glyphIndex) |
| 494 { |
| 495 return m_runs[runIndex]->m_glyphData[glyphIndex].glyph; |
| 496 } |
| 497 |
| 498 float ShapeResult::advanceForTesting(unsigned runIndex, size_t glyphIndex) |
| 499 { |
| 500 return m_runs[runIndex]->m_glyphData[glyphIndex].advance; |
| 501 } |
| 502 |
| 57 template<typename T> | 503 template<typename T> |
| 58 class HarfBuzzScopedPtr { | 504 class HarfBuzzScopedPtr { |
| 59 public: | 505 public: |
| 60 typedef void (*DestroyFunction)(T*); | 506 typedef void (*DestroyFunction)(T*); |
| 61 | 507 |
| 62 HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy) | 508 HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy) |
| 63 : m_ptr(ptr) | 509 : m_ptr(ptr) |
| 64 , m_destroy(destroy) | 510 , m_destroy(destroy) |
| 65 { | 511 { |
| 66 ASSERT(m_destroy); | 512 ASSERT(m_destroy); |
| 67 } | 513 } |
| 68 ~HarfBuzzScopedPtr() | 514 ~HarfBuzzScopedPtr() |
| 69 { | 515 { |
| 70 if (m_ptr) | 516 if (m_ptr) |
| 71 (*m_destroy)(m_ptr); | 517 (*m_destroy)(m_ptr); |
| 72 } | 518 } |
| 73 | 519 |
| 74 T* get() { return m_ptr; } | 520 T* get() { return m_ptr; } |
| 75 void set(T* ptr) { m_ptr = ptr; } | 521 void set(T* ptr) { m_ptr = ptr; } |
| 76 private: | 522 private: |
| 77 T* m_ptr; | 523 T* m_ptr; |
| 78 DestroyFunction m_destroy; | 524 DestroyFunction m_destroy; |
| 79 }; | 525 }; |
| 80 | 526 |
| 81 | |
| 82 static const unsigned cHarfBuzzCacheMaxSize = 256; | |
| 83 | |
| 84 struct CachedShapingResultsLRUNode; | |
| 85 struct CachedShapingResults; | |
| 86 typedef std::map<std::wstring, CachedShapingResults*> CachedShapingResultsMap; | |
| 87 typedef std::list<CachedShapingResultsLRUNode*> CachedShapingResultsLRU; | |
| 88 | |
| 89 struct CachedShapingResults { | |
| 90 CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* runFont, hb_di
rection_t runDir, const String& newLocale); | |
| 91 ~CachedShapingResults(); | |
| 92 | |
| 93 hb_buffer_t* buffer; | |
| 94 Font font; | |
| 95 hb_direction_t dir; | |
| 96 String locale; | |
| 97 CachedShapingResultsLRU::iterator lru; | |
| 98 }; | |
| 99 | |
| 100 struct CachedShapingResultsLRUNode { | |
| 101 CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEn
try); | |
| 102 ~CachedShapingResultsLRUNode(); | |
| 103 | |
| 104 CachedShapingResultsMap::iterator entry; | |
| 105 }; | |
| 106 | |
| 107 CachedShapingResults::CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Fo
nt* fontData, hb_direction_t dirData, const String& newLocale) | |
| 108 : buffer(harfBuzzBuffer) | |
| 109 , font(*fontData) | |
| 110 , dir(dirData) | |
| 111 , locale(newLocale) | |
| 112 { | |
| 113 } | |
| 114 | |
| 115 CachedShapingResults::~CachedShapingResults() | |
| 116 { | |
| 117 hb_buffer_destroy(buffer); | |
| 118 } | |
| 119 | |
| 120 CachedShapingResultsLRUNode::CachedShapingResultsLRUNode(const CachedShapingResu
ltsMap::iterator& cacheEntry) | |
| 121 : entry(cacheEntry) | |
| 122 { | |
| 123 } | |
| 124 | |
| 125 CachedShapingResultsLRUNode::~CachedShapingResultsLRUNode() | |
| 126 { | |
| 127 } | |
| 128 | |
| 129 class HarfBuzzRunCache { | |
| 130 public: | |
| 131 HarfBuzzRunCache(); | |
| 132 ~HarfBuzzRunCache(); | |
| 133 | |
| 134 CachedShapingResults* find(const std::wstring& key) const; | |
| 135 void remove(CachedShapingResults* node); | |
| 136 void moveToBack(CachedShapingResults* node); | |
| 137 bool insert(const std::wstring& key, CachedShapingResults* run); | |
| 138 | |
| 139 private: | |
| 140 CachedShapingResultsMap m_harfBuzzRunMap; | |
| 141 CachedShapingResultsLRU m_harfBuzzRunLRU; | |
| 142 }; | |
| 143 | |
| 144 | |
| 145 HarfBuzzRunCache::HarfBuzzRunCache() | |
| 146 { | |
| 147 } | |
| 148 | |
| 149 HarfBuzzRunCache::~HarfBuzzRunCache() | |
| 150 { | |
| 151 for (CachedShapingResultsMap::iterator it = m_harfBuzzRunMap.begin(); it !=
m_harfBuzzRunMap.end(); ++it) | |
| 152 delete it->second; | |
| 153 for (CachedShapingResultsLRU::iterator it = m_harfBuzzRunLRU.begin(); it !=
m_harfBuzzRunLRU.end(); ++it) | |
| 154 delete *it; | |
| 155 } | |
| 156 | |
| 157 bool HarfBuzzRunCache::insert(const std::wstring& key, CachedShapingResults* dat
a) | |
| 158 { | |
| 159 std::pair<CachedShapingResultsMap::iterator, bool> results = | |
| 160 m_harfBuzzRunMap.insert(CachedShapingResultsMap::value_type(key, data)); | |
| 161 | |
| 162 if (!results.second) | |
| 163 return false; | |
| 164 | |
| 165 CachedShapingResultsLRUNode* node = new CachedShapingResultsLRUNode(results.
first); | |
| 166 | |
| 167 m_harfBuzzRunLRU.push_back(node); | |
| 168 data->lru = --m_harfBuzzRunLRU.end(); | |
| 169 | |
| 170 if (m_harfBuzzRunMap.size() > cHarfBuzzCacheMaxSize) { | |
| 171 CachedShapingResultsLRUNode* lru = m_harfBuzzRunLRU.front(); | |
| 172 CachedShapingResults* foo = lru->entry->second; | |
| 173 m_harfBuzzRunMap.erase(lru->entry); | |
| 174 m_harfBuzzRunLRU.pop_front(); | |
| 175 delete foo; | |
| 176 delete lru; | |
| 177 } | |
| 178 | |
| 179 return true; | |
| 180 } | |
| 181 | |
| 182 inline CachedShapingResults* HarfBuzzRunCache::find(const std::wstring& key) con
st | |
| 183 { | |
| 184 CachedShapingResultsMap::const_iterator it = m_harfBuzzRunMap.find(key); | |
| 185 | |
| 186 return it != m_harfBuzzRunMap.end() ? it->second : 0; | |
| 187 } | |
| 188 | |
| 189 inline void HarfBuzzRunCache::remove(CachedShapingResults* node) | |
| 190 { | |
| 191 CachedShapingResultsLRUNode* lruNode = *node->lru; | |
| 192 | |
| 193 m_harfBuzzRunLRU.erase(node->lru); | |
| 194 m_harfBuzzRunMap.erase(lruNode->entry); | |
| 195 delete lruNode; | |
| 196 delete node; | |
| 197 } | |
| 198 | |
| 199 inline void HarfBuzzRunCache::moveToBack(CachedShapingResults* node) | |
| 200 { | |
| 201 CachedShapingResultsLRUNode* lruNode = *node->lru; | |
| 202 m_harfBuzzRunLRU.erase(node->lru); | |
| 203 m_harfBuzzRunLRU.push_back(lruNode); | |
| 204 node->lru = --m_harfBuzzRunLRU.end(); | |
| 205 } | |
| 206 | |
| 207 HarfBuzzRunCache& harfBuzzRunCache() | |
| 208 { | |
| 209 DEFINE_STATIC_LOCAL(HarfBuzzRunCache, globalHarfBuzzRunCache, ()); | |
| 210 return globalHarfBuzzRunCache; | |
| 211 } | |
| 212 | |
| 213 static inline float harfBuzzPositionToFloat(hb_position_t value) | 527 static inline float harfBuzzPositionToFloat(hb_position_t value) |
| 214 { | 528 { |
| 215 return static_cast<float>(value) / (1 << 16); | 529 return static_cast<float>(value) / (1 << 16); |
| 216 } | 530 } |
| 217 | 531 |
| 218 static inline unsigned countGraphemesInCluster(const UChar* normalizedBuffer, un
signed normalizedBufferLength, uint16_t startIndex, uint16_t endIndex) | |
| 219 { | |
| 220 if (startIndex > endIndex) { | |
| 221 uint16_t tempIndex = startIndex; | |
| 222 startIndex = endIndex; | |
| 223 endIndex = tempIndex; | |
| 224 } | |
| 225 uint16_t length = endIndex - startIndex; | |
| 226 ASSERT(static_cast<unsigned>(startIndex + length) <= normalizedBufferLength)
; | |
| 227 TextBreakIterator* cursorPosIterator = cursorMovementIterator(&normalizedBuf
fer[startIndex], length); | |
| 228 | |
| 229 int cursorPos = cursorPosIterator->current(); | |
| 230 int numGraphemes = -1; | |
| 231 while (0 <= cursorPos) { | |
| 232 cursorPos = cursorPosIterator->next(); | |
| 233 numGraphemes++; | |
| 234 } | |
| 235 return numGraphemes < 0 ? 0 : numGraphemes; | |
| 236 } | |
| 237 | |
| 238 inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData,
unsigned startIndex, unsigned numCharacters, hb_direction_t direction, hb_script
_t script) | 532 inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData,
unsigned startIndex, unsigned numCharacters, hb_direction_t direction, hb_script
_t script) |
| 239 : m_fontData(fontData) | 533 : m_fontData(fontData) |
| 240 , m_startIndex(startIndex) | 534 , m_startIndex(startIndex) |
| 241 , m_numCharacters(numCharacters) | 535 , m_numCharacters(numCharacters) |
| 242 , m_numGlyphs(0) | |
| 243 , m_direction(direction) | 536 , m_direction(direction) |
| 244 , m_script(script) | 537 , m_script(script) |
| 245 , m_width(0) | 538 { |
| 246 { | 539 } |
| 247 } | 540 |
| 248 | |
| 249 inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const HarfBuzzRun& rhs) | 541 inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const HarfBuzzRun& rhs) |
| 250 : m_fontData(rhs.m_fontData) | 542 : m_fontData(rhs.m_fontData) |
| 251 , m_startIndex(rhs.m_startIndex) | 543 , m_startIndex(rhs.m_startIndex) |
| 252 , m_numCharacters(rhs.m_numCharacters) | 544 , m_numCharacters(rhs.m_numCharacters) |
| 253 , m_numGlyphs(rhs.m_numGlyphs) | |
| 254 , m_direction(rhs.m_direction) | 545 , m_direction(rhs.m_direction) |
| 255 , m_script(rhs.m_script) | 546 , m_script(rhs.m_script) |
| 256 , m_glyphData(rhs.m_glyphData) | |
| 257 , m_width(rhs.m_width) | |
| 258 { | 547 { |
| 259 } | 548 } |
| 260 | 549 |
| 261 HarfBuzzShaper::HarfBuzzRun::~HarfBuzzRun() | 550 HarfBuzzShaper::HarfBuzzRun::~HarfBuzzRun() |
| 262 { | 551 { |
| 263 } | 552 } |
| 264 | 553 |
| 265 inline void HarfBuzzShaper::HarfBuzzRun::applyShapeResult(hb_buffer_t* harfBuzzB
uffer) | |
| 266 { | |
| 267 m_numGlyphs = hb_buffer_get_length(harfBuzzBuffer); | |
| 268 m_glyphData.resize(m_numGlyphs); | |
| 269 } | |
| 270 | |
| 271 inline void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, ui
nt16_t glyphId, float advance, float offsetX, float offsetY) | |
| 272 { | |
| 273 HarfBuzzRunGlyphData& data = glyphData(index); | |
| 274 data.glyph = glyphId; | |
| 275 data.advance = advance; | |
| 276 data.offset = FloatSize(offsetX, offsetY); | |
| 277 } | |
| 278 | |
| 279 void HarfBuzzShaper::HarfBuzzRun::addAdvance(unsigned index, float advance) | |
| 280 { | |
| 281 glyphData(index).advance += advance; | |
| 282 } | |
| 283 | |
| 284 int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX) | |
| 285 { | |
| 286 ASSERT(targetX <= m_width); | |
| 287 float currentX = 0; | |
| 288 float currentAdvance = m_glyphData[0].advance; | |
| 289 unsigned glyphIndex = 0; | |
| 290 | |
| 291 // Sum up advances that belong to a character. | |
| 292 while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].characterInde
x == m_glyphData[glyphIndex + 1].characterIndex) | |
| 293 currentAdvance += m_glyphData[++glyphIndex].advance; | |
| 294 currentAdvance = currentAdvance / 2.0; | |
| 295 if (targetX <= currentAdvance) | |
| 296 return rtl() ? m_numCharacters : 0; | |
| 297 | |
| 298 currentX = currentAdvance; | |
| 299 ++glyphIndex; | |
| 300 while (glyphIndex < m_numGlyphs) { | |
| 301 unsigned prevCharacterIndex = m_glyphData[glyphIndex - 1].characterIndex
; | |
| 302 float prevAdvance = currentAdvance; | |
| 303 currentAdvance = m_glyphData[glyphIndex].advance; | |
| 304 while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].character
Index == m_glyphData[glyphIndex + 1].characterIndex) | |
| 305 currentAdvance += m_glyphData[++glyphIndex].advance; | |
| 306 currentAdvance = currentAdvance / 2.0; | |
| 307 float nextX = currentX + prevAdvance + currentAdvance; | |
| 308 if (currentX <= targetX && targetX <= nextX) | |
| 309 return rtl() ? prevCharacterIndex : m_glyphData[glyphIndex].characte
rIndex; | |
| 310 currentX = nextX; | |
| 311 ++glyphIndex; | |
| 312 } | |
| 313 | |
| 314 return rtl() ? 0 : m_numCharacters; | |
| 315 } | |
| 316 | |
| 317 float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset) | |
| 318 { | |
| 319 ASSERT(offset < m_numCharacters); | |
| 320 unsigned glyphIndex = 0; | |
| 321 float position = 0; | |
| 322 if (rtl()) { | |
| 323 while (glyphIndex < m_numGlyphs && m_glyphData[glyphIndex].characterInde
x > offset) { | |
| 324 position += m_glyphData[glyphIndex].advance; | |
| 325 ++glyphIndex; | |
| 326 } | |
| 327 // For RTL, we need to return the right side boundary of the character. | |
| 328 // Add advance of glyphs which are part of the character. | |
| 329 while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].character
Index == m_glyphData[glyphIndex + 1].characterIndex) { | |
| 330 position += m_glyphData[glyphIndex].advance; | |
| 331 ++glyphIndex; | |
| 332 } | |
| 333 position += m_glyphData[glyphIndex].advance; | |
| 334 } else { | |
| 335 while (glyphIndex < m_numGlyphs && m_glyphData[glyphIndex].characterInde
x < offset) { | |
| 336 position += m_glyphData[glyphIndex].advance; | |
| 337 ++glyphIndex; | |
| 338 } | |
| 339 } | |
| 340 return position; | |
| 341 } | |
| 342 | |
| 343 static void normalizeCharacters(const TextRun& run, unsigned length, UChar* dest
ination, unsigned* destinationLength) | 554 static void normalizeCharacters(const TextRun& run, unsigned length, UChar* dest
ination, unsigned* destinationLength) |
| 344 { | 555 { |
| 345 unsigned position = 0; | 556 unsigned position = 0; |
| 346 bool error = false; | 557 bool error = false; |
| 347 const UChar* source; | 558 const UChar* source; |
| 348 String stringFor8BitRun; | 559 String stringFor8BitRun; |
| 349 if (run.is8Bit()) { | 560 if (run.is8Bit()) { |
| 350 stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), ru
n.length()); | 561 stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), ru
n.length()); |
| 351 source = stringFor8BitRun.characters16(); | 562 source = stringFor8BitRun.characters16(); |
| 352 } else { | 563 } else { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 363 else if (Character::treatAsSpace(character) && character != tabulationCh
aracter) | 574 else if (Character::treatAsSpace(character) && character != tabulationCh
aracter) |
| 364 character = spaceCharacter; | 575 character = spaceCharacter; |
| 365 else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) | 576 else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) |
| 366 character = zeroWidthSpaceCharacter; | 577 character = zeroWidthSpaceCharacter; |
| 367 | 578 |
| 368 U16_APPEND(destination, *destinationLength, length, character, error); | 579 U16_APPEND(destination, *destinationLength, length, character, error); |
| 369 ASSERT_UNUSED(error, !error); | 580 ASSERT_UNUSED(error, !error); |
| 370 } | 581 } |
| 371 } | 582 } |
| 372 | 583 |
| 373 HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, const Glyph
Data* emphasisData, | 584 HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, |
| 374 HashSet<const SimpleFontData*>* fallbackFonts, FloatRect* bounds) | 585 HashSet<const SimpleFontData*>* fallbackFonts) |
| 375 : Shaper(font, run, emphasisData, fallbackFonts, bounds) | 586 : Shaper(font, run, nullptr, fallbackFonts) |
| 376 , m_normalizedBufferLength(0) | 587 , m_normalizedBufferLength(0) |
| 377 , m_wordSpacingAdjustment(font->fontDescription().wordSpacing()) | 588 , m_wordSpacingAdjustment(font->fontDescription().wordSpacing()) |
| 378 , m_letterSpacing(font->fontDescription().letterSpacing()) | 589 , m_letterSpacing(font->fontDescription().letterSpacing()) |
| 379 , m_expansionOpportunityCount(0) | 590 , m_expansionOpportunityCount(0) |
| 380 , m_fromIndex(0) | |
| 381 , m_toIndex(m_textRun.length()) | |
| 382 , m_totalWidth(0) | |
| 383 { | 591 { |
| 384 m_normalizedBuffer = adoptArrayPtr(new UChar[m_textRun.length() + 1]); | 592 m_normalizedBuffer = adoptArrayPtr(new UChar[m_textRun.length() + 1]); |
| 385 normalizeCharacters(m_textRun, m_textRun.length(), m_normalizedBuffer.get(),
&m_normalizedBufferLength); | 593 normalizeCharacters(m_textRun, m_textRun.length(), m_normalizedBuffer.get(),
&m_normalizedBufferLength); |
| 386 setExpansion(m_textRun.expansion()); | 594 setExpansion(m_textRun.expansion()); |
| 387 setFontFeatures(); | 595 setFontFeatures(); |
| 388 } | 596 } |
| 389 | 597 |
| 390 float HarfBuzzShaper::nextExpansionPerOpportunity() | 598 float HarfBuzzShaper::nextExpansionPerOpportunity() |
| 391 { | 599 { |
| 392 if (!m_expansionOpportunityCount) { | 600 if (!m_expansionOpportunityCount) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 418 ASSERT(m_expansionOpportunityCount > 0); | 626 ASSERT(m_expansionOpportunityCount > 0); |
| 419 --m_expansionOpportunityCount; | 627 --m_expansionOpportunityCount; |
| 420 } | 628 } |
| 421 | 629 |
| 422 if (m_expansionOpportunityCount) | 630 if (m_expansionOpportunityCount) |
| 423 m_expansionPerOpportunity = m_expansion / m_expansionOpportunityCount; | 631 m_expansionPerOpportunity = m_expansion / m_expansionOpportunityCount; |
| 424 else | 632 else |
| 425 m_expansionPerOpportunity = 0; | 633 m_expansionPerOpportunity = 0; |
| 426 } | 634 } |
| 427 | 635 |
| 428 | |
| 429 void HarfBuzzShaper::setDrawRange(int from, int to) | |
| 430 { | |
| 431 ASSERT_WITH_SECURITY_IMPLICATION(from >= 0); | |
| 432 ASSERT_WITH_SECURITY_IMPLICATION(to <= m_textRun.length()); | |
| 433 m_fromIndex = from; | |
| 434 m_toIndex = to; | |
| 435 } | |
| 436 | |
| 437 void HarfBuzzShaper::setFontFeatures() | 636 void HarfBuzzShaper::setFontFeatures() |
| 438 { | 637 { |
| 439 const FontDescription& description = m_font->fontDescription(); | 638 const FontDescription& description = m_font->fontDescription(); |
| 440 | 639 |
| 441 static hb_feature_t noKern = { HB_TAG('k', 'e', 'r', 'n'), 0, 0, static_cast
<unsigned>(-1) }; | 640 static hb_feature_t noKern = { HB_TAG('k', 'e', 'r', 'n'), 0, 0, static_cast
<unsigned>(-1) }; |
| 442 static hb_feature_t noVkrn = { HB_TAG('v', 'k', 'r', 'n'), 0, 0, static_cast
<unsigned>(-1) }; | 641 static hb_feature_t noVkrn = { HB_TAG('v', 'k', 'r', 'n'), 0, 0, static_cast
<unsigned>(-1) }; |
| 443 switch (description.kerning()) { | 642 switch (description.kerning()) { |
| 444 case FontDescription::NormalKerning: | 643 case FontDescription::NormalKerning: |
| 445 // kern/vkrn are enabled by default | 644 // kern/vkrn are enabled by default |
| 446 break; | 645 break; |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 524 hb_feature_t feature; | 723 hb_feature_t feature; |
| 525 const AtomicString& tag = settings->at(i).tag(); | 724 const AtomicString& tag = settings->at(i).tag(); |
| 526 feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]); | 725 feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]); |
| 527 feature.value = settings->at(i).value(); | 726 feature.value = settings->at(i).value(); |
| 528 feature.start = 0; | 727 feature.start = 0; |
| 529 feature.end = static_cast<unsigned>(-1); | 728 feature.end = static_cast<unsigned>(-1); |
| 530 m_features.append(feature); | 729 m_features.append(feature); |
| 531 } | 730 } |
| 532 } | 731 } |
| 533 | 732 |
| 534 bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer) | 733 PassRefPtr<ShapeResult> HarfBuzzShaper::shapeResult() |
| 535 { | 734 { |
| 536 if (!createHarfBuzzRuns()) | 735 if (!createHarfBuzzRuns()) |
| 537 return false; | 736 return nullptr; |
| 538 | 737 |
| 539 if (!shapeHarfBuzzRuns()) | 738 ShapeResult* result = new ShapeResult(); |
| 540 return false; | 739 result->m_numCharacters = m_normalizedBufferLength; |
| 740 result->m_direction = m_textRun.direction(); |
| 541 | 741 |
| 542 if (glyphBuffer && !fillGlyphBuffer(glyphBuffer)) | 742 if (!shapeHarfBuzzRuns(result)) { |
| 543 return false; | 743 delete result; |
| 744 return nullptr; |
| 745 } |
| 544 | 746 |
| 545 return true; | 747 return adoptRef(result); |
| 546 } | 748 } |
| 547 | 749 |
| 548 struct CandidateRun { | 750 struct CandidateRun { |
| 549 UChar32 character; | 751 UChar32 character; |
| 550 unsigned start; | 752 unsigned start; |
| 551 unsigned end; | 753 unsigned end; |
| 552 const SimpleFontData* fontData; | 754 const SimpleFontData* fontData; |
| 553 UScriptCode script; | 755 UScriptCode script; |
| 554 }; | 756 }; |
| 555 | 757 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 590 if (U_FAILURE(errorCode)) | 792 if (U_FAILURE(errorCode)) |
| 591 return false; | 793 return false; |
| 592 if (lastCharacter == zeroWidthJoinerCharacter) | 794 if (lastCharacter == zeroWidthJoinerCharacter) |
| 593 currentFontData = nextFontData; | 795 currentFontData = nextFontData; |
| 594 if ((nextFontData != currentFontData) || ((currentScript != nextScri
pt) && (nextScript != USCRIPT_INHERITED) && (!uscript_hasScript(character, curre
ntScript)))) | 796 if ((nextFontData != currentFontData) || ((currentScript != nextScri
pt) && (nextScript != USCRIPT_INHERITED) && (!uscript_hasScript(character, curre
ntScript)))) |
| 595 break; | 797 break; |
| 596 currentCharacterPosition = iterator.characters(); | 798 currentCharacterPosition = iterator.characters(); |
| 597 lastCharacter = character; | 799 lastCharacter = character; |
| 598 } | 800 } |
| 599 | 801 |
| 600 CandidateRun run = { character, startIndexOfCurrentRun, static_cast<unsi
gned>(iterator.offset()), currentFontData, currentScript }; | 802 CandidateRun run = { lastCharacter, startIndexOfCurrentRun, static_cast<
unsigned>(iterator.offset()), currentFontData, currentScript }; |
| 601 runs->append(run); | 803 runs->append(run); |
| 602 | 804 |
| 603 startIndexOfCurrentRun = iterator.offset(); | 805 startIndexOfCurrentRun = iterator.offset(); |
| 604 } while (iterator.consume(character)); | 806 } while (iterator.consume(character)); |
| 605 | 807 |
| 606 return true; | 808 return true; |
| 607 } | 809 } |
| 608 | 810 |
| 609 static inline bool matchesAdjacentRun(UScriptCode* scriptExtensions, int length, | 811 static inline bool matchesAdjacentRun(UScriptCode* scriptExtensions, int length, |
| 610 CandidateRun& adjacentRun) | 812 CandidateRun& adjacentRun) |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 759 ASSERT(endCharacter > startCharacter); | 961 ASSERT(endCharacter > startCharacter); |
| 760 ASSERT(script != USCRIPT_INVALID_CODE); | 962 ASSERT(script != USCRIPT_INVALID_CODE); |
| 761 if (m_fallbackFonts) | 963 if (m_fallbackFonts) |
| 762 trackNonPrimaryFallbackFont(fontData); | 964 trackNonPrimaryFallbackFont(fontData); |
| 763 return m_harfBuzzRuns.append(HarfBuzzRun::create(fontData, | 965 return m_harfBuzzRuns.append(HarfBuzzRun::create(fontData, |
| 764 startCharacter, endCharacter - startCharacter, | 966 startCharacter, endCharacter - startCharacter, |
| 765 TextDirectionToHBDirection(m_textRun.direction(), m_font->fontDescriptio
n().orientation(), fontData), | 967 TextDirectionToHBDirection(m_textRun.direction(), m_font->fontDescriptio
n().orientation(), fontData), |
| 766 ICUScriptToHBScript(script))); | 968 ICUScriptToHBScript(script))); |
| 767 } | 969 } |
| 768 | 970 |
| 769 static inline bool isValidCachedResult(const Font* font, hb_direction_t dir, | |
| 770 const String& localeString, const CachedShapingResults* cachedResults) | |
| 771 { | |
| 772 ASSERT(cachedResults); | |
| 773 return cachedResults->dir == dir | |
| 774 && cachedResults->font == *font | |
| 775 && !cachedResults->font.loadingCustomFonts() | |
| 776 && !font->loadingCustomFonts() | |
| 777 && cachedResults->locale == localeString; | |
| 778 } | |
| 779 | |
| 780 static const uint16_t* toUint16(const UChar* src) | 971 static const uint16_t* toUint16(const UChar* src) |
| 781 { | 972 { |
| 782 // FIXME: This relies on undefined behavior however it works on the | 973 // FIXME: This relies on undefined behavior however it works on the |
| 783 // current versions of all compilers we care about and avoids making | 974 // current versions of all compilers we care about and avoids making |
| 784 // a copy of the string. | 975 // a copy of the string. |
| 785 static_assert(sizeof(UChar) == sizeof(uint16_t), "UChar should be the same s
ize as uint16_t"); | 976 static_assert(sizeof(UChar) == sizeof(uint16_t), "UChar should be the same s
ize as uint16_t"); |
| 786 return reinterpret_cast<const uint16_t*>(src); | 977 return reinterpret_cast<const uint16_t*>(src); |
| 787 } | 978 } |
| 788 | 979 |
| 789 static inline void addToHarfBuzzBufferInternal(hb_buffer_t* buffer, | 980 static inline void addToHarfBuzzBufferInternal(hb_buffer_t* buffer, |
| 790 const FontDescription& fontDescription, const UChar* normalizedBuffer, | 981 const FontDescription& fontDescription, const UChar* normalizedBuffer, |
| 791 unsigned startIndex, unsigned numCharacters) | 982 unsigned startIndex, unsigned numCharacters) |
| 792 { | 983 { |
| 793 if (fontDescription.variant() == FontVariantSmallCaps | 984 if (fontDescription.variant() == FontVariantSmallCaps |
| 794 && u_islower(normalizedBuffer[startIndex])) { | 985 && u_islower(normalizedBuffer[startIndex])) { |
| 795 String upperText = String(normalizedBuffer + startIndex, numCharacters) | 986 String upperText = String(normalizedBuffer + startIndex, numCharacters) |
| 796 .upper(); | 987 .upper(); |
| 797 // TextRun is 16 bit, therefore upperText is 16 bit, even after we call | 988 // TextRun is 16 bit, therefore upperText is 16 bit, even after we call |
| 798 // makeUpper(). | 989 // makeUpper(). |
| 799 ASSERT(!upperText.is8Bit()); | 990 ASSERT(!upperText.is8Bit()); |
| 800 hb_buffer_add_utf16(buffer, toUint16(upperText.characters16()), | 991 hb_buffer_add_utf16(buffer, toUint16(upperText.characters16()), |
| 801 numCharacters, 0, numCharacters); | 992 numCharacters, 0, numCharacters); |
| 802 } else { | 993 } else { |
| 803 hb_buffer_add_utf16(buffer, toUint16(normalizedBuffer + startIndex), | 994 hb_buffer_add_utf16(buffer, toUint16(normalizedBuffer + startIndex), |
| 804 numCharacters, 0, numCharacters); | 995 numCharacters, 0, numCharacters); |
| 805 } | 996 } |
| 806 } | 997 } |
| 807 | 998 |
| 808 bool HarfBuzzShaper::shapeHarfBuzzRuns() | 999 bool HarfBuzzShaper::shapeHarfBuzzRuns(ShapeResult* result) |
| 809 { | 1000 { |
| 810 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_
destroy); | 1001 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_
destroy); |
| 811 | 1002 |
| 812 HarfBuzzRunCache& runCache = harfBuzzRunCache(); | |
| 813 const FontDescription& fontDescription = m_font->fontDescription(); | 1003 const FontDescription& fontDescription = m_font->fontDescription(); |
| 814 const String& localeString = fontDescription.locale(); | 1004 const String& localeString = fontDescription.locale(); |
| 815 CString locale = localeString.latin1(); | 1005 CString locale = localeString.latin1(); |
| 816 const hb_language_t language = hb_language_from_string(locale.data(), locale
.length()); | 1006 const hb_language_t language = hb_language_from_string(locale.data(), locale
.length()); |
| 817 | 1007 |
| 1008 result->m_runs.resize(m_harfBuzzRuns.size()); |
| 818 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { | 1009 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { |
| 819 unsigned runIndex = m_textRun.rtl() ? m_harfBuzzRuns.size() - i - 1 : i; | 1010 unsigned runIndex = m_textRun.rtl() ? m_harfBuzzRuns.size() - i - 1 : i; |
| 820 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); | 1011 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); |
| 821 | 1012 |
| 822 const SimpleFontData* currentFontData = currentRun->fontData(); | 1013 const SimpleFontData* currentFontData = currentRun->fontData(); |
| 823 FontPlatformData* platformData = const_cast<FontPlatformData*>(¤tF
ontData->platformData()); | 1014 FontPlatformData* platformData = const_cast<FontPlatformData*>(¤tF
ontData->platformData()); |
| 824 HarfBuzzFace* face = platformData->harfBuzzFace(); | 1015 HarfBuzzFace* face = platformData->harfBuzzFace(); |
| 825 if (!face) | 1016 if (!face) |
| 826 return false; | 1017 return false; |
| 827 | 1018 |
| 828 hb_buffer_set_language(harfBuzzBuffer.get(), language); | 1019 hb_buffer_set_language(harfBuzzBuffer.get(), language); |
| 829 hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script()); | 1020 hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script()); |
| 830 hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->direction()); | 1021 hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->direction()); |
| 831 | 1022 |
| 832 const UChar* src = m_normalizedBuffer.get() + currentRun->startIndex(); | |
| 833 std::wstring key(src, src + currentRun->numCharacters()); | |
| 834 | |
| 835 CachedShapingResults* cachedResults = runCache.find(key); | |
| 836 if (cachedResults) { | |
| 837 if (isValidCachedResult(m_font, currentRun->direction(), | |
| 838 localeString, cachedResults)) { | |
| 839 currentRun->applyShapeResult(cachedResults->buffer); | |
| 840 setGlyphPositionsForHarfBuzzRun(currentRun, | |
| 841 cachedResults->buffer); | |
| 842 hb_buffer_clear_contents(harfBuzzBuffer.get()); | |
| 843 runCache.moveToBack(cachedResults); | |
| 844 continue; | |
| 845 } | |
| 846 runCache.remove(cachedResults); | |
| 847 } | |
| 848 | |
| 849 // Add a space as pre-context to the buffer. This prevents showing dotte
d-circle | 1023 // Add a space as pre-context to the buffer. This prevents showing dotte
d-circle |
| 850 // for combining marks at the beginning of runs. | 1024 // for combining marks at the beginning of runs. |
| 851 static const uint16_t preContext = spaceCharacter; | 1025 static const uint16_t preContext = spaceCharacter; |
| 852 hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0); | 1026 hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0); |
| 853 | 1027 |
| 854 addToHarfBuzzBufferInternal(harfBuzzBuffer.get(), | 1028 addToHarfBuzzBufferInternal(harfBuzzBuffer.get(), |
| 855 fontDescription, m_normalizedBuffer.get(), currentRun->startIndex(), | 1029 fontDescription, m_normalizedBuffer.get(), currentRun->startIndex(), |
| 856 currentRun->numCharacters()); | 1030 currentRun->numCharacters()); |
| 857 | 1031 |
| 858 if (fontDescription.isVerticalAnyUpright()) | 1032 if (fontDescription.isVerticalAnyUpright()) |
| 859 face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get()); | 1033 face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get()); |
| 860 | 1034 |
| 861 HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(), hb_font_de
stroy); | 1035 HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(), hb_font_de
stroy); |
| 1036 hb_shape(harfBuzzFont.get(), harfBuzzBuffer.get(), m_features.isEmpty()
? 0 : m_features.data(), m_features.size()); |
| 1037 shapeResult(result, i, currentRun, harfBuzzBuffer.get()); |
| 862 | 1038 |
| 863 hb_shape(harfBuzzFont.get(), harfBuzzBuffer.get(), m_features.isEmpty()
? 0 : m_features.data(), m_features.size()); | 1039 hb_buffer_reset(harfBuzzBuffer.get()); |
| 864 currentRun->applyShapeResult(harfBuzzBuffer.get()); | |
| 865 setGlyphPositionsForHarfBuzzRun(currentRun, harfBuzzBuffer.get()); | |
| 866 | |
| 867 runCache.insert(key, new CachedShapingResults(harfBuzzBuffer.get(), m_fo
nt, currentRun->direction(), localeString)); | |
| 868 | |
| 869 harfBuzzBuffer.set(hb_buffer_create()); | |
| 870 } | 1040 } |
| 871 | 1041 |
| 872 // We should have consumed all expansion opportunities. | 1042 // We should have consumed all expansion opportunities. |
| 873 // Failures here means that our logic does not match to the one in expansion
OpportunityCount(). | 1043 // Failures here means that our logic does not match to the one in expansion
OpportunityCount(). |
| 874 // FIXME: Ideally, we should ASSERT(!m_expansionOpportunityCount) here to en
sure that, | 1044 // FIXME: Ideally, we should ASSERT(!m_expansionOpportunityCount) here to en
sure that, |
| 875 // or unify the two logics (and the one in SimplePath too,) but there are so
me cases where our impl | 1045 // or unify the two logics (and the one in SimplePath too,) but there are so
me cases where our impl |
| 876 // does not support justification very well yet such as U+3099, and it'll ca
use the ASSERT to fail. | 1046 // does not support justification very well yet such as U+3099, and it'll ca
use the ASSERT to fail. |
| 877 // It's to be fixed because they're very rarely used, and a broken justifica
tion is still somewhat readable. | 1047 // It's to be fixed because they're very rarely used, and a broken justifica
tion is still somewhat readable. |
| 878 | 1048 |
| 879 return true; | 1049 return true; |
| 880 } | 1050 } |
| 881 | 1051 |
| 882 void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb
_buffer_t* harfBuzzBuffer) | 1052 void HarfBuzzShaper::shapeResult(ShapeResult* result, unsigned index, |
| 1053 HarfBuzzRun* currentRun, hb_buffer_t* harfBuzzBuffer) |
| 883 { | 1054 { |
| 884 // Skip runs that only contain control characters. | 1055 unsigned numGlyphs = hb_buffer_get_length(harfBuzzBuffer); |
| 885 if (!currentRun->numGlyphs()) | 1056 if (!numGlyphs) { |
| 1057 result->m_runs[index] = nullptr; |
| 886 return; | 1058 return; |
| 1059 } |
| 1060 |
| 1061 ShapeResult::RunInfo* run = new ShapeResult::RunInfo(currentRun->fontData(), |
| 1062 currentRun->direction(), currentRun->script(), currentRun->startIndex(), |
| 1063 numGlyphs, currentRun->numCharacters()); |
| 1064 result->m_runs[index] = run; |
| 1065 result->m_numGlyphs += numGlyphs; |
| 887 | 1066 |
| 888 const SimpleFontData* currentFontData = currentRun->fontData(); | 1067 const SimpleFontData* currentFontData = currentRun->fontData(); |
| 889 hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); | 1068 hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); |
| 890 hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzz
Buffer, 0); | 1069 hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzz
Buffer, 0); |
| 891 | 1070 |
| 892 unsigned numGlyphs = currentRun->numGlyphs(); | |
| 893 float totalAdvance = 0; | 1071 float totalAdvance = 0; |
| 894 FloatPoint glyphOrigin; | 1072 FloatPoint glyphOrigin; |
| 895 float offsetX, offsetY; | 1073 float offsetX, offsetY; |
| 896 float* directionOffset = m_font->fontDescription().isVerticalAnyUpright() ?
&offsetY : &offsetX; | 1074 float* directionOffset = m_font->fontDescription().isVerticalAnyUpright() ?
&offsetY : &offsetX; |
| 897 | 1075 |
| 898 // HarfBuzz returns the shaping result in visual order. We need not to flip
for RTL. | 1076 // HarfBuzz returns result in visual order, no need to flip for RTL. |
| 899 for (size_t i = 0; i < numGlyphs; ++i) { | 1077 for (size_t i = 0; i < numGlyphs; ++i) { |
| 900 bool runEnd = i + 1 == numGlyphs; | 1078 bool runEnd = i + 1 == numGlyphs; |
| 901 uint16_t glyph = glyphInfos[i].codepoint; | 1079 uint16_t glyph = glyphInfos[i].codepoint; |
| 902 offsetX = harfBuzzPositionToFloat(glyphPositions[i].x_offset); | 1080 offsetX = harfBuzzPositionToFloat(glyphPositions[i].x_offset); |
| 903 offsetY = -harfBuzzPositionToFloat(glyphPositions[i].y_offset); | 1081 offsetY = -harfBuzzPositionToFloat(glyphPositions[i].y_offset); |
| 1082 |
| 904 // One out of x_advance and y_advance is zero, depending on | 1083 // One out of x_advance and y_advance is zero, depending on |
| 905 // whether the buffer direction is horizontal or vertical. | 1084 // whether the buffer direction is horizontal or vertical. |
| 906 float advance = harfBuzzPositionToFloat(glyphPositions[i].x_advance - gl
yphPositions[i].y_advance); | 1085 float advance = harfBuzzPositionToFloat(glyphPositions[i].x_advance - gl
yphPositions[i].y_advance); |
| 907 | |
| 908 unsigned currentCharacterIndex = currentRun->startIndex() + glyphInfos[i
].cluster; | 1086 unsigned currentCharacterIndex = currentRun->startIndex() + glyphInfos[i
].cluster; |
| 909 RELEASE_ASSERT(m_normalizedBufferLength > currentCharacterIndex); | 1087 RELEASE_ASSERT(m_normalizedBufferLength > currentCharacterIndex); |
| 910 bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1]
.cluster; | 1088 bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1]
.cluster; |
| 911 float spacing = 0; | 1089 float spacing = 0; |
| 912 | 1090 |
| 913 currentRun->glyphData(i).characterIndex = glyphInfos[i].cluster; | 1091 run->m_glyphData[i].characterIndex = glyphInfos[i].cluster; |
| 914 | 1092 |
| 915 if (isClusterEnd) | 1093 if (isClusterEnd) |
| 916 spacing += adjustSpacing(currentRun, i, currentCharacterIndex, *dire
ctionOffset, totalAdvance); | 1094 spacing += adjustSpacing(run, i, currentCharacterIndex, *directionOf
fset, totalAdvance); |
| 917 | 1095 |
| 918 if (currentFontData->isZeroWidthSpaceGlyph(glyph)) { | 1096 if (currentFontData->isZeroWidthSpaceGlyph(glyph)) { |
| 919 currentRun->setGlyphAndPositions(i, glyph, 0, 0, 0); | 1097 run->setGlyphAndPositions(i, glyph, 0, 0, 0); |
| 920 continue; | 1098 continue; |
| 921 } | 1099 } |
| 922 | 1100 |
| 923 advance += spacing; | 1101 advance += spacing; |
| 924 if (m_textRun.rtl()) { | 1102 if (m_textRun.rtl()) { |
| 925 // In RTL, spacing should be added to left side of glyphs. | 1103 // In RTL, spacing should be added to left side of glyphs. |
| 926 *directionOffset += spacing; | 1104 *directionOffset += spacing; |
| 927 if (!isClusterEnd) | 1105 if (!isClusterEnd) |
| 928 *directionOffset += m_letterSpacing; | 1106 *directionOffset += m_letterSpacing; |
| 929 } | 1107 } |
| 930 | 1108 |
| 931 currentRun->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY); | 1109 run->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY); |
| 1110 totalAdvance += advance; |
| 932 | 1111 |
| 933 if (m_glyphBoundingBox) { | 1112 FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph); |
| 934 FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph); | 1113 glyphBounds.move(glyphOrigin.x(), glyphOrigin.y()); |
| 935 glyphBounds.move(glyphOrigin.x(), glyphOrigin.y()); | 1114 result->m_glyphBoundingBox.unite(glyphBounds); |
| 936 m_glyphBoundingBox->unite(glyphBounds); | 1115 glyphOrigin += FloatSize(advance + offsetX, offsetY); |
| 937 glyphOrigin += FloatSize(advance + offsetX, offsetY); | 1116 } |
| 938 } | |
| 939 | 1117 |
| 940 totalAdvance += advance; | 1118 run->m_width = totalAdvance > 0.0 ? totalAdvance : 0.0; |
| 941 } | 1119 result->m_width += run->m_width; |
| 942 currentRun->setWidth(totalAdvance > 0.0 ? totalAdvance : 0.0); | |
| 943 m_totalWidth += currentRun->width(); | |
| 944 } | 1120 } |
| 945 | 1121 |
| 946 float HarfBuzzShaper::adjustSpacing(HarfBuzzRun* currentRun, size_t glyphIndex,
unsigned currentCharacterIndex, float& offset, float& totalAdvance) | 1122 float HarfBuzzShaper::adjustSpacing(ShapeResult::RunInfo* run, size_t glyphIndex
, unsigned currentCharacterIndex, float& offset, float& totalAdvance) |
| 947 { | 1123 { |
| 948 float spacing = 0; | 1124 float spacing = 0; |
| 949 UChar32 character = m_normalizedBuffer[currentCharacterIndex]; | 1125 UChar32 character = m_normalizedBuffer[currentCharacterIndex]; |
| 950 if (m_letterSpacing && !Character::treatAsZeroWidthSpace(character)) | 1126 if (m_letterSpacing && !Character::treatAsZeroWidthSpace(character)) |
| 951 spacing += m_letterSpacing; | 1127 spacing += m_letterSpacing; |
| 952 | 1128 |
| 953 bool treatAsSpace = Character::treatAsSpace(character); | 1129 bool treatAsSpace = Character::treatAsSpace(character); |
| 954 if (treatAsSpace && currentCharacterIndex && (character != '\t' || !m_textRu
n.allowTabs())) | 1130 if (treatAsSpace && currentCharacterIndex && (character != '\t' || !m_textRu
n.allowTabs())) |
| 955 spacing += m_wordSpacingAdjustment; | 1131 spacing += m_wordSpacingAdjustment; |
| 956 | 1132 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 975 if (!Character::isCJKIdeographOrSymbol(character)) { | 1151 if (!Character::isCJKIdeographOrSymbol(character)) { |
| 976 m_isAfterExpansion = false; | 1152 m_isAfterExpansion = false; |
| 977 return spacing; | 1153 return spacing; |
| 978 } | 1154 } |
| 979 | 1155 |
| 980 if (!m_isAfterExpansion) { | 1156 if (!m_isAfterExpansion) { |
| 981 // Take the expansion opportunity before this ideograph. | 1157 // Take the expansion opportunity before this ideograph. |
| 982 float expandBefore = nextExpansionPerOpportunity(); | 1158 float expandBefore = nextExpansionPerOpportunity(); |
| 983 if (expandBefore) { | 1159 if (expandBefore) { |
| 984 if (glyphIndex > 0) { | 1160 if (glyphIndex > 0) { |
| 985 currentRun->addAdvance(glyphIndex - 1, expandBefore); | 1161 run->addAdvance(glyphIndex - 1, expandBefore); |
| 986 totalAdvance += expandBefore; | 1162 totalAdvance += expandBefore; |
| 987 } else { | 1163 } else { |
| 988 offset += expandBefore; | 1164 offset += expandBefore; |
| 989 spacing += expandBefore; | 1165 spacing += expandBefore; |
| 990 } | 1166 } |
| 991 } | 1167 } |
| 992 if (!m_expansionOpportunityCount) | 1168 if (!m_expansionOpportunityCount) |
| 993 return spacing; | 1169 return spacing; |
| 994 } | 1170 } |
| 995 | 1171 |
| 996 // Don't need to check m_textRun.allowsTrailingExpansion() since it's covere
d by !m_expansionOpportunityCount above | 1172 // Don't need to check m_textRun.allowsTrailingExpansion() since it's covere
d by !m_expansionOpportunityCount above |
| 997 spacing += nextExpansionPerOpportunity(); | 1173 spacing += nextExpansionPerOpportunity(); |
| 998 m_isAfterExpansion = true; | 1174 m_isAfterExpansion = true; |
| 999 return spacing; | 1175 return spacing; |
| 1000 } | 1176 } |
| 1001 | 1177 |
| 1002 float HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, | |
| 1003 HarfBuzzRun* currentRun, float initialAdvance) | |
| 1004 { | |
| 1005 unsigned numGlyphs = currentRun->numGlyphs(); | |
| 1006 float advanceSoFar = initialAdvance; | |
| 1007 if (m_textRun.rtl()) { | |
| 1008 for (unsigned i = 0; i < numGlyphs; ++i) { | |
| 1009 HarfBuzzRunGlyphData& glyphData = currentRun->glyphData(i); | |
| 1010 uint16_t currentCharacterIndex = currentRun->startIndex() + glyphDat
a.characterIndex; | |
| 1011 if (currentCharacterIndex >= m_toIndex) { | |
| 1012 advanceSoFar += glyphData.advance; | |
| 1013 } else if (currentCharacterIndex >= m_fromIndex) { | |
| 1014 FloatPoint runStartOffset = HB_DIRECTION_IS_HORIZONTAL(currentRu
n->direction()) ? | |
| 1015 FloatPoint(advanceSoFar, 0) : FloatPoint(0, advanceSoFar); | |
| 1016 glyphBuffer->add(glyphData.glyph, currentRun->fontData(), runSta
rtOffset + glyphData.offset); | |
| 1017 advanceSoFar += glyphData.advance; | |
| 1018 } | |
| 1019 } | |
| 1020 } else { | |
| 1021 for (unsigned i = 0; i < numGlyphs; ++i) { | |
| 1022 HarfBuzzRunGlyphData& glyphData = currentRun->glyphData(i); | |
| 1023 uint16_t currentCharacterIndex = currentRun->startIndex() + glyphDat
a.characterIndex; | |
| 1024 if (currentCharacterIndex < m_fromIndex) { | |
| 1025 advanceSoFar += glyphData.advance; | |
| 1026 } else if (currentCharacterIndex < m_toIndex) { | |
| 1027 FloatPoint runStartOffset = HB_DIRECTION_IS_HORIZONTAL(currentRu
n->direction()) ? | |
| 1028 FloatPoint(advanceSoFar, 0) : FloatPoint(0, advanceSoFar); | |
| 1029 glyphBuffer->add(glyphData.glyph, currentRun->fontData(), runSta
rtOffset + glyphData.offset); | |
| 1030 advanceSoFar += glyphData.advance; | |
| 1031 } | |
| 1032 } | |
| 1033 } | |
| 1034 | |
| 1035 return advanceSoFar - initialAdvance; | |
| 1036 } | |
| 1037 | |
| 1038 float HarfBuzzShaper::fillGlyphBufferForTextEmphasis(GlyphBuffer* glyphBuffer, H
arfBuzzRun* currentRun, float initialAdvance) | |
| 1039 { | |
| 1040 unsigned numGlyphs = currentRun->numGlyphs(); | |
| 1041 unsigned graphemesInCluster = 1; | |
| 1042 float clusterAdvance = 0; | |
| 1043 uint16_t clusterStart; | |
| 1044 | |
| 1045 // A "cluster" in this context means a cluster as it is used by HarfBuzz: | |
| 1046 // The minimal group of characters and corresponding glyphs, that cannot be
broken | |
| 1047 // down further from a text shaping point of view. | |
| 1048 // A cluster can contain multiple glyphs and grapheme clusters, with mutuall
y | |
| 1049 // overlapping boundaries. Below we count grapheme clusters per HarfBuzz clu
sters, | |
| 1050 // then linearly split the sum of corresponding glyph advances by the number
of | |
| 1051 // grapheme clusters in order to find positions for emphasis mark drawing. | |
| 1052 | |
| 1053 if (m_textRun.rtl()) | |
| 1054 clusterStart = currentRun->startIndex() + currentRun->numCharacters(); | |
| 1055 else | |
| 1056 clusterStart = currentRun->glyphToCharacterIndex(0); | |
| 1057 | |
| 1058 float advanceSoFar = initialAdvance; | |
| 1059 for (unsigned i = 0; i < numGlyphs; ++i) { | |
| 1060 HarfBuzzRunGlyphData& glyphData = currentRun->glyphData(i); | |
| 1061 uint16_t currentCharacterIndex = currentRun->startIndex() + glyphData.ch
aracterIndex; | |
| 1062 bool isRunEnd = (i + 1 == numGlyphs); | |
| 1063 bool isClusterEnd = isRunEnd || (currentRun->glyphToCharacterIndex(i +
1) != currentCharacterIndex); | |
| 1064 | |
| 1065 if ((m_textRun.rtl() && currentCharacterIndex >= m_toIndex) || (!m_textR
un.rtl() && currentCharacterIndex < m_fromIndex)) { | |
| 1066 advanceSoFar += glyphData.advance; | |
| 1067 m_textRun.rtl() ? --clusterStart : ++clusterStart; | |
| 1068 continue; | |
| 1069 } | |
| 1070 | |
| 1071 clusterAdvance += glyphData.advance; | |
| 1072 | |
| 1073 if (isClusterEnd) { | |
| 1074 uint16_t clusterEnd; | |
| 1075 if (m_textRun.rtl()) | |
| 1076 clusterEnd = currentCharacterIndex; | |
| 1077 else | |
| 1078 clusterEnd = isRunEnd ? currentRun->startIndex() + currentRun->n
umCharacters() : currentRun->glyphToCharacterIndex(i + 1); | |
| 1079 | |
| 1080 graphemesInCluster = countGraphemesInCluster(m_normalizedBuffer.get(
), m_normalizedBufferLength, clusterStart, clusterEnd); | |
| 1081 if (!graphemesInCluster || !clusterAdvance) | |
| 1082 continue; | |
| 1083 | |
| 1084 float glyphAdvanceX = clusterAdvance / graphemesInCluster; | |
| 1085 for (unsigned j = 0; j < graphemesInCluster; ++j) { | |
| 1086 // Do not put emphasis marks on space, separator, and control ch
aracters. | |
| 1087 if (Character::canReceiveTextEmphasis(m_textRun[currentCharacter
Index])) | |
| 1088 addEmphasisMark(glyphBuffer, advanceSoFar + glyphAdvanceX /
2); | |
| 1089 | |
| 1090 advanceSoFar += glyphAdvanceX; | |
| 1091 } | |
| 1092 clusterStart = clusterEnd; | |
| 1093 clusterAdvance = 0; | |
| 1094 } | |
| 1095 } | |
| 1096 | |
| 1097 return advanceSoFar - initialAdvance; | |
| 1098 } | |
| 1099 | |
| 1100 bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer) | |
| 1101 { | |
| 1102 ASSERT(glyphBuffer); | |
| 1103 | |
| 1104 unsigned numRuns = m_harfBuzzRuns.size(); | |
| 1105 float advanceSoFar = 0; | |
| 1106 for (unsigned runIndex = 0; runIndex < numRuns; ++runIndex) { | |
| 1107 HarfBuzzRun* currentRun = m_harfBuzzRuns[m_textRun.ltr() ? runIndex : nu
mRuns - runIndex - 1].get(); | |
| 1108 // Skip runs that only contain control characters. | |
| 1109 if (!currentRun->numGlyphs()) | |
| 1110 continue; | |
| 1111 advanceSoFar += forTextEmphasis() | |
| 1112 ? fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun, advanceSoF
ar) | |
| 1113 : fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, advanceSoF
ar); | |
| 1114 } | |
| 1115 return glyphBuffer->size(); | |
| 1116 } | |
| 1117 | |
| 1118 int HarfBuzzShaper::offsetForPosition(float targetX) | |
| 1119 { | |
| 1120 int charactersSoFar = 0; | |
| 1121 float currentX = 0; | |
| 1122 | |
| 1123 if (m_textRun.rtl()) { | |
| 1124 charactersSoFar = m_normalizedBufferLength; | |
| 1125 for (int i = m_harfBuzzRuns.size() - 1; i >= 0; --i) { | |
| 1126 charactersSoFar -= m_harfBuzzRuns[i]->numCharacters(); | |
| 1127 float nextX = currentX + m_harfBuzzRuns[i]->width(); | |
| 1128 float offsetForRun = targetX - currentX; | |
| 1129 if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width())
{ | |
| 1130 // The x value in question is within this script run. | |
| 1131 const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosi
tion(offsetForRun); | |
| 1132 return charactersSoFar + index; | |
| 1133 } | |
| 1134 currentX = nextX; | |
| 1135 } | |
| 1136 } else { | |
| 1137 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { | |
| 1138 float nextX = currentX + m_harfBuzzRuns[i]->width(); | |
| 1139 float offsetForRun = targetX - currentX; | |
| 1140 if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width())
{ | |
| 1141 const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosi
tion(offsetForRun); | |
| 1142 return charactersSoFar + index; | |
| 1143 } | |
| 1144 charactersSoFar += m_harfBuzzRuns[i]->numCharacters(); | |
| 1145 currentX = nextX; | |
| 1146 } | |
| 1147 } | |
| 1148 | |
| 1149 return charactersSoFar; | |
| 1150 } | |
| 1151 | |
| 1152 FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int
from, int to) | |
| 1153 { | |
| 1154 float currentX = 0; | |
| 1155 float fromX = 0; | |
| 1156 float toX = 0; | |
| 1157 bool foundFromX = false; | |
| 1158 bool foundToX = false; | |
| 1159 | |
| 1160 if (m_textRun.rtl()) | |
| 1161 currentX = m_totalWidth; | |
| 1162 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { | |
| 1163 if (m_textRun.rtl()) | |
| 1164 currentX -= m_harfBuzzRuns[i]->width(); | |
| 1165 int numCharacters = m_harfBuzzRuns[i]->numCharacters(); | |
| 1166 if (!foundFromX && from >= 0 && from < numCharacters) { | |
| 1167 fromX = m_harfBuzzRuns[i]->xPositionForOffset(from) + currentX; | |
| 1168 foundFromX = true; | |
| 1169 } else { | |
| 1170 from -= numCharacters; | |
| 1171 } | |
| 1172 | |
| 1173 if (!foundToX && to >= 0 && to < numCharacters) { | |
| 1174 toX = m_harfBuzzRuns[i]->xPositionForOffset(to) + currentX; | |
| 1175 foundToX = true; | |
| 1176 } else { | |
| 1177 to -= numCharacters; | |
| 1178 } | |
| 1179 | |
| 1180 if (foundFromX && foundToX) | |
| 1181 break; | |
| 1182 if (!m_textRun.rtl()) | |
| 1183 currentX += m_harfBuzzRuns[i]->width(); | |
| 1184 } | |
| 1185 | |
| 1186 // The position in question might be just after the text. | |
| 1187 if (!foundFromX) | |
| 1188 fromX = 0; | |
| 1189 if (!foundToX) | |
| 1190 toX = m_textRun.rtl() ? 0 : m_totalWidth; | |
| 1191 // None of our HarfBuzzRuns is part of the selection, | |
| 1192 // possibly invalid from, to arguments. | |
| 1193 if (!foundToX && !foundFromX) | |
| 1194 fromX = toX = 0; | |
| 1195 | |
| 1196 if (fromX < toX) | |
| 1197 return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); | |
| 1198 return FloatRect(point.x() + toX, point.y(), fromX - toX, height); | |
| 1199 } | |
| 1200 | |
| 1201 } // namespace blink | 1178 } // namespace blink |
| OLD | NEW |