Improve color space support in SkImage::readPixels()
Correct handling of kGray, k565, k4444 etc. is still a TODO.
SkImage_Generator and SkImage_Gpu are still TODOs.
BUG=skia:6021
Change-Id: Ib53d97d3a866b2b4934fd85c10100855743a8fab
Reviewed-on: https://skia-review.googlesource.com/6396
Commit-Queue: Matt Sarett <msarett@google.com>
Reviewed-by: Mike Reed <reed@google.com>
diff --git a/src/core/SkConfig8888.cpp b/src/core/SkConfig8888.cpp
index 08635a2..b882146 100644
--- a/src/core/SkConfig8888.cpp
+++ b/src/core/SkConfig8888.cpp
@@ -7,10 +7,13 @@
#include "SkBitmap.h"
#include "SkCanvas.h"
+#include "SkColorSpaceXform.h"
+#include "SkColorSpaceXformPriv.h"
#include "SkConfig8888.h"
#include "SkColorPriv.h"
#include "SkDither.h"
#include "SkMathPriv.h"
+#include "SkPM4fPriv.h"
#include "SkRasterPipeline.h"
#include "SkUnPreMultiply.h"
@@ -35,11 +38,6 @@
src_srgb = false; // untagged dst means ignore tags on src
}
- // Nothing in the pipeline distinguishes between r,g,b, so if we need to swap, it doesn't
- // matter when we do it. Just need to track if we need it at all.
- const bool swap_rb = (kBGRA_8888_SkColorType == srcInfo.colorType())
- ^ (kBGRA_8888_SkColorType == dstInfo.colorType());
-
SkRasterPipeline pipeline;
switch (srcInfo.colorType()) {
@@ -49,7 +47,7 @@
if (src_srgb) {
pipeline.append_from_srgb(srcInfo.alphaType());
}
- if (swap_rb) {
+ if (kBGRA_8888_SkColorType == srcInfo.colorType()) {
pipeline.append(SkRasterPipeline::swap_rb);
}
break;
@@ -65,6 +63,11 @@
return false; // src colortype unsupported
}
+ float matrix[12];
+ if (!append_gamut_transform(&pipeline, matrix, srcInfo.colorSpace(), dstInfo.colorSpace())) {
+ return false;
+ }
+
SkAlphaType sat = srcInfo.alphaType();
SkAlphaType dat = dstInfo.alphaType();
if (sat == kPremul_SkAlphaType && dat == kUnpremul_SkAlphaType) {
@@ -76,6 +79,9 @@
switch (dstInfo.colorType()) {
case kRGBA_8888_SkColorType:
case kBGRA_8888_SkColorType:
+ if (kBGRA_8888_SkColorType == dstInfo.colorType()) {
+ pipeline.append(SkRasterPipeline::swap_rb);
+ }
if (dst_srgb) {
pipeline.append(SkRasterPipeline::to_srgb);
}
@@ -317,6 +323,73 @@
return true;
}
+static inline bool optimized_color_xform(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo) {
+ if (kUnpremul_SkAlphaType == dstInfo.alphaType() && kPremul_SkAlphaType == srcInfo.alphaType())
+ {
+ return false;
+ }
+
+ switch (dstInfo.colorType()) {
+ case kRGBA_8888_SkColorType:
+ case kBGRA_8888_SkColorType:
+ case kRGBA_F16_SkColorType:
+ break;
+ default:
+ return false;
+ }
+
+ switch (srcInfo.colorType()) {
+ case kRGBA_8888_SkColorType:
+ case kBGRA_8888_SkColorType:
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static inline void apply_color_xform(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
+ const SkImageInfo& srcInfo, const void* srcPixels,
+ size_t srcRB) {
+ SkColorSpaceXform::ColorFormat dstFormat = select_xform_format(dstInfo.colorType());
+ SkColorSpaceXform::ColorFormat srcFormat = select_xform_format(srcInfo.colorType());
+ SkAlphaType xformAlpha;
+ switch (srcInfo.alphaType()) {
+ case kOpaque_SkAlphaType:
+ xformAlpha = kOpaque_SkAlphaType;
+ break;
+ case kPremul_SkAlphaType:
+ SkASSERT(kPremul_SkAlphaType == dstInfo.alphaType());
+
+ // This signal means: copy the src alpha to the dst, do not premultiply (in this
+ // case because the pixels are already premultiplied).
+ xformAlpha = kUnpremul_SkAlphaType;
+ break;
+ case kUnpremul_SkAlphaType:
+ SkASSERT(kPremul_SkAlphaType == dstInfo.alphaType() ||
+ kUnpremul_SkAlphaType == dstInfo.alphaType());
+
+ xformAlpha = dstInfo.alphaType();
+ break;
+ default:
+ SkASSERT(false);
+ xformAlpha = kUnpremul_SkAlphaType;
+ break;
+ }
+
+ std::unique_ptr<SkColorSpaceXform> xform = SkColorSpaceXform::New(srcInfo.colorSpace(),
+ dstInfo.colorSpace());
+ SkASSERT(xform);
+
+ for (int y = 0; y < dstInfo.height(); y++) {
+ SkAssertResult(xform->apply(dstFormat, dstPixels, srcFormat, srcPixels, dstInfo.width(),
+ xformAlpha));
+ dstPixels = SkTAddOffset<void>(dstPixels, dstRB);
+ srcPixels = SkTAddOffset<const void>(srcPixels, srcRB);
+ }
+}
+
bool SkPixelInfo::CopyPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
const SkImageInfo& srcInfo, const void* srcPixels, size_t srcRB,
SkColorTable* ctable) {
@@ -330,6 +403,23 @@
return false; // can't convert from alpha to non-alpha
}
+ if (dstInfo.colorSpace() &&
+ SkColorSpace_Base::Type::kXYZ != as_CSB(dstInfo.colorSpace())->type())
+ {
+ return false; // unsupported dst space
+ }
+
+ const bool srcIsF16 = kRGBA_F16_SkColorType == srcInfo.colorType();
+ const bool dstIsF16 = kRGBA_F16_SkColorType == dstInfo.colorType();
+ if (srcIsF16 || dstIsF16) {
+ if (!srcInfo.colorSpace() || !dstInfo.colorSpace() ||
+ (srcIsF16 && !srcInfo.colorSpace()->gammaIsLinear()) ||
+ (dstIsF16 && !dstInfo.colorSpace()->gammaIsLinear()))
+ {
+ return false;
+ }
+ }
+
const int width = srcInfo.width();
const int height = srcInfo.height();
@@ -344,8 +434,10 @@
return true;
}
+ const bool isColorAware = srcInfo.colorSpace() && dstInfo.colorSpace();
+
// Handle fancy alpha swizzling if both are ARGB32
- if (4 == srcInfo.bytesPerPixel() && 4 == dstInfo.bytesPerPixel()) {
+ if (4 == srcInfo.bytesPerPixel() && 4 == dstInfo.bytesPerPixel() && !isColorAware) {
SkDstPixelInfo dstPI;
dstPI.fColorType = dstInfo.colorType();
dstPI.fAlphaType = dstInfo.alphaType();
@@ -361,13 +453,21 @@
return srcPI.convertPixelsTo(&dstPI, width, height);
}
+ if (isColorAware && optimized_color_xform(dstInfo, srcInfo)) {
+ apply_color_xform(dstInfo, dstPixels, dstRB, srcInfo, srcPixels, srcRB);
+ return true;
+ }
+
// If they agree on colorType and the alphaTypes are compatible, then we just memcpy.
// Note: we've already taken care of 32bit colortypes above.
if (srcInfo.colorType() == dstInfo.colorType()) {
switch (srcInfo.colorType()) {
+ case kRGBA_F16_SkColorType:
+ if (!SkColorSpace::Equals(srcInfo.colorSpace(), dstInfo.colorSpace())) {
+ break;
+ }
case kIndex_8_SkColorType:
case kARGB_4444_SkColorType:
- case kRGBA_F16_SkColorType:
if (srcInfo.alphaType() != dstInfo.alphaType()) {
break;
}