handle failure when creating a scalercontext
authorreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 11 Feb 2009 15:07:19 +0000 (15:07 +0000)
committerreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 11 Feb 2009 15:07:19 +0000 (15:07 +0000)
git-svn-id: http://skia.googlecode.com/svn/trunk@90 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkScalerContext.h
src/core/SkScalerContext.cpp
src/images/SkImageDecoder.cpp
src/ports/SkFontHost_FreeType.cpp

index ec2cd0db441b27b233d7723d2003b22f233b46f8..583833e18e29e6508cee2f12fcc1cce505426ca2 100644 (file)
@@ -85,6 +85,12 @@ struct SkGlyph {
     
     size_t computeImageSize() const;
     
+    /** Call this to set all of the metrics fields to 0 (e.g. if the scaler
+        encounters an error measuring a glyph). Note: this does not alter the
+        fImage, fPath, fID, fMaskFormat fields.
+     */
+    void zeroMetrics();
+    
     enum {
         kSubBits = 2,
         kSubMask = ((1 << kSubBits) - 1),
index f63e729389b397bab1996d855b7bddff52723634..bbcc1308c1325e6407d1433e11044d40815564a7 100644 (file)
@@ -52,6 +52,19 @@ size_t SkGlyph::computeImageSize() const {
     return size;
 }
 
+void SkGlyph::zeroMetrics() {
+    fAdvanceX = 0;
+    fAdvanceY = 0;
+    fWidth    = 0;
+    fHeight   = 0;
+    fTop      = 0;
+    fLeft     = 0;
+    fRsbDelta = 0;
+    fLsbDelta = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 #ifdef SK_DEBUG
     #define DUMP_RECx
 #endif
@@ -521,10 +534,46 @@ void SkScalerContext::Rec::getSingleMatrix(SkMatrix* m) const
     }
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
 #include "SkFontHost.h"
 
+class SkScalerContext_Empty : public SkScalerContext {
+public:
+    SkScalerContext_Empty(const SkDescriptor* desc) : SkScalerContext(desc) {}
+
+protected:
+    virtual unsigned generateGlyphCount() const {
+        return 0;
+    }
+    virtual uint16_t generateCharToGlyph(SkUnichar uni) {
+        return 0;
+    }
+    virtual void generateAdvance(SkGlyph* glyph) {
+        glyph->zeroMetrics();
+    }
+    virtual void generateMetrics(SkGlyph* glyph) {
+        glyph->zeroMetrics();
+    }
+    virtual void generateImage(const SkGlyph& glyph) {}
+    virtual void generatePath(const SkGlyph& glyph, SkPath* path) {}
+    virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
+                                     SkPaint::FontMetrics* my) {
+        if (mx) {
+            bzero(mx, sizeof(*mx));
+        }
+        if (my) {
+            bzero(my, sizeof(*my));
+        }
+    }
+};
+
 SkScalerContext* SkScalerContext::Create(const SkDescriptor* desc)
 {
-    return SkFontHost::CreateScalerContext(desc);
+    SkScalerContext* c = SkFontHost::CreateScalerContext(desc);
+    if (NULL == c) {
+        c = SkNEW_ARGS(SkScalerContext_Empty, (desc));
+    }
+    return c;
 }
 
index 2d477ef4aa055921a3295d26cb5626ef71257f50..f0fff1b9b38f5e6fc13330a95b2054291dc38d49 100644 (file)
@@ -91,25 +91,62 @@ bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
 
 ///////////////////////////////////////////////////////////////////////////////
 
+/*  Technically, this should be 342, since that is the cutoff point between
+    an index and 32bit bitmap (they take equal ram), but since 32bit is almost
+    always faster, I bump up the value a bit.
+*/
+#define MIN_SIZE_FOR_INDEX  (512)
+
+/*  Return the "optimal" config for this bitmap. In this case, we just look to
+    promote index bitmaps to full-color, since those are a little faster to
+    draw (fewer memory lookups).
+
+    Seems like we could expose this to the caller through some exising or new
+    proxy object, allowing them to decide (after sniffing some aspect of the
+    original bitmap) what config they really want.
+ */
+static SkBitmap::Config optimal_config(const SkBitmap& bm,
+                                       SkBitmap::Config pref) {
+    if (bm.config() != pref) {
+        if (bm.config() == SkBitmap::kIndex8_Config) {
+            Sk64 size64 = bm.getSize64();
+            if (size64.is32()) {
+                int32_t size = size64.get32();
+                if (size < MIN_SIZE_FOR_INDEX) {
+                    return SkBitmap::kARGB_8888_Config;
+                }
+            }
+        }
+    }
+    return bm.config();
+}
+
 bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
                             SkBitmap::Config pref, Mode mode) {
+    // pass a temporary bitmap, so that if we return false, we are assured of
+    // leaving the caller's bitmap untouched.
     SkBitmap    tmp;
 
     // we reset this to false before calling onDecode
     fShouldCancelDecode = false;
 
-    // pass a temporary bitmap, so that if we return false, we are assured of
-    // leaving the caller's bitmap untouched.
-    if (this->onDecode(stream, &tmp, pref, mode)) {
-        /*  We operate on a tmp bitmap until we know we succeed. This way
-         we're sure we don't change the caller's bitmap and then later
-         return false. Returning false must mean that their parameter
-         is unchanged.
-         */
-        bm->swap(tmp);
-        return true;
+    if (!this->onDecode(stream, &tmp, pref, mode)) {
+        return false;
     }
-    return false;
+
+    SkBitmap::Config c = optimal_config(tmp, pref);
+    if (c != tmp.config()) {
+        if (mode == kDecodeBounds_Mode) {
+            tmp.setConfig(c, tmp.width(), tmp.height());
+        } else {
+            SkBitmap tmp2;
+            if (tmp.copyTo(&tmp2, c, this->getAllocator())) {
+                tmp.swap(tmp2);
+            }
+        }
+    }
+    bm->swap(tmp);
+    return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
index 5adeb136048fe79f4a334bdc80bc672e5ddb7faf..742a861ee4ca7f7c2b0cf45c8bcfc4d6c439dece 100644 (file)
@@ -63,6 +63,10 @@ class SkScalerContext_FreeType : public SkScalerContext {
 public:
     SkScalerContext_FreeType(const SkDescriptor* desc);
     virtual ~SkScalerContext_FreeType();
+    
+    bool success() const {
+        return fFaceRec != NULL && fFTSize != NULL;
+    }
 
 protected:
     virtual unsigned generateGlyphCount() const;
@@ -146,6 +150,7 @@ SkFaceRec::SkFaceRec(SkStream* strm, uint32_t fontID)
     fFTStream.close = sk_stream_close;
 }
 
+// Will return 0 on failure
 static SkFaceRec* ref_ft_face(uint32_t fontID) {
     SkFaceRec* rec = gFaceRecHead;
     while (rec) {
@@ -160,7 +165,6 @@ static SkFaceRec* ref_ft_face(uint32_t fontID) {
     SkStream* strm = SkFontHost::OpenStream(fontID);
     if (NULL == strm) {
         SkDEBUGF(("SkFontHost::OpenStream failed opening %x\n", fontID));
-        sk_throw();
         return 0;
     }
 
@@ -187,7 +191,6 @@ static SkFaceRec* ref_ft_face(uint32_t fontID) {
     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);
@@ -225,7 +228,7 @@ static void unref_ft_face(FT_Face face) {
 ///////////////////////////////////////////////////////////////////////////
 
 SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc)
-        : SkScalerContext(desc), fFTSize(NULL) {
+        : SkScalerContext(desc) {
     SkAutoMutexAcquire  ac(gFTMutex);
 
     FT_Error    err;
@@ -238,8 +241,13 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc)
     ++gFTCount;
 
     // load the font file
+    fFTSize = NULL;
+    fFace = NULL;
     fFaceRec = ref_ft_face(fRec.fFontID);
-    fFace = fFaceRec ? fFaceRec->fFace : NULL;
+    if (NULL == fFaceRec) {
+        return;
+    }
+    fFace = fFaceRec->fFace;
 
     // compute our factors from the record
 
@@ -417,17 +425,6 @@ static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
     }
 }
 
-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
@@ -437,7 +434,7 @@ void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) {
         SkAutoMutexAcquire  ac(gFTMutex);
 
         if (this->setupSize()) {
-            set_glyph_metrics_on_error(glyph);
+            glyph->zeroMetrics();
             return;
         }
 
@@ -478,7 +475,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
         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);
+        glyph->zeroMetrics();
         return;
     }
 
@@ -769,7 +766,12 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx,
 ////////////////////////////////////////////////////////////////////////
 
 SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
-    return SkNEW_ARGS(SkScalerContext_FreeType, (desc));
+    SkScalerContext_FreeType* c = SkNEW_ARGS(SkScalerContext_FreeType, (desc));
+    if (!c->success()) {
+        SkDELETE(c);
+        c = NULL;
+    }
+    return c;
 }
 
 ///////////////////////////////////////////////////////////////////////////////