SkBitmap now really stores SkImageInfo -- config is just a ruse

BUG=skia:
R=scroggo@google.com

Author: reed@google.com

Review URL: https://codereview.chromium.org/159653004

git-svn-id: http://skia.googlecode.com/svn/trunk@13411 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h
index 87d4649..42ef877 100644
--- a/include/core/SkBitmap.h
+++ b/include/core/SkBitmap.h
@@ -79,10 +79,42 @@
     //  This method is not exported to java.
     void swap(SkBitmap& other);
 
+    ///////////////////////////////////////////////////////////////////////////
+
+    const SkImageInfo& info() const { return fInfo; }
+
+    int width() const { return fInfo.fWidth; }
+    int height() const { return fInfo.fHeight; }
+    SkColorType colorType() const { return fInfo.fColorType; }
+    SkAlphaType alphaType() const { return fInfo.fAlphaType; }
+
+    /** Return the number of bytes per pixel based on the config. If the config
+     does not have at least 1 byte per (e.g. kA1_Config) then 0 is returned.
+     */
+    int bytesPerPixel() const { return fInfo.bytesPerPixel(); }
+
+    /** Return the rowbytes expressed as a number of pixels (like width and
+     height). Note, for 1-byte per pixel configs like kA8_Config, this will
+     return the same as rowBytes(). Is undefined for configs that are less
+     than 1-byte per pixel (e.g. kA1_Config)
+     */
+    int rowBytesAsPixels() const {
+        return fRowBytes >> this->shiftPerPixel();
+    }
+
+    /** Return the shift amount per pixel (i.e. 0 for 1-byte per pixel, 1 for
+     2-bytes per pixel configs, 2 for 4-bytes per pixel configs). Return 0
+     for configs that are not at least 1-byte per pixel (e.g. kA1_Config
+     or kNo_Config)
+     */
+    int shiftPerPixel() const { return this->bytesPerPixel() >> 1; }
+
+    ///////////////////////////////////////////////////////////////////////////
+
     /** Return true iff the bitmap has empty dimensions.
      *  Hey!  Before you use this, see if you really want to know drawsNothing() instead.
      */
-    bool empty() const { return 0 == fWidth || 0 == fHeight; }
+    bool empty() const { return fInfo.isEmpty(); }
 
     /** Return true iff the bitmap has no pixelref. Note: this can return true even if the
      *  dimensions of the bitmap are > 0 (see empty()).
@@ -95,41 +127,14 @@
     bool drawsNothing() const { return this->empty() || this->isNull(); }
 
     /** Return the config for the bitmap. */
-    Config  config() const { return (Config)fConfig; }
+    Config  config() const;
 
     SK_ATTR_DEPRECATED("use config()")
     Config  getConfig() const { return this->config(); }
 
-    /** Return the bitmap's width, in pixels. */
-    int width() const { return fWidth; }
-
-    /** Return the bitmap's height, in pixels. */
-    int height() const { return fHeight; }
-
     /** Return the number of bytes between subsequent rows of the bitmap. */
     size_t rowBytes() const { return fRowBytes; }
 
-    /** Return the shift amount per pixel (i.e. 0 for 1-byte per pixel, 1 for
-        2-bytes per pixel configs, 2 for 4-bytes per pixel configs). Return 0
-        for configs that are not at least 1-byte per pixel (e.g. kA1_Config
-        or kNo_Config)
-    */
-    int shiftPerPixel() const { return fBytesPerPixel >> 1; }
-
-    /** Return the number of bytes per pixel based on the config. If the config
-        does not have at least 1 byte per (e.g. kA1_Config) then 0 is returned.
-    */
-    int bytesPerPixel() const { return fBytesPerPixel; }
-
-    /** Return the rowbytes expressed as a number of pixels (like width and
-        height). Note, for 1-byte per pixel configs like kA8_Config, this will
-        return the same as rowBytes(). Is undefined for configs that are less
-        than 1-byte per pixel (e.g. kA1_Config)
-    */
-    int rowBytesAsPixels() const { return fRowBytes >> (fBytesPerPixel >> 1); }
-
-    SkAlphaType alphaType() const { return (SkAlphaType)fAlphaType; }
-
     /**
      *  Set the bitmap's alphaType, returning true on success. If false is
      *  returned, then the specified new alphaType is incompatible with the
@@ -149,19 +154,19 @@
         Note this truncates the result to 32bits. Call getSize64() to detect
         if the real size exceeds 32bits.
     */
-    size_t getSize() const { return fHeight * fRowBytes; }
+    size_t getSize() const { return fInfo.fHeight * fRowBytes; }
 
     /** Return the number of bytes from the pointer returned by getPixels()
         to the end of the allocated space in the buffer. Required in
         cases where extractSubset has been called.
     */
-    size_t getSafeSize() const ;
+    size_t getSafeSize() const { return fInfo.getSafeSize(fRowBytes); }
 
     /**
      *  Return the full size of the bitmap, in bytes.
      */
     int64_t computeSize64() const {
-        return sk_64_mul(fHeight, fRowBytes);
+        return sk_64_mul(fInfo.fHeight, fRowBytes);
     }
 
     /**
@@ -170,7 +175,7 @@
      *  than computeSize64() if there is any rowbytes padding beyond the width.
      */
     int64_t computeSafeSize64() const {
-        return ComputeSafeSize64((Config)fConfig, fWidth, fHeight, fRowBytes);
+        return fInfo.getSafeSize64(fRowBytes);
     }
 
     /** Returns true if this bitmap is marked as immutable, meaning that the
@@ -304,11 +309,18 @@
                        void* context);
 
     /**
-     *  If the bitmap's config can be represented as SkImageInfo, return true,
-     *  and if info is not-null, set it to the bitmap's info. If it cannot be
-     *  represented as SkImageInfo, return false and ignore the info parameter.
+     *  DEPRECATED: call info().
      */
-    bool asImageInfo(SkImageInfo* info) const;
+    bool asImageInfo(SkImageInfo* info) const {
+        // compatibility: return false for kUnknown
+        if (kUnknown_SkColorType == this->colorType()) {
+            return false;
+        }
+        if (info) {
+            *info = this->info();
+        }
+        return true;
+    }
 
     /** Use this to assign a new pixel address for an existing bitmap. This
         will automatically release any pixelref previously installed. Only call
@@ -450,8 +462,8 @@
      */
     GrTexture* getTexture() const;
 
-    /** Return the bitmap's colortable, if it uses one (i.e. fConfig is
-        kIndex8_Config) and the pixels are locked.
+    /** Return the bitmap's colortable, if it uses one (i.e. colorType is
+        Index_8) and the pixels are locked.
         Otherwise returns NULL. Does not affect the colortable's
         reference count.
     */
@@ -742,13 +754,11 @@
 #endif
     };
 
+    SkImageInfo fInfo;
+
     uint32_t    fRowBytes;
-    uint32_t    fWidth;
-    uint32_t    fHeight;
-    uint8_t     fConfig;
-    uint8_t     fAlphaType;
+
     uint8_t     fFlags;
-    uint8_t     fBytesPerPixel; // based on config
 
     void internalErase(const SkIRect&, U8CPU a, U8CPU r, U8CPU g, U8CPU b)const;
 
@@ -860,29 +870,29 @@
 
 inline uint32_t* SkBitmap::getAddr32(int x, int y) const {
     SkASSERT(fPixels);
-    SkASSERT(fConfig == kARGB_8888_Config);
-    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    SkASSERT(this->config() == kARGB_8888_Config);
+    SkASSERT((unsigned)x < (unsigned)this->width() && (unsigned)y < (unsigned)this->height());
     return (uint32_t*)((char*)fPixels + y * fRowBytes + (x << 2));
 }
 
 inline uint16_t* SkBitmap::getAddr16(int x, int y) const {
     SkASSERT(fPixels);
-    SkASSERT(fConfig == kRGB_565_Config || fConfig == kARGB_4444_Config);
-    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    SkASSERT(this->config() == kRGB_565_Config || this->config() == kARGB_4444_Config);
+    SkASSERT((unsigned)x < (unsigned)this->width() && (unsigned)y < (unsigned)this->height());
     return (uint16_t*)((char*)fPixels + y * fRowBytes + (x << 1));
 }
 
 inline uint8_t* SkBitmap::getAddr8(int x, int y) const {
     SkASSERT(fPixels);
-    SkASSERT(fConfig == kA8_Config || fConfig == kIndex8_Config);
-    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    SkASSERT(this->config() == kA8_Config || this->config() == kIndex8_Config);
+    SkASSERT((unsigned)x < (unsigned)this->width() && (unsigned)y < (unsigned)this->height());
     return (uint8_t*)fPixels + y * fRowBytes + x;
 }
 
 inline SkPMColor SkBitmap::getIndex8Color(int x, int y) const {
     SkASSERT(fPixels);
-    SkASSERT(fConfig == kIndex8_Config);
-    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    SkASSERT(this->config() == kIndex8_Config);
+    SkASSERT((unsigned)x < (unsigned)this->width() && (unsigned)y < (unsigned)this->height());
     SkASSERT(fColorTable);
     return (*fColorTable)[*((const uint8_t*)fPixels + y * fRowBytes + x)];
 }
diff --git a/include/core/SkImageInfo.h b/include/core/SkImageInfo.h
index 722ff27..8ca87eb 100644
--- a/include/core/SkImageInfo.h
+++ b/include/core/SkImageInfo.h
@@ -8,7 +8,7 @@
 #ifndef SkImageInfo_DEFINED
 #define SkImageInfo_DEFINED
 
-#include "SkTypes.h"
+#include "SkMath.h"
 #include "SkSize.h"
 
 class SkWriteBuffer;
@@ -59,12 +59,17 @@
     return (unsigned)at <= kOpaque_SkAlphaType;
 }
 
+static inline bool SkAlphaTypeIsValid(unsigned value) {
+    return value <= kLastEnum_SkAlphaType;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  *  Describes how to interpret the components of a pixel.
  */
 enum SkColorType {
+    kUnknown_SkColorType,
     kAlpha_8_SkColorType,
     kRGB_565_SkColorType,
     kARGB_4444_SkColorType,
@@ -85,6 +90,7 @@
 
 static int SkColorTypeBytesPerPixel(SkColorType ct) {
     static const uint8_t gSize[] = {
+        0,  // Unknown
         1,  // Alpha_8
         2,  // RGB_565
         2,  // ARGB_4444
@@ -99,6 +105,14 @@
     return gSize[ct];
 }
 
+static inline size_t SkColorTypeMinRowBytes(SkColorType ct, int width) {
+    return width * SkColorTypeBytesPerPixel(ct);
+}
+
+static inline bool SkColorTypeIsValid(unsigned value) {
+    return value <= kLastEnum_SkColorType;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
@@ -159,16 +173,29 @@
         return info;
     }
 
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+    SkColorType colorType() const { return fColorType; }
+    SkAlphaType alphaType() const { return fAlphaType; }
+
+    bool isEmpty() const { return fWidth <= 0 || fHeight <= 0; }
+
     bool isOpaque() const {
         return SkAlphaTypeIsOpaque(fAlphaType);
     }
 
+    SkISize dimensions() const { return SkISize::Make(fWidth, fHeight); }
+
     int bytesPerPixel() const {
         return SkColorTypeBytesPerPixel(fColorType);
     }
 
+    uint64_t minRowBytes64() const {
+        return sk_64_mul(fWidth, this->bytesPerPixel());
+    }
+
     size_t minRowBytes() const {
-        return fWidth * this->bytesPerPixel();
+        return (size_t)this->minRowBytes64();
     }
 
     bool operator==(const SkImageInfo& other) const {
@@ -181,12 +208,23 @@
     void unflatten(SkReadBuffer&);
     void flatten(SkWriteBuffer&) const;
 
-    size_t getSafeSize(size_t rowBytes) const {
+    int64_t getSafeSize64(size_t rowBytes) const {
         if (0 == fHeight) {
             return 0;
         }
-        return (fHeight - 1) * rowBytes + fWidth * this->bytesPerPixel();
+        return sk_64_mul(fHeight - 1, rowBytes) + fWidth * this->bytesPerPixel();
     }
+
+    size_t getSafeSize(size_t rowBytes) const {
+        return (size_t)this->getSafeSize64(rowBytes);
+    }
+
+    bool validRowBytes(size_t rowBytes) const {
+        uint64_t rb = sk_64_mul(fWidth, this->bytesPerPixel());
+        return rowBytes >= rb;
+    }
+
+    SkDEBUGCODE(void validate() const;)
 };
 
 #endif
diff --git a/include/core/SkPathRef.h b/include/core/SkPathRef.h
index 3c663f7..2e8c547 100644
--- a/include/core/SkPathRef.h
+++ b/include/core/SkPathRef.h
@@ -106,7 +106,7 @@
 
         // In some cases we need to inject a leading moveTo before we add points
         // for lineTo, quadTo, conicTo, cubicTo
-        // 
+        //
         // SkPath path; path.lineTo(...);   <--- need a leading moveTo(0, 0)
         // SkPath path; ... path.close(); path.lineTo(...) <-- need a moveTo(previous moveTo)
         void injectMoveToIfNeeded() { fPathRef->injectMoveToIfNeeded(); }
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
index 6a70c1e..96d683c 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -24,6 +24,11 @@
 #include "SkPackBits.h"
 #include <new>
 
+static bool reset_return_false(SkBitmap* bm) {
+    bm->reset();
+    return false;
+}
+
 struct MipLevel {
     void*       fPixels;
     uint32_t    fRowBytes;
@@ -125,13 +130,9 @@
     SkTSwap(fPixelLockCount, other.fPixelLockCount);
     SkTSwap(fMipMap, other.fMipMap);
     SkTSwap(fPixels, other.fPixels);
+    SkTSwap(fInfo, other.fInfo);
     SkTSwap(fRowBytes, other.fRowBytes);
-    SkTSwap(fWidth, other.fWidth);
-    SkTSwap(fHeight, other.fHeight);
-    SkTSwap(fConfig, other.fConfig);
-    SkTSwap(fAlphaType, other.fAlphaType);
     SkTSwap(fFlags, other.fFlags);
-    SkTSwap(fBytesPerPixel, other.fBytesPerPixel);
 
     SkDEBUGCODE(this->validate();)
 }
@@ -141,6 +142,10 @@
     sk_bzero(this, sizeof(*this));
 }
 
+SkBitmap::Config SkBitmap::config() const {
+    return SkColorTypeToBitmapConfig(fInfo.colorType());
+}
+
 int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
     int bpp;
     switch (config) {
@@ -167,39 +172,12 @@
 }
 
 size_t SkBitmap::ComputeRowBytes(Config c, int width) {
-    if (width < 0) {
-        return 0;
-    }
-
-    int64_t rowBytes = 0;
-
-    switch (c) {
-        case kNo_Config:
-            break;
-        case kA8_Config:
-        case kIndex8_Config:
-            rowBytes = width;
-            break;
-        case kRGB_565_Config:
-        case kARGB_4444_Config:
-            // assign and then shift, so we don't overflow int
-            rowBytes = width;
-            rowBytes <<= 1;
-            break;
-        case kARGB_8888_Config:
-            // assign and then shift, so we don't overflow int
-            rowBytes = width;
-            rowBytes <<= 2;
-            break;
-        default:
-            SkDEBUGFAIL("unknown config");
-            break;
-    }
-    return sk_64_isS32(rowBytes) ? sk_64_asS32(rowBytes) : 0;
+    return SkColorTypeMinRowBytes(SkBitmapConfigToColorType(c), width);
 }
 
 int64_t SkBitmap::ComputeSize64(Config config, int width, int height) {
-    int64_t rowBytes = sk_64_mul(ComputeBytesPerPixel(config), width);
+    SkColorType ct = SkBitmapConfigToColorType(config);
+    int64_t rowBytes = sk_64_mul(SkColorTypeBytesPerPixel(ct), width);
     return rowBytes * height;
 }
 
@@ -212,13 +190,10 @@
                                     uint32_t width,
                                     uint32_t height,
                                     size_t rowBytes) {
-    int64_t safeSize = 0;
-    if (height > 0) {
-        int64_t lastRow = sk_64_mul(ComputeBytesPerPixel(config), width);
-        safeSize = sk_64_mul(height - 1, rowBytes) + lastRow;
-    }
-    SkASSERT(safeSize >= 0);
-    return safeSize;
+    SkImageInfo info = SkImageInfo::Make(width, height,
+                                         SkBitmapConfigToColorType(config),
+                                         kPremul_SkAlphaType);
+    return info.getSafeSize64(rowBytes);
 }
 
 size_t SkBitmap::ComputeSafeSize(Config config,
@@ -237,35 +212,36 @@
 void SkBitmap::getBounds(SkRect* bounds) const {
     SkASSERT(bounds);
     bounds->set(0, 0,
-                SkIntToScalar(fWidth), SkIntToScalar(fHeight));
+                SkIntToScalar(fInfo.fWidth), SkIntToScalar(fInfo.fHeight));
 }
 
 void SkBitmap::getBounds(SkIRect* bounds) const {
     SkASSERT(bounds);
-    bounds->set(0, 0, fWidth, fHeight);
+    bounds->set(0, 0, fInfo.fWidth, fInfo.fHeight);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static bool validate_alphaType(SkBitmap::Config config, SkAlphaType alphaType,
+static bool validate_alphaType(SkColorType colorType, SkAlphaType alphaType,
                                SkAlphaType* canonical = NULL) {
-    switch (config) {
-        case SkBitmap::kNo_Config:
+    switch (colorType) {
+        case kUnknown_SkColorType:
             alphaType = kIgnore_SkAlphaType;
             break;
-        case SkBitmap::kA8_Config:
+        case kAlpha_8_SkColorType:
             if (kUnpremul_SkAlphaType == alphaType) {
                 alphaType = kPremul_SkAlphaType;
             }
             // fall-through
-        case SkBitmap::kIndex8_Config:
-        case SkBitmap::kARGB_4444_Config:
-        case SkBitmap::kARGB_8888_Config:
+        case kIndex_8_SkColorType:
+        case kARGB_4444_SkColorType:
+        case kRGBA_8888_SkColorType:
+        case kBGRA_8888_SkColorType:
             if (kIgnore_SkAlphaType == alphaType) {
                 return false;
             }
             break;
-        case SkBitmap::kRGB_565_Config:
+        case kRGB_565_SkColorType:
             alphaType = kOpaque_SkAlphaType;
             break;
         default:
@@ -277,52 +253,48 @@
     return true;
 }
 
-bool SkBitmap::setConfig(Config config, int width, int height, size_t rowBytes,
-                         SkAlphaType alphaType) {
-    if ((width | height) < 0) {
-        goto BAD_CONFIG;
+bool SkBitmap::setConfig(const SkImageInfo& info, size_t rowBytes) {
+    // require that rowBytes fit in 31bits
+    int64_t mrb = info.minRowBytes64();
+    if ((int32_t)mrb != mrb) {
+        return reset_return_false(this);
     }
-    if (rowBytes == 0) {
-        rowBytes = SkBitmap::ComputeRowBytes(config, width);
-        if (0 == rowBytes && kNo_Config != config && width > 0) {
-            goto BAD_CONFIG;
-        }
+    if ((int64_t)rowBytes != (int32_t)rowBytes) {
+        return reset_return_false(this);
     }
 
-    if (!validate_alphaType(config, alphaType, &alphaType)) {
-        goto BAD_CONFIG;
+    if (info.width() < 0 || info.height() < 0) {
+        return reset_return_false(this);
+    }
+
+    if (kUnknown_SkColorType == info.colorType()) {
+        rowBytes = 0;
+    } else if (0 == rowBytes) {
+        rowBytes = (size_t)mrb;
+    } else if (rowBytes < info.minRowBytes()) {
+        return reset_return_false(this);
     }
 
     this->freePixels();
 
-    fConfig     = SkToU8(config);
-    fAlphaType  = SkToU8(alphaType);
-    fWidth      = width;
-    fHeight     = height;
-    fRowBytes   = SkToU32(rowBytes);
-
-    fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(config);
-
-    SkDEBUGCODE(this->validate();)
+    fInfo = info;
+    fRowBytes = SkToU32(rowBytes);
     return true;
-
-    // if we got here, we had an error, so we reset the bitmap to empty
-BAD_CONFIG:
-    this->reset();
-    return false;
 }
 
-bool SkBitmap::setConfig(const SkImageInfo& info, size_t rowBytes) {
-    return this->setConfig(SkImageInfoToBitmapConfig(info), info.fWidth,
-                           info.fHeight, rowBytes, info.fAlphaType);
+bool SkBitmap::setConfig(Config config, int width, int height, size_t rowBytes,
+                         SkAlphaType alphaType) {
+    SkColorType ct = SkBitmapConfigToColorType(config);
+    return this->setConfig(SkImageInfo::Make(width, height, ct, alphaType),
+                           rowBytes);
 }
 
 bool SkBitmap::setAlphaType(SkAlphaType alphaType) {
-    if (!validate_alphaType(this->config(), alphaType, &alphaType)) {
+    if (!validate_alphaType(fInfo.fColorType, alphaType, &alphaType)) {
         return false;
     }
-    if (fAlphaType != alphaType) {
-        fAlphaType = SkToU8(alphaType);
+    if (fInfo.fAlphaType != alphaType) {
+        fInfo.fAlphaType = alphaType;
         if (fPixelRef) {
             fPixelRef->changeAlphaType(alphaType);
         }
@@ -339,7 +311,7 @@
             if (NULL != p) {
                 p = (char*)p
                     + fPixelRefOrigin.fY * fRowBytes
-                    + fPixelRefOrigin.fX * fBytesPerPixel;
+                    + fPixelRefOrigin.fX * fInfo.bytesPerPixel();
             }
             fPixels = p;
             fColorTable = fPixelRef->colorTable();
@@ -379,20 +351,6 @@
     return true;
 }
 
-bool SkBitmap::asImageInfo(SkImageInfo* info) const {
-    SkColorType ct;
-    if (!config_to_colorType(this->config(), &ct)) {
-        return false;
-    }
-    if (info) {
-        info->fWidth = fWidth;
-        info->fHeight = fHeight;
-        info->fAlphaType = this->alphaType();
-        info->fColorType = ct;
-    }
-    return true;
-}
-
 SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, int dx, int dy) {
 #ifdef SK_DEBUG
     if (pr) {
@@ -404,7 +362,7 @@
             SkASSERT(info.fColorType == prInfo.fColorType);
             switch (prInfo.fAlphaType) {
                 case kIgnore_SkAlphaType:
-                    SkASSERT(fAlphaType == kIgnore_SkAlphaType);
+                    SkASSERT(fInfo.fAlphaType == kIgnore_SkAlphaType);
                     break;
                 case kOpaque_SkAlphaType:
                 case kPremul_SkAlphaType:
@@ -502,11 +460,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static bool reset_return_false(SkBitmap* bm) {
-    bm->reset();
-    return false;
-}
-
 bool SkBitmap::allocPixels(const SkImageInfo& info, SkPixelRefFactory* factory,
                            SkColorTable* ctable) {
     if (kIndex_8_SkColorType == info.fColorType && NULL == ctable) {
@@ -562,10 +515,6 @@
     }
 
     SkAlphaType at = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
-    if (!validate_alphaType(config, at, &at)) {
-        return false;
-    }
-
     return this->allocPixels(SkImageInfo::Make(width, height, ct, at));
 }
 
@@ -637,13 +586,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-size_t SkBitmap::getSafeSize() const {
-    // This is intended to be a size_t version of ComputeSafeSize64(), just
-    // faster. The computation is meant to be identical.
-    return (fHeight ? ((fHeight - 1) * fRowBytes) +
-            ComputeRowBytes(this->config(), fWidth): 0);
-}
-
 bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
                             size_t dstRowBytes, bool preserveDstPad) const {
 
@@ -651,9 +593,10 @@
         dstRowBytes = fRowBytes;
     }
 
-    if (dstRowBytes < ComputeRowBytes(this->config(), fWidth) ||
-        dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
+    if (dstRowBytes < fInfo.minRowBytes() ||
+        dst == NULL || (getPixels() == NULL && pixelRef() == NULL)) {
         return false;
+    }
 
     if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
         size_t safeSize = this->getSafeSize();
@@ -670,16 +613,15 @@
         }
     } else {
         // If destination has different stride than us, then copy line by line.
-        if (ComputeSafeSize(this->config(), fWidth, fHeight, dstRowBytes) >
-            dstSize)
+        if (fInfo.getSafeSize(dstRowBytes) > dstSize) {
             return false;
-        else {
+        } else {
             // Just copy what we need on each line.
-            size_t rowBytes = ComputeRowBytes(this->config(), fWidth);
+            size_t rowBytes = fInfo.minRowBytes();
             SkAutoLockPixels lock(*this);
             const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
             uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
-            for (uint32_t row = 0; row < fHeight;
+            for (int row = 0; row < fInfo.fHeight;
                  row++, srcP += fRowBytes, dstP += dstRowBytes) {
                 memcpy(dstP, srcP, rowBytes);
             }
@@ -723,19 +665,19 @@
     char* base = (char*)this->getPixels();
     if (base) {
         base += y * this->rowBytes();
-        switch (this->config()) {
-            case SkBitmap::kARGB_8888_Config:
+        switch (this->colorType()) {
+            case kRGBA_8888_SkColorType:
+            case kBGRA_8888_SkColorType:
                 base += x << 2;
                 break;
-            case SkBitmap::kARGB_4444_Config:
-            case SkBitmap::kRGB_565_Config:
+            case kARGB_4444_SkColorType:
+            case kRGB_565_SkColorType:
                 base += x << 1;
                 break;
-            case SkBitmap::kA8_Config:
-            case SkBitmap::kIndex8_Config:
+            case kAlpha_8_SkColorType:
+            case kIndex_8_SkColorType:
                 base += x;
                 break;
-                break;
             default:
                 SkDEBUGFAIL("Can't return addr for config");
                 base = NULL;
@@ -873,8 +815,12 @@
     }
 #endif
 
-    if (kNo_Config == fConfig || kIndex8_Config == fConfig) {
-        return;
+    switch (fInfo.colorType()) {
+        case kUnknown_SkColorType:
+        case kIndex_8_SkColorType:
+            return; // can't erase
+        default:
+            break;
     }
 
     SkAutoLockPixels alp(*this);
@@ -894,8 +840,8 @@
         b = SkAlphaMul(b, a);
     }
 
-    switch (fConfig) {
-        case kA8_Config: {
+    switch (this->colorType()) {
+        case kAlpha_8_SkColorType: {
             uint8_t* p = this->getAddr8(area.fLeft, area.fTop);
             while (--height >= 0) {
                 memset(p, a, width);
@@ -903,12 +849,12 @@
             }
             break;
         }
-        case kARGB_4444_Config:
-        case kRGB_565_Config: {
+        case kARGB_4444_SkColorType:
+        case kRGB_565_SkColorType: {
             uint16_t* p = this->getAddr16(area.fLeft, area.fTop);;
             uint16_t v;
 
-            if (kARGB_4444_Config == fConfig) {
+            if (kARGB_4444_SkColorType == this->colorType()) {
                 v = pack_8888_to_4444(a, r, g, b);
             } else {
                 v = SkPackRGB16(r >> (8 - SK_R16_BITS),
@@ -921,7 +867,9 @@
             }
             break;
         }
-        case kARGB_8888_Config: {
+        case kPMColor_SkColorType: {
+            // what to do about BGRA or RGBA (which ever is != PMColor ?
+            // for now we don't support them.
             uint32_t* p = this->getAddr32(area.fLeft, area.fTop);
             uint32_t  v = SkPackARGB32(a, r, g, b);
 
@@ -931,6 +879,8 @@
             }
             break;
         }
+        default:
+            return; // no change, so don't call notifyPixelsChanged()
     }
 
     this->notifyPixelsChanged();
@@ -1046,7 +996,8 @@
 
     if (fPixelRef) {
         SkIRect subset;
-        subset.setXYWH(fPixelRefOrigin.fX, fPixelRefOrigin.fY, fWidth, fHeight);
+        subset.setXYWH(fPixelRefOrigin.fX, fPixelRefOrigin.fY,
+                       fInfo.width(), fInfo.height());
         if (fPixelRef->readPixels(&tmpSrc, &subset)) {
             SkASSERT(tmpSrc.width() == this->width());
             SkASSERT(tmpSrc.height() == this->height());
@@ -1160,6 +1111,8 @@
 }
 
 bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const {
+    const SkColorType dstCT = SkBitmapConfigToColorType(dstConfig);
+
     if (!this->canCopyTo(dstConfig)) {
         return false;
     }
@@ -1170,7 +1123,7 @@
         SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
         if (pixelRef) {
             uint32_t rowBytes;
-            if (dstConfig == fConfig) {
+            if (this->colorType() == dstCT) {
                 // Since there is no subset to pass to deepCopy, and deepCopy
                 // succeeded, the new pixel ref must be identical.
                 SkASSERT(fPixelRef->info() == pixelRef->info());
@@ -1181,7 +1134,12 @@
                 // With the new config, an appropriate fRowBytes will be computed by setConfig.
                 rowBytes = 0;
             }
-            dst->setConfig(dstConfig, fWidth, fHeight, rowBytes);
+
+            SkImageInfo info = fInfo;
+            info.fColorType = dstCT;
+            if (!dst->setConfig(info, rowBytes)) {
+                return false;
+            }
             dst->setPixelRef(pixelRef, fPixelRefOrigin)->unref();
             return true;
         }
@@ -1584,11 +1542,8 @@
 };
 
 void SkBitmap::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeInt(fWidth);
-    buffer.writeInt(fHeight);
+    fInfo.flatten(buffer);
     buffer.writeInt(fRowBytes);
-    buffer.writeInt(fConfig);
-    buffer.writeInt(fAlphaType);
 
     if (fPixelRef) {
         if (fPixelRef->getFactory()) {
@@ -1608,19 +1563,17 @@
 void SkBitmap::unflatten(SkReadBuffer& buffer) {
     this->reset();
 
-    int width = buffer.readInt();
-    int height = buffer.readInt();
-    int rowBytes = buffer.readInt();
-    Config config = (Config)buffer.readInt();
-    SkAlphaType alphaType = (SkAlphaType)buffer.readInt();
-    buffer.validate((width >= 0) && (height >= 0) && (rowBytes >= 0) &&
-                    SkIsValidConfig(config) && validate_alphaType(config, alphaType));
+    SkImageInfo info;
+    info.unflatten(buffer);
+    size_t rowBytes = buffer.readInt();
+    buffer.validate((info.width() >= 0) && (info.height() >= 0) &&
+                    SkColorTypeIsValid(info.fColorType) &&
+                    SkAlphaTypeIsValid(info.fAlphaType) &&
+                    validate_alphaType(info.fColorType, info.fAlphaType) &&
+                    info.validRowBytes(rowBytes));
 
-    bool configIsValid = this->setConfig(config, width, height, rowBytes, alphaType);
-    // Note : Using (fRowBytes >= (fWidth * fBytesPerPixel)) in the following test can create false
-    //        positives if the multiplication causes an integer overflow. Use the division instead.
-    buffer.validate(configIsValid && (fBytesPerPixel > 0) &&
-                    ((fRowBytes / fBytesPerPixel) >= fWidth));
+    bool configIsValid = this->setConfig(info, rowBytes);
+    buffer.validate(configIsValid);
 
     int reftype = buffer.readInt();
     if (buffer.validate((SERIALIZE_PIXELTYPE_REF_DATA == reftype) ||
@@ -1630,7 +1583,7 @@
                 SkIPoint origin;
                 origin.fX = buffer.readInt();
                 origin.fY = buffer.readInt();
-                size_t offset = origin.fY * rowBytes + origin.fX * fBytesPerPixel;
+                size_t offset = origin.fY * rowBytes + origin.fX * info.bytesPerPixel();
                 SkPixelRef* pr = buffer.readPixelRef();
                 if (!buffer.validate((NULL == pr) ||
                        (pr->getAllocatedSizeInBytes() >= (offset + this->getSafeSize())))) {
@@ -1663,15 +1616,14 @@
 
 #ifdef SK_DEBUG
 void SkBitmap::validate() const {
-    SkASSERT(fConfig < kConfigCount);
-    SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
+    fInfo.validate();
+    SkASSERT(fInfo.validRowBytes(fRowBytes));
     uint8_t allFlags = kImageIsOpaque_Flag | kImageIsVolatile_Flag | kImageIsImmutable_Flag;
 #ifdef SK_BUILD_FOR_ANDROID
     allFlags |= kHasHardwareMipMap_Flag;
 #endif
     SkASSERT(fFlags <= allFlags);
     SkASSERT(fPixelLockCount >= 0);
-    SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
 
     if (fPixels) {
         SkASSERT(fPixelRef);
@@ -1680,9 +1632,9 @@
         SkASSERT(fPixelRef->rowBytes() == fRowBytes);
         SkASSERT(fPixelRefOrigin.fX >= 0);
         SkASSERT(fPixelRefOrigin.fY >= 0);
-        SkASSERT(fPixelRef->info().fWidth >= (int)fWidth + fPixelRefOrigin.fX);
-        SkASSERT(fPixelRef->info().fHeight >= (int)fHeight + fPixelRefOrigin.fY);
-        SkASSERT(fPixelRef->rowBytes() >= fWidth * fBytesPerPixel);
+        SkASSERT(fPixelRef->info().width() >= (int)this->width() + fPixelRefOrigin.fX);
+        SkASSERT(fPixelRef->info().fHeight >= (int)this->height() + fPixelRefOrigin.fY);
+        SkASSERT(fPixelRef->rowBytes() >= fInfo.minRowBytes());
     } else {
         SkASSERT(NULL == fColorTable);
     }
@@ -1728,3 +1680,14 @@
     str->append(")");
 }
 #endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+void SkImageInfo::validate() const {
+    SkASSERT(fWidth >= 0);
+    SkASSERT(fHeight >= 0);
+    SkASSERT(SkColorTypeIsValid(fColorType));
+    SkASSERT(SkAlphaTypeIsValid(fAlphaType));
+}
+#endif
diff --git a/src/image/SkImagePriv.cpp b/src/image/SkImagePriv.cpp
index 43cc44b..a044aad 100644
--- a/src/image/SkImagePriv.cpp
+++ b/src/image/SkImagePriv.cpp
@@ -37,35 +37,19 @@
     return SkColorTypeToBitmapConfig(info.fColorType);
 }
 
-bool SkBitmapConfigToColorType(SkBitmap::Config config, SkColorType* ctOut) {
-    SkColorType ct;
-    switch (config) {
-        case SkBitmap::kA8_Config:
-            ct = kAlpha_8_SkColorType;
-            break;
-        case SkBitmap::kIndex8_Config:
-            ct = kIndex_8_SkColorType;
-            break;
-        case SkBitmap::kRGB_565_Config:
-            ct = kRGB_565_SkColorType;
-            break;
-        case SkBitmap::kARGB_4444_Config:
-            ct = kARGB_4444_SkColorType;
-            break;
-        case SkBitmap::kARGB_8888_Config:
-            ct = kPMColor_SkColorType;
-            break;
-        case SkBitmap::kNo_Config:
-        default:
-            return false;
-    }
-    if (ctOut) {
-        *ctOut = ct;
-    }
-    return true;
+SkColorType SkBitmapConfigToColorType(SkBitmap::Config config) {
+    static const SkColorType gCT[] = {
+        kUnknown_SkColorType,   // kNo_Config
+        kAlpha_8_SkColorType,   // kA8_Config
+        kIndex_8_SkColorType,   // kIndex8_Config
+        kRGB_565_SkColorType,   // kRGB_565_Config
+        kARGB_4444_SkColorType, // kARGB_4444_Config
+        kPMColor_SkColorType,   // kARGB_8888_Config
+    };
+    SkASSERT((unsigned)config < SK_ARRAY_COUNT(gCT));
+    return gCT[config];
 }
 
-
 SkImage* SkNewImageFromBitmap(const SkBitmap& bm, bool canSharePixelRef) {
     SkImageInfo info;
     if (!bm.asImageInfo(&info)) {
diff --git a/src/image/SkImagePriv.h b/src/image/SkImagePriv.h
index 7c19c73..8883e2c 100644
--- a/src/image/SkImagePriv.h
+++ b/src/image/SkImagePriv.h
@@ -15,7 +15,7 @@
 
 extern SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo&);
 extern SkBitmap::Config SkColorTypeToBitmapConfig(SkColorType);
-extern bool SkBitmapConfigToColorType(SkBitmap::Config, SkColorType* ctOut);
+extern SkColorType SkBitmapConfigToColorType(SkBitmap::Config);
 
 // Call this if you explicitly want to use/share this pixelRef in the image
 extern SkImage* SkNewImageFromPixelRef(const SkImageInfo&, SkPixelRef*,
diff --git a/src/images/SkDecodingImageGenerator.cpp b/src/images/SkDecodingImageGenerator.cpp
index 153d1e2..1e28136 100644
--- a/src/images/SkDecodingImageGenerator.cpp
+++ b/src/images/SkDecodingImageGenerator.cpp
@@ -275,11 +275,6 @@
         info.fHeight = bitmap.height();
         info.fColorType = opts.fRequestedColorType;
         info.fAlphaType = bitmap.alphaType();
-
-        // Sanity check.
-        SkDEBUGCODE(SkColorType tmp;)
-        SkASSERT(SkBitmapConfigToColorType(config, &tmp));
-        SkASSERT(tmp == opts.fRequestedColorType);
     }
     return SkNEW_ARGS(SkDecodingImageGenerator,
                       (data, autoStream.detach(), info,
diff --git a/tests/ImageDecodingTest.cpp b/tests/ImageDecodingTest.cpp
index 81f9006..2e0225a 100644
--- a/tests/ImageDecodingTest.cpp
+++ b/tests/ImageDecodingTest.cpp
@@ -333,10 +333,7 @@
     SkASSERT(stream != NULL);
     SkASSERT(stream->rewind());
     SkASSERT(stream->unique());
-    SkColorType colorType;
-    if (!SkBitmapConfigToColorType(bitmap->config(), &colorType)) {
-        return NULL;
-    }
+    SkColorType colorType = bitmap->colorType();
     SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
     SkAutoTDelete<SkImageGenerator> gen(
         SkDecodingImageGenerator::Create(stream, opts));
diff --git a/tests/PixelRefTest.cpp b/tests/PixelRefTest.cpp
index c0bdf54..aeb3070 100644
--- a/tests/PixelRefTest.cpp
+++ b/tests/PixelRefTest.cpp
@@ -30,7 +30,7 @@
         REPORTER_ASSERT(reporter, success);
         success = bitmap.asImageInfo(&info);
         REPORTER_ASSERT(reporter, success == gRec[i].fExpectedSuccess);
-        if (gRec[i].fExpectedSuccess) {
+        if (success && gRec[i].fExpectedSuccess) {
             REPORTER_ASSERT(reporter, info.fAlphaType == gRec[i].fAlphaType);
             REPORTER_ASSERT(reporter, info.fColorType == gRec[i].fExpectedColorType);
         }