WIP: Skia support library for ICC tasks
As a starting point, this would be mostly trivial to implement using SkColorSpace. This also would give us the flexibility to begin to move all of the ICC related code from SkColorSpace to SkICC. What are the advantages of moving this away from SkColorSpace? (1) A long term goal (once Chrome uses SkCodec), might be to move SkColorSpace::MakeICC() out of the public API. That way, we can guarantee that we can draw to/from *any* SkColorSpace. (2) Keeps SkColorSpace separate from ICC-specific representations like SkColorSpaceTransferFn etc. BUG=skia: Change-Id: Iddeb9903221fb57fbfc01218d8641c928b4a5165 Reviewed-on: https://skia-review.googlesource.com/5676 Commit-Queue: Matt Sarett <msarett@google.com> Reviewed-by: Brian Osman <brianosman@google.com> Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
parent
0eb6ed421c
commit
fc8dc3194a
@ -153,6 +153,7 @@ skia_core_sources = [
|
||||
"$_src/core/SkGraphics.cpp",
|
||||
"$_src/core/SkHalf.cpp",
|
||||
"$_src/core/SkHalf.h",
|
||||
"$_src/core/SkICC.cpp",
|
||||
"$_src/core/SkImageFilter.cpp",
|
||||
"$_src/core/SkImageFilterCache.cpp",
|
||||
"$_src/core/SkImageFilterCache.h",
|
||||
|
@ -99,6 +99,7 @@ tests_sources = [
|
||||
"$_tests/GrTRecorderTest.cpp",
|
||||
"$_tests/HashTest.cpp",
|
||||
"$_tests/image-bitmap.cpp",
|
||||
"$_tests/ICCTest.cpp",
|
||||
"$_tests/ImageCacheTest.cpp",
|
||||
"$_tests/ImageFilterCacheTest.cpp",
|
||||
"$_tests/ImageFilterTest.cpp",
|
||||
|
73
include/core/SkICC.h
Normal file
73
include/core/SkICC.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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 SkICC_DEFINED
|
||||
#define SkICC_DEFINED
|
||||
|
||||
#include "SkRefCnt.h"
|
||||
|
||||
struct SkColorSpaceTransferFn;
|
||||
class SkColorSpace;
|
||||
class SkData;
|
||||
class SkMatrix44;
|
||||
|
||||
class SK_API SkICC : public SkRefCnt {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Parse an ICC profile.
|
||||
*
|
||||
* Returns nullptr if the data is not a valid ICC profile or if the profile
|
||||
* input space is not RGB.
|
||||
*/
|
||||
static sk_sp<SkICC> Make(const void*, size_t);
|
||||
|
||||
/**
|
||||
* If the gamut can be represented as transformation into XYZ D50, returns
|
||||
* true and sets the proper values in |toXYZD50|.
|
||||
*
|
||||
* If not, returns false. This indicates that the ICC data is too complex
|
||||
* to isolate a simple gamut transformation.
|
||||
*/
|
||||
bool toXYZD50(SkMatrix44* toXYZD50) const;
|
||||
|
||||
/**
|
||||
* If the transfer function can be represented as coefficients to the standard
|
||||
* equation, returns true and sets |fn| to the proper values.
|
||||
*
|
||||
* If not, returns false. This indicates one of the following:
|
||||
* (1) The R, G, and B transfer functions are not the same.
|
||||
* (2) The transfer function is represented as a table that we have not managed
|
||||
* to match to a standard curve.
|
||||
* (3) The ICC data is too complex to isolate a single transfer function.
|
||||
*/
|
||||
bool isNumericalTransferFn(SkColorSpaceTransferFn* fn) const;
|
||||
|
||||
/**
|
||||
* If the transfer function can be approximated as coefficients to the standard
|
||||
* equation, returns true and sets |fn| to the proper values.
|
||||
*
|
||||
* If not, returns false. This indicates one of the following:
|
||||
* (1) The R, G, and B transfer functions are not the same.
|
||||
* (2) The transfer function is represented as a table that is not increasing with
|
||||
* end points at zero and one.
|
||||
* (3) The ICC data is too complex to isolate a single transfer function.
|
||||
*/
|
||||
bool approximateNumericalTransferFn(SkColorSpaceTransferFn* fn) const;
|
||||
|
||||
/**
|
||||
* Write an ICC profile with transfer function |fn| and gamut |toXYZD50|.
|
||||
*/
|
||||
static sk_sp<SkData> WriteToICC(const SkColorSpaceTransferFn& fn, const SkMatrix44& toXYZD50);
|
||||
|
||||
private:
|
||||
SkICC(sk_sp<SkColorSpace> colorSpace);
|
||||
|
||||
sk_sp<SkColorSpace> fColorSpace;
|
||||
};
|
||||
|
||||
#endif
|
@ -5,14 +5,22 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#define SkColorSpacePrintf(...)
|
||||
|
||||
static inline bool color_space_almost_equal(float a, float b) {
|
||||
return SkTAbs(a - b) < 0.01f;
|
||||
}
|
||||
|
||||
static inline float add_epsilon(float v) {
|
||||
return v + FLT_MIN;
|
||||
}
|
||||
|
||||
static inline bool is_zero_to_one(float v) {
|
||||
return (0.0f <= v) && (v <= 1.0f);
|
||||
// Because we allow a value just barely larger than 1, the client can use an
|
||||
// entirely linear transfer function.
|
||||
return (0.0f <= v) && (v <= add_epsilon(1.0f));
|
||||
}
|
||||
|
||||
static inline bool is_valid_transfer_fn(const SkColorSpaceTransferFn& coeffs) {
|
||||
@ -84,3 +92,44 @@ static inline bool is_almost_2dot2(const SkColorSpaceTransferFn& coeffs) {
|
||||
color_space_almost_equal(0.0f, coeffs.fF) &&
|
||||
color_space_almost_equal(2.2f, coeffs.fG);
|
||||
}
|
||||
|
||||
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->fA = 1.0f / 1.055f;
|
||||
coeffs->fB = 0.055f / 1.055f;
|
||||
coeffs->fC = 0.0f;
|
||||
coeffs->fD = 0.04045f;
|
||||
coeffs->fE = 1.0f / 12.92f;
|
||||
coeffs->fF = 0.0f;
|
||||
coeffs->fG = 2.4f;
|
||||
return true;
|
||||
case k2Dot2Curve_SkGammaNamed:
|
||||
value_to_parametric(coeffs, 2.2f);
|
||||
return true;
|
||||
case kLinear_SkGammaNamed:
|
||||
coeffs->fA = 0.0f;
|
||||
coeffs->fB = 0.0f;
|
||||
coeffs->fC = 0.0f;
|
||||
// Make sure that we use the linear segment of the transfer function even
|
||||
// when the x-value is 1.0f.
|
||||
coeffs->fD = add_epsilon(1.0f);
|
||||
coeffs->fE = 1.0f;
|
||||
coeffs->fF = 0.0f;
|
||||
coeffs->fG = 0.0f;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -69,35 +69,19 @@ bool SkColorSpaceXform_A2B::onApply(ColorFormat dstFormat, void* dst, ColorForma
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline SkColorSpaceTransferFn value_to_parametric(float exp) {
|
||||
return {exp, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f};
|
||||
}
|
||||
|
||||
static inline SkColorSpaceTransferFn gammanamed_to_parametric(SkGammaNamed gammaNamed) {
|
||||
switch (gammaNamed) {
|
||||
case kLinear_SkGammaNamed:
|
||||
return value_to_parametric(1.f);
|
||||
case kSRGB_SkGammaNamed:
|
||||
return {2.4f, (1.f / 1.055f), (0.055f / 1.055f), 0.f, 0.04045f, (1.f / 12.92f), 0.f};
|
||||
case k2Dot2Curve_SkGammaNamed:
|
||||
return value_to_parametric(2.2f);
|
||||
default:
|
||||
SkASSERT(false);
|
||||
return {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f};
|
||||
}
|
||||
}
|
||||
|
||||
static inline SkColorSpaceTransferFn gamma_to_parametric(const SkGammas& gammas, int channel) {
|
||||
static inline bool gamma_to_parametric(SkColorSpaceTransferFn* coeffs, const SkGammas& gammas,
|
||||
int channel) {
|
||||
switch (gammas.type(channel)) {
|
||||
case SkGammas::Type::kNamed_Type:
|
||||
return gammanamed_to_parametric(gammas.data(channel).fNamed);
|
||||
return named_to_parametric(coeffs, gammas.data(channel).fNamed);
|
||||
case SkGammas::Type::kValue_Type:
|
||||
return value_to_parametric(gammas.data(channel).fValue);
|
||||
value_to_parametric(coeffs, gammas.data(channel).fValue);
|
||||
return true;
|
||||
case SkGammas::Type::kParam_Type:
|
||||
return gammas.params(channel);
|
||||
*coeffs = gammas.params(channel);
|
||||
return true;
|
||||
default:
|
||||
SkASSERT(false);
|
||||
return {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f};
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static inline SkColorSpaceTransferFn invert_parametric(const SkColorSpaceTransferFn& fn) {
|
||||
@ -183,6 +167,10 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
|
||||
currentChannels = e.outputChannels();
|
||||
switch (e.type()) {
|
||||
case SkColorSpace_A2B::Element::Type::kGammaNamed:
|
||||
if (kLinear_SkGammaNamed == e.gammaNamed()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// take the fast path for 3-channel named gammas
|
||||
if (3 == currentChannels) {
|
||||
if (k2Dot2Curve_SkGammaNamed == e.gammaNamed()) {
|
||||
@ -196,12 +184,11 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (kLinear_SkGammaNamed != e.gammaNamed()) {
|
||||
SkCSXformPrintf("Gamma stage added: %s\n",
|
||||
debugGammaNamed[(int)e.gammaNamed()]);
|
||||
SkColorSpaceTransferFn fn = gammanamed_to_parametric(e.gammaNamed());
|
||||
this->addTransferFns(fn, currentChannels);
|
||||
}
|
||||
|
||||
SkCSXformPrintf("Gamma stage added: %s\n", debugGammaNamed[(int)e.gammaNamed()]);
|
||||
SkColorSpaceTransferFn fn;
|
||||
SkAssertResult(named_to_parametric(&fn, e.gammaNamed()));
|
||||
this->addTransferFns(fn, currentChannels);
|
||||
break;
|
||||
case SkColorSpace_A2B::Element::Type::kGammas: {
|
||||
const SkGammas& gammas = e.gammas();
|
||||
@ -221,7 +208,8 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
|
||||
this->addTableFn(table, channel);
|
||||
gammaNeedsRef = true;
|
||||
} else {
|
||||
SkColorSpaceTransferFn fn = gamma_to_parametric(gammas, channel);
|
||||
SkColorSpaceTransferFn fn;
|
||||
SkAssertResult(gamma_to_parametric(&fn, gammas, channel));
|
||||
this->addTransferFn(fn, channel);
|
||||
}
|
||||
}
|
||||
@ -301,9 +289,9 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
|
||||
|
||||
this->addTableFn(table, channel);
|
||||
} else {
|
||||
SkColorSpaceTransferFn fn =
|
||||
invert_parametric(gamma_to_parametric(gammas, channel));
|
||||
this->addTransferFn(fn, channel);
|
||||
SkColorSpaceTransferFn fn;
|
||||
SkAssertResult(gamma_to_parametric(&fn, gammas, channel));
|
||||
this->addTransferFn(invert_parametric(fn), channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,15 +47,10 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool onGammaCloseToSRGB() const override {
|
||||
// There is no single gamma curve in an A2B0 profile
|
||||
return false;
|
||||
}
|
||||
|
||||
bool onGammaIsLinear() const override {
|
||||
// There is no single gamma curve in an A2B0 profile
|
||||
return false;
|
||||
}
|
||||
// There is no single gamma curve in an A2B0 profile
|
||||
bool onGammaCloseToSRGB() const override { return false; }
|
||||
bool onGammaIsLinear() const override { return false; }
|
||||
bool onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const override { return false; }
|
||||
|
||||
sk_sp<SkColorSpace> makeLinearGamma() override {
|
||||
// TODO: Analyze the extrema of our projection into XYZ and use suitable primaries?
|
||||
|
@ -55,6 +55,10 @@ struct SkGammas : SkRefCnt {
|
||||
this->fTable.fSize == that.fTable.fSize;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Data& that) const {
|
||||
return !(*this == that);
|
||||
}
|
||||
|
||||
SkGammaNamed fNamed;
|
||||
float fValue;
|
||||
Table fTable;
|
||||
@ -153,6 +157,8 @@ public:
|
||||
|
||||
virtual bool onGammaIsLinear() const = 0;
|
||||
|
||||
virtual bool onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const = 0;
|
||||
|
||||
/**
|
||||
* Returns a color space with the same gamut as this one, but with a linear gamma.
|
||||
* For color spaces whose gamut can not be described in terms of XYZ D50, returns
|
||||
|
@ -5,8 +5,9 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkColorSpace_XYZ.h"
|
||||
#include "SkChecksum.h"
|
||||
#include "SkColorSpace_XYZ.h"
|
||||
#include "SkColorSpacePriv.h"
|
||||
#include "SkColorSpaceXform_Base.h"
|
||||
|
||||
static constexpr float gSRGB_toXYZD50[] {
|
||||
@ -64,6 +65,29 @@ bool SkColorSpace_XYZ::onGammaIsLinear() const {
|
||||
return kLinear_SkGammaNamed == fGammaNamed;
|
||||
}
|
||||
|
||||
bool SkColorSpace_XYZ::onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const {
|
||||
if (named_to_parametric(coeffs, fGammaNamed)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SkASSERT(fGammas);
|
||||
if (fGammas->data(0) != fGammas->data(1) || fGammas->data(0) != fGammas->data(2)) {
|
||||
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;
|
||||
}
|
||||
|
||||
sk_sp<SkColorSpace> SkColorSpace_XYZ::makeLinearGamma() {
|
||||
if (this->gammaIsLinear()) {
|
||||
return sk_ref_sp(this);
|
||||
|
@ -23,6 +23,8 @@ public:
|
||||
|
||||
bool onGammaIsLinear() const override;
|
||||
|
||||
bool onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const override;
|
||||
|
||||
Type type() const override { return Type::kXYZ; }
|
||||
|
||||
sk_sp<SkColorSpace> makeLinearGamma() override;
|
||||
|
36
src/core/SkICC.cpp
Normal file
36
src/core/SkICC.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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_Base.h"
|
||||
#include "SkICC.h"
|
||||
|
||||
SkICC::SkICC(sk_sp<SkColorSpace> colorSpace)
|
||||
: fColorSpace(std::move(colorSpace))
|
||||
{}
|
||||
|
||||
sk_sp<SkICC> SkICC::Make(const void* ptr, size_t len) {
|
||||
sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeICC(ptr, len);
|
||||
if (!colorSpace) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return sk_sp<SkICC>(new SkICC(std::move(colorSpace)));
|
||||
}
|
||||
|
||||
bool SkICC::toXYZD50(SkMatrix44* toXYZD50) const {
|
||||
const SkMatrix44* m = as_CSB(fColorSpace)->toXYZD50();
|
||||
if (!m) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*toXYZD50 = *m;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkICC::isNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const {
|
||||
return as_CSB(fColorSpace)->onIsNumericalTransferFn(coeffs);
|
||||
}
|
97
tests/ICCTest.cpp
Normal file
97
tests/ICCTest.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 "Resources.h"
|
||||
#include "SkColorSpace.h"
|
||||
#include "SkData.h"
|
||||
#include "SkICC.h"
|
||||
#include "SkMatrix44.h"
|
||||
#include "Test.h"
|
||||
|
||||
static bool almost_equal(float a, float b) {
|
||||
return SkTAbs(a - b) < 0.001f;
|
||||
}
|
||||
|
||||
static inline void test_to_xyz_d50(skiatest::Reporter* r, SkICC* icc, bool shouldSucceed,
|
||||
const float* reference) {
|
||||
SkMatrix44 result;
|
||||
REPORTER_ASSERT(r, shouldSucceed == icc->toXYZD50(&result));
|
||||
if (shouldSucceed) {
|
||||
float resultVals[16];
|
||||
result.asColMajorf(resultVals);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
REPORTER_ASSERT(r, almost_equal(resultVals[i], reference[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(ICC_ToXYZD50, r) {
|
||||
const float z30Reference[16] = {
|
||||
0.59825f, 0.27103f, 0.00603f, 0.0f, 0.22243f, 0.67447f, 0.07368f, 0.0f, 0.14352f, 0.05449f,
|
||||
0.74519f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
|
||||
sk_sp<SkData> data = SkData::MakeFromFileName(
|
||||
GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str());
|
||||
sk_sp<SkICC> z30 = SkICC::Make(data->data(), data->size());
|
||||
test_to_xyz_d50(r, z30.get(), true, z30Reference);
|
||||
|
||||
const float z32Reference[16] = {
|
||||
0.61583f, 0.28789f, 0.00513f, 0.0f, 0.20428f, 0.66972f, 0.06609f, 0.0f, 0.14409f, 0.04237f,
|
||||
0.75368f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
|
||||
data = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str());
|
||||
sk_sp<SkICC> z32 = SkICC::Make(data->data(), data->size());
|
||||
test_to_xyz_d50(r, z32.get(), true, z32Reference);
|
||||
|
||||
data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str());
|
||||
sk_sp<SkICC> upperLeft = SkICC::Make(data->data(), data->size());
|
||||
test_to_xyz_d50(r, upperLeft.get(), false, z32Reference);
|
||||
|
||||
data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str());
|
||||
sk_sp<SkICC> upperRight = SkICC::Make(data->data(), data->size());
|
||||
test_to_xyz_d50(r, upperRight.get(), false, z32Reference);
|
||||
}
|
||||
|
||||
static inline void test_is_numerical_transfer_fn(skiatest::Reporter* r, SkICC* icc,
|
||||
bool shouldSucceed,
|
||||
const SkColorSpaceTransferFn& reference) {
|
||||
SkColorSpaceTransferFn result;
|
||||
REPORTER_ASSERT(r, shouldSucceed == icc->isNumericalTransferFn(&result));
|
||||
if (shouldSucceed) {
|
||||
REPORTER_ASSERT(r, 0 == memcmp(&result, &reference, sizeof(SkColorSpaceTransferFn)));
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(ICC_IsNumericalTransferFn, r) {
|
||||
SkColorSpaceTransferFn referenceFn;
|
||||
referenceFn.fA = 1.0f;
|
||||
referenceFn.fB = 0.0f;
|
||||
referenceFn.fC = 0.0f;
|
||||
referenceFn.fD = 0.0f;
|
||||
referenceFn.fE = 0.0f;
|
||||
referenceFn.fF = 0.0f;
|
||||
referenceFn.fG = 2.2f;
|
||||
|
||||
sk_sp<SkData> data = SkData::MakeFromFileName(
|
||||
GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str());
|
||||
sk_sp<SkICC> z30 = SkICC::Make(data->data(), data->size());
|
||||
test_is_numerical_transfer_fn(r, z30.get(), true, referenceFn);
|
||||
|
||||
data = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str());
|
||||
sk_sp<SkICC> z32 = SkICC::Make(data->data(), data->size());
|
||||
test_is_numerical_transfer_fn(r, z32.get(), true, referenceFn);
|
||||
|
||||
data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str());
|
||||
sk_sp<SkICC> upperLeft = SkICC::Make(data->data(), data->size());
|
||||
test_is_numerical_transfer_fn(r, upperLeft.get(), false, referenceFn);
|
||||
|
||||
data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str());
|
||||
sk_sp<SkICC> upperRight = SkICC::Make(data->data(), data->size());
|
||||
test_is_numerical_transfer_fn(r, upperRight.get(), false, referenceFn);
|
||||
}
|
Loading…
Reference in New Issue
Block a user