diff options
author | msarett <msarett@google.com> | 2016-06-01 07:55:30 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-06-01 07:55:30 -0700 |
commit | 740cc88ee3d63c75e52d31238f2a32600cc57a8c (patch) | |
tree | 06e999c848a7e0eb99c76d52ab949eda5cdddf86 /src | |
parent | 30e78c9737ff4861dc4e3fa1e4cd010680ed6965 (diff) |
Create SkColorSpaceXform to handle color conversions
Also adds testing of qcms color correction, so we can compare
SkColorSpaceXform outputs to qcms outputs.
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1952063002
Review-Url: https://codereview.chromium.org/1952063002
Diffstat (limited to 'src')
-rw-r--r-- | src/codec/SkJpegCodec.cpp | 23 | ||||
-rw-r--r-- | src/codec/SkJpegCodec.h | 7 | ||||
-rw-r--r-- | src/core/SkColorSpaceXform.cpp | 109 | ||||
-rw-r--r-- | src/core/SkColorSpaceXform.h | 51 |
4 files changed, 182 insertions, 8 deletions
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp index 85de356118..0413f79a3c 100644 --- a/src/codec/SkJpegCodec.cpp +++ b/src/codec/SkJpegCodec.cpp @@ -120,7 +120,7 @@ static bool is_icc_marker(jpeg_marker_struct* marker) { * (1) Discover all ICC profile markers and verify that they are numbered properly. * (2) Copy the data from each marker into a contiguous ICC profile. */ -static sk_sp<SkColorSpace> get_icc_profile(jpeg_decompress_struct* dinfo) { +static sk_sp<SkData> get_icc_profile(jpeg_decompress_struct* dinfo) { // Note that 256 will be enough storage space since each markerIndex is stored in 8-bits. jpeg_marker_struct* markerSequence[256]; memset(markerSequence, 0, sizeof(markerSequence)); @@ -165,8 +165,8 @@ static sk_sp<SkColorSpace> get_icc_profile(jpeg_decompress_struct* dinfo) { } // Combine the ICC marker data into a contiguous profile. - SkAutoMalloc iccData(totalBytes); - void* dst = iccData.get(); + sk_sp<SkData> iccData = SkData::MakeUninitialized(totalBytes); + void* dst = iccData->writable_data(); for (uint32_t i = 1; i <= numMarkers; i++) { jpeg_marker_struct* marker = markerSequence[i]; if (!marker) { @@ -180,7 +180,7 @@ static sk_sp<SkColorSpace> get_icc_profile(jpeg_decompress_struct* dinfo) { dst = SkTAddOffset<void>(dst, bytes); } - return SkColorSpace::NewICC(iccData.get(), totalBytes); + return iccData; } bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, @@ -221,7 +221,14 @@ bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, SkEncodedInfo info = SkEncodedInfo::Make(color, SkEncodedInfo::kOpaque_Alpha, 8); Origin orientation = get_exif_orientation(decoderMgr->dinfo()); - sk_sp<SkColorSpace> colorSpace = get_icc_profile(decoderMgr->dinfo()); + sk_sp<SkData> iccData = get_icc_profile(decoderMgr->dinfo()); + sk_sp<SkColorSpace> colorSpace = nullptr; + if (iccData) { + colorSpace = SkColorSpace::NewICC(iccData->data(), iccData->size()); + if (!colorSpace) { + SkCodecPrintf("Could not create SkColorSpace from ICC data.\n"); + } + } if (!colorSpace) { // Treat unmarked jpegs as sRGB. colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); @@ -230,7 +237,7 @@ bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, const int width = decoderMgr->dinfo()->image_width; const int height = decoderMgr->dinfo()->image_height; *codecOut = new SkJpegCodec(width, height, info, stream, decoderMgr.release(), - std::move(colorSpace), orientation); + std::move(colorSpace), orientation, std::move(iccData)); } else { SkASSERT(nullptr != decoderMgrOut); *decoderMgrOut = decoderMgr.release(); @@ -251,11 +258,13 @@ SkCodec* SkJpegCodec::NewFromStream(SkStream* stream) { } SkJpegCodec::SkJpegCodec(int width, int height, const SkEncodedInfo& info, SkStream* stream, - JpegDecoderMgr* decoderMgr, sk_sp<SkColorSpace> colorSpace, Origin origin) + JpegDecoderMgr* decoderMgr, sk_sp<SkColorSpace> colorSpace, Origin origin, + sk_sp<SkData> iccData) : INHERITED(width, height, info, stream, std::move(colorSpace), origin) , fDecoderMgr(decoderMgr) , fReadyState(decoderMgr->dinfo()->global_state) , fSwizzlerSubset(SkIRect::MakeEmpty()) + , fICCData(std::move(iccData)) {} /* diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h index f8cddd0221..7aa275ce4e 100644 --- a/src/codec/SkJpegCodec.h +++ b/src/codec/SkJpegCodec.h @@ -58,6 +58,8 @@ protected: bool onDimensionsSupported(const SkISize&) override; + sk_sp<SkData> getICCData() const override { return fICCData; } + private: /* @@ -92,7 +94,8 @@ private: * takes ownership */ SkJpegCodec(int width, int height, const SkEncodedInfo& info, SkStream* stream, - JpegDecoderMgr* decoderMgr, sk_sp<SkColorSpace> colorSpace, Origin origin); + JpegDecoderMgr* decoderMgr, sk_sp<SkColorSpace> colorSpace, Origin origin, + sk_sp<SkData> iccData); /* * Checks if the conversion between the input image and the requested output @@ -123,6 +126,8 @@ private: SkIRect fSwizzlerSubset; SkAutoTDelete<SkSwizzler> fSwizzler; + sk_sp<SkData> fICCData; + typedef SkCodec INHERITED; }; diff --git a/src/core/SkColorSpaceXform.cpp b/src/core/SkColorSpaceXform.cpp new file mode 100644 index 0000000000..473e715353 --- /dev/null +++ b/src/core/SkColorSpaceXform.cpp @@ -0,0 +1,109 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkColorPriv.h" +#include "SkColorSpace_Base.h" +#include "SkColorSpaceXform.h" + +bool compute_gamut_xform(SkMatrix44* srcToDst, const SkMatrix44& srcToXYZ, + const SkMatrix44& dstToXYZ) { + if (!dstToXYZ.invert(srcToDst)) { + return false; + } + + srcToDst->postConcat(srcToXYZ); + return true; +} + +std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpace>& srcSpace, + const sk_sp<SkColorSpace>& dstSpace) { + if (!srcSpace || !dstSpace) { + return nullptr; + } + + if (as_CSB(srcSpace)->gammas()->isValues() && as_CSB(dstSpace)->gammas()->isValues()) { + SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor); + if (!compute_gamut_xform(&srcToDst, srcSpace->xyz(), dstSpace->xyz())) { + return nullptr; + } + + float srcGammas[3]; + float dstGammas[3]; + srcGammas[0] = as_CSB(srcSpace)->gammas()->fRed.fValue; + srcGammas[1] = as_CSB(srcSpace)->gammas()->fGreen.fValue; + srcGammas[2] = as_CSB(srcSpace)->gammas()->fBlue.fValue; + dstGammas[0] = 1.0f / as_CSB(dstSpace)->gammas()->fRed.fValue; + dstGammas[1] = 1.0f / as_CSB(dstSpace)->gammas()->fGreen.fValue; + dstGammas[2] = 1.0f / as_CSB(dstSpace)->gammas()->fBlue.fValue; + + return std::unique_ptr<SkColorSpaceXform>( + new SkGammaByValueXform(srcGammas, srcToDst, dstGammas)); + } + + // Unimplemeted + return nullptr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +SkGammaByValueXform::SkGammaByValueXform(float srcGammas[3], const SkMatrix44& srcToDst, + float dstGammas[3]) + : fSrcToDst(srcToDst) +{ + memcpy(fSrcGammas, srcGammas, 3 * sizeof(float)); + memcpy(fDstGammas, dstGammas, 3 * sizeof(float)); +} + +static uint8_t clamp_float_to_byte(float v) { + v = v * 255.0f; + if (v > 255.0f) { + return 255; + } else if (v <= 0.0f) { + return 0; + } else { + return (uint8_t) (v + 0.5f); + } +} + +void SkGammaByValueXform::xform_RGBA_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const { + while (len-- > 0) { + float srcFloats[3]; + srcFloats[0] = ((*src >> 0) & 0xFF) * (1.0f / 255.0f); + srcFloats[1] = ((*src >> 8) & 0xFF) * (1.0f / 255.0f); + srcFloats[2] = ((*src >> 16) & 0xFF) * (1.0f / 255.0f); + + // Convert to linear. + srcFloats[0] = pow(srcFloats[0], fSrcGammas[0]); + srcFloats[1] = pow(srcFloats[1], fSrcGammas[1]); + srcFloats[2] = pow(srcFloats[2], fSrcGammas[2]); + + // Convert to dst gamut. + float dstFloats[3]; + dstFloats[0] = srcFloats[0] * fSrcToDst.getFloat(0, 0) + + srcFloats[1] * fSrcToDst.getFloat(1, 0) + + srcFloats[2] * fSrcToDst.getFloat(2, 0) + fSrcToDst.getFloat(3, 0); + dstFloats[1] = srcFloats[0] * fSrcToDst.getFloat(0, 1) + + srcFloats[1] * fSrcToDst.getFloat(1, 1) + + srcFloats[2] * fSrcToDst.getFloat(2, 1) + fSrcToDst.getFloat(3, 1); + dstFloats[2] = srcFloats[0] * fSrcToDst.getFloat(0, 2) + + srcFloats[1] * fSrcToDst.getFloat(1, 2) + + srcFloats[2] * fSrcToDst.getFloat(2, 2) + fSrcToDst.getFloat(3, 2); + + // Convert to dst gamma. + dstFloats[0] = pow(dstFloats[0], fDstGammas[0]); + dstFloats[1] = pow(dstFloats[1], fDstGammas[1]); + dstFloats[2] = pow(dstFloats[2], fDstGammas[2]); + + *dst = SkPackARGB32NoCheck(((*src >> 24) & 0xFF), + clamp_float_to_byte(dstFloats[0]), + clamp_float_to_byte(dstFloats[1]), + clamp_float_to_byte(dstFloats[2])); + + dst++; + src++; + } +} diff --git a/src/core/SkColorSpaceXform.h b/src/core/SkColorSpaceXform.h new file mode 100644 index 0000000000..c3010f0cca --- /dev/null +++ b/src/core/SkColorSpaceXform.h @@ -0,0 +1,51 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkColorSpaceXform_DEFINED +#define SkColorSpaceXform_DEFINED + +#include "SkColorSpace.h" + +class SkColorSpaceXform { +public: + + /** + * Create an object to handle color space conversions. + * + * @param srcSpace The encoded color space. + * @param dstSpace The destination color space. + * + */ + static std::unique_ptr<SkColorSpaceXform> New(const sk_sp<SkColorSpace>& srcSpace, + const sk_sp<SkColorSpace>& dstSpace); + + /** + * Apply the color conversion to a src buffer, storing the output in the dst buffer. + * The src is stored in RGBA_8888 and the dst is stored in 8888 platform format. + * The output is not premultiplied. + */ + virtual void xform_RGBA_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const = 0; + + virtual ~SkColorSpaceXform() {} +}; + +class SkGammaByValueXform : public SkColorSpaceXform { +public: + + void xform_RGBA_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const override; + +private: + SkGammaByValueXform(float srcGammas[3], const SkMatrix44& srcToDst, float dstGammas[3]); + + float fSrcGammas[3]; + const SkMatrix44 fSrcToDst; + float fDstGammas[3]; + + friend class SkColorSpaceXform; +}; + +#endif |