Simplify font-chaining (fallbacks) to have fonthost just return the next
authorreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 17 Mar 2009 17:59:53 +0000 (17:59 +0000)
committerreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 17 Mar 2009 17:59:53 +0000 (17:59 +0000)
logical fontID.
Extend ImageRef to accept an imagedecoder factory, to replace calling the std
one.

git-svn-id: http://skia.googlecode.com/svn/trunk@125 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkFontHost.h
include/core/SkScalerContext.h
include/images/SkImageDecoder.h
include/images/SkImageRef.h
src/core/SkScalerContext.cpp
src/images/SkImageRef.cpp
src/ports/SkFontHost_android.cpp
src/ports/SkFontHost_mac.cpp
src/ports/SkFontHost_none.cpp

index d98c55f..942350f 100644 (file)
@@ -117,11 +117,14 @@ public:
     */
     static SkScalerContext* CreateScalerContext(const SkDescriptor* desc);
 
-    /** Return a scalercontext using the "fallback" font. If there is no
-        designated fallback, return null.
+    /** Given a "current" fontID, return the next logical fontID to use
+        when searching fonts for a given unicode value. Typically the caller
+        will query a given font, and if a unicode value is not supported, they
+        will call this, and if 0 is not returned, will search that font, and so
+        on. This process must be finite, and when the fonthost sees a
+        font with no logical successor, it must return 0.
     */
-    static SkScalerContext* CreateFallbackScalerContext(
-                                                const SkScalerContext::Rec&);
+    static uint32_t NextLogicalFont(uint32_t fontID);
 
     ///////////////////////////////////////////////////////////////////////////
     
index 583833e..b06a443 100644 (file)
@@ -169,10 +169,16 @@ public:
     SkScalerContext(const SkDescriptor* desc);
     virtual ~SkScalerContext();
 
+    // remember our glyph offset/base
     void setBaseGlyphCount(unsigned baseGlyphCount) {
         fBaseGlyphCount = baseGlyphCount;
     }
 
+    /** Return the corresponding glyph for the specified unichar. Since contexts
+        may be chained (under the hood), the glyphID that is returned may in
+        fact correspond to a different font/context. In that case, we use the
+        base-glyph-count to know how to translate back into local glyph space.
+     */
     uint16_t    charToGlyphID(SkUnichar uni);
 
     unsigned    getGlyphCount() const { return this->generateGlyphCount(); }
@@ -208,12 +214,15 @@ private:
     void internalGetPath(const SkGlyph& glyph, SkPath* fillPath,
                          SkPath* devPath, SkMatrix* fillToDevMatrix);
 
-    mutable SkScalerContext* fAuxScalerContext;
+    // return the next context, treating fNextContext as a cache of the answer
+    SkScalerContext* getNextContext();
 
-    SkScalerContext* getGlyphContext(const SkGlyph& glyph) const;
-    
-    // return loaded fAuxScalerContext or NULL
-    SkScalerContext* loadAuxContext() const;
+    // returns the right context from our link-list for this glyph. If no match
+    // is found, just returns the original context (this)
+    SkScalerContext* getGlyphContext(const SkGlyph& glyph);
+
+    // link-list of context, to handle missing chars. null-terminated.
+    SkScalerContext* fNextContext;
 };
 
 #define kRec_SkDescriptorTag            SkSetFourByteTag('s', 'r', 'e', 'c')
index 3ea6198..c85a7cd 100644 (file)
@@ -147,7 +147,7 @@ public:
         If none is found, the method returns NULL.
     */
     static SkImageDecoder* Factory(SkStream*);
-    
+
     /** Decode the image stored in the specified file, and store the result
         in bitmap. Return true for success or false on failure.
 
@@ -260,4 +260,24 @@ private:
     SkImageDecoder& operator=(const SkImageDecoder&);
 };
 
+/** Calling newDecoder with a stream returns a new matching imagedecoder
+    instance, or NULL if none can be found. The caller must manage its ownership
+    of the stream as usual, calling unref() when it is done, as the returned
+    decoder may have called ref() (and if so, the decoder is responsible for
+    balancing its ownership when it is destroyed).
+ */
+class SkImageDecoderFactory : public SkRefCnt {
+public:
+    virtual SkImageDecoder* newDecoder(SkStream*) = 0;
+};
+
+class SkDefaultImageDecoderFactory : SkImageDecoderFactory {
+public:
+    // calls SkImageDecoder::Factory(stream)
+    virtual SkImageDecoder* newDecoder(SkStream* stream) {
+        return SkImageDecoder::Factory(stream);
+    }
+};
+
+
 #endif
index 26ade5e..6ab6e52 100644 (file)
@@ -52,6 +52,10 @@ public:
         and ignore the bitmap parameter.
     */
     bool getInfo(SkBitmap* bm);
+    
+    SkImageDecoderFactory* getDecoderFactory() const { return fFactory; }
+    // returns the factory parameter
+    SkImageDecoderFactory* setDecoderFactory(SkImageDecoderFactory*);
 
     // overrides
     virtual void flatten(SkFlattenableWriteBuffer&) const;
@@ -81,10 +85,11 @@ private:
     // requested state (or further, i.e. has pixels)
     bool prepareBitmap(SkImageDecoder::Mode);
 
-    SkStream*           fStream;
-    SkBitmap::Config    fConfig;
-    int                 fSampleSize;
-    bool                fErrorInDecoding;
+    SkImageDecoderFactory*  fFactory;    // may be null
+    SkStream*               fStream;
+    SkBitmap::Config        fConfig;
+    int                     fSampleSize;
+    bool                    fErrorInDecoding;
     
     friend class SkImageRefPool;
     
index bbcc130..8f84cbb 100644 (file)
@@ -95,7 +95,7 @@ SkScalerContext::SkScalerContext(const SkDescriptor* desc)
     }
 
     fBaseGlyphCount = 0;
-    fAuxScalerContext = NULL;
+    fNextContext = NULL;
 
     const Rec* rec = (const Rec*)desc->findEntry(kRec_SkDescriptorTag, NULL);
     SkASSERT(rec);
@@ -121,78 +121,97 @@ SkScalerContext::SkScalerContext(const SkDescriptor* desc)
 }
 
 SkScalerContext::~SkScalerContext() {
+    SkDELETE(fNextContext);
+
     fPathEffect->safeUnref();
     fMaskFilter->safeUnref();
     fRasterizer->safeUnref();
-
-    SkDELETE(fAuxScalerContext);
 }
 
-SkScalerContext* SkScalerContext::loadAuxContext() const {
-    if (NULL == fAuxScalerContext) {
-        fAuxScalerContext = SkFontHost::CreateFallbackScalerContext(fRec);
-        if (NULL != fAuxScalerContext) {
-            fAuxScalerContext->setBaseGlyphCount(this->getGlyphCount());
-        }
+static SkScalerContext* allocNextContext(const SkScalerContext::Rec& rec) {
+    // fonthost will determine the next possible font to search, based
+    // on the current font in fRec. It will return NULL if ctx is our
+    // last font that can be searched (i.e. ultimate fallback font)
+    uint32_t newFontID = SkFontHost::NextLogicalFont(rec.fFontID);
+    if (0 == newFontID) {
+        return NULL;
     }
-    return fAuxScalerContext;
-}
 
-#ifdef TRACK_MISSING_CHARS
-    static uint8_t gMissingChars[1 << 13];
-#endif
+    SkAutoDescriptor    ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1));
+    SkDescriptor*       desc = ad.getDesc();
 
-uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) {
-    unsigned glyphID = this->generateCharToGlyph(uni);
-
-    if (0 == glyphID) {   // try auxcontext
-        SkScalerContext* ctx = this->loadAuxContext();
-        if (NULL != ctx) {
-            glyphID = ctx->generateCharToGlyph(uni);
-            if (0 != glyphID) {   // only fiddle with it if its not missing
-                glyphID += this->getGlyphCount();
-                if (glyphID > 0xFFFF) {
-                    glyphID = 0;
-                }
-            }
+    desc->init();
+    SkScalerContext::Rec* newRec =
+    (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag,
+                                          sizeof(rec), &rec);
+    newRec->fFontID = newFontID;
+    desc->computeChecksum();
+
+    return SkFontHost::CreateScalerContext(desc);
+}
+
+/*  Return the next context, creating it if its not already created, but return
+    NULL if the fonthost says there are no more fonts to fallback to.
+ */
+SkScalerContext* SkScalerContext::getNextContext() {
+    SkScalerContext* next = fNextContext;
+    // if next is null, then either it isn't cached yet, or we're at the
+    // end of our possible chain
+    if (NULL == next) {
+        next = allocNextContext(fRec);
+        if (NULL == next) {
+            return NULL;
         }
+        // next's base is our base + our local count
+        next->setBaseGlyphCount(fBaseGlyphCount + this->getGlyphCount());
+        // cache the answer
+        fNextContext = next;
     }
-#ifdef TRACK_MISSING_CHARS
-    if (0 == glyphID) {
-        bool announce = false;
-        if (uni > 0xFFFF) {   // we don't record these
-            announce = true;
-        } else {
-            unsigned index = uni >> 3;
-            unsigned mask = 1 << (uni & 7);
-            SkASSERT(index < SK_ARRAY_COUNT(gMissingChars));
-            if ((gMissingChars[index] & mask) == 0) {
-                gMissingChars[index] |= mask;
-                announce = true;
-            }
+    return next;
+}
+
+SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) {
+    unsigned glyphID = glyph.getGlyphID();
+    SkScalerContext* ctx = this;
+    for (;;) {
+        unsigned count = ctx->getGlyphCount();
+        if (glyphID < count) {
+            break;
         }
-        if (announce) {
-            printf(">>> MISSING CHAR <<< 0x%04X\n", uni);
+        glyphID -= count;
+        ctx = ctx->getNextContext();
+        if (NULL == ctx) {
+            SkDebugf("--- no context for glyph %x\n", glyph.getGlyphID());
+            // just return the original context (this)
+            return this;
         }
     }
-#endif
-    return SkToU16(glyphID);
+    return ctx;
 }
 
-/*  Internal routine to resolve auxContextID into a real context.
-    Only makes sense to call once the glyph has been given a
-    valid auxGlyphID.
-*/
-SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) const {
-    SkScalerContext* ctx = const_cast<SkScalerContext*>(this);
-    
-    if (glyph.getGlyphID() >= this->getGlyphCount()) {
-        ctx = this->loadAuxContext();
-        if (NULL == ctx) {    // if no aux, just return us
-            ctx = const_cast<SkScalerContext*>(this);
+/*  This loops through all available fallback contexts (if needed) until it
+    finds some context that can handle the unichar. If all fail, returns 0
+ */
+uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) {
+    SkScalerContext* ctx = this;
+    unsigned glyphID;
+    for (;;) {
+        glyphID = ctx->generateCharToGlyph(uni);
+        if (glyphID) {
+            break;  // found it
+        }
+        ctx = ctx->getNextContext();
+        if (NULL == ctx) {
+            return 0;   // no more contexts, return missing glyph
         }
     }
-    return ctx;
+    // add the ctx's base, making glyphID unique for chain of contexts
+    glyphID += ctx->fBaseGlyphCount;
+    // check for overflow of 16bits, since our glyphID cannot exceed that
+    if (glyphID > 0xFFFF) {
+        glyphID = 0;
+    }
+    return SkToU16(glyphID);
 }
 
 void SkScalerContext::getAdvance(SkGlyph* glyph) {
index 8b14375..0865c88 100644 (file)
@@ -20,6 +20,7 @@ SkImageRef::SkImageRef(SkStream* stream, SkBitmap::Config config,
     fConfig = config;
     fSampleSize = sampleSize;
     fPrev = fNext = NULL;
+    fFactory = NULL;
 
 #ifdef DUMP_IMAGEREF_LIFECYCLE
     SkDebugf("add ImageRef %p [%d] data=%d\n",
@@ -36,6 +37,7 @@ SkImageRef::~SkImageRef() {
 #endif
 
     fStream->unref();
+    fFactory->safeUnref();
 }
 
 bool SkImageRef::getInfo(SkBitmap* bitmap) {
@@ -52,6 +54,12 @@ bool SkImageRef::getInfo(SkBitmap* bitmap) {
     return true;
 }
 
+SkImageDecoderFactory* SkImageRef::setDecoderFactory(
+                                                SkImageDecoderFactory* fact) {
+    SkRefCnt_SafeAssign(fFactory, fact);
+    return fact;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkImageRef::onDecode(SkImageDecoder* codec, SkStream* stream,
@@ -84,8 +92,14 @@ bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) {
     SkASSERT(fBitmap.getPixels() == NULL);
 
     fStream->rewind();
-        
-    SkImageDecoder* codec = SkImageDecoder::Factory(fStream);
+
+    SkImageDecoder* codec;
+    if (fFactory) {
+        codec = fFactory->newDecoder(fStream);
+    } else {
+        codec = SkImageDecoder::Factory(fStream);
+    }
+
     if (codec) {
         SkAutoTDelete<SkImageDecoder> ad(codec);
 
index 43bbde3..6ee75dc 100644 (file)
@@ -590,22 +590,14 @@ SkStream* SkFontHost::OpenStream(uint32_t fontID)
     return stream;
 }
 
-SkScalerContext* SkFontHost::CreateFallbackScalerContext(
-                                                const SkScalerContext::Rec& rec)
-{
+uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) {
     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);
+    if (gFallBackTypeface->uniqueID() == fontID) {
+        // no where to go, just return NULL
+        return 0;
+    }
+    return gFallBackTypeface->uniqueID();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
index 6b8f9a3..ff5905f 100755 (executable)
@@ -446,19 +446,12 @@ SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
     return new SkScalerContext_Mac(desc);
 }
 
-SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec)
-{
-    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 = find_default_fontID();
-    desc->computeChecksum();
-    
-    return SkFontHost::CreateScalerContext(desc);
+uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) {
+    uint32_t newFontID = find_default_fontID();
+    if (newFontID == fontID) {
+        newFontID = 0;
+    }
+    return newFontID;
 }
 
 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
index 608ee29..471ffd8 100644 (file)
@@ -62,10 +62,8 @@ SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
     return NULL;
 }
 
-SkScalerContext* SkFontHost::CreateFallbackScalerContext(
-                                                const SkScalerContext::Rec&) {
-    SkASSERT(!"SkFontHost::CreateFallbackScalerContext unimplemented");
-    return NULL;
+uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) {
+    return 0;
 }