flag imageinfo as srgb

intended uses:
- flag a SkSurface as sRGB (only supported by Ganesh for now)
- flag images (e.g. png or jpeg) as sRGB if the codec tells us that

wins:
- faster gamma-correct text (esp. w/ distance-fields) when we can use sRGB for text
- better color fidelity when the screen really is sRGB

Review URL: https://codereview.chromium.org/676883003
diff --git a/include/core/SkImageInfo.h b/include/core/SkImageInfo.h
index 6fbaf6b..a9d5aea 100644
--- a/include/core/SkImageInfo.h
+++ b/include/core/SkImageInfo.h
@@ -148,8 +148,16 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+enum SkColorProfileType {
+    kLinear_SkColorProfileType,
+    kSRGB_SkColorProfileType,
+
+    kLastEnum_SkColorProfileType = kSRGB_SkColorProfileType
+};
+
 /**
  *  Describe an image's dimensions and pixel type.
+ *  Used for both src images and render-targets (surfaces).
  */
 struct SkImageInfo {
 public:
@@ -158,39 +166,46 @@
         , fHeight(0)
         , fColorType(kUnknown_SkColorType)
         , fAlphaType(kIgnore_SkAlphaType)
+        , fProfileType(kLinear_SkColorProfileType)
     {}
 
-    static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at) {
-        return SkImageInfo(width, height, ct, at);
+    static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at,
+                            SkColorProfileType pt = kLinear_SkColorProfileType) {
+        return SkImageInfo(width, height, ct, at, pt);
     }
 
     /**
      *  Sets colortype to the native ARGB32 type.
      */
-    static SkImageInfo MakeN32(int width, int height, SkAlphaType at) {
-        return SkImageInfo(width, height, kN32_SkColorType, at);
+    static SkImageInfo MakeN32(int width, int height, SkAlphaType at,
+                               SkColorProfileType pt = kLinear_SkColorProfileType) {
+        return SkImageInfo(width, height, kN32_SkColorType, at, pt);
     }
 
     /**
      *  Sets colortype to the native ARGB32 type, and the alphatype to premul.
      */
-    static SkImageInfo MakeN32Premul(int width, int height) {
-        return SkImageInfo(width, height, kN32_SkColorType, kPremul_SkAlphaType);
+    static SkImageInfo MakeN32Premul(int width, int height,
+                                     SkColorProfileType pt = kLinear_SkColorProfileType) {
+        return SkImageInfo(width, height, kN32_SkColorType, kPremul_SkAlphaType, pt);
     }
 
     /**
      *  Sets colortype to the native ARGB32 type, and the alphatype to premul.
      */
-    static SkImageInfo MakeN32Premul(const SkISize& size) {
-        return MakeN32Premul(size.width(), size.height());
+    static SkImageInfo MakeN32Premul(const SkISize& size,
+                                     SkColorProfileType pt = kLinear_SkColorProfileType) {
+        return MakeN32Premul(size.width(), size.height(), pt);
     }
 
     static SkImageInfo MakeA8(int width, int height) {
-        return SkImageInfo(width, height, kAlpha_8_SkColorType, kPremul_SkAlphaType);
+        return SkImageInfo(width, height, kAlpha_8_SkColorType, kPremul_SkAlphaType,
+                           kLinear_SkColorProfileType);
     }
 
     static SkImageInfo MakeUnknown(int width, int height) {
-        return SkImageInfo(width, height, kUnknown_SkColorType, kIgnore_SkAlphaType);
+        return SkImageInfo(width, height, kUnknown_SkColorType, kIgnore_SkAlphaType,
+                           kLinear_SkColorProfileType);
     }
 
     static SkImageInfo MakeUnknown() {
@@ -201,6 +216,7 @@
     int height() const { return fHeight; }
     SkColorType colorType() const { return fColorType; }
     SkAlphaType alphaType() const { return fAlphaType; }
+    SkColorProfileType profileType() const { return fProfileType; }
 
     bool isEmpty() const { return fWidth <= 0 || fHeight <= 0; }
 
@@ -208,25 +224,28 @@
         return SkAlphaTypeIsOpaque(fAlphaType);
     }
 
-    SkIRect bounds() const { return SkIRect::MakeWH(fWidth, fHeight); }
+    bool isLinear() const { return kLinear_SkColorProfileType == fProfileType; }
+    bool isSRGB() const { return kSRGB_SkColorProfileType == fProfileType; }
+
     SkISize dimensions() const { return SkISize::Make(fWidth, fHeight); }
+    SkIRect bounds() const { return SkIRect::MakeWH(fWidth, fHeight); }
 
     /**
      *  Return a new ImageInfo with the same colortype and alphatype as this info,
      *  but with the specified width and height.
      */
     SkImageInfo makeWH(int newWidth, int newHeight) const {
-        return SkImageInfo::Make(newWidth, newHeight, fColorType, fAlphaType);
+        return SkImageInfo::Make(newWidth, newHeight, fColorType, fAlphaType, fProfileType);
     }
 
     SkImageInfo makeAlphaType(SkAlphaType newAlphaType) const {
-        return SkImageInfo::Make(fWidth, fHeight, fColorType, newAlphaType);
+        return SkImageInfo::Make(fWidth, fHeight, fColorType, newAlphaType, fProfileType);
     }
     
     SkImageInfo makeColorType(SkColorType newColorType) const {
-        return SkImageInfo::Make(fWidth, fHeight, newColorType, fAlphaType);
+        return SkImageInfo::Make(fWidth, fHeight, newColorType, fAlphaType, fProfileType);
     }
-    
+
     int bytesPerPixel() const {
         return SkColorTypeBytesPerPixel(fColorType);
     }
@@ -272,18 +291,21 @@
 #else
 private:
 #endif
-    int         fWidth;
-    int         fHeight;
-    SkColorType fColorType;
-    SkAlphaType fAlphaType;
+    int                 fWidth;
+    int                 fHeight;
+    SkColorType         fColorType;
+    SkAlphaType         fAlphaType;
 
 private:
-    SkImageInfo(int width, int height, SkColorType ct, SkAlphaType at)
+    SkImageInfo(int width, int height, SkColorType ct, SkAlphaType at, SkColorProfileType pt)
         : fWidth(width)
         , fHeight(height)
         , fColorType(ct)
         , fAlphaType(at)
+        , fProfileType(pt)
     {}
+
+    SkColorProfileType  fProfileType;
 };
 
 #endif
diff --git a/src/core/SkImageInfo.cpp b/src/core/SkImageInfo.cpp
index e61cd7d..7931358 100644
--- a/src/core/SkImageInfo.cpp
+++ b/src/core/SkImageInfo.cpp
@@ -9,6 +9,10 @@
 #include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
 
+static bool profile_type_is_valid(SkColorProfileType profileType) {
+    return (profileType >= 0) && (profileType <= kLastEnum_SkColorProfileType);
+}
+
 static bool alpha_type_is_valid(SkAlphaType alphaType) {
     return (alphaType >= 0) && (alphaType <= kLastEnum_SkAlphaType);
 }
@@ -22,10 +26,12 @@
     fHeight = buffer.read32();
 
     uint32_t packed = buffer.read32();
-    SkASSERT(0 == (packed >> 16));
+    SkASSERT(0 == (packed >> 24));
+    fProfileType = (SkColorProfileType)((packed >> 16) & 0xFF);
     fAlphaType = (SkAlphaType)((packed >> 8) & 0xFF);
     fColorType = (SkColorType)((packed >> 0) & 0xFF);
-    buffer.validate(alpha_type_is_valid(fAlphaType) &&
+    buffer.validate(profile_type_is_valid(fProfileType) &&
+                    alpha_type_is_valid(fAlphaType) &&
                     color_type_is_valid(fColorType));
 }
 
@@ -33,9 +39,10 @@
     buffer.write32(fWidth);
     buffer.write32(fHeight);
 
+    SkASSERT(0 == (fProfileType & ~0xFF));
     SkASSERT(0 == (fAlphaType & ~0xFF));
     SkASSERT(0 == (fColorType & ~0xFF));
-    uint32_t packed = (fAlphaType << 8) | fColorType;
+    uint32_t packed = (fProfileType << 16) | (fAlphaType << 8) | fColorType;
     buffer.write32(packed);
 }
 
diff --git a/src/ports/SkImageDecoder_CG.cpp b/src/ports/SkImageDecoder_CG.cpp
index 73a95fc..8d8d0c8 100644
--- a/src/ports/SkImageDecoder_CG.cpp
+++ b/src/ports/SkImageDecoder_CG.cpp
@@ -105,6 +105,16 @@
 
 #define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast)
 
+static bool icc_profile_is_sRGB(const void* data, size_t length) {
+    // found by inspection -- need a cleaner way to sniff a profile
+    const size_t ICC_PROFILE_OFFSET_TO_SRGB_TAG = 52;
+
+    if (length >= ICC_PROFILE_OFFSET_TO_SRGB_TAG + 4) {
+        return !memcmp((const char*)data + ICC_PROFILE_OFFSET_TO_SRGB_TAG, "sRGB", 4);
+    }
+    return false;
+}
+
 SkImageDecoder::Result SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream);
 
@@ -121,8 +131,21 @@
 
     const int width = SkToInt(CGImageGetWidth(image));
     const int height = SkToInt(CGImageGetHeight(image));
+    SkColorProfileType cpType = kLinear_SkColorProfileType;
 
-    bm->setInfo(SkImageInfo::MakeN32Premul(width, height));
+    CGColorSpaceRef cs = CGImageGetColorSpace(image);
+    if (cs) {
+        CGColorSpaceModel m = CGColorSpaceGetModel(cs);
+        if (kCGColorSpaceModelRGB == m) {
+            CFDataRef data = CGColorSpaceCopyICCProfile(cs);
+            if (data && icc_profile_is_sRGB(CFDataGetBytePtr(data), CFDataGetLength(data))) {
+                cpType = kSRGB_SkColorProfileType;
+                CFRelease(data);
+            }
+        }
+    }
+
+    bm->setInfo(SkImageInfo::MakeN32Premul(width, height, cpType));
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
         return kSuccess;
     }
diff --git a/tests/ImageIsOpaqueTest.cpp b/tests/ImageIsOpaqueTest.cpp
index 3fe5b3d..6fdbc81 100644
--- a/tests/ImageIsOpaqueTest.cpp
+++ b/tests/ImageIsOpaqueTest.cpp
@@ -6,13 +6,47 @@
  */
 
 #include "SkTypes.h"
+#include "Test.h"
+
 #if SK_SUPPORT_GPU
 #include "GrContextFactory.h"
 #endif
 #include "SkImage.h"
 #include "SkSurface.h"
+#include "SkReadBuffer.h"
+#include "SkWriteBuffer.h"
 
-#include "Test.h"
+static void test_flatten(skiatest::Reporter* reporter, const SkImageInfo& info) {
+    // just need a safe amount of storage
+    char storage[sizeof(SkImageInfo)*2];
+    SkWriteBuffer wb(storage, sizeof(storage));
+    info.flatten(wb);
+    SkASSERT(wb.bytesWritten() < sizeof(storage));
+
+    SkReadBuffer rb(storage, wb.bytesWritten());
+    SkImageInfo info2;
+
+    // pick a noisy byte pattern, so we ensure that unflatten sets all of our fields
+    memset(&info2, 0xB8, sizeof(info2));
+
+    info2.unflatten(rb);
+    REPORTER_ASSERT(reporter, rb.offset() == wb.bytesWritten());
+    REPORTER_ASSERT(reporter, info == info2);
+}
+
+DEF_TEST(ImageInfo_flattening, reporter) {
+    for (int ct = 0; ct <= kLastEnum_SkColorType; ++ct) {
+        for (int at = 0; at <= kLastEnum_SkAlphaType; ++at) {
+            for (int pt = 0; pt <= kLastEnum_SkColorProfileType; ++pt) {
+                SkImageInfo info = SkImageInfo::Make(100, 200,
+                                                     static_cast<SkColorType>(ct),
+                                                     static_cast<SkAlphaType>(at),
+                                                     static_cast<SkColorProfileType>(pt));
+                test_flatten(reporter, info);
+            }
+        }
+    }
+}
 
 static void check_isopaque(skiatest::Reporter* reporter, SkSurface* surface, bool expectedOpaque) {
     SkAutoTUnref<SkImage> image(surface->newImageSnapshot());