Commit d22f961f authored by bashi@chromium.org's avatar bashi@chromium.org
Browse files

[Chromium] Introduce caches for HarfBuzzShaper

https://bugs.webkit.org/show_bug.cgi?id=97993

Reviewed by Tony Chang.

- Implement canRenderCombiningCharacterSequence() for ports which use
HarfBuzzShaper. This function caches the result and will improve the
performance of HarfBuzzShaper::collectHarfBuzzRuns.
- Add a HashMap to HarfBuzzNGFace. It is used as a cache that holds
glyph indexes of codepoints. It reduces the number of
SkPaint::textToGlyphs() calls.

This patch makes the intl2 page cycler 4.4% faster on my machine.

No new tests. No changes in behavior.

* platform/graphics/SimpleFontData.h:
(SimpleFontData): Enabled canRenderCombiningCharacterSequence() if USE(HARFBUZZ_NG) is enabled.
* platform/graphics/freetype/SimpleFontDataFreeType.cpp:
(WebCore):
(WebCore::SimpleFontData::canRenderCombiningCharacterSequence): Added.
* platform/graphics/harfbuzz/ng/HarfBuzzNGFace.cpp:
(WebCore):
(FaceCacheEntry): Added.
(WebCore::HarfBuzzNGFace::HarfBuzzNGFace):
Lookup the cache entry in harfBuzzFaceCache. Create the entry if there is no entry in the cache.
Increment the ref count of the entry and set cache entry values to member variables.
(WebCore::HarfBuzzNGFace::~HarfBuzzNGFace):
Decrement the ref count of the cache entry. Remove the entry if no one refers the cache.
* platform/graphics/harfbuzz/ng/HarfBuzzNGFace.h:
(HarfBuzzNGFace):
* platform/graphics/harfbuzz/ng/HarfBuzzNGFaceSkia.cpp:
(HarfBuzzFontData): Added. Used as |userData| of harfbuzz callback functions.
(WebCore):
(WebCore::SkiaGetGlyphWidthAndExtents):
(WebCore::harfbuzzGetGlyph):
Look up the glyphChache first. If the cache entry doesn't exist, call
SkPaint::textToGlyphs() to get glyph index and store it to the cache.
(WebCore::harfbuzzGetGlyphHorizontalAdvance):
(WebCore::harfbuzzGetGlyphExtents):
(WebCore::destroyHarfBuzzFontData): Added.
(WebCore::HarfBuzzNGFace::createFont):
Create HarfBuzzFontData and pass it to harfbuzz.
* platform/graphics/harfbuzz/ng/HarfBuzzShaper.cpp:
(WebCore::HarfBuzzShaper::HarfBuzzRun::applyShapeResult):
Don't initialize m_glyphToCharacterIndexes here.
(WebCore::HarfBuzzShaper::collectHarfBuzzRuns):
Use SimpleFontData::canRenderCombiningCharacterSequence() instead of
fontDataForCombiningCharacterSequence().
(WebCore::HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun):
Set glyphToCharacterIndexes of the current HarfBuzzRun.
* platform/graphics/skia/SimpleFontDataSkia.cpp:
(WebCore):
(WebCore::SimpleFontData::canRenderCombiningCharacterSequence): Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@130231 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 3f0e8435
2012-10-02 Kenichi Ishibashi <bashi@chromium.org>
[Chromium] Introduce caches for HarfBuzzShaper
https://bugs.webkit.org/show_bug.cgi?id=97993
Reviewed by Tony Chang.
- Implement canRenderCombiningCharacterSequence() for ports which use
HarfBuzzShaper. This function caches the result and will improve the
performance of HarfBuzzShaper::collectHarfBuzzRuns.
- Add a HashMap to HarfBuzzNGFace. It is used as a cache that holds
glyph indexes of codepoints. It reduces the number of
SkPaint::textToGlyphs() calls.
This patch makes the intl2 page cycler 4.4% faster on my machine.
No new tests. No changes in behavior.
* platform/graphics/SimpleFontData.h:
(SimpleFontData): Enabled canRenderCombiningCharacterSequence() if USE(HARFBUZZ_NG) is enabled.
* platform/graphics/freetype/SimpleFontDataFreeType.cpp:
(WebCore):
(WebCore::SimpleFontData::canRenderCombiningCharacterSequence): Added.
* platform/graphics/harfbuzz/ng/HarfBuzzNGFace.cpp:
(WebCore):
(FaceCacheEntry): Added.
(WebCore::HarfBuzzNGFace::HarfBuzzNGFace):
Lookup the cache entry in harfBuzzFaceCache. Create the entry if there is no entry in the cache.
Increment the ref count of the entry and set cache entry values to member variables.
(WebCore::HarfBuzzNGFace::~HarfBuzzNGFace):
Decrement the ref count of the cache entry. Remove the entry if no one refers the cache.
* platform/graphics/harfbuzz/ng/HarfBuzzNGFace.h:
(HarfBuzzNGFace):
* platform/graphics/harfbuzz/ng/HarfBuzzNGFaceSkia.cpp:
(HarfBuzzFontData): Added. Used as |userData| of harfbuzz callback functions.
(WebCore):
(WebCore::SkiaGetGlyphWidthAndExtents):
(WebCore::harfbuzzGetGlyph):
Look up the glyphChache first. If the cache entry doesn't exist, call
SkPaint::textToGlyphs() to get glyph index and store it to the cache.
(WebCore::harfbuzzGetGlyphHorizontalAdvance):
(WebCore::harfbuzzGetGlyphExtents):
(WebCore::destroyHarfBuzzFontData): Added.
(WebCore::HarfBuzzNGFace::createFont):
Create HarfBuzzFontData and pass it to harfbuzz.
* platform/graphics/harfbuzz/ng/HarfBuzzShaper.cpp:
(WebCore::HarfBuzzShaper::HarfBuzzRun::applyShapeResult):
Don't initialize m_glyphToCharacterIndexes here.
(WebCore::HarfBuzzShaper::collectHarfBuzzRuns):
Use SimpleFontData::canRenderCombiningCharacterSequence() instead of
fontDataForCombiningCharacterSequence().
(WebCore::HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun):
Set glyphToCharacterIndexes of the current HarfBuzzRun.
* platform/graphics/skia/SimpleFontDataSkia.cpp:
(WebCore):
(WebCore::SimpleFontData::canRenderCombiningCharacterSequence): Added.
2012-10-02 Beth Dakin <bdakin@apple.com>
 
https://bugs.webkit.org/show_bug.cgi?id=98182
......@@ -184,6 +184,9 @@ public:
#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) || (PLATFORM(WX) && OS(DARWIN))
CFDictionaryRef getCFStringAttributes(TypesettingFeatures, FontOrientation) const;
#endif
#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) || (PLATFORM(WX) && OS(DARWIN)) || USE(HARFBUZZ_NG)
bool canRenderCombiningCharacterSequence(const UChar*, size_t) const;
#endif
......@@ -288,6 +291,9 @@ private:
#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) || (PLATFORM(WX) && OS(DARWIN))
mutable HashMap<unsigned, RetainPtr<CFDictionaryRef> > m_CFStringAttributes;
#endif
#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) || (PLATFORM(WX) && OS(DARWIN)) || USE(HARFBUZZ_NG)
mutable OwnPtr<HashMap<String, bool> > m_combiningCharacterSequenceSupport;
#endif
......
......@@ -171,4 +171,11 @@ float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
return w;
}
#if USE(HARFBUZZ_NG)
bool SimpleFontData::canRenderCombiningCharacterSequence(const UChar*, size_t) const
{
return false;
}
#endif
}
......@@ -33,7 +33,6 @@
#include "FontPlatformData.h"
#include "hb.h"
#include <wtf/HashMap.h>
namespace WebCore {
......@@ -41,8 +40,32 @@ namespace WebCore {
// WebKit's font objects, we also need additional caching layer for HarfBuzz
// to reduce the memory consumption because hb_face_t should be associated with
// underling font data (e.g. CTFontRef, FTFace).
typedef pair<hb_face_t*, unsigned> FaceCacheEntry;
typedef HashMap<uint64_t, FaceCacheEntry, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t> > HarfBuzzNGFaceCache;
class FaceCacheEntry : public RefCounted<FaceCacheEntry> {
public:
static PassRefPtr<FaceCacheEntry> create(hb_face_t* face)
{
ASSERT(face);
return adoptRef(new FaceCacheEntry(face));
}
~FaceCacheEntry()
{
hb_face_destroy(m_face);
}
hb_face_t* face() { return m_face; }
HashMap<uint32_t, uint16_t>* glyphCache() { return &m_glyphCache; }
private:
explicit FaceCacheEntry(hb_face_t* face)
: m_face(face)
{ }
hb_face_t* m_face;
HashMap<uint32_t, uint16_t> m_glyphCache;
};
typedef HashMap<uint64_t, RefPtr<FaceCacheEntry>, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t> > HarfBuzzNGFaceCache;
static HarfBuzzNGFaceCache* harfbuzzFaceCache()
{
......@@ -54,27 +77,22 @@ HarfBuzzNGFace::HarfBuzzNGFace(FontPlatformData* platformData, uint64_t uniqueID
: m_platformData(platformData)
, m_uniqueID(uniqueID)
{
HarfBuzzNGFaceCache::iterator result = harfbuzzFaceCache()->find(m_uniqueID);
if (result == harfbuzzFaceCache()->end()) {
m_face = createFace();
ASSERT(m_face);
harfbuzzFaceCache()->set(m_uniqueID, FaceCacheEntry(m_face, 1));
} else {
++(result.get()->second.second);
m_face = result.get()->second.first;
}
HarfBuzzNGFaceCache::AddResult result = harfbuzzFaceCache()->add(m_uniqueID, 0);
if (result.isNewEntry)
result.iterator->second = FaceCacheEntry::create(createFace());
result.iterator->second->ref();
m_face = result.iterator->second->face();
m_glyphCacheForFaceCacheEntry = result.iterator->second->glyphCache();
}
HarfBuzzNGFace::~HarfBuzzNGFace()
{
HarfBuzzNGFaceCache::iterator result = harfbuzzFaceCache()->find(m_uniqueID);
ASSERT(result != harfbuzzFaceCache()->end());
ASSERT(result.get()->second.second > 0);
--(result.get()->second.second);
if (!(result.get()->second.second)) {
hb_face_destroy(result.get()->second.first);
ASSERT(result.get()->second->refCount() > 1);
result.get()->second->deref();
if (result.get()->second->refCount() == 1)
harfbuzzFaceCache()->remove(m_uniqueID);
}
}
} // namespace WebCore
......@@ -33,6 +33,7 @@
#include <hb.h>
#include <wtf/HashMap.h>
#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
......@@ -59,6 +60,7 @@ private:
FontPlatformData* m_platformData;
uint64_t m_uniqueID;
hb_face_t* m_face;
WTF::HashMap<uint32_t, uint16_t>* m_glyphCacheForFaceCacheEntry;
};
}
......
......@@ -43,12 +43,21 @@
#include "SkUtils.h"
#include "hb.h"
#include <wtf/HashMap.h>
namespace WebCore {
// Our implementation of the callbacks which Harfbuzz requires by using Skia
// calls. See the Harfbuzz source for references about what these callbacks do.
struct HarfBuzzFontData {
HarfBuzzFontData(WTF::HashMap<uint32_t, uint16_t>* glyphCacheForFaceCacheEntry)
: m_glyphCacheForFaceCacheEntry(glyphCacheForFaceCacheEntry)
{ }
SkPaint m_paint;
WTF::HashMap<uint32_t, uint16_t>* m_glyphCacheForFaceCacheEntry;
};
static hb_position_t SkiaScalarToHarfbuzzPosition(SkScalar value)
{
return SkScalarToFixed(value);
......@@ -56,8 +65,7 @@ static hb_position_t SkiaScalarToHarfbuzzPosition(SkScalar value)
static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents)
{
if (codepoint > 0xFFFF)
return;
ASSERT(codepoint <= 0xFFFF);
paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
SkScalar skWidth;
......@@ -78,23 +86,27 @@ static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint
static hb_bool_t harfbuzzGetGlyph(hb_font_t* hbFont, void* fontData, hb_codepoint_t unicode, hb_codepoint_t variationSelector, hb_codepoint_t* glyph, void* userData)
{
SkPaint* paint = reinterpret_cast<SkPaint*>(fontData);
paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
uint16_t text[4];
size_t length = SkUTF16_FromUnichar(unicode, text);
uint16_t glyph16;
paint->textToGlyphs(text, length, &glyph16);
*glyph = glyph16;
HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
WTF::HashMap<uint32_t, uint16_t>::AddResult result = hbFontData->m_glyphCacheForFaceCacheEntry->add(unicode, 0);
if (result.isNewEntry) {
SkPaint* paint = &hbFontData->m_paint;
paint->setTextEncoding(SkPaint::kUTF32_TextEncoding);
uint16_t glyph16;
paint->textToGlyphs(&unicode, sizeof(hb_codepoint_t), &glyph16);
result.iterator->second = glyph16;
*glyph = glyph16;
}
*glyph = result.iterator->second;
return !!*glyph;
}
static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, void* userData)
{
SkPaint* paint = reinterpret_cast<SkPaint*>(fontData);
HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
hb_position_t advance = 0;
SkiaGetGlyphWidthAndExtents(paint, glyph, &advance, 0);
SkiaGetGlyphWidthAndExtents(&hbFontData->m_paint, glyph, &advance, 0);
return advance;
}
......@@ -107,9 +119,9 @@ static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* hbFont, void* fontD
static hb_bool_t harfbuzzGetGlyphExtents(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_glyph_extents_t* extents, void* userData)
{
SkPaint* paint = reinterpret_cast<SkPaint*>(fontData);
HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
SkiaGetGlyphWidthAndExtents(paint, glyph, 0, extents);
SkiaGetGlyphWidthAndExtents(&hbFontData->m_paint, glyph, 0, extents);
return true;
}
......@@ -151,10 +163,10 @@ static hb_blob_t* harfbuzzSkiaGetTable(hb_face_t* face, hb_tag_t tag, void* user
HB_MEMORY_MODE_WRITABLE, buffer, fastFree);
}
static void destroyPaint(void* userData)
static void destroyHarfBuzzFontData(void* userData)
{
SkPaint* paint = reinterpret_cast<SkPaint*>(userData);
delete paint;
HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(userData);
delete hbFontData;
}
hb_face_t* HarfBuzzNGFace::createFace()
......@@ -166,10 +178,10 @@ hb_face_t* HarfBuzzNGFace::createFace()
hb_font_t* HarfBuzzNGFace::createFont()
{
HarfBuzzFontData* hbFontData = new HarfBuzzFontData(m_glyphCacheForFaceCacheEntry);
m_platformData->setupPaint(&hbFontData->m_paint);
hb_font_t* font = hb_font_create(m_face);
SkPaint* paint = new SkPaint;
m_platformData->setupPaint(paint);
hb_font_set_funcs(font, harfbuzzSkiaGetFontFuncs(), paint, destroyPaint);
hb_font_set_funcs(font, harfbuzzSkiaGetFontFuncs(), hbFontData, destroyHarfBuzzFontData);
float size = m_platformData->size();
int scale = SkiaScalarToHarfbuzzPosition(size);
hb_font_set_scale(font, scale, scale);
......
......@@ -87,10 +87,6 @@ void HarfBuzzShaper::HarfBuzzRun::applyShapeResult(hb_buffer_t* harfbuzzBuffer)
m_advances.resize(m_numGlyphs);
m_glyphToCharacterIndexes.resize(m_numGlyphs);
m_offsets.resize(m_numGlyphs);
hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(harfbuzzBuffer, 0);
for (unsigned i = 0; i < m_numGlyphs; ++i)
m_glyphToCharacterIndexes[i] = infos[i].cluster;
}
void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, float offsetX, float offsetY)
......@@ -240,20 +236,6 @@ FloatPoint HarfBuzzShaper::adjustStartPoint(const FloatPoint& point)
return point + m_startOffset;
}
static const SimpleFontData* fontDataForCombiningCharacterSequence(const Font* font, const UChar* characters, size_t length)
{
UErrorCode error = U_ZERO_ERROR;
Vector<UChar, 4> normalizedCharacters(length);
int32_t normalizedLength = unorm_normalize(characters, length, UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], length, &error);
// Should fallback if we have an error or no composition occurred.
if (U_FAILURE(error) || (static_cast<size_t>(normalizedLength) == length))
return 0;
UChar32 normalizedCharacter;
size_t index = 0;
U16_NEXT(&normalizedCharacters[0], index, static_cast<size_t>(normalizedLength), normalizedCharacter);
return font->glyphDataForCharacter(normalizedCharacter, false).fontData;
}
bool HarfBuzzShaper::collectHarfBuzzRuns()
{
const UChar* normalizedBufferEnd = m_normalizedBuffer.get() + m_normalizedBufferLength;
......@@ -278,6 +260,7 @@ bool HarfBuzzShaper::collectHarfBuzzRuns()
for (iterator.advance(clusterLength); iterator.consume(character, clusterLength); iterator.advance(clusterLength)) {
if (Font::treatAsZeroWidthSpace(character))
continue;
if (U_GET_GC_MASK(character) & U_GC_M_MASK) {
int markLength = clusterLength;
const UChar* markCharactersEnd = iterator.characters() + clusterLength;
......@@ -290,11 +273,12 @@ bool HarfBuzzShaper::collectHarfBuzzRuns()
markLength += nextCharacterLength;
markCharactersEnd += nextCharacterLength;
}
nextFontData = fontDataForCombiningCharacterSequence(m_font, currentCharacterPosition, markCharactersEnd - currentCharacterPosition);
if (nextFontData)
if (currentFontData->canRenderCombiningCharacterSequence(currentCharacterPosition, markCharactersEnd - currentCharacterPosition)) {
clusterLength = markLength;
else
nextFontData = m_font->glyphDataForCharacter(character, false).fontData;
continue;
}
nextFontData = m_font->glyphDataForCharacter(character, false).fontData;
} else
nextFontData = m_font->glyphDataForCharacter(character, false).fontData;
......@@ -305,6 +289,7 @@ bool HarfBuzzShaper::collectHarfBuzzRuns()
break;
if (nextScript == USCRIPT_INHERITED)
nextScript = currentScript;
currentCharacterPosition = iterator.characters();
}
unsigned numCharactersOfCurrentRun = iterator.currentCharacter() - startIndexOfCurrentRun;
m_harfbuzzRuns.append(HarfBuzzRun::create(currentFontData, startIndexOfCurrentRun, numCharactersOfCurrentRun, m_run.direction()));
......@@ -361,6 +346,7 @@ void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb
hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfbuzzBuffer, 0);
unsigned numGlyphs = currentRun->numGlyphs();
uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes();
float totalAdvance = 0;
// HarfBuzz returns the shaping result in visual order. We need not to flip for RTL.
......@@ -374,6 +360,9 @@ void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb
unsigned currentCharacterIndex = currentRun->startIndex() + glyphInfos[i].cluster;
bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1].cluster;
float spacing = 0;
glyphToCharacterIndexes[i] = glyphInfos[i].cluster;
if (isClusterEnd && !Font::treatAsZeroWidthSpace(m_normalizedBuffer[currentCharacterIndex]))
spacing += m_letterSpacing;
......
......@@ -42,6 +42,8 @@
#include "SkTypeface.h"
#include "SkTypes.h"
#include "VDMXParser.h"
#include <unicode/normlzr.h>
#include <wtf/unicode/Unicode.h>
namespace WebCore {
......@@ -250,4 +252,32 @@ float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
return SkScalarToFloat(width);
}
#if USE(HARFBUZZ_NG)
bool SimpleFontData::canRenderCombiningCharacterSequence(const UChar* characters, size_t length) const
{
if (!m_combiningCharacterSequenceSupport)
m_combiningCharacterSequenceSupport = adoptPtr(new HashMap<String, bool>);
WTF::HashMap<String, bool>::AddResult addResult = m_combiningCharacterSequenceSupport->add(String(characters, length), false);
if (!addResult.isNewEntry)
return addResult.iterator->second;
UErrorCode error = U_ZERO_ERROR;
Vector<UChar, 4> normalizedCharacters(length);
int32_t normalizedLength = unorm_normalize(characters, length, UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], length, &error);
// Can't render if we have an error or no composition occurred.
if (U_FAILURE(error) || (static_cast<size_t>(normalizedLength) == length))
return false;
SkPaint paint;
m_platformData.setupPaint(&paint);
paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
if (paint.textToGlyphs(&normalizedCharacters[0], normalizedLength * 2, 0)) {
addResult.iterator->second = true;
return true;
}
return false;
}
#endif
} // namespace WebCore
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment