michael@0: # HG changeset patch michael@0: # Parent 9ee29e4aace683ddf6cf8ddb2893cd34fcfc772c michael@0: # User James Willcox michael@0: diff --git a/gfx/skia/Makefile.in b/gfx/skia/Makefile.in michael@0: --- a/gfx/skia/Makefile.in michael@0: +++ b/gfx/skia/Makefile.in michael@0: @@ -305,21 +305,20 @@ CPPSRCS += \ michael@0: SkFontHost_mac_coretext.cpp \ michael@0: SkTime_Unix.cpp \ michael@0: $(NULL) michael@0: endif michael@0: michael@0: ifeq (android,$(MOZ_WIDGET_TOOLKIT)) michael@0: CPPSRCS += \ michael@0: SkFontHost_FreeType.cpp \ michael@0: SkFontHost_android.cpp \ michael@0: SkFontHost_gamma.cpp \ michael@0: - FontHostConfiguration_android.cpp \ michael@0: SkMMapStream.cpp \ michael@0: SkTime_Unix.cpp \ michael@0: $(NULL) michael@0: michael@0: DEFINES += -DSK_BUILD_FOR_ANDROID_NDK michael@0: OS_CXXFLAGS += $(CAIRO_FT_CFLAGS) michael@0: endif michael@0: michael@0: ifeq (gtk2,$(MOZ_WIDGET_TOOLKIT)) michael@0: CPPSRCS += \ michael@0: diff --git a/gfx/skia/src/ports/SkFontHost_android.cpp b/gfx/skia/src/ports/SkFontHost_android.cpp michael@0: --- a/gfx/skia/src/ports/SkFontHost_android.cpp michael@0: +++ b/gfx/skia/src/ports/SkFontHost_android.cpp michael@0: @@ -1,38 +1,31 @@ michael@0: + michael@0: /* michael@0: -** michael@0: -** Copyright 2006, The Android Open Source Project michael@0: -** michael@0: -** Licensed under the Apache License, Version 2.0 (the "License"); michael@0: -** you may not use this file except in compliance with the License. michael@0: -** You may obtain a copy of the License at michael@0: -** michael@0: -** http://www.apache.org/licenses/LICENSE-2.0 michael@0: -** michael@0: -** Unless required by applicable law or agreed to in writing, software michael@0: -** distributed under the License is distributed on an "AS IS" BASIS, michael@0: -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: -** See the License for the specific language governing permissions and michael@0: -** limitations under the License. michael@0: -*/ michael@0: + * Copyright 2006 The Android Open Source Project michael@0: + * michael@0: + * Use of this source code is governed by a BSD-style license that can be michael@0: + * found in the LICENSE file. michael@0: + */ michael@0: + michael@0: michael@0: #include "SkFontHost.h" michael@0: #include "SkDescriptor.h" michael@0: #include "SkMMapStream.h" michael@0: #include "SkPaint.h" michael@0: #include "SkString.h" michael@0: #include "SkStream.h" michael@0: #include "SkThread.h" michael@0: #include "SkTSearch.h" michael@0: -#include "FontHostConfiguration_android.h" michael@0: #include michael@0: michael@0: +#define FONT_CACHE_MEMORY_BUDGET (768 * 1024) michael@0: + michael@0: #ifndef SK_FONT_FILE_PREFIX michael@0: #define SK_FONT_FILE_PREFIX "/fonts/" michael@0: #endif michael@0: michael@0: SkTypeface::Style find_name_and_attributes(SkStream* stream, SkString* name, michael@0: bool* isFixedWidth); michael@0: michael@0: static void GetFullPathForSysFonts(SkString* full, const char name[]) { michael@0: full->set(getenv("ANDROID_ROOT")); michael@0: full->append(SK_FONT_FILE_PREFIX); michael@0: @@ -99,21 +92,21 @@ static SkTypeface* find_best_face(const michael@0: if (faces[SkTypeface::kNormal] != NULL) { michael@0: return faces[SkTypeface::kNormal]; michael@0: } michael@0: // look for anything michael@0: for (int i = 0; i < 4; i++) { michael@0: if (faces[i] != NULL) { michael@0: return faces[i]; michael@0: } michael@0: } michael@0: // should never get here, since the faces list should not be empty michael@0: - SkDEBUGFAIL("faces list is empty"); michael@0: + SkASSERT(!"faces list is empty"); michael@0: return NULL; michael@0: } michael@0: michael@0: static FamilyRec* find_family(const SkTypeface* member) { michael@0: FamilyRec* curr = gFamilyHead; michael@0: while (curr != NULL) { michael@0: for (int i = 0; i < 4; i++) { michael@0: if (curr->fFaces[i] == member) { michael@0: return curr; michael@0: } michael@0: @@ -138,31 +131,27 @@ static SkTypeface* find_from_uniqueID(ui michael@0: curr = curr->fNext; michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: /* Remove reference to this face from its family. If the resulting family michael@0: is empty (has no faces), return that family, otherwise return NULL michael@0: */ michael@0: static FamilyRec* remove_from_family(const SkTypeface* face) { michael@0: FamilyRec* family = find_family(face); michael@0: - if (family) { michael@0: - SkASSERT(family->fFaces[face->style()] == face); michael@0: - family->fFaces[face->style()] = NULL; michael@0: + SkASSERT(family->fFaces[face->style()] == face); michael@0: + family->fFaces[face->style()] = NULL; michael@0: michael@0: - for (int i = 0; i < 4; i++) { michael@0: - if (family->fFaces[i] != NULL) { // family is non-empty michael@0: - return NULL; michael@0: - } michael@0: + for (int i = 0; i < 4; i++) { michael@0: + if (family->fFaces[i] != NULL) { // family is non-empty michael@0: + return NULL; michael@0: } michael@0: - } else { michael@0: -// SkDebugf("remove_from_family(%p) face not found", face); michael@0: } michael@0: return family; // return the empty family michael@0: } michael@0: michael@0: // maybe we should make FamilyRec be doubly-linked michael@0: static void detach_and_delete_family(FamilyRec* family) { michael@0: FamilyRec* curr = gFamilyHead; michael@0: FamilyRec* prev = NULL; michael@0: michael@0: while (curr != NULL) { michael@0: @@ -172,21 +161,21 @@ static void detach_and_delete_family(Fam michael@0: gFamilyHead = next; michael@0: } else { michael@0: prev->fNext = next; michael@0: } michael@0: SkDELETE(family); michael@0: return; michael@0: } michael@0: prev = curr; michael@0: curr = next; michael@0: } michael@0: - SkDEBUGFAIL("Yikes, couldn't find family in our list to remove/delete"); michael@0: + SkASSERT(!"Yikes, couldn't find family in our list to remove/delete"); michael@0: } michael@0: michael@0: static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) { michael@0: NameFamilyPair* list = gNameList.begin(); michael@0: int count = gNameList.count(); michael@0: michael@0: int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0])); michael@0: michael@0: if (index >= 0) { michael@0: return find_best_face(list[index].fFamily, style); michael@0: @@ -387,111 +376,90 @@ static bool get_name_and_style(const cha michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // used to record our notion of the pre-existing fonts michael@0: struct FontInitRec { michael@0: const char* fFileName; michael@0: const char* const* fNames; // null-terminated list michael@0: }; michael@0: michael@0: +static const char* gSansNames[] = { michael@0: + "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL michael@0: +}; michael@0: + michael@0: +static const char* gSerifNames[] = { michael@0: + "serif", "times", "times new roman", "palatino", "georgia", "baskerville", michael@0: + "goudy", "fantasy", "cursive", "ITC Stone Serif", NULL michael@0: +}; michael@0: + michael@0: +static const char* gMonoNames[] = { michael@0: + "monospace", "courier", "courier new", "monaco", NULL michael@0: +}; michael@0: + michael@0: // deliberately empty, but we use the address to identify fallback fonts michael@0: static const char* gFBNames[] = { NULL }; michael@0: michael@0: +/* Fonts must be grouped by family, with the first font in a family having the michael@0: + list of names (even if that list is empty), and the following members having michael@0: + null for the list. The names list must be NULL-terminated michael@0: +*/ michael@0: +static const FontInitRec gSystemFonts[] = { michael@0: + { "DroidSans.ttf", gSansNames }, michael@0: + { "DroidSans-Bold.ttf", NULL }, michael@0: + { "DroidSerif-Regular.ttf", gSerifNames }, michael@0: + { "DroidSerif-Bold.ttf", NULL }, michael@0: + { "DroidSerif-Italic.ttf", NULL }, michael@0: + { "DroidSerif-BoldItalic.ttf", NULL }, michael@0: + { "DroidSansMono.ttf", gMonoNames }, michael@0: + /* These are optional, and can be ignored if not found in the file system. michael@0: + These are appended to gFallbackFonts[] as they are seen, so we list michael@0: + them in the order we want them to be accessed by NextLogicalFont(). michael@0: + */ michael@0: + { "DroidSansArabic.ttf", gFBNames }, michael@0: + { "DroidSansHebrew.ttf", gFBNames }, michael@0: + { "DroidSansThai.ttf", gFBNames }, michael@0: + { "MTLmr3m.ttf", gFBNames }, // Motoya Japanese Font michael@0: + { "MTLc3m.ttf", gFBNames }, // Motoya Japanese Font michael@0: + { "DroidSansJapanese.ttf", gFBNames }, michael@0: + { "DroidSansFallback.ttf", gFBNames } michael@0: +}; michael@0: michael@0: -/* Fonts are grouped by family, with the first font in a family having the michael@0: - list of names (even if that list is empty), and the following members having michael@0: - null for the list. The names list must be NULL-terminated. michael@0: -*/ michael@0: -static FontInitRec *gSystemFonts; michael@0: -static size_t gNumSystemFonts = 0; michael@0: - michael@0: -#define SYSTEM_FONTS_FILE "/system/etc/system_fonts.cfg" michael@0: +#define DEFAULT_NAMES gSansNames michael@0: michael@0: // these globals are assigned (once) by load_system_fonts() michael@0: static FamilyRec* gDefaultFamily; michael@0: static SkTypeface* gDefaultNormal; michael@0: -static char** gDefaultNames = NULL; michael@0: -static uint32_t *gFallbackFonts; michael@0: michael@0: -/* Load info from a configuration file that populates the system/fallback font structures michael@0: -*/ michael@0: -static void load_font_info() { michael@0: -// load_font_info_xml("/system/etc/system_fonts.xml"); michael@0: - SkTDArray fontFamilies; michael@0: - getFontFamilies(fontFamilies); michael@0: - michael@0: - SkTDArray fontInfo; michael@0: - bool firstInFamily = false; michael@0: - for (int i = 0; i < fontFamilies.count(); ++i) { michael@0: - FontFamily *family = fontFamilies[i]; michael@0: - firstInFamily = true; michael@0: - for (int j = 0; j < family->fFileNames.count(); ++j) { michael@0: - FontInitRec fontInfoRecord; michael@0: - fontInfoRecord.fFileName = family->fFileNames[j]; michael@0: - if (j == 0) { michael@0: - if (family->fNames.count() == 0) { michael@0: - // Fallback font michael@0: - fontInfoRecord.fNames = (char **)gFBNames; michael@0: - } else { michael@0: - SkTDArray names = family->fNames; michael@0: - const char **nameList = (const char**) michael@0: - malloc((names.count() + 1) * sizeof(char*)); michael@0: - if (nameList == NULL) { michael@0: - // shouldn't get here michael@0: - break; michael@0: - } michael@0: - if (gDefaultNames == NULL) { michael@0: - gDefaultNames = (char**) nameList; michael@0: - } michael@0: - for (int i = 0; i < names.count(); ++i) { michael@0: - nameList[i] = names[i]; michael@0: - } michael@0: - nameList[names.count()] = NULL; michael@0: - fontInfoRecord.fNames = nameList; michael@0: - } michael@0: - } else { michael@0: - fontInfoRecord.fNames = NULL; michael@0: - } michael@0: - *fontInfo.append() = fontInfoRecord; michael@0: - } michael@0: - } michael@0: - gNumSystemFonts = fontInfo.count(); michael@0: - gSystemFonts = (FontInitRec*) malloc(gNumSystemFonts * sizeof(FontInitRec)); michael@0: - gFallbackFonts = (uint32_t*) malloc((gNumSystemFonts + 1) * sizeof(uint32_t)); michael@0: - if (gSystemFonts == NULL) { michael@0: - // shouldn't get here michael@0: - gNumSystemFonts = 0; michael@0: - } michael@0: - for (size_t i = 0; i < gNumSystemFonts; ++i) { michael@0: - gSystemFonts[i].fFileName = fontInfo[i].fFileName; michael@0: - gSystemFonts[i].fNames = fontInfo[i].fNames; michael@0: - } michael@0: - fontFamilies.deleteAll(); michael@0: -} michael@0: +/* This is sized conservatively, assuming that it will never be a size issue. michael@0: + It will be initialized in load_system_fonts(), and will be filled with the michael@0: + fontIDs that can be used for fallback consideration, in sorted order (sorted michael@0: + meaning element[0] should be used first, then element[1], etc. When we hit michael@0: + a fontID==0 in the array, the list is done, hence our allocation size is michael@0: + +1 the total number of possible system fonts. Also see NextLogicalFont(). michael@0: + */ michael@0: +static uint32_t gFallbackFonts[SK_ARRAY_COUNT(gSystemFonts)+1]; michael@0: michael@0: /* Called once (ensured by the sentinel check at the beginning of our body). michael@0: Initializes all the globals, and register the system fonts. michael@0: */ michael@0: static void load_system_fonts() { michael@0: // check if we've already be called michael@0: if (NULL != gDefaultNormal) { michael@0: return; michael@0: } michael@0: michael@0: - load_font_info(); michael@0: - michael@0: const FontInitRec* rec = gSystemFonts; michael@0: SkTypeface* firstInFamily = NULL; michael@0: int fallbackCount = 0; michael@0: michael@0: - for (size_t i = 0; i < gNumSystemFonts; i++) { michael@0: + for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) { michael@0: // if we're the first in a new family, clear firstInFamily michael@0: if (rec[i].fNames != NULL) { michael@0: firstInFamily = NULL; michael@0: } michael@0: michael@0: bool isFixedWidth; michael@0: SkString name; michael@0: SkTypeface::Style style; michael@0: michael@0: // we expect all the fonts, except the "fallback" fonts michael@0: @@ -515,120 +483,75 @@ static void load_system_fonts() { michael@0: // SkDebugf("---- adding %s as fallback[%d] fontID %d\n", michael@0: // rec[i].fFileName, fallbackCount, tf->uniqueID()); michael@0: gFallbackFonts[fallbackCount++] = tf->uniqueID(); michael@0: } michael@0: michael@0: firstInFamily = tf; michael@0: FamilyRec* family = find_family(tf); michael@0: const char* const* names = rec[i].fNames; michael@0: michael@0: // record the default family if this is it michael@0: - if (names == gDefaultNames) { michael@0: + if (names == DEFAULT_NAMES) { michael@0: gDefaultFamily = family; michael@0: } michael@0: // add the names to map to this family michael@0: while (*names) { michael@0: add_name(*names, family); michael@0: names += 1; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // do this after all fonts are loaded. This is our default font, and it michael@0: // acts as a sentinel so we only execute load_system_fonts() once michael@0: gDefaultNormal = find_best_face(gDefaultFamily, SkTypeface::kNormal); michael@0: // now terminate our fallback list with the sentinel value michael@0: gFallbackFonts[fallbackCount] = 0; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) { michael@0: - // lookup and record if the font is custom (i.e. not a system font) michael@0: - bool isCustomFont = !((FamilyTypeface*)face)->isSysFont(); michael@0: - stream->writeBool(isCustomFont); michael@0: + const char* name = ((FamilyTypeface*)face)->getUniqueString(); michael@0: michael@0: - if (isCustomFont) { michael@0: - SkStream* fontStream = ((FamilyTypeface*)face)->openStream(); michael@0: + stream->write8((uint8_t)face->style()); michael@0: michael@0: - // store the length of the custom font michael@0: - uint32_t len = fontStream->getLength(); michael@0: - stream->write32(len); michael@0: - michael@0: - // store the entire font in the serialized stream michael@0: - void* fontData = malloc(len); michael@0: - michael@0: - fontStream->read(fontData, len); michael@0: - stream->write(fontData, len); michael@0: - michael@0: - fontStream->unref(); michael@0: - free(fontData); michael@0: -// SkDebugf("--- fonthost custom serialize %d %d\n", face->style(), len); michael@0: - michael@0: + if (NULL == name || 0 == *name) { michael@0: + stream->writePackedUInt(0); michael@0: +// SkDebugf("--- fonthost serialize null\n"); michael@0: } else { michael@0: - const char* name = ((FamilyTypeface*)face)->getUniqueString(); michael@0: - michael@0: - stream->write8((uint8_t)face->style()); michael@0: - michael@0: - if (NULL == name || 0 == *name) { michael@0: - stream->writePackedUInt(0); michael@0: -// SkDebugf("--- fonthost serialize null\n"); michael@0: - } else { michael@0: - uint32_t len = strlen(name); michael@0: - stream->writePackedUInt(len); michael@0: - stream->write(name, len); michael@0: -// SkDebugf("--- fonthost serialize <%s> %d\n", name, face->style()); michael@0: - } michael@0: + uint32_t len = strlen(name); michael@0: + stream->writePackedUInt(len); michael@0: + stream->write(name, len); michael@0: +// SkDebugf("--- fonthost serialize <%s> %d\n", name, face->style()); michael@0: } michael@0: } michael@0: michael@0: SkTypeface* SkFontHost::Deserialize(SkStream* stream) { michael@0: load_system_fonts(); michael@0: michael@0: - // check if the font is a custom or system font michael@0: - bool isCustomFont = stream->readBool(); michael@0: + int style = stream->readU8(); michael@0: michael@0: - if (isCustomFont) { michael@0: + int len = stream->readPackedUInt(); michael@0: + if (len > 0) { michael@0: + SkString str; michael@0: + str.resize(len); michael@0: + stream->read(str.writable_str(), len); michael@0: michael@0: - // read the length of the custom font from the stream michael@0: - uint32_t len = stream->readU32(); michael@0: - michael@0: - // generate a new stream to store the custom typeface michael@0: - SkMemoryStream* fontStream = new SkMemoryStream(len); michael@0: - stream->read((void*)fontStream->getMemoryBase(), len); michael@0: - michael@0: - SkTypeface* face = CreateTypefaceFromStream(fontStream); michael@0: - michael@0: - fontStream->unref(); michael@0: - michael@0: -// SkDebugf("--- fonthost custom deserialize %d %d\n", face->style(), len); michael@0: - return face; michael@0: - michael@0: - } else { michael@0: - int style = stream->readU8(); michael@0: - michael@0: - int len = stream->readPackedUInt(); michael@0: - if (len > 0) { michael@0: - SkString str; michael@0: - str.resize(len); michael@0: - stream->read(str.writable_str(), len); michael@0: - michael@0: - const FontInitRec* rec = gSystemFonts; michael@0: - for (size_t i = 0; i < gNumSystemFonts; i++) { michael@0: - if (strcmp(rec[i].fFileName, str.c_str()) == 0) { michael@0: - // backup until we hit the fNames michael@0: - for (int j = i; j >= 0; --j) { michael@0: - if (rec[j].fNames != NULL) { michael@0: - return SkFontHost::CreateTypeface(NULL, michael@0: - rec[j].fNames[0], NULL, 0, michael@0: - (SkTypeface::Style)style); michael@0: - } michael@0: + const FontInitRec* rec = gSystemFonts; michael@0: + for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) { michael@0: + if (strcmp(rec[i].fFileName, str.c_str()) == 0) { michael@0: + // backup until we hit the fNames michael@0: + for (int j = i; j >= 0; --j) { michael@0: + if (rec[j].fNames != NULL) { michael@0: + return SkFontHost::CreateTypeface(NULL, michael@0: + rec[j].fNames[0], NULL, 0, (SkTypeface::Style)style); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: @@ -697,49 +620,32 @@ size_t SkFontHost::GetFileName(SkFontID michael@0: } michael@0: return size; michael@0: } else { michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) { michael@0: load_system_fonts(); michael@0: michael@0: - const SkTypeface* origTypeface = find_from_uniqueID(origFontID); michael@0: - const SkTypeface* currTypeface = find_from_uniqueID(currFontID); michael@0: - michael@0: - SkASSERT(origTypeface != 0); michael@0: - SkASSERT(currTypeface != 0); michael@0: - michael@0: - // Our fallback list always stores the id of the plain in each fallback michael@0: - // family, so we transform currFontID to its plain equivalent. michael@0: - currFontID = find_typeface(currTypeface, SkTypeface::kNormal)->uniqueID(); michael@0: - michael@0: /* First see if fontID is already one of our fallbacks. If so, return michael@0: its successor. If fontID is not in our list, then return the first one michael@0: in our list. Note: list is zero-terminated, and returning zero means michael@0: we have no more fonts to use for fallbacks. michael@0: */ michael@0: const uint32_t* list = gFallbackFonts; michael@0: for (int i = 0; list[i] != 0; i++) { michael@0: if (list[i] == currFontID) { michael@0: - if (list[i+1] == 0) michael@0: - return 0; michael@0: - const SkTypeface* nextTypeface = find_from_uniqueID(list[i+1]); michael@0: - return find_typeface(nextTypeface, origTypeface->style())->uniqueID(); michael@0: + return list[i+1]; michael@0: } michael@0: } michael@0: - michael@0: - // If we get here, currFontID was not a fallback, so we start at the michael@0: - // beginning of our list. michael@0: - const SkTypeface* firstTypeface = find_from_uniqueID(list[0]); michael@0: - return find_typeface(firstTypeface, origTypeface->style())->uniqueID(); michael@0: + return list[0]; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { michael@0: if (NULL == stream || stream->getLength() <= 0) { michael@0: return NULL; michael@0: } michael@0: michael@0: bool isFixedWidth; michael@0: @@ -754,10 +660,11 @@ SkTypeface* SkFontHost::CreateTypefaceFr michael@0: } michael@0: michael@0: SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { michael@0: SkStream* stream = SkNEW_ARGS(SkMMAPStream, (path)); michael@0: SkTypeface* face = SkFontHost::CreateTypefaceFromStream(stream); michael@0: // since we created the stream, we let go of our ref() here michael@0: stream->unref(); michael@0: return face; michael@0: } michael@0: michael@0: +///////////////////////////////////////////////////////////////////////////////