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

Unified Diff: Source/platform/fonts/shaping/HarfBuzzShaper.cpp

Issue 957473003: Optimize complex text shaping (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/platform/fonts/shaping/HarfBuzzShaper.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/platform/fonts/shaping/HarfBuzzShaper.cpp
diff --git a/Source/platform/fonts/shaping/HarfBuzzShaper.cpp b/Source/platform/fonts/shaping/HarfBuzzShaper.cpp
index 6545d138566b560682b4a72f1b8eba273ab58e53..80f3b114aedd7fc0ae16559346d0b80fcb0ea9a0 100644
--- a/Source/platform/fonts/shaping/HarfBuzzShaper.cpp
+++ b/Source/platform/fonts/shaping/HarfBuzzShaper.cpp
@@ -215,7 +215,7 @@ static inline float harfBuzzPositionToFloat(hb_position_t value)
return static_cast<float>(value) / (1 << 16);
}
-static inline unsigned countGraphemesInCluster(const UChar* normalizedBuffer, unsigned normalizedBufferLength, uint16_t startIndex, uint16_t endIndex)
+static inline unsigned countGraphemesInCluster(const UChar* str, unsigned strLength, uint16_t startIndex, uint16_t endIndex)
{
if (startIndex > endIndex) {
uint16_t tempIndex = startIndex;
@@ -223,8 +223,8 @@ static inline unsigned countGraphemesInCluster(const UChar* normalizedBuffer, un
endIndex = tempIndex;
}
uint16_t length = endIndex - startIndex;
- ASSERT(static_cast<unsigned>(startIndex + length) <= normalizedBufferLength);
- TextBreakIterator* cursorPosIterator = cursorMovementIterator(&normalizedBuffer[startIndex], length);
+ ASSERT(static_cast<unsigned>(startIndex + length) <= strLength);
+ TextBreakIterator* cursorPosIterator = cursorMovementIterator(&str[startIndex], length);
int cursorPos = cursorPosIterator->current();
int numGraphemes = -1;
@@ -346,39 +346,9 @@ float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset)
return position;
}
-static void normalizeCharacters(const TextRun& run, unsigned length, UChar* destination, unsigned* destinationLength)
-{
- unsigned position = 0;
- bool error = false;
- const UChar* source;
- String stringFor8BitRun;
- if (run.is8Bit()) {
- stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), run.length());
- source = stringFor8BitRun.characters16();
- } else {
- source = run.characters16();
- }
-
- *destinationLength = 0;
- while (position < length) {
- UChar32 character;
- U16_NEXT(source, position, length, character);
- // Don't normalize tabs as they are not treated as spaces for word-end.
- if (run.normalizeSpace() && Character::isNormalizedCanvasSpaceCharacter(character))
- character = space;
- else if (Character::treatAsSpace(character) && character != characterTabulation)
- character = space;
- else if (Character::treatAsZeroWidthSpaceInComplexScript(character))
- character = zeroWidthSpace;
-
- U16_APPEND(destination, *destinationLength, length, character, error);
- ASSERT_UNUSED(error, !error);
- }
-}
HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, ForTextEmphasisOrNot forTextEmphasis, HashSet<const SimpleFontData*>* fallbackFonts, FloatRect* bounds)
: m_font(font)
- , m_normalizedBufferLength(0)
, m_run(run)
, m_wordSpacingAdjustment(font->fontDescription().wordSpacing())
, m_expansion(0)
@@ -392,8 +362,6 @@ HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, ForTextEmph
, m_glyphBoundingBox(bounds)
, m_fallbackFonts(fallbackFonts)
{
- m_normalizedBuffer = adoptArrayPtr(new UChar[m_run.length() + 1]);
- normalizeCharacters(m_run, m_run.length(), m_normalizedBuffer.get(), &m_normalizedBufferLength);
setExpansion(m_run.expansion());
setFontFeatures();
}
@@ -424,7 +392,11 @@ void HarfBuzzShaper::setExpansion(float padding)
// If we have padding to distribute, then we try to give an equal
// amount to each expansion opportunity.
bool isAfterExpansion = m_isAfterExpansion;
- m_expansionOpportunityCount = Character::expansionOpportunityCount(m_normalizedBuffer.get(), m_normalizedBufferLength, m_run.direction(), isAfterExpansion, m_run.textJustify());
+ if (m_run.is8Bit())
+ m_expansionOpportunityCount = Character::expansionOpportunityCount(m_run.characters8(), m_run.length(), m_run.direction(), isAfterExpansion, m_run.textJustify());
+ else
+ m_expansionOpportunityCount = Character::expansionOpportunityCount(m_run.characters16(), m_run.length(), m_run.direction(), isAfterExpansion, m_run.textJustify());
+
if (isAfterExpansion && !m_run.allowsTrailingExpansion()) {
ASSERT(m_expansionOpportunityCount > 0);
--m_expansionOpportunityCount;
@@ -544,17 +516,9 @@ void HarfBuzzShaper::setFontFeatures()
bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer)
{
- if (!createHarfBuzzRuns())
- return false;
-
- m_totalWidth = 0;
- if (!shapeHarfBuzzRuns())
- return false;
-
- if (glyphBuffer && !fillGlyphBuffer(glyphBuffer))
- return false;
-
- return true;
+ if (m_run.is8Bit())
+ return shapeInternal<LChar>(glyphBuffer);
+ return shapeInternal<UChar>(glyphBuffer);
}
static inline int handleMultipleUChar(
@@ -563,14 +527,14 @@ static inline int handleMultipleUChar(
const SimpleFontData* currentFontData,
const UChar* currentCharacterPosition,
const UChar* markCharactersEnd,
- const UChar* normalizedBufferEnd)
+ const UChar* strEnd)
{
if (U_GET_GC_MASK(character) & U_GC_M_MASK) {
int markLength = clusterLength;
- while (markCharactersEnd < normalizedBufferEnd) {
+ while (markCharactersEnd < strEnd) {
UChar32 nextCharacter;
int nextCharacterLength = 0;
- U16_NEXT(markCharactersEnd, nextCharacterLength, normalizedBufferEnd - markCharactersEnd, nextCharacter);
+ U16_NEXT(markCharactersEnd, nextCharacterLength, strEnd - markCharactersEnd, nextCharacter);
if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK))
break;
markLength += nextCharacterLength;
@@ -592,11 +556,13 @@ struct CandidateRun {
UScriptCode script;
};
-static inline bool collectCandidateRuns(const UChar* normalizedBuffer,
- size_t bufferLength, const Font* font, Vector<CandidateRun>* runs, bool isSpaceNormalize)
+template<typename CharType>
+bool collectCandidateRuns(const TextRun& textRun,
+ const Font* font, Vector<CandidateRun>* runs, bool isSpaceNormalize)
{
- const UChar* normalizedBufferEnd = normalizedBuffer + bufferLength;
- SurrogatePairAwareTextIterator iterator(normalizedBuffer, 0, bufferLength, bufferLength);
+ const CharType* str = textRun.characters16();
+ const CharType* strEnd = str + textRun.length();
+ SurrogatePairAwareTextIterator iterator(str, 0, textRun.length(), textRun.length());
UChar32 character;
unsigned clusterLength = 0;
unsigned startIndexOfCurrentRun = 0;
@@ -611,7 +577,7 @@ static inline bool collectCandidateRuns(const UChar* normalizedBuffer,
return false;
do {
- const UChar* currentCharacterPosition = iterator.characters();
+ const CharType* currentCharacterPosition = iterator.characters();
const SimpleFontData* currentFontData = nextFontData;
UScriptCode currentScript = nextScript;
@@ -620,7 +586,7 @@ static inline bool collectCandidateRuns(const UChar* normalizedBuffer,
if (Character::treatAsZeroWidthSpace(character))
continue;
- int length = handleMultipleUChar(character, clusterLength, currentFontData, currentCharacterPosition, iterator.characters() + clusterLength, normalizedBufferEnd);
+ int length = handleMultipleUChar(character, clusterLength, currentFontData, currentCharacterPosition, iterator.characters() + clusterLength, strEnd);
if (length) {
clusterLength = length;
continue;
@@ -647,6 +613,22 @@ static inline bool collectCandidateRuns(const UChar* normalizedBuffer,
return true;
}
+template<>
+bool collectCandidateRuns<LChar>(const TextRun& textRun,
+ const Font* font, Vector<CandidateRun>* runs, bool isSpaceNormalize)
+{
+ const LChar* str = textRun.characters8();
+ UChar32 character = *str;
+ const SimpleFontData* fontData = font->glyphDataForCharacter(character,
+ false, isSpaceNormalize).fontData;
+
+ // For 8-bit runs we do not need to split based on script or check for
+ // clusters as the entire run is guaranteed to be latin-1.
+ CandidateRun run = { *str, 0, textRun.length(), fontData, USCRIPT_LATIN };
+ runs->append(run);
+ return true;
+}
+
static inline bool matchesAdjacentRun(UScriptCode* scriptExtensions, int length,
CandidateRun& adjacentRun)
{
@@ -736,11 +718,12 @@ static inline bool resolveCandidateRuns(Vector<CandidateRun>& runs)
// because most characters have break opportunities both before and after.
bool HarfBuzzShaper::createHarfBuzzRunsForSingleCharacter()
{
- ASSERT(m_normalizedBufferLength == 1);
- UChar32 character = m_normalizedBuffer[0];
+ ASSERT(m_run.length() == 1);
+ UChar32 character = m_run[0];
if (!U16_IS_SINGLE(character))
return false;
- const SimpleFontData* fontData = m_font->glyphDataForCharacter(character, false, m_run.normalizeSpace()).fontData;
+ const SimpleFontData* fontData = m_font->glyphDataForCharacter(character,
+ false, m_run.normalizeSpace()).fontData;
UErrorCode errorCode = U_ZERO_ERROR;
UScriptCode script = uscript_getScript(character, &errorCode);
if (U_FAILURE(errorCode))
@@ -749,14 +732,31 @@ bool HarfBuzzShaper::createHarfBuzzRunsForSingleCharacter()
return true;
}
+template<typename CharType>
+bool HarfBuzzShaper::shapeInternal(GlyphBuffer* glyphBuffer)
+{
+ if (!createHarfBuzzRuns<CharType>())
+ return false;
+
+ m_totalWidth = 0;
+ if (!shapeHarfBuzzRuns<CharType>())
+ return false;
+
+ if (glyphBuffer && !fillGlyphBuffer(glyphBuffer))
+ return false;
+
+ return true;
+}
+
+template<typename CharType>
bool HarfBuzzShaper::createHarfBuzzRuns()
{
- if (m_normalizedBufferLength == 1)
+ if (m_run.length() == 1)
return createHarfBuzzRunsForSingleCharacter();
Vector<CandidateRun> candidateRuns;
- if (!collectCandidateRuns(m_normalizedBuffer.get(),
- m_normalizedBufferLength, m_font, &candidateRuns, m_run.normalizeSpace()))
+ if (!collectCandidateRuns<CharType>(m_run, m_font, &candidateRuns,
+ m_run.normalizeSpace()))
return false;
if (!resolveCandidateRuns(candidateRuns))
@@ -772,7 +772,8 @@ bool HarfBuzzShaper::createHarfBuzzRuns()
break;
lastMatchingRun = candidateRuns[i];
}
- addHarfBuzzRun(run.start, lastMatchingRun.end, run.fontData, run.script);
+ if (lastMatchingRun.end > run.start)
+ addHarfBuzzRun(run.start, lastMatchingRun.end, run.fontData, run.script);
}
return !m_harfBuzzRuns.isEmpty();
}
@@ -807,17 +808,6 @@ void HarfBuzzShaper::addHarfBuzzRun(unsigned startCharacter,
ICUScriptToHBScript(script)));
}
-static inline bool isValidCachedResult(const Font* font, hb_direction_t dir,
- const String& localeString, const CachedShapingResults* cachedResults)
-{
- ASSERT(cachedResults);
- return cachedResults->dir == dir
- && cachedResults->font == *font
- && !cachedResults->font.loadingCustomFonts()
- && !font->loadingCustomFonts()
- && cachedResults->locale == localeString;
-}
-
static const uint16_t* toUint16(const UChar* src)
{
// FIXME: This relies on undefined behavior however it works on the
@@ -827,25 +817,111 @@ static const uint16_t* toUint16(const UChar* src)
return reinterpret_cast<const uint16_t*>(src);
}
-static inline void addToHarfBuzzBufferInternal(hb_buffer_t* buffer,
- const FontDescription& fontDescription, const UChar* normalizedBuffer,
+template<typename CharType>
+inline const CharType* charactersForRun(const TextRun& textRun,
+ unsigned startIndex)
+{
+ return textRun.data16(startIndex);
+}
+
+template<>
+inline const LChar* charactersForRun<LChar>(const TextRun& textRun,
+ unsigned startIndex)
+{
+ return textRun.data8(startIndex);
+}
+
+template<typename CharType>
+inline const CharType* charactersForString(const String& string)
+{
+ return string.characters16();
+}
+
+template<>
+inline const LChar* charactersForString<LChar>(const String& string)
+{
+ return string.characters8();
+}
+
+template<typename CharType>
+inline void addToHarfBuzzBufferInternal(hb_buffer_t* buffer,
+ const CharType* characters, unsigned length)
+{
+ hb_buffer_add_utf16(buffer, toUint16(characters), length, 0, length);
+}
+
+template<>
+inline void addToHarfBuzzBufferInternal<LChar>(hb_buffer_t* buffer,
+ const LChar* characters, unsigned length)
+{
+ // On linux we use the system version of harfbuzz so we cannot depend on
+ // hb_buffer_add_latin1 being available as it was only added in 0.9.38.
+ // FIXME: Remove this hack once all supported distros have HB >= 0.9.38.
skobes 2015/02/27 19:20:03 Is there a bug # to associate this FIXME with?
+#if OS(LINUX)
+ String converted = String::make16BitFrom8BitSource(characters, length);
+ hb_buffer_add_utf16(buffer, toUint16(converted.characters16()),
+ length, 0, length);
+#else
+ hb_buffer_add_latin1(buffer, characters, length, 0, length);
+#endif
+}
+
+template<typename CharType>
+inline void addToHarfBuzzBuffer(hb_buffer_t* buffer,
+ const FontDescription& fontDescription, const TextRun& textRun,
unsigned startIndex, unsigned numCharacters)
{
if (fontDescription.variant() == FontVariantSmallCaps
- && u_islower(normalizedBuffer[startIndex])) {
- String upperText = String(normalizedBuffer + startIndex, numCharacters)
- .upper();
- // TextRun is 16 bit, therefore upperText is 16 bit, even after we call
- // makeUpper().
- ASSERT(!upperText.is8Bit());
- hb_buffer_add_utf16(buffer, toUint16(upperText.characters16()),
- numCharacters, 0, numCharacters);
+ && u_islower(textRun[startIndex])) {
+ String upperText = String(charactersForRun<CharType>(
+ textRun, startIndex), numCharacters).upper();
+ // If TextRun is 16 bit upperText should be 16 bit too.
+ ASSERT(upperText.is8Bit() == textRun.is8Bit());
+ addToHarfBuzzBufferInternal<CharType>(buffer,
+ charactersForString<CharType>(upperText), numCharacters);
} else {
- hb_buffer_add_utf16(buffer, toUint16(normalizedBuffer + startIndex),
- numCharacters, 0, numCharacters);
+ addToHarfBuzzBufferInternal(buffer,
+ charactersForRun<CharType>(textRun, startIndex), numCharacters);
+ }
+}
+
+static inline UChar32 normalizeCharacter(UChar32 character,
+ bool normalizeSpace)
+{
+ if (normalizeSpace && Character::isNormalizedCanvasSpaceCharacter(character))
+ return space;
+ if (Character::treatAsSpace(character) && character != characterTabulation)
+ return space;
+ if (Character::treatAsZeroWidthSpaceInComplexScript(character))
+ return zeroWidthSpace;
+ return character;
+}
+
+static inline void normalizeHarfBuzzBuffer(hb_buffer_t* buffer,
+ bool normalizeSpace)
+{
+ unsigned length;
+ hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(buffer, &length);
+ for (unsigned i = 0; i < length; i++) {
+ UChar32 character = glyphInfos[i].codepoint;
+ UChar32 normalized = normalizeCharacter(character, normalizeSpace);
+ if (character != normalized)
+ glyphInfos[i].codepoint = normalized;
}
}
+static inline bool isValidCachedResult(const Font* font, hb_direction_t dir,
+ const String& localeString, const CachedShapingResults* cachedResults)
+{
+ ASSERT(cachedResults);
+ return cachedResults->dir == dir
+ && cachedResults->font == *font
+ && !cachedResults->font.loadingCustomFonts()
+ && !font->loadingCustomFonts()
+ && cachedResults->locale == localeString;
+}
+
+template<typename CharType>
bool HarfBuzzShaper::shapeHarfBuzzRuns()
{
HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_destroy);
@@ -870,7 +946,7 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns()
hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script());
hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->direction());
- const UChar* src = m_normalizedBuffer.get() + currentRun->startIndex();
+ const CharType* src = charactersForRun<CharType>(m_run, currentRun->startIndex());
std::wstring key(src, src + currentRun->numCharacters());
CachedShapingResults* cachedResults = runCache.find(key);
@@ -893,9 +969,10 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns()
static const uint16_t preContext = space;
hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0);
- addToHarfBuzzBufferInternal(harfBuzzBuffer.get(),
- fontDescription, m_normalizedBuffer.get(), currentRun->startIndex(),
+ addToHarfBuzzBuffer<CharType>(harfBuzzBuffer.get(),
+ fontDescription, m_run, currentRun->startIndex(),
currentRun->numCharacters());
+ normalizeHarfBuzzBuffer(harfBuzzBuffer.get(), m_run.normalizeSpace());
if (fontDescription.orientation() == Vertical)
face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get());
@@ -948,8 +1025,8 @@ void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb
// whether the buffer direction is horizontal or vertical.
float advance = harfBuzzPositionToFloat(glyphPositions[i].x_advance - glyphPositions[i].y_advance);
- unsigned currentCharacterIndex = currentRun->startIndex() + glyphInfos[i].cluster;
- RELEASE_ASSERT(m_normalizedBufferLength > currentCharacterIndex);
+ int currentCharacterIndex = currentRun->startIndex() + glyphInfos[i].cluster;
skobes 2015/02/27 19:20:03 Why is the index signed here, but unsigned in adju
+ RELEASE_ASSERT(m_run.length() > currentCharacterIndex);
bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1].cluster;
float spacing = 0;
@@ -989,7 +1066,7 @@ void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb
float HarfBuzzShaper::adjustSpacing(HarfBuzzRun* currentRun, size_t glyphIndex, unsigned currentCharacterIndex, HarfBuzzRun* previousRun, float& offsetX, float& totalAdvance)
{
float spacing = 0;
- UChar32 character = m_normalizedBuffer[currentCharacterIndex];
+ UChar32 character = normalizeCharacter(m_run[currentCharacterIndex], m_run.normalizeSpace());
if (m_letterSpacing && !Character::treatAsZeroWidthSpace(character))
spacing += m_letterSpacing;
@@ -1013,8 +1090,8 @@ float HarfBuzzShaper::adjustSpacing(HarfBuzzRun* currentRun, size_t glyphIndex,
// isCJKIdeographOrSymbol() has expansion opportunities both before and after each character.
// http://www.w3.org/TR/jlreq/#line_adjustment
- if (U16_IS_LEAD(character) && currentCharacterIndex + 1 < m_normalizedBufferLength && U16_IS_TRAIL(m_normalizedBuffer[currentCharacterIndex + 1]))
- character = U16_GET_SUPPLEMENTARY(character, m_normalizedBuffer[currentCharacterIndex + 1]);
+ if (!m_run.is8Bit() && U16_IS_LEAD(character) && currentCharacterIndex + 1 < static_cast<unsigned>(m_run.charactersLength()) && U16_IS_TRAIL(m_run[currentCharacterIndex + 1]))
+ character = U16_GET_SUPPLEMENTARY(character, normalizeCharacter(m_run[currentCharacterIndex + 1], m_run.normalizeSpace()));
if (!Character::isCJKIdeographOrSymbol(character)) {
m_isAfterExpansion = false;
return spacing;
@@ -1130,7 +1207,7 @@ float HarfBuzzShaper::fillGlyphBufferForTextEmphasis(GlyphBuffer* glyphBuffer, H
else
clusterEnd = isRunEnd ? currentRun->startIndex() + currentRun->numCharacters() : currentRun->startIndex() + glyphToCharacterIndexes[i + 1];
- graphemesInCluster = countGraphemesInCluster(m_normalizedBuffer.get(), m_normalizedBufferLength, clusterStart, clusterEnd);
+ graphemesInCluster = m_run.is8Bit() ? 1 : countGraphemesInCluster(m_run.characters16(), m_run.length(), clusterStart, clusterEnd);
if (!graphemesInCluster || !clusterAdvance)
continue;
@@ -1174,7 +1251,7 @@ int HarfBuzzShaper::offsetForPosition(float targetX)
float currentX = 0;
if (m_run.rtl()) {
- charactersSoFar = m_normalizedBufferLength;
+ charactersSoFar = m_run.length();
for (int i = m_harfBuzzRuns.size() - 1; i >= 0; --i) {
charactersSoFar -= m_harfBuzzRuns[i]->numCharacters();
float nextX = currentX + m_harfBuzzRuns[i]->width();
« no previous file with comments | « Source/platform/fonts/shaping/HarfBuzzShaper.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698