Clean up FreeType library ref counting.

The existing reference counting and code for LCD is difficult to
understand. This cleans it up a bit in preperation for the typefaces
holding the references and faces themselves which will clean this up
quite a bit more.

Review URL: https://codereview.chromium.org/742483004
This commit is contained in:
bungeman 2014-12-01 14:01:32 -08:00 committed by Commit bot
parent f786901ff0
commit 9dc24686e7

View File

@ -1,4 +1,3 @@
/* /*
* Copyright 2006 The Android Open Source Project * Copyright 2006 The Android Open Source Project
* *
@ -12,7 +11,6 @@
#include "SkColorPriv.h" #include "SkColorPriv.h"
#include "SkDescriptor.h" #include "SkDescriptor.h"
#include "SkFDot6.h" #include "SkFDot6.h"
#include "SkFloatingPoint.h"
#include "SkFontHost.h" #include "SkFontHost.h"
#include "SkFontHost_FreeType_common.h" #include "SkFontHost_FreeType_common.h"
#include "SkGlyph.h" #include "SkGlyph.h"
@ -20,12 +18,12 @@
#include "SkMaskGamma.h" #include "SkMaskGamma.h"
#include "SkMatrix22.h" #include "SkMatrix22.h"
#include "SkOTUtils.h" #include "SkOTUtils.h"
#include "SkOnce.h"
#include "SkScalerContext.h" #include "SkScalerContext.h"
#include "SkStream.h" #include "SkStream.h"
#include "SkString.h" #include "SkString.h"
#include "SkTemplates.h" #include "SkTemplates.h"
#include "SkThread.h" #include "SkThread.h"
#include "SkTypes.h"
#if defined(SK_CAN_USE_DLOPEN) #if defined(SK_CAN_USE_DLOPEN)
#include <dlfcn.h> #include <dlfcn.h>
@ -35,8 +33,10 @@
#include FT_BITMAP_H #include FT_BITMAP_H
#include FT_FREETYPE_H #include FT_FREETYPE_H
#include FT_LCD_FILTER_H #include FT_LCD_FILTER_H
#include FT_MODULE_H
#include FT_OUTLINE_H #include FT_OUTLINE_H
#include FT_SIZES_H #include FT_SIZES_H
#include FT_SYSTEM_H
#include FT_TRUETYPE_TABLES_H #include FT_TRUETYPE_TABLES_H
#include FT_TYPE1_TABLES_H #include FT_TYPE1_TABLES_H
#include FT_XFREE86_H #include FT_XFREE86_H
@ -65,97 +65,121 @@
using namespace skia_advanced_typeface_metrics_utils; using namespace skia_advanced_typeface_metrics_utils;
static bool isLCD(const SkScalerContext::Rec& rec) { static bool isLCD(const SkScalerContext::Rec& rec) {
switch (rec.fMaskFormat) { return SkMask::kLCD16_Format == rec.fMaskFormat;
case SkMask::kLCD16_Format:
return true;
default:
return false;
}
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
extern "C" {
static void* sk_ft_alloc(FT_Memory, long size) {
return sk_malloc_throw(size);
}
static void sk_ft_free(FT_Memory, void* block) {
sk_free(block);
}
static void* sk_ft_realloc(FT_Memory, long cur_size, long new_size, void* block) {
return sk_realloc_throw(block, new_size);
}
};
FT_MemoryRec_ gFTMemory = { NULL, sk_ft_alloc, sk_ft_free, sk_ft_realloc };
class FreeTypeLibrary : SkNoncopyable {
public:
FreeTypeLibrary() : fLibrary(NULL), fIsLCDSupported(false), fLCDExtra(0) {
if (FT_New_Library(&gFTMemory, &fLibrary)) {
return;
}
FT_Add_Default_Modules(fLibrary);
// Setup LCD filtering. This reduces color fringes for LCD smoothed glyphs.
// Default { 0x10, 0x40, 0x70, 0x40, 0x10 } adds up to 0x110, simulating ink spread.
// SetLcdFilter must be called before SetLcdFilterWeights.
if (FT_Library_SetLcdFilter(fLibrary, FT_LCD_FILTER_DEFAULT) == 0) {
fIsLCDSupported = true;
fLCDExtra = 2; //Using a filter adds one full pixel to each side.
#ifdef SK_FONTHOST_FREETYPE_USE_NORMAL_LCD_FILTER
// Adds to 0x110 simulating ink spread, but provides better results than default.
static unsigned char gGaussianLikeHeavyWeights[] = { 0x1A, 0x43, 0x56, 0x43, 0x1A, };
# if SK_FONTHOST_FREETYPE_RUNTIME_VERSION > 0x020400
FT_Library_SetLcdFilterWeights(fLibrary, gGaussianLikeHeavyWeights);
# elif SK_CAN_USE_DLOPEN == 1
//The FreeType library is already loaded, so symbols are available in process.
void* self = dlopen(NULL, RTLD_LAZY);
if (self) {
FT_Library_SetLcdFilterWeightsProc setLcdFilterWeights;
//The following cast is non-standard, but safe for POSIX.
*reinterpret_cast<void**>(&setLcdFilterWeights) =
dlsym(self, "FT_Library_SetLcdFilterWeights");
dlclose(self);
if (setLcdFilterWeights) {
setLcdFilterWeights(fLibrary, gGaussianLikeHeavyWeights);
}
}
# endif
#endif
}
}
~FreeTypeLibrary() {
if (fLibrary) {
FT_Done_Library(fLibrary);
}
}
FT_Library library() { return fLibrary; }
bool isLCDSupported() { return fIsLCDSupported; }
int lcdExtra() { return fLCDExtra; }
private:
FT_Library fLibrary;
bool fIsLCDSupported;
int fLCDExtra;
// FT_Library_SetLcdFilterWeights was introduced in FreeType 2.4.0.
// The following platforms provide FreeType of at least 2.4.0.
// Ubuntu >= 11.04 (previous deprecated April 2013)
// Debian >= 6.0 (good)
// OpenSuse >= 11.4 (previous deprecated January 2012 / Nov 2013 for Evergreen 11.2)
// Fedora >= 14 (good)
// Android >= Gingerbread (good)
typedef FT_Error (*FT_Library_SetLcdFilterWeightsProc)(FT_Library, unsigned char*);
};
struct SkFaceRec; struct SkFaceRec;
SK_DECLARE_STATIC_MUTEX(gFTMutex); SK_DECLARE_STATIC_MUTEX(gFTMutex);
static int gFTCount; static FreeTypeLibrary* gFTLibrary;
static FT_Library gFTLibrary; static SkFaceRec* gFaceRecHead;
static SkFaceRec* gFaceRecHead;
static bool gLCDSupportValid; // true iff |gLCDSupport| has been set.
static bool gLCDSupport; // true iff LCD is supported by the runtime.
static int gLCDExtra; // number of extra pixels for filtering.
///////////////////////////////////////////////////////////////////////// // Private to RefFreeType and UnrefFreeType
static int gFTCount;
// FT_Library_SetLcdFilterWeights was introduced in FreeType 2.4.0.
// The following platforms provide FreeType of at least 2.4.0.
// Ubuntu >= 11.04 (previous deprecated April 2013)
// Debian >= 6.0 (good)
// OpenSuse >= 11.4 (previous deprecated January 2012 / Nov 2013 for Evergreen 11.2)
// Fedora >= 14 (good)
// Android >= Gingerbread (good)
typedef FT_Error (*FT_Library_SetLcdFilterWeightsProc)(FT_Library, unsigned char*);
// Caller must lock gFTMutex before calling this function. // Caller must lock gFTMutex before calling this function.
static bool InitFreetype() { static bool ref_ft_library() {
gFTMutex.assertHeld(); gFTMutex.assertHeld();
SkASSERT(gFTCount >= 0);
FT_Error err = FT_Init_FreeType(&gFTLibrary); if (0 == gFTCount) {
if (err) { SkASSERT(NULL == gFTLibrary);
return false; gFTLibrary = SkNEW(FreeTypeLibrary);
} }
++gFTCount;
// Setup LCD filtering. This reduces color fringes for LCD smoothed glyphs. return gFTLibrary->library();
// Use default { 0x10, 0x40, 0x70, 0x40, 0x10 }, as it adds up to 0x110, simulating ink spread.
// SetLcdFilter must be called before SetLcdFilterWeights.
err = FT_Library_SetLcdFilter(gFTLibrary, FT_LCD_FILTER_DEFAULT);
if (0 == err) {
gLCDSupport = true;
gLCDExtra = 2; //Using a filter adds one full pixel to each side.
#ifdef SK_FONTHOST_FREETYPE_USE_NORMAL_LCD_FILTER
// This also adds to 0x110 simulating ink spread, but provides better results than default.
static unsigned char gGaussianLikeHeavyWeights[] = { 0x1A, 0x43, 0x56, 0x43, 0x1A, };
#if SK_FONTHOST_FREETYPE_RUNTIME_VERSION > 0x020400
err = FT_Library_SetLcdFilterWeights(gFTLibrary, gGaussianLikeHeavyWeights);
#elif SK_CAN_USE_DLOPEN == 1
//The FreeType library is already loaded, so symbols are available in process.
void* self = dlopen(NULL, RTLD_LAZY);
if (self) {
FT_Library_SetLcdFilterWeightsProc setLcdFilterWeights;
//The following cast is non-standard, but safe for POSIX.
*reinterpret_cast<void**>(&setLcdFilterWeights) = dlsym(self, "FT_Library_SetLcdFilterWeights");
dlclose(self);
if (setLcdFilterWeights) {
err = setLcdFilterWeights(gFTLibrary, gGaussianLikeHeavyWeights);
}
}
#endif
#endif
}
gLCDSupportValid = true;
return true;
} }
// Called while holding gFTMutex. // Caller must lock gFTMutex before calling this function.
static void determine_lcd_support(bool* lcdSupported) { static void unref_ft_library() {
if (!gLCDSupportValid) { gFTMutex.assertHeld();
// This will determine LCD support as a side effect. SkASSERT(gFTCount > 0);
InitFreetype();
FT_Done_FreeType(gFTLibrary);
}
SkASSERT(gLCDSupportValid);
*lcdSupported = gLCDSupport;
}
// Lazy, once, wrapper to ask the FreeType Library if it can support LCD text --gFTCount;
static bool is_lcd_supported() { if (0 == gFTCount) {
static bool lcdSupported = false; SkASSERT(NULL != gFTLibrary);
SkOnce(&gLCDSupportValid, &gFTMutex, determine_lcd_support, &lcdSupported); SkDELETE(gFTLibrary);
return lcdSupported; SkDEBUGCODE(gFTLibrary = NULL;)
}
} }
class SkScalerContext_FreeType : public SkScalerContext_FreeType_Base { class SkScalerContext_FreeType : public SkScalerContext_FreeType_Base {
@ -224,34 +248,28 @@ struct SkFaceRec {
}; };
extern "C" { extern "C" {
static unsigned long sk_stream_read(FT_Stream stream, static unsigned long sk_ft_stream_io(FT_Stream stream,
unsigned long offset, unsigned long offset,
unsigned char* buffer, unsigned char* buffer,
unsigned long count ) { unsigned long count)
{
SkStream* str = (SkStream*)stream->descriptor.pointer; SkStream* str = (SkStream*)stream->descriptor.pointer;
if (count) { if (count) {
if (!str->rewind()) { if (!str->rewind()) {
return 0; return 0;
} else { }
unsigned long ret; if (offset) {
if (offset) { if (str->skip(offset) != offset) {
ret = str->read(NULL, offset);
if (ret != offset) {
return 0;
}
}
ret = str->read(buffer, count);
if (ret != count) {
return 0; return 0;
} }
count = ret;
} }
count = str->read(buffer, count);
} }
return count; return count;
} }
static void sk_stream_close(FT_Stream) {} static void sk_ft_stream_close(FT_Stream) {}
} }
SkFaceRec::SkFaceRec(SkStream* strm, uint32_t fontID) SkFaceRec::SkFaceRec(SkStream* strm, uint32_t fontID)
@ -261,8 +279,8 @@ SkFaceRec::SkFaceRec(SkStream* strm, uint32_t fontID)
sk_bzero(&fFTStream, sizeof(fFTStream)); sk_bzero(&fFTStream, sizeof(fFTStream));
fFTStream.size = fSkStream->getLength(); fFTStream.size = fSkStream->getLength();
fFTStream.descriptor.pointer = fSkStream; fFTStream.descriptor.pointer = fSkStream;
fFTStream.read = sk_stream_read; fFTStream.read = sk_ft_stream_io;
fFTStream.close = sk_stream_close; fFTStream.close = sk_ft_stream_close;
} }
// Will return 0 on failure // Will return 0 on failure
@ -290,7 +308,7 @@ static SkFaceRec* ref_ft_face(const SkTypeface* typeface) {
// this passes ownership of strm to the rec // this passes ownership of strm to the rec
rec = SkNEW_ARGS(SkFaceRec, (strm, fontID)); rec = SkNEW_ARGS(SkFaceRec, (strm, fontID));
FT_Open_Args args; FT_Open_Args args;
memset(&args, 0, sizeof(args)); memset(&args, 0, sizeof(args));
const void* memoryBase = strm->getMemoryBase(); const void* memoryBase = strm->getMemoryBase();
@ -303,17 +321,16 @@ static SkFaceRec* ref_ft_face(const SkTypeface* typeface) {
args.stream = &rec->fFTStream; args.stream = &rec->fFTStream;
} }
FT_Error err = FT_Open_Face(gFTLibrary, &args, face_index, &rec->fFace); FT_Error err = FT_Open_Face(gFTLibrary->library(), &args, face_index, &rec->fFace);
if (err) { // bad filename, try the default font if (err) { // bad filename, try the default font
SkDEBUGF(("ERROR: unable to open font '%x'\n", fontID)); SkDEBUGF(("ERROR: unable to open font '%x'\n", fontID));
SkDELETE(rec); SkDELETE(rec);
return NULL; return NULL;
} else {
SkASSERT(rec->fFace);
rec->fNext = gFaceRecHead;
gFaceRecHead = rec;
return rec;
} }
SkASSERT(rec->fFace);
rec->fNext = gFaceRecHead;
gFaceRecHead = rec;
return rec;
} }
// Caller must lock gFTMutex before calling this function. // Caller must lock gFTMutex before calling this function.
@ -346,10 +363,8 @@ class AutoFTAccess {
public: public:
AutoFTAccess(const SkTypeface* tf) : fRec(NULL), fFace(NULL) { AutoFTAccess(const SkTypeface* tf) : fRec(NULL), fFace(NULL) {
gFTMutex.acquire(); gFTMutex.acquire();
if (1 == ++gFTCount) { if (!ref_ft_library()) {
if (!InitFreetype()) { sk_throw();
sk_throw();
}
} }
fRec = ref_ft_face(tf); fRec = ref_ft_face(tf);
if (fRec) { if (fRec) {
@ -361,9 +376,7 @@ public:
if (fFace) { if (fFace) {
unref_ft_face(fFace); unref_ft_face(fFace);
} }
if (0 == --gFTCount) { unref_ft_library();
FT_Done_FreeType(gFTLibrary);
}
gFTMutex.release(); gFTMutex.release();
} }
@ -684,7 +697,8 @@ void SkTypeface_FreeType::onFilterRec(SkScalerContextRec* rec) const {
rec->fTextSize = SkIntToScalar(1 << 14); rec->fTextSize = SkIntToScalar(1 << 14);
} }
if (!is_lcd_supported() && isLCD(*rec)) { AutoFTAccess fta(this);
if (!gFTLibrary->isLCDSupported() && isLCD(*rec)) {
// If the runtime Freetype library doesn't support LCD mode, we disable // If the runtime Freetype library doesn't support LCD mode, we disable
// it here. // it here.
rec->fMaskFormat = SkMask::kA8_Format; rec->fMaskFormat = SkMask::kA8_Format;
@ -789,12 +803,9 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface,
: SkScalerContext_FreeType_Base(typeface, desc) { : SkScalerContext_FreeType_Base(typeface, desc) {
SkAutoMutexAcquire ac(gFTMutex); SkAutoMutexAcquire ac(gFTMutex);
if (gFTCount == 0) { if (!ref_ft_library()) {
if (!InitFreetype()) { sk_throw();
sk_throw();
}
} }
++gFTCount;
// load the font file // load the font file
fStrikeIndex = -1; fStrikeIndex = -1;
@ -990,10 +1001,8 @@ SkScalerContext_FreeType::~SkScalerContext_FreeType() {
if (fFace != NULL) { if (fFace != NULL) {
unref_ft_face(fFace); unref_ft_face(fFace);
} }
if (--gFTCount == 0) {
FT_Done_FreeType(gFTLibrary); unref_ft_library();
SkDEBUGCODE(gFTLibrary = NULL;)
}
} }
/* We call this before each use of the fFace, since we may be sharing /* We call this before each use of the fFace, since we may be sharing
@ -1124,11 +1133,11 @@ bool SkScalerContext_FreeType::getCBoxForLetter(char letter, FT_BBox* bbox) {
void SkScalerContext_FreeType::updateGlyphIfLCD(SkGlyph* glyph) { void SkScalerContext_FreeType::updateGlyphIfLCD(SkGlyph* glyph) {
if (isLCD(fRec)) { if (isLCD(fRec)) {
if (fLCDIsVert) { if (fLCDIsVert) {
glyph->fHeight += gLCDExtra; glyph->fHeight += gFTLibrary->lcdExtra();
glyph->fTop -= gLCDExtra >> 1; glyph->fTop -= gFTLibrary->lcdExtra() >> 1;
} else { } else {
glyph->fWidth += gLCDExtra; glyph->fWidth += gFTLibrary->lcdExtra();
glyph->fLeft -= gLCDExtra >> 1; glyph->fLeft -= gFTLibrary->lcdExtra() >> 1;
} }
} }
} }
@ -1604,14 +1613,16 @@ size_t SkTypeface_FreeType::onGetTableData(SkFontTableTag tag, size_t offset,
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
SkTypeface_FreeType::Scanner::Scanner() : fLibrary(NULL) {
SkTypeface_FreeType::Scanner::Scanner() { if (FT_New_Library(&gFTMemory, &fLibrary)) {
if (FT_Init_FreeType(&fLibrary)) { return;
fLibrary = NULL;
} }
FT_Add_Default_Modules(fLibrary);
} }
SkTypeface_FreeType::Scanner::~Scanner() { SkTypeface_FreeType::Scanner::~Scanner() {
FT_Done_FreeType(fLibrary); if (fLibrary) {
FT_Done_Library(fLibrary);
}
} }
FT_Face SkTypeface_FreeType::Scanner::openFace(SkStream* stream, int ttcIndex, FT_Face SkTypeface_FreeType::Scanner::openFace(SkStream* stream, int ttcIndex,
@ -1634,8 +1645,8 @@ FT_Face SkTypeface_FreeType::Scanner::openFace(SkStream* stream, int ttcIndex,
memset(ftStream, 0, sizeof(*ftStream)); memset(ftStream, 0, sizeof(*ftStream));
ftStream->size = stream->getLength(); ftStream->size = stream->getLength();
ftStream->descriptor.pointer = stream; ftStream->descriptor.pointer = stream;
ftStream->read = sk_stream_read; ftStream->read = sk_ft_stream_io;
ftStream->close = sk_stream_close; ftStream->close = sk_ft_stream_close;
args.flags = FT_OPEN_STREAM; args.flags = FT_OPEN_STREAM;
args.stream = ftStream; args.stream = ftStream;