diff options
author | reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2008-12-17 15:59:43 +0000 |
---|---|---|
committer | reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2008-12-17 15:59:43 +0000 |
commit | 8a1c16ff38322f0210116fa7293eb8817c7e477e (patch) | |
tree | fe40e07f6c8983318a2f79032b9a706ede1090c1 /src/ports | |
parent | 2559c629078f738ac37095d896d580b681ac6a30 (diff) |
grab from latest android
git-svn-id: http://skia.googlecode.com/svn/trunk@27 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/ports')
25 files changed, 5519 insertions, 0 deletions
diff --git a/src/ports/SkFontHost_FONTPATH.cpp b/src/ports/SkFontHost_FONTPATH.cpp new file mode 100644 index 0000000000..3cbccaf36e --- /dev/null +++ b/src/ports/SkFontHost_FONTPATH.cpp @@ -0,0 +1,415 @@ +/* libs/graphics/ports/SkFontHost_android.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkFontHost.h" +#include "SkDescriptor.h" +#include "SkString.h" +#include "SkStream.h" +#include <stdio.h> + +/* define this if we can use mmap() to access fonts from the filesystem */ +#define SK_CAN_USE_MMAP + +#ifndef SK_FONTPATH + #define SK_FONTPATH "the complete path for a font file" +#endif + +struct FontFaceRec { + const char* fFileName; + uint8_t fFamilyIndex; + SkBool8 fBold; + SkBool8 fItalic; + + static const FontFaceRec& FindFace(const FontFaceRec rec[], int count, int isBold, int isItalic); +}; + +struct FontFamilyRec { + const FontFaceRec* fFaces; + int fFaceCount; +}; + +const FontFaceRec& FontFaceRec::FindFace(const FontFaceRec rec[], int count, int isBold, int isItalic) +{ + SkASSERT(count > 0); + + int i; + + // look for an exact match + for (i = 0; i < count; i++) { + if (rec[i].fBold == isBold && rec[i].fItalic == isItalic) + return rec[i]; + } + // look for a match in the bold field + for (i = 0; i < count; i++) { + if (rec[i].fBold == isBold) + return rec[i]; + } + // look for a normal/regular face + for (i = 0; i < count; i++) { + if (!rec[i].fBold && !rec[i].fItalic) + return rec[i]; + } + // give up + return rec[0]; +} + +enum { + DEFAULT_FAMILY_INDEX, + + FAMILY_INDEX_COUNT +}; + +static const FontFaceRec gDefaultFaces[] = { + { SK_FONTPATH, DEFAULT_FAMILY_INDEX, 0, 0 } +}; + +// This table must be in the same order as the ..._FAMILY_INDEX enum specifies +static const FontFamilyRec gFamilies[] = { + { gDefaultFaces, SK_ARRAY_COUNT(gDefaultFaces) } +}; + +#define DEFAULT_FAMILY_INDEX DEFAULT_FAMILY_INDEX +#define DEFAULT_FAMILY_FACE_INDEX 0 + +//////////////////////////////////////////////////////////////////////////////////////// + +/* map common "web" font names to our font list */ + +struct FontFamilyMatchRec { + const char* fLCName; + int fFamilyIndex; +}; + +/* This is a table of synonyms for collapsing font names + down to their pseudo-equivalents (i.e. in terms of fonts + we actually have.) + Keep this sorted by the first field so we can do a binary search. + If this gets big, we could switch to a hash... +*/ +static const FontFamilyMatchRec gMatches[] = { +#if 0 + { "Ahem", Ahem_FAMILY_INDEX }, + { "arial", SANS_FAMILY_INDEX }, + { "courier", MONO_FAMILY_INDEX }, + { "courier new", MONO_FAMILY_INDEX }, + { "cursive", SERIF_FAMILY_INDEX }, + { "fantasy", SERIF_FAMILY_INDEX }, + { "georgia", SERIF_FAMILY_INDEX }, + { "goudy", SERIF_FAMILY_INDEX }, + { "helvetica", SANS_FAMILY_INDEX }, + { "palatino", SERIF_FAMILY_INDEX }, + { "tahoma", SANS_FAMILY_INDEX }, + { "sans-serif", SANS_FAMILY_INDEX }, + { "serif", SERIF_FAMILY_INDEX }, + { "times", SERIF_FAMILY_INDEX }, + { "times new roman", SERIF_FAMILY_INDEX }, + { "verdana", SANS_FAMILY_INDEX } +#endif +}; + +//////////////////////////////////////////////////////////////////////////////////////// + +#include "SkTSearch.h" + +static bool contains_only_ascii(const char s[]) +{ + for (;;) + { + int c = *s++; + if (c == 0) + break; + if ((c >> 7) != 0) + return false; + } + return true; +} + +#define TRACE_FONT_NAME(code) +//#define TRACE_FONT_NAME(code) code + +const FontFamilyRec* find_family_rec(const char target[]) +{ + int index; + + // If we're asked for a font name that contains non-ascii, + // 1) SkStrLCSearch can't handle it + // 2) All of our fonts are have ascii names, so... + +TRACE_FONT_NAME(printf("----------------- font request <%s>", target);) + + if (contains_only_ascii(target)) + { + // Search for the font by matching the entire name + index = SkStrLCSearch(&gMatches[0].fLCName, SK_ARRAY_COUNT(gMatches), target, sizeof(gMatches[0])); + if (index >= 0) + { + TRACE_FONT_NAME(printf(" found %d\n", index);) + return &gFamilies[gMatches[index].fFamilyIndex]; + } + } + + // Sniff for key words... + +#if 0 + if (strstr(target, "sans") || strstr(target, "Sans")) + { + TRACE_FONT_NAME(printf(" found sans\n");) + return &gFamilies[SANS_FAMILY_INDEX]; + } + if (strstr(target, "serif") || strstr(target, "Serif")) + { + TRACE_FONT_NAME(printf(" found serif\n");) + return &gFamilies[SERIF_FAMILY_INDEX]; + } + if (strstr(target, "mono") || strstr(target, "Mono")) + { + TRACE_FONT_NAME(printf(" found mono\n");) + return &gFamilies[MONO_FAMILY_INDEX]; + } +#endif + + TRACE_FONT_NAME(printf(" use default\n");) + // we give up, just give them the default font + return &gFamilies[DEFAULT_FAMILY_INDEX]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static const FontFaceRec* get_default_face() +{ + return &gFamilies[DEFAULT_FAMILY_INDEX].fFaces[DEFAULT_FAMILY_FACE_INDEX]; +} + +class FontFaceRec_Typeface : public SkTypeface { +public: + FontFaceRec_Typeface(const FontFaceRec& face) : fFace(face) + { + int style = 0; + if (face.fBold) + style |= SkTypeface::kBold; + if (face.fItalic) + style |= SkTypeface::kItalic; + this->setStyle((SkTypeface::Style)style); + } + + // This global const reference completely identifies the face + const FontFaceRec& fFace; +}; + +static const FontFaceRec* get_typeface_rec(const SkTypeface* face) +{ + const FontFaceRec_Typeface* f = (FontFaceRec_Typeface*)face; + return f ? &f->fFace : get_default_face(); +} + +static uint32_t ptr2uint32(const void* p) +{ + // cast so we avoid warnings on 64bit machines that a ptr difference + // which might be 64bits is being trucated from 64 to 32 + return (uint32_t)((char*)p - (char*)0); +} + +uint32_t SkFontHost::TypefaceHash(const SkTypeface* face) +{ + // just use our address as the hash value + return ptr2uint32(get_typeface_rec(face)); +} + +bool SkFontHost::TypefaceEqual(const SkTypeface* facea, const SkTypeface* faceb) +{ + return get_typeface_rec(facea) == get_typeface_rec(faceb); +} + +SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) +{ + const FontFamilyRec* family; + + if (familyFace) + family = &gFamilies[((FontFaceRec_Typeface*)familyFace)->fFace.fFamilyIndex]; + else if (familyName) + family = find_family_rec(familyName); + else + family = &gFamilies[DEFAULT_FAMILY_INDEX]; + + const FontFaceRec& face = FontFaceRec::FindFace(family->fFaces, family->fFaceCount, + (style & SkTypeface::kBold) != 0, + (style & SkTypeface::kItalic) != 0); + + // if we're returning our input parameter, no need to create a new instance + if (familyFace != NULL && &((FontFaceRec_Typeface*)familyFace)->fFace == &face) + { + familyFace->ref(); + return (SkTypeface*)familyFace; + } + return SkNEW_ARGS(FontFaceRec_Typeface, (face)); +} + +uint32_t SkFontHost::FlattenTypeface(const SkTypeface* tface, void* buffer) +{ + const FontFaceRec* face; + + if (tface) + face = &((const FontFaceRec_Typeface*)tface)->fFace; + else + face = get_default_face(); + + size_t size = sizeof(face); + if (buffer) + memcpy(buffer, &face, size); + return size; +} + +void SkFontHost::GetDescriptorKeyString(const SkDescriptor* desc, SkString* key) +{ + key->set(SK_FONTPATH); +} + +#ifdef SK_CAN_USE_MMAP +#include <unistd.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <errno.h> + +class SkMMAPStream : public SkMemoryStream { +public: + SkMMAPStream(const char filename[]); + virtual ~SkMMAPStream(); + + virtual void setMemory(const void* data, size_t length); +private: + int fFildes; + void* fAddr; + size_t fSize; + + void closeMMap(); + + typedef SkMemoryStream INHERITED; +}; + +SkMMAPStream::SkMMAPStream(const char filename[]) +{ + fFildes = -1; // initialize to failure case + + int fildes = open(filename, O_RDONLY); + if (fildes < 0) + { + SkDEBUGF(("---- failed to open(%s) for mmap stream error=%d\n", filename, errno)); + return; + } + + off_t size = lseek(fildes, 0, SEEK_END); // find the file size + if (size == -1) + { + SkDEBUGF(("---- failed to lseek(%s) for mmap stream error=%d\n", filename, errno)); + close(fildes); + return; + } + (void)lseek(fildes, 0, SEEK_SET); // restore file offset to beginning + + void* addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fildes, 0); + if (MAP_FAILED == addr) + { + SkDEBUGF(("---- failed to mmap(%s) for mmap stream error=%d\n", filename, errno)); + close(fildes); + return; + } + + this->INHERITED::setMemory(addr, size); + + fFildes = fildes; + fAddr = addr; + fSize = size; +} + +SkMMAPStream::~SkMMAPStream() +{ + this->closeMMap(); +} + +void SkMMAPStream::setMemory(const void* data, size_t length) +{ + this->closeMMap(); + this->INHERITED::setMemory(data, length); +} + +void SkMMAPStream::closeMMap() +{ + if (fFildes >= 0) + { + munmap(fAddr, fSize); + close(fFildes); + fFildes = -1; + } +} + +#endif + +SkStream* SkFontHost::OpenDescriptorStream(const SkDescriptor* desc, const char keyString[]) +{ + // our key string IS our filename, so we can ignore desc + SkStream* strm; + +#ifdef SK_CAN_USE_MMAP + strm = new SkMMAPStream(keyString); + if (strm->getLength() > 0) + return strm; + + // strm not valid + delete strm; + // fall through to FILEStream attempt +#endif + + strm = new SkFILEStream(keyString); + if (strm->getLength() > 0) + return strm; + + // strm not valid + delete strm; + return NULL; +} + +SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec) +{ + const FontFaceRec* face = get_default_face(); + + SkAutoDescriptor ad(sizeof(rec) + sizeof(face) + SkDescriptor::ComputeOverhead(2)); + SkDescriptor* desc = ad.getDesc(); + + desc->init(); + desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); + desc->addEntry(kTypeface_SkDescriptorTag, sizeof(face), &face); + desc->computeChecksum(); + + return SkFontHost::CreateScalerContext(desc); +} + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) +{ + return 0; // nothing to do (change me if you want to limit the font cache) +} + +int SkFontHost::ComputeGammaFlag(const SkPaint& paint) +{ + return 0; +} + +void SkFontHost::GetGammaTables(const uint8_t* tables[2]) +{ + tables[0] = NULL; // black gamma (e.g. exp=1.4) + tables[1] = NULL; // white gamma (e.g. exp= 1/1.4) +} + diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp new file mode 100644 index 0000000000..5855eec1b1 --- /dev/null +++ b/src/ports/SkFontHost_FreeType.cpp @@ -0,0 +1,829 @@ +/* libs/graphics/ports/SkFontHost_FreeType.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkScalerContext.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkDescriptor.h" +#include "SkFDot6.h" +#include "SkFontHost.h" +#include "SkMask.h" +#include "SkStream.h" +#include "SkString.h" +#include "SkThread.h" +#include "SkTemplates.h" + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_SIZES_H +#ifdef FT_ADVANCES_H +#include FT_ADVANCES_H +#endif + +//#define ENABLE_GLYPH_SPEW // for tracing calls +//#define DUMP_STRIKE_CREATION + +#ifdef SK_DEBUG + #define SkASSERT_CONTINUE(pred) \ + do { \ + if (!(pred)) \ + SkDebugf("file %s:%d: assert failed '" #pred "'\n", __FILE__, __LINE__); \ + } while (false) +#else + #define SkASSERT_CONTINUE(pred) +#endif + +////////////////////////////////////////////////////////////////////////// + +struct SkFaceRec; + +static SkMutex gFTMutex; +static int gFTCount; +static FT_Library gFTLibrary; +static SkFaceRec* gFaceRecHead; + +///////////////////////////////////////////////////////////////////////// + +class SkScalerContext_FreeType : public SkScalerContext { +public: + SkScalerContext_FreeType(const SkDescriptor* desc); + virtual ~SkScalerContext_FreeType(); + +protected: + virtual unsigned generateGlyphCount() const; + virtual uint16_t generateCharToGlyph(SkUnichar uni); + virtual void generateAdvance(SkGlyph* glyph); + virtual void generateMetrics(SkGlyph* glyph); + virtual void generateImage(const SkGlyph& glyph); + virtual void generatePath(const SkGlyph& glyph, SkPath* path); + virtual void generateFontMetrics(SkPaint::FontMetrics* mx, + SkPaint::FontMetrics* my); + +private: + SkFaceRec* fFaceRec; + FT_Face fFace; // reference to shared face in gFaceRecHead + FT_Size fFTSize; // our own copy + SkFixed fScaleX, fScaleY; + FT_Matrix fMatrix22; + uint32_t fLoadGlyphFlags; + + FT_Error setupSize(); +}; + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +#include "SkStream.h" + +struct SkFaceRec { + SkFaceRec* fNext; + FT_Face fFace; + FT_StreamRec fFTStream; + SkStream* fSkStream; + uint32_t fRefCnt; + uint32_t fFontID; + + SkFaceRec(SkStream* strm, uint32_t fontID); + ~SkFaceRec() { + SkFontHost::CloseStream(fFontID, fSkStream); + } +}; + +extern "C" { + static unsigned long sk_stream_read(FT_Stream stream, + unsigned long offset, + unsigned char* buffer, + unsigned long count ) { + SkStream* str = (SkStream*)stream->descriptor.pointer; + + if (count) { + if (!str->rewind()) { + return 0; + } else { + unsigned long ret; + if (offset) { + ret = str->read(NULL, offset); + if (ret != offset) { + return 0; + } + } + ret = str->read(buffer, count); + if (ret != count) { + return 0; + } + count = ret; + } + } + return count; + } + + static void sk_stream_close( FT_Stream stream) {} +} + +SkFaceRec::SkFaceRec(SkStream* strm, uint32_t fontID) + : fSkStream(strm), fFontID(fontID) { +// SkDEBUGF(("SkFaceRec: opening %s (%p)\n", key.c_str(), strm)); + + bzero(&fFTStream, sizeof(fFTStream)); + fFTStream.size = fSkStream->getLength(); + fFTStream.descriptor.pointer = fSkStream; + fFTStream.read = sk_stream_read; + fFTStream.close = sk_stream_close; +} + +static SkFaceRec* ref_ft_face(uint32_t fontID) { + SkFaceRec* rec = gFaceRecHead; + while (rec) { + if (rec->fFontID == fontID) { + SkASSERT(rec->fFace); + rec->fRefCnt += 1; + return rec; + } + rec = rec->fNext; + } + + SkStream* strm = SkFontHost::OpenStream(fontID); + if (NULL == strm) { + SkDEBUGF(("SkFontHost::OpenStream failed opening %x\n", fontID)); + sk_throw(); + return 0; + } + + // this passes ownership of strm to the rec + rec = SkNEW_ARGS(SkFaceRec, (strm, fontID)); + + FT_Open_Args args; + memset(&args, 0, sizeof(args)); + const void* memoryBase = strm->getMemoryBase(); + + if (NULL != memoryBase) { +//printf("mmap(%s)\n", keyString.c_str()); + args.flags = FT_OPEN_MEMORY; + args.memory_base = (const FT_Byte*)memoryBase; + args.memory_size = strm->getLength(); + } else { +//printf("fopen(%s)\n", keyString.c_str()); + args.flags = FT_OPEN_STREAM; + args.stream = &rec->fFTStream; + } + + FT_Error err = FT_Open_Face(gFTLibrary, &args, 0, &rec->fFace); + + if (err) { // bad filename, try the default font + fprintf(stderr, "ERROR: unable to open font '%x'\n", fontID); + SkDELETE(rec); + sk_throw(); + return 0; + } else { + SkASSERT(rec->fFace); + //fprintf(stderr, "Opened font '%s'\n", filename.c_str()); + rec->fNext = gFaceRecHead; + gFaceRecHead = rec; + rec->fRefCnt = 1; + return rec; + } +} + +static void unref_ft_face(FT_Face face) { + SkFaceRec* rec = gFaceRecHead; + SkFaceRec* prev = NULL; + while (rec) { + SkFaceRec* next = rec->fNext; + if (rec->fFace == face) { + if (--rec->fRefCnt == 0) { + if (prev) { + prev->fNext = next; + } else { + gFaceRecHead = next; + } + FT_Done_Face(face); + SkDELETE(rec); + } + return; + } + prev = rec; + rec = next; + } + SkASSERT("shouldn't get here, face not in list"); +} + +/////////////////////////////////////////////////////////////////////////// + +SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) + : SkScalerContext(desc), fFTSize(NULL) { + SkAutoMutexAcquire ac(gFTMutex); + + FT_Error err; + + if (gFTCount == 0) { + err = FT_Init_FreeType(&gFTLibrary); +// SkDEBUGF(("FT_Init_FreeType returned %d\n", err)); + SkASSERT(err == 0); + } + ++gFTCount; + + // load the font file + fFaceRec = ref_ft_face(fRec.fFontID); + fFace = fFaceRec ? fFaceRec->fFace : NULL; + + // compute our factors from the record + + SkMatrix m; + + fRec.getSingleMatrix(&m); + +#ifdef DUMP_STRIKE_CREATION + SkString keyString; + SkFontHost::GetDescriptorKeyString(desc, &keyString); + printf("========== strike [%g %g %g] [%g %g %g %g] hints %d format %d %s\n", SkScalarToFloat(fRec.fTextSize), + SkScalarToFloat(fRec.fPreScaleX), SkScalarToFloat(fRec.fPreSkewX), + SkScalarToFloat(fRec.fPost2x2[0][0]), SkScalarToFloat(fRec.fPost2x2[0][1]), + SkScalarToFloat(fRec.fPost2x2[1][0]), SkScalarToFloat(fRec.fPost2x2[1][1]), + fRec.fHints, fRec.fMaskFormat, keyString.c_str()); +#endif + + // now compute our scale factors + SkScalar sx = m.getScaleX(); + SkScalar sy = m.getScaleY(); + + if (m.getSkewX() || m.getSkewY() || sx < 0 || sy < 0) { + // sort of give up on hinting + sx = SkMaxScalar(SkScalarAbs(sx), SkScalarAbs(m.getSkewX())); + sy = SkMaxScalar(SkScalarAbs(m.getSkewY()), SkScalarAbs(sy)); + sx = sy = SkScalarAve(sx, sy); + + SkScalar inv = SkScalarInvert(sx); + + // flip the skew elements to go from our Y-down system to FreeType's + fMatrix22.xx = SkScalarToFixed(SkScalarMul(m.getScaleX(), inv)); + fMatrix22.xy = -SkScalarToFixed(SkScalarMul(m.getSkewX(), inv)); + fMatrix22.yx = -SkScalarToFixed(SkScalarMul(m.getSkewY(), inv)); + fMatrix22.yy = SkScalarToFixed(SkScalarMul(m.getScaleY(), inv)); + } else { + fMatrix22.xx = fMatrix22.yy = SK_Fixed1; + fMatrix22.xy = fMatrix22.yx = 0; + } + + fScaleX = SkScalarToFixed(sx); + fScaleY = SkScalarToFixed(sy); + + // compute the flags we send to Load_Glyph + { + uint32_t flags = FT_LOAD_DEFAULT; + uint32_t render_flags = FT_LOAD_TARGET_NORMAL; + + // we force autohinting at the moment + + switch (fRec.fHints) { + case kNo_Hints: + flags |= FT_LOAD_NO_HINTING; + break; + case kSubpixel_Hints: + flags |= FT_LOAD_FORCE_AUTOHINT; + render_flags = FT_LOAD_TARGET_LIGHT; + break; + case kNormal_Hints: + flags |= FT_LOAD_FORCE_AUTOHINT; +#ifdef ANDROID + /* Switch to light hinting (vertical only) to address some chars + that behaved poorly with NORMAL. In the future we could consider + making this choice exposed at runtime to the caller. + */ + render_flags = FT_LOAD_TARGET_LIGHT; +#endif + break; + } + + if (SkMask::kBW_Format == fRec.fMaskFormat) + render_flags = FT_LOAD_TARGET_MONO; + else if (SkMask::kLCD_Format == fRec.fMaskFormat) + render_flags = FT_LOAD_TARGET_LCD; + + fLoadGlyphFlags = flags | render_flags; + } + + // now create the FT_Size + + { + FT_Error err; + + err = FT_New_Size(fFace, &fFTSize); + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n", + fFaceRec->fFontID, fScaleX, fScaleY, err)); + fFace = NULL; + return; + } + + err = FT_Activate_Size(fFTSize); + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", + fFaceRec->fFontID, fScaleX, fScaleY, err)); + fFTSize = NULL; + } + + err = FT_Set_Char_Size( fFace, + SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), + 72, 72); + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n", + fFaceRec->fFontID, fScaleX, fScaleY, err)); + fFace = NULL; + return; + } + + FT_Set_Transform( fFace, &fMatrix22, NULL); + } +} + +SkScalerContext_FreeType::~SkScalerContext_FreeType() { + if (fFTSize != NULL) { + FT_Done_Size(fFTSize); + } + + SkAutoMutexAcquire ac(gFTMutex); + + if (fFace != NULL) { + unref_ft_face(fFace); + } + if (--gFTCount == 0) { +// SkDEBUGF(("FT_Done_FreeType\n")); + FT_Done_FreeType(gFTLibrary); + SkDEBUGCODE(gFTLibrary = NULL;) + } +} + +/* We call this before each use of the fFace, since we may be sharing + this face with other context (at different sizes). +*/ +FT_Error SkScalerContext_FreeType::setupSize() { + /* In the off-chance that a font has been removed, we want to error out + right away, so call resolve just to be sure. + + TODO: perhaps we can skip this, by walking the global font cache and + killing all of the contexts when we know that a given fontID is going + away... + */ + if (SkFontHost::ResolveTypeface(fRec.fFontID) == NULL) { + return (FT_Error)-1; + } + + FT_Error err = FT_Activate_Size(fFTSize); + + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", + fFaceRec->fFontID, fScaleX, fScaleY, err)); + fFTSize = NULL; + } else { + // seems we need to reset this every time (not sure why, but without it + // I get random italics from some other fFTSize) + FT_Set_Transform( fFace, &fMatrix22, NULL); + } + return err; +} + +unsigned SkScalerContext_FreeType::generateGlyphCount() const { + return fFace->num_glyphs; +} + +uint16_t SkScalerContext_FreeType::generateCharToGlyph(SkUnichar uni) { + return SkToU16(FT_Get_Char_Index( fFace, uni )); +} + +static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) { + switch (format) { + case SkMask::kBW_Format: + return FT_PIXEL_MODE_MONO; + case SkMask::kLCD_Format: + return FT_PIXEL_MODE_LCD; + case SkMask::kA8_Format: + default: + return FT_PIXEL_MODE_GRAY; + } +} + +static void set_glyph_metrics_on_error(SkGlyph* glyph) { + glyph->fRsbDelta = 0; + glyph->fLsbDelta = 0; + glyph->fWidth = 0; + glyph->fHeight = 0; + glyph->fTop = 0; + glyph->fLeft = 0; + glyph->fAdvanceX = 0; + glyph->fAdvanceY = 0; +} + +void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) { +#ifdef FT_ADVANCES_H + /* unhinted and light hinted text have linearly scaled advances + * which are very cheap to compute with some font formats... + */ + { + SkAutoMutexAcquire ac(gFTMutex); + + if (this->setupSize()) { + set_glyph_metrics_on_error(glyph); + return; + } + + FT_Error error; + FT_Fixed advance; + + error = FT_Get_Advance( fFace, glyph->getGlyphID(fBaseGlyphCount), + fLoadGlyphFlags | FT_ADVANCE_FLAG_FAST_ONLY, + &advance ); + if (0 == error) { + glyph->fRsbDelta = 0; + glyph->fLsbDelta = 0; + glyph->fAdvanceX = advance; // advance *2/3; //DEBUG + glyph->fAdvanceY = 0; + return; + } + } +#endif /* FT_ADVANCES_H */ + /* otherwise, we need to load/hint the glyph, which is slower */ + this->generateMetrics(glyph); + return; +} + +void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { + SkAutoMutexAcquire ac(gFTMutex); + + glyph->fRsbDelta = 0; + glyph->fLsbDelta = 0; + + FT_Error err; + + if (this->setupSize()) { + goto ERROR; + } + + err = FT_Load_Glyph( fFace, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags ); + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::generateMetrics(%x): FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n", + fFaceRec->fFontID, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, err)); + ERROR: + set_glyph_metrics_on_error(glyph); + return; + } + + switch ( fFace->glyph->format ) { + case FT_GLYPH_FORMAT_OUTLINE: + FT_BBox bbox; + + FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox); + + if (kSubpixel_Hints == fRec.fHints) { + int dx = glyph->getSubXFixed() >> 10; + int dy = glyph->getSubYFixed() >> 10; + // negate dy since freetype-y-goes-up and skia-y-goes-down + bbox.xMin += dx; + bbox.yMin -= dy; + bbox.xMax += dx; + bbox.yMax -= dy; + } + + bbox.xMin &= ~63; + bbox.yMin &= ~63; + bbox.xMax = (bbox.xMax + 63) & ~63; + bbox.yMax = (bbox.yMax + 63) & ~63; + + glyph->fWidth = SkToU16((bbox.xMax - bbox.xMin) >> 6); + glyph->fHeight = SkToU16((bbox.yMax - bbox.yMin) >> 6); + glyph->fTop = -SkToS16(bbox.yMax >> 6); + glyph->fLeft = SkToS16(bbox.xMin >> 6); + break; + + case FT_GLYPH_FORMAT_BITMAP: + glyph->fWidth = SkToU16(fFace->glyph->bitmap.width); + glyph->fHeight = SkToU16(fFace->glyph->bitmap.rows); + glyph->fTop = -SkToS16(fFace->glyph->bitmap_top); + glyph->fLeft = SkToS16(fFace->glyph->bitmap_left); + break; + + default: + SkASSERT(!"unknown glyph format"); + goto ERROR; + } + + if (kNormal_Hints == fRec.fHints) { + glyph->fAdvanceX = SkFDot6ToFixed(fFace->glyph->advance.x); + glyph->fAdvanceY = -SkFDot6ToFixed(fFace->glyph->advance.y); + if (fRec.fFlags & kDevKernText_Flag) { + glyph->fRsbDelta = SkToS8(fFace->glyph->rsb_delta); + glyph->fLsbDelta = SkToS8(fFace->glyph->lsb_delta); + } + } else { + glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, fFace->glyph->linearHoriAdvance); + glyph->fAdvanceY = -SkFixedMul(fMatrix22.yx, fFace->glyph->linearHoriAdvance); + } + +#ifdef ENABLE_GLYPH_SPEW + SkDEBUGF(("FT_Set_Char_Size(this:%p sx:%x sy:%x ", this, fScaleX, fScaleY)); + SkDEBUGF(("Metrics(glyph:%d flags:0x%x) w:%d\n", glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, glyph->fWidth)); +#endif +} + +void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) { + SkAutoMutexAcquire ac(gFTMutex); + + FT_Error err; + + if (this->setupSize()) { + goto ERROR; + } + + err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), fLoadGlyphFlags); + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::generateImage: FT_Load_Glyph(glyph:%d width:%d height:%d rb:%d flags:%d) returned 0x%x\n", + glyph.getGlyphID(fBaseGlyphCount), glyph.fWidth, glyph.fHeight, glyph.rowBytes(), fLoadGlyphFlags, err)); + ERROR: + memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); + return; + } + + switch ( fFace->glyph->format ) { + case FT_GLYPH_FORMAT_OUTLINE: { + FT_Outline* outline = &fFace->glyph->outline; + FT_BBox bbox; + FT_Bitmap target; + + int dx = 0, dy = 0; + if (kSubpixel_Hints == fRec.fHints) { + dx = glyph.getSubXFixed() >> 10; + dy = glyph.getSubYFixed() >> 10; + // negate dy since freetype-y-goes-up and skia-y-goes-down + dy = -dy; + } + FT_Outline_Get_CBox(outline, &bbox); + /* + what we really want to do for subpixel is + offset(dx, dy) + compute_bounds + offset(bbox & !63) + but that is two calls to offset, so we do the following, which + achieves the same thing with only one offset call. + */ + FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63), + dy - ((bbox.yMin + dy) & ~63)); + + target.width = glyph.fWidth; + target.rows = glyph.fHeight; + target.pitch = glyph.rowBytes(); + target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage); + target.pixel_mode = compute_pixel_mode( + (SkMask::Format)fRec.fMaskFormat); + target.num_grays = 256; + + memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); + FT_Outline_Get_Bitmap(gFTLibrary, outline, &target); + } break; + + case FT_GLYPH_FORMAT_BITMAP: { + SkASSERT_CONTINUE(glyph.fWidth == fFace->glyph->bitmap.width); + SkASSERT_CONTINUE(glyph.fHeight == fFace->glyph->bitmap.rows); + SkASSERT_CONTINUE(glyph.fTop == -fFace->glyph->bitmap_top); + SkASSERT_CONTINUE(glyph.fLeft == fFace->glyph->bitmap_left); + + const uint8_t* src = (const uint8_t*)fFace->glyph->bitmap.buffer; + uint8_t* dst = (uint8_t*)glyph.fImage; + unsigned srcRowBytes = fFace->glyph->bitmap.pitch; + unsigned dstRowBytes = glyph.rowBytes(); + unsigned minRowBytes = SkMin32(srcRowBytes, dstRowBytes); + unsigned extraRowBytes = dstRowBytes - minRowBytes; + + for (int y = fFace->glyph->bitmap.rows - 1; y >= 0; --y) { + memcpy(dst, src, minRowBytes); + memset(dst + minRowBytes, 0, extraRowBytes); + src += srcRowBytes; + dst += dstRowBytes; + } + } break; + + default: + SkASSERT(!"unknown glyph format"); + goto ERROR; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#define ft2sk(x) SkFixedToScalar((x) << 10) + +#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 3 + #define CONST_PARAM const +#else // older freetype doesn't use const here + #define CONST_PARAM +#endif + +static int move_proc(CONST_PARAM FT_Vector* pt, void* ctx) { + SkPath* path = (SkPath*)ctx; + path->close(); // to close the previous contour (if any) + path->moveTo(ft2sk(pt->x), -ft2sk(pt->y)); + return 0; +} + +static int line_proc(CONST_PARAM FT_Vector* pt, void* ctx) { + SkPath* path = (SkPath*)ctx; + path->lineTo(ft2sk(pt->x), -ft2sk(pt->y)); + return 0; +} + +static int quad_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1, + void* ctx) { + SkPath* path = (SkPath*)ctx; + path->quadTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x), -ft2sk(pt1->y)); + return 0; +} + +static int cubic_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1, + CONST_PARAM FT_Vector* pt2, void* ctx) { + SkPath* path = (SkPath*)ctx; + path->cubicTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x), + -ft2sk(pt1->y), ft2sk(pt2->x), -ft2sk(pt2->y)); + return 0; +} + +void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph, + SkPath* path) { + SkAutoMutexAcquire ac(gFTMutex); + + SkASSERT(&glyph && path); + + if (this->setupSize()) { + path->reset(); + return; + } + + uint32_t flags = fLoadGlyphFlags; + flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline + flags &= ~FT_LOAD_RENDER; // don't scan convert (we just want the outline) + + FT_Error err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), flags); + + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n", + glyph.getGlyphID(fBaseGlyphCount), flags, err)); + path->reset(); + return; + } + + FT_Outline_Funcs funcs; + + funcs.move_to = move_proc; + funcs.line_to = line_proc; + funcs.conic_to = quad_proc; + funcs.cubic_to = cubic_proc; + funcs.shift = 0; + funcs.delta = 0; + + err = FT_Outline_Decompose(&fFace->glyph->outline, &funcs, path); + + if (err != 0) { + SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n", + glyph.getGlyphID(fBaseGlyphCount), flags, err)); + path->reset(); + return; + } + + path->close(); +} + +void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx, + SkPaint::FontMetrics* my) { + if (NULL == mx && NULL == my) { + return; + } + + SkAutoMutexAcquire ac(gFTMutex); + + if (this->setupSize()) { + if (mx) { + bzero(mx, sizeof(SkPaint::FontMetrics)); + } + if (my) { + bzero(my, sizeof(SkPaint::FontMetrics)); + } + return; + } + + SkPoint pts[5]; + SkFixed ys[5]; + FT_Face face = fFace; + int upem = face->units_per_EM; + SkFixed scaleY = fScaleY; + SkFixed mxy = fMatrix22.xy; + SkFixed myy = fMatrix22.yy; + + int leading = face->height - face->ascender + face->descender; + if (leading < 0) { + leading = 0; + } + + ys[0] = -face->bbox.yMax; + ys[1] = -face->ascender; + ys[2] = -face->descender; + ys[3] = -face->bbox.yMin; + ys[4] = leading; + + // convert upem-y values into scalar points + for (int i = 0; i < 5; i++) { + SkFixed y = SkMulDiv(scaleY, ys[i], upem); + SkFixed x = SkFixedMul(mxy, y); + y = SkFixedMul(myy, y); + pts[i].set(SkFixedToScalar(x), SkFixedToScalar(y)); + } + + if (mx) { + mx->fTop = pts[0].fX; + mx->fAscent = pts[1].fX; + mx->fDescent = pts[2].fX; + mx->fBottom = pts[3].fX; + mx->fLeading = pts[4].fX; + } + if (my) { + my->fTop = pts[0].fY; + my->fAscent = pts[1].fY; + my->fDescent = pts[2].fY; + my->fBottom = pts[3].fY; + my->fLeading = pts[4].fY; + } +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) { + return SkNEW_ARGS(SkScalerContext_FreeType, (desc)); +} + +/////////////////////////////////////////////////////////////////////////////// + +/* Export this so that other parts of our FonttHost port can make use of our + ability to extract the name+style from a stream, using FreeType's api. +*/ +SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name) { + FT_Library library; + if (FT_Init_FreeType(&library)) { + name->set(NULL); + return SkTypeface::kNormal; + } + + FT_Open_Args args; + memset(&args, 0, sizeof(args)); + + const void* memoryBase = stream->getMemoryBase(); + FT_StreamRec streamRec; + + if (NULL != memoryBase) { + args.flags = FT_OPEN_MEMORY; + args.memory_base = (const FT_Byte*)memoryBase; + args.memory_size = stream->getLength(); + } else { + memset(&streamRec, 0, sizeof(streamRec)); + streamRec.size = stream->read(NULL, 0); + streamRec.descriptor.pointer = stream; + streamRec.read = sk_stream_read; + streamRec.close = sk_stream_close; + + args.flags = FT_OPEN_STREAM; + args.stream = &streamRec; + } + + FT_Face face; + if (FT_Open_Face(library, &args, 0, &face)) { + FT_Done_FreeType(library); + name->set(NULL); + return SkTypeface::kNormal; + } + + name->set(face->family_name); + int style = SkTypeface::kNormal; + + if (face->style_flags & FT_STYLE_FLAG_BOLD) { + style |= SkTypeface::kBold; + } + if (face->style_flags & FT_STYLE_FLAG_ITALIC) { + style |= SkTypeface::kItalic; + } + + FT_Done_Face(face); + FT_Done_FreeType(library); + return (SkTypeface::Style)style; +} + diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp new file mode 100644 index 0000000000..0922e7b35e --- /dev/null +++ b/src/ports/SkFontHost_android.cpp @@ -0,0 +1,637 @@ +/* libs/graphics/ports/SkFontHost_android.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkFontHost.h" +#include "SkDescriptor.h" +#include "SkMMapStream.h" +#include "SkPaint.h" +#include "SkString.h" +#include "SkStream.h" +#include "SkThread.h" +#include "SkTSearch.h" +#include <stdio.h> + +#define FONT_CACHE_MEMORY_BUDGET (768 * 1024) + +#ifndef SK_FONT_FILE_PREFIX + #define SK_FONT_FILE_PREFIX "/fonts/" +#endif + +SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name); + +static void GetFullPathForSysFonts(SkString* full, const char name[]) +{ + full->set(getenv("ANDROID_ROOT")); + full->append(SK_FONT_FILE_PREFIX); + full->append(name); +} + +/////////////////////////////////////////////////////////////////////////////// + +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 +static SkMutex 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 + SkASSERT(!"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* resolve_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; + } + SkASSERT(!"Yikes, couldn't find family in our list to remove/delete"); +} + +static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) +{ + NameFamilyPair* list = gNameList.begin(); + int count = gNameList.count(); + + int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0])); + + if (index >= 0) { + return find_best_face(list[index].fFamily, style); + } + return 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, SkTypeface* familyMember) + : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) + { + fIsSysFont = sysFont; + + SkAutoMutexAcquire ac(gFamilyMutex); + + FamilyRec* rec = NULL; + if (familyMember) { + rec = find_family(familyMember); + SkASSERT(rec); + } else { + rec = SkNEW(FamilyRec); + } + rec->fFaces[style] = this; + } + + 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; } + + virtual SkStream* openStream() = 0; + virtual void closeStream(SkStream*) = 0; + virtual const char* getUniqueString() const = 0; + +private: + bool fIsSysFont; + + typedef SkTypeface INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class StreamTypeface : public FamilyTypeface { +public: + StreamTypeface(Style style, bool sysFont, SkTypeface* familyMember, + SkStream* stream) + : INHERITED(style, sysFont, familyMember) + { + fStream = stream; + } + virtual ~StreamTypeface() + { + SkDELETE(fStream); + } + + // overrides + virtual SkStream* openStream() { return fStream; } + virtual void closeStream(SkStream*) {} + virtual const char* getUniqueString() const { return NULL; } + +private: + SkStream* fStream; + + typedef FamilyTypeface INHERITED; +}; + +class FileTypeface : public FamilyTypeface { +public: + FileTypeface(Style style, bool sysFont, SkTypeface* familyMember, + const char path[]) + : INHERITED(style, sysFont, familyMember) + { + SkString fullpath; + + if (sysFont) { + GetFullPathForSysFonts(&fullpath, path); + path = fullpath.c_str(); + } + fPath.set(path); + } + + // overrides + virtual SkStream* openStream() + { + SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str())); + + // check for failure + if (stream->getLength() <= 0) { + SkDELETE(stream); + // maybe MMAP isn't supported. try FILE + stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str())); + if (stream->getLength() <= 0) { + SkDELETE(stream); + stream = NULL; + } + } + return stream; + } + virtual void closeStream(SkStream* stream) + { + SkDELETE(stream); + } + virtual const char* getUniqueString() const { + 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) +{ + SkString fullpath; + GetFullPathForSysFonts(&fullpath, path); + + SkMMAPStream stream(fullpath.c_str()); + if (stream.getLength() > 0) { + *style = find_name_and_style(&stream, name); + return true; + } + else { + SkFILEStream stream(fullpath.c_str()); + if (stream.getLength() > 0) { + *style = find_name_and_style(&stream, name); + return true; + } + } + + SkDebugf("---- failed to open <%s> as a font\n", fullpath.c_str()); + return false; +} + +struct FontInitRec { + const char* fFileName; + const char* const* fNames; // null-terminated list +}; + +static const char* gSansNames[] = { + "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL +}; + +static const char* gSerifNames[] = { + "serif", "times", "times new roman", "palatino", "georgia", "baskerville", + "goudy", "fantasy", "cursive", "ITC Stone Serif", NULL +}; + +static const char* gMonoNames[] = { + "monospace", "courier", "courier new", "monaco", NULL +}; + +static const char* gFBNames[] = { NULL }; + +/* Fonts must be grouped by family, with the first font in a family having the + list of names (even if that list is empty), and the following members having + null for the list. The names list must be NULL-terminated +*/ +static const FontInitRec gSystemFonts[] = { + { "DroidSans.ttf", gSansNames }, + { "DroidSans-Bold.ttf", NULL }, + { "DroidSerif-Regular.ttf", gSerifNames }, + { "DroidSerif-Bold.ttf", NULL }, + { "DroidSerif-Italic.ttf", NULL }, + { "DroidSerif-BoldItalic.ttf", NULL }, + { "DroidSansMono.ttf", gMonoNames }, +#ifdef NO_FALLBACK_FONT + { "DroidSans.ttf", gFBNames } +#else + { "DroidSansFallback.ttf", gFBNames } +#endif +}; + +#define DEFAULT_NAMES gSansNames + +// these globals are assigned (once) by load_system_fonts() +static SkTypeface* gFallBackTypeface; +static FamilyRec* gDefaultFamily; +static SkTypeface* gDefaultNormal; + +static void load_system_fonts() +{ + // check if we've already be called + if (NULL != gDefaultNormal) { + return; + } + + const FontInitRec* rec = gSystemFonts; + SkTypeface* firstInFamily = NULL; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) { + // if we're the first in a new family, clear firstInFamily + if (rec[i].fNames != NULL) { + firstInFamily = NULL; + } + + SkString name; + SkTypeface::Style style; + + if (!get_name_and_style(rec[i].fFileName, &name, &style)) { + SkDebugf("------ can't load <%s> as a font\n", rec[i].fFileName); + continue; + } + + SkTypeface* tf = SkNEW_ARGS(FileTypeface, + (style, + true, // system-font (cannot delete) + firstInFamily, // what family to join + rec[i].fFileName) // filename + ); + + if (rec[i].fNames != NULL) { + firstInFamily = tf; + const char* const* names = rec[i].fNames; + + // record the fallback if this is it + if (names == gFBNames) { + gFallBackTypeface = tf; + } + // record the default family if this is it + if (names == DEFAULT_NAMES) { + gDefaultFamily = find_family(tf); + } + // add the names to map to this family + FamilyRec* family = find_family(tf); + while (*names) { + add_name(*names, family); + names += 1; + } + } + + } + + // 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 + gDefaultNormal = find_best_face(gDefaultFamily, SkTypeface::kNormal); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) { + const char* name = ((FamilyTypeface*)face)->getUniqueString(); + + stream->write8((uint8_t)face->getStyle()); + + if (NULL == name || 0 == *name) { + stream->writePackedUInt(0); +// SkDebugf("--- fonthost serialize null\n"); + } else { + uint32_t len = strlen(name); + stream->writePackedUInt(len); + stream->write(name, len); +// SkDebugf("--- fonthost serialize <%s> %d\n", name, face->getStyle()); + } +} + +SkTypeface* SkFontHost::Deserialize(SkStream* stream) { + load_system_fonts(); + + int style = stream->readU8(); + + int len = stream->readPackedUInt(); + if (len > 0) { + SkString str; + str.resize(len); + stream->read(str.writable_str(), len); + + const FontInitRec* rec = gSystemFonts; + for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) { + if (strcmp(rec[i].fFileName, str.c_str()) == 0) { + // backup until we hit the fNames + for (int j = i; j >= 0; --j) { + if (rec[j].fNames != NULL) { + return SkFontHost::FindTypeface(NULL, rec[j].fNames[0], + (SkTypeface::Style)style); + } + } + } + } + } + return SkFontHost::FindTypeface(NULL, NULL, (SkTypeface::Style)style); +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkFontHost::FindTypeface(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); + } + + return tf; +} + +SkTypeface* SkFontHost::ResolveTypeface(uint32_t fontID) +{ + SkAutoMutexAcquire ac(gFamilyMutex); + + return resolve_uniqueID(fontID); +} + +SkStream* SkFontHost::OpenStream(uint32_t fontID) +{ + + FamilyTypeface* tf = (FamilyTypeface*)SkFontHost::ResolveTypeface(fontID); + SkStream* stream = tf ? tf->openStream() : NULL; + + if (NULL == stream || stream->getLength() == 0) { + delete stream; + stream = NULL; + } + return stream; +} + +void SkFontHost::CloseStream(uint32_t fontID, SkStream* stream) +{ + FamilyTypeface* tf = (FamilyTypeface*)SkFontHost::ResolveTypeface(fontID); + if (NULL != tf) { + tf->closeStream(stream); + } +} + +SkScalerContext* SkFontHost::CreateFallbackScalerContext( + const SkScalerContext::Rec& rec) +{ + load_system_fonts(); + + SkAutoDescriptor ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1)); + SkDescriptor* desc = ad.getDesc(); + + desc->init(); + SkScalerContext::Rec* newRec = + (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, + sizeof(rec), &rec); + newRec->fFontID = gFallBackTypeface->uniqueID(); + desc->computeChecksum(); + + return SkFontHost::CreateScalerContext(desc); +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) +{ + if (NULL == stream || stream->getLength() <= 0) { + SkDELETE(stream); + return NULL; + } + + SkString name; + SkTypeface::Style style = find_name_and_style(stream, &name); + + return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream)); +} + +/////////////////////////////////////////////////////////////////////////////// + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) +{ + if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET) + return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET; + else + return 0; // nothing to do +} + diff --git a/src/ports/SkFontHost_ascender.cpp b/src/ports/SkFontHost_ascender.cpp new file mode 100644 index 0000000000..2148850f5b --- /dev/null +++ b/src/ports/SkFontHost_ascender.cpp @@ -0,0 +1,211 @@ +#include "SkScalerContext.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkDescriptor.h" +#include "SkFDot6.h" +#include "SkFontHost.h" +#include "SkMask.h" +#include "SkStream.h" +#include "SkString.h" +#include "SkThread.h" +#include "SkTemplates.h" + +#include <acaapi.h> + +////////////////////////////////////////////////////////////////////////// + +#include "SkMMapStream.h" + +class SkScalerContext_Ascender : public SkScalerContext { +public: + SkScalerContext_Ascender(const SkDescriptor* desc); + virtual ~SkScalerContext_Ascender(); + +protected: + virtual unsigned generateGlyphCount() const; + virtual uint16_t generateCharToGlyph(SkUnichar uni); + virtual void generateMetrics(SkGlyph* glyph); + virtual void generateImage(const SkGlyph& glyph); + virtual void generatePath(const SkGlyph& glyph, SkPath* path); + virtual void generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my); + +private: + aca_FontHandle fHandle; + void* fWorkspace; + void* fGlyphWorkspace; + SkStream* fFontStream; + SkStream* fHintStream; +}; + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +SkScalerContext_Ascender::SkScalerContext_Ascender(const SkDescriptor* desc) + : SkScalerContext(desc) +{ + int size = aca_Get_FontHandleRec_Size(); + fHandle = (aca_FontHandle)sk_malloc_throw(size); + + // get the pointer to the font + + fFontStream = new SkMMAPStream("/UcsGB2312-Hei-H.FDL"); + fHintStream = new SkMMAPStream("/genv6-23.bin"); + + void* hints = sk_malloc_throw(fHintStream->getLength()); + memcpy(hints, fHintStream->getMemoryBase(), fHintStream->getLength()); + + aca_Create_Font_Handle(fHandle, + (void*)fFontStream->getMemoryBase(), fFontStream->getLength(), + "fred", + hints, fHintStream->getLength()); + + // compute our factors from the record + + SkMatrix m; + + fRec.getSingleMatrix(&m); + + // now compute our scale factors + SkScalar sx = m.getScaleX(); + SkScalar sy = m.getScaleY(); + + int ppemX = SkScalarRound(sx); + int ppemY = SkScalarRound(sy); + + size = aca_Find_Font_Memory_Required(fHandle, ppemX, ppemY); + size *= 8; // Jeff suggests this :) + fWorkspace = sk_malloc_throw(size); + aca_Set_Font_Memory(fHandle, (uint8_t*)fWorkspace, size); + + aca_GlyphAttribsRec rec; + + memset(&rec, 0, sizeof(rec)); + rec.xSize = ppemX; + rec.ySize = ppemY; + rec.doAdjust = true; + rec.doExceptions = true; + rec.doGlyphHints = true; + rec.doInterpolate = true; + rec.grayMode = 2; + aca_Set_Font_Attributes(fHandle, &rec, &size); + + fGlyphWorkspace = sk_malloc_throw(size); + aca_Set_Glyph_Memory(fHandle, fGlyphWorkspace); +} + +SkScalerContext_Ascender::~SkScalerContext_Ascender() +{ + delete fHintStream; + delete fFontStream; + sk_free(fGlyphWorkspace); + sk_free(fWorkspace); + sk_free(fHandle); +} + +unsigned SkScalerContext_Ascender::generateGlyphCount() const +{ + return 1000; +} + +uint16_t SkScalerContext_Ascender::generateCharToGlyph(SkUnichar uni) +{ + return (uint16_t)(uni & 0xFFFF); +} + +void SkScalerContext_Ascender::generateMetrics(SkGlyph* glyph) +{ + glyph->fRsbDelta = 0; + glyph->fLsbDelta = 0; + + aca_GlyphImageRec rec; + aca_Vector topLeft; + + int adv = aca_Get_Adv_Width(fHandle, glyph->getGlyphID()); + if (aca_GLYPH_NOT_PRESENT == adv) + goto ERROR; + + aca_Rasterize(glyph->getGlyphID(), fHandle, &rec, &topLeft); + + if (false) // error + { +ERROR: + glyph->fWidth = 0; + glyph->fHeight = 0; + glyph->fTop = 0; + glyph->fLeft = 0; + glyph->fAdvanceX = 0; + glyph->fAdvanceY = 0; + return; + } + + glyph->fWidth = rec.width; + glyph->fHeight = rec.rows; + glyph->fRowBytes = rec.width; + glyph->fTop = -topLeft.y; + glyph->fLeft = topLeft.x; + glyph->fAdvanceX = SkIntToFixed(adv); + glyph->fAdvanceY = SkIntToFixed(0); +} + +void SkScalerContext_Ascender::generateImage(const SkGlyph& glyph) +{ + aca_GlyphImageRec rec; + aca_Vector topLeft; + + aca_Rasterize(glyph.getGlyphID(), fHandle, &rec, &topLeft); + + const uint8_t* src = (const uint8_t*)rec.buffer; + uint8_t* dst = (uint8_t*)glyph.fImage; + int height = glyph.fHeight; + + src += rec.y0 * rec.pitch + rec.x0; + while (--height >= 0) + { + memcpy(dst, src, glyph.fWidth); + src += rec.pitch; + dst += glyph.fRowBytes; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +void SkScalerContext_Ascender::generatePath(const SkGlyph& glyph, SkPath* path) +{ + SkRect r; + + r.set(0, 0, SkIntToScalar(4), SkIntToScalar(4)); + path->reset(); + path->addRect(r); +} + +void SkScalerContext_Ascender::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) +{ + if (NULL == mx && NULL == my) + return; + + if (mx) + { + mx->fTop = SkIntToScalar(-16); + mx->fAscent = SkIntToScalar(-16); + mx->fDescent = SkIntToScalar(4); + mx->fBottom = SkIntToScalar(4); + mx->fLeading = 0; + } + if (my) + { + my->fTop = SkIntToScalar(-16); + my->fAscent = SkIntToScalar(-16); + my->fDescent = SkIntToScalar(4); + my->fBottom = SkIntToScalar(4); + my->fLeading = 0; + } +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) +{ + return SkNEW_ARGS(SkScalerContext_Ascender, (desc)); +} + diff --git a/src/ports/SkFontHost_gamma.cpp b/src/ports/SkFontHost_gamma.cpp new file mode 100644 index 0000000000..0b95bce1d6 --- /dev/null +++ b/src/ports/SkFontHost_gamma.cpp @@ -0,0 +1,118 @@ +#include "SkFontHost.h" +#include <math.h> + +// define this to use pre-compiled tables for gamma. This is slightly faster, +// and doesn't create any RW global memory, but means we cannot change the +// gamma at runtime. +#define USE_PREDEFINED_GAMMA_TABLES + +#ifndef USE_PREDEFINED_GAMMA_TABLES + // define this if you want to spew out the "C" code for the tables, given + // the current values for SK_BLACK_GAMMA and SK_WHITE_GAMMA. + #define DUMP_GAMMA_TABLESx +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef USE_PREDEFINED_GAMMA_TABLES + +#include "sk_predefined_gamma.h" + +#else // use writable globals for gamma tables + +static bool gGammaIsBuilt; +static uint8_t gBlackGamma[256], gWhiteGamma[256]; + +#define SK_BLACK_GAMMA (1.4f) +#define SK_WHITE_GAMMA (1/1.4f) + +static void build_power_table(uint8_t table[], float ee) +{ + // printf("------ build_power_table %g\n", ee); + for (int i = 0; i < 256; i++) + { + float x = i / 255.f; + // printf(" %d %g", i, x); + x = powf(x, ee); + // printf(" %g", x); + int xx = SkScalarRound(SkFloatToScalar(x * 255)); + // printf(" %d\n", xx); + table[i] = SkToU8(xx); + } +} + +#ifdef DUMP_GAMMA_TABLES + +#include "SkString.h" + +static void dump_a_table(const char name[], const uint8_t table[], + float gamma) { + SkDebugf("\n"); + SkDebugf("\/\/ Gamma table for %g\n", gamma); + SkDebugf("static const uint8_t %s[] = {\n", name); + for (int y = 0; y < 16; y++) { + SkString line, tmp; + for (int x = 0; x < 16; x++) { + tmp.printf("0x%02X, ", *table++); + line.append(tmp); + } + SkDebugf(" %s\n", line.c_str()); + } + SkDebugf("};\n"); +} + +#endif + +#endif + +/////////////////////////////////////////////////////////////////////////////// + +void SkFontHost::GetGammaTables(const uint8_t* tables[2]) +{ +#ifndef USE_PREDEFINED_GAMMA_TABLES + if (!gGammaIsBuilt) + { + build_power_table(gBlackGamma, SK_BLACK_GAMMA); + build_power_table(gWhiteGamma, SK_WHITE_GAMMA); + gGammaIsBuilt = true; + +#ifdef DUMP_GAMMA_TABLES + dump_a_table("gBlackGamma", gBlackGamma, SK_BLACK_GAMMA); + dump_a_table("gWhiteGamma", gWhiteGamma, SK_WHITE_GAMMA); +#endif + } +#endif + tables[0] = gBlackGamma; + tables[1] = gWhiteGamma; +} + +// If the luminance is <= this value, then apply the black gamma table +#define BLACK_GAMMA_THRESHOLD 0x40 + +// If the luminance is >= this value, then apply the white gamma table +#define WHITE_GAMMA_THRESHOLD 0xC0 + +int SkFontHost::ComputeGammaFlag(const SkPaint& paint) +{ + if (paint.getShader() == NULL) + { + SkColor c = paint.getColor(); + int r = SkColorGetR(c); + int g = SkColorGetG(c); + int b = SkColorGetB(c); + int luminance = (r * 2 + g * 5 + b) >> 3; + + if (luminance <= BLACK_GAMMA_THRESHOLD) + { + // printf("------ black gamma for [%d %d %d]\n", r, g, b); + return SkScalerContext::kGammaForBlack_Flag; + } + if (luminance >= WHITE_GAMMA_THRESHOLD) + { + // printf("------ white gamma for [%d %d %d]\n", r, g, b); + return SkScalerContext::kGammaForWhite_Flag; + } + } + return 0; +} + diff --git a/src/ports/SkFontHost_linux.cpp b/src/ports/SkFontHost_linux.cpp new file mode 100644 index 0000000000..f75718d5d4 --- /dev/null +++ b/src/ports/SkFontHost_linux.cpp @@ -0,0 +1,604 @@ +/* libs/graphics/ports/SkFontHost_android.cpp + ** + ** Copyright 2006, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include "SkFontHost.h" +#include "SkDescriptor.h" +#include "SkMMapStream.h" +#include "SkOSFile.h" +#include "SkPaint.h" +#include "SkString.h" +#include "SkStream.h" +#include "SkThread.h" +#include "SkTSearch.h" +#include <stdio.h> + +#define FONT_CACHE_MEMORY_BUDGET (1 * 1024 * 1024) + +#ifndef SK_FONT_FILE_PREFIX + #define SK_FONT_FILE_PREFIX "/usr/share/fonts/truetype/msttcorefonts/" +#endif + +SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name); + +static void GetFullPathForSysFonts(SkString* full, const char name[]) +{ + full->append(SK_FONT_FILE_PREFIX); + full->append(name); +} + +/////////////////////////////////////////////////////////////////////////////// + +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 +static SkMutex 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 + SkASSERT(!"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* resolve_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; + } + SkASSERT(!"Yikes, couldn't find family in our list to remove/delete"); +} + +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) + : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) + { + 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; } + + virtual SkStream* openStream() = 0; + virtual void closeStream(SkStream*) = 0; + virtual const char* getUniqueString() const = 0; + +private: + FamilyRec* fFamilyRec; // we don't own this, just point to it + bool fIsSysFont; + + typedef SkTypeface INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class StreamTypeface : public FamilyTypeface { +public: + StreamTypeface(Style style, bool sysFont, FamilyRec* family, + SkStream* stream) + : INHERITED(style, sysFont, family) + { + fStream = stream; + } + virtual ~StreamTypeface() + { + SkDELETE(fStream); + } + + // overrides + virtual SkStream* openStream() { return fStream; } + virtual void closeStream(SkStream*) {} + virtual const char* getUniqueString() const { return NULL; } + +private: + SkStream* fStream; + + typedef FamilyTypeface INHERITED; +}; + +class FileTypeface : public FamilyTypeface { +public: + FileTypeface(Style style, bool sysFont, FamilyRec* family, + const char path[]) + : INHERITED(style, sysFont, family) { + fPath.set(path); + } + + // overrides + virtual SkStream* openStream() + { + SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str())); + + // check for failure + if (stream->getLength() <= 0) { + SkDELETE(stream); + // maybe MMAP isn't supported. try FILE + stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str())); + if (stream->getLength() <= 0) { + SkDELETE(stream); + stream = NULL; + } + } + return stream; + } + virtual void closeStream(SkStream* stream) + { + SkDELETE(stream); + } + virtual const char* getUniqueString() const { + 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) +{ + SkMMAPStream stream(path); + if (stream.getLength() > 0) { + *style = find_name_and_style(&stream, name); + return true; + } + else { + SkFILEStream stream(path); + if (stream.getLength() > 0) { + *style = find_name_and_style(&stream, name); + return true; + } + } + + 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_system_fonts() +{ + // check if we've already be called + if (NULL != gDefaultNormal) { + return; + } + + SkOSFile::Iter iter(SK_FONT_FILE_PREFIX, ".ttf"); + SkString name; + + while (iter.next(&name, false)) { + SkString filename; + GetFullPathForSysFonts(&filename, name.c_str()); +// while (filename.size() == 0) { filename.set("/usr/share/fonts/truetype/msttcorefonts/Arial.ttf"); + + SkString realname; + SkTypeface::Style style; + + if (!get_name_and_style(filename.c_str(), &realname, &style)) { + SkDebugf("------ can't load <%s> as a font\n", filename.c_str()); + continue; + } + +// SkDebugf("font: <%s> %d <%s>\n", realname.c_str(), style, filename.c_str()); + + FamilyRec* family = find_familyrec(realname.c_str()); + // 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()) // filename + ); + + if (NULL == family) { + add_name(realname.c_str(), tf->getFamily()); + } + } + + // 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); + +// SkDebugf("---- default %p head %p family %p\n", gDefaultNormal, gFamilyHead, gDefaultFamily); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) { +#if 0 + const char* name = ((FamilyTypeface*)face)->getUniqueString(); + + stream->write8((uint8_t)face->getStyle()); + + if (NULL == name || 0 == *name) { + stream->writePackedUInt(0); + // SkDebugf("--- fonthost serialize null\n"); + } else { + uint32_t len = strlen(name); + stream->writePackedUInt(len); + stream->write(name, len); + // SkDebugf("--- fonthost serialize <%s> %d\n", name, face->getStyle()); + } +#endif + sk_throw(); +} + +SkTypeface* SkFontHost::Deserialize(SkStream* stream) { +#if 0 + load_system_fonts(); + + int style = stream->readU8(); + + int len = stream->readPackedUInt(); + if (len > 0) { + SkString str; + str.resize(len); + stream->read(str.writable_str(), len); + + const FontInitRec* rec = gSystemFonts; + for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) { + if (strcmp(rec[i].fFileName, str.c_str()) == 0) { + // backup until we hit the fNames + for (int j = i; j >= 0; --j) { + if (rec[j].fNames != NULL) { + return SkFontHost::FindTypeface(NULL, rec[j].fNames[0], + (SkTypeface::Style)style); + } + } + } + } + } + return SkFontHost::FindTypeface(NULL, NULL, (SkTypeface::Style)style); +#endif + sk_throw(); +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkFontHost::FindTypeface(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); + } + + return tf; +} + +SkTypeface* SkFontHost::ResolveTypeface(uint32_t fontID) +{ + SkAutoMutexAcquire ac(gFamilyMutex); + + return resolve_uniqueID(fontID); +} + +SkStream* SkFontHost::OpenStream(uint32_t fontID) +{ + + FamilyTypeface* tf = (FamilyTypeface*)SkFontHost::ResolveTypeface(fontID); + SkStream* stream = tf ? tf->openStream() : NULL; + + if (NULL == stream || stream->getLength() == 0) { + delete stream; + stream = NULL; + } + return stream; +} + +void SkFontHost::CloseStream(uint32_t fontID, SkStream* stream) +{ + FamilyTypeface* tf = (FamilyTypeface*)SkFontHost::ResolveTypeface(fontID); + if (NULL != tf) { + tf->closeStream(stream); + } +} + +SkScalerContext* SkFontHost::CreateFallbackScalerContext( + const SkScalerContext::Rec& rec) +{ + load_system_fonts(); + + SkAutoDescriptor ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1)); + SkDescriptor* desc = ad.getDesc(); + + desc->init(); + SkScalerContext::Rec* newRec = + (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, + sizeof(rec), &rec); + newRec->fFontID = gFallBackTypeface->uniqueID(); + desc->computeChecksum(); + + return SkFontHost::CreateScalerContext(desc); +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) +{ + if (NULL == stream || stream->getLength() <= 0) { + SkDELETE(stream); + return NULL; + } + + SkString name; + SkTypeface::Style style = find_name_and_style(stream, &name); + + return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream)); +} + +/////////////////////////////////////////////////////////////////////////////// + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) +{ + if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET) + return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET; + else + return 0; // nothing to do +} + diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp new file mode 100755 index 0000000000..af44cf5266 --- /dev/null +++ b/src/ports/SkFontHost_mac.cpp @@ -0,0 +1,563 @@ +/*
+ ** Copyright 2006, The Android Open Source Project + **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+*/
+
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+
+// Give 1MB font cache budget
+#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024)
+
+const char* gDefaultfont = "Arial"; // hard code for now
+static SkMutex gFTMutex;
+
+inline SkPoint F32PtToSkPoint(const Float32Point p)
+{
+ SkPoint sp = { SkFloatToFixed(p.x),SkFloatToFixed(p.y) };
+ return sp;
+}
+
+static inline uint32_t _rotl(uint32_t v, uint32_t r)
+{
+ return (v << r | v >> (32 - r));
+}
+
+// This will generate a unique ID based on the fontname + fontstyle
+// and also used by upper layer
+uint32_t FontFaceChecksum(const char *name,SkTypeface::Style style)
+{
+ if (!name) return style;
+
+ char* q = (char*)name;
+
+ // From "Performance in Practice of String Hashing Functions"
+ // Ramakrishna & Zobel
+ const uint32_t L = 5;
+ const uint32_t R = 2;
+
+ uint32_t h = 0x12345678;
+ while (*q) {
+ uint32_t ql = tolower(*q);
+ h ^= ((h << L) + (h >> R) + ql);
+ q ++;
+ }
+
+ // add style
+ h = _rotl(h, 3) ^ style;
+
+ return h;
+}
+
+#pragma mark -
+struct SkFaceRec {
+ SkFaceRec* fNext;
+ uint32_t fRefCnt;
+ ATSUFontID fFontID;
+ ATSUStyle fStyle;
+
+ SkFaceRec() : fFontID(0), fRefCnt(0), fStyle(NULL) {};
+
+ ~SkFaceRec() {
+ if (fStyle) {
+ ::ATSUDisposeStyle(fStyle);
+ fStyle = NULL;
+ }
+ }
+
+ uint32_t ref() {
+ return ++fRefCnt;
+ }
+};
+
+// Font Face list
+static SkFaceRec* gFaceRecHead = NULL;
+
+static SkFaceRec* find_ft_face(const ATSUFontID fontID) {
+ SkFaceRec* rec = gFaceRecHead;
+ while (rec) {
+ if (rec->fFontID == fontID) {
+ return rec;
+ }
+ rec = rec->fNext;
+ }
+
+ return NULL;
+}
+
+static SkFaceRec* insert_ft_face(const ATSUFontID afontID, const ATSUStyle atsuStyle) {
+ SkFaceRec* rec = find_ft_face(afontID);
+ if (rec) {
+ return rec; // found?
+ }
+
+ rec = SkNEW(SkFaceRec);
+ rec->fFontID = afontID;
+ rec->fStyle = atsuStyle;
+ rec->fNext = gFaceRecHead;
+ gFaceRecHead = rec;
+
+ return rec;
+}
+
+static void unref_ft_face(const ATSUFontID fontID) {
+
+ SkFaceRec* rec = gFaceRecHead;
+ SkFaceRec* prev = NULL;
+ while (rec) {
+ SkFaceRec* next = rec->fNext;
+ if (rec->fFontID == fontID) {
+ if (--rec->fRefCnt == 0) {
+ if (prev)
+ prev->fNext = next;
+ else
+ gFaceRecHead = next;
+
+ SkDELETE(rec);
+ }
+ return;
+ }
+ prev = rec;
+ rec = next;
+ }
+ SkASSERT("shouldn't get here, face not in list");
+}
+
+#pragma mark -
+
+// have to do this because SkTypeface::SkTypeface() is protected
+class SkTypeface_Mac : public SkTypeface {
+public:
+ SkTypeface_Mac(SkTypeface::Style style, uint32_t id) : SkTypeface(style, id) {}
+
+ ~SkTypeface_Mac() {}
+};
+
+#pragma mark -
+
+static SkTypeface* CreateTypeface_(const char *name, const SkTypeface::Style style) {
+
+ OSStatus err;
+ ATSUStyle atsuStyle;
+ ::ATSUCreateStyle(&atsuStyle);
+ if (name != NULL) {
+ static const ATSUAttributeTag fontTag = kATSUFontTag;
+ static const ByteCount fontTagSize = sizeof(ATSUFontID);
+
+ ATSUFontID fontID = 0;
+#if 1
+ err = ::ATSUFindFontFromName(
+ name,strlen(name),kFontNoNameCode, /* instead of regular, kFontFamilyName returns bold and/or italic sometimes, but why this works?? */
+ kFontMacintoshPlatform,kFontNoScriptCode,kFontNoLanguageCode,&fontID);
+#else
+ CFStringRef cfontName = CFStringCreateWithCString(NULL, name, kCFStringEncodingASCII);
+ ATSFontRef fontRef = ::ATSFontFindFromName(cfontName,kATSOptionFlagsDefault);
+ fontID = ::FMGetFontFromATSFontRef(fontRef);
+ CFRelease(cfontName);
+#endif
+ if (0 != fontID) {
+ const ATSUAttributeValuePtr values[] = { &fontID };
+ err = ::ATSUSetAttributes(atsuStyle,1,&fontTag,&fontTagSize,values);
+ }
+ else {
+ }
+ }
+ if (style != SkTypeface::kNormal) {
+ Boolean fontItalic = ((style & SkTypeface::kItalic) != 0);
+ Boolean fontBold = ((style & SkTypeface::kBold) != 0);
+ const ATSUAttributeTag tags[2] = { kATSUQDBoldfaceTag, kATSUQDItalicTag };
+ const ATSUAttributeValuePtr values[2] = { &fontBold, &fontItalic };
+ const ByteCount sizes[2] = { sizeof(Boolean), sizeof(Boolean) };
+ err = ::ATSUSetAttributes(atsuStyle,2,tags,sizes,values);
+ }
+
+ uint32_t cs = FontFaceChecksum(name,style);
+ SkTypeface_Mac* ptypeface = new SkTypeface_Mac(style,cs);
+
+ if (NULL == ptypeface) {
+ SkASSERT(false);
+ return NULL;
+ }
+
+ SkFaceRec* rec = insert_ft_face(cs, atsuStyle);
+ SkASSERT(rec);
+
+ return ptypeface;
+}
+
+static SkTypeface* CreateTypeface_(const SkFaceRec* rec, const SkTypeface::Style style) {
+
+ OSStatus err;
+ ATSUStyle atsuStyle;
+ err = ::ATSUCreateAndCopyStyle(rec->fStyle, &atsuStyle);
+
+ Boolean fontItalic = ((style & SkTypeface::kItalic) != 0);
+ Boolean fontBold = ((style & SkTypeface::kBold) != 0);
+ const ATSUAttributeTag tags[2] = { kATSUQDBoldfaceTag, kATSUQDItalicTag };
+ const ATSUAttributeValuePtr values[2] = { &fontBold, &fontItalic };
+ const ByteCount sizes[2] = { sizeof(Boolean), sizeof(Boolean) };
+ err = ::ATSUSetAttributes(atsuStyle,2,tags,sizes,values);
+
+ // get old font id and name
+ ATSUFontID fontID = 0;
+ ByteCount actual = 0;
+ err = ::ATSUGetAttribute(rec->fStyle,kATSUFontTag,sizeof(ATSUFontID),&fontID,&actual);
+
+ ByteCount actualLength = 0;
+ char *fontname = NULL;
+ err = ::ATSUFindFontName(fontID , kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode,
+ kFontNoLanguageCode , 0 , NULL , &actualLength , NULL );
+ if ( err == noErr)
+ {
+ actualLength += 1 ;
+ fontname = (char*)malloc( actualLength );
+ err = ::ATSUFindFontName(fontID, kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode,
+ kFontNoLanguageCode, actualLength, fontname , NULL, NULL);
+ }
+
+ SkTypeface_Mac* ptypeface = NULL;
+ if (fontname == NULL) {
+ ptypeface = new SkTypeface_Mac(style,rec->fFontID);
+ return ptypeface;
+ }
+ else {
+ uint32_t cs = FontFaceChecksum(fontname,style);
+ ptypeface = new SkTypeface_Mac(style, cs);
+
+ if (NULL == ptypeface) {
+ SkASSERT(false);
+ return NULL;
+ }
+
+ free(fontname);
+
+ insert_ft_face(cs,atsuStyle);
+ }
+ return ptypeface;
+}
+
+#pragma mark -
+
+class SkScalerContext_Mac : public SkScalerContext {
+public:
+ SkScalerContext_Mac(const SkDescriptor* desc);
+ virtual ~SkScalerContext_Mac();
+
+protected:
+ virtual unsigned generateGlyphCount() const;
+ virtual uint16_t generateCharToGlyph(SkUnichar uni);
+ virtual void generateAdvance(SkGlyph* glyph);
+ virtual void generateMetrics(SkGlyph* glyph);
+ virtual void generateImage(const SkGlyph& glyph);
+ virtual void generatePath(const SkGlyph& glyph, SkPath* path);
+ virtual void generateLineHeight(SkPoint* ascent, SkPoint* descent);
+ virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
+ virtual SkDeviceContext getDC() { return NULL; } // not implemented on Mac
+
+private:
+ ATSUTextLayout fLayout;
+ ATSUStyle fStyle;
+
+ static OSStatus MoveTo(const Float32Point *pt, void *cb);
+ static OSStatus Line(const Float32Point *pt, void *cb);
+ static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb);
+ static OSStatus Close(void *cb);
+};
+
+SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
+ : SkScalerContext(desc), fLayout(0), fStyle(0)
+{
+ SkAutoMutexAcquire ac(gFTMutex);
+ OSStatus err;
+
+ SkFaceRec* rec = find_ft_face(fRec.fFontID);
+ if (rec) {
+ rec->ref();
+ err = ::ATSUCreateAndCopyStyle(rec->fStyle, &fStyle);
+ }
+ else {
+ SkASSERT(false);
+ // create a default
+ err = ::ATSUCreateStyle(&fStyle);
+ }
+
+ uint32_t size = SkFixedFloor(fRec.fTextSize);
+ Fixed fixedSize = IntToFixed(size);
+ static const ATSUAttributeTag sizeTag = kATSUSizeTag;
+ static const ByteCount sizeTagSize = sizeof(Fixed);
+ const ATSUAttributeValuePtr values[] = { &fixedSize };
+ err = ::ATSUSetAttributes(fStyle,1,&sizeTag,&sizeTagSize,values);
+
+ err = ::ATSUCreateTextLayout(&fLayout);
+}
+
+SkScalerContext_Mac::~SkScalerContext_Mac()
+{
+ unref_ft_face(fRec.fFontID);
+
+ ::ATSUDisposeTextLayout(fLayout);
+ ::ATSUDisposeStyle(fStyle);
+}
+
+unsigned SkScalerContext_Mac::generateGlyphCount() const
+{
+ return 0xFFFF;
+}
+
+uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
+{
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ OSStatus err;
+ UniChar achar = uni;
+ err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1);
+ err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd);
+
+ ATSLayoutRecord *layoutPtr;
+ ItemCount count;
+ ATSGlyphRef glyph;
+
+ err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count);
+ glyph = layoutPtr->glyphID;
+ ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr);
+ return glyph;
+}
+
+void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
+ this->generateMetrics(glyph);
+}
+
+void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph)
+{
+ GlyphID glyphID = glyph->fID;
+ ATSGlyphScreenMetrics metrics= { 0 };
+
+ glyph->fRsbDelta = 0;
+ glyph->fLsbDelta = 0;
+
+ OSStatus err = ATSUGlyphGetScreenMetrics(fStyle,1,&glyphID,0,true,true,&metrics);
+ if (err == noErr) {
+ glyph->fAdvanceX = SkFloatToFixed(metrics.deviceAdvance.x);
+ glyph->fAdvanceY = SkFloatToFixed(metrics.deviceAdvance.y);
+ //glyph->fWidth = metrics.width;
+ //glyph->fHeight = metrics.height;
+ glyph->fWidth = metrics.width + ceil(metrics.sideBearing.x - metrics.otherSideBearing.x);
+ glyph->fHeight = metrics.height + ceil(metrics.sideBearing.y - metrics.otherSideBearing.y) + 1;
+
+ glyph->fTop = -metrics.topLeft.y;
+ glyph->fLeft = metrics.topLeft.x;
+ }
+}
+
+void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
+ //SkASSERT(false);
+ if (mx)
+ memset(mx, 0, sizeof(SkPaint::FontMetrics));
+ if (my)
+ memset(my, 0, sizeof(SkPaint::FontMetrics));
+ return;
+}
+
+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
+{
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ GlyphID glyphID = glyph.fID;
+ ATSGlyphScreenMetrics metrics= { 0 };
+
+ SkASSERT(fLayout);
+ OSStatus err = ::ATSUGlyphGetScreenMetrics(fStyle,1,&glyphID,0,true,true,&metrics);
+
+// uint32_t w = metrics.width;
+// uint32_t h = metrics.height;
+// uint32_t pitch = (w + 3) & ~0x3;
+// if (pitch != glyph.rowBytes()) {
+// SkASSERT(false); // it's different from previously cacluated in generateMetrics(), so the size of glyph.fImage buffer is incorrect!
+// }
+
+ CGColorSpaceRef greyColorSpace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
+ CGContextRef contextRef = ::CGBitmapContextCreate((uint8_t*)glyph.fImage, glyph.fWidth, glyph.fHeight, 8, glyph.rowBytes(), greyColorSpace, kCGImageAlphaNone);
+ if (!contextRef) {
+ SkASSERT(false);
+ return;
+ }
+
+ ::CGContextSetFillColorSpace(contextRef, greyColorSpace);
+ ::CGContextSetStrokeColorSpace(contextRef, greyColorSpace);
+
+ ::CGContextSetGrayFillColor(contextRef, 0.0, 1.0);
+ ::CGContextFillRect(contextRef, ::CGRectMake(0, 0, glyph.fWidth, glyph.fHeight));
+
+ ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);
+ ::CGContextSetGrayStrokeColor(contextRef, 1.0, 1.0);
+ ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);
+
+ ATSUAttributeTag tag = kATSUCGContextTag;
+ ByteCount size = sizeof(CGContextRef);
+ ATSUAttributeValuePtr value = &contextRef;
+ err = ::ATSUSetLayoutControls(fLayout,1,&tag,&size,&value);
+ err = ::ATSUDrawText(fLayout,kATSUFromTextBeginning,kATSUToTextEnd,FloatToFixed(-metrics.topLeft.x),FloatToFixed(glyph.fHeight-metrics.topLeft.y));
+ ::CGContextRelease(contextRef);
+}
+
+void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)
+{
+ SkAutoMutexAcquire ac(gFTMutex);
+ OSStatus err,result;
+
+ err = ::ATSUGlyphGetCubicPaths(
+ fStyle,glyph.fID,
+ &SkScalerContext_Mac::MoveTo,
+ &SkScalerContext_Mac::Line,
+ &SkScalerContext_Mac::Curve,
+ &SkScalerContext_Mac::Close,
+ path,&result);
+ SkASSERT(err == noErr);
+}
+
+void SkScalerContext_Mac::generateLineHeight(SkPoint* ascent, SkPoint* descent)
+{
+ ATSUTextMeasurement textAscent, textDescent;
+ ByteCount actual = 0;
+ OSStatus err = ::ATSUGetAttribute(fStyle,kATSULineAscentTag,sizeof(ATSUTextMeasurement),&textAscent,&actual);
+ ascent->set(0,textAscent);
+ err = ::ATSUGetAttribute(fStyle,kATSULineDescentTag,sizeof(ATSUTextMeasurement),&textDescent,&actual);
+ descent->set(0,textDescent);
+}
+
+OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb)
+{
+ reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt));
+ return noErr;
+}
+
+OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb)
+{
+ reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt));
+ return noErr;
+}
+
+OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb)
+{
+ reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1),F32PtToSkPoint(*pt2),F32PtToSkPoint(*pt3));
+ return noErr;
+}
+
+OSStatus SkScalerContext_Mac::Close(void *cb)
+{
+ reinterpret_cast<SkPath*>(cb)->close();
+ return noErr;
+}
+
+#pragma mark -
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+ SkASSERT(!"SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+ SkASSERT(!"SkFontHost::Deserialize unimplemented");
+ return NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) {
+
+ //Should not be used on Mac, keep linker happy
+ SkASSERT(false);
+ return CreateTypeface_(gDefaultfont,SkTypeface::kNormal);
+}
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc)
+{
+ return new SkScalerContext_Mac(desc);
+}
+
+SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec)
+{
+ SkAutoDescriptor ad(sizeof(rec) + sizeof(gDefaultfont) + SkDescriptor::ComputeOverhead(2));
+ SkDescriptor* desc = ad.getDesc();
+
+ desc->init();
+ SkScalerContext::Rec* newRec =
+ (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+
+ CreateTypeface_(gDefaultfont,SkTypeface::kNormal);
+ newRec->fFontID = FontFaceChecksum(gDefaultfont,SkTypeface::kNormal);
+ desc->computeChecksum();
+
+ return SkFontHost::CreateScalerContext(desc);
+}
+
+
+ /** Return the closest matching typeface given either an existing family
+ (specified by a typeface in that family) or by a familyName, and a
+ requested style.
+ 1) If familyFace is null, use famillyName.
+ 2) If famillyName is null, use familyFace.
+ 3) If both are null, return the default font that best matches style
+ This MUST not return NULL.
+ */
+
+SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) {
+
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ // clip to legal style bits
+ style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
+
+ SkTypeface* tf = NULL;
+
+ if (NULL == familyFace && NULL == familyName) {
+ tf = CreateTypeface_(gDefaultfont,style);
+ }
+ else {
+ if (NULL != familyFace) {
+ uint32_t id = familyFace->uniqueID();
+ SkFaceRec* rec = find_ft_face(id);
+ if (!rec) {
+ SkASSERT(false);
+ tf = CreateTypeface_(gDefaultfont,style);
+ }
+ else {
+ tf = CreateTypeface_(rec,style);
+ }
+ }
+ else {
+ tf = CreateTypeface_(familyName,style);
+ }
+ }
+
+ if (NULL == tf) {
+ tf = CreateTypeface_(gDefaultfont,style);
+ }
+ return tf;
+
+}
+
+size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
+ if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
+ return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
+ else
+ return 0; // nothing to do
+}
+
+int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
+ return 0;
+}
+
+void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
+ tables[0] = NULL; // black gamma (e.g. exp=1.4)
+ tables[1] = NULL; // white gamma (e.g. exp= 1/1.4)
+}
+
diff --git a/src/ports/SkFontHost_none.cpp b/src/ports/SkFontHost_none.cpp new file mode 100644 index 0000000000..b45cf16fff --- /dev/null +++ b/src/ports/SkFontHost_none.cpp @@ -0,0 +1,82 @@ +/* Copyright 2006-2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkFontHost.h" + +SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, + const char famillyName[], + SkTypeface::Style style) { + SkASSERT(!"SkFontHost::FindTypeface unimplemented"); + return NULL; +} + +SkTypeface* SkFontHost::ResolveTypeface(uint32_t uniqueID) { + SkASSERT(!"SkFontHost::ResolveTypeface unimplemented"); + return NULL; +} + +SkStream* SkFontHost::OpenStream(uint32_t uniqueID) { + SkASSERT(!"SkFontHost::OpenStream unimplemented"); + return NULL; +} + +void SkFontHost::CloseStream(uint32_t uniqueID, SkStream*) { + SkASSERT(!"SkFontHost::CloseStream unimplemented"); +} + +SkTypeface* SkFontHost::CreateTypeface(SkStream*) { + SkASSERT(!"SkFontHost::CreateTypeface unimplemented"); + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) { + SkASSERT(!"SkFontHost::Serialize unimplemented"); +} + +SkTypeface* SkFontHost::Deserialize(SkStream* stream) { + SkASSERT(!"SkFontHost::Deserialize unimplemented"); + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) { + SkASSERT(!"SkFontHost::CreateScalarContext unimplemented"); + return NULL; +} + +SkScalerContext* SkFontHost::CreateFallbackScalerContext( + const SkScalerContext::Rec&) { + SkASSERT(!"SkFontHost::CreateFallbackScalerContext unimplemented"); + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) { + return 0; // nothing to do (change me if you want to limit the font cache) +} + +int SkFontHost::ComputeGammaFlag(const SkPaint& paint) { + return 0; +} + +void SkFontHost::GetGammaTables(const uint8_t* tables[2]) { + tables[0] = NULL; // black gamma (e.g. exp=1.4) + tables[1] = NULL; // white gamma (e.g. exp= 1/1.4) +} + diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp new file mode 100644 index 0000000000..c2ee51e379 --- /dev/null +++ b/src/ports/SkFontHost_win.cpp @@ -0,0 +1,601 @@ +/*
+ ** Copyright 2006, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "SkString.h"
+//#include "SkStream.h"
+
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+#include "SkThread.h"
+
+#ifdef WIN32
+#include "windows.h"
+#include "tchar.h"
+
+// client3d has to undefine this for now
+#define CAN_USE_LOGFONT_NAME
+
+static SkMutex gFTMutex;
+
+// these globals are loaded (once) by get_default_font()
+static LOGFONT gDefaultFont = {0};
+
+static const uint16_t BUFFERSIZE = (16384 - 32);
+static uint8_t glyphbuf[BUFFERSIZE];
+
+// Give 1MB font cache budget
+#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024)
+
+static inline FIXED SkFixedToFIXED(SkFixed x) {
+ return *(FIXED*)(&x);
+}
+
+static inline FIXED SkScalarToFIXED(SkScalar x) {
+ return SkFixedToFIXED(SkScalarToFixed(x));
+}
+
+// This will generate a unique ID based on the fontname + fontstyle
+// and also used by upper layer
+uint32_t FontFaceChecksum(const TCHAR *q, SkTypeface::Style style)
+{
+ if (!q) return style;
+
+ // From "Performance in Practice of String Hashing Functions"
+ // Ramakrishna & Zobel
+ const uint32_t L = 5;
+ const uint32_t R = 2;
+
+ uint32_t h = 0x12345678;
+ while (*q) {
+ //uint32_t ql = tolower(*q);
+ h ^= ((h << L) + (h >> R) + *q);
+ q ++;
+ }
+
+ // add style
+ h = _rotl(h, 3) ^ style;
+
+ return h;
+}
+
+static SkTypeface::Style GetFontStyle(const LOGFONT& lf) {
+ int style = SkTypeface::kNormal;
+ if (lf.lfWeight == FW_SEMIBOLD || lf.lfWeight == FW_DEMIBOLD || lf.lfWeight == FW_BOLD)
+ style |= SkTypeface::kBold;
+ if (lf.lfItalic)
+ style |= SkTypeface::kItalic;
+
+ return (SkTypeface::Style)style;
+}
+
+struct SkFaceRec {
+ SkFaceRec* fNext;
+ uint32_t fRefCnt;
+ uint32_t fFontID; // checksum of fFace
+ LOGFONT fFace;
+
+ SkFaceRec() : fFontID(-1), fRefCnt(0) {
+ memset(&fFace, 0, sizeof(LOGFONT));
+ }
+ ~SkFaceRec() {}
+
+ uint32_t ref() {
+ return ++fRefCnt;
+ }
+};
+
+// Font Face list
+static SkFaceRec* gFaceRecHead = NULL;
+
+static SkFaceRec* find_ft_face(uint32_t fontID) {
+ SkFaceRec* rec = gFaceRecHead;
+ while (rec) {
+ if (rec->fFontID == fontID) {
+ return rec;
+ }
+ rec = rec->fNext;
+ }
+
+ return NULL;
+}
+
+static SkFaceRec* insert_ft_face(const LOGFONT& lf) {
+ // need a const char*
+ uint32_t id = FontFaceChecksum(&(lf.lfFaceName[0]), GetFontStyle(lf));
+ SkFaceRec* rec = find_ft_face(id);
+ if (rec) {
+ return rec; // found?
+ }
+
+ rec = SkNEW(SkFaceRec);
+ rec->fFontID = id;
+ memcpy(&(rec->fFace), &lf, sizeof(LOGFONT));
+ rec->fNext = gFaceRecHead;
+ gFaceRecHead = rec;
+
+ return rec;
+}
+
+static void unref_ft_face(uint32_t fontID) {
+
+ SkFaceRec* rec = gFaceRecHead;
+ SkFaceRec* prev = NULL;
+ while (rec) {
+ SkFaceRec* next = rec->fNext;
+ if (rec->fFontID == fontID) {
+ if (--rec->fRefCnt == 0) {
+ if (prev)
+ prev->fNext = next;
+ else
+ gFaceRecHead = next;
+
+ SkDELETE(rec);
+ }
+ return;
+ }
+ prev = rec;
+ rec = next;
+ }
+ SkASSERT("shouldn't get here, face not in list");
+}
+
+// have to do this because SkTypeface::SkTypeface() is protected
+class FontFaceRec_Typeface : public SkTypeface {
+public:
+
+ FontFaceRec_Typeface(Style style, uint32_t id) : SkTypeface(style, id) {};
+
+ virtual ~FontFaceRec_Typeface() {};
+};
+
+static const LOGFONT* get_default_font() {
+ // don't hardcode on Windows, Win2000, XP, Vista, and international all have different default
+ // and the user could change too
+
+ if (gDefaultFont.lfFaceName[0] != 0) {
+ return &gDefaultFont;
+ }
+
+ NONCLIENTMETRICS ncm;
+ ncm.cbSize = sizeof(NONCLIENTMETRICS);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
+
+ memcpy(&gDefaultFont, &(ncm.lfMessageFont), sizeof(LOGFONT));
+
+ return &gDefaultFont;
+}
+
+static SkTypeface* CreateTypeface_(const LOGFONT& lf) {
+
+ SkTypeface::Style style = GetFontStyle(lf);
+ FontFaceRec_Typeface* ptypeface = new FontFaceRec_Typeface(style, FontFaceChecksum(lf.lfFaceName, style));
+
+ if (NULL == ptypeface) {
+ SkASSERT(false);
+ return NULL;
+ }
+
+ SkFaceRec* rec = insert_ft_face(lf);
+ SkASSERT(rec);
+
+ return ptypeface;
+}
+
+class SkScalerContext_Windows : public SkScalerContext {
+public:
+ SkScalerContext_Windows(const SkDescriptor* desc);
+ virtual ~SkScalerContext_Windows();
+
+protected:
+ virtual unsigned generateGlyphCount() const;
+ virtual uint16_t generateCharToGlyph(SkUnichar uni);
+ virtual void generateAdvance(SkGlyph* glyph);
+ virtual void generateMetrics(SkGlyph* glyph);
+ virtual void generateImage(const SkGlyph& glyph);
+ virtual void generatePath(const SkGlyph& glyph, SkPath* path);
+ virtual void generateLineHeight(SkPoint* ascent, SkPoint* descent);
+ virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
+ //virtual SkDeviceContext getDC() {return ddc;}
+private:
+ uint32_t fFontID;
+ LOGFONT lf;
+ MAT2 mat22;
+ HDC ddc;
+ HFONT savefont;
+ HFONT font;
+};
+
+SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc) : SkScalerContext(desc), ddc(0), font(0), savefont(0) {
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ fFontID = fRec.fFontID;
+ SkFaceRec* rec = find_ft_face(fRec.fFontID);
+ if (rec) {
+ rec->ref();
+ memcpy(&lf, &(rec->fFace), sizeof(LOGFONT));
+ }
+ else {
+ SkASSERT(false);
+ memcpy(&lf, &gDefaultFont, sizeof(LOGFONT));
+ }
+
+ mat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
+ mat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
+ mat22.eM21 = SkScalarToFIXED(fRec.fPost2x2[1][0]);
+ mat22.eM22 = SkScalarToFIXED(-fRec.fPost2x2[1][1]);
+
+ ddc = ::CreateCompatibleDC(NULL);
+ SetBkMode(ddc, TRANSPARENT);
+
+ lf.lfHeight = SkScalarFloor(fRec.fTextSize);
+ font = CreateFontIndirect(&lf);
+ savefont = (HFONT)SelectObject(ddc, font);
+}
+
+SkScalerContext_Windows::~SkScalerContext_Windows() {
+ unref_ft_face(fFontID);
+
+ if (ddc) {
+ ::SelectObject(ddc, savefont);
+ ::DeleteDC(ddc);
+ ddc = NULL;
+ }
+ if (font) {
+ ::DeleteObject(font);
+ }
+}
+
+unsigned SkScalerContext_Windows::generateGlyphCount() const {
+ return 0xFFFF;
+ // return fFace->num_glyphs;
+}
+
+uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
+
+ //uint16_t index = 0;
+ //GetGlyphIndicesW(ddc, &(uint16_t&)uni, 1, &index, 0);
+ //return index;
+
+ // let's just use the uni as index on Windows
+ return SkToU16(uni);
+}
+
+void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
+ this->generateMetrics(glyph);
+}
+
+void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
+
+ SkASSERT(ddc);
+
+ GLYPHMETRICS gm;
+ memset(&gm, 0, sizeof(gm));
+
+ glyph->fRsbDelta = 0;
+ glyph->fLsbDelta = 0;
+
+ UINT glyphIndexFlag = 0; //glyph->fIsCodePoint ? 0 : GGO_GLYPH_INDEX;
+ // UINT glyphIndexFlag = GGO_GLYPH_INDEX;
+ // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller
+ // BlackBlox; we need the bigger one in case we need the image. fAdvance is the same.
+ uint32_t ret = GetGlyphOutlineW(ddc, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | glyphIndexFlag, &gm, 0, NULL, &mat22);
+
+ if (GDI_ERROR != ret) {
+ if (ret == 0) {
+ // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly!
+ gm.gmBlackBoxX = gm.gmBlackBoxY = 0;
+ }
+ glyph->fWidth = gm.gmBlackBoxX;
+ glyph->fHeight = gm.gmBlackBoxY;
+ glyph->fTop = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);
+ glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
+ glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
+ glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY);
+ } else {
+ glyph->fWidth = 0;
+ }
+
+#if 0
+ char buf[1024];
+ sprintf(buf, "generateMetrics: id:%d, w=%d, h=%d, font:%s, fh:%d\n", glyph->fID, glyph->fWidth, glyph->fHeight, lf.lfFaceName, lf.lfHeight);
+ OutputDebugString(buf);
+#endif
+}
+
+void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
+ //SkASSERT(false);
+ if (mx)
+ memset(mx, 0, sizeof(SkPaint::FontMetrics));
+ if (my)
+ memset(my, 0, sizeof(SkPaint::FontMetrics));
+ return;
+}
+
+void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
+
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ SkASSERT(ddc);
+
+ GLYPHMETRICS gm;
+ memset(&gm, 0, sizeof(gm));
+
+#if 0
+ char buf[1024];
+ sprintf(buf, "generateImage: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
+ OutputDebugString(buf);
+#endif
+
+ uint32_t bytecount = 0;
+ UINT glyphIndexFlag = 0; //glyph.fIsCodePoint ? 0 : GGO_GLYPH_INDEX;
+ // UINT glyphIndexFlag = GGO_GLYPH_INDEX;
+ uint32_t total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_GRAY8_BITMAP | glyphIndexFlag, &gm, 0, NULL, &mat22);
+ if (GDI_ERROR != total_size && total_size > 0) {
+ uint8_t *pBuff = new uint8_t[total_size];
+ if (NULL != pBuff) {
+ total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_GRAY8_BITMAP | glyphIndexFlag, &gm, total_size, pBuff, &mat22);
+
+ SkASSERT(total_size != GDI_ERROR);
+
+ SkASSERT(glyph.fWidth == gm.gmBlackBoxX);
+ SkASSERT(glyph.fHeight == gm.gmBlackBoxY);
+
+ uint8_t* dst = (uint8_t*)glyph.fImage;
+ uint32_t pitch = (gm.gmBlackBoxX + 3) & ~0x3;
+ if (pitch != glyph.rowBytes()) {
+ SkASSERT(false); // glyph.fImage has different rowsize!?
+ }
+
+ for (int32_t y = gm.gmBlackBoxY - 1; y >= 0; y--) {
+ uint8_t* src = pBuff + pitch * y;
+
+ for (uint32_t x = 0; x < gm.gmBlackBoxX; x++) {
+ if (*src > 63) {
+ *dst = 0xFF;
+ }
+ else {
+ *dst = *src << 2; // scale to 0-255
+ }
+ dst++;
+ src++;
+ bytecount++;
+ }
+ memset(dst, 0, glyph.rowBytes() - glyph.fWidth);
+ dst += glyph.rowBytes() - glyph.fWidth;
+ }
+
+ delete[] pBuff;
+ }
+ }
+
+ SkASSERT(GDI_ERROR != total_size && total_size >= 0);
+
+}
+
+void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
+
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ SkASSERT(&glyph && path);
+ SkASSERT(ddc);
+
+ path->reset();
+
+#if 0
+ char buf[1024];
+ sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
+ OutputDebugString(buf);
+#endif
+
+ GLYPHMETRICS gm;
+ UINT glyphIndexFlag = 0; //glyph.fIsCodePoint ? 0 : GGO_GLYPH_INDEX;
+ uint32_t total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_NATIVE | glyphIndexFlag, &gm, BUFFERSIZE, glyphbuf, &mat22);
+
+ if (GDI_ERROR != total_size) {
+
+ const uint8_t* cur_glyph = glyphbuf;
+ const uint8_t* end_glyph = glyphbuf + total_size;
+
+ while(cur_glyph < end_glyph) {
+ const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
+
+ const uint8_t* end_poly = cur_glyph + th->cb;
+ const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
+
+ path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y)));
+
+ while(cur_poly < end_poly) {
+ const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
+
+ if (pc->wType == TT_PRIM_LINE) {
+ for (uint16_t i = 0; i < pc->cpfx; i++) {
+ path->lineTo(SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y)));
+ }
+ }
+
+ if (pc->wType == TT_PRIM_QSPLINE) {
+ for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
+ POINTFX pnt_b = pc->apfx[u]; // B is always the current point
+ POINTFX pnt_c = pc->apfx[u+1];
+
+ if (u < pc->cpfx - 2) { // If not on last spline, compute C
+ pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x)));
+ pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y)));
+ }
+
+ path->quadTo(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&pnt_c.y)));
+ }
+ }
+ cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx;
+ }
+ cur_glyph += th->cb;
+ path->close();
+ }
+ }
+ else {
+ SkASSERT(false);
+ }
+ //char buf[1024];
+ //sprintf(buf, "generatePath: count:%d\n", count);
+ //OutputDebugString(buf);
+}
+
+
+// Note: not sure this is the correct implementation
+void SkScalerContext_Windows::generateLineHeight(SkPoint* ascent, SkPoint* descent) {
+
+ SkASSERT(ddc);
+
+ OUTLINETEXTMETRIC otm;
+
+ uint32_t ret = GetOutlineTextMetrics(ddc, sizeof(otm), &otm);
+
+ if (sizeof(otm) == ret) {
+ if (ascent)
+ ascent->iset(0, otm.otmAscent);
+ if (descent)
+ descent->iset(0, otm.otmDescent);
+ }
+
+ return;
+}
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+ SkASSERT(!"SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+ SkASSERT(!"SkFontHost::Deserialize unimplemented");
+ return NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) {
+
+ //Should not be used on Windows, keep linker happy
+ SkASSERT(false);
+ get_default_font();
+ return CreateTypeface_(gDefaultFont);
+}
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+ return SkNEW_ARGS(SkScalerContext_Windows, (desc));
+}
+
+SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec) {
+ get_default_font();
+
+ SkAutoDescriptor ad(sizeof(rec) + sizeof(gDefaultFont) + SkDescriptor::ComputeOverhead(2));
+ SkDescriptor* desc = ad.getDesc();
+
+ desc->init();
+ SkScalerContext::Rec* newRec =
+ (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+
+ get_default_font();
+ CreateTypeface_(gDefaultFont);
+ newRec->fFontID = FontFaceChecksum(gDefaultFont.lfFaceName, GetFontStyle(gDefaultFont));
+ desc->computeChecksum();
+
+ return SkFontHost::CreateScalerContext(desc);
+}
+
+/** Return the closest matching typeface given either an existing family
+ (specified by a typeface in that family) or by a familyName, and a
+ requested style.
+ 1) If familyFace is null, use famillyName.
+ 2) If famillyName is null, use familyFace.
+ 3) If both are null, return the default font that best matches style
+ This MUST not return NULL.
+ */
+
+SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) {
+
+ SkAutoMutexAcquire ac(gFTMutex);
+
+#ifndef CAN_USE_LOGFONT_NAME
+ familyName = NULL;
+ familyFace = NULL;
+#endif
+
+ // clip to legal style bits
+ style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
+
+ SkTypeface* tf = NULL;
+ if (NULL == familyFace && NULL == familyName) {
+ LOGFONT lf;
+ get_default_font();
+ memcpy(&lf, &gDefaultFont, sizeof(LOGFONT));
+ lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
+ lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
+ tf = CreateTypeface_(lf);
+ } else {
+#ifdef CAN_USE_LOGFONT_NAME
+ LOGFONT lf;
+ if (NULL != familyFace) {
+ uint32_t id = familyFace->uniqueID();
+ SkFaceRec* rec = find_ft_face(id);
+ if (!rec) {
+ SkASSERT(false);
+ get_default_font();
+ memcpy(&lf, &gDefaultFont, sizeof(LOGFONT));
+ }
+ else {
+ memcpy(&lf, &(rec->fFace), sizeof(LOGFONT));
+ }
+ }
+ else {
+ memset(&lf, 0, sizeof(LOGFONT));
+
+ lf.lfHeight = -11; // default
+ lf.lfQuality = PROOF_QUALITY;
+ lf.lfCharSet = DEFAULT_CHARSET;
+
+ _tcsncpy(lf.lfFaceName, familyName, LF_FACESIZE);
+ lf.lfFaceName[LF_FACESIZE-1] = '\0';
+ }
+
+ // use the style desired
+ lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
+ lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
+ tf = CreateTypeface_(lf);
+#endif
+ }
+
+ if (NULL == tf) {
+ get_default_font();
+ tf = CreateTypeface_(gDefaultFont);
+ }
+ return tf;
+}
+
+size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
+ if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
+ return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
+ else
+ return 0; // nothing to do
+}
+
+int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
+ return 0;
+}
+
+void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
+ tables[0] = NULL; // black gamma (e.g. exp=1.4)
+ tables[1] = NULL; // white gamma (e.g. exp= 1/1.4)
+}
+
+#endif // WIN32
+
diff --git a/src/ports/SkGlobals_global.cpp b/src/ports/SkGlobals_global.cpp new file mode 100644 index 0000000000..d87568b979 --- /dev/null +++ b/src/ports/SkGlobals_global.cpp @@ -0,0 +1,28 @@ +/* libs/graphics/ports/SkGlobals_global.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkGlobals.h" +#include "SkThread.h" + +static SkGlobals::BootStrap gBootStrap; + +SkGlobals::BootStrap& SkGlobals::GetBootStrap() +{ + return gBootStrap; +} + + diff --git a/src/ports/SkImageDecoder_Factory.cpp b/src/ports/SkImageDecoder_Factory.cpp new file mode 100644 index 0000000000..d0053cfac0 --- /dev/null +++ b/src/ports/SkImageDecoder_Factory.cpp @@ -0,0 +1,104 @@ +/* libs/graphics/ports/SkImageDecoder_Factory.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkImageDecoder.h" +#include "SkMovie.h" +#include "SkStream.h" + +extern SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream*); +extern SkImageDecoder* SkImageDecoder_BMP_Factory(SkStream*); +extern SkImageDecoder* SkImageDecoder_ICO_Factory(SkStream*); +extern SkImageDecoder* SkImageDecoder_PNG_Factory(SkStream*); +extern SkImageDecoder* SkImageDecoder_WBMP_Factory(SkStream*); +extern SkImageDecoder* SkImageDecoder_JPEG_Factory(SkStream*); + +typedef SkImageDecoder* (*SkImageDecoderFactoryProc)(SkStream*); + +struct CodecFormat { + SkImageDecoderFactoryProc fProc; + SkImageDecoder::Format fFormat; +}; + +static const CodecFormat gPairs[] = { + { SkImageDecoder_GIF_Factory, SkImageDecoder::kGIF_Format }, + { SkImageDecoder_PNG_Factory, SkImageDecoder::kPNG_Format }, + { SkImageDecoder_ICO_Factory, SkImageDecoder::kICO_Format }, + { SkImageDecoder_WBMP_Factory, SkImageDecoder::kWBMP_Format }, + { SkImageDecoder_BMP_Factory, SkImageDecoder::kBMP_Format }, + { SkImageDecoder_JPEG_Factory, SkImageDecoder::kJPEG_Format } +}; + +SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) { + for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) { + SkImageDecoder* codec = gPairs[i].fProc(stream); + stream->rewind(); + if (NULL != codec) { + return codec; + } + } + return NULL; +} + +bool SkImageDecoder::SupportsFormat(Format format) { + for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) { + if (gPairs[i].fFormat == format) { + return true; + } + } + return false; +} + +///////////////////////////////////////////////////////////////////////// + +typedef SkMovie* (*SkMovieFactoryProc)(SkStream*); + +extern SkMovie* SkMovie_GIF_Factory(SkStream*); + +static const SkMovieFactoryProc gMovieProcs[] = { + SkMovie_GIF_Factory +}; + +SkMovie* SkMovie::DecodeStream(SkStream* stream) { + for (unsigned i = 0; i < SK_ARRAY_COUNT(gMovieProcs); i++) { + SkMovie* movie = gMovieProcs[i](stream); + if (NULL != movie) { + return movie; + } + stream->rewind(); + } + return NULL; +} + +///////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +extern SkImageEncoder* SkImageEncoder_JPEG_Factory(); +extern SkImageEncoder* SkImageEncoder_PNG_Factory(); + +SkImageEncoder* SkImageEncoder::Create(Type t) { + switch (t) { + case kJPEG_Type: + return SkImageEncoder_JPEG_Factory(); + case kPNG_Type: + return SkImageEncoder_PNG_Factory(); + default: + return NULL; + } +} + +#endif diff --git a/src/ports/SkImageRef_ashmem.cpp b/src/ports/SkImageRef_ashmem.cpp new file mode 100644 index 0000000000..a904bae95c --- /dev/null +++ b/src/ports/SkImageRef_ashmem.cpp @@ -0,0 +1,203 @@ +#include "SkImageRef_ashmem.h" +#include "SkImageDecoder.h" +#include "SkThread.h" + +#include <sys/mman.h> +#include <unistd.h> +#include <cutils/ashmem.h> + +//#define TRACE_ASH_PURGE // just trace purges + +#ifdef DUMP_IMAGEREF_LIFECYCLE + #define DUMP_ASHMEM_LIFECYCLE +#else +// #define DUMP_ASHMEM_LIFECYCLE +#endif + +// ashmem likes lengths on page boundaries +static size_t roundToPageSize(size_t size) { + const size_t mask = getpagesize() - 1; + size_t newsize = (size + mask) & ~mask; +// SkDebugf("---- oldsize %d newsize %d\n", size, newsize); + return newsize; +} + +SkImageRef_ashmem::SkImageRef_ashmem(SkStream* stream, + SkBitmap::Config config, + int sampleSize) + : SkImageRef(stream, config, sampleSize) { + + fRec.fFD = -1; + fRec.fAddr = NULL; + fRec.fSize = 0; + fRec.fPinned = false; + + fCT = NULL; +} + +SkImageRef_ashmem::~SkImageRef_ashmem() { + fCT->safeUnref(); + this->closeFD(); +} + +void SkImageRef_ashmem::closeFD() { + if (-1 != fRec.fFD) { +#ifdef DUMP_ASHMEM_LIFECYCLE + SkDebugf("=== ashmem close %d\n", fRec.fFD); +#endif + SkASSERT(fRec.fAddr); + SkASSERT(fRec.fSize); + munmap(fRec.fAddr, fRec.fSize); + close(fRec.fFD); + fRec.fFD = -1; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +class AshmemAllocator : public SkBitmap::Allocator { +public: + AshmemAllocator(SkAshmemRec* rec, const char name[]) + : fRec(rec), fName(name) {} + + virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) { + const size_t size = roundToPageSize(bm->getSize()); + int fd = fRec->fFD; + void* addr = fRec->fAddr; + + SkASSERT(!fRec->fPinned); + + if (-1 == fd) { + SkASSERT(NULL == addr); + SkASSERT(0 == fRec->fSize); + + fd = ashmem_create_region(fName, size); +#ifdef DUMP_ASHMEM_LIFECYCLE + SkDebugf("=== ashmem_create_region %s size=%d fd=%d\n", fName, size, fd); +#endif + if (-1 == fd) { + SkDebugf("------- imageref_ashmem create failed <%s> %d\n", + fName, size); + return false; + } + + int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); + if (err) { + SkDebugf("------ ashmem_set_prot_region(%d) failed %d %d\n", + fd, err, errno); + return false; + } + + addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (-1 == (long)addr) { + SkDebugf("---------- mmap failed for imageref_ashmem size=%d err=%d\n", + size, errno); + return false; + } + + fRec->fFD = fd; + fRec->fAddr = addr; + fRec->fSize = size; + } else { + SkASSERT(addr); + SkASSERT(size == fRec->fSize); + (void)ashmem_pin_region(fd, 0, 0); + } + + bm->setPixels(addr, ct); + fRec->fPinned = true; + return true; + } + +private: + // we just point to our caller's memory, these are not copies + SkAshmemRec* fRec; + const char* fName; +}; + +bool SkImageRef_ashmem::onDecode(SkImageDecoder* codec, SkStream* stream, + SkBitmap* bitmap, SkBitmap::Config config, + SkImageDecoder::Mode mode) { + + if (SkImageDecoder::kDecodeBounds_Mode == mode) { + return this->INHERITED::onDecode(codec, stream, bitmap, config, mode); + } + + AshmemAllocator alloc(&fRec, this->getURI()); + + codec->setAllocator(&alloc); + bool success = this->INHERITED::onDecode(codec, stream, bitmap, config, + mode); + // remove the allocator, since its on the stack + codec->setAllocator(NULL); + + if (success) { + // remember the colortable (if any) + SkRefCnt_SafeAssign(fCT, bitmap->getColorTable()); + return true; + } else { + if (fRec.fPinned) { + ashmem_unpin_region(fRec.fFD, 0, 0); + fRec.fPinned = false; + } + this->closeFD(); + return false; + } +} + +void* SkImageRef_ashmem::onLockPixels(SkColorTable** ct) { + SkASSERT(fBitmap.getPixels() == NULL); + SkASSERT(fBitmap.getColorTable() == NULL); + + // fast case: check if we can just pin and get the cached data + if (-1 != fRec.fFD) { + SkASSERT(fRec.fAddr); + SkASSERT(!fRec.fPinned); + int pin = ashmem_pin_region(fRec.fFD, 0, 0); + + if (ASHMEM_NOT_PURGED == pin) { // yea, fast case! + fBitmap.setPixels(fRec.fAddr, fCT); + fRec.fPinned = true; + } else if (ASHMEM_WAS_PURGED == pin) { + ashmem_unpin_region(fRec.fFD, 0, 0); + // let go of our colortable if we lost the pixels. Well get it back + // again when we re-decode + if (fCT) { + fCT->unref(); + fCT = NULL; + } +#if defined(DUMP_ASHMEM_LIFECYCLE) || defined(TRACE_ASH_PURGE) + SkDebugf("===== ashmem purged %d\n", fBitmap.getSize()); +#endif + } else { + SkDebugf("===== ashmem pin_region(%d) returned %d, treating as error %d\n", + fRec.fFD, pin, errno); + // return null result for failure + if (ct) { + *ct = NULL; + } + return NULL; + } + } else { + // no FD, will create an ashmem region in allocator + } + + return this->INHERITED::onLockPixels(ct); +} + +void SkImageRef_ashmem::onUnlockPixels() { + this->INHERITED::onUnlockPixels(); + + if (-1 != fRec.fFD) { + SkASSERT(fRec.fAddr); + SkASSERT(fRec.fPinned); + + ashmem_unpin_region(fRec.fFD, 0, 0); + fRec.fPinned = false; + } + + // we clear this with or without an error, since we've either closed or + // unpinned the region + fBitmap.setPixels(NULL, NULL); +} + diff --git a/src/ports/SkImageRef_ashmem.h b/src/ports/SkImageRef_ashmem.h new file mode 100644 index 0000000000..193a01d35f --- /dev/null +++ b/src/ports/SkImageRef_ashmem.h @@ -0,0 +1,35 @@ +#ifndef SkImageRef_ashmem_DEFINED +#define SkImageRef_ashmem_DEFINED + +#include "SkImageRef.h" + +struct SkAshmemRec { + int fFD; + void* fAddr; + size_t fSize; + bool fPinned; +}; + +class SkImageRef_ashmem : public SkImageRef { +public: + SkImageRef_ashmem(SkStream*, SkBitmap::Config, int sampleSize = 1); + virtual ~SkImageRef_ashmem(); + +protected: + virtual bool onDecode(SkImageDecoder* codec, SkStream* stream, + SkBitmap* bitmap, SkBitmap::Config config, + SkImageDecoder::Mode mode); + + virtual void* onLockPixels(SkColorTable**); + virtual void onUnlockPixels(); + +private: + void closeFD(); + + SkColorTable* fCT; + SkAshmemRec fRec; + + typedef SkImageRef INHERITED; +}; + +#endif diff --git a/src/ports/SkOSEvent_android.cpp b/src/ports/SkOSEvent_android.cpp new file mode 100644 index 0000000000..59d6191684 --- /dev/null +++ b/src/ports/SkOSEvent_android.cpp @@ -0,0 +1,155 @@ +/* libs/graphics/ports/SkOSEvent_android.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkEvent.h" +#include "utils/threads.h" +#include <stdio.h> + +using namespace android; + +Mutex gEventQMutex; +Condition gEventQCondition; + +void SkEvent::SignalNonEmptyQueue() +{ + gEventQCondition.broadcast(); +} + +/////////////////////////////////////////////////////////////////// + +#ifdef FMS_ARCH_ANDROID_ARM + +// don't have pthreads.h, and therefore no timedwait(), so we punt for the demo + +void SkEvent::SignalQueueTimer(SkMSec delay) +{ +} + +void SkEvent_start_timer_thread() +{ +} + +void SkEvent_stop_timer_thread() +{ +} + +#else + +#include <pthread.h> +#include <errno.h> + +static pthread_t gTimerThread; +static pthread_mutex_t gTimerMutex; +static pthread_cond_t gTimerCond; +static timespec gTimeSpec; + +static void* timer_event_thread_proc(void*) +{ + for (;;) + { + int status; + + pthread_mutex_lock(&gTimerMutex); + + timespec spec = gTimeSpec; + // mark our global to be zero + // so we don't call timedwait again on a stale value + gTimeSpec.tv_sec = 0; + gTimeSpec.tv_nsec = 0; + + if (spec.tv_sec == 0 && spec.tv_nsec == 0) + status = pthread_cond_wait(&gTimerCond, &gTimerMutex); + else + status = pthread_cond_timedwait(&gTimerCond, &gTimerMutex, &spec); + + if (status == 0) // someone signaled us with a new time + { + pthread_mutex_unlock(&gTimerMutex); + } + else + { + SkASSERT(status == ETIMEDOUT); // no need to unlock the mutex (its unlocked) + // this is the payoff. Signal the event queue to wake up + // and also check the delay-queue + gEventQCondition.broadcast(); + } + } + return 0; +} + +#define kThousand (1000) +#define kMillion (kThousand * kThousand) +#define kBillion (kThousand * kThousand * kThousand) + +void SkEvent::SignalQueueTimer(SkMSec delay) +{ + pthread_mutex_lock(&gTimerMutex); + + if (delay) + { + struct timeval tv; + gettimeofday(&tv, NULL); + + // normalize tv + if (tv.tv_usec >= kMillion) + { + tv.tv_sec += tv.tv_usec / kMillion; + tv.tv_usec %= kMillion; + } + + // add tv + delay, scale each up to land on nanoseconds + gTimeSpec.tv_nsec = (tv.tv_usec + (delay % kThousand) * kThousand) * kThousand; + gTimeSpec.tv_sec = (tv.tv_sec + (delay / kThousand) * kThousand) * kThousand; + + // check for overflow in nsec + if ((unsigned long)gTimeSpec.tv_nsec >= kBillion) + { + gTimeSpec.tv_nsec -= kBillion; + gTimeSpec.tv_sec += 1; + SkASSERT((unsigned long)gTimeSpec.tv_nsec < kBillion); + } + + // printf("SignalQueueTimer(%d) timespec(%d %d)\n", delay, gTimeSpec.tv_sec, gTimeSpec.tv_nsec); + } + else // cancel the timer + { + gTimeSpec.tv_nsec = 0; + gTimeSpec.tv_sec = 0; + } + + pthread_mutex_unlock(&gTimerMutex); + pthread_cond_signal(&gTimerCond); +} + +void SkEvent_start_timer_thread() +{ + int status; + pthread_attr_t attr; + + status = pthread_attr_init(&attr); + SkASSERT(status == 0); + status = pthread_create(&gTimerThread, &attr, timer_event_thread_proc, 0); + SkASSERT(status == 0); +} + +void SkEvent_stop_timer_thread() +{ + int status = pthread_cancel(gTimerThread); + SkASSERT(status == 0); +} + +#endif diff --git a/src/ports/SkOSEvent_dummy.cpp b/src/ports/SkOSEvent_dummy.cpp new file mode 100644 index 0000000000..f061b6e3a2 --- /dev/null +++ b/src/ports/SkOSEvent_dummy.cpp @@ -0,0 +1,28 @@ +/* libs/graphics/ports/SkOSEvent_dummy.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkEvent.h" + +void SkEvent::SignalNonEmptyQueue() +{ + +} + +void SkEvent::SignalQueueTimer(SkMSec delay) +{ + +} diff --git a/src/ports/SkOSFile_stdio.cpp b/src/ports/SkOSFile_stdio.cpp new file mode 100644 index 0000000000..7438f7b76d --- /dev/null +++ b/src/ports/SkOSFile_stdio.cpp @@ -0,0 +1,106 @@ +/* libs/graphics/ports/SkOSFile_stdio.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkOSFile.h" + +#ifndef SK_BUILD_FOR_BREW + +#include <stdio.h> +#include <errno.h> + +SkFILE* sk_fopen(const char path[], SkFILE_Flags flags) +{ + char perm[4]; + char* p = perm; + + if (flags & kRead_SkFILE_Flag) + *p++ = 'r'; + if (flags & kWrite_SkFILE_Flag) + *p++ = 'w'; + *p++ = 'b'; + *p = 0; + + SkFILE* f = (SkFILE*)::fopen(path, perm); +#if 0 + if (NULL == f) + SkDebugf("sk_fopen failed for %s (%s), errno=%s\n", path, perm, strerror(errno)); +#endif + return f; +} + +size_t sk_fgetsize(SkFILE* f) +{ + SkASSERT(f); + + size_t curr = ::ftell((FILE*)f); // remember where we are + ::fseek((FILE*)f, 0, SEEK_END); // go to the end + size_t size = ::ftell((FILE*)f); // record the size + ::fseek((FILE*)f, (long)curr, SEEK_SET); // go back to our prev loc + return size; +} + +bool sk_frewind(SkFILE* f) +{ + SkASSERT(f); + ::rewind((FILE*)f); +// ::fseek((FILE*)f, 0, SEEK_SET); + return true; +} + +size_t sk_fread(void* buffer, size_t byteCount, SkFILE* f) +{ + SkASSERT(f); + if (buffer == NULL) + { + size_t curr = ::ftell((FILE*)f); + if ((long)curr == -1) { + SkDEBUGF(("sk_fread: ftell(%p) returned -1 feof:%d ferror:%d\n", f, feof((FILE*)f), ferror((FILE*)f))); + return 0; + } + // ::fseek((FILE*)f, (long)(curr + byteCount), SEEK_SET); + int err = ::fseek((FILE*)f, (long)byteCount, SEEK_CUR); + if (err != 0) { + SkDEBUGF(("sk_fread: fseek(%d) tell:%d failed with feof:%d ferror:%d returned:%d\n", + byteCount, curr, feof((FILE*)f), ferror((FILE*)f), err)); + return 0; + } + return byteCount; + } + else + return ::fread(buffer, 1, byteCount, (FILE*)f); +} + +size_t sk_fwrite(const void* buffer, size_t byteCount, SkFILE* f) +{ + SkASSERT(f); + return ::fwrite(buffer, 1, byteCount, (FILE*)f); +} + +void sk_fflush(SkFILE* f) +{ + SkASSERT(f); + ::fflush((FILE*)f); +} + +void sk_fclose(SkFILE* f) +{ + SkASSERT(f); + ::fclose((FILE*)f); +} + +#endif + diff --git a/src/ports/SkThread_none.cpp b/src/ports/SkThread_none.cpp new file mode 100644 index 0000000000..37a3834e42 --- /dev/null +++ b/src/ports/SkThread_none.cpp @@ -0,0 +1,49 @@ +/* libs/graphics/ports/SkThread_none.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkThread.h" + +int32_t sk_atomic_inc(int32_t* addr) +{ + int32_t value = *addr; + *addr = value + 1; + return value; +} + +int32_t sk_atomic_dec(int32_t* addr) +{ + int32_t value = *addr; + *addr = value - 1; + return value; +} + +SkMutex::SkMutex(bool /* isGlobal */) +{ +} + +SkMutex::~SkMutex() +{ +} + +void SkMutex::acquire() +{ +} + +void SkMutex::release() +{ +} + diff --git a/src/ports/SkThread_pthread.cpp b/src/ports/SkThread_pthread.cpp new file mode 100644 index 0000000000..4ee857d51c --- /dev/null +++ b/src/ports/SkThread_pthread.cpp @@ -0,0 +1,90 @@ +#include "SkThread.h" + +#include <pthread.h> +#include <errno.h> + +SkMutex gAtomicMutex; + +int32_t sk_atomic_inc(int32_t* addr) +{ + SkAutoMutexAcquire ac(gAtomicMutex); + + int32_t value = *addr; + *addr = value + 1; + return value; +} + +int32_t sk_atomic_dec(int32_t* addr) +{ + SkAutoMutexAcquire ac(gAtomicMutex); + + int32_t value = *addr; + *addr = value - 1; + return value; +} + +////////////////////////////////////////////////////////////////////////////// + +static void print_pthread_error(int status) +{ + switch (status) { + case 0: // success + break; + case EINVAL: + printf("pthread error [%d] EINVAL\n", status); + break; + case EBUSY: + printf("pthread error [%d] EBUSY\n", status); + break; + default: + printf("pthread error [%d] unknown\n", status); + break; + } +} + +SkMutex::SkMutex(bool isGlobal) : fIsGlobal(isGlobal) +{ + if (sizeof(pthread_mutex_t) > sizeof(fStorage)) + { + SkDEBUGF(("pthread mutex size = %d\n", sizeof(pthread_mutex_t))); + SkASSERT(!"mutex storage is too small"); + } + + int status; + pthread_mutexattr_t attr; + + status = pthread_mutexattr_init(&attr); + print_pthread_error(status); + SkASSERT(0 == status); + + status = pthread_mutex_init((pthread_mutex_t*)fStorage, &attr); + print_pthread_error(status); + SkASSERT(0 == status); +} + +SkMutex::~SkMutex() +{ + int status = pthread_mutex_destroy((pthread_mutex_t*)fStorage); + + // only report errors on non-global mutexes + if (!fIsGlobal) + { + print_pthread_error(status); + SkASSERT(0 == status); + } +} + +void SkMutex::acquire() +{ + int status = pthread_mutex_lock((pthread_mutex_t*)fStorage); + print_pthread_error(status); + SkASSERT(0 == status); +} + +void SkMutex::release() +{ + int status = pthread_mutex_unlock((pthread_mutex_t*)fStorage); + print_pthread_error(status); + SkASSERT(0 == status); +} + diff --git a/src/ports/SkThread_win.cpp b/src/ports/SkThread_win.cpp new file mode 100644 index 0000000000..d3f3e214be --- /dev/null +++ b/src/ports/SkThread_win.cpp @@ -0,0 +1,64 @@ +/* libs/graphics/ports/SkThread_none.cpp +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <windows.h> +#include "SkThread.h" + +namespace { + +template <bool> +struct CompileAssert { +}; + +} // namespace + +#define COMPILE_ASSERT(expr, msg) \ + typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] + +int32_t sk_atomic_inc(int32_t* addr) +{ + // InterlockedIncrement returns the new value, we want to return the old. + return InterlockedIncrement(reinterpret_cast<LONG*>(addr)) - 1; +} + +int32_t sk_atomic_dec(int32_t* addr) +{ + return InterlockedDecrement(reinterpret_cast<LONG*>(addr)) + 1; +} + +SkMutex::SkMutex(bool /* isGlobal */) +{ + COMPILE_ASSERT(sizeof(fStorage) > sizeof(CRITICAL_SECTION), + NotEnoughSizeForCriticalSection); + InitializeCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage)); +} + +SkMutex::~SkMutex() +{ + DeleteCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage)); +} + +void SkMutex::acquire() +{ + EnterCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage)); +} + +void SkMutex::release() +{ + LeaveCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage)); +} + diff --git a/src/ports/SkTime_Unix.cpp b/src/ports/SkTime_Unix.cpp new file mode 100644 index 0000000000..1bf3a767fb --- /dev/null +++ b/src/ports/SkTime_Unix.cpp @@ -0,0 +1,50 @@ +/* libs/graphics/ports/SkTime_Unix.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTime.h" + +#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) +#include <sys/time.h> +#include <time.h> + +void SkTime::GetDateTime(DateTime* dt) +{ + if (dt) + { + time_t m_time; + time(&m_time); + struct tm* tstruct; + tstruct = localtime(&m_time); + + dt->fYear = tstruct->tm_year; + dt->fMonth = SkToU8(tstruct->tm_mon + 1); + dt->fDayOfWeek = SkToU8(tstruct->tm_wday); + dt->fDay = SkToU8(tstruct->tm_mday); + dt->fHour = SkToU8(tstruct->tm_hour); + dt->fMinute = SkToU8(tstruct->tm_min); + dt->fSecond = SkToU8(tstruct->tm_sec); + } +} + +SkMSec SkTime::GetMSecs() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (SkMSec) (tv.tv_sec * 1000 + tv.tv_usec / 1000 ); // microseconds to milliseconds +} + +#endif diff --git a/src/ports/SkXMLParser_empty.cpp b/src/ports/SkXMLParser_empty.cpp new file mode 100644 index 0000000000..9a27306fb0 --- /dev/null +++ b/src/ports/SkXMLParser_empty.cpp @@ -0,0 +1,36 @@ +/* libs/graphics/ports/SkXMLParser_empty.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// Copyright Skia Inc. 2004 - 2005 +// +#include "SkXMLParser.h" + +bool SkXMLParser::parse(SkStream& docStream) +{ + return false; +} + +bool SkXMLParser::parse(const char doc[], size_t len) +{ + return false; +} + +void SkXMLParser::GetNativeErrorString(int error, SkString* str) +{ + +} + diff --git a/src/ports/SkXMLParser_expat.cpp b/src/ports/SkXMLParser_expat.cpp new file mode 100644 index 0000000000..7694d50f94 --- /dev/null +++ b/src/ports/SkXMLParser_expat.cpp @@ -0,0 +1,149 @@ +/* libs/graphics/ports/SkXMLParser_expat.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkXMLParser.h" +#include "SkString.h" +#include "SkStream.h" + +#include "expat.h" + +#ifdef SK_BUILD_FOR_PPI +#define CHAR_16_TO_9 +#endif + +#if defined CHAR_16_TO_9 +inline size_t sk_wcslen(const short* char16) { + const short* start = char16; + while (*char16) + char16++; + return char16 - start; +} + +inline const char* ConvertUnicodeToChar(const short* ch16, size_t len, SkAutoMalloc& ch8Malloc) { + char* ch8 = (char*) ch8Malloc.get(); + int index; + for (index = 0; index < len; index++) + ch8[index] = (char) ch16[index]; + ch8[index] = '\0'; + return ch8; +} +#endif + +static void XMLCALL start_proc(void *data, const char *el, const char **attr) +{ +#if defined CHAR_16_TO_9 + size_t len = sk_wcslen((const short*) el); + SkAutoMalloc el8(len + 1); + el = ConvertUnicodeToChar((const short*) el, len, el8); +#endif + if (((SkXMLParser*)data)->startElement(el)) { + XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false); + return; + } + while (*attr) + { + const char* attr0 = attr[0]; + const char* attr1 = attr[1]; +#if defined CHAR_16_TO_9 + size_t len0 = sk_wcslen((const short*) attr0); + SkAutoMalloc attr0_8(len0 + 1); + attr0 = ConvertUnicodeToChar((const short*) attr0, len0, attr0_8); + size_t len1 = sk_wcslen((const short*) attr1); + SkAutoMalloc attr1_8(len1 + 1); + attr1 = ConvertUnicodeToChar((const short*) attr1, len1, attr1_8); +#endif + if (((SkXMLParser*)data)->addAttribute(attr0, attr1)) { + XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false); + return; + } + attr += 2; + } +} + +static void XMLCALL end_proc(void *data, const char *el) +{ +#if defined CHAR_16_TO_9 + size_t len = sk_wcslen((const short*) el); + SkAutoMalloc el8(len + 1); + el = ConvertUnicodeToChar((const short*) el, len, el8); +#endif + if (((SkXMLParser*)data)->endElement(el)) + XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false); +} + +static void XMLCALL text_proc(void* data, const char* text, int len) +{ +#if defined CHAR_16_TO_9 + SkAutoMalloc text8(len + 1); + text = ConvertUnicodeToChar((const short*) text, len, text8); +#endif + if (((SkXMLParser*)data)->text(text, len)) + XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false); +} + +bool SkXMLParser::parse(const char doc[], size_t len) +{ + if (len == 0) { + fError->fCode = SkXMLParserError::kEmptyFile; + reportError(NULL); + return false; + } + XML_Parser p = XML_ParserCreate(NULL); + SkASSERT(p); + fParser = p; + XML_SetElementHandler(p, start_proc, end_proc); + XML_SetCharacterDataHandler(p, text_proc); + XML_SetUserData(p, this); + + bool success = true; + int error = XML_Parse(p, doc, len, true); + if (error == XML_STATUS_ERROR) { + reportError(p); + success = false; + } + XML_ParserFree(p); + return success; +} + +bool SkXMLParser::parse(SkStream& input) +{ + size_t len = input.read(NULL, 0); + SkAutoMalloc am(len); + char* doc = (char*)am.get(); + + input.rewind(); + size_t len2 = input.read(doc, len); + SkASSERT(len2 == len); + + return this->parse(doc, len2); +} + +void SkXMLParser::reportError(void* p) +{ + XML_Parser parser = (XML_Parser) p; + if (fError && parser) { + fError->fNativeCode = XML_GetErrorCode(parser); + fError->fLineNumber = XML_GetCurrentLineNumber(parser); + } +} + +void SkXMLParser::GetNativeErrorString(int error, SkString* str) +{ + if (str) + str->set(XML_ErrorString((XML_Error) error)); +} + diff --git a/src/ports/SkXMLParser_tinyxml.cpp b/src/ports/SkXMLParser_tinyxml.cpp new file mode 100644 index 0000000000..7f57b8013b --- /dev/null +++ b/src/ports/SkXMLParser_tinyxml.cpp @@ -0,0 +1,96 @@ +/* libs/graphics/ports/SkXMLParser_tinyxml.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkXMLParser.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "tinyxml.h" + +static void walk_elem(SkXMLParser* parser, const TiXmlElement* elem) +{ + //printf("walk_elem(%s) ", elem->Value()); + + parser->startElement(elem->Value()); + + const TiXmlAttribute* attr = elem->FirstAttribute(); + while (attr) + { + //printf("walk_elem_attr(%s=\"%s\") ", attr->Name(), attr->Value()); + + parser->addAttribute(attr->Name(), attr->Value()); + attr = attr->Next(); + } + //printf("\n"); + + const TiXmlNode* node = elem->FirstChild(); + while (node) + { + if (node->ToElement()) + walk_elem(parser, node->ToElement()); + else if (node->ToText()) + parser->text(node->Value(), strlen(node->Value())); + node = node->NextSibling(); + } + + parser->endElement(elem->Value()); +} + +static bool load_buf(SkXMLParser* parser, const char buf[]) +{ + TiXmlDocument doc; + + (void)doc.Parse(buf); + if (doc.Error()) + { + printf("tinyxml error: <%s> row[%d] col[%d]\n", doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol()); + return false; + } + + walk_elem(parser, doc.RootElement()); + return true; +} + +bool SkXMLParser::parse(SkStream& stream) +{ + size_t size = stream.read(NULL, 0); + + SkAutoMalloc buffer(size + 1); + char* buf = (char*)buffer.get(); + + stream.read(buf, size); + buf[size] = 0; + + return load_buf(this, buf); +} + +bool SkXMLParser::parse(const char doc[], size_t len) +{ + SkAutoMalloc buffer(len + 1); + char* buf = (char*)buffer.get(); + + memcpy(buf, doc, len); + buf[len] = 0; + + return load_buf(this, buf); +} + +void SkXMLParser::GetNativeErrorString(int error, SkString* str) +{ + if (str) + str->set("GetNativeErrorString not implemented for TinyXml"); +} + diff --git a/src/ports/SkXMLPullParser_expat.cpp b/src/ports/SkXMLPullParser_expat.cpp new file mode 100644 index 0000000000..949c7a9fdd --- /dev/null +++ b/src/ports/SkXMLPullParser_expat.cpp @@ -0,0 +1,222 @@ +/* libs/graphics/ports/SkXMLParser_expat.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkXMLParser.h" +#include "SkChunkAlloc.h" +#include "SkString.h" +#include "SkStream.h" + +#include "expat.h" + +static inline char* dupstr(SkChunkAlloc& chunk, const char src[], size_t len) +{ + SkASSERT(src); + char* dst = (char*)chunk.alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType); + + memcpy(dst, src, len); + dst[len] = 0; + return dst; +} + +static inline int count_pairs(const char** p) +{ + const char** start = p; + while (*p) + { + SkASSERT(p[1] != NULL); + p += 2; + } + return (p - start) >> 1; +} + +struct Data { + Data() : fAlloc(2048), fState(NORMAL) {} + + XML_Parser fParser; + SkXMLPullParser::Curr* fCurr; + SkChunkAlloc fAlloc; + + enum State { + NORMAL, + MISSED_START_TAG, + RETURN_END_TAG + }; + State fState; + const char* fEndTag; // if state is RETURN_END_TAG +}; + +static void XMLCALL start_proc(void *data, const char *el, const char **attr) +{ + Data* p = (Data*)data; + SkXMLPullParser::Curr* c = p->fCurr; + SkChunkAlloc& alloc = p->fAlloc; + + c->fName = dupstr(alloc, el, strlen(el)); + + int n = count_pairs(attr); + SkXMLPullParser::AttrInfo* info = (SkXMLPullParser::AttrInfo*)alloc.alloc(n * sizeof(SkXMLPullParser::AttrInfo), + SkChunkAlloc::kThrow_AllocFailType); + c->fAttrInfoCount = n; + c->fAttrInfos = info; + + for (int i = 0; i < n; i++) + { + info[i].fName = dupstr(alloc, attr[0], strlen(attr[0])); + info[i].fValue = dupstr(alloc, attr[1], strlen(attr[1])); + attr += 2; + } + + c->fEventType = SkXMLPullParser::START_TAG; + XML_StopParser(p->fParser, true); +} + +static void XMLCALL end_proc(void *data, const char *el) +{ + Data* p = (Data*)data; + SkXMLPullParser::Curr* c = p->fCurr; + + if (c->fEventType == SkXMLPullParser::START_TAG) + { + /* if we get here, we were called with a start_tag immediately + followed by this end_tag. The caller will only see the end_tag, + so we set a flag to notify them of the missed start_tag + */ + p->fState = Data::MISSED_START_TAG; + + SkASSERT(c->fName != NULL); + SkASSERT(strcmp(c->fName, el) == 0); + } + else + c->fName = dupstr(p->fAlloc, el, strlen(el)); + + c->fEventType = SkXMLPullParser::END_TAG; + XML_StopParser(p->fParser, true); +} + +#include <ctype.h> + +static bool isws(const char s[]) +{ + for (; *s; s++) + if (!isspace(*s)) + return false; + return true; +} + +static void XMLCALL text_proc(void* data, const char* text, int len) +{ + Data* p = (Data*)data; + SkXMLPullParser::Curr* c = p->fCurr; + + c->fName = dupstr(p->fAlloc, text, len); + c->fIsWhitespace = isws(c->fName); + + c->fEventType = SkXMLPullParser::TEXT; + XML_StopParser(p->fParser, true); +} + +////////////////////////////////////////////////////////////////////////// + +struct SkXMLPullParser::Impl { + Data fData; + void* fBuffer; + size_t fBufferLen; +}; + +static void reportError(XML_Parser parser) +{ + XML_Error code = XML_GetErrorCode(parser); + int lineNumber = XML_GetCurrentLineNumber(parser); + const char* msg = XML_ErrorString(code); + + printf("-------- XML error [%d] on line %d, %s\n", code, lineNumber, msg); +} + +bool SkXMLPullParser::onInit() +{ + fImpl = new Impl; + + XML_Parser p = XML_ParserCreate(NULL); + SkASSERT(p); + + fImpl->fData.fParser = p; + fImpl->fData.fCurr = &fCurr; + + XML_SetElementHandler(p, start_proc, end_proc); + XML_SetCharacterDataHandler(p, text_proc); + XML_SetUserData(p, &fImpl->fData); + + size_t len = fStream->read(NULL, 0); + fImpl->fBufferLen = len; + fImpl->fBuffer = sk_malloc_throw(len); + fStream->rewind(); + size_t len2 = fStream->read(fImpl->fBuffer, len); + return len2 == len; +} + +void SkXMLPullParser::onExit() +{ + sk_free(fImpl->fBuffer); + XML_ParserFree(fImpl->fData.fParser); + delete fImpl; + fImpl = NULL; +} + +SkXMLPullParser::EventType SkXMLPullParser::onNextToken() +{ + if (Data::RETURN_END_TAG == fImpl->fData.fState) + { + fImpl->fData.fState = Data::NORMAL; + fCurr.fName = fImpl->fData.fEndTag; // restore name from (below) save + return SkXMLPullParser::END_TAG; + } + + fImpl->fData.fAlloc.reuse(); + + XML_Parser p = fImpl->fData.fParser; + XML_Status status; + + status = XML_ResumeParser(p); + +CHECK_STATUS: + switch (status) { + case XML_STATUS_OK: + return SkXMLPullParser::END_DOCUMENT; + + case XML_STATUS_ERROR: + if (XML_GetErrorCode(p) != XML_ERROR_NOT_SUSPENDED) + { + reportError(p); + return SkXMLPullParser::ERROR; + } + status = XML_Parse(p, (const char*)fImpl->fBuffer, fImpl->fBufferLen, true); + goto CHECK_STATUS; + + case XML_STATUS_SUSPENDED: + if (Data::MISSED_START_TAG == fImpl->fData.fState) + { + // return a start_tag, and clear the flag so we return end_tag next + SkASSERT(SkXMLPullParser::END_TAG == fCurr.fEventType); + fImpl->fData.fState = Data::RETURN_END_TAG; + fImpl->fData.fEndTag = fCurr.fName; // save this pointer + return SkXMLPullParser::START_TAG; + } + break; + } + return fCurr.fEventType; +} + diff --git a/src/ports/sk_predefined_gamma.h b/src/ports/sk_predefined_gamma.h new file mode 100644 index 0000000000..0818b30a89 --- /dev/null +++ b/src/ports/sk_predefined_gamma.h @@ -0,0 +1,44 @@ +#ifndef SK_PREDEFINED_GAMMA_H +#define SK_PREDEFINED_GAMMA_H + +// Gamma table for 1.4 +static const uint8_t gBlackGamma[] = { + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, + 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0A, 0x0A, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D, + 0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, + 0x19, 0x19, 0x1A, 0x1B, 0x1C, 0x1C, 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21, 0x22, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, + 0x61, 0x62, 0x63, 0x64, 0x65, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x91, 0x92, 0x93, 0x94, 0x95, 0x97, + 0x98, 0x99, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA3, 0xA4, 0xA5, 0xA6, 0xA8, 0xA9, 0xAA, + 0xAB, 0xAD, 0xAE, 0xAF, 0xB0, 0xB2, 0xB3, 0xB4, 0xB5, 0xB7, 0xB8, 0xB9, 0xBB, 0xBC, 0xBD, 0xBE, + 0xC0, 0xC1, 0xC2, 0xC4, 0xC5, 0xC6, 0xC8, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF, 0xD1, 0xD2, 0xD3, + 0xD5, 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDD, 0xDE, 0xDF, 0xE1, 0xE2, 0xE3, 0xE5, 0xE6, 0xE8, 0xE9, + 0xEA, 0xEC, 0xED, 0xEE, 0xF0, 0xF1, 0xF2, 0xF4, 0xF5, 0xF7, 0xF8, 0xF9, 0xFB, 0xFC, 0xFE, 0xFF, +}; + +// Gamma table for 0.714286 +static const uint8_t gWhiteGamma[] = { + 0x00, 0x05, 0x08, 0x0B, 0x0D, 0x0F, 0x12, 0x14, 0x16, 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20, 0x22, + 0x23, 0x25, 0x26, 0x28, 0x29, 0x2B, 0x2C, 0x2E, 0x2F, 0x31, 0x32, 0x33, 0x35, 0x36, 0x37, 0x39, + 0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x40, 0x41, 0x43, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4B, 0x4C, + 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, + 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, + 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, + 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, + 0x8E, 0x8F, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x97, 0x98, 0x99, 0x9A, 0x9B, + 0x9C, 0x9D, 0x9E, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, + 0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB4, 0xB5, 0xB6, + 0xB7, 0xB8, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC0, 0xC1, 0xC2, 0xC3, + 0xC4, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD3, 0xD4, 0xD5, 0xD6, 0xD6, 0xD7, 0xD8, 0xD9, 0xD9, 0xDA, 0xDB, 0xDC, + 0xDC, 0xDD, 0xDE, 0xDF, 0xDF, 0xE0, 0xE1, 0xE2, 0xE2, 0xE3, 0xE4, 0xE5, 0xE5, 0xE6, 0xE7, 0xE8, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEB, 0xEC, 0xED, 0xEE, 0xEE, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2, 0xF3, 0xF3, + 0xF4, 0xF5, 0xF6, 0xF6, 0xF7, 0xF8, 0xF9, 0xF9, 0xFA, 0xFB, 0xFB, 0xFC, 0xFD, 0xFE, 0xFE, 0xFF, +}; + +#endif |