Avoid copying of font table data am: a8e8948

am: 85759c3

* commit '85759c393cdfec504c216b08297b40b4e098a95d':
  Avoid copying of font table data

Change-Id: Ie1e0be38e1875702414b7ca005c5837bd60b82d8
This commit is contained in:
Raph Levien
2016-04-08 16:57:34 +00:00
committed by android-build-merger
10 changed files with 137 additions and 62 deletions

View File

@@ -94,6 +94,9 @@ struct MinikinRect {
class MinikinFontFreeType;
// Callback for freeing data
typedef void (*MinikinDestroyFunc) (void* data);
class MinikinFont : public MinikinRefCounted {
public:
virtual ~MinikinFont();
@@ -104,8 +107,23 @@ public:
virtual void GetBounds(MinikinRect* bounds, uint32_t glyph_id,
const MinikinPaint &paint) const = 0;
// If buf is NULL, just update size
virtual bool GetTable(uint32_t tag, uint8_t *buf, size_t *size) = 0;
virtual const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy) = 0;
// Override if font can provide access to raw data
virtual const void* GetFontData() const {
return nullptr;
}
// Override if font can provide access to raw data
virtual size_t GetFontSize() const {
return 0;
}
// Override if font can provide access to raw data.
// Returns index within OpenType collection
virtual int GetFontIndex() const {
return 0;
}
virtual int32_t GetUniqueId() const = 0;

View File

@@ -48,8 +48,9 @@ public:
void GetBounds(MinikinRect* bounds, uint32_t glyph_id,
const MinikinPaint& paint) const;
// If buf is NULL, just update size
bool GetTable(uint32_t tag, uint8_t *buf, size_t *size);
const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy);
// TODO: provide access to raw data, as an optimization.
int32_t GetUniqueId() const;

View File

@@ -77,15 +77,11 @@ FontFamily::~FontFamily() {
bool FontFamily::addFont(MinikinFont* typeface) {
AutoMutex _l(gMinikinLock);
const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2');
size_t os2Size = 0;
bool ok = typeface->GetTable(os2Tag, NULL, &os2Size);
if (!ok) return false;
UniquePtr<uint8_t[]> os2Data(new uint8_t[os2Size]);
ok = typeface->GetTable(os2Tag, os2Data.get(), &os2Size);
if (!ok) return false;
HbBlob os2Table(getFontTable(typeface, os2Tag));
if (os2Table.get() == nullptr) return false;
int weight;
bool italic;
if (analyzeStyle(os2Data.get(), os2Size, &weight, &italic)) {
if (analyzeStyle(os2Table.get(), os2Table.size(), &weight, &italic)) {
//ALOGD("analyzed weight = %d, italic = %s", weight, italic ? "true" : "false");
FontStyle style(weight, italic);
addFontLocked(typeface, style);
@@ -165,20 +161,15 @@ const SparseBitSet* FontFamily::getCoverage() {
const FontStyle defaultStyle;
MinikinFont* typeface = getClosestMatch(defaultStyle).font;
const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
size_t cmapSize = 0;
if (!typeface->GetTable(cmapTag, NULL, &cmapSize)) {
HbBlob cmapTable(getFontTable(typeface, cmapTag));
if (cmapTable.get() == nullptr) {
ALOGE("Could not get cmap table size!\n");
// Note: This means we will retry on the next call to getCoverage, as we can't store
// the failure. This is fine, as we assume this doesn't really happen in practice.
return nullptr;
}
UniquePtr<uint8_t[]> cmapData(new uint8_t[cmapSize]);
if (!typeface->GetTable(cmapTag, cmapData.get(), &cmapSize)) {
ALOGE("Unexpected failure to read cmap table!\n");
return nullptr;
}
// TODO: Error check?
CmapCoverage::getCoverage(mCoverage, cmapData.get(), cmapSize, &mHasVSTable);
CmapCoverage::getCoverage(mCoverage, cmapTable.get(), cmapTable.size(), &mHasVSTable);
#ifdef VERBOSE_DEBUG
ALOGD("font coverage length=%d, first ch=%x\n", mCoverage.length(),
mCoverage.nextSetBit(0));
@@ -198,7 +189,9 @@ bool FontFamily::hasVariationSelector(uint32_t codepoint, uint32_t variationSele
MinikinFont* minikinFont = getClosestMatch(defaultStyle).font;
hb_font_t* font = getHbFontLocked(minikinFont);
uint32_t unusedGlyph;
return hb_font_get_glyph(font, codepoint, variationSelector, &unusedGlyph);
bool result = hb_font_get_glyph(font, codepoint, variationSelector, &unusedGlyph);
hb_font_destroy(font);
return result;
}
bool FontFamily::hasVSTable() const {

View File

@@ -30,26 +30,18 @@ namespace android {
static hb_blob_t* referenceTable(hb_face_t* /* face */, hb_tag_t tag, void* userData) {
MinikinFont* font = reinterpret_cast<MinikinFont*>(userData);
size_t length = 0;
bool ok = font->GetTable(tag, NULL, &length);
if (!ok) {
return 0;
MinikinDestroyFunc destroy = 0;
size_t size = 0;
const void* buffer = font->GetTable(tag, &size, &destroy);
if (buffer == nullptr) {
return nullptr;
}
char* buffer = reinterpret_cast<char*>(malloc(length));
if (!buffer) {
return 0;
}
ok = font->GetTable(tag, reinterpret_cast<uint8_t*>(buffer), &length);
#ifdef VERBOSE_DEBUG
ALOGD("referenceTable %c%c%c%c length=%zd %d",
(tag >>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff, length, ok);
ALOGD("referenceTable %c%c%c%c length=%zd",
(tag >>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff, size);
#endif
if (!ok) {
free(buffer);
return 0;
}
return hb_blob_create(const_cast<char*>(buffer), length,
HB_MEMORY_MODE_WRITABLE, buffer, free);
return hb_blob_create(reinterpret_cast<const char*>(buffer), size,
HB_MEMORY_MODE_READONLY, const_cast<void*>(buffer), destroy);
}
class HbFontCache : private OnEntryRemoved<int32_t, hb_font_t*> {
@@ -105,24 +97,37 @@ void purgeHbFont(const MinikinFont* minikinFont) {
getFontCacheLocked()->remove(fontId);
}
// Returns a new reference to a hb_font_t object, caller is
// responsible for calling hb_font_destroy() on it.
hb_font_t* getHbFontLocked(MinikinFont* minikinFont) {
assertMinikinLocked();
// TODO: get rid of nullFaceFont
static hb_font_t* nullFaceFont = nullptr;
if (minikinFont == nullptr) {
if (nullFaceFont == nullptr) {
nullFaceFont = hb_font_create(nullptr);
}
return nullFaceFont;
return hb_font_reference(nullFaceFont);
}
HbFontCache* fontCache = getFontCacheLocked();
const int32_t fontId = minikinFont->GetUniqueId();
hb_font_t* font = fontCache->get(fontId);
if (font != nullptr) {
return font;
return hb_font_reference(font);
}
hb_face_t* face = hb_face_create_for_tables(referenceTable, minikinFont, nullptr);
hb_face_t* face;
const void* buf = minikinFont->GetFontData();
if (buf == nullptr) {
face = hb_face_create_for_tables(referenceTable, minikinFont, nullptr);
} else {
size_t size = minikinFont->GetFontSize();
hb_blob_t* blob = hb_blob_create(reinterpret_cast<const char*>(buf), size,
HB_MEMORY_MODE_READONLY, nullptr, nullptr);
face = hb_face_create(blob, minikinFont->GetFontIndex());
hb_blob_destroy(blob);
}
hb_font_t* parent_font = hb_font_create(face);
hb_ot_font_set_funcs(parent_font);
@@ -133,7 +138,7 @@ hb_font_t* getHbFontLocked(MinikinFont* minikinFont) {
hb_font_destroy(parent_font);
hb_face_destroy(face);
fontCache->put(fontId, font);
return font;
return hb_font_reference(font);
}
} // namespace android

View File

@@ -99,6 +99,7 @@ struct LayoutContext {
void clearHbFonts() {
for (size_t i = 0; i < hbFonts.size(); i++) {
hb_font_set_funcs(hbFonts[i], nullptr, nullptr, nullptr);
hb_font_destroy(hbFonts[i]);
}
hbFonts.clear();
}

View File

@@ -41,8 +41,8 @@ MinikinFontFreeType::~MinikinFontFreeType() {
float MinikinFontFreeType::GetHorizontalAdvance(uint32_t glyph_id,
const MinikinPaint &paint) const {
FT_Set_Pixel_Sizes(mTypeface, 0, paint.size);
FT_UInt32 flags = FT_LOAD_DEFAULT; // TODO: respect hinting settings
FT_Fixed advance;
FT_UInt32 flags = FT_LOAD_DEFAULT; // TODO: respect hinting settings
FT_Fixed advance;
FT_Get_Advance(mTypeface, glyph_id, flags, &advance);
return advance * (1.0 / 65536);
}
@@ -52,18 +52,28 @@ void MinikinFontFreeType::GetBounds(MinikinRect* /* bounds */, uint32_t /* glyph
// TODO: NYI
}
bool MinikinFontFreeType::GetTable(uint32_t tag, uint8_t *buf, size_t *size) {
FT_ULong ftsize = *size;
FT_Error error = FT_Load_Sfnt_Table(mTypeface, tag, 0, buf, &ftsize);
if (error != 0) {
return false;
}
*size = ftsize;
return true;
const void* MinikinFontFreeType::GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy) {
FT_ULong ftsize = 0;
FT_Error error = FT_Load_Sfnt_Table(mTypeface, tag, 0, nullptr, &ftsize);
if (error != 0) {
return nullptr;
}
FT_Byte* buf = reinterpret_cast<FT_Byte*>(malloc(ftsize));
if (buf == nullptr) {
return nullptr;
}
error = FT_Load_Sfnt_Table(mTypeface, tag, 0, buf, &ftsize);
if (error != 0) {
free(buf);
return nullptr;
}
*destroy = free;
*size = ftsize;
return buf;
}
int32_t MinikinFontFreeType::GetUniqueId() const {
return mUniqueId;
return mUniqueId;
}
bool MinikinFontFreeType::Render(uint32_t glyph_id, const MinikinPaint& /* paint */,

View File

@@ -17,6 +17,7 @@
// Definitions internal to Minikin
#include "MinikinInternal.h"
#include "HbFontCache.h"
#include <cutils/log.h>
@@ -72,4 +73,13 @@ bool isEmojiBase(uint32_t c) {
}
}
hb_blob_t* getFontTable(MinikinFont* minikinFont, uint32_t tag) {
assertMinikinLocked();
hb_font_t* font = getHbFontLocked(minikinFont);
hb_face_t* face = hb_font_get_face(font);
hb_blob_t* blob = hb_face_reference_table(face, tag);
hb_font_destroy(font);
return blob;
}
}

View File

@@ -19,8 +19,12 @@
#ifndef MINIKIN_INTERNAL_H
#define MINIKIN_INTERNAL_H
#include <hb.h>
#include <utils/Mutex.h>
#include <minikin/MinikinFont.h>
namespace android {
// All external Minikin interfaces are designed to be thread-safe.
@@ -38,6 +42,35 @@ bool isEmojiBase(uint32_t c);
// Returns true if c is emoji modifier.
bool isEmojiModifier(uint32_t c);
hb_blob_t* getFontTable(MinikinFont* minikinFont, uint32_t tag);
// An RAII wrapper for hb_blob_t
class HbBlob {
public:
// Takes ownership of hb_blob_t object, caller is no longer
// responsible for calling hb_blob_destroy().
HbBlob(hb_blob_t* blob) : mBlob(blob) {
}
~HbBlob() {
hb_blob_destroy(mBlob);
}
const uint8_t* get() const {
const char* data = hb_blob_get_data(mBlob, nullptr);
return reinterpret_cast<const uint8_t*>(data);
}
size_t size() const {
unsigned int length = 0;
hb_blob_get_data(mBlob, &length);
return (size_t)length;
}
private:
hb_blob_t* mBlob;
};
}
#endif // MINIKIN_INTERNAL_H

View File

@@ -40,16 +40,20 @@ void MinikinFontForTest::GetBounds(android::MinikinRect* /* bounds */, uint32_t
LOG_ALWAYS_FATAL("MinikinFontForTest::GetBounds is not yet implemented");
}
bool MinikinFontForTest::GetTable(uint32_t tag, uint8_t *buf, size_t *size) {
if (buf == NULL) {
const size_t tableSize = mTypeface->getTableSize(tag);
*size = tableSize;
return tableSize != 0;
} else {
const size_t actualSize = mTypeface->getTableData(tag, 0, *size, buf);
*size = actualSize;
return actualSize != 0;
const void* MinikinFontForTest::GetTable(uint32_t tag, size_t* size,
android::MinikinDestroyFunc* destroy) {
const size_t tableSize = mTypeface->getTableSize(tag);
*size = tableSize;
if (tableSize == 0) {
return nullptr;
}
void* buf = malloc(tableSize);
if (buf == nullptr) {
return nullptr;
}
mTypeface->getTableData(tag, 0, tableSize, buf);
*destroy = free;
return buf;
}
int32_t MinikinFontForTest::GetUniqueId() const {

View File

@@ -30,7 +30,7 @@ public:
float GetHorizontalAdvance(uint32_t glyph_id, const android::MinikinPaint &paint) const;
void GetBounds(android::MinikinRect* bounds, uint32_t glyph_id,
const android::MinikinPaint& paint) const;
bool GetTable(uint32_t tag, uint8_t *buf, size_t *size);
const void* GetTable(uint32_t tag, size_t* size, android::MinikinDestroyFunc* destroy);
int32_t GetUniqueId() const;
const std::string& fontPath() const { return mFontPath; }