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> |
drott
2015/06/23 12:45:52
I found this a bit surprising, why did you chose t
eae
2015/06/23 22:25:15
Less code duplication than having two copies of th
|
+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; |
+ 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 |