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