aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ports
diff options
context:
space:
mode:
authorGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2008-12-17 15:59:43 +0000
committerGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2008-12-17 15:59:43 +0000
commit8a1c16ff38322f0210116fa7293eb8817c7e477e (patch)
treefe40e07f6c8983318a2f79032b9a706ede1090c1 /src/ports
parent2559c629078f738ac37095d896d580b681ac6a30 (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')
-rw-r--r--src/ports/SkFontHost_FONTPATH.cpp415
-rw-r--r--src/ports/SkFontHost_FreeType.cpp829
-rw-r--r--src/ports/SkFontHost_android.cpp637
-rw-r--r--src/ports/SkFontHost_ascender.cpp211
-rw-r--r--src/ports/SkFontHost_gamma.cpp118
-rw-r--r--src/ports/SkFontHost_linux.cpp604
-rwxr-xr-xsrc/ports/SkFontHost_mac.cpp563
-rw-r--r--src/ports/SkFontHost_none.cpp82
-rw-r--r--src/ports/SkFontHost_win.cpp601
-rw-r--r--src/ports/SkGlobals_global.cpp28
-rw-r--r--src/ports/SkImageDecoder_Factory.cpp104
-rw-r--r--src/ports/SkImageRef_ashmem.cpp203
-rw-r--r--src/ports/SkImageRef_ashmem.h35
-rw-r--r--src/ports/SkOSEvent_android.cpp155
-rw-r--r--src/ports/SkOSEvent_dummy.cpp28
-rw-r--r--src/ports/SkOSFile_stdio.cpp106
-rw-r--r--src/ports/SkThread_none.cpp49
-rw-r--r--src/ports/SkThread_pthread.cpp90
-rw-r--r--src/ports/SkThread_win.cpp64
-rw-r--r--src/ports/SkTime_Unix.cpp50
-rw-r--r--src/ports/SkXMLParser_empty.cpp36
-rw-r--r--src/ports/SkXMLParser_expat.cpp149
-rw-r--r--src/ports/SkXMLParser_tinyxml.cpp96
-rw-r--r--src/ports/SkXMLPullParser_expat.cpp222
-rw-r--r--src/ports/sk_predefined_gamma.h44
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