skia2/src/ports/SkFontHost_linux.cpp

573 lines
17 KiB
C++
Raw Normal View History

Automatic update of all copyright notices to reflect new license terms. I have manually examined all of these diffs and restored a few files that seem to require manual adjustment. The following files still need to be modified manually, in a separate CL: android_sample/SampleApp/AndroidManifest.xml android_sample/SampleApp/res/layout/layout.xml android_sample/SampleApp/res/menu/sample.xml android_sample/SampleApp/res/values/strings.xml android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java android_sample/SampleApp/src/com/skia/sampleapp/SampleView.java experimental/CiCarbonSampleMain.c experimental/CocoaDebugger/main.m experimental/FileReaderApp/main.m experimental/SimpleCocoaApp/main.m experimental/iOSSampleApp/Shared/SkAlertPrompt.h experimental/iOSSampleApp/Shared/SkAlertPrompt.m experimental/iOSSampleApp/SkiOSSampleApp-Base.xcconfig experimental/iOSSampleApp/SkiOSSampleApp-Debug.xcconfig experimental/iOSSampleApp/SkiOSSampleApp-Release.xcconfig gpu/src/android/GrGLDefaultInterface_android.cpp gyp/common.gypi gyp_skia include/ports/SkHarfBuzzFont.h include/views/SkOSWindow_wxwidgets.h make.bat make.py src/opts/memset.arm.S src/opts/memset16_neon.S src/opts/memset32_neon.S src/opts/opts_check_arm.cpp src/ports/SkDebug_brew.cpp src/ports/SkMemory_brew.cpp src/ports/SkOSFile_brew.cpp src/ports/SkXMLParser_empty.cpp src/utils/ios/SkImageDecoder_iOS.mm src/utils/ios/SkOSFile_iOS.mm src/utils/ios/SkStream_NSData.mm tests/FillPathTest.cpp Review URL: http://codereview.appspot.com/4816058 git-svn-id: http://skia.googlecode.com/svn/trunk@1982 2bbb7eff-a529-9590-31e7-b0007b416f81
2011-07-28 14:26:00 +00:00
/*
* Copyright 2006 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
Automatic update of all copyright notices to reflect new license terms. I have manually examined all of these diffs and restored a few files that seem to require manual adjustment. The following files still need to be modified manually, in a separate CL: android_sample/SampleApp/AndroidManifest.xml android_sample/SampleApp/res/layout/layout.xml android_sample/SampleApp/res/menu/sample.xml android_sample/SampleApp/res/values/strings.xml android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java android_sample/SampleApp/src/com/skia/sampleapp/SampleView.java experimental/CiCarbonSampleMain.c experimental/CocoaDebugger/main.m experimental/FileReaderApp/main.m experimental/SimpleCocoaApp/main.m experimental/iOSSampleApp/Shared/SkAlertPrompt.h experimental/iOSSampleApp/Shared/SkAlertPrompt.m experimental/iOSSampleApp/SkiOSSampleApp-Base.xcconfig experimental/iOSSampleApp/SkiOSSampleApp-Debug.xcconfig experimental/iOSSampleApp/SkiOSSampleApp-Release.xcconfig gpu/src/android/GrGLDefaultInterface_android.cpp gyp/common.gypi gyp_skia include/ports/SkHarfBuzzFont.h include/views/SkOSWindow_wxwidgets.h make.bat make.py src/opts/memset.arm.S src/opts/memset16_neon.S src/opts/memset32_neon.S src/opts/opts_check_arm.cpp src/ports/SkDebug_brew.cpp src/ports/SkMemory_brew.cpp src/ports/SkOSFile_brew.cpp src/ports/SkXMLParser_empty.cpp src/utils/ios/SkImageDecoder_iOS.mm src/utils/ios/SkOSFile_iOS.mm src/utils/ios/SkStream_NSData.mm tests/FillPathTest.cpp Review URL: http://codereview.appspot.com/4816058 git-svn-id: http://skia.googlecode.com/svn/trunk@1982 2bbb7eff-a529-9590-31e7-b0007b416f81
2011-07-28 14:26:00 +00:00
#include "SkFontHost.h"
#include "SkFontDescriptor.h"
#include "SkDescriptor.h"
#include "SkOSFile.h"
#include "SkPaint.h"
#include "SkString.h"
#include "SkStream.h"
#include "SkThread.h"
#include "SkTSearch.h"
#ifndef SK_FONT_FILE_PREFIX
#define SK_FONT_FILE_PREFIX "/usr/share/fonts/truetype/"
#endif
#ifndef SK_FONT_FILE_DIR_SEPERATOR
#define SK_FONT_FILE_DIR_SEPERATOR "/"
#endif
bool find_name_and_attributes(SkStream* stream, SkString* name,
SkTypeface::Style* style, bool* isFixedWidth);
///////////////////////////////////////////////////////////////////////////////
struct FamilyRec;
/* This guy holds a mapping of a name -> family, used for looking up fonts.
Since it is stored in a stretchy array that doesn't preserve object
semantics, we don't use constructor/destructors, but just have explicit
helpers to manage our internal bookkeeping.
*/
struct NameFamilyPair {
const char* fName; // we own this
FamilyRec* fFamily; // we don't own this, we just reference it
void construct(const char name[], FamilyRec* family)
{
fName = strdup(name);
fFamily = family; // we don't own this, so just record the referene
}
void destruct()
{
free((char*)fName);
// we don't own family, so just ignore our reference
}
};
// we use atomic_inc to grow this for each typeface we create
static int32_t gUniqueFontID;
// this is the mutex that protects these globals
posix: Avoid static initializers in static/global mutexes This patch removes static initializers related to static and global mutexes from the final library's machine code when building on a pthread-capable system. We use PTHREAD_MUTEX_INITIALIZER to perform POD-style initialization. You need a line like the following to declare a global mutex with it: SkBaseMutex gMutex = { PTHREAD_MUTEX_INITIALIZER }; We introduce the SK_DECLARE_STATIC_MUTEX and SK_DECLARE_GLOBAL_MUTEX macros to be able to declare static/global mutexes in the source tree uniformly. SkMutex is now defined as a sub-class of SkBaseMutex, with standard construction/destruction semantics. This is useful if the mutex object is a member of another C++ class, or allocated dynamically. We also modify a few places to refer to SkBaseMutex instead of a SkMutex, where it makes sense. Generally speaking, client code should hold and use pointers to SkBaseMutex whenever they can now. We defined a new built-time macro named SK_USE_POSIX_THREADS to indicate that we're using a pthread-based SkThread.h interface. The macro will also be used in future patches to implement other helper thread synchronization classes. Finally, we inline the acquire() and release() functions in the case of Posix to improve performance a bit. Running: 'bench -repeat 10 -match mutex' on an Android device or a 2.4GHz Xeon Linux desktop shows the following improvements: Before After Galaxy Nexus 1.64 1.45 Nexus S 1.47 1.16 Xoom 1.86 1.66 Xeon 0.36 0.31 This removes 5 static mutex initializers from the library Review URL: https://codereview.appspot.com/5501066 git-svn-id: http://skia.googlecode.com/svn/trunk@3091 2bbb7eff-a529-9590-31e7-b0007b416f81
2012-01-26 21:26:40 +00:00
SK_DECLARE_STATIC_MUTEX(gFamilyMutex);
static FamilyRec* gFamilyHead;
static SkTDArray<NameFamilyPair> gNameList;
struct FamilyRec {
FamilyRec* fNext;
SkTypeface* fFaces[4];
FamilyRec()
{
fNext = gFamilyHead;
memset(fFaces, 0, sizeof(fFaces));
gFamilyHead = this;
}
};
static SkTypeface* find_best_face(const FamilyRec* family,
SkTypeface::Style style) {
SkTypeface* const* faces = family->fFaces;
if (faces[style] != NULL) { // exact match
return faces[style];
}
// look for a matching bold
style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
if (faces[style] != NULL) {
return faces[style];
}
// look for the plain
if (faces[SkTypeface::kNormal] != NULL) {
return faces[SkTypeface::kNormal];
}
// look for anything
for (int i = 0; i < 4; i++) {
if (faces[i] != NULL) {
return faces[i];
}
}
// should never get here, since the faces list should not be empty
SkDEBUGFAIL("faces list is empty");
return NULL;
}
static FamilyRec* find_family(const SkTypeface* member) {
FamilyRec* curr = gFamilyHead;
while (curr != NULL) {
for (int i = 0; i < 4; i++) {
if (curr->fFaces[i] == member) {
return curr;
}
}
curr = curr->fNext;
}
return NULL;
}
static SkTypeface* find_from_uniqueID(uint32_t uniqueID) {
FamilyRec* curr = gFamilyHead;
while (curr != NULL) {
for (int i = 0; i < 4; i++) {
SkTypeface* face = curr->fFaces[i];
if (face != NULL && face->uniqueID() == uniqueID) {
return face;
}
}
curr = curr->fNext;
}
return NULL;
}
/* Remove reference to this face from its family. If the resulting family
is empty (has no faces), return that family, otherwise return NULL
*/
static FamilyRec* remove_from_family(const SkTypeface* face) {
FamilyRec* family = find_family(face);
SkASSERT(family->fFaces[face->style()] == face);
family->fFaces[face->style()] = NULL;
for (int i = 0; i < 4; i++) {
if (family->fFaces[i] != NULL) { // family is non-empty
return NULL;
}
}
return family; // return the empty family
}
// maybe we should make FamilyRec be doubly-linked
static void detach_and_delete_family(FamilyRec* family) {
FamilyRec* curr = gFamilyHead;
FamilyRec* prev = NULL;
while (curr != NULL) {
FamilyRec* next = curr->fNext;
if (curr == family) {
if (prev == NULL) {
gFamilyHead = next;
} else {
prev->fNext = next;
}
SkDELETE(family);
return;
}
prev = curr;
curr = next;
}
SkDEBUGFAIL("Yikes, couldn't find family in our list to remove/delete");
}
static const char* find_family_name(const SkTypeface* familyMember) {
const FamilyRec* familyRec = find_family(familyMember);
for (int i = 0; i < gNameList.count(); i++) {
if (gNameList[i].fFamily == familyRec) {
return gNameList[i].fName;
}
}
return NULL;
}
static FamilyRec* find_familyrec(const char name[]) {
const NameFamilyPair* list = gNameList.begin();
int index = SkStrLCSearch(&list[0].fName, gNameList.count(), name,
sizeof(list[0]));
return index >= 0 ? list[index].fFamily : NULL;
}
static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) {
FamilyRec* rec = find_familyrec(name);
return rec ? find_best_face(rec, style) : NULL;
}
static SkTypeface* find_typeface(const SkTypeface* familyMember,
SkTypeface::Style style) {
const FamilyRec* family = find_family(familyMember);
return family ? find_best_face(family, style) : NULL;
}
static void add_name(const char name[], FamilyRec* family) {
SkAutoAsciiToLC tolc(name);
name = tolc.lc();
NameFamilyPair* list = gNameList.begin();
int count = gNameList.count();
int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
if (index < 0) {
list = gNameList.insert(~index);
list->construct(name, family);
}
}
static void remove_from_names(FamilyRec* emptyFamily) {
#ifdef SK_DEBUG
for (int i = 0; i < 4; i++) {
SkASSERT(emptyFamily->fFaces[i] == NULL);
}
#endif
SkTDArray<NameFamilyPair>& list = gNameList;
// must go backwards when removing
for (int i = list.count() - 1; i >= 0; --i) {
NameFamilyPair* pair = &list[i];
if (pair->fFamily == emptyFamily) {
pair->destruct();
list.remove(i);
}
}
}
///////////////////////////////////////////////////////////////////////////////
class FamilyTypeface : public SkTypeface {
public:
FamilyTypeface(Style style, bool sysFont, FamilyRec* family, bool isFixedWidth)
: SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1, isFixedWidth) {
fIsSysFont = sysFont;
SkAutoMutexAcquire ac(gFamilyMutex);
if (NULL == family) {
family = SkNEW(FamilyRec);
}
family->fFaces[style] = this;
fFamilyRec = family; // just record it so we can return it if asked
}
virtual ~FamilyTypeface() {
SkAutoMutexAcquire ac(gFamilyMutex);
// remove us from our family. If the family is now empty, we return
// that and then remove that family from the name list
FamilyRec* family = remove_from_family(this);
if (NULL != family) {
remove_from_names(family);
detach_and_delete_family(family);
}
}
bool isSysFont() const { return fIsSysFont; }
FamilyRec* getFamily() const { return fFamilyRec; }
// openStream returns a SkStream that has been ref-ed
virtual SkStream* openStream() = 0;
virtual const char* getUniqueString() const = 0;
private:
FamilyRec* fFamilyRec; // we don't own this, just point to it
bool fIsSysFont;
typedef SkTypeface INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
/* This subclass is just a place holder for when we have no fonts available.
It exists so that our globals (e.g. gFamilyHead) that expect *something*
will not be null.
*/
class EmptyTypeface : public FamilyTypeface {
public:
EmptyTypeface() : INHERITED(SkTypeface::kNormal, true, NULL, false) {}
// overrides
virtual SkStream* openStream() SK_OVERRIDE { return NULL; }
virtual const char* getUniqueString() SK_OVERRIDE const { return NULL; }
private:
typedef FamilyTypeface INHERITED;
};
class StreamTypeface : public FamilyTypeface {
public:
StreamTypeface(Style style, bool sysFont, FamilyRec* family,
SkStream* stream, bool isFixedWidth)
: INHERITED(style, sysFont, family, isFixedWidth) {
stream->ref();
fStream = stream;
}
virtual ~StreamTypeface() {
fStream->unref();
}
virtual SkStream* openStream() SK_OVERRIDE {
// openStream returns a refed stream.
fStream->ref();
return fStream;
}
virtual const char* getUniqueString() const SK_OVERRIDE { return NULL; }
private:
SkStream* fStream;
typedef FamilyTypeface INHERITED;
};
class FileTypeface : public FamilyTypeface {
public:
FileTypeface(Style style, bool sysFont, FamilyRec* family,
const char path[], bool isFixedWidth)
: INHERITED(style, sysFont, family, isFixedWidth) {
fPath.set(path);
}
virtual SkStream* openStream() SK_OVERRIDE {
return SkStream::NewFromFile(fPath.c_str());
}
virtual const char* getUniqueString() const SK_OVERRIDE {
const char* str = strrchr(fPath.c_str(), '/');
if (str) {
str += 1; // skip the '/'
}
return str;
}
private:
SkString fPath;
typedef FamilyTypeface INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static bool get_name_and_style(const char path[], SkString* name,
SkTypeface::Style* style, bool* isFixedWidth) {
SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
if (stream.get()) {
return find_name_and_attributes(stream, name, style, isFixedWidth);
} else {
SkDebugf("---- failed to open <%s> as a font\n", path);
return false;
}
}
// these globals are assigned (once) by load_system_fonts()
static SkTypeface* gFallBackTypeface;
static FamilyRec* gDefaultFamily;
static SkTypeface* gDefaultNormal;
static void load_directory_fonts(const SkString& directory, unsigned int* count) {
SkOSFile::Iter iter(directory.c_str(), ".ttf");
SkString name;
while (iter.next(&name, false)) {
SkString filename(directory);
filename.append(name);
bool isFixedWidth;
SkString realname;
SkTypeface::Style style = SkTypeface::kNormal; // avoid uninitialized warning
if (!get_name_and_style(filename.c_str(), &realname, &style, &isFixedWidth)) {
SkDebugf("------ can't load <%s> as a font\n", filename.c_str());
continue;
}
FamilyRec* family = find_familyrec(realname.c_str());
if (family && family->fFaces[style]) {
continue;
}
// this constructor puts us into the global gFamilyHead llist
FamilyTypeface* tf = SkNEW_ARGS(FileTypeface,
(style,
true, // system-font (cannot delete)
family, // what family to join
filename.c_str(),
isFixedWidth) // filename
);
if (NULL == family) {
add_name(realname.c_str(), tf->getFamily());
}
*count += 1;
}
SkOSFile::Iter dirIter(directory.c_str());
while (dirIter.next(&name, true)) {
if (name.startsWith(".")) {
continue;
}
SkString dirname(directory);
dirname.append(name);
dirname.append(SK_FONT_FILE_DIR_SEPERATOR);
load_directory_fonts(dirname, count);
}
}
static void load_system_fonts() {
// check if we've already be called
if (NULL != gDefaultNormal) {
return;
}
SkString baseDirectory(SK_FONT_FILE_PREFIX);
unsigned int count = 0;
load_directory_fonts(baseDirectory, &count);
if (0 == count) {
SkNEW(EmptyTypeface);
}
// do this after all fonts are loaded. This is our default font, and it
// acts as a sentinel so we only execute load_system_fonts() once
static const char* gDefaultNames[] = {
"Arial", "Verdana", "Times New Roman", NULL
};
const char** names = gDefaultNames;
while (*names) {
SkTypeface* tf = find_typeface(*names++, SkTypeface::kNormal);
if (tf) {
gDefaultNormal = tf;
break;
}
}
// check if we found *something*
if (NULL == gDefaultNormal) {
if (NULL == gFamilyHead) {
sk_throw();
}
for (int i = 0; i < 4; i++) {
if ((gDefaultNormal = gFamilyHead->fFaces[i]) != NULL) {
break;
}
}
}
if (NULL == gDefaultNormal) {
sk_throw();
}
gFallBackTypeface = gDefaultNormal;
gDefaultFamily = find_family(gDefaultNormal);
}
///////////////////////////////////////////////////////////////////////////////
void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
SkFontDescriptor descriptor;
descriptor.setFamilyName(find_family_name(face));
descriptor.setStyle(face->style());
descriptor.setFontFileName(((FamilyTypeface*)face)->getUniqueString());
descriptor.serialize(stream);
const bool isCustomFont = !((FamilyTypeface*)face)->isSysFont();
if (isCustomFont) {
// store the entire font in the fontData
SkStream* fontStream = ((FamilyTypeface*)face)->openStream();
const uint32_t length = fontStream->getLength();
stream->writePackedUInt(length);
stream->writeStream(fontStream, length);
fontStream->unref();
} else {
stream->writePackedUInt(0);
}
}
SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
load_system_fonts();
SkFontDescriptor descriptor(stream);
const char* familyName = descriptor.getFamilyName();
const SkTypeface::Style style = descriptor.getStyle();
const uint32_t customFontDataLength = stream->readPackedUInt();
if (customFontDataLength > 0) {
// generate a new stream to store the custom typeface
SkMemoryStream* fontStream = new SkMemoryStream(customFontDataLength - 1);
stream->read((void*)fontStream->getMemoryBase(), customFontDataLength - 1);
SkTypeface* face = CreateTypefaceFromStream(fontStream);
fontStream->unref();
return face;
}
return SkFontHost::CreateTypeface(NULL, familyName, style);
}
///////////////////////////////////////////////////////////////////////////////
SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
const char familyName[],
SkTypeface::Style style) {
load_system_fonts();
SkAutoMutexAcquire ac(gFamilyMutex);
// clip to legal style bits
style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
SkTypeface* tf = NULL;
if (NULL != familyFace) {
tf = find_typeface(familyFace, style);
} else if (NULL != familyName) {
// SkDebugf("======= familyName <%s>\n", familyName);
tf = find_typeface(familyName, style);
}
if (NULL == tf) {
tf = find_best_face(gDefaultFamily, style);
}
SkSafeRef(tf);
return tf;
}
SkStream* SkFontHost::OpenStream(uint32_t fontID) {
FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
SkStream* stream = tf ? tf->openStream() : NULL;
if (stream && stream->getLength() == 0) {
stream->unref();
stream = NULL;
}
return stream;
}
size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
int32_t* index) {
// SkDebugf("SkFontHost::GetFileName unimplemented\n");
return 0;
}
SkTypeface* SkFontHost::NextLogicalTypeface(SkFontID currFontID, SkFontID origFontID) {
return NULL;
}
///////////////////////////////////////////////////////////////////////////////
SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
if (NULL == stream || stream->getLength() <= 0) {
SkDELETE(stream);
return NULL;
}
bool isFixedWidth;
SkTypeface::Style style;
if (find_name_and_attributes(stream, NULL, &style, &isFixedWidth)) {
return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream, isFixedWidth));
} else {
return NULL;
}
}
SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
return stream.get() ? CreateTypefaceFromStream(stream) : NULL;
}