Chromium Code Reviews| Index: Source/platform/fonts/shaping/HarfBuzzShaper.cpp |
| diff --git a/Source/platform/fonts/shaping/HarfBuzzShaper.cpp b/Source/platform/fonts/shaping/HarfBuzzShaper.cpp |
| index 7c32148823d86b126295366d6c93b47cfba77001..7add6e4e50e825210457c985815c546b7adeabb6 100644 |
| --- a/Source/platform/fonts/shaping/HarfBuzzShaper.cpp |
| +++ b/Source/platform/fonts/shaping/HarfBuzzShaper.cpp |
| @@ -54,185 +54,457 @@ |
| namespace blink { |
| -template<typename T> |
| -class HarfBuzzScopedPtr { |
| -public: |
| - typedef void (*DestroyFunction)(T*); |
| +struct HarfBuzzRunGlyphData { |
| + uint16_t glyph; |
| + uint16_t characterIndex; |
| + float advance; |
| + FloatSize offset; |
| +}; |
| - HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy) |
| - : m_ptr(ptr) |
| - , m_destroy(destroy) |
| +struct ShapeResult::RunInfo { |
| + RunInfo(const SimpleFontData* font, hb_direction_t dir, hb_script_t script, |
| + unsigned startIndex, unsigned numGlyphs, unsigned numCharacters) |
| + : m_fontData(font), m_direction(dir), m_script(script) |
| + , m_startIndex(startIndex), m_numCharacters(numCharacters) |
| + , m_numGlyphs(numGlyphs) |
| { |
| - ASSERT(m_destroy); |
| + m_glyphData.resize(m_numGlyphs); |
| } |
| - ~HarfBuzzScopedPtr() |
| + |
| + float xPositionForOffset(unsigned) const; |
| + int characterIndexForXPosition(float) const; |
| + void setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, |
| + float offsetX, float offsetY); |
| + |
| + void addAdvance(unsigned index, float advance) |
| { |
| - if (m_ptr) |
| - (*m_destroy)(m_ptr); |
| + m_glyphData[index].advance += advance; |
| } |
| - T* get() { return m_ptr; } |
| - void set(T* ptr) { m_ptr = ptr; } |
| -private: |
| - T* m_ptr; |
| - DestroyFunction m_destroy; |
| -}; |
| - |
| + size_t glyphToCharacterIndex(size_t i) const |
| + { |
| + return m_startIndex + m_glyphData[i].characterIndex; |
| + } |
| -static const unsigned cHarfBuzzCacheMaxSize = 256; |
| + const SimpleFontData* m_fontData; |
| + hb_direction_t m_direction; |
| + hb_script_t m_script; |
| + Vector<HarfBuzzRunGlyphData> m_glyphData; |
| + unsigned m_startIndex; |
| + unsigned m_numCharacters; |
| + unsigned m_numGlyphs; |
| + float m_width; |
| +}; |
| -struct CachedShapingResultsLRUNode; |
| -struct CachedShapingResults; |
| -typedef std::map<std::wstring, CachedShapingResults*> CachedShapingResultsMap; |
| -typedef std::list<CachedShapingResultsLRUNode*> CachedShapingResultsLRU; |
| +float ShapeResult::RunInfo::xPositionForOffset(unsigned offset) const |
| +{ |
| + ASSERT(offset < m_numCharacters); |
| + unsigned glyphIndex = 0; |
| + float position = 0; |
| + if (m_direction == HB_DIRECTION_RTL) { |
| + while (glyphIndex < m_numGlyphs && m_glyphData[glyphIndex].characterIndex > offset) { |
| + position += m_glyphData[glyphIndex].advance; |
| + ++glyphIndex; |
| + } |
| + // For RTL, we need to return the right side boundary of the character. |
| + // Add advance of glyphs which are part of the character. |
| + while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].characterIndex == m_glyphData[glyphIndex + 1].characterIndex) { |
| + position += m_glyphData[glyphIndex].advance; |
| + ++glyphIndex; |
| + } |
| + position += m_glyphData[glyphIndex].advance; |
| + } else { |
| + while (glyphIndex < m_numGlyphs && m_glyphData[glyphIndex].characterIndex < offset) { |
| + position += m_glyphData[glyphIndex].advance; |
| + ++glyphIndex; |
| + } |
| + } |
| + return position; |
| +} |
| -struct CachedShapingResults { |
| - CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* runFont, hb_direction_t runDir, const String& newLocale); |
| - ~CachedShapingResults(); |
| +int ShapeResult::RunInfo::characterIndexForXPosition(float targetX) const |
| +{ |
| + ASSERT(targetX <= m_width); |
| + float currentX = 0; |
| + float currentAdvance = m_glyphData[0].advance; |
| + unsigned glyphIndex = 0; |
| - hb_buffer_t* buffer; |
| - Font font; |
| - hb_direction_t dir; |
| - String locale; |
| - CachedShapingResultsLRU::iterator lru; |
| -}; |
| + // Sum up advances that belong to the first character. |
| + while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].characterIndex == m_glyphData[glyphIndex + 1].characterIndex) |
| + currentAdvance += m_glyphData[++glyphIndex].advance; |
| + currentAdvance = currentAdvance / 2.0; |
| + if (targetX <= currentAdvance) |
| + return m_direction == HB_DIRECTION_RTL ? m_numCharacters : 0; |
| -struct CachedShapingResultsLRUNode { |
| - CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEntry); |
| - ~CachedShapingResultsLRUNode(); |
| + currentX = currentAdvance; |
| + ++glyphIndex; |
| + while (glyphIndex < m_numGlyphs) { |
| + unsigned prevCharacterIndex = m_glyphData[glyphIndex - 1].characterIndex; |
| + float prevAdvance = currentAdvance; |
| + currentAdvance = m_glyphData[glyphIndex].advance; |
| + while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].characterIndex == m_glyphData[glyphIndex + 1].characterIndex) |
| + currentAdvance += m_glyphData[++glyphIndex].advance; |
| + currentAdvance = currentAdvance / 2.0; |
| + float nextX = currentX + prevAdvance + currentAdvance; |
| + if (currentX <= targetX && targetX <= nextX) |
| + return m_direction == HB_DIRECTION_RTL ? prevCharacterIndex : m_glyphData[glyphIndex].characterIndex; |
| + currentX = nextX; |
| + ++glyphIndex; |
| + } |
| - CachedShapingResultsMap::iterator entry; |
| -}; |
| + return m_direction == HB_DIRECTION_RTL ? 0 : m_numCharacters; |
| +} |
| -CachedShapingResults::CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* fontData, hb_direction_t dirData, const String& newLocale) |
| - : buffer(harfBuzzBuffer) |
| - , font(*fontData) |
| - , dir(dirData) |
| - , locale(newLocale) |
| +void ShapeResult::RunInfo::setGlyphAndPositions(unsigned index, |
| + uint16_t glyphId, float advance, float offsetX, float offsetY) |
| { |
| + HarfBuzzRunGlyphData& data = m_glyphData[index]; |
| + data.glyph = glyphId; |
| + data.advance = advance; |
| + data.offset = FloatSize(offsetX, offsetY); |
| } |
| -CachedShapingResults::~CachedShapingResults() |
| +ShapeResult::~ShapeResult() |
| { |
| - hb_buffer_destroy(buffer); |
| + unsigned destroyed = 0; |
| + for (unsigned i = 0; i < m_runs.size(); i++) { |
| + if (m_runs[i]) { |
| + delete m_runs[i]; |
| + destroyed++; |
| + } |
| + } |
| } |
| -CachedShapingResultsLRUNode::CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEntry) |
| - : entry(cacheEntry) |
| +float ShapeResult::fillGlyphBuffer(GlyphBuffer* glyphBuffer, |
| + float initialAdvance, unsigned from, unsigned to, unsigned runOffset) |
| { |
| + ASSERT(glyphBuffer); |
| + unsigned numRuns = m_runs.size(); |
| + float advanceSoFar = initialAdvance; |
| + |
| + if (m_direction == LTR) { |
| + for (unsigned i = 0; i < numRuns; i++) { |
| + advanceSoFar += fillGlyphBufferForRun<LTR>(glyphBuffer, |
| + m_runs[i], advanceSoFar, from, to, runOffset); |
| + } |
| + } else { |
| + for (unsigned i = 0; i < numRuns; i++) { |
| + advanceSoFar += fillGlyphBufferForRun<RTL>(glyphBuffer, |
| + m_runs[i], advanceSoFar, from, to, runOffset); |
| + } |
| + } |
| + |
| + return advanceSoFar - initialAdvance; |
| } |
| -CachedShapingResultsLRUNode::~CachedShapingResultsLRUNode() |
| +float ShapeResult::fillGlyphBufferForTextEmphasis(GlyphBuffer* glyphBuffer, |
| + float initialAdvance, const TextRun& textRun, const GlyphData* emphasisData, |
| + unsigned from, unsigned to, unsigned runOffset) |
| { |
| -} |
| + ASSERT(glyphBuffer); |
| + unsigned numRuns = m_runs.size(); |
| + float advanceSoFar = initialAdvance; |
| + for (unsigned i = 0; i < numRuns; i++) { |
| + const RunInfo* run = m_runs[m_direction == LTR ? i : (numRuns - i - 1)]; |
| + advanceSoFar += fillGlyphBufferForTextEmphasisRun(glyphBuffer, |
| + run, textRun, emphasisData, |
| + advanceSoFar, from, to, runOffset); |
| + } |
| -class HarfBuzzRunCache { |
| -public: |
| - HarfBuzzRunCache(); |
| - ~HarfBuzzRunCache(); |
| + return advanceSoFar - initialAdvance; |
| +} |
| - CachedShapingResults* find(const std::wstring& key) const; |
| - void remove(CachedShapingResults* node); |
| - void moveToBack(CachedShapingResults* node); |
| - bool insert(const std::wstring& key, CachedShapingResults* run); |
| +static inline void addGlyphToBuffer(GlyphBuffer* glyphBuffer, float advance, |
| + hb_direction_t direction, const SimpleFontData* fontData, |
| + const HarfBuzzRunGlyphData& glyphData) |
| +{ |
| + FloatPoint startOffset = HB_DIRECTION_IS_HORIZONTAL(direction) |
| + ? FloatPoint(advance, 0) |
| + : FloatPoint(0, advance); |
| + glyphBuffer->add(glyphData.glyph, fontData, startOffset + glyphData.offset); |
| +} |
| -private: |
| - CachedShapingResultsMap m_harfBuzzRunMap; |
| - CachedShapingResultsLRU m_harfBuzzRunLRU; |
| -}; |
| +template<TextDirection direction> |
| +float ShapeResult::fillGlyphBufferForRun(GlyphBuffer* glyphBuffer, |
| + const RunInfo* run, float initialAdvance, unsigned from, unsigned to, |
| + unsigned runOffset) |
| +{ |
| + float advanceSoFar = initialAdvance; |
| + unsigned numGlyphs = run->m_numGlyphs; |
| + for (unsigned i = 0; i < numGlyphs; ++i) { |
| + const HarfBuzzRunGlyphData& glyphData = run->m_glyphData[i]; |
| + uint16_t currentCharacterIndex = run->m_startIndex + |
| + glyphData.characterIndex + runOffset; |
| + if ((direction == RTL && currentCharacterIndex >= to) |
| + || (direction == LTR && currentCharacterIndex < from)) { |
| + advanceSoFar += glyphData.advance; |
| + } else if ((direction == RTL && currentCharacterIndex >= from) |
| + || (direction == LTR && currentCharacterIndex < to)) { |
| + addGlyphToBuffer(glyphBuffer, advanceSoFar, run->m_direction, |
| + run->m_fontData, glyphData); |
| + advanceSoFar += glyphData.advance; |
| + } |
| + } |
| + return advanceSoFar - initialAdvance; |
| +} |
| -HarfBuzzRunCache::HarfBuzzRunCache() |
| +static inline unsigned countGraphemesInCluster(const UChar* str, |
| + unsigned strLength, uint16_t startIndex, uint16_t endIndex) |
| { |
| + if (startIndex > endIndex) { |
| + uint16_t tempIndex = startIndex; |
| + startIndex = endIndex; |
| + endIndex = tempIndex; |
| + } |
| + uint16_t length = endIndex - startIndex; |
| + ASSERT(static_cast<unsigned>(startIndex + length) <= strLength); |
| + TextBreakIterator* cursorPosIterator = cursorMovementIterator(&str[startIndex], length); |
| + |
| + int cursorPos = cursorPosIterator->current(); |
| + int numGraphemes = -1; |
| + while (0 <= cursorPos) { |
| + cursorPos = cursorPosIterator->next(); |
| + numGraphemes++; |
| + } |
| + return numGraphemes < 0 ? 0 : numGraphemes; |
| } |
| -HarfBuzzRunCache::~HarfBuzzRunCache() |
| +static inline void addEmphasisMark(GlyphBuffer* buffer, |
| + const GlyphData* emphasisData, FloatPoint glyphCenter, |
| + float midGlyphOffset) |
| { |
| - for (CachedShapingResultsMap::iterator it = m_harfBuzzRunMap.begin(); it != m_harfBuzzRunMap.end(); ++it) |
| - delete it->second; |
| - for (CachedShapingResultsLRU::iterator it = m_harfBuzzRunLRU.begin(); it != m_harfBuzzRunLRU.end(); ++it) |
| - delete *it; |
| + ASSERT(buffer); |
| + ASSERT(emphasisData); |
| + |
| + const SimpleFontData* emphasisFontData = emphasisData->fontData; |
| + ASSERT(emphasisFontData); |
| + |
| + bool isVertical = emphasisFontData->platformData().isVerticalAnyUpright() |
| + && emphasisFontData->verticalData(); |
| + |
| + if (!isVertical) { |
| + buffer->add(emphasisData->glyph, emphasisFontData, |
| + midGlyphOffset - glyphCenter.x()); |
| + } else { |
| + buffer->add(emphasisData->glyph, emphasisFontData, |
| + FloatPoint(-glyphCenter.x(), midGlyphOffset - glyphCenter.y())); |
| + } |
| } |
| -bool HarfBuzzRunCache::insert(const std::wstring& key, CachedShapingResults* data) |
| +float ShapeResult::fillGlyphBufferForTextEmphasisRun(GlyphBuffer* glyphBuffer, |
| + const RunInfo* run, const TextRun& textRun, const GlyphData* emphasisData, |
| + float initialAdvance, unsigned from, unsigned to, unsigned runOffset) |
| { |
| - std::pair<CachedShapingResultsMap::iterator, bool> results = |
| - m_harfBuzzRunMap.insert(CachedShapingResultsMap::value_type(key, data)); |
| + unsigned numGlyphs = run->m_numGlyphs; |
| + if (!numGlyphs) |
| + return 0; |
| - if (!results.second) |
| - return false; |
| + unsigned graphemesInCluster = 1; |
| + float clusterAdvance = 0; |
| + uint16_t clusterStart; |
| + |
| + FloatPoint glyphCenter = emphasisData->fontData-> |
| + boundsForGlyph(emphasisData->glyph).center(); |
| + |
| + // A "cluster" in this context means a cluster as it is used by HarfBuzz: |
| + // The minimal group of characters and corresponding glyphs, that cannot be broken |
| + // down further from a text shaping point of view. |
| + // A cluster can contain multiple glyphs and grapheme clusters, with mutually |
| + // overlapping boundaries. Below we count grapheme clusters per HarfBuzz clusters, |
| + // then linearly split the sum of corresponding glyph advances by the number of |
| + // grapheme clusters in order to find positions for emphasis mark drawing. |
| + if (m_direction == RTL) |
| + clusterStart = run->m_startIndex + run->m_numCharacters; |
| + else |
| + clusterStart = run->glyphToCharacterIndex(0); |
| - CachedShapingResultsLRUNode* node = new CachedShapingResultsLRUNode(results.first); |
| + float advanceSoFar = initialAdvance; |
| + for (unsigned i = 0; i < numGlyphs; ++i) { |
| + const HarfBuzzRunGlyphData& glyphData = run->m_glyphData[i]; |
| + uint16_t currentCharacterIndex = run->m_startIndex + runOffset + |
| + glyphData.characterIndex; |
| + bool isRunEnd = (i + 1 == numGlyphs); |
| + bool isClusterEnd = isRunEnd || (run->glyphToCharacterIndex(i + 1) != currentCharacterIndex); |
| - m_harfBuzzRunLRU.push_back(node); |
| - data->lru = --m_harfBuzzRunLRU.end(); |
| + if ((m_direction == RTL && currentCharacterIndex >= to) || (m_direction != RTL && currentCharacterIndex < from)) { |
| + advanceSoFar += glyphData.advance; |
| + m_direction == RTL ? --clusterStart : ++clusterStart; |
| + continue; |
| + } |
| + |
| + clusterAdvance += glyphData.advance; |
| - if (m_harfBuzzRunMap.size() > cHarfBuzzCacheMaxSize) { |
| - CachedShapingResultsLRUNode* lru = m_harfBuzzRunLRU.front(); |
| - CachedShapingResults* foo = lru->entry->second; |
| - m_harfBuzzRunMap.erase(lru->entry); |
| - m_harfBuzzRunLRU.pop_front(); |
| - delete foo; |
| - delete lru; |
| + if (textRun.is8Bit()) { |
| + float glyphAdvanceX = glyphData.advance; |
| + if (Character::canReceiveTextEmphasis(textRun[currentCharacterIndex])) { |
| + addEmphasisMark(glyphBuffer, emphasisData, glyphCenter, advanceSoFar + glyphAdvanceX / 2); |
| + } |
| + advanceSoFar += glyphAdvanceX; |
| + } else if (isClusterEnd) { |
| + uint16_t clusterEnd; |
| + if (m_direction == RTL) |
| + clusterEnd = currentCharacterIndex; |
| + else |
| + clusterEnd = isRunEnd ? run->m_startIndex + run->m_numCharacters : run->glyphToCharacterIndex(i + 1); |
| + |
| + graphemesInCluster = countGraphemesInCluster(textRun.characters16(), textRun.charactersLength(), clusterStart, clusterEnd); |
| + if (!graphemesInCluster || !clusterAdvance) |
| + continue; |
| + |
| + float glyphAdvanceX = clusterAdvance / graphemesInCluster; |
| + for (unsigned j = 0; j < graphemesInCluster; ++j) { |
| + // Do not put emphasis marks on space, separator, and control characters. |
| + if (Character::canReceiveTextEmphasis(textRun[currentCharacterIndex])) |
| + addEmphasisMark(glyphBuffer, emphasisData, glyphCenter, advanceSoFar + glyphAdvanceX / 2); |
| + advanceSoFar += glyphAdvanceX; |
| + } |
| + clusterStart = clusterEnd; |
| + clusterAdvance = 0; |
| + } |
| } |
| - return true; |
| + return advanceSoFar - initialAdvance; |
| } |
| -inline CachedShapingResults* HarfBuzzRunCache::find(const std::wstring& key) const |
| +FloatRect ShapeResult::selectionRect(const FloatPoint& point, int height, |
| + unsigned from, unsigned to) |
| { |
| - CachedShapingResultsMap::const_iterator it = m_harfBuzzRunMap.find(key); |
| + float currentX = 0; |
| + float fromX = 0; |
|
leviw_travelin_and_unemployed
2015/06/22 18:52:08
I may have called this minX and maxX.
eae
2015/06/22 19:19:53
This method (as well as offsetForPosition) needs m
|
| + float toX = 0; |
| + bool foundFromX = false; |
| + bool foundToX = false; |
| + |
| + if (m_direction == RTL) |
| + currentX = m_width; |
| + for (unsigned i = 0; i < m_runs.size(); i++) { |
| + if (m_direction == RTL) |
| + currentX -= m_runs[i]->m_width; |
| + unsigned numCharacters = m_runs[i]->m_numCharacters; |
| + if (!foundFromX && from < numCharacters) { |
| + fromX = m_runs[i]->xPositionForOffset(from) + currentX; |
| + foundFromX = true; |
| + } else { |
| + from -= numCharacters; |
| + } |
| + |
| + if (!foundToX && to < numCharacters) { |
| + toX = m_runs[i]->xPositionForOffset(to) + currentX; |
| + foundToX = true; |
| + } else { |
| + to -= numCharacters; |
| + } |
| + |
| + if (foundFromX && foundToX) |
| + break; |
| + if (m_direction != RTL) |
| + currentX += m_runs[i]->m_width; |
| + } |
| + |
| + // The position in question might be just after the text. |
| + if (!foundFromX) |
| + fromX = 0; |
| + if (!foundToX) |
| + toX = m_direction == RTL ? 0 : m_width; |
| + // None of our HarfBuzzRuns is part of the selection, |
| + // possibly invalid from, to arguments. |
| + if (!foundToX && !foundFromX) |
| + fromX = toX = 0; |
| - return it != m_harfBuzzRunMap.end() ? it->second : 0; |
| + if (fromX < toX) |
| + return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); |
| + return FloatRect(point.x() + toX, point.y(), fromX - toX, height); |
| } |
| -inline void HarfBuzzRunCache::remove(CachedShapingResults* node) |
| +int ShapeResult::offsetForPosition(float targetX) |
| { |
| - CachedShapingResultsLRUNode* lruNode = *node->lru; |
| + int charactersSoFar = 0; |
| + float currentX = 0; |
| - m_harfBuzzRunLRU.erase(node->lru); |
| - m_harfBuzzRunMap.erase(lruNode->entry); |
| - delete lruNode; |
| - delete node; |
| + if (m_direction == RTL) { |
| + charactersSoFar = m_numCharacters; |
| + for (unsigned i = 0; i < m_runs.size(); ++i) { |
| + charactersSoFar -= m_runs[i]->m_numCharacters; |
| + float nextX = currentX + m_runs[i]->m_width; |
| + float offsetForRun = targetX - currentX; |
| + if (offsetForRun >= 0 && offsetForRun <= m_runs[i]->m_width) { |
| + // The x value in question is within this script run. |
| + const unsigned index = m_runs[i]->characterIndexForXPosition(offsetForRun); |
| + return charactersSoFar + index; |
| + } |
| + currentX = nextX; |
| + } |
| + } else { |
| + for (unsigned i = 0; i < m_runs.size(); ++i) { |
| + float nextX = currentX + m_runs[i]->m_width; |
| + float offsetForRun = targetX - currentX; |
| + if (offsetForRun >= 0 && offsetForRun <= m_runs[i]->m_width) { |
| + const unsigned index = m_runs[i]->characterIndexForXPosition(offsetForRun); |
| + return charactersSoFar + index; |
| + } |
| + charactersSoFar += m_runs[i]->m_numCharacters; |
| + currentX = nextX; |
| + } |
| + } |
| + |
| + return charactersSoFar; |
| } |
| -inline void HarfBuzzRunCache::moveToBack(CachedShapingResults* node) |
| +unsigned ShapeResult::numberOfRunsForTesting() const |
| { |
| - CachedShapingResultsLRUNode* lruNode = *node->lru; |
| - m_harfBuzzRunLRU.erase(node->lru); |
| - m_harfBuzzRunLRU.push_back(lruNode); |
| - node->lru = --m_harfBuzzRunLRU.end(); |
| + return m_runs.size(); |
| } |
| -HarfBuzzRunCache& harfBuzzRunCache() |
| +bool ShapeResult::runInfoForTesting(unsigned runIndex, unsigned& startIndex, |
| + unsigned& numGlyphs, hb_script_t& script) |
| { |
| - DEFINE_STATIC_LOCAL(HarfBuzzRunCache, globalHarfBuzzRunCache, ()); |
| - return globalHarfBuzzRunCache; |
| + if (runIndex < m_runs.size() && m_runs[runIndex]) { |
| + startIndex = m_runs[runIndex]->m_startIndex; |
| + numGlyphs = m_runs[runIndex]->m_numGlyphs; |
| + script = m_runs[runIndex]->m_script; |
| + return true; |
| + } |
| + return false; |
| } |
| -static inline float harfBuzzPositionToFloat(hb_position_t value) |
| +uint16_t ShapeResult::glyphForTesting(unsigned runIndex, size_t glyphIndex) |
| { |
| - return static_cast<float>(value) / (1 << 16); |
| + return m_runs[runIndex]->m_glyphData[glyphIndex].glyph; |
| } |
| -static inline unsigned countGraphemesInCluster(const UChar* normalizedBuffer, unsigned normalizedBufferLength, uint16_t startIndex, uint16_t endIndex) |
| +float ShapeResult::advanceForTesting(unsigned runIndex, size_t glyphIndex) |
| { |
| - if (startIndex > endIndex) { |
| - uint16_t tempIndex = startIndex; |
| - startIndex = endIndex; |
| - endIndex = tempIndex; |
| - } |
| - uint16_t length = endIndex - startIndex; |
| - ASSERT(static_cast<unsigned>(startIndex + length) <= normalizedBufferLength); |
| - TextBreakIterator* cursorPosIterator = cursorMovementIterator(&normalizedBuffer[startIndex], length); |
| + return m_runs[runIndex]->m_glyphData[glyphIndex].advance; |
| +} |
| - int cursorPos = cursorPosIterator->current(); |
| - int numGraphemes = -1; |
| - while (0 <= cursorPos) { |
| - cursorPos = cursorPosIterator->next(); |
| - numGraphemes++; |
| +template<typename T> |
| +class HarfBuzzScopedPtr { |
| +public: |
| + typedef void (*DestroyFunction)(T*); |
| + |
| + HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy) |
| + : m_ptr(ptr) |
| + , m_destroy(destroy) |
| + { |
| + ASSERT(m_destroy); |
| } |
| - return numGraphemes < 0 ? 0 : numGraphemes; |
| + ~HarfBuzzScopedPtr() |
| + { |
| + if (m_ptr) |
| + (*m_destroy)(m_ptr); |
| + } |
| + |
| + T* get() { return m_ptr; } |
| + void set(T* ptr) { m_ptr = ptr; } |
| +private: |
| + T* m_ptr; |
| + DestroyFunction m_destroy; |
| +}; |
| + |
| +static inline float harfBuzzPositionToFloat(hb_position_t value) |
| +{ |
| + return static_cast<float>(value) / (1 << 16); |
| } |
| inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData, unsigned startIndex, unsigned numCharacters, hb_direction_t direction, hb_script_t script) |
| @@ -242,7 +514,6 @@ inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData, |
| , m_numGlyphs(0) |
| , m_direction(direction) |
| , m_script(script) |
| - , m_width(0) |
| { |
| } |
| @@ -253,8 +524,6 @@ inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const HarfBuzzRun& rhs) |
| , m_numGlyphs(rhs.m_numGlyphs) |
| , m_direction(rhs.m_direction) |
| , m_script(rhs.m_script) |
| - , m_glyphData(rhs.m_glyphData) |
| - , m_width(rhs.m_width) |
| { |
| } |
| @@ -262,84 +531,6 @@ HarfBuzzShaper::HarfBuzzRun::~HarfBuzzRun() |
| { |
| } |
| -inline void HarfBuzzShaper::HarfBuzzRun::applyShapeResult(hb_buffer_t* harfBuzzBuffer) |
| -{ |
| - m_numGlyphs = hb_buffer_get_length(harfBuzzBuffer); |
| - m_glyphData.resize(m_numGlyphs); |
| -} |
| - |
| -inline void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, float offsetX, float offsetY) |
| -{ |
| - HarfBuzzRunGlyphData& data = glyphData(index); |
| - data.glyph = glyphId; |
| - data.advance = advance; |
| - data.offset = FloatSize(offsetX, offsetY); |
| -} |
| - |
| -void HarfBuzzShaper::HarfBuzzRun::addAdvance(unsigned index, float advance) |
| -{ |
| - glyphData(index).advance += advance; |
| -} |
| - |
| -int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX) |
| -{ |
| - ASSERT(targetX <= m_width); |
| - float currentX = 0; |
| - float currentAdvance = m_glyphData[0].advance; |
| - unsigned glyphIndex = 0; |
| - |
| - // Sum up advances that belong to a character. |
| - while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].characterIndex == m_glyphData[glyphIndex + 1].characterIndex) |
| - currentAdvance += m_glyphData[++glyphIndex].advance; |
| - currentAdvance = currentAdvance / 2.0; |
| - if (targetX <= currentAdvance) |
| - return rtl() ? m_numCharacters : 0; |
| - |
| - currentX = currentAdvance; |
| - ++glyphIndex; |
| - while (glyphIndex < m_numGlyphs) { |
| - unsigned prevCharacterIndex = m_glyphData[glyphIndex - 1].characterIndex; |
| - float prevAdvance = currentAdvance; |
| - currentAdvance = m_glyphData[glyphIndex].advance; |
| - while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].characterIndex == m_glyphData[glyphIndex + 1].characterIndex) |
| - currentAdvance += m_glyphData[++glyphIndex].advance; |
| - currentAdvance = currentAdvance / 2.0; |
| - float nextX = currentX + prevAdvance + currentAdvance; |
| - if (currentX <= targetX && targetX <= nextX) |
| - return rtl() ? prevCharacterIndex : m_glyphData[glyphIndex].characterIndex; |
| - currentX = nextX; |
| - ++glyphIndex; |
| - } |
| - |
| - return rtl() ? 0 : m_numCharacters; |
| -} |
| - |
| -float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset) |
| -{ |
| - ASSERT(offset < m_numCharacters); |
| - unsigned glyphIndex = 0; |
| - float position = 0; |
| - if (rtl()) { |
| - while (glyphIndex < m_numGlyphs && m_glyphData[glyphIndex].characterIndex > offset) { |
| - position += m_glyphData[glyphIndex].advance; |
| - ++glyphIndex; |
| - } |
| - // For RTL, we need to return the right side boundary of the character. |
| - // Add advance of glyphs which are part of the character. |
| - while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].characterIndex == m_glyphData[glyphIndex + 1].characterIndex) { |
| - position += m_glyphData[glyphIndex].advance; |
| - ++glyphIndex; |
| - } |
| - position += m_glyphData[glyphIndex].advance; |
| - } else { |
| - while (glyphIndex < m_numGlyphs && m_glyphData[glyphIndex].characterIndex < offset) { |
| - position += m_glyphData[glyphIndex].advance; |
| - ++glyphIndex; |
| - } |
| - } |
| - return position; |
| -} |
| - |
| static void normalizeCharacters(const TextRun& run, unsigned length, UChar* destination, unsigned* destinationLength) |
| { |
| unsigned position = 0; |
| @@ -370,16 +561,13 @@ static void normalizeCharacters(const TextRun& run, unsigned length, UChar* dest |
| } |
| } |
| -HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, const GlyphData* emphasisData, |
| - HashSet<const SimpleFontData*>* fallbackFonts, FloatRect* bounds) |
| - : Shaper(font, run, emphasisData, fallbackFonts, bounds) |
| +HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, |
| + HashSet<const SimpleFontData*>* fallbackFonts) |
| + : Shaper(font, run, nullptr, fallbackFonts) |
| , m_normalizedBufferLength(0) |
| , m_wordSpacingAdjustment(font->fontDescription().wordSpacing()) |
| , m_letterSpacing(font->fontDescription().letterSpacing()) |
| , m_expansionOpportunityCount(0) |
| - , m_fromIndex(0) |
| - , m_toIndex(m_textRun.length()) |
| - , m_totalWidth(0) |
| { |
| m_normalizedBuffer = adoptArrayPtr(new UChar[m_textRun.length() + 1]); |
| normalizeCharacters(m_textRun, m_textRun.length(), m_normalizedBuffer.get(), &m_normalizedBufferLength); |
| @@ -425,15 +613,6 @@ void HarfBuzzShaper::setExpansion(float padding) |
| m_expansionPerOpportunity = 0; |
| } |
| - |
| -void HarfBuzzShaper::setDrawRange(int from, int to) |
| -{ |
| - ASSERT_WITH_SECURITY_IMPLICATION(from >= 0); |
| - ASSERT_WITH_SECURITY_IMPLICATION(to <= m_textRun.length()); |
| - m_fromIndex = from; |
| - m_toIndex = to; |
| -} |
| - |
| void HarfBuzzShaper::setFontFeatures() |
| { |
| const FontDescription& description = m_font->fontDescription(); |
| @@ -531,18 +710,20 @@ void HarfBuzzShaper::setFontFeatures() |
| } |
| } |
| -bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer) |
| +PassRefPtr<ShapeResult> HarfBuzzShaper::shapeResult() |
| { |
| if (!createHarfBuzzRuns()) |
| - return false; |
| - |
| - if (!shapeHarfBuzzRuns()) |
| - return false; |
| - |
| - if (glyphBuffer && !fillGlyphBuffer(glyphBuffer)) |
| - return false; |
| + return nullptr; |
| + |
| + ShapeResult* result = new ShapeResult(); |
| + result->m_numCharacters = m_normalizedBufferLength; |
| + result->m_direction = m_textRun.direction(); |
| + if (!shapeHarfBuzzRuns(result)) { |
| + delete result; |
| + return nullptr; |
| + } |
| - return true; |
| + return adoptRef(result); |
| } |
| struct CandidateRun { |
| @@ -766,17 +947,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 |
| @@ -805,16 +975,16 @@ static inline void addToHarfBuzzBufferInternal(hb_buffer_t* buffer, |
| } |
| } |
| -bool HarfBuzzShaper::shapeHarfBuzzRuns() |
| +bool HarfBuzzShaper::shapeHarfBuzzRuns(ShapeResult* result) |
| { |
| HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_destroy); |
| - HarfBuzzRunCache& runCache = harfBuzzRunCache(); |
| const FontDescription& fontDescription = m_font->fontDescription(); |
| const String& localeString = fontDescription.locale(); |
| CString locale = localeString.latin1(); |
| const hb_language_t language = hb_language_from_string(locale.data(), locale.length()); |
| + result->m_runs.resize(m_harfBuzzRuns.size()); |
| for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { |
| unsigned runIndex = m_textRun.rtl() ? m_harfBuzzRuns.size() - i - 1 : i; |
| HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); |
| @@ -829,23 +999,6 @@ 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(); |
| - std::wstring key(src, src + currentRun->numCharacters()); |
| - |
| - CachedShapingResults* cachedResults = runCache.find(key); |
| - if (cachedResults) { |
| - if (isValidCachedResult(m_font, currentRun->direction(), |
| - localeString, cachedResults)) { |
| - currentRun->applyShapeResult(cachedResults->buffer); |
| - setGlyphPositionsForHarfBuzzRun(currentRun, |
| - cachedResults->buffer); |
| - hb_buffer_clear_contents(harfBuzzBuffer.get()); |
| - runCache.moveToBack(cachedResults); |
| - continue; |
| - } |
| - runCache.remove(cachedResults); |
| - } |
| - |
| // Add a space as pre-context to the buffer. This prevents showing dotted-circle |
| // for combining marks at the beginning of runs. |
| static const uint16_t preContext = spaceCharacter; |
| @@ -859,14 +1012,10 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns() |
| face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get()); |
| HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(), hb_font_destroy); |
| - |
| hb_shape(harfBuzzFont.get(), harfBuzzBuffer.get(), m_features.isEmpty() ? 0 : m_features.data(), m_features.size()); |
| - currentRun->applyShapeResult(harfBuzzBuffer.get()); |
| - setGlyphPositionsForHarfBuzzRun(currentRun, harfBuzzBuffer.get()); |
| + shapeResult(result, i, currentRun, harfBuzzBuffer.get()); |
| - runCache.insert(key, new CachedShapingResults(harfBuzzBuffer.get(), m_font, currentRun->direction(), localeString)); |
| - |
| - harfBuzzBuffer.set(hb_buffer_create()); |
| + hb_buffer_reset(harfBuzzBuffer.get()); |
| } |
| // We should have consumed all expansion opportunities. |
| @@ -879,44 +1028,53 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns() |
| return true; |
| } |
| -void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb_buffer_t* harfBuzzBuffer) |
| +void HarfBuzzShaper::shapeResult(ShapeResult* result, unsigned index, |
| + HarfBuzzRun* currentRun, hb_buffer_t* harfBuzzBuffer) |
| { |
| - // Skip runs that only contain control characters. |
| - if (!currentRun->numGlyphs()) |
| + unsigned numGlyphs = hb_buffer_get_length(harfBuzzBuffer); |
| + if (!numGlyphs) { |
| + result->m_runs[index] = nullptr; |
| return; |
| + } |
| + |
| + currentRun->setNumGlyphs(numGlyphs); |
| + ShapeResult::RunInfo* run = new ShapeResult::RunInfo(currentRun->fontData(), |
| + currentRun->direction(), currentRun->script(), currentRun->startIndex(), |
| + numGlyphs, currentRun->numCharacters()); |
| + result->m_runs[index] = run; |
| + result->m_numGlyphs += numGlyphs; |
| const SimpleFontData* currentFontData = currentRun->fontData(); |
| hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); |
| hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzzBuffer, 0); |
| - unsigned numGlyphs = currentRun->numGlyphs(); |
| float totalAdvance = 0; |
| FloatPoint glyphOrigin; |
| float offsetX, offsetY; |
| float* directionOffset = m_font->fontDescription().isVerticalAnyUpright() ? &offsetY : &offsetX; |
| - // HarfBuzz returns the shaping result in visual order. We need not to flip for RTL. |
| + // HarfBuzz returns result in visual order, no need to flip for RTL. |
| for (size_t i = 0; i < numGlyphs; ++i) { |
| bool runEnd = i + 1 == numGlyphs; |
| uint16_t glyph = glyphInfos[i].codepoint; |
| offsetX = harfBuzzPositionToFloat(glyphPositions[i].x_offset); |
| offsetY = -harfBuzzPositionToFloat(glyphPositions[i].y_offset); |
| + |
| // One out of x_advance and y_advance is zero, depending on |
| // 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); |
| bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1].cluster; |
| float spacing = 0; |
| - currentRun->glyphData(i).characterIndex = glyphInfos[i].cluster; |
| + run->m_glyphData[i].characterIndex = glyphInfos[i].cluster; |
| if (isClusterEnd) |
| - spacing += adjustSpacing(currentRun, i, currentCharacterIndex, *directionOffset, totalAdvance); |
| + spacing += adjustSpacing(run, i, currentCharacterIndex, *directionOffset, totalAdvance); |
| if (currentFontData->isZeroWidthSpaceGlyph(glyph)) { |
| - currentRun->setGlyphAndPositions(i, glyph, 0, 0, 0); |
| + run->setGlyphAndPositions(i, glyph, 0, 0, 0); |
| continue; |
| } |
| @@ -928,22 +1086,20 @@ void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb |
| *directionOffset += m_letterSpacing; |
| } |
| - currentRun->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY); |
| - |
| - if (m_glyphBoundingBox) { |
| - FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph); |
| - glyphBounds.move(glyphOrigin.x(), glyphOrigin.y()); |
| - m_glyphBoundingBox->unite(glyphBounds); |
| - glyphOrigin += FloatSize(advance + offsetX, offsetY); |
| - } |
| - |
| + run->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY); |
| totalAdvance += advance; |
| + |
| + FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph); |
| + glyphBounds.move(glyphOrigin.x(), glyphOrigin.y()); |
| + result->m_glyphBoundingBox.unite(glyphBounds); |
| + glyphOrigin += FloatSize(advance + offsetX, offsetY); |
| } |
| - currentRun->setWidth(totalAdvance > 0.0 ? totalAdvance : 0.0); |
| - m_totalWidth += currentRun->width(); |
| + |
| + run->m_width = totalAdvance > 0.0 ? totalAdvance : 0.0; |
| + result->m_width += run->m_width; |
| } |
| -float HarfBuzzShaper::adjustSpacing(HarfBuzzRun* currentRun, size_t glyphIndex, unsigned currentCharacterIndex, float& offset, float& totalAdvance) |
| +float HarfBuzzShaper::adjustSpacing(ShapeResult::RunInfo* run, size_t glyphIndex, unsigned currentCharacterIndex, float& offset, float& totalAdvance) |
| { |
| float spacing = 0; |
| UChar32 character = m_normalizedBuffer[currentCharacterIndex]; |
| @@ -982,7 +1138,7 @@ float HarfBuzzShaper::adjustSpacing(HarfBuzzRun* currentRun, size_t glyphIndex, |
| float expandBefore = nextExpansionPerOpportunity(); |
| if (expandBefore) { |
| if (glyphIndex > 0) { |
| - currentRun->addAdvance(glyphIndex - 1, expandBefore); |
| + run->addAdvance(glyphIndex - 1, expandBefore); |
| totalAdvance += expandBefore; |
| } else { |
| offset += expandBefore; |
| @@ -999,203 +1155,4 @@ float HarfBuzzShaper::adjustSpacing(HarfBuzzRun* currentRun, size_t glyphIndex, |
| return spacing; |
| } |
| -float HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, |
| - HarfBuzzRun* currentRun, float initialAdvance) |
| -{ |
| - unsigned numGlyphs = currentRun->numGlyphs(); |
| - float advanceSoFar = initialAdvance; |
| - if (m_textRun.rtl()) { |
| - for (unsigned i = 0; i < numGlyphs; ++i) { |
| - HarfBuzzRunGlyphData& glyphData = currentRun->glyphData(i); |
| - uint16_t currentCharacterIndex = currentRun->startIndex() + glyphData.characterIndex; |
| - if (currentCharacterIndex >= m_toIndex) { |
| - advanceSoFar += glyphData.advance; |
| - } else if (currentCharacterIndex >= m_fromIndex) { |
| - FloatPoint runStartOffset = HB_DIRECTION_IS_HORIZONTAL(currentRun->direction()) ? |
| - FloatPoint(advanceSoFar, 0) : FloatPoint(0, advanceSoFar); |
| - glyphBuffer->add(glyphData.glyph, currentRun->fontData(), runStartOffset + glyphData.offset); |
| - advanceSoFar += glyphData.advance; |
| - } |
| - } |
| - } else { |
| - for (unsigned i = 0; i < numGlyphs; ++i) { |
| - HarfBuzzRunGlyphData& glyphData = currentRun->glyphData(i); |
| - uint16_t currentCharacterIndex = currentRun->startIndex() + glyphData.characterIndex; |
| - if (currentCharacterIndex < m_fromIndex) { |
| - advanceSoFar += glyphData.advance; |
| - } else if (currentCharacterIndex < m_toIndex) { |
| - FloatPoint runStartOffset = HB_DIRECTION_IS_HORIZONTAL(currentRun->direction()) ? |
| - FloatPoint(advanceSoFar, 0) : FloatPoint(0, advanceSoFar); |
| - glyphBuffer->add(glyphData.glyph, currentRun->fontData(), runStartOffset + glyphData.offset); |
| - advanceSoFar += glyphData.advance; |
| - } |
| - } |
| - } |
| - |
| - return advanceSoFar - initialAdvance; |
| -} |
| - |
| -float HarfBuzzShaper::fillGlyphBufferForTextEmphasis(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun, float initialAdvance) |
| -{ |
| - unsigned numGlyphs = currentRun->numGlyphs(); |
| - unsigned graphemesInCluster = 1; |
| - float clusterAdvance = 0; |
| - uint16_t clusterStart; |
| - |
| - // A "cluster" in this context means a cluster as it is used by HarfBuzz: |
| - // The minimal group of characters and corresponding glyphs, that cannot be broken |
| - // down further from a text shaping point of view. |
| - // A cluster can contain multiple glyphs and grapheme clusters, with mutually |
| - // overlapping boundaries. Below we count grapheme clusters per HarfBuzz clusters, |
| - // then linearly split the sum of corresponding glyph advances by the number of |
| - // grapheme clusters in order to find positions for emphasis mark drawing. |
| - |
| - if (m_textRun.rtl()) |
| - clusterStart = currentRun->startIndex() + currentRun->numCharacters(); |
| - else |
| - clusterStart = currentRun->glyphToCharacterIndex(0); |
| - |
| - float advanceSoFar = initialAdvance; |
| - for (unsigned i = 0; i < numGlyphs; ++i) { |
| - HarfBuzzRunGlyphData& glyphData = currentRun->glyphData(i); |
| - uint16_t currentCharacterIndex = currentRun->startIndex() + glyphData.characterIndex; |
| - bool isRunEnd = (i + 1 == numGlyphs); |
| - bool isClusterEnd = isRunEnd || (currentRun->glyphToCharacterIndex(i + 1) != currentCharacterIndex); |
| - |
| - if ((m_textRun.rtl() && currentCharacterIndex >= m_toIndex) || (!m_textRun.rtl() && currentCharacterIndex < m_fromIndex)) { |
| - advanceSoFar += glyphData.advance; |
| - m_textRun.rtl() ? --clusterStart : ++clusterStart; |
| - continue; |
| - } |
| - |
| - clusterAdvance += glyphData.advance; |
| - |
| - if (isClusterEnd) { |
| - uint16_t clusterEnd; |
| - if (m_textRun.rtl()) |
| - clusterEnd = currentCharacterIndex; |
| - else |
| - clusterEnd = isRunEnd ? currentRun->startIndex() + currentRun->numCharacters() : currentRun->glyphToCharacterIndex(i + 1); |
| - |
| - graphemesInCluster = countGraphemesInCluster(m_normalizedBuffer.get(), m_normalizedBufferLength, clusterStart, clusterEnd); |
| - if (!graphemesInCluster || !clusterAdvance) |
| - continue; |
| - |
| - float glyphAdvanceX = clusterAdvance / graphemesInCluster; |
| - for (unsigned j = 0; j < graphemesInCluster; ++j) { |
| - // Do not put emphasis marks on space, separator, and control characters. |
| - if (Character::canReceiveTextEmphasis(m_textRun[currentCharacterIndex])) |
| - addEmphasisMark(glyphBuffer, advanceSoFar + glyphAdvanceX / 2); |
| - |
| - advanceSoFar += glyphAdvanceX; |
| - } |
| - clusterStart = clusterEnd; |
| - clusterAdvance = 0; |
| - } |
| - } |
| - |
| - return advanceSoFar - initialAdvance; |
| -} |
| - |
| -bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer) |
| -{ |
| - ASSERT(glyphBuffer); |
| - |
| - unsigned numRuns = m_harfBuzzRuns.size(); |
| - float advanceSoFar = 0; |
| - for (unsigned runIndex = 0; runIndex < numRuns; ++runIndex) { |
| - HarfBuzzRun* currentRun = m_harfBuzzRuns[m_textRun.ltr() ? runIndex : numRuns - runIndex - 1].get(); |
| - // Skip runs that only contain control characters. |
| - if (!currentRun->numGlyphs()) |
| - continue; |
| - advanceSoFar += forTextEmphasis() |
| - ? fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun, advanceSoFar) |
| - : fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, advanceSoFar); |
| - } |
| - return glyphBuffer->size(); |
| -} |
| - |
| -int HarfBuzzShaper::offsetForPosition(float targetX) |
| -{ |
| - int charactersSoFar = 0; |
| - float currentX = 0; |
| - |
| - if (m_textRun.rtl()) { |
| - charactersSoFar = m_normalizedBufferLength; |
| - for (int i = m_harfBuzzRuns.size() - 1; i >= 0; --i) { |
| - charactersSoFar -= m_harfBuzzRuns[i]->numCharacters(); |
| - float nextX = currentX + m_harfBuzzRuns[i]->width(); |
| - float offsetForRun = targetX - currentX; |
| - if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width()) { |
| - // The x value in question is within this script run. |
| - const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosition(offsetForRun); |
| - return charactersSoFar + index; |
| - } |
| - currentX = nextX; |
| - } |
| - } else { |
| - for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { |
| - float nextX = currentX + m_harfBuzzRuns[i]->width(); |
| - float offsetForRun = targetX - currentX; |
| - if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width()) { |
| - const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosition(offsetForRun); |
| - return charactersSoFar + index; |
| - } |
| - charactersSoFar += m_harfBuzzRuns[i]->numCharacters(); |
| - currentX = nextX; |
| - } |
| - } |
| - |
| - return charactersSoFar; |
| -} |
| - |
| -FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int from, int to) |
| -{ |
| - float currentX = 0; |
| - float fromX = 0; |
| - float toX = 0; |
| - bool foundFromX = false; |
| - bool foundToX = false; |
| - |
| - if (m_textRun.rtl()) |
| - currentX = m_totalWidth; |
| - for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { |
| - if (m_textRun.rtl()) |
| - currentX -= m_harfBuzzRuns[i]->width(); |
| - int numCharacters = m_harfBuzzRuns[i]->numCharacters(); |
| - if (!foundFromX && from >= 0 && from < numCharacters) { |
| - fromX = m_harfBuzzRuns[i]->xPositionForOffset(from) + currentX; |
| - foundFromX = true; |
| - } else { |
| - from -= numCharacters; |
| - } |
| - |
| - if (!foundToX && to >= 0 && to < numCharacters) { |
| - toX = m_harfBuzzRuns[i]->xPositionForOffset(to) + currentX; |
| - foundToX = true; |
| - } else { |
| - to -= numCharacters; |
| - } |
| - |
| - if (foundFromX && foundToX) |
| - break; |
| - if (!m_textRun.rtl()) |
| - currentX += m_harfBuzzRuns[i]->width(); |
| - } |
| - |
| - // The position in question might be just after the text. |
| - if (!foundFromX) |
| - fromX = 0; |
| - if (!foundToX) |
| - toX = m_textRun.rtl() ? 0 : m_totalWidth; |
| - // None of our HarfBuzzRuns is part of the selection, |
| - // possibly invalid from, to arguments. |
| - if (!foundToX && !foundFromX) |
| - fromX = toX = 0; |
| - |
| - if (fromX < toX) |
| - return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); |
| - return FloatRect(point.x() + toX, point.y(), fromX - toX, height); |
| -} |
| - |
| } // namespace blink |