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(); }