| 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 20 matching lines...) Expand all Loading... |
| 31 | 31 |
| 32 #include "config.h" | 32 #include "config.h" |
| 33 #include "platform/fonts/harfbuzz/HarfBuzzShaper.h" | 33 #include "platform/fonts/harfbuzz/HarfBuzzShaper.h" |
| 34 | 34 |
| 35 #include "RuntimeEnabledFeatures.h" | 35 #include "RuntimeEnabledFeatures.h" |
| 36 #include "hb-icu.h" | 36 #include "hb-icu.h" |
| 37 #include "platform/fonts/Character.h" | 37 #include "platform/fonts/Character.h" |
| 38 #include "platform/fonts/Font.h" | 38 #include "platform/fonts/Font.h" |
| 39 #include "platform/fonts/harfbuzz/HarfBuzzFace.h" | 39 #include "platform/fonts/harfbuzz/HarfBuzzFace.h" |
| 40 #include "platform/text/SurrogatePairAwareTextIterator.h" | 40 #include "platform/text/SurrogatePairAwareTextIterator.h" |
| 41 #include "platform/text/TextBreakIterator.h" |
| 41 #include "wtf/MathExtras.h" | 42 #include "wtf/MathExtras.h" |
| 42 #include "wtf/unicode/Unicode.h" | 43 #include "wtf/unicode/Unicode.h" |
| 43 #include <unicode/normlzr.h> | 44 #include <unicode/normlzr.h> |
| 44 #include <unicode/uchar.h> | 45 #include <unicode/uchar.h> |
| 45 | 46 |
| 46 #include <list> | 47 #include <list> |
| 47 #include <map> | 48 #include <map> |
| 48 #include <string> | 49 #include <string> |
| 49 | 50 |
| 50 namespace WebCore { | 51 namespace WebCore { |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 201 { | 202 { |
| 202 DEFINE_STATIC_LOCAL(HarfBuzzRunCache, globalHarfBuzzRunCache, ()); | 203 DEFINE_STATIC_LOCAL(HarfBuzzRunCache, globalHarfBuzzRunCache, ()); |
| 203 return globalHarfBuzzRunCache; | 204 return globalHarfBuzzRunCache; |
| 204 } | 205 } |
| 205 | 206 |
| 206 static inline float harfBuzzPositionToFloat(hb_position_t value) | 207 static inline float harfBuzzPositionToFloat(hb_position_t value) |
| 207 { | 208 { |
| 208 return static_cast<float>(value) / (1 << 16); | 209 return static_cast<float>(value) / (1 << 16); |
| 209 } | 210 } |
| 210 | 211 |
| 212 static inline unsigned countGraphemesInCluster(const TextRun& run, uint16_t star
tIndex, uint16_t endIndex) |
| 213 { |
| 214 ASSERT(!run.is8Bit()); // Only accepting normalized runs. |
| 215 if (startIndex > endIndex) { |
| 216 uint16_t tempIndex = startIndex; |
| 217 startIndex = endIndex; |
| 218 endIndex = tempIndex; |
| 219 } |
| 220 uint16_t length = endIndex - startIndex; |
| 221 TextRun subRun = run.subRun(startIndex, length); |
| 222 |
| 223 TextBreakIterator* cursorPosIterator = cursorMovementIterator(subRun.charact
ers16(), length); |
| 224 |
| 225 int cursorPos = cursorPosIterator->current(); |
| 226 int numGraphemes = -1; |
| 227 while (0 <= cursorPos) { |
| 228 cursorPos = cursorPosIterator->next(); |
| 229 numGraphemes++; |
| 230 } |
| 231 return numGraphemes < 0 ? 0 : numGraphemes; |
| 232 } |
| 233 |
| 211 inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData,
unsigned startIndex, unsigned numCharacters, TextDirection direction, hb_script_
t script) | 234 inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData,
unsigned startIndex, unsigned numCharacters, TextDirection direction, hb_script_
t script) |
| 212 : m_fontData(fontData) | 235 : m_fontData(fontData) |
| 213 , m_startIndex(startIndex) | 236 , m_startIndex(startIndex) |
| 214 , m_numCharacters(numCharacters) | 237 , m_numCharacters(numCharacters) |
| 215 , m_numGlyphs(0) | 238 , m_numGlyphs(0) |
| 216 , m_direction(direction) | 239 , m_direction(direction) |
| 217 , m_script(script) | 240 , m_script(script) |
| 218 , m_width(0) | 241 , m_width(0) |
| 219 { | 242 { |
| 220 } | 243 } |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 342 // Don't normalize tabs as they are not treated as spaces for word-end. | 365 // Don't normalize tabs as they are not treated as spaces for word-end. |
| 343 if (Character::treatAsSpace(character) && character != '\t') | 366 if (Character::treatAsSpace(character) && character != '\t') |
| 344 character = ' '; | 367 character = ' '; |
| 345 else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) | 368 else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) |
| 346 character = zeroWidthSpace; | 369 character = zeroWidthSpace; |
| 347 U16_APPEND(destination, *destinationLength, length, character, error); | 370 U16_APPEND(destination, *destinationLength, length, character, error); |
| 348 ASSERT_UNUSED(error, !error); | 371 ASSERT_UNUSED(error, !error); |
| 349 } | 372 } |
| 350 } | 373 } |
| 351 | 374 |
| 352 HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run) | 375 HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, bool forTex
tEmphasis) |
| 353 : m_font(font) | 376 : m_font(font) |
| 354 , m_normalizedBufferLength(0) | 377 , m_normalizedBufferLength(0) |
| 355 , m_run(run) | 378 , m_run(run) |
| 356 , m_wordSpacingAdjustment(font->fontDescription().wordSpacing()) | 379 , m_wordSpacingAdjustment(font->fontDescription().wordSpacing()) |
| 357 , m_padding(0) | 380 , m_padding(0) |
| 358 , m_padPerWordBreak(0) | 381 , m_padPerWordBreak(0) |
| 359 , m_padError(0) | 382 , m_padError(0) |
| 360 , m_letterSpacing(font->fontDescription().letterSpacing()) | 383 , m_letterSpacing(font->fontDescription().letterSpacing()) |
| 361 , m_fromIndex(0) | 384 , m_fromIndex(0) |
| 362 , m_toIndex(m_run.length()) | 385 , m_toIndex(m_run.length()) |
| 386 , m_forTextEmphasis(forTextEmphasis) |
| 363 { | 387 { |
| 364 m_normalizedBuffer = adoptArrayPtr(new UChar[m_run.length() + 1]); | 388 m_normalizedBuffer = adoptArrayPtr(new UChar[m_run.length() + 1]); |
| 365 normalizeCharacters(m_run, m_run.length(), m_normalizedBuffer.get(), &m_norm
alizedBufferLength); | 389 normalizeCharacters(m_run, m_run.length(), m_normalizedBuffer.get(), &m_norm
alizedBufferLength); |
| 366 setPadding(m_run.expansion()); | 390 setPadding(m_run.expansion()); |
| 367 setFontFeatures(); | 391 setFontFeatures(); |
| 368 } | 392 } |
| 369 | 393 |
| 370 static void normalizeSpacesAndMirrorChars(const UChar* source, unsigned length,
UChar* destination, unsigned* destinationLength, HarfBuzzShaper::NormalizeMode n
ormalizeMode) | 394 static void normalizeSpacesAndMirrorChars(const UChar* source, unsigned length,
UChar* destination, unsigned* destinationLength, HarfBuzzShaper::NormalizeMode n
ormalizeMode) |
| 371 { | 395 { |
| 372 unsigned position = 0; | 396 unsigned position = 0; |
| (...skipping 575 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 948 m_totalWidth += currentRun->width(); | 972 m_totalWidth += currentRun->width(); |
| 949 } | 973 } |
| 950 | 974 |
| 951 void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, Ha
rfBuzzRun* currentRun, FloatPoint& firstOffsetOfNextRun) | 975 void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, Ha
rfBuzzRun* currentRun, FloatPoint& firstOffsetOfNextRun) |
| 952 { | 976 { |
| 953 FloatPoint* offsets = currentRun->offsets(); | 977 FloatPoint* offsets = currentRun->offsets(); |
| 954 uint16_t* glyphs = currentRun->glyphs(); | 978 uint16_t* glyphs = currentRun->glyphs(); |
| 955 float* advances = currentRun->advances(); | 979 float* advances = currentRun->advances(); |
| 956 unsigned numGlyphs = currentRun->numGlyphs(); | 980 unsigned numGlyphs = currentRun->numGlyphs(); |
| 957 uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); | 981 uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); |
| 958 | |
| 959 for (unsigned i = 0; i < numGlyphs; ++i) { | 982 for (unsigned i = 0; i < numGlyphs; ++i) { |
| 960 uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToChara
cterIndexes[i]; | 983 uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToChara
cterIndexes[i]; |
| 961 FloatPoint& currentOffset = offsets[i]; | 984 FloatPoint& currentOffset = offsets[i]; |
| 962 FloatPoint& nextOffset = (i == numGlyphs - 1) ? firstOffsetOfNextRun : o
ffsets[i + 1]; | 985 FloatPoint& nextOffset = (i == numGlyphs - 1) ? firstOffsetOfNextRun : o
ffsets[i + 1]; |
| 963 float glyphAdvanceX = advances[i] + nextOffset.x() - currentOffset.x(); | 986 float glyphAdvanceX = advances[i] + nextOffset.x() - currentOffset.x(); |
| 964 float glyphAdvanceY = nextOffset.y() - currentOffset.y(); | 987 float glyphAdvanceY = nextOffset.y() - currentOffset.y(); |
| 988 |
| 965 if (m_run.rtl()) { | 989 if (m_run.rtl()) { |
| 966 if (currentCharacterIndex >= m_toIndex) | 990 if (currentCharacterIndex >= m_toIndex) |
| 967 m_startOffset.move(glyphAdvanceX, glyphAdvanceY); | 991 m_startOffset.move(glyphAdvanceX, glyphAdvanceY); |
| 968 else if (currentCharacterIndex >= m_fromIndex) | 992 else if (currentCharacterIndex >= m_fromIndex) |
| 969 glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphB
ufferAdvance(glyphAdvanceX, glyphAdvanceY)); | 993 glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphB
ufferAdvance(glyphAdvanceX, glyphAdvanceY)); |
| 970 } else { | 994 } else { |
| 971 if (currentCharacterIndex < m_fromIndex) | 995 if (currentCharacterIndex < m_fromIndex) |
| 972 m_startOffset.move(glyphAdvanceX, glyphAdvanceY); | 996 m_startOffset.move(glyphAdvanceX, glyphAdvanceY); |
| 973 else if (currentCharacterIndex < m_toIndex) | 997 else if (currentCharacterIndex < m_toIndex) |
| 974 glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphB
ufferAdvance(glyphAdvanceX, glyphAdvanceY)); | 998 glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphB
ufferAdvance(glyphAdvanceX, glyphAdvanceY)); |
| 975 } | 999 } |
| 976 } | 1000 } |
| 977 } | 1001 } |
| 978 | 1002 |
| 1003 void HarfBuzzShaper::fillGlyphBufferForTextEmphasis(GlyphBuffer* glyphBuffer, Ha
rfBuzzRun* currentRun) |
| 1004 { |
| 1005 // FIXME: Instead of generating a synthetic GlyphBuffer here which is then u
sed by the |
| 1006 // drawEmphasisMarks method of FontFastPath, we should roll our own emphasis
mark drawing function. |
| 1007 |
| 1008 float* advances = currentRun->advances(); |
| 1009 unsigned numGlyphs = currentRun->numGlyphs(); |
| 1010 uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); |
| 1011 unsigned graphemesInCluster = 1; |
| 1012 float clusterAdvance = 0; |
| 1013 uint16_t clusterStart; |
| 1014 |
| 1015 // A "cluster" in this context means a cluster as it is used by HarfBuzz: |
| 1016 // The minimal group of characters and corresponding glyphs, that cannot be
broken |
| 1017 // down further from a text shaping point of view. |
| 1018 // A cluster can contain multiple glyphs and grapheme clusters, with mutuall
y |
| 1019 // overlapping boundaries. Below we count grapheme clusters per HarfBuzz clu
sters, |
| 1020 // then linearly split the sum of corresponding glyph advances by the number
of |
| 1021 // grapheme clusters in order to find positions for emphasis mark drawing. |
| 1022 |
| 1023 if (m_run.rtl()) |
| 1024 clusterStart = currentRun->startIndex() + currentRun->numCharacters(); |
| 1025 else |
| 1026 clusterStart = currentRun->startIndex() + glyphToCharacterIndexes[0]; |
| 1027 |
| 1028 for (unsigned i = 0; i < numGlyphs; ++i) { |
| 1029 uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToChara
cterIndexes[i]; |
| 1030 bool isRunEnd = (i + 1 == numGlyphs); |
| 1031 bool isClusterEnd = isRunEnd || (currentRun->startIndex() + glyphToChar
acterIndexes[i + 1] != currentCharacterIndex); |
| 1032 clusterAdvance += advances[i]; |
| 1033 |
| 1034 if (isClusterEnd) { |
| 1035 uint16_t clusterEnd; |
| 1036 if (m_run.rtl()) |
| 1037 clusterEnd = currentCharacterIndex; |
| 1038 else |
| 1039 clusterEnd = isRunEnd ? currentRun->startIndex() + currentRun->n
umCharacters() : currentRun->startIndex() + glyphToCharacterIndexes[i + 1]; |
| 1040 |
| 1041 graphemesInCluster = countGraphemesInCluster(m_run, clusterStart, cl
usterEnd); |
| 1042 if (!graphemesInCluster || !clusterAdvance) |
| 1043 continue; |
| 1044 |
| 1045 float glyphAdvanceX = clusterAdvance / graphemesInCluster; |
| 1046 for (unsigned j = 0; j < graphemesInCluster; ++j) { |
| 1047 // Do not put emphasis marks on space, separator, and control ch
aracters. |
| 1048 GlyphBufferGlyph glyphToAdd = Character::canReceiveTextEmphasis(
m_run[currentCharacterIndex]) ? 1 : 0; |
| 1049 glyphBuffer->add(glyphToAdd, currentRun->fontData(), createGlyph
BufferAdvance(glyphAdvanceX, 0)); |
| 1050 } |
| 1051 clusterStart = clusterEnd; |
| 1052 clusterAdvance = 0; |
| 1053 } |
| 1054 } |
| 1055 } |
| 1056 |
| 979 bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer) | 1057 bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer) |
| 980 { | 1058 { |
| 981 unsigned numRuns = m_harfBuzzRuns.size(); | 1059 unsigned numRuns = m_harfBuzzRuns.size(); |
| 982 if (m_run.rtl()) { | 1060 if (m_run.rtl()) { |
| 983 m_startOffset = m_harfBuzzRuns.last()->offsets()[0]; | 1061 m_startOffset = m_harfBuzzRuns.last()->offsets()[0]; |
| 984 for (int runIndex = numRuns - 1; runIndex >= 0; --runIndex) { | 1062 for (int runIndex = numRuns - 1; runIndex >= 0; --runIndex) { |
| 985 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); | 1063 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); |
| 986 FloatPoint firstOffsetOfNextRun = !runIndex ? FloatPoint() : m_harfB
uzzRuns[runIndex - 1]->offsets()[0]; | 1064 FloatPoint firstOffsetOfNextRun = !runIndex ? FloatPoint() : m_harfB
uzzRuns[runIndex - 1]->offsets()[0]; |
| 987 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetO
fNextRun); | 1065 if (m_forTextEmphasis) |
| 1066 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun); |
| 1067 else |
| 1068 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOff
setOfNextRun); |
| 988 } | 1069 } |
| 989 } else { | 1070 } else { |
| 990 m_startOffset = m_harfBuzzRuns.first()->offsets()[0]; | 1071 m_startOffset = m_harfBuzzRuns.first()->offsets()[0]; |
| 991 for (unsigned runIndex = 0; runIndex < numRuns; ++runIndex) { | 1072 for (unsigned runIndex = 0; runIndex < numRuns; ++runIndex) { |
| 992 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); | 1073 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); |
| 993 FloatPoint firstOffsetOfNextRun = runIndex == numRuns - 1 ? FloatPoi
nt() : m_harfBuzzRuns[runIndex + 1]->offsets()[0]; | 1074 FloatPoint firstOffsetOfNextRun = runIndex == numRuns - 1 ? FloatPoi
nt() : m_harfBuzzRuns[runIndex + 1]->offsets()[0]; |
| 994 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetO
fNextRun); | 1075 if (m_forTextEmphasis) |
| 1076 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun); |
| 1077 else |
| 1078 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOff
setOfNextRun); |
| 995 } | 1079 } |
| 996 } | 1080 } |
| 997 return glyphBuffer->size(); | 1081 return glyphBuffer->size(); |
| 998 } | 1082 } |
| 999 | 1083 |
| 1000 int HarfBuzzShaper::offsetForPosition(float targetX) | 1084 int HarfBuzzShaper::offsetForPosition(float targetX) |
| 1001 { | 1085 { |
| 1002 int charactersSoFar = 0; | 1086 int charactersSoFar = 0; |
| 1003 float currentX = 0; | 1087 float currentX = 0; |
| 1004 | 1088 |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1069 if (!foundToX) | 1153 if (!foundToX) |
| 1070 toX = m_run.rtl() ? 0 : m_totalWidth; | 1154 toX = m_run.rtl() ? 0 : m_totalWidth; |
| 1071 | 1155 |
| 1072 // Using floorf() and roundf() as the same as mac port. | 1156 // Using floorf() and roundf() as the same as mac port. |
| 1073 if (fromX < toX) | 1157 if (fromX < toX) |
| 1074 return FloatRect(floorf(point.x() + fromX), point.y(), roundf(toX - from
X), height); | 1158 return FloatRect(floorf(point.x() + fromX), point.y(), roundf(toX - from
X), height); |
| 1075 return FloatRect(floorf(point.x() + toX), point.y(), roundf(fromX - toX), he
ight); | 1159 return FloatRect(floorf(point.x() + toX), point.y(), roundf(fromX - toX), he
ight); |
| 1076 } | 1160 } |
| 1077 | 1161 |
| 1078 } // namespace WebCore | 1162 } // namespace WebCore |
| OLD | NEW |