Changeset 69972 in webkit
- Timestamp:
- Oct 18, 2010 10:41:19 AM (14 years ago)
- Location:
- trunk/WebCore
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog
r69971 r69972 1 2010-10-18 Xiaomei Ji <xji@chromium.org> 2 3 Reviewed by David Levin. 4 5 Code cleanup: Move most function members in TextRunWorker from inlined. 6 https://bugs.webkit.org/show_bug.cgi?id=47732 7 8 No functionality change, so no tests added. 9 10 * platform/graphics/chromium/FontLinux.cpp: 11 (WebCore::TextRunWalker::setWordSpacingAdjustment): 12 (WebCore::TextRunWalker::setLetterSpacingAdjustment): 13 (WebCore::TextRunWalker::setXOffsetToZero): 14 (WebCore::TextRunWalker::rtl): 15 (WebCore::TextRunWalker::glyphs): 16 (WebCore::TextRunWalker::length): 17 (WebCore::TextRunWalker::xPositions): 18 (WebCore::TextRunWalker::advances): 19 (WebCore::TextRunWalker::width): 20 (WebCore::TextRunWalker::logClusters): 21 (WebCore::TextRunWalker::numCodePoints): 22 (WebCore::TextRunWalker::fontPlatformDataForScriptRun): 23 (WebCore::TextRunWalker::isCodepointSpace): 24 (WebCore::TextRunWalker::TextRunWalker): 25 (WebCore::TextRunWalker::~TextRunWalker): 26 (WebCore::TextRunWalker::isWordBreak): 27 (WebCore::TextRunWalker::setPadding): 28 (WebCore::TextRunWalker::reset): 29 (WebCore::TextRunWalker::setBackwardsIteration): 30 (WebCore::TextRunWalker::nextScriptRun): 31 (WebCore::TextRunWalker::widthOfFullRun): 32 (WebCore::TextRunWalker::getTextRun): 33 (WebCore::TextRunWalker::getNormalizedTextRun): 34 (WebCore::TextRunWalker::setupFontForScriptRun): 35 (WebCore::TextRunWalker::allocHarfbuzzFont): 36 (WebCore::TextRunWalker::deleteGlyphArrays): 37 (WebCore::TextRunWalker::createGlyphArrays): 38 (WebCore::TextRunWalker::resetGlyphArrays): 39 (WebCore::TextRunWalker::shapeGlyphs): 40 (WebCore::TextRunWalker::setGlyphXPositions): 41 (WebCore::TextRunWalker::mirrorCharacters): 42 1 43 2010-10-18 No'am Rosenthal <noam.rosenthal@nokia.com> 2 44 -
trunk/WebCore/platform/graphics/chromium/FontLinux.cpp
r68660 r69972 162 162 class TextRunWalker { 163 163 public: 164 TextRunWalker(const TextRun& run, unsigned startingX, const Font* font) 165 : m_font(font) 166 , m_startingX(startingX) 167 , m_offsetX(m_startingX) 168 , m_run(getTextRun(run)) 169 , m_iterateBackwards(m_run.rtl()) 170 , m_wordSpacingAdjustment(0) 171 , m_padding(0) 172 , m_padError(0) 173 { 174 // Do not use |run| inside this constructor. Use |m_run| instead. 175 176 memset(&m_item, 0, sizeof(m_item)); 177 // We cannot know, ahead of time, how many glyphs a given script run 178 // will produce. We take a guess that script runs will not produce more 179 // than twice as many glyphs as there are code points plus a bit of 180 // padding and fallback if we find that we are wrong. 181 createGlyphArrays((m_run.length() + 2) * 2); 182 183 m_item.log_clusters = new unsigned short[m_run.length()]; 184 185 m_item.face = 0; 186 m_item.font = allocHarfbuzzFont(); 187 188 m_item.item.bidiLevel = m_run.rtl(); 189 190 int length = m_run.length(); 191 m_item.stringLength = length; 192 193 if (!m_item.item.bidiLevel) 194 m_item.string = m_run.characters(); 195 else { 196 // Assume mirrored character is in the same Unicode multilingual plane as the original one. 197 UChar* string = new UChar[length]; 198 mirrorCharacters(string, m_run.characters(), length); 199 m_item.string = string; 200 } 201 202 reset(); 203 } 204 205 ~TextRunWalker() 206 { 207 fastFree(m_item.font); 208 deleteGlyphArrays(); 209 delete[] m_item.log_clusters; 210 if (m_item.item.bidiLevel) 211 delete[] m_item.string; 212 } 164 TextRunWalker(const TextRun&, unsigned, const Font*); 165 ~TextRunWalker(); 166 167 bool isWordBreak(unsigned, bool); 168 // setPadding sets a number of pixels to be distributed across the TextRun. 169 // WebKit uses this to justify text. 170 void setPadding(int); 171 void reset(); 172 void setBackwardsIteration(bool); 173 // Advance to the next script run, returning false when the end of the 174 // TextRun has been reached. 175 bool nextScriptRun(); 176 float widthOfFullRun(); 213 177 214 178 // setWordSpacingAdjustment sets a delta (in pixels) which is applied at 215 179 // each word break in the TextRun. 216 void setWordSpacingAdjustment(int wordSpacingAdjustment) 217 { 218 m_wordSpacingAdjustment = wordSpacingAdjustment; 219 } 180 void setWordSpacingAdjustment(int wordSpacingAdjustment) { m_wordSpacingAdjustment = wordSpacingAdjustment; } 220 181 221 182 // setLetterSpacingAdjustment sets an additional number of pixels that is … … 225 186 // (NOTE: currently does nothing because I don't know how to get the 226 187 // cluster information from Harfbuzz.) 227 void setLetterSpacingAdjustment(int letterSpacingAdjustment) 228 { 229 m_letterSpacing = letterSpacingAdjustment; 230 } 231 232 bool isWordBreak(unsigned i, bool isRTL) 233 { 234 if (!isRTL) 235 return i && isCodepointSpace(m_item.string[i]) && !isCodepointSpace(m_item.string[i - 1]); 236 return i != m_item.stringLength - 1 && isCodepointSpace(m_item.string[i]) && !isCodepointSpace(m_item.string[i + 1]); 237 } 238 239 // setPadding sets a number of pixels to be distributed across the TextRun. 240 // WebKit uses this to justify text. 241 void setPadding(int padding) 242 { 243 m_padding = padding; 244 if (!m_padding) 245 return; 246 247 // If we have padding to distribute, then we try to give an equal 248 // amount to each space. The last space gets the smaller amount, if 249 // any. 250 unsigned numWordBreaks = 0; 251 bool isRTL = m_iterateBackwards; 252 253 for (unsigned i = 0; i < m_item.stringLength; i++) { 254 if (isWordBreak(i, isRTL)) 255 numWordBreaks++; 256 } 257 258 if (numWordBreaks) 259 m_padPerWordBreak = m_padding / numWordBreaks; 260 else 261 m_padPerWordBreak = 0; 262 } 263 264 void reset() 265 { 266 if (m_iterateBackwards) 267 m_indexOfNextScriptRun = m_run.length() - 1; 268 else 269 m_indexOfNextScriptRun = 0; 270 m_offsetX = m_startingX; 271 } 188 void setLetterSpacingAdjustment(int letterSpacingAdjustment) { m_letterSpacing = letterSpacingAdjustment; } 272 189 273 190 // Set the x offset for the next script run. This affects the values in 274 191 // |xPositions| 275 void setXOffsetToZero() 276 { 277 m_offsetX = 0; 278 } 279 280 bool rtl() const 281 { 282 return m_run.rtl(); 283 } 284 285 void setBackwardsIteration(bool isBackwards) 286 { 287 m_iterateBackwards = isBackwards; 288 reset(); 289 } 290 291 // Advance to the next script run, returning false when the end of the 292 // TextRun has been reached. 293 bool nextScriptRun() 294 { 295 if (m_iterateBackwards) { 296 // In right-to-left mode we need to render the shaped glyph backwards and 297 // also render the script runs themselves backwards. So given a TextRun: 298 // AAAAAAACTTTTTTT (A = Arabic, C = Common, T = Thai) 299 // we render: 300 // TTTTTTCAAAAAAA 301 // (and the glyphs in each A, C and T section are backwards too) 302 if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) 303 return false; 304 } else { 305 if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) 306 return false; 307 308 // It is actually wrong to consider script runs at all in this code. 309 // Other WebKit code (e.g. Mac) segments complex text just by finding 310 // the longest span of text covered by a single font. 311 // But we currently need to call hb_utf16_script_run_next anyway to fill 312 // in the harfbuzz data structures to e.g. pick the correct script's shaper. 313 // So we allow that to run first, then do a second pass over the range it 314 // found and take the largest subregion that stays within a single font. 315 const FontData* glyphData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false, false).fontData; 316 unsigned endOfRun; 317 for (endOfRun = 1; endOfRun < m_item.item.length; ++endOfRun) { 318 const FontData* nextGlyphData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos + endOfRun], false, false).fontData; 319 if (nextGlyphData != glyphData) 320 break; 321 } 322 m_item.item.length = endOfRun; 323 m_indexOfNextScriptRun = m_item.item.pos + endOfRun; 324 } 325 326 setupFontForScriptRun(); 327 shapeGlyphs(); 328 setGlyphXPositions(rtl()); 329 330 return true; 331 } 332 333 const uint16_t* glyphs() const 334 { 335 return m_glyphs16; 336 } 192 void setXOffsetToZero() { m_offsetX = 0; } 193 bool rtl() const { return m_run.rtl(); } 194 const uint16_t* glyphs() const { return m_glyphs16; } 337 195 338 196 // Return the length of the array returned by |glyphs| 339 const unsigned length() const 340 { 341 return m_item.num_glyphs; 342 } 197 const unsigned length() const { return m_item.num_glyphs; } 343 198 344 199 // Return the x offset for each of the glyphs. Note that this is translated 345 200 // by the current x offset and that the x offset is updated for each script 346 201 // run. 347 const SkScalar* xPositions() const 348 { 349 return m_xPositions; 350 } 202 const SkScalar* xPositions() const { return m_xPositions; } 351 203 352 204 // Get the advances (widths) for each glyph. 353 const HB_Fixed* advances() const 354 { 355 return m_item.advances; 356 } 205 const HB_Fixed* advances() const { return m_item.advances; } 357 206 358 207 // Return the width (in px) of the current script run. 359 const unsigned width() const 360 { 361 return m_pixelWidth; 362 } 208 const unsigned width() const { return m_pixelWidth; } 363 209 364 210 // Return the cluster log for the current script run. For example: … … 367 213 // So, for each input code point, the log tells you which output glyph was 368 214 // generated for it. 369 const unsigned short* logClusters() const 370 { 371 return m_item.log_clusters; 372 } 215 const unsigned short* logClusters() const { return m_item.log_clusters; } 373 216 374 217 // return the number of code points in the current script run 375 const unsigned numCodePoints() const 376 { 377 return m_numCodePoints; 378 } 379 380 const FontPlatformData* fontPlatformDataForScriptRun() 381 { 382 return reinterpret_cast<FontPlatformData*>(m_item.font->userData); 383 } 384 385 float widthOfFullRun() 386 { 387 float widthSum = 0; 388 while (nextScriptRun()) 389 widthSum += width(); 390 391 return widthSum; 392 } 218 const unsigned numCodePoints() const { return m_numCodePoints; } 219 220 const FontPlatformData* fontPlatformDataForScriptRun() { return reinterpret_cast<FontPlatformData*>(m_item.font->userData); } 393 221 394 222 private: 395 const TextRun& getTextRun(const TextRun& originalRun) 396 { 397 // Normalize the text run in two ways: 398 // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks 399 // (U+0300..) are used in the run. This conversion is necessary since most OpenType 400 // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in 401 // their GSUB tables. 402 // 403 // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since 404 // the API returns FALSE (= not normalized) for complex runs that don't require NFC 405 // normalization (e.g., Arabic text). Unless the run contains the diacritical marks, 406 // Harfbuzz will do the same thing for us using the GSUB table. 407 // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs 408 // for characters like '\n' otherwise. 409 for (int i = 0; i < originalRun.length(); ++i) { 410 UChar ch = originalRun[i]; 411 UBlockCode block = ::ublock_getCode(ch); 412 if (block == UBLOCK_COMBINING_DIACRITICAL_MARKS || (Font::treatAsSpace(ch) && ch != ' ')) { 413 return getNormalizedTextRun(originalRun); 414 } 415 } 416 return originalRun; 417 } 418 419 const TextRun& getNormalizedTextRun(const TextRun& originalRun) 420 { 421 icu::UnicodeString normalizedString; 422 UErrorCode error = U_ZERO_ERROR; 423 icu::Normalizer::normalize(icu::UnicodeString(originalRun.characters(), originalRun.length()), UNORM_NFC, 0 /* no options */, normalizedString, error); 424 if (U_FAILURE(error)) 425 return originalRun; 426 427 m_normalizedBuffer.set(new UChar[normalizedString.length() + 1]); 428 normalizedString.extract(m_normalizedBuffer.get(), normalizedString.length() + 1, error); 429 ASSERT(U_SUCCESS(error)); 430 431 for (int i = 0; i < normalizedString.length(); ++i) { 432 if (Font::treatAsSpace(m_normalizedBuffer[i])) 433 m_normalizedBuffer[i] = ' '; 434 } 435 436 m_normalizedRun.set(new TextRun(originalRun)); 437 m_normalizedRun->setText(m_normalizedBuffer.get(), normalizedString.length()); 438 return *m_normalizedRun; 439 } 440 441 void setupFontForScriptRun() 442 { 443 const FontData* fontData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false, false).fontData; 444 const FontPlatformData& platformData = fontData->fontDataForCharacter(' ')->platformData(); 445 m_item.face = platformData.harfbuzzFace(); 446 void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData); 447 m_item.font->userData = opaquePlatformData; 448 } 449 450 HB_FontRec* allocHarfbuzzFont() 451 { 452 HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec))); 453 memset(font, 0, sizeof(HB_FontRec)); 454 font->klass = &harfbuzzSkiaClass; 455 font->userData = 0; 456 // The values which harfbuzzSkiaClass returns are already scaled to 457 // pixel units, so we just set all these to one to disable further 458 // scaling. 459 font->x_ppem = 1; 460 font->y_ppem = 1; 461 font->x_scale = 1; 462 font->y_scale = 1; 463 464 return font; 465 } 466 467 void deleteGlyphArrays() 468 { 469 delete[] m_item.glyphs; 470 delete[] m_item.attributes; 471 delete[] m_item.advances; 472 delete[] m_item.offsets; 473 delete[] m_glyphs16; 474 delete[] m_xPositions; 475 } 476 477 void createGlyphArrays(int size) 478 { 479 m_item.glyphs = new HB_Glyph[size]; 480 m_item.attributes = new HB_GlyphAttributes[size]; 481 m_item.advances = new HB_Fixed[size]; 482 m_item.offsets = new HB_FixedPoint[size]; 483 484 m_glyphs16 = new uint16_t[size]; 485 m_xPositions = new SkScalar[size]; 486 487 m_item.num_glyphs = size; 488 m_glyphsArrayCapacity = size; // Save the GlyphArrays size. 489 resetGlyphArrays(); 490 } 491 492 void resetGlyphArrays() 493 { 494 int size = m_item.num_glyphs; 495 // All the types here don't have pointers. It is safe to reset to 496 // zero unless Harfbuzz breaks the compatibility in the future. 497 memset(m_item.glyphs, 0, size * sizeof(HB_Glyph)); 498 memset(m_item.attributes, 0, size * sizeof(HB_GlyphAttributes)); 499 memset(m_item.advances, 0, size * sizeof(HB_Fixed)); 500 memset(m_item.offsets, 0, size * sizeof(HB_FixedPoint)); 501 memset(m_glyphs16, 0, size * sizeof(uint16_t)); 502 memset(m_xPositions, 0, size * sizeof(SkScalar)); 503 } 504 505 void shapeGlyphs() 506 { 507 // HB_ShapeItem() resets m_item.num_glyphs. If the previous call to 508 // HB_ShapeItem() used less space than was available, the capacity of 509 // the array may be larger than the current value of m_item.num_glyphs. 510 // So, we need to reset the num_glyphs to the capacity of the array. 511 m_item.num_glyphs = m_glyphsArrayCapacity; 512 resetGlyphArrays(); 513 while (!HB_ShapeItem(&m_item)) { 514 // We overflowed our arrays. Resize and retry. 515 // HB_ShapeItem fills in m_item.num_glyphs with the needed size. 516 deleteGlyphArrays(); 517 // The |+ 1| here is a workaround for a bug in Harfbuzz: the Khmer 518 // shaper (at least) can fail because of insufficient glyph buffers 519 // and request 0 additional glyphs: throwing us into an infinite 520 // loop. 521 createGlyphArrays(m_item.num_glyphs + 1); 522 } 523 } 524 525 void setGlyphXPositions(bool isRTL) 526 { 527 double position = 0; 528 // logClustersIndex indexes logClusters for the first (or last when 529 // RTL) codepoint of the current glyph. Each time we advance a glyph, 530 // we skip over all the codepoints that contributed to the current 531 // glyph. 532 unsigned logClustersIndex = isRTL ? m_item.num_glyphs - 1 : 0; 533 534 for (unsigned iter = 0; iter < m_item.num_glyphs; ++iter) { 535 // Glyphs are stored in logical order, but for layout purposes we 536 // always go left to right. 537 int i = isRTL ? m_item.num_glyphs - iter - 1 : iter; 538 539 m_glyphs16[i] = m_item.glyphs[i]; 540 double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x); 541 m_xPositions[i] = m_offsetX + position + offsetX; 542 543 double advance = truncateFixedPointToInteger(m_item.advances[i]); 544 // The first half of the conjuction works around the case where 545 // output glyphs aren't associated with any codepoints by the 546 // clusters log. 547 if (logClustersIndex < m_item.item.length 548 && isWordBreak(m_item.item.pos + logClustersIndex, isRTL)) { 549 advance += m_wordSpacingAdjustment; 550 551 if (m_padding > 0) { 552 unsigned toPad = roundf(m_padPerWordBreak + m_padError); 553 m_padError += m_padPerWordBreak - toPad; 554 555 if (m_padding < toPad) 556 toPad = m_padding; 557 m_padding -= toPad; 558 advance += toPad; 559 } 560 } 561 562 // We would like to add m_letterSpacing after each cluster, but I 563 // don't know where the cluster information is. This is typically 564 // fine for Roman languages, but breaks more complex languages 565 // terribly. 566 // advance += m_letterSpacing; 567 568 if (isRTL) { 569 while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i) 570 logClustersIndex--; 571 } else { 572 while (logClustersIndex < m_item.item.length && logClusters()[logClustersIndex] == i) 573 logClustersIndex++; 574 } 575 576 position += advance; 577 } 578 579 m_pixelWidth = position; 580 m_offsetX += m_pixelWidth; 581 } 582 583 static bool isCodepointSpace(HB_UChar16 c) 584 { 585 // This matches the logic in RenderBlock::findNextLineBreak 586 return c == ' ' || c == '\t'; 587 } 588 589 void mirrorCharacters(UChar* destination, const UChar* source, int length) const 590 { 591 int position = 0; 592 bool error = false; 593 // Iterate characters in source and mirror character if needed. 594 while (position < length) { 595 UChar32 character; 596 int nextPosition = position; 597 U16_NEXT(source, nextPosition, length, character); 598 character = u_charMirror(character); 599 U16_APPEND(destination, position, length, character, error); 600 ASSERT(!error); 601 position = nextPosition; 602 } 603 } 223 const TextRun& getTextRun(const TextRun&); 224 const TextRun& getNormalizedTextRun(const TextRun&); 225 void setupFontForScriptRun(); 226 HB_FontRec* allocHarfbuzzFont(); 227 void deleteGlyphArrays(); 228 void createGlyphArrays(int); 229 void resetGlyphArrays(); 230 void shapeGlyphs(); 231 void setGlyphXPositions(bool); 232 void mirrorCharacters(UChar*, const UChar*, int) const; 233 234 // This matches the logic in RenderBlock::findNextLineBreak 235 static bool isCodepointSpace(HB_UChar16 c) { return c == ' ' || c == '\t'; } 604 236 605 237 const Font* const m_font; … … 627 259 unsigned m_letterSpacing; // pixels to be added after each glyph. 628 260 }; 261 262 263 TextRunWalker::TextRunWalker(const TextRun& run, unsigned startingX, const Font* font) 264 : m_font(font) 265 , m_startingX(startingX) 266 , m_offsetX(m_startingX) 267 , m_run(getTextRun(run)) 268 , m_iterateBackwards(m_run.rtl()) 269 , m_wordSpacingAdjustment(0) 270 , m_padding(0) 271 , m_padError(0) 272 { 273 // Do not use |run| inside this constructor. Use |m_run| instead. 274 275 memset(&m_item, 0, sizeof(m_item)); 276 // We cannot know, ahead of time, how many glyphs a given script run 277 // will produce. We take a guess that script runs will not produce more 278 // than twice as many glyphs as there are code points plus a bit of 279 // padding and fallback if we find that we are wrong. 280 createGlyphArrays((m_run.length() + 2) * 2); 281 282 m_item.log_clusters = new unsigned short[m_run.length()]; 283 284 m_item.face = 0; 285 m_item.font = allocHarfbuzzFont(); 286 287 m_item.item.bidiLevel = m_run.rtl(); 288 289 int length = m_run.length(); 290 m_item.stringLength = length; 291 292 if (!m_item.item.bidiLevel) 293 m_item.string = m_run.characters(); 294 else { 295 // Assume mirrored character is in the same Unicode multilingual plane as the original one. 296 UChar* string = new UChar[length]; 297 mirrorCharacters(string, m_run.characters(), length); 298 m_item.string = string; 299 } 300 301 reset(); 302 } 303 304 TextRunWalker::~TextRunWalker() 305 { 306 fastFree(m_item.font); 307 deleteGlyphArrays(); 308 delete[] m_item.log_clusters; 309 if (m_item.item.bidiLevel) 310 delete[] m_item.string; 311 } 312 313 bool TextRunWalker::isWordBreak(unsigned index, bool isRTL) 314 { 315 if (!isRTL) 316 return index && isCodepointSpace(m_item.string[index]) && !isCodepointSpace(m_item.string[index - 1]); 317 return index != m_item.stringLength - 1 && isCodepointSpace(m_item.string[index]) && !isCodepointSpace(m_item.string[index + 1]); 318 } 319 320 // setPadding sets a number of pixels to be distributed across the TextRun. 321 // WebKit uses this to justify text. 322 void TextRunWalker::setPadding(int padding) 323 { 324 m_padding = padding; 325 if (!m_padding) 326 return; 327 328 // If we have padding to distribute, then we try to give an equal 329 // amount to each space. The last space gets the smaller amount, if 330 // any. 331 unsigned numWordBreaks = 0; 332 bool isRTL = m_iterateBackwards; 333 334 for (unsigned i = 0; i < m_item.stringLength; i++) { 335 if (isWordBreak(i, isRTL)) 336 numWordBreaks++; 337 } 338 339 if (numWordBreaks) 340 m_padPerWordBreak = m_padding / numWordBreaks; 341 else 342 m_padPerWordBreak = 0; 343 } 344 345 void TextRunWalker::reset() 346 { 347 if (m_iterateBackwards) 348 m_indexOfNextScriptRun = m_run.length() - 1; 349 else 350 m_indexOfNextScriptRun = 0; 351 m_offsetX = m_startingX; 352 } 353 354 void TextRunWalker::setBackwardsIteration(bool isBackwards) 355 { 356 m_iterateBackwards = isBackwards; 357 reset(); 358 } 359 360 // Advance to the next script run, returning false when the end of the 361 // TextRun has been reached. 362 bool TextRunWalker::nextScriptRun() 363 { 364 if (m_iterateBackwards) { 365 // In right-to-left mode we need to render the shaped glyph backwards and 366 // also render the script runs themselves backwards. So given a TextRun: 367 // AAAAAAACTTTTTTT (A = Arabic, C = Common, T = Thai) 368 // we render: 369 // TTTTTTCAAAAAAA 370 // (and the glyphs in each A, C and T section are backwards too) 371 if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) 372 return false; 373 } else { 374 if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) 375 return false; 376 377 // It is actually wrong to consider script runs at all in this code. 378 // Other WebKit code (e.g. Mac) segments complex text just by finding 379 // the longest span of text covered by a single font. 380 // But we currently need to call hb_utf16_script_run_next anyway to fill 381 // in the harfbuzz data structures to e.g. pick the correct script's shaper. 382 // So we allow that to run first, then do a second pass over the range it 383 // found and take the largest subregion that stays within a single font. 384 const FontData* glyphData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false, false).fontData; 385 unsigned endOfRun; 386 for (endOfRun = 1; endOfRun < m_item.item.length; ++endOfRun) { 387 const FontData* nextGlyphData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos + endOfRun], false, false).fontData; 388 if (nextGlyphData != glyphData) 389 break; 390 } 391 m_item.item.length = endOfRun; 392 m_indexOfNextScriptRun = m_item.item.pos + endOfRun; 393 } 394 395 setupFontForScriptRun(); 396 shapeGlyphs(); 397 setGlyphXPositions(rtl()); 398 399 return true; 400 } 401 402 float TextRunWalker::widthOfFullRun() 403 { 404 float widthSum = 0; 405 while (nextScriptRun()) 406 widthSum += width(); 407 408 return widthSum; 409 } 410 411 const TextRun& TextRunWalker::getTextRun(const TextRun& originalRun) 412 { 413 // Normalize the text run in two ways: 414 // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks 415 // (U+0300..) are used in the run. This conversion is necessary since most OpenType 416 // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in 417 // their GSUB tables. 418 // 419 // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since 420 // the API returns FALSE (= not normalized) for complex runs that don't require NFC 421 // normalization (e.g., Arabic text). Unless the run contains the diacritical marks, 422 // Harfbuzz will do the same thing for us using the GSUB table. 423 // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs 424 // for characters like '\n' otherwise. 425 for (int i = 0; i < originalRun.length(); ++i) { 426 UChar ch = originalRun[i]; 427 UBlockCode block = ::ublock_getCode(ch); 428 if (block == UBLOCK_COMBINING_DIACRITICAL_MARKS || (Font::treatAsSpace(ch) && ch != ' ')) 429 return getNormalizedTextRun(originalRun); 430 } 431 return originalRun; 432 } 433 434 const TextRun& TextRunWalker::getNormalizedTextRun(const TextRun& originalRun) 435 { 436 icu::UnicodeString normalizedString; 437 UErrorCode error = U_ZERO_ERROR; 438 icu::Normalizer::normalize(icu::UnicodeString(originalRun.characters(), originalRun.length()), UNORM_NFC, 0 /* no options */, normalizedString, error); 439 if (U_FAILURE(error)) 440 return originalRun; 441 442 m_normalizedBuffer.set(new UChar[normalizedString.length() + 1]); 443 normalizedString.extract(m_normalizedBuffer.get(), normalizedString.length() + 1, error); 444 ASSERT(U_SUCCESS(error)); 445 446 for (int i = 0; i < normalizedString.length(); ++i) { 447 if (Font::treatAsSpace(m_normalizedBuffer[i])) 448 m_normalizedBuffer[i] = ' '; 449 } 450 451 m_normalizedRun.set(new TextRun(originalRun)); 452 m_normalizedRun->setText(m_normalizedBuffer.get(), normalizedString.length()); 453 return *m_normalizedRun; 454 } 455 456 void TextRunWalker::setupFontForScriptRun() 457 { 458 const FontData* fontData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false, false).fontData; 459 const FontPlatformData& platformData = fontData->fontDataForCharacter(' ')->platformData(); 460 m_item.face = platformData.harfbuzzFace(); 461 void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData); 462 m_item.font->userData = opaquePlatformData; 463 } 464 465 HB_FontRec* TextRunWalker::allocHarfbuzzFont() 466 { 467 HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec))); 468 memset(font, 0, sizeof(HB_FontRec)); 469 font->klass = &harfbuzzSkiaClass; 470 font->userData = 0; 471 // The values which harfbuzzSkiaClass returns are already scaled to 472 // pixel units, so we just set all these to one to disable further 473 // scaling. 474 font->x_ppem = 1; 475 font->y_ppem = 1; 476 font->x_scale = 1; 477 font->y_scale = 1; 478 479 return font; 480 } 481 482 void TextRunWalker::deleteGlyphArrays() 483 { 484 delete[] m_item.glyphs; 485 delete[] m_item.attributes; 486 delete[] m_item.advances; 487 delete[] m_item.offsets; 488 delete[] m_glyphs16; 489 delete[] m_xPositions; 490 } 491 492 void TextRunWalker::createGlyphArrays(int size) 493 { 494 m_item.glyphs = new HB_Glyph[size]; 495 m_item.attributes = new HB_GlyphAttributes[size]; 496 m_item.advances = new HB_Fixed[size]; 497 m_item.offsets = new HB_FixedPoint[size]; 498 499 m_glyphs16 = new uint16_t[size]; 500 m_xPositions = new SkScalar[size]; 501 502 m_item.num_glyphs = size; 503 m_glyphsArrayCapacity = size; // Save the GlyphArrays size. 504 resetGlyphArrays(); 505 } 506 507 void TextRunWalker::resetGlyphArrays() 508 { 509 int size = m_item.num_glyphs; 510 // All the types here don't have pointers. It is safe to reset to 511 // zero unless Harfbuzz breaks the compatibility in the future. 512 memset(m_item.glyphs, 0, size * sizeof(HB_Glyph)); 513 memset(m_item.attributes, 0, size * sizeof(HB_GlyphAttributes)); 514 memset(m_item.advances, 0, size * sizeof(HB_Fixed)); 515 memset(m_item.offsets, 0, size * sizeof(HB_FixedPoint)); 516 memset(m_glyphs16, 0, size * sizeof(uint16_t)); 517 memset(m_xPositions, 0, size * sizeof(SkScalar)); 518 } 519 520 void TextRunWalker::shapeGlyphs() 521 { 522 // HB_ShapeItem() resets m_item.num_glyphs. If the previous call to 523 // HB_ShapeItem() used less space than was available, the capacity of 524 // the array may be larger than the current value of m_item.num_glyphs. 525 // So, we need to reset the num_glyphs to the capacity of the array. 526 m_item.num_glyphs = m_glyphsArrayCapacity; 527 resetGlyphArrays(); 528 while (!HB_ShapeItem(&m_item)) { 529 // We overflowed our arrays. Resize and retry. 530 // HB_ShapeItem fills in m_item.num_glyphs with the needed size. 531 deleteGlyphArrays(); 532 // The |+ 1| here is a workaround for a bug in Harfbuzz: the Khmer 533 // shaper (at least) can fail because of insufficient glyph buffers 534 // and request 0 additional glyphs: throwing us into an infinite 535 // loop. 536 createGlyphArrays(m_item.num_glyphs + 1); 537 } 538 } 539 540 void TextRunWalker::setGlyphXPositions(bool isRTL) 541 { 542 double position = 0; 543 // logClustersIndex indexes logClusters for the first (or last when 544 // RTL) codepoint of the current glyph. Each time we advance a glyph, 545 // we skip over all the codepoints that contributed to the current 546 // glyph. 547 unsigned logClustersIndex = isRTL ? m_item.num_glyphs - 1 : 0; 548 549 for (unsigned iter = 0; iter < m_item.num_glyphs; ++iter) { 550 // Glyphs are stored in logical order, but for layout purposes we 551 // always go left to right. 552 int i = isRTL ? m_item.num_glyphs - iter - 1 : iter; 553 554 m_glyphs16[i] = m_item.glyphs[i]; 555 double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x); 556 m_xPositions[i] = m_offsetX + position + offsetX; 557 558 double advance = truncateFixedPointToInteger(m_item.advances[i]); 559 // The first half of the conjuction works around the case where 560 // output glyphs aren't associated with any codepoints by the 561 // clusters log. 562 if (logClustersIndex < m_item.item.length 563 && isWordBreak(m_item.item.pos + logClustersIndex, isRTL)) { 564 advance += m_wordSpacingAdjustment; 565 566 if (m_padding > 0) { 567 unsigned toPad = roundf(m_padPerWordBreak + m_padError); 568 m_padError += m_padPerWordBreak - toPad; 569 570 if (m_padding < toPad) 571 toPad = m_padding; 572 m_padding -= toPad; 573 advance += toPad; 574 } 575 } 576 577 // We would like to add m_letterSpacing after each cluster, but I 578 // don't know where the cluster information is. This is typically 579 // fine for Roman languages, but breaks more complex languages 580 // terribly. 581 // advance += m_letterSpacing; 582 583 if (isRTL) { 584 while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i) 585 logClustersIndex--; 586 } else { 587 while (logClustersIndex < m_item.item.length && logClusters()[logClustersIndex] == i) 588 logClustersIndex++; 589 } 590 591 position += advance; 592 } 593 594 m_pixelWidth = position; 595 m_offsetX += m_pixelWidth; 596 } 597 598 void TextRunWalker::mirrorCharacters(UChar* destination, const UChar* source, int length) const 599 { 600 int position = 0; 601 bool error = false; 602 // Iterate characters in source and mirror character if needed. 603 while (position < length) { 604 UChar32 character; 605 int nextPosition = position; 606 U16_NEXT(source, nextPosition, length, character); 607 character = u_charMirror(character); 608 U16_APPEND(destination, position, length, character, error); 609 ASSERT(!error); 610 position = nextPosition; 611 } 612 } 629 613 630 614 static void setupForTextPainting(SkPaint* paint, SkColor color)
Note: See TracChangeset
for help on using the changeset viewer.