Remove old ICC parser, A2B SkColorSpace, SkGammas, etc...
Docs-Preview: https://skia.org/?cl=148807
Change-Id: I2d77f6543e390c4948d57242a518af77443f0165
Reviewed-on: https://skia-review.googlesource.com/148807
Commit-Queue: Mike Klein <mtklein@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
diff --git a/gn/core.gni b/gn/core.gni
index 6696887..254ca94 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -75,11 +75,8 @@
"$_src/core/SkColorMatrixFilterRowMajor255.cpp",
"$_src/core/SkColorMatrixFilterRowMajor255.h",
"$_src/core/SkColorSpace.cpp",
- "$_src/core/SkColorSpace_A2B.cpp",
- "$_src/core/SkColorSpace_A2B.h",
"$_src/core/SkColorSpace_XYZ.cpp",
"$_src/core/SkColorSpace_XYZ.h",
- "$_src/core/SkColorSpace_ICC.cpp",
"$_src/core/SkColorSpaceXform.cpp",
"$_src/core/SkColorSpaceXformCanvas.cpp",
"$_src/core/SkColorSpaceXformSteps.cpp",
diff --git a/include/core/SkColorSpace.h b/include/core/SkColorSpace.h
index eaafc25..7ad2f10 100644
--- a/include/core/SkColorSpace.h
+++ b/include/core/SkColorSpace.h
@@ -127,11 +127,6 @@
static sk_sp<SkColorSpace> MakeRGB(SkGammaNamed gammaNamed, const SkMatrix44& toXYZD50);
/**
- * Create an SkColorSpace from an ICC profile.
- */
- static sk_sp<SkColorSpace> MakeICC(const void*, size_t);
-
- /**
* Create an SkColorSpace from a parsed (skcms) ICC profile.
*/
static sk_sp<SkColorSpace> Make(const skcms_ICCProfile&);
@@ -141,16 +136,6 @@
*/
void toProfile(skcms_ICCProfile*) const;
- /**
- * Types of colorspaces.
- */
- enum Type {
- kRGB_Type,
- kCMYK_Type,
- kGray_Type,
- };
- Type type() const;
-
SkGammaNamed gammaNamed() const;
/**
@@ -265,9 +250,6 @@
virtual bool onGammaCloseToSRGB() const = 0;
virtual bool onGammaIsLinear() const = 0;
virtual bool onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const = 0;
- virtual bool onIsCMYK() const { return false; }
-
- virtual const SkData* onProfileData() const { return nullptr; }
using INHERITED = SkRefCnt;
};
diff --git a/site/user/sample/color.md b/site/user/sample/color.md
index 5870b03..eb9df66 100644
--- a/site/user/sample/color.md
+++ b/site/user/sample/color.md
@@ -80,9 +80,6 @@
// Choose a common gamut and a common transfer function
sk_sp<SkColorSpace> MakeRGB(RenderTargetGamma, Gamut);
-
- // Create a color space from an ICC profile
- sk_sp<SkColorSpace> MakeICC();
Starting with **sources** (the things that you draw), there are a number of ways to make sure
that they are tagged with a color space.
diff --git a/src/core/SkColorSpace.cpp b/src/core/SkColorSpace.cpp
index 239f98a..81747e9 100644
--- a/src/core/SkColorSpace.cpp
+++ b/src/core/SkColorSpace.cpp
@@ -9,6 +9,7 @@
#include "SkColorSpace_XYZ.h"
#include "SkColorSpacePriv.h"
#include "SkPoint3.h"
+#include "SkTemplates.h"
#include <new>
bool SkColorSpacePrimaries::toXYZD50(SkMatrix44* toXYZ_D50) const {
@@ -127,7 +128,7 @@
break;
}
- return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed, toXYZD50));
+ return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed, nullptr, toXYZD50));
}
sk_sp<SkColorSpace> SkColorSpace::MakeRGB(RenderTargetGamma gamma, const SkMatrix44& toXYZD50) {
@@ -159,18 +160,7 @@
return SkColorSpace::MakeRGB(kLinear_SkGammaNamed, toXYZD50);
}
- void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(SkColorSpaceTransferFn));
- sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
- SkColorSpaceTransferFn* fn = SkTAddOffset<SkColorSpaceTransferFn>(memory, sizeof(SkGammas));
- *fn = coeffs;
- SkGammas::Data data;
- data.fParamOffset = 0;
- for (int channel = 0; channel < 3; ++channel) {
- gammas->fType[channel] = SkGammas::Type::kParam_Type;
- gammas->fData[channel] = data;
- }
- return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(kNonStandard_SkGammaNamed,
- std::move(gammas), toXYZD50, nullptr));
+ return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(kNonStandard_SkGammaNamed, &coeffs, toXYZD50));
}
sk_sp<SkColorSpace> SkColorSpace::MakeRGB(RenderTargetGamma gamma, Gamut gamut) {
@@ -189,7 +179,7 @@
SkMatrix44 m44(SkMatrix44::kUninitialized_Constructor);
m44.set3x3RowMajorf(to_xyz);
(void)m44.getType(); // Force typemask to be computed to avoid races.
- return new SkColorSpace_XYZ(gamma, m44);
+ return new SkColorSpace_XYZ(gamma, nullptr, m44);
}
SkColorSpace* sk_srgb_singleton() {
@@ -211,14 +201,6 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
-SkColorSpace::Type SkColorSpace::type() const {
- const SkMatrix44* m = this->toXYZD50();
- if (m) {
- return m->isScale() ? kGray_Type : kRGB_Type;
- }
- return this->onIsCMYK() ? kCMYK_Type : kRGB_Type;
-}
-
SkGammaNamed SkColorSpace::gammaNamed() const {
return this->onGammaNamed();
}
@@ -289,6 +271,7 @@
* If kICC_Flag is set, we will write an ICC profile after the header.
* The ICC profile will be written as a uint32 size, followed immediately
* by the data (padded to 4 bytes).
+ * DEPRECATED / UNUSED
*/
static constexpr uint8_t kICC_Flag = 1 << 1;
@@ -324,87 +307,61 @@
};
size_t SkColorSpace::writeToMemory(void* memory) const {
- // Start by trying the serialization fast path. If we haven't saved ICC profile data,
- // we must have a profile that we can serialize easily.
- if (!this->onProfileData()) {
- // Profile data is mandatory for A2B0 color spaces, so we must be XYZ.
- SkASSERT(this->toXYZD50());
- // If we have a named profile, only write the enum.
- const SkGammaNamed gammaNamed = this->gammaNamed();
- if (this == sk_srgb_singleton()) {
- if (memory) {
- *((ColorSpaceHeader*) memory) = ColorSpaceHeader::Pack(
- k0_Version, kSRGB_NamedColorSpace, gammaNamed, 0);
- }
- return sizeof(ColorSpaceHeader);
- } else if (this == sk_srgb_linear_singleton()) {
- if (memory) {
- *((ColorSpaceHeader*) memory) = ColorSpaceHeader::Pack(
- k0_Version, kSRGBLinear_NamedColorSpace, gammaNamed, 0);
- }
- return sizeof(ColorSpaceHeader);
+ // If we have a named profile, only write the enum.
+ const SkGammaNamed gammaNamed = this->gammaNamed();
+ if (this == sk_srgb_singleton()) {
+ if (memory) {
+ *((ColorSpaceHeader*) memory) = ColorSpaceHeader::Pack(
+ k0_Version, kSRGB_NamedColorSpace, gammaNamed, 0);
}
+ return sizeof(ColorSpaceHeader);
+ } else if (this == sk_srgb_linear_singleton()) {
+ if (memory) {
+ *((ColorSpaceHeader*) memory) = ColorSpaceHeader::Pack(
+ k0_Version, kSRGBLinear_NamedColorSpace, gammaNamed, 0);
+ }
+ return sizeof(ColorSpaceHeader);
+ }
- // If we have a named gamma, write the enum and the matrix.
- switch (gammaNamed) {
- case kSRGB_SkGammaNamed:
- case k2Dot2Curve_SkGammaNamed:
- case kLinear_SkGammaNamed: {
- if (memory) {
- *((ColorSpaceHeader*) memory) =
- ColorSpaceHeader::Pack(k0_Version, 0, gammaNamed,
- ColorSpaceHeader::kMatrix_Flag);
- memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader));
- this->toXYZD50()->as3x4RowMajorf((float*) memory);
- }
- return sizeof(ColorSpaceHeader) + 12 * sizeof(float);
+ // If we have a named gamma, write the enum and the matrix.
+ switch (gammaNamed) {
+ case kSRGB_SkGammaNamed:
+ case k2Dot2Curve_SkGammaNamed:
+ case kLinear_SkGammaNamed: {
+ if (memory) {
+ *((ColorSpaceHeader*) memory) =
+ ColorSpaceHeader::Pack(k0_Version, 0, gammaNamed,
+ ColorSpaceHeader::kMatrix_Flag);
+ memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader));
+ this->toXYZD50()->as3x4RowMajorf((float*) memory);
}
- default: {
- SkColorSpaceTransferFn transferFn;
- SkAssertResult(this->isNumericalTransferFn(&transferFn));
+ return sizeof(ColorSpaceHeader) + 12 * sizeof(float);
+ }
+ default: {
+ SkColorSpaceTransferFn transferFn;
+ SkAssertResult(this->isNumericalTransferFn(&transferFn));
- if (memory) {
- *((ColorSpaceHeader*) memory) =
- ColorSpaceHeader::Pack(k0_Version, 0, gammaNamed,
- ColorSpaceHeader::kTransferFn_Flag);
- memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader));
+ if (memory) {
+ *((ColorSpaceHeader*) memory) =
+ ColorSpaceHeader::Pack(k0_Version, 0, gammaNamed,
+ ColorSpaceHeader::kTransferFn_Flag);
+ memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader));
- *(((float*) memory) + 0) = transferFn.fA;
- *(((float*) memory) + 1) = transferFn.fB;
- *(((float*) memory) + 2) = transferFn.fC;
- *(((float*) memory) + 3) = transferFn.fD;
- *(((float*) memory) + 4) = transferFn.fE;
- *(((float*) memory) + 5) = transferFn.fF;
- *(((float*) memory) + 6) = transferFn.fG;
- memory = SkTAddOffset<void>(memory, 7 * sizeof(float));
+ *(((float*) memory) + 0) = transferFn.fA;
+ *(((float*) memory) + 1) = transferFn.fB;
+ *(((float*) memory) + 2) = transferFn.fC;
+ *(((float*) memory) + 3) = transferFn.fD;
+ *(((float*) memory) + 4) = transferFn.fE;
+ *(((float*) memory) + 5) = transferFn.fF;
+ *(((float*) memory) + 6) = transferFn.fG;
+ memory = SkTAddOffset<void>(memory, 7 * sizeof(float));
- this->toXYZD50()->as3x4RowMajorf((float*) memory);
- }
-
- return sizeof(ColorSpaceHeader) + 19 * sizeof(float);
+ this->toXYZD50()->as3x4RowMajorf((float*) memory);
}
+
+ return sizeof(ColorSpaceHeader) + 19 * sizeof(float);
}
}
-
- // Otherwise, serialize the ICC data.
- size_t profileSize = this->onProfileData()->size();
- if (SkAlign4(profileSize) != (uint32_t) SkAlign4(profileSize)) {
- return 0;
- }
-
- if (memory) {
- *((ColorSpaceHeader*) memory) = ColorSpaceHeader::Pack(k0_Version, 0,
- kNonStandard_SkGammaNamed,
- ColorSpaceHeader::kICC_Flag);
- memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader));
-
- *((uint32_t*) memory) = (uint32_t) SkAlign4(profileSize);
- memory = SkTAddOffset<void>(memory, sizeof(uint32_t));
-
- memcpy(memory, this->onProfileData()->data(), profileSize);
- memset(SkTAddOffset<void>(memory, profileSize), 0, SkAlign4(profileSize) - profileSize);
- }
- return sizeof(ColorSpaceHeader) + sizeof(uint32_t) + SkAlign4(profileSize);
}
sk_sp<SkData> SkColorSpace::serialize() const {
@@ -455,18 +412,8 @@
switch (header.fFlags) {
case ColorSpaceHeader::kICC_Flag: {
- if (length < sizeof(uint32_t)) {
- return nullptr;
- }
-
- uint32_t profileSize = *((uint32_t*) data);
- data = SkTAddOffset<const void>(data, sizeof(uint32_t));
- length -= sizeof(uint32_t);
- if (length < profileSize) {
- return nullptr;
- }
-
- return MakeICC(data, profileSize);
+ // Deprecated and unsupported code path
+ return nullptr;
}
case ColorSpaceHeader::kTransferFn_Flag: {
if (length < 19 * sizeof(float)) {
@@ -501,18 +448,6 @@
return false;
}
- const SkData* srcData = src->onProfileData();
- const SkData* dstData = dst->onProfileData();
- if (srcData || dstData) {
- if (srcData && dstData) {
- return srcData->size() == dstData->size() &&
- 0 == memcmp(srcData->data(), dstData->data(), srcData->size());
- }
-
- return false;
- }
-
- // Profiles are mandatory for A2B0 color spaces, so these must be XYZ
if (src->gammaNamed() != dst->gammaNamed()) {
return false;
}
@@ -528,6 +463,7 @@
return false;
default:
// It is unlikely that we will reach this case.
+ // TODO: Simplify this case now that color spaces have one representation.
sk_sp<SkData> serializedSrcData = src->serialize();
sk_sp<SkData> serializedDstData = dst->serialize();
return serializedSrcData->size() == serializedDstData->size() &&
diff --git a/src/core/SkColorSpacePriv.h b/src/core/SkColorSpacePriv.h
index 7df6c97..898a61e 100644
--- a/src/core/SkColorSpacePriv.h
+++ b/src/core/SkColorSpacePriv.h
@@ -188,43 +188,6 @@
return linearExp || linearFn;
}
-static inline bool is_just_gamma(const SkColorSpaceTransferFn& coeffs) {
- return transfer_fn_almost_equal(coeffs.fA, 1.0f)
- && transfer_fn_almost_equal(coeffs.fB, 0.0f)
- && transfer_fn_almost_equal(coeffs.fC, 0.0f)
- && transfer_fn_almost_equal(coeffs.fD, 0.0f)
- && transfer_fn_almost_equal(coeffs.fE, 0.0f)
- && transfer_fn_almost_equal(coeffs.fF, 0.0f);
-}
-
-
-static inline void value_to_parametric(SkColorSpaceTransferFn* coeffs, float exponent) {
- coeffs->fA = 1.0f;
- coeffs->fB = 0.0f;
- coeffs->fC = 0.0f;
- coeffs->fD = 0.0f;
- coeffs->fE = 0.0f;
- coeffs->fF = 0.0f;
- coeffs->fG = exponent;
-}
-
-static inline bool named_to_parametric(SkColorSpaceTransferFn* coeffs,
- SkGammaNamed gammaNamed) {
- switch (gammaNamed) {
- case kSRGB_SkGammaNamed:
- *coeffs = gSRGB_TransferFn;
- return true;
- case k2Dot2Curve_SkGammaNamed:
- *coeffs = g2Dot2_TransferFn;
- return true;
- case kLinear_SkGammaNamed:
- *coeffs = gLinear_TransferFn;
- return true;
- default:
- return false;
- }
-}
-
// Return raw pointers to commonly used SkColorSpaces.
// No need to ref/unref these, but if you do, do it in pairs.
SkColorSpace* sk_srgb_singleton();
diff --git a/src/core/SkColorSpaceXform.cpp b/src/core/SkColorSpaceXform.cpp
index 61a37ad..8596abf 100644
--- a/src/core/SkColorSpaceXform.cpp
+++ b/src/core/SkColorSpaceXform.cpp
@@ -77,23 +77,19 @@
}
void SkColorSpace::toProfile(skcms_ICCProfile* profile) const {
- if (auto blob = this->onProfileData()) {
- SkAssertResult(skcms_Parse(blob->data(), blob->size(), profile));
- } else {
- SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor);
- SkColorSpaceTransferFn tf;
- SkAssertResult(this->toXYZD50(&toXYZ) && this->isNumericalTransferFn(&tf));
+ SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor);
+ SkColorSpaceTransferFn tf;
+ SkAssertResult(this->toXYZD50(&toXYZ) && this->isNumericalTransferFn(&tf));
- skcms_Matrix3x3 m = { {
- { toXYZ.get(0, 0), toXYZ.get(0, 1), toXYZ.get(0, 2) },
- { toXYZ.get(1, 0), toXYZ.get(1, 1), toXYZ.get(1, 2) },
- { toXYZ.get(2, 0), toXYZ.get(2, 1), toXYZ.get(2, 2) },
- } };
+ skcms_Matrix3x3 m = { {
+ { toXYZ.get(0, 0), toXYZ.get(0, 1), toXYZ.get(0, 2) },
+ { toXYZ.get(1, 0), toXYZ.get(1, 1), toXYZ.get(1, 2) },
+ { toXYZ.get(2, 0), toXYZ.get(2, 1), toXYZ.get(2, 2) },
+ } };
- skcms_Init(profile);
- skcms_SetTransferFunction(profile, (const skcms_TransferFunction*)&tf);
- skcms_SetXYZD50(profile, &m);
- }
+ skcms_Init(profile);
+ skcms_SetTransferFunction(profile, (const skcms_TransferFunction*)&tf);
+ skcms_SetXYZD50(profile, &m);
}
std::unique_ptr<SkColorSpaceXform> SkMakeColorSpaceXform(SkColorSpace* src, SkColorSpace* dst) {
diff --git a/src/core/SkColorSpace_A2B.cpp b/src/core/SkColorSpace_A2B.cpp
deleted file mode 100644
index 295f28f..0000000
--- a/src/core/SkColorSpace_A2B.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * 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 "SkColorSpace_A2B.h"
-
-SkColorSpace_A2B::SkColorSpace_A2B(SkColorSpace::Type iccType, std::vector<Element> elements,
- PCS pcs, sk_sp<SkData> profileData)
- : fProfileData(std::move(profileData))
- , fICCType(iccType)
- , fElements(std::move(elements))
- , fPCS(pcs)
-{
- SkASSERT(SkColorSpace::kRGB_Type == iccType || SkColorSpace::kCMYK_Type == iccType);
-}
diff --git a/src/core/SkColorSpace_A2B.h b/src/core/SkColorSpace_A2B.h
deleted file mode 100644
index 48e8a70..0000000
--- a/src/core/SkColorSpace_A2B.h
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * 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 SkColorSpace_A2B_DEFINED
-#define SkColorSpace_A2B_DEFINED
-
-#include "SkColorLookUpTable.h"
-#include "SkColorSpace.h"
-#include "SkGammas.h"
-#include <vector>
-
-// An alternative SkColorSpace that represents all the color space data that
-// is stored in an A2B0 ICC tag. This allows us to use alternative profile
-// connection spaces (CIELAB instead of just CIEXYZ), use color-lookup-tables
-// to do color space transformations not representable as TRC functions or
-// matrix operations, as well as have multiple TRC functions. The CLUT also
-// allows conversion between non-3-channel input color spaces ie CMYK(4) to
-// a workable PCS (ie XYZ).
-//
-// AtoBType, lut8Type and lut16Type A2B0 tag types are supported. There are
-// also MPET (multi-processing-elements) A2B0 tags in the standard which allow
-// you to combine these 3 primitives (TRC, CLUT, matrix) in any order/quantity.
-// MPET tags are currently unsupported by the MakeICC parser, could be supported
-// here by the nature of the design.
-class SkColorSpace_A2B : public SkColorSpace {
-public:
- const SkMatrix44* onToXYZD50() const override {
- // the matrix specified in A2B0 profiles is not necessarily
- // a to-XYZ matrix, as to-Lab is supported as well so returning
- // that could be misleading. Additionally, B-curves are applied
- // after the matrix is, but a toXYZD50 matrix is the last thing
- // applied in order to get into the (XYZ) profile connection space.
- return nullptr;
- }
-
- uint32_t onToXYZD50Hash() const override {
- // See onToXYZD50()'s comment.
- return 0;
- }
-
- const SkMatrix44* onFromXYZD50() const override {
- // See onToXYZD50()'s comment. Also, A2B0 profiles are not supported
- // as destination color spaces, so an inverse matrix is never wanted.
- return nullptr;
- }
-
- // There is no single gamma curve in an A2B0 profile
- SkGammaNamed onGammaNamed() const override { return kNonStandard_SkGammaNamed; }
- bool onGammaCloseToSRGB() const override { return false; }
- bool onGammaIsLinear() const override { return false; }
- bool onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const override { return false; }
-
- bool onIsCMYK() const override { return SkColorSpace::kCMYK_Type == fICCType; }
-
- const SkData* onProfileData() const override { return fProfileData.get(); }
-
- sk_sp<SkColorSpace> makeLinearGamma() const override {
- // TODO: Analyze the extrema of our projection into XYZ and use suitable primaries?
- // For now, just fall back to a default, because we don't have a good answer.
- return SkColorSpace::MakeSRGBLinear();
- }
-
- sk_sp<SkColorSpace> makeSRGBGamma() const override {
- // See comment in makeLinearGamma
- return SkColorSpace::MakeSRGB();
- }
-
- class Element {
- public:
- Element(SkGammaNamed gammaNamed, int channelCount)
- : fType(Type::kGammaNamed)
- , fGammaNamed(gammaNamed)
- , fMatrix(SkMatrix44::kUninitialized_Constructor)
- , fInputChannels(channelCount)
- , fOutputChannels(channelCount) {
- SkASSERT(gammaNamed != kNonStandard_SkGammaNamed);
- }
-
- explicit Element(sk_sp<SkGammas> gammas)
- : fType(Type::kGammas)
- , fGammas(std::move(gammas))
- , fMatrix(SkMatrix44::kUninitialized_Constructor)
- , fInputChannels(fGammas->channels())
- , fOutputChannels(fGammas->channels()) {
- for (int i = 0; i < fGammas->channels(); ++i) {
- if (SkGammas::Type::kTable_Type == fGammas->type(i)) {
- SkASSERT(fGammas->data(i).fTable.fSize >= 2);
- }
- }
- }
-
- explicit Element(sk_sp<SkColorLookUpTable> colorLUT)
- : fType(Type::kCLUT)
- , fCLUT(std::move(colorLUT))
- , fMatrix(SkMatrix44::kUninitialized_Constructor)
- , fInputChannels(fCLUT->inputChannels())
- , fOutputChannels(fCLUT->outputChannels())
- {}
-
- explicit Element(const SkMatrix44& matrix)
- : fType(Type::kMatrix)
- , fMatrix(matrix)
- , fInputChannels(3)
- , fOutputChannels(3)
- {}
-
- enum class Type {
- kGammaNamed,
- kGammas,
- kCLUT,
- kMatrix
- };
-
- Type type() const { return fType; }
-
- SkGammaNamed gammaNamed() const {
- SkASSERT(Type::kGammaNamed == fType);
- return fGammaNamed;
- }
-
- const SkGammas& gammas() const {
- SkASSERT(Type::kGammas == fType);
- return *fGammas;
- }
-
- const SkColorLookUpTable& colorLUT() const {
- SkASSERT(Type::kCLUT == fType);
- return *fCLUT;
- }
-
- const SkMatrix44& matrix() const {
- SkASSERT(Type::kMatrix == fType);
- return fMatrix;
- }
-
- int inputChannels() const { return fInputChannels; }
-
- int outputChannels() const { return fOutputChannels; }
-
- private:
- Type fType;
- SkGammaNamed fGammaNamed;
- sk_sp<SkGammas> fGammas;
- sk_sp<SkColorLookUpTable> fCLUT;
- SkMatrix44 fMatrix;
- int fInputChannels;
- int fOutputChannels;
- };
- const Element& element(int i) const { return fElements[i]; }
-
- int count() const { return (int)fElements.size(); }
-
- // the intermediate profile connection space that this color space
- // represents the transformation to
- enum class PCS : uint8_t {
- kLAB, // CIELAB
- kXYZ // CIEXYZ
- };
-
- PCS pcs() const { return fPCS; }
-
- SkColorSpace::Type iccType() const { return fICCType; }
-
- SkColorSpace_A2B(SkColorSpace::Type iccType, std::vector<Element> elements, PCS pcs,
- sk_sp<SkData> profileData);
-
-private:
- sk_sp<SkData> fProfileData;
-
- SkColorSpace::Type fICCType;
- std::vector<Element> fElements;
- PCS fPCS;
-
- friend class ColorSpaceXformTest;
-};
-
-#endif
diff --git a/src/core/SkColorSpace_ICC.cpp b/src/core/SkColorSpace_ICC.cpp
deleted file mode 100644
index 3cc82ed..0000000
--- a/src/core/SkColorSpace_ICC.cpp
+++ /dev/null
@@ -1,1556 +0,0 @@
-/*
- * 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 "SkAutoMalloc.h"
-#include "SkColorSpace.h"
-#include "SkColorSpacePriv.h"
-#include "SkColorSpace_A2B.h"
-#include "SkColorSpace_XYZ.h"
-#include "SkEndian.h"
-#include "SkFixed.h"
-#include "SkICCPriv.h"
-#include "SkTemplates.h"
-#include "../../third_party/skcms/skcms.h"
-
-#define return_if_false(pred, msg) \
- do { \
- if (!(pred)) { \
- SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \
- return false; \
- } \
- } while (0)
-
-#define return_null(msg) \
- do { \
- SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \
- return nullptr; \
- } while (0)
-
-static uint16_t read_big_endian_u16(const uint8_t* ptr) {
- return ptr[0] << 8 | ptr[1];
-}
-
-static uint32_t read_big_endian_u32(const uint8_t* ptr) {
- return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
-}
-
-static int32_t read_big_endian_i32(const uint8_t* ptr) {
- return (int32_t) read_big_endian_u32(ptr);
-}
-
-static constexpr float kWhitePointD50[] = { 0.96420f, 1.00000f, 0.82491f, };
-
-struct ICCProfileHeader {
- uint32_t fSize;
-
- // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.).
- // We're always going to use this one.
- uint32_t fCMMType_ignored;
-
- uint32_t fVersion;
- uint32_t fProfileClass;
- uint32_t fInputColorSpace;
- uint32_t fPCS;
- uint32_t fDateTime_ignored[3];
- uint32_t fSignature;
-
- // Indicates the platform that this profile was created for (ex: Apple, Microsoft). This
- // doesn't really matter to us.
- uint32_t fPlatformTarget_ignored;
-
- // Flags can indicate:
- // (1) Whether this profile was embedded in a file. This flag is consistently wrong.
- // Ex: The profile came from a file but indicates that it did not.
- // (2) Whether we are allowed to use the profile independently of the color data. If set,
- // this may allow us to use the embedded profile for testing separate from the original
- // image.
- uint32_t fFlags_ignored;
-
- // We support many output devices. It doesn't make sense to think about the attributes of
- // the device in the context of the image profile.
- uint32_t fDeviceManufacturer_ignored;
- uint32_t fDeviceModel_ignored;
- uint32_t fDeviceAttributes_ignored[2];
-
- uint32_t fRenderingIntent;
- int32_t fIlluminantXYZ[3];
-
- // We don't care who created the profile.
- uint32_t fCreator_ignored;
-
- // This is an MD5 checksum. Could be useful for checking if profiles are equal.
- uint32_t fProfileId_ignored[4];
-
- // Reserved for future use.
- uint32_t fReserved_ignored[7];
-
- uint32_t fTagCount;
-
- void init(const uint8_t* src, size_t len) {
- SkASSERT(kICCHeaderSize == sizeof(*this));
-
- uint32_t* dst = (uint32_t*) this;
- for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) {
- dst[i] = read_big_endian_u32(src);
- }
- }
-
- bool valid() const {
- return_if_false(fSize >= kICCHeaderSize, "Size is too small");
-
- uint8_t majorVersion = fVersion >> 24;
- return_if_false(majorVersion <= 4, "Unsupported version");
-
- // These are the four basic classes of profiles that we might expect to see embedded
- // in images. Additional classes exist, but they generally are used as a convenient
- // way for CMMs to store calculated transforms.
- return_if_false(fProfileClass == kDisplay_Profile ||
- fProfileClass == kInput_Profile ||
- fProfileClass == kOutput_Profile ||
- fProfileClass == kColorSpace_Profile,
- "Unsupported profile");
-
- switch (fInputColorSpace) {
- case kRGB_ColorSpace:
- SkColorSpacePrintf("RGB Input Color Space");
- break;
- case kCMYK_ColorSpace:
- SkColorSpacePrintf("CMYK Input Color Space\n");
- break;
- case kGray_ColorSpace:
- SkColorSpacePrintf("Gray Input Color Space\n");
- break;
- default:
- SkColorSpacePrintf("Unsupported Input Color Space: %c%c%c%c\n",
- (fInputColorSpace>>24)&0xFF, (fInputColorSpace>>16)&0xFF,
- (fInputColorSpace>> 8)&0xFF, (fInputColorSpace>> 0)&0xFF);
- return false;
- }
-
- switch (fPCS) {
- case kXYZ_PCSSpace:
- SkColorSpacePrintf("XYZ PCS\n");
- break;
- case kLAB_PCSSpace:
- SkColorSpacePrintf("Lab PCS\n");
- break;
- default:
- // ICC currently (V4.3) only specifices XYZ and Lab PCS spaces
- SkColorSpacePrintf("Unsupported PCS space: %c%c%c%c\n",
- (fPCS>>24)&0xFF, (fPCS>>16)&0xFF,
- (fPCS>> 8)&0xFF, (fPCS>> 0)&0xFF);
- return false;
- }
-
- return_if_false(fSignature == kACSP_Signature, "Bad signature");
-
- // TODO (msarett):
- // Should we treat different rendering intents differently?
- // Valid rendering intents include kPerceptual (0), kRelative (1),
- // kSaturation (2), and kAbsolute (3).
- if (fRenderingIntent > 3) {
- // Warn rather than fail here. Occasionally, we see perfectly
- // normal profiles with wacky rendering intents.
- SkColorSpacePrintf("Warning, bad rendering intent.\n");
- }
-
- return_if_false(
- color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0]), kWhitePointD50[0]) &&
- color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1]), kWhitePointD50[1]) &&
- color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2]), kWhitePointD50[2]),
- "Illuminant must be D50");
-
- return_if_false(fTagCount <= 100, "Too many tags");
-
- return true;
- }
-};
-
-template <class T>
-static bool safe_add(T arg1, T arg2, size_t* result) {
- SkASSERT(arg1 >= 0);
- SkASSERT(arg2 >= 0);
- if (arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) {
- T sum = arg1 + arg2;
- if (sum <= std::numeric_limits<size_t>::max()) {
- *result = static_cast<size_t>(sum);
- return true;
- }
- }
- return false;
-}
-
-static bool safe_mul(uint32_t arg1, uint32_t arg2, uint32_t* result) {
- uint64_t product64 = (uint64_t) arg1 * (uint64_t) arg2;
- uint32_t product32 = (uint32_t) product64;
- if (product32 != product64) {
- return false;
- }
-
- *result = product32;
- return true;
-}
-
-struct ICCTag {
- uint32_t fSignature;
- uint32_t fOffset;
- uint32_t fLength;
-
- const uint8_t* init(const uint8_t* src) {
- fSignature = read_big_endian_u32(src);
- fOffset = read_big_endian_u32(src + 4);
- fLength = read_big_endian_u32(src + 8);
- return src + 12;
- }
-
- bool valid(size_t len) {
- size_t tagEnd;
- return_if_false(safe_add(fOffset, fLength, &tagEnd),
- "Tag too large, overflows integer addition");
- return_if_false(tagEnd <= len, "Tag too large for ICC profile");
- return true;
- }
-
- const uint8_t* addr(const uint8_t* src) const {
- return src + fOffset;
- }
-
- static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature) {
- for (int i = 0; i < count; ++i) {
- if (tags[i].fSignature == signature) {
- return &tags[i];
- }
- }
- return nullptr;
- }
-};
-
-static bool load_xyz(float dst[3], const uint8_t* src, size_t len) {
- if (len < 20) {
- SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len);
- return false;
- }
-
- dst[0] = SkFixedToFloat(read_big_endian_i32(src + 8));
- dst[1] = SkFixedToFloat(read_big_endian_i32(src + 12));
- dst[2] = SkFixedToFloat(read_big_endian_i32(src + 16));
- SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]);
- return true;
-}
-
-static SkGammas::Type set_gamma_value(SkGammas::Data* data, float value) {
- if (color_space_almost_equal(2.2f, value)) {
- data->fNamed = k2Dot2Curve_SkGammaNamed;
- return SkGammas::Type::kNamed_Type;
- }
-
- if (color_space_almost_equal(1.0f, value)) {
- data->fNamed = kLinear_SkGammaNamed;
- return SkGammas::Type::kNamed_Type;
- }
-
- if (color_space_almost_equal(0.0f, value)) {
- return SkGammas::Type::kNone_Type;
- }
-
- data->fValue = value;
- return SkGammas::Type::kValue_Type;
-}
-
-static float read_big_endian_16_dot_16(const uint8_t buf[4]) {
- // It just so happens that SkFixed is also 16.16!
- return SkFixedToFloat(read_big_endian_i32(buf));
-}
-
-/**
- * @param outData Set to the appropriate value on success. If we have table or
- * parametric gamma, it is the responsibility of the caller to set
- * fOffset.
- * @param outParams If this is a parametric gamma, this is set to the appropriate
- * parameters on success.
- * @param outTagBytes Will be set to the length of the tag on success.
- * @src Pointer to tag data.
- * @len Length of tag data in bytes.
- *
- * @return kNone_Type on failure, otherwise the type of the gamma tag.
- */
-static SkGammas::Type parse_gamma(SkGammas::Data* outData, SkColorSpaceTransferFn* outParams,
- size_t* outTagBytes, const uint8_t* src, size_t len) {
- if (len < 12) {
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
- return SkGammas::Type::kNone_Type;
- }
-
- // In the case of consecutive gamma tags, we need to count the number of bytes in the
- // tag, so that we can move on to the next tag.
- size_t tagBytes;
-
- uint32_t type = read_big_endian_u32(src);
- // Bytes 4-7 are reserved and should be set to zero.
- switch (type) {
- case kTAG_CurveType: {
- uint32_t count = read_big_endian_u32(src + 8);
-
- // tagBytes = 12 + 2 * count
- // We need to do safe addition here to avoid integer overflow.
- if (!safe_add(count, count, &tagBytes) ||
- !safe_add((size_t) 12, tagBytes, &tagBytes))
- {
- SkColorSpacePrintf("Invalid gamma count");
- return SkGammas::Type::kNone_Type;
- }
-
- if (len < tagBytes) {
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
- return SkGammas::Type::kNone_Type;
- }
- *outTagBytes = tagBytes;
-
- if (0 == count) {
- // Some tags require a gamma curve, but the author doesn't actually want
- // to transform the data. In this case, it is common to see a curve with
- // a count of 0.
- outData->fNamed = kLinear_SkGammaNamed;
- return SkGammas::Type::kNamed_Type;
- }
-
- const uint16_t* table = (const uint16_t*) (src + 12);
- if (1 == count) {
- // The table entry is the gamma (with a bias of 256).
- float value = (read_big_endian_u16((const uint8_t*) table)) / 256.0f;
- SkColorSpacePrintf("gamma %g\n", value);
-
- return set_gamma_value(outData, value);
- }
-
- // This optimization is especially important for A2B profiles, where we do
- // not resize tables or interpolate lookups.
- if (2 == count) {
- if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
- 65535 == read_big_endian_u16((const uint8_t*) &table[1])) {
- outData->fNamed = kLinear_SkGammaNamed;
- return SkGammas::Type::kNamed_Type;
- }
- }
-
- // Check for frequently occurring sRGB curves.
- // We do this by sampling a few values and see if they match our expectation.
- // A more robust solution would be to compare each value in this curve against
- // an sRGB curve to see if we remain below an error threshold. At this time,
- // we haven't seen any images in the wild that make this kind of
- // calculation necessary. We encounter identical gamma curves over and
- // over again, but relatively few variations.
- if (1024 == count) {
- // The magic values were chosen because they match both the very common
- // HP sRGB gamma table and the less common Canon sRGB gamma table (which use
- // different rounding rules).
- if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
- 3366 == read_big_endian_u16((const uint8_t*) &table[257]) &&
- 14116 == read_big_endian_u16((const uint8_t*) &table[513]) &&
- 34318 == read_big_endian_u16((const uint8_t*) &table[768]) &&
- 65535 == read_big_endian_u16((const uint8_t*) &table[1023])) {
- outData->fNamed = kSRGB_SkGammaNamed;
- return SkGammas::Type::kNamed_Type;
- }
- }
-
- if (26 == count) {
- // The magic values match a clever "minimum size" approach to representing sRGB.
- // code.facebook.com/posts/411525055626587/under-the-hood-improving-facebook-photos
- if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
- 3062 == read_big_endian_u16((const uint8_t*) &table[6]) &&
- 12824 == read_big_endian_u16((const uint8_t*) &table[12]) &&
- 31237 == read_big_endian_u16((const uint8_t*) &table[18]) &&
- 65535 == read_big_endian_u16((const uint8_t*) &table[25])) {
- outData->fNamed = kSRGB_SkGammaNamed;
- return SkGammas::Type::kNamed_Type;
- }
- }
-
- if (4096 == count) {
- // The magic values were chosen because they match Nikon, Epson, and
- // lcms2 sRGB gamma tables (all of which use different rounding rules).
- if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
- 950 == read_big_endian_u16((const uint8_t*) &table[515]) &&
- 3342 == read_big_endian_u16((const uint8_t*) &table[1025]) &&
- 14079 == read_big_endian_u16((const uint8_t*) &table[2051]) &&
- 65535 == read_big_endian_u16((const uint8_t*) &table[4095])) {
- outData->fNamed = kSRGB_SkGammaNamed;
- return SkGammas::Type::kNamed_Type;
- }
- }
-
- // Otherwise, we will represent gamma with a table.
- outData->fTable.fSize = count;
- return SkGammas::Type::kTable_Type;
- }
- case kTAG_ParaCurveType: {
- // Determine the format of the parametric curve tag.
- uint16_t format = read_big_endian_u16(src + 8);
- if (format > kGABCDEF_ParaCurveType) {
- SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
- return SkGammas::Type::kNone_Type;
- }
-
- if (kExponential_ParaCurveType == format) {
- tagBytes = 12 + 4;
- if (len < tagBytes) {
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
- return SkGammas::Type::kNone_Type;
- }
-
- // Y = X^g
- float g = read_big_endian_16_dot_16(src + 12);
-
- *outTagBytes = tagBytes;
- return set_gamma_value(outData, g);
- }
-
- // Here's where the real parametric gammas start. There are many
- // permutations of the same equations.
- //
- // Y = (aX + b)^g + e for X >= d
- // Y = cX + f otherwise
- //
- // We will fill in with zeros as necessary to always match the above form.
- if (len < 24) {
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
- return SkGammas::Type::kNone_Type;
- }
- float g = read_big_endian_16_dot_16(src + 12);
- float a = read_big_endian_16_dot_16(src + 16);
- float b = read_big_endian_16_dot_16(src + 20);
- float c = 0.0f, d = 0.0f, e = 0.0f, f = 0.0f;
- switch(format) {
- case kGAB_ParaCurveType:
- tagBytes = 12 + 12;
-
- // Y = (aX + b)^g for X >= -b/a
- // Y = 0 otherwise
- d = -b / a;
- break;
- case kGABC_ParaCurveType:
- tagBytes = 12 + 16;
- if (len < tagBytes) {
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
- return SkGammas::Type::kNone_Type;
- }
-
- // Y = (aX + b)^g + e for X >= -b/a
- // Y = e otherwise
- e = read_big_endian_16_dot_16(src + 24);
- d = -b / a;
- f = e;
- break;
- case kGABDE_ParaCurveType:
- tagBytes = 12 + 20;
- if (len < tagBytes) {
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
- return SkGammas::Type::kNone_Type;
- }
-
- // Y = (aX + b)^g for X >= d
- // Y = cX otherwise
- c = read_big_endian_16_dot_16(src + 24);
- d = read_big_endian_16_dot_16(src + 28);
- break;
- case kGABCDEF_ParaCurveType:
- tagBytes = 12 + 28;
- if (len < tagBytes) {
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
- return SkGammas::Type::kNone_Type;
- }
-
- // Y = (aX + b)^g + e for X >= d
- // Y = cX + f otherwise
- c = read_big_endian_16_dot_16(src + 24);
- d = read_big_endian_16_dot_16(src + 28);
- e = read_big_endian_16_dot_16(src + 32);
- f = read_big_endian_16_dot_16(src + 36);
- break;
- default:
- SkASSERT(false);
- return SkGammas::Type::kNone_Type;
- }
-
- outParams->fG = g;
- outParams->fA = a;
- outParams->fB = b;
- outParams->fC = c;
- outParams->fD = d;
- outParams->fE = e;
- outParams->fF = f;
-
- if (!is_valid_transfer_fn(*outParams)) {
- return SkGammas::Type::kNone_Type;
- }
-
- if (is_almost_srgb(*outParams)) {
- outData->fNamed = kSRGB_SkGammaNamed;
- return SkGammas::Type::kNamed_Type;
- }
-
- if (is_almost_2dot2(*outParams)) {
- outData->fNamed = k2Dot2Curve_SkGammaNamed;
- return SkGammas::Type::kNamed_Type;
- }
-
- *outTagBytes = tagBytes;
- return SkGammas::Type::kParam_Type;
- }
- default:
- SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
- return SkGammas::Type::kNone_Type;
- }
-}
-
-/**
- * Returns the additional size in bytes needed to store the gamma tag.
- */
-static size_t gamma_alloc_size(SkGammas::Type type, const SkGammas::Data& data) {
- switch (type) {
- case SkGammas::Type::kNamed_Type:
- case SkGammas::Type::kValue_Type:
- return 0;
- case SkGammas::Type::kTable_Type:
- return sizeof(float) * data.fTable.fSize;
- case SkGammas::Type::kParam_Type:
- return sizeof(SkColorSpaceTransferFn);
- default:
- SkASSERT(false);
- return 0;
- }
-}
-
-/**
- * Sets invalid gamma to the default value.
- */
-static void handle_invalid_gamma(SkGammas::Type* type, SkGammas::Data* data) {
- if (SkGammas::Type::kNone_Type == *type) {
- *type = SkGammas::Type::kNamed_Type;
-
- // Guess sRGB in the case of a malformed transfer function.
- data->fNamed = kSRGB_SkGammaNamed;
- }
-}
-
-/**
- * Finish loading the gammas, now that we have allocated memory for the SkGammas struct.
- *
- * There's nothing to do for the simple cases, but for table gammas we need to actually
- * read the table into heap memory. And for parametric gammas, we need to copy over the
- * parameter values.
- *
- * @param memory Pointer to start of the SkGammas memory block
- * @param offset Bytes of memory (after the SkGammas struct) that are already in use.
- * @param data In-out variable. Will fill in the offset to the table or parameters
- * if necessary.
- * @param params Parameters for gamma curve. Only initialized/used when we have a
- * parametric gamma.
- * @param src Pointer to start of the gamma tag.
- *
- * @return Additional bytes of memory that are being used by this gamma curve.
- */
-static size_t load_gammas(void* memory, size_t offset, SkGammas::Type type,
- SkGammas::Data* data, const SkColorSpaceTransferFn& params,
- const uint8_t* src) {
- void* storage = SkTAddOffset<void>(memory, offset + sizeof(SkGammas));
-
- switch (type) {
- case SkGammas::Type::kNamed_Type:
- case SkGammas::Type::kValue_Type:
- // Nothing to do here.
- return 0;
- case SkGammas::Type::kTable_Type: {
- data->fTable.fOffset = offset;
-
- float* outTable = (float*) storage;
- const uint16_t* inTable = (const uint16_t*) (src + 12);
- for (int i = 0; i < data->fTable.fSize; i++) {
- outTable[i] = (read_big_endian_u16((const uint8_t*) &inTable[i])) / 65535.0f;
- }
-
- return sizeof(float) * data->fTable.fSize;
- }
- case SkGammas::Type::kParam_Type:
- data->fTable.fOffset = offset;
- memcpy(storage, ¶ms, sizeof(SkColorSpaceTransferFn));
- return sizeof(SkColorSpaceTransferFn);
- default:
- SkASSERT(false);
- return 0;
- }
-}
-
-static constexpr uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' ');
-static constexpr uint32_t kTAG_lut8Type = SkSetFourByteTag('m', 'f', 't', '1');
-static constexpr uint32_t kTAG_lut16Type = SkSetFourByteTag('m', 'f', 't', '2');
-
-static bool load_color_lut(sk_sp<SkColorLookUpTable>* colorLUT, uint32_t inputChannels,
- size_t precision, const uint8_t gridPoints[3], const uint8_t* src,
- size_t len) {
- switch (precision) {
- case 1: // 8-bit data
- case 2: // 16-bit data
- break;
- default:
- SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit. Found: %d-bit\n",
- 8*precision);
- return false;
- }
-
- uint32_t numEntries = SkColorLookUpTable::kOutputChannels;
- for (uint32_t i = 0; i < inputChannels; i++) {
- if (1 >= gridPoints[i]) {
- SkColorSpacePrintf("Each input channel must have at least two grid points.");
- return false;
- }
-
- if (!safe_mul(numEntries, gridPoints[i], &numEntries)) {
- SkColorSpacePrintf("Too many entries in Color LUT.");
- return false;
- }
- }
-
- uint32_t clutBytes;
- if (!safe_mul(numEntries, precision, &clutBytes)) {
- SkColorSpacePrintf("Too many entries in Color LUT.\n");
- return false;
- }
-
- if (len < clutBytes) {
- SkColorSpacePrintf("Color LUT tag is too small (%d / %d bytes).\n", len, clutBytes);
- return false;
- }
-
- // Movable struct colorLUT has ownership of fTable.
- void* memory = sk_malloc_throw(sizeof(SkColorLookUpTable) + sizeof(float) * numEntries);
- *colorLUT = sk_sp<SkColorLookUpTable>(new (memory) SkColorLookUpTable(inputChannels,
- gridPoints));
-
- float* table = SkTAddOffset<float>(memory, sizeof(SkColorLookUpTable));
- const uint8_t* ptr = src;
- for (uint32_t i = 0; i < numEntries; i++, ptr += precision) {
- if (1 == precision) {
- table[i] = ((float) *ptr) / 255.0f;
- } else {
- table[i] = ((float) read_big_endian_u16(ptr)) / 65535.0f;
- }
- }
-
- return true;
-}
-
-/**
- * Reads a matrix out of an A2B tag of an ICC profile.
- * If |translate| is true, it will load a 3x4 matrix out that corresponds to a XYZ
- * transform as well as a translation, and if |translate| is false it only loads a
- * 3x3 matrix with no translation
- *
- * @param matrix The matrix to store the result in
- * @param src Data to load the matrix out of.
- * @param len The length of |src|.
- * Must have 48 bytes if |translate| is set and 36 bytes otherwise.
- * @param translate Whether to read the translation column or not
- * @param pcs The profile connection space of the profile this matrix is for
- *
- * @return false on failure, true on success
- */
-static bool load_matrix(SkMatrix44* matrix, const uint8_t* src, size_t len, bool translate,
- SkColorSpace_A2B::PCS pcs) {
- const size_t minLen = translate ? 48 : 36;
- if (len < minLen) {
- SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len);
- return false;
- }
-
- float encodingFactor;
- switch (pcs) {
- case SkColorSpace_A2B::PCS::kLAB:
- encodingFactor = 1.f;
- break;
- case SkColorSpace_A2B::PCS::kXYZ:
- encodingFactor = 65535 / 32768.f;
- break;
- default:
- encodingFactor = 1.f;
- SkASSERT(false);
- break;
- }
- float array[16];
- array[ 0] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src));
- array[ 1] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 4));
- array[ 2] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 8));
-
- array[ 4] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 12));
- array[ 5] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 16));
- array[ 6] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 20));
-
- array[ 8] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 24));
- array[ 9] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 28));
- array[10] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 32));
-
- if (translate) {
- array[ 3] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 36)); // translate R
- array[ 7] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 40)); // translate G
- array[11] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 44)); // translate B
- } else {
- array[ 3] = 0.0f;
- array[ 7] = 0.0f;
- array[11] = 0.0f;
- }
-
- array[12] = 0.0f;
- array[13] = 0.0f;
- array[14] = 0.0f;
- array[15] = 1.0f;
- matrix->setRowMajorf(array);
- SkColorSpacePrintf("A2B0 matrix loaded:\n");
- for (int r = 0; r < 4; ++r) {
- SkColorSpacePrintf("|");
- for (int c = 0; c < 4; ++c) {
- SkColorSpacePrintf(" %f ", matrix->get(r, c));
- }
- SkColorSpacePrintf("|\n");
- }
- return true;
-}
-
-static inline SkGammaNamed is_named(const sk_sp<SkGammas>& gammas) {
- for (uint8_t i = 0; i < gammas->channels(); ++i) {
- if (!gammas->isNamed(i) || gammas->data(i).fNamed != gammas->data(0).fNamed) {
- return kNonStandard_SkGammaNamed;
- }
- }
- return gammas->data(0).fNamed;
-}
-
-/**
- * Parse and load an entire stored curve. Handles invalid gammas as well.
- *
- * There's nothing to do for the simple cases, but for table gammas we need to actually
- * read the table into heap memory. And for parametric gammas, we need to copy over the
- * parameter values.
- *
- * @param gammaNamed Out-variable. The named gamma curve.
- * @param gammas Out-variable. The stored gamma curve information. Can be null if
- * gammaNamed is a named curve
- * @param inputChannels The number of gamma input channels
- * @param rTagPtr Pointer to start of the gamma tag.
- * @param taglen The size in bytes of the tag
- *
- * @return false on failure, true on success
- */
-static bool parse_and_load_gamma(SkGammaNamed* gammaNamed, sk_sp<SkGammas>* gammas,
- uint8_t inputChannels, const uint8_t* tagSrc, size_t tagLen) {
- SkGammas::Data data[kMaxColorChannels];
- SkColorSpaceTransferFn params[kMaxColorChannels];
- SkGammas::Type type[kMaxColorChannels];
- const uint8_t* tagPtr[kMaxColorChannels];
-
- tagPtr[0] = tagSrc;
-
- *gammaNamed = kNonStandard_SkGammaNamed;
-
- // On an invalid first gamma, tagBytes remains set as zero. This causes the two
- // subsequent to be treated as identical (which is what we want).
- size_t tagBytes = 0;
- type[0] = parse_gamma(&data[0], ¶ms[0], &tagBytes, tagPtr[0], tagLen);
- handle_invalid_gamma(&type[0], &data[0]);
- size_t alignedTagBytes = SkAlign4(tagBytes);
-
- bool allChannelsSame = false;
- if (inputChannels * alignedTagBytes <= tagLen) {
- allChannelsSame = true;
- for (uint8_t i = 1; i < inputChannels; ++i) {
- if (0 != memcmp(tagSrc, tagSrc + i * alignedTagBytes, tagBytes)) {
- allChannelsSame = false;
- break;
- }
- }
- }
- if (allChannelsSame) {
- if (SkGammas::Type::kNamed_Type == type[0]) {
- *gammaNamed = data[0].fNamed;
- } else {
- size_t allocSize = sizeof(SkGammas);
- return_if_false(safe_add(allocSize, gamma_alloc_size(type[0], data[0]), &allocSize),
- "SkGammas struct is too large to allocate");
- void* memory = sk_malloc_throw(allocSize);
- *gammas = sk_sp<SkGammas>(new (memory) SkGammas(inputChannels));
- load_gammas(memory, 0, type[0], &data[0], params[0], tagPtr[0]);
-
- for (uint8_t channel = 0; channel < inputChannels; ++channel) {
- (*gammas)->fType[channel] = type[0];
- (*gammas)->fData[channel] = data[0];
- }
- }
- } else {
- for (uint8_t channel = 1; channel < inputChannels; ++channel) {
- tagPtr[channel] = tagPtr[channel - 1] + alignedTagBytes;
- tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0;
- tagBytes = 0;
- type[channel] = parse_gamma(&data[channel], ¶ms[channel], &tagBytes,
- tagPtr[channel], tagLen);
- handle_invalid_gamma(&type[channel], &data[channel]);
- alignedTagBytes = SkAlign4(tagBytes);
- }
-
- size_t allocSize = sizeof(SkGammas);
- for (uint8_t channel = 0; channel < inputChannels; ++channel) {
- return_if_false(safe_add(allocSize, gamma_alloc_size(type[channel], data[channel]),
- &allocSize),
- "SkGammas struct is too large to allocate");
- }
- void* memory = sk_malloc_throw(allocSize);
- *gammas = sk_sp<SkGammas>(new (memory) SkGammas(inputChannels));
-
- uint32_t offset = 0;
- for (uint8_t channel = 0; channel < inputChannels; ++channel) {
- (*gammas)->fType[channel] = type[channel];
- offset += load_gammas(memory,offset, type[channel], &data[channel], params[channel],
- tagPtr[channel]);
- (*gammas)->fData[channel] = data[channel];
-
- }
- }
-
- if (kNonStandard_SkGammaNamed == *gammaNamed) {
- *gammaNamed = is_named(*gammas);
- if (kNonStandard_SkGammaNamed != *gammaNamed) {
- // No need to keep the gammas struct, the enum is enough.
- *gammas = nullptr;
- }
- }
- return true;
-}
-
-static bool is_lut_gamma_linear(const uint8_t* src, size_t count, size_t precision) {
- // check for linear gamma (this is very common in lut gammas, as they aren't optional)
- const float normalizeX = 1.f / (count - 1);
- for (uint32_t x = 0; x < count; ++x) {
- const float y = precision == 1 ? (src[x] / 255.f)
- : (read_big_endian_u16(src + 2*x) / 65535.f);
- if (!color_space_almost_equal(x * normalizeX, y)) {
- return false;
- }
- }
- return true;
-}
-
-static bool load_lut_gammas(sk_sp<SkGammas>* gammas, SkGammaNamed* gammaNamed, size_t numTables,
- size_t entriesPerTable, size_t precision, const uint8_t* src,
- size_t len) {
- if (precision != 1 && precision != 2) {
- SkColorSpacePrintf("Invalid gamma table precision %d\n", precision);
- return false;
- }
- uint32_t totalEntries;
- return_if_false(safe_mul(entriesPerTable, numTables, &totalEntries),
- "Too many entries in gamma table.");
- uint32_t readBytes;
- return_if_false(safe_mul(precision, totalEntries, &readBytes),
- "SkGammas struct is too large to read");
- if (len < readBytes) {
- SkColorSpacePrintf("Gamma table is too small. Provided: %d. Required: %d\n",
- len, readBytes);
- return false;
- }
-
- uint32_t writeBytesPerChannel;
- return_if_false(safe_mul(sizeof(float), entriesPerTable, &writeBytesPerChannel),
- "SkGammas struct is too large to allocate");
- const size_t readBytesPerChannel = precision * entriesPerTable;
- size_t numTablesToUse = 1;
- for (size_t tableIndex = 1; tableIndex < numTables; ++tableIndex) {
- if (0 != memcmp(src, src + readBytesPerChannel * tableIndex, readBytesPerChannel)) {
- numTablesToUse = numTables;
- break;
- }
- }
-
- if (1 == numTablesToUse) {
- if (is_lut_gamma_linear(src, entriesPerTable, precision)) {
- *gammaNamed = kLinear_SkGammaNamed;
- return true;
- }
- }
- *gammaNamed = kNonStandard_SkGammaNamed;
-
- uint32_t writetableBytes;
- return_if_false(safe_mul(numTablesToUse, writeBytesPerChannel, &writetableBytes),
- "SkGammas struct is too large to allocate");
- size_t allocSize = sizeof(SkGammas);
- return_if_false(safe_add(allocSize, (size_t)writetableBytes, &allocSize),
- "SkGammas struct is too large to allocate");
-
- void* memory = sk_malloc_throw(allocSize);
- *gammas = sk_sp<SkGammas>(new (memory) SkGammas(numTables));
-
- for (size_t tableIndex = 0; tableIndex < numTablesToUse; ++tableIndex) {
- const uint8_t* ptr = src + readBytesPerChannel * tableIndex;
- const size_t offset = sizeof(SkGammas) + tableIndex * writeBytesPerChannel;
- float* table = SkTAddOffset<float>(memory, offset);
- if (1 == precision) {
- for (uint32_t i = 0; i < entriesPerTable; ++i, ptr += 1) {
- table[i] = ((float) *ptr) / 255.0f;
- }
- } else if (2 == precision) {
- for (uint32_t i = 0; i < entriesPerTable; ++i, ptr += 2) {
- table[i] = ((float) read_big_endian_u16(ptr)) / 65535.0f;
- }
- }
- }
-
- SkASSERT(1 == numTablesToUse|| numTables == numTablesToUse);
-
- size_t tableOffset = 0;
- for (size_t tableIndex = 0; tableIndex < numTables; ++tableIndex) {
- (*gammas)->fType[tableIndex] = SkGammas::Type::kTable_Type;
- (*gammas)->fData[tableIndex].fTable.fOffset = tableOffset;
- (*gammas)->fData[tableIndex].fTable.fSize = entriesPerTable;
- if (numTablesToUse > 1) {
- tableOffset += writeBytesPerChannel;
- }
- }
-
- return true;
-}
-
-bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, const uint8_t* src,
- size_t len, SkColorSpace_A2B::PCS pcs) {
- SkASSERT(len >= 32);
- // Read the number of channels. The four bytes (4-7) that we skipped are reserved and
- // must be zero.
- const uint8_t inputChannels = src[8];
- const uint8_t outputChannels = src[9];
- if (SkColorLookUpTable::kOutputChannels != outputChannels) {
- // We only handle RGB outputs. The number of output channels must be 3.
- SkColorSpacePrintf("Output channels (%d) must equal 3 in A to B tag.\n", outputChannels);
- return false;
- }
- if (inputChannels == 0 || inputChannels > 4) {
- // And we only support 4 input channels.
- // ICC says up to 16 but our decode can only handle 4.
- // It could easily be extended to support up to 8, but we only allow CMYK/RGB
- // input color spaces which are 3 and 4 so let's restrict it to 4 instead of 8.
- // We can always change this check when we support bigger input spaces.
- SkColorSpacePrintf("Input channels (%d) must be between 1 and 4 in A to B tag.\n",
- inputChannels);
- return false;
- }
-
-
- // It is important that these are loaded in the order of application, as the
- // order you construct an A2B color space's elements is the order it is applied
-
- // If the offset is non-zero it indicates that the element is present.
- const uint32_t offsetToACurves = read_big_endian_i32(src + 28);
- if (0 != offsetToACurves && offsetToACurves < len) {
- const size_t tagLen = len - offsetToACurves;
- SkGammaNamed gammaNamed;
- sk_sp<SkGammas> gammas;
- if (!parse_and_load_gamma(&gammaNamed, &gammas, inputChannels, src + offsetToACurves,
- tagLen)) {
- return false;
- }
- if (gammas) {
- elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
- } else if (kLinear_SkGammaNamed != gammaNamed) {
- elements->push_back(SkColorSpace_A2B::Element(gammaNamed, inputChannels));
- }
- }
-
- const uint32_t offsetToColorLUT = read_big_endian_i32(src + 24);
- if (0 != offsetToColorLUT && offsetToColorLUT < len) {
- sk_sp<SkColorLookUpTable> colorLUT;
- const uint8_t* clutSrc = src + offsetToColorLUT;
- const size_t clutLen = len - offsetToColorLUT;
- // 16 bytes reserved for grid points, 1 for precision, 3 for padding.
- // The color LUT data follows after this header.
- static constexpr uint32_t kColorLUTHeaderSize = 20;
- if (clutLen < kColorLUTHeaderSize) {
- SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", clutLen);
- return false;
- }
-
- SkASSERT(inputChannels <= kMaxColorChannels);
- uint8_t gridPoints[kMaxColorChannels];
- for (uint32_t i = 0; i < inputChannels; ++i) {
- gridPoints[i] = clutSrc[i];
- }
- // Space is provided for a maximum of 16 input channels.
- // Now we determine the precision of the table values.
- const uint8_t precision = clutSrc[16];
- if (!load_color_lut(&colorLUT, inputChannels, precision, gridPoints,
- clutSrc + kColorLUTHeaderSize, clutLen - kColorLUTHeaderSize)) {
- SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n");
- return false;
- }
- elements->push_back(SkColorSpace_A2B::Element(std::move(colorLUT)));
- }
-
- const uint32_t offsetToMCurves = read_big_endian_i32(src + 20);
- if (0 != offsetToMCurves && offsetToMCurves < len) {
- const size_t tagLen = len - offsetToMCurves;
- SkGammaNamed gammaNamed;
- sk_sp<SkGammas> gammas;
- if (!parse_and_load_gamma(&gammaNamed, &gammas, outputChannels, src + offsetToMCurves,
- tagLen)) {
- return false;
- }
- if (gammas) {
- elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
- } else if (kLinear_SkGammaNamed != gammaNamed) {
- elements->push_back(SkColorSpace_A2B::Element(gammaNamed, outputChannels));
- }
- }
-
- const uint32_t offsetToMatrix = read_big_endian_i32(src + 16);
- if (0 != offsetToMatrix && offsetToMatrix < len) {
- SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
- if (!load_matrix(&matrix, src + offsetToMatrix, len - offsetToMatrix, true, pcs)) {
- SkColorSpacePrintf("Failed to read matrix from A to B tag.\n");
- } else if (!matrix.isIdentity()) {
- elements->push_back(SkColorSpace_A2B::Element(matrix));
- }
- }
-
- const uint32_t offsetToBCurves = read_big_endian_i32(src + 12);
- if (0 != offsetToBCurves && offsetToBCurves < len) {
- const size_t tagLen = len - offsetToBCurves;
- SkGammaNamed gammaNamed;
- sk_sp<SkGammas> gammas;
- if (!parse_and_load_gamma(&gammaNamed, &gammas, outputChannels, src + offsetToBCurves,
- tagLen)) {
- return false;
- }
- if (gammas) {
- elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
- } else if (kLinear_SkGammaNamed != gammaNamed) {
- elements->push_back(SkColorSpace_A2B::Element(gammaNamed, outputChannels));
- }
- }
-
- return true;
-}
-
-bool load_a2b0_lutn_type(std::vector<SkColorSpace_A2B::Element>* elements, const uint8_t* src,
- size_t len, SkColorSpace_A2B::PCS pcs) {
- const uint32_t type = read_big_endian_u32(src);
- switch (type) {
- case kTAG_lut8Type:
- SkASSERT(len >= 48);
- break;
- case kTAG_lut16Type:
- SkASSERT(len >= 52);
- break;
- default:
- SkASSERT(false);
- return false;
- }
- // Read the number of channels.
- // The four bytes (4-7) that we skipped are reserved and must be zero.
- const uint8_t inputChannels = src[8];
- const uint8_t outputChannels = src[9];
- if (SkColorLookUpTable::kOutputChannels != outputChannels) {
- // We only handle RGB outputs. The number of output channels must be 3.
- SkColorSpacePrintf("Output channels (%d) must equal 3 in A to B tag.\n", outputChannels);
- return false;
- }
- if (inputChannels == 0 || inputChannels > 4) {
- // And we only support 4 input channels.
- // ICC says up to 16 but our decode can only handle 4.
- // It could easily be extended to support up to 8, but we only allow CMYK/RGB
- // input color spaces which are 3 and 4 so let's restrict it to 4 instead of 8.
- // We can always change this check when we support bigger input spaces.
- SkColorSpacePrintf("Input channels (%d) must be between 1 and 4 in A to B tag.\n",
- inputChannels);
- return false;
- }
-
- const uint8_t clutGridPoints = src[10];
- // 11th byte reserved for padding (required to be zero)
-
- SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
- load_matrix(&matrix, &src[12], len - 12, false, pcs);
- if (!matrix.isIdentity()) {
- // ICC specs (10.8/10.9) say lut8/16Type profiles must have identity matrices
- // if the input color space is not PCSXYZ, and we do not support PCSXYZ input color spaces
- // so we should never encounter a non-identity matrix here.
- // However, 2 test images from the ICC website have RGB input spaces and non-identity
- // matrices so we're not going to fail here, despite being against the spec.
- SkColorSpacePrintf("Warning: non-Identity matrix found in non-XYZ input color space"
- "lut profile");
- elements->push_back(SkColorSpace_A2B::Element(matrix));
- }
-
- size_t dataOffset = 48;
- // # of input table entries
- size_t inTableEntries = 256;
- // # of output table entries
- size_t outTableEntries = 256;
- size_t precision = 1;
- if (kTAG_lut16Type == type) {
- dataOffset = 52;
- inTableEntries = read_big_endian_u16(src + 48);
- outTableEntries = read_big_endian_u16(src + 50);
- precision = 2;
-
- constexpr size_t kMaxLut16GammaEntries = 4096;
- if (inTableEntries < 2) {
- SkColorSpacePrintf("Too few (%d) input gamma table entries. Must have at least 2.\n",
- inTableEntries);
- return false;
- } else if (inTableEntries > kMaxLut16GammaEntries) {
- SkColorSpacePrintf("Too many (%d) input gamma table entries. Must have at most %d.\n",
- inTableEntries, kMaxLut16GammaEntries);
- return false;
- }
-
- if (outTableEntries < 2) {
- SkColorSpacePrintf("Too few (%d) output gamma table entries. Must have at least 2.\n",
- outTableEntries);
- return false;
- } else if (outTableEntries > kMaxLut16GammaEntries) {
- SkColorSpacePrintf("Too many (%d) output gamma table entries. Must have at most %d.\n",
- outTableEntries, kMaxLut16GammaEntries);
- return false;
- }
- }
-
- const size_t inputOffset = dataOffset;
- return_if_false(len >= inputOffset, "A2B0 lutnType tag too small for input gamma table");
- sk_sp<SkGammas> inputGammas;
- SkGammaNamed inputGammaNamed;
- if (!load_lut_gammas(&inputGammas, &inputGammaNamed, inputChannels, inTableEntries, precision,
- src + inputOffset, len - inputOffset)) {
- SkColorSpacePrintf("Failed to read input gammas from lutnType tag.\n");
- return false;
- }
- SkASSERT(inputGammas || inputGammaNamed != kNonStandard_SkGammaNamed);
- if (kLinear_SkGammaNamed != inputGammaNamed) {
- if (kNonStandard_SkGammaNamed != inputGammaNamed) {
- elements->push_back(SkColorSpace_A2B::Element(inputGammaNamed, inputChannels));
- } else {
- elements->push_back(SkColorSpace_A2B::Element(std::move(inputGammas)));
- }
- }
-
- const size_t clutOffset = inputOffset + precision*inTableEntries*inputChannels;
- return_if_false(len >= clutOffset, "A2B0 lutnType tag too small for CLUT");
- sk_sp<SkColorLookUpTable> colorLUT;
- const uint8_t gridPoints[kMaxColorChannels] = {
- clutGridPoints, clutGridPoints, clutGridPoints, clutGridPoints
- };
- if (!load_color_lut(&colorLUT, inputChannels, precision, gridPoints, src + clutOffset,
- len - clutOffset)) {
- SkColorSpacePrintf("Failed to read color LUT from lutnType tag.\n");
- return false;
- }
- SkASSERT(colorLUT);
- elements->push_back(SkColorSpace_A2B::Element(std::move(colorLUT)));
-
- size_t clutSize = precision * outputChannels;
- for (int i = 0; i < inputChannels; ++i) {
- clutSize *= clutGridPoints;
- }
- const size_t outputOffset = clutOffset + clutSize;
- return_if_false(len >= outputOffset, "A2B0 lutnType tag too small for output gamma table");
- sk_sp<SkGammas> outputGammas;
- SkGammaNamed outputGammaNamed;
- if (!load_lut_gammas(&outputGammas, &outputGammaNamed, outputChannels, outTableEntries,
- precision, src + outputOffset, len - outputOffset)) {
- SkColorSpacePrintf("Failed to read output gammas from lutnType tag.\n");
- return false;
- }
- SkASSERT(outputGammas || outputGammaNamed != kNonStandard_SkGammaNamed);
- if (kLinear_SkGammaNamed != outputGammaNamed) {
- if (kNonStandard_SkGammaNamed != outputGammaNamed) {
- elements->push_back(SkColorSpace_A2B::Element(outputGammaNamed, outputChannels));
- } else {
- elements->push_back(SkColorSpace_A2B::Element(std::move(outputGammas)));
- }
- }
-
- return true;
-}
-
-static inline int icf_channels(SkColorSpace::Type iccType) {
- switch (iccType) {
- case SkColorSpace::kRGB_Type:
- return 3;
- case SkColorSpace::kCMYK_Type:
- return 4;
- default:
- SkASSERT(false);
- return 0;
- }
-}
-
-static bool load_a2b0(std::vector<SkColorSpace_A2B::Element>* elements, const uint8_t* src,
- size_t len, SkColorSpace_A2B::PCS pcs,
- SkColorSpace::Type iccType) {
- if (len < 4) {
- return false;
- }
- const uint32_t type = read_big_endian_u32(src);
-
- switch (type) {
- case kTAG_AtoBType:
- if (len < 32) {
- SkColorSpacePrintf("A to B tag is too small (%d bytes).", len);
- return false;
- }
- SkColorSpacePrintf("A2B0 tag is of type lutAtoBType\n");
- if (!load_a2b0_a_to_b_type(elements, src, len, pcs)) {
- return false;
- }
- break;
- case kTAG_lut8Type:
- if (len < 48) {
- SkColorSpacePrintf("lut8 tag is too small (%d bytes).", len);
- return false;
- }
- SkColorSpacePrintf("A2B0 tag of type lut8Type\n");
- if (!load_a2b0_lutn_type(elements, src, len, pcs)) {
- return false;
- }
- break;
- case kTAG_lut16Type:
- if (len < 52) {
- SkColorSpacePrintf("lut16 tag is too small (%d bytes).", len);
- return false;
- }
- SkColorSpacePrintf("A2B0 tag of type lut16Type\n");
- if (!load_a2b0_lutn_type(elements, src, len, pcs)) {
- return false;
- }
- break;
- default:
- SkColorSpacePrintf("Unsupported A to B tag type: %c%c%c%c\n", (type>>24)&0xFF,
- (type>>16)&0xFF, (type>>8)&0xFF, type&0xFF);
- return false;
- }
- SkASSERT(SkColorSpace_A2B::PCS::kLAB == pcs || SkColorSpace_A2B::PCS::kXYZ == pcs);
- static constexpr int kPCSChannels = 3; // must be PCSLAB or PCSXYZ
- if (elements->empty()) {
- return kPCSChannels == icf_channels(iccType);
- }
- // now let's verify that the input/output channels of each A2B element actually match up
- if (icf_channels(iccType) != elements->front().inputChannels()) {
- SkColorSpacePrintf("Input channel count does not match first A2B element's input count");
- return false;
- }
- for (size_t i = 1; i < elements->size(); ++i) {
- if ((*elements)[i - 1].outputChannels() != (*elements)[i].inputChannels()) {
- SkColorSpacePrintf("A2B elements don't agree in input/output channel counts");
- return false;
- }
- }
- if (kPCSChannels != elements->back().outputChannels()) {
- SkColorSpacePrintf("PCS channel count doesn't match last A2B element's output count");
- return false;
- }
- return true;
-}
-
-static bool tag_equals(const ICCTag* a, const ICCTag* b, const uint8_t* base) {
- if (!a || !b) {
- return a == b;
- }
-
- if (a->fLength != b->fLength) {
- return false;
- }
-
- if (a->fOffset == b->fOffset) {
- return true;
- }
-
- return !memcmp(a->addr(base), b->addr(base), a->fLength);
-}
-
-static inline bool is_close_to_d50(const SkMatrix44& matrix) {
- // rX + gX + bX
- float X = matrix.getFloat(0, 0) + matrix.getFloat(0, 1) + matrix.getFloat(0, 2);
-
- // rY + gY + bY
- float Y = matrix.getFloat(1, 0) + matrix.getFloat(1, 1) + matrix.getFloat(1, 2);
-
- // rZ + gZ + bZ
- float Z = matrix.getFloat(2, 0) + matrix.getFloat(2, 1) + matrix.getFloat(2, 2);
-
- static const float kD50_WhitePoint[3] = { 0.96420f, 1.00000f, 0.82491f };
-
- // This is a bit more lenient than QCMS and Adobe. Is there a reason to be stricter here?
- return (SkTAbs(X - kD50_WhitePoint[0]) <= 0.04f) &&
- (SkTAbs(Y - kD50_WhitePoint[1]) <= 0.04f) &&
- (SkTAbs(Z - kD50_WhitePoint[2]) <= 0.04f);
-}
-
-static sk_sp<SkColorSpace> make_xyz(const ICCProfileHeader& header, ICCTag* tags, int tagCount,
- const uint8_t* base, sk_sp<SkData> profileData) {
- if (kLAB_PCSSpace == header.fPCS) {
- return nullptr;
- }
-
- // Recognize the rXYZ, gXYZ, and bXYZ tags.
- const ICCTag* r = ICCTag::Find(tags, tagCount, kTAG_rXYZ);
- const ICCTag* g = ICCTag::Find(tags, tagCount, kTAG_gXYZ);
- const ICCTag* b = ICCTag::Find(tags, tagCount, kTAG_bXYZ);
- if (!r || !g || !b) {
- return nullptr;
- }
-
- float toXYZ[9];
- if (!load_xyz(&toXYZ[0], r->addr(base), r->fLength) ||
- !load_xyz(&toXYZ[3], g->addr(base), g->fLength) ||
- !load_xyz(&toXYZ[6], b->addr(base), b->fLength))
- {
- return_null("Need valid rgb tags for XYZ space");
- }
- SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
- mat.set3x3(toXYZ[0], toXYZ[1], toXYZ[2],
- toXYZ[3], toXYZ[4], toXYZ[5],
- toXYZ[6], toXYZ[7], toXYZ[8]);
- if (!is_close_to_d50(mat)) {
- return_null("XYZ matrix is not D50");
- }
-
- // If some, but not all, of the gamma tags are missing, assume that all
- // gammas are meant to be the same.
- r = ICCTag::Find(tags, tagCount, kTAG_rTRC);
- g = ICCTag::Find(tags, tagCount, kTAG_gTRC);
- b = ICCTag::Find(tags, tagCount, kTAG_bTRC);
- if (!r || !g || !b) {
- return_null("Need valid TRC tags for XYZ space");
- }
-
- SkGammaNamed gammaNamed = kNonStandard_SkGammaNamed;
- sk_sp<SkGammas> gammas = nullptr;
- size_t tagBytes;
- if (tag_equals(r, g, base) && tag_equals(g, b, base)) {
- SkGammas::Data data;
- SkColorSpaceTransferFn params;
- SkGammas::Type type =
- parse_gamma(&data, ¶ms, &tagBytes, r->addr(base), r->fLength);
- handle_invalid_gamma(&type, &data);
-
- if (SkGammas::Type::kNamed_Type == type) {
- gammaNamed = data.fNamed;
- } else {
- size_t allocSize = sizeof(SkGammas);
- if (!safe_add(allocSize, gamma_alloc_size(type, data), &allocSize)) {
- return_null("SkGammas struct is too large to allocate");
- }
- void* memory = sk_malloc_throw(allocSize);
- gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
- load_gammas(memory, 0, type, &data, params, r->addr(base));
-
- for (int i = 0; i < 3; ++i) {
- gammas->fType[i] = type;
- gammas->fData[i] = data;
- }
- }
- } else {
- SkGammas::Data rData;
- SkColorSpaceTransferFn rParams;
- SkGammas::Type rType =
- parse_gamma(&rData, &rParams, &tagBytes, r->addr(base), r->fLength);
- handle_invalid_gamma(&rType, &rData);
-
- SkGammas::Data gData;
- SkColorSpaceTransferFn gParams;
- SkGammas::Type gType =
- parse_gamma(&gData, &gParams, &tagBytes, g->addr(base), g->fLength);
- handle_invalid_gamma(&gType, &gData);
-
- SkGammas::Data bData;
- SkColorSpaceTransferFn bParams;
- SkGammas::Type bType =
- parse_gamma(&bData, &bParams, &tagBytes, b->addr(base), b->fLength);
- handle_invalid_gamma(&bType, &bData);
-
- size_t allocSize = sizeof(SkGammas);
- if (!safe_add(allocSize, gamma_alloc_size(rType, rData), &allocSize) ||
- !safe_add(allocSize, gamma_alloc_size(gType, gData), &allocSize) ||
- !safe_add(allocSize, gamma_alloc_size(bType, bData), &allocSize)) {
- return_null("SkGammas struct is too large to allocate");
- }
- void* memory = sk_malloc_throw(allocSize);
- gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
-
- uint32_t offset = 0;
- gammas->fType[0] = rType;
- offset += load_gammas(memory, offset, rType, &rData, rParams,
- r->addr(base));
-
- gammas->fType[1] = gType;
- offset += load_gammas(memory, offset, gType, &gData, gParams,
- g->addr(base));
-
- gammas->fType[2] = bType;
- load_gammas(memory, offset, bType, &bData, bParams, b->addr(base));
-
- gammas->fData[0] = rData;
- gammas->fData[1] = gData;
- gammas->fData[2] = bData;
- }
-
-
- if (kNonStandard_SkGammaNamed == gammaNamed) {
- // It's possible that we'll initially detect non-matching gammas, only for
- // them to evaluate to the same named gamma curve.
- gammaNamed = is_named(gammas);
- }
-
- if (kNonStandard_SkGammaNamed == gammaNamed) {
- return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed,
- std::move(gammas),
- mat, std::move(profileData)));
- }
-
- return SkColorSpace::MakeRGB(gammaNamed, mat);
-}
-
-static sk_sp<SkColorSpace> make_gray(const ICCProfileHeader& header, ICCTag* tags, int tagCount,
- const uint8_t* base, sk_sp<SkData> profileData) {
- if (kLAB_PCSSpace == header.fPCS) {
- return nullptr;
- }
-
- const ICCTag* grayTRC = ICCTag::Find(tags, tagCount, kTAG_kTRC);
- if (!grayTRC) {
- return_null("grayTRC tag required for monochrome profiles.");
- }
- SkGammas::Data data;
- SkColorSpaceTransferFn params;
- size_t tagBytes;
- SkGammas::Type type =
- parse_gamma(&data, ¶ms, &tagBytes, grayTRC->addr(base), grayTRC->fLength);
- handle_invalid_gamma(&type, &data);
-
- SkMatrix44 toXYZD50(SkMatrix44::kIdentity_Constructor);
- toXYZD50.setFloat(0, 0, kWhitePointD50[0]);
- toXYZD50.setFloat(1, 1, kWhitePointD50[1]);
- toXYZD50.setFloat(2, 2, kWhitePointD50[2]);
- if (SkGammas::Type::kNamed_Type == type) {
- return SkColorSpace::MakeRGB(data.fNamed, toXYZD50);
- }
-
- size_t allocSize = sizeof(SkGammas);
- if (!safe_add(allocSize, gamma_alloc_size(type, data), &allocSize)) {
- return_null("SkGammas struct is too large to allocate");
- }
- void* memory = sk_malloc_throw(allocSize);
- sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
- load_gammas(memory, 0, type, &data, params, grayTRC->addr(base));
- for (int i = 0; i < 3; ++i) {
- gammas->fType[i] = type;
- gammas->fData[i] = data;
- }
-
- return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(kNonStandard_SkGammaNamed,
- std::move(gammas),
- toXYZD50, std::move(profileData)));
-}
-
-static sk_sp<SkColorSpace> make_a2b(SkColorSpace::Type iccType,
- const ICCProfileHeader& header, ICCTag* tags, int tagCount,
- const uint8_t* base, sk_sp<SkData> profileData) {
- const ICCTag* a2b0 = ICCTag::Find(tags, tagCount, kTAG_A2B0);
- if (a2b0) {
- const SkColorSpace_A2B::PCS pcs = kXYZ_PCSSpace == header.fPCS
- ? SkColorSpace_A2B::PCS::kXYZ
- : SkColorSpace_A2B::PCS::kLAB;
- std::vector<SkColorSpace_A2B::Element> elements;
- if (load_a2b0(&elements, a2b0->addr(base), a2b0->fLength, pcs, iccType)) {
- return sk_sp<SkColorSpace>(new SkColorSpace_A2B(iccType, std::move(elements),
- pcs, std::move(profileData)));
- }
- }
-
- return nullptr;
-}
-
-sk_sp<SkColorSpace> SkColorSpace::MakeICC(const void* input, size_t len) {
- if (!input || len < kICCHeaderSize) {
- return_null("Data is null or not large enough to contain an ICC profile");
- }
-
- // Make sure we're at least as strict as skcms_Parse().
- skcms_ICCProfile p;
- if (!skcms_Parse(input, len, &p)) {
- return nullptr;
- }
-
- // Create our own copy of the input.
- void* memory = sk_malloc_throw(len);
- memcpy(memory, input, len);
- sk_sp<SkData> profileData = SkData::MakeFromMalloc(memory, len);
- const uint8_t* base = profileData->bytes();
- const uint8_t* ptr = base;
-
- // Read the ICC profile header and check to make sure that it is valid.
- ICCProfileHeader header;
- header.init(ptr, len);
- if (!header.valid()) {
- return nullptr;
- }
-
- // Adjust ptr and len before reading the tags.
- if (len < header.fSize) {
- SkColorSpacePrintf("ICC profile might be truncated.\n");
- } else if (len > header.fSize) {
- SkColorSpacePrintf("Caller provided extra data beyond the end of the ICC profile.\n");
- len = header.fSize;
- }
- ptr += kICCHeaderSize;
- len -= kICCHeaderSize;
-
- // Parse tag headers.
- uint32_t tagCount = header.fTagCount;
- SkColorSpacePrintf("ICC profile contains %d tags.\n", tagCount);
- if (len < kICCTagTableEntrySize * tagCount) {
- return_null("Not enough input data to read tag table entries");
- }
-
- SkAutoTArray<ICCTag> tags(tagCount);
- for (uint32_t i = 0; i < tagCount; i++) {
- ptr = tags[i].init(ptr);
- SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24) & 0xFF,
- (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >> 8) & 0xFF,
- (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLength);
-
- if (!tags[i].valid(kICCHeaderSize + len)) {
- return_null("Tag is too large to fit in ICC profile");
- }
- }
-
- Type a2b_type = kRGB_Type;
- switch (header.fInputColorSpace) {
- case kRGB_ColorSpace: {
- sk_sp<SkColorSpace> colorSpace =
- make_xyz(header, tags.get(), tagCount, base, profileData);
- if (colorSpace) {
- return colorSpace;
- }
- break;
- }
- case kGray_ColorSpace: {
- return make_gray(header, tags.get(), tagCount, base, profileData);
- }
- case kCMYK_ColorSpace:
- a2b_type = kCMYK_Type;
- break;
- default:
- return_null("ICC profile contains unsupported colorspace");
- }
-
- return make_a2b(a2b_type, header, tags.get(), tagCount, base, profileData);
-}
diff --git a/src/core/SkColorSpace_XYZ.cpp b/src/core/SkColorSpace_XYZ.cpp
index 8a38f52..cbb55cc 100644
--- a/src/core/SkColorSpace_XYZ.cpp
+++ b/src/core/SkColorSpace_XYZ.cpp
@@ -10,30 +10,16 @@
#include "SkColorSpaceXformPriv.h"
#include "SkOpts.h"
-SkColorSpace_XYZ::SkColorSpace_XYZ(SkGammaNamed gammaNamed, const SkMatrix44& toXYZD50)
- : fProfileData(nullptr)
- , fGammaNamed(gammaNamed)
- , fGammas(nullptr)
- , fToXYZD50(toXYZD50)
- , fToXYZD50Hash(SkOpts::hash_fn(toXYZD50.values(), 16 * sizeof(SkMScalar), 0))
- , fFromXYZD50(SkMatrix44::kUninitialized_Constructor)
-{}
-
-SkColorSpace_XYZ::SkColorSpace_XYZ(SkGammaNamed gammaNamed, sk_sp<SkGammas> gammas,
- const SkMatrix44& toXYZD50, sk_sp<SkData> profileData)
- : fProfileData(std::move(profileData))
- , fGammaNamed(gammaNamed)
- , fGammas(std::move(gammas))
- , fToXYZD50(toXYZD50)
- , fToXYZD50Hash(SkOpts::hash_fn(toXYZD50.values(), 16 * sizeof(SkMScalar), 0))
- , fFromXYZD50(SkMatrix44::kUninitialized_Constructor) {
- SkASSERT(!fGammas || 3 == fGammas->channels());
- if (fGammas) {
- for (int i = 0; i < fGammas->channels(); ++i) {
- if (SkGammas::Type::kTable_Type == fGammas->type(i)) {
- SkASSERT(fGammas->data(i).fTable.fSize >= 2);
- }
- }
+SkColorSpace_XYZ::SkColorSpace_XYZ(SkGammaNamed gammaNamed,
+ const SkColorSpaceTransferFn* transferFn,
+ const SkMatrix44& toXYZD50)
+ : fGammaNamed(gammaNamed)
+ , fToXYZD50(toXYZD50)
+ , fToXYZD50Hash(SkOpts::hash_fn(toXYZD50.values(), 16 * sizeof(SkMScalar), 0))
+ , fFromXYZD50(SkMatrix44::kUninitialized_Constructor) {
+ SkASSERT(fGammaNamed != kNonStandard_SkGammaNamed || transferFn);
+ if (transferFn) {
+ fTransferFn = *transferFn;
}
}
@@ -59,26 +45,25 @@
}
bool SkColorSpace_XYZ::onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const {
- if (named_to_parametric(coeffs, fGammaNamed)) {
- return true;
+ switch (fGammaNamed) {
+ case kSRGB_SkGammaNamed:
+ *coeffs = gSRGB_TransferFn;
+ break;
+ case k2Dot2Curve_SkGammaNamed:
+ *coeffs = g2Dot2_TransferFn;
+ break;
+ case kLinear_SkGammaNamed:
+ *coeffs = gLinear_TransferFn;
+ break;
+ case kNonStandard_SkGammaNamed:
+ *coeffs = fTransferFn;
+ break;
+ default:
+ SkDEBUGFAIL("Unknown named gamma");
+ return false;
}
- SkASSERT(fGammas);
- if (!fGammas->allChannelsSame()) {
- return false;
- }
-
- if (fGammas->isValue(0)) {
- value_to_parametric(coeffs, fGammas->data(0).fValue);
- return true;
- }
-
- if (fGammas->isParametric(0)) {
- *coeffs = fGammas->params(0);
- return true;
- }
-
- return false;
+ return true;
}
sk_sp<SkColorSpace> SkColorSpace_XYZ::makeLinearGamma() const {
@@ -100,5 +85,5 @@
spin.set3x3(0, 1, 0, 0, 0, 1, 1, 0, 0);
spin.postConcat(fToXYZD50);
(void)spin.getType(); // Pre-cache spin matrix type to avoid races in future getType() calls.
- return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(fGammaNamed, fGammas, spin, fProfileData));
+ return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(fGammaNamed, &fTransferFn, spin));
}
diff --git a/src/core/SkColorSpace_XYZ.h b/src/core/SkColorSpace_XYZ.h
index 72a1d62..97e3dbc 100644
--- a/src/core/SkColorSpace_XYZ.h
+++ b/src/core/SkColorSpace_XYZ.h
@@ -10,7 +10,6 @@
#include "SkColorSpace.h"
#include "SkData.h"
-#include "SkGammas.h"
#include "SkOnce.h"
class SkColorSpace_XYZ : public SkColorSpace {
@@ -24,29 +23,19 @@
bool onGammaIsLinear() const override;
bool onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const override;
- const SkData* onProfileData() const override { return fProfileData.get(); }
-
sk_sp<SkColorSpace> makeLinearGamma() const override;
sk_sp<SkColorSpace> makeSRGBGamma() const override;
sk_sp<SkColorSpace> makeColorSpin() const override;
SkGammaNamed onGammaNamed() const override { return fGammaNamed; }
- const SkGammas* gammas() const { return fGammas.get(); }
-
- void toDstGammaTables(const uint8_t* tables[3], sk_sp<SkData>* storage, int numTables) const;
-
- SkColorSpace_XYZ(SkGammaNamed gammaNamed, const SkMatrix44& toXYZ);
-
- SkColorSpace_XYZ(SkGammaNamed gammaNamed, sk_sp<SkGammas> gammas,
- const SkMatrix44& toXYZ, sk_sp<SkData> profileData);
+ SkColorSpace_XYZ(SkGammaNamed gammaNamed, const SkColorSpaceTransferFn* transferFn,
+ const SkMatrix44& toXYZ);
private:
- sk_sp<SkData> fProfileData;
-
- const SkGammaNamed fGammaNamed;
- sk_sp<SkGammas> fGammas;
- const SkMatrix44 fToXYZD50;
+ SkGammaNamed fGammaNamed;
+ SkColorSpaceTransferFn fTransferFn;
+ SkMatrix44 fToXYZD50;
uint32_t fToXYZD50Hash;
mutable SkMatrix44 fFromXYZD50;
diff --git a/src/core/SkGammas.h b/src/core/SkGammas.h
deleted file mode 100644
index 6cb504c..0000000
--- a/src/core/SkGammas.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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 SkGammas_DEFINED
-#define SkGammas_DEFINED
-
-#include "SkColorSpace.h"
-#include "SkData.h"
-#include "SkTemplates.h"
-
-struct SkGammas : SkRefCnt {
-
- // There are four possible representations for gamma curves. kNone_Type is used
- // as a placeholder until the struct is initialized. It is not a valid value.
- enum class Type {
- kNone_Type,
- kNamed_Type,
- kValue_Type,
- kTable_Type,
- kParam_Type,
- };
-
- // Contains information for a gamma table.
- struct Table {
- size_t fOffset;
- int fSize;
-
- const float* table(const SkGammas* base) const {
- return SkTAddOffset<const float>(base, sizeof(SkGammas) + fOffset);
- }
- };
-
- // Contains the actual gamma curve information. Should be interpreted
- // based on the type of the gamma curve.
- union Data {
- Data() : fTable{0, 0} {}
-
- SkGammaNamed fNamed;
- float fValue;
- Table fTable;
- size_t fParamOffset;
-
- const SkColorSpaceTransferFn& params(const SkGammas* base) const {
- return *SkTAddOffset<const SkColorSpaceTransferFn>(base,
- sizeof(SkGammas) + fParamOffset);
- }
- };
-
- bool allChannelsSame() const {
- // All channels are the same type?
- Type type = this->type(0);
- for (int i = 1; i < this->channels(); i++) {
- if (type != this->type(i)) {
- return false;
- }
- }
-
- // All data the same?
- auto& first = this->data(0);
- for (int i = 1; i < this->channels(); i++) {
- auto& data = this->data(i);
- switch (type) {
- case Type:: kNone_Type: break;
- case Type::kNamed_Type: if (first.fNamed != data.fNamed) { return false; } break;
- case Type::kValue_Type: if (first.fValue != data.fValue) { return false; } break;
- case Type::kTable_Type:
- if (first.fTable.fOffset != data.fTable.fOffset) { return false; }
- if (first.fTable.fSize != data.fTable.fSize ) { return false; }
- break;
- case Type::kParam_Type:
- if (0 != memcmp(&first.params(this), &data.params(this),
- sizeof(SkColorSpaceTransferFn))) {
- return false;
- }
- break;
- }
- }
- return true;
- }
-
- bool isNamed (int i) const { return Type::kNamed_Type == this->type(i); }
- bool isValue (int i) const { return Type::kValue_Type == this->type(i); }
- bool isTable (int i) const { return Type::kTable_Type == this->type(i); }
- bool isParametric(int i) const { return Type::kParam_Type == this->type(i); }
-
- const Data& data(int i) const {
- SkASSERT(i >= 0 && i < fChannels);
- return fData[i];
- }
-
- const float* table(int i) const {
- SkASSERT(this->isTable(i));
- return this->data(i).fTable.table(this);
- }
-
- int tableSize(int i) const {
- SkASSERT(this->isTable(i));
- return this->data(i).fTable.fSize;
- }
-
- const SkColorSpaceTransferFn& params(int i) const {
- SkASSERT(this->isParametric(i));
- return this->data(i).params(this);
- }
-
- Type type(int i) const {
- SkASSERT(i >= 0 && i < fChannels);
- return fType[i];
- }
-
- int channels() const { return fChannels; }
-
- SkGammas(int channels) : fChannels(channels) {
- SkASSERT(channels <= (int)SK_ARRAY_COUNT(fType));
- for (Type& t : fType) {
- t = Type::kNone_Type;
- }
- }
-
- // These fields should only be modified when initializing the struct.
- int fChannels;
- Data fData[4];
- Type fType[4];
-
- // Objects of this type are sometimes created in a custom fashion using
- // sk_malloc_throw and therefore must be sk_freed. We overload new to
- // also call sk_malloc_throw so that memory can be unconditionally released
- // using sk_free in an overloaded delete. Overloading regular new means we
- // must also overload placement new.
- void* operator new(size_t size) { return sk_malloc_throw(size); }
- void* operator new(size_t, void* p) { return p; }
- void operator delete(void* p) { sk_free(p); }
-};
-
-#endif