Flutter Engine
The Flutter Engine
SkColorSpace.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
11#include "modules/skcms/skcms.h"
12#include "src/core/SkChecksum.h"
14
15#include <cmath>
16#include <cstring>
17
19 return skcms_PrimariesToXYZD50(fRX, fRY, fGX, fGY, fBX, fBY, fWX, fWY, toXYZ_D50);
20}
21
22SkColorSpace::SkColorSpace(const skcms_TransferFunction& transferFn,
23 const skcms_Matrix3x3& toXYZD50)
24 : fTransferFn(transferFn)
25 , fToXYZD50(toXYZD50) {
26 fTransferFnHash = SkChecksum::Hash32(&fTransferFn, 7*sizeof(float));
27 fToXYZD50Hash = SkChecksum::Hash32(&fToXYZD50, 9*sizeof(float));
28}
29
30static bool xyz_almost_equal(const skcms_Matrix3x3& mA, const skcms_Matrix3x3& mB) {
31 for (int r = 0; r < 3; ++r) {
32 for (int c = 0; c < 3; ++c) {
33 if (!color_space_almost_equal(mA.vals[r][c], mB.vals[r][c])) {
34 return false;
35 }
36 }
37 }
38
39 return true;
40}
41
43 const skcms_Matrix3x3& toXYZ) {
45 return nullptr;
46 }
47
49
53 }
55 } else if (is_almost_2dot2(transferFn)) {
57 } else if (is_almost_linear(transferFn)) {
60 }
62 }
63
64 return sk_sp<SkColorSpace>(new SkColorSpace(*tf, toXYZ));
65}
66
68public:
69 static SkColorSpace* Make(const skcms_TransferFunction& transferFn,
70 const skcms_Matrix3x3& to_xyz) {
71 return new SkColorSpace(transferFn, to_xyz);
72 }
73};
74
78 return cs;
79}
80
84 return cs;
85}
86
89}
90
93}
94
95void SkColorSpace::computeLazyDstFields() const {
96 fLazyDstFieldsOnce([this] {
97
98 // Invert 3x3 gamut, defaulting to sRGB if we can't.
99 {
100 if (!skcms_Matrix3x3_invert(&fToXYZD50, &fFromXYZD50)) {
102 &fFromXYZD50));
103 }
104 }
105
106 // Invert transfer function, defaulting to sRGB if we can't.
107 {
108 if (!skcms_TransferFunction_invert(&fTransferFn, &fInvTransferFn)) {
109 fInvTransferFn = *skcms_sRGB_Inverse_TransferFunction();
110 }
111 }
112
113 });
114}
115
117 // TODO: Change transferFn/invTransferFn to just operate on skcms_TransferFunction (all callers
118 // already pass pointers to an skcms struct). Then remove this function, and update the two
119 // remaining callers to do the right thing with transferFn and classify.
120 this->transferFn(coeffs);
122}
123
124void SkColorSpace::transferFn(float gabcdef[7]) const {
125 memcpy(gabcdef, &fTransferFn, 7*sizeof(float));
126}
127
129 *fn = fTransferFn;
130}
131
133 this->computeLazyDstFields();
134 *fn = fInvTransferFn;
135}
136
138 *toXYZD50 = fToXYZD50;
139 return true;
140}
141
143 dst->computeLazyDstFields();
144 *src_to_dst = skcms_Matrix3x3_concat(&dst->fFromXYZD50, &fToXYZD50);
145}
146
148 return sk_srgb_singleton() == this;
149}
150
152 // Nearly-equal transfer functions were snapped at construction time, so just do an exact test
153 return memcmp(&fTransferFn, &SkNamedTransferFn::kSRGB, 7*sizeof(float)) == 0;
154}
155
157 // Nearly-equal transfer functions were snapped at construction time, so just do an exact test
158 return memcmp(&fTransferFn, &SkNamedTransferFn::kLinear, 7*sizeof(float)) == 0;
159}
160
162 if (this->gammaIsLinear()) {
163 return sk_ref_sp(const_cast<SkColorSpace*>(this));
164 }
166}
167
169 if (this->gammaCloseToSRGB()) {
170 return sk_ref_sp(const_cast<SkColorSpace*>(this));
171 }
173}
174
177 { 0, 0, 1 },
178 { 1, 0, 0 },
179 { 0, 1, 0 },
180 }};
181
182 skcms_Matrix3x3 spun = skcms_Matrix3x3_concat(&fToXYZD50, &spin);
183
184 return sk_sp<SkColorSpace>(new SkColorSpace(fTransferFn, spun));
185}
186
189 skcms_SetTransferFunction(profile, &fTransferFn);
190 skcms_SetXYZD50 (profile, &fToXYZD50);
191}
192
194 // TODO: move below ≈sRGB test?
195 if (!profile.has_toXYZD50 || !profile.has_trc) {
196 return nullptr;
197 }
198
200 return SkColorSpace::MakeSRGB();
201 }
202
203 // TODO: can we save this work and skip lazily inverting the matrix later?
205 if (!skcms_Matrix3x3_invert(&profile.toXYZD50, &inv)) {
206 return nullptr;
207 }
208
209 // We can't work with tables or mismatched parametric curves,
210 // but if they all look close enough to sRGB, that's fine.
211 // TODO: should we maybe do this unconditionally to snap near-sRGB parametrics to sRGB?
212 const skcms_Curve* trc = profile.trc;
213 if (trc[0].table_entries != 0 ||
214 trc[1].table_entries != 0 ||
215 trc[2].table_entries != 0 ||
216 0 != memcmp(&trc[0].parametric, &trc[1].parametric, sizeof(trc[0].parametric)) ||
217 0 != memcmp(&trc[0].parametric, &trc[2].parametric, sizeof(trc[0].parametric)))
218 {
221 }
222 return nullptr;
223 }
224
225 return SkColorSpace::MakeRGB(profile.trc[0].parametric, profile.toXYZD50);
226}
227
228///////////////////////////////////////////////////////////////////////////////////////////////////
229
231 k0_Version, // Initial (deprecated) version, no longer supported
232 k1_Version, // Simple header (version tag) + 16 floats
233
235};
236
239
240 // Other fields were only used by k0_Version. Could be re-purposed in future versions.
241 uint8_t fReserved0 = 0;
242 uint8_t fReserved1 = 0;
243 uint8_t fReserved2 = 0;
244};
245
246size_t SkColorSpace::writeToMemory(void* memory) const {
247 if (memory) {
248 *((ColorSpaceHeader*) memory) = ColorSpaceHeader();
249 memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader));
250
251 memcpy(memory, &fTransferFn, 7 * sizeof(float));
252 memory = SkTAddOffset<void>(memory, 7 * sizeof(float));
253
254 memcpy(memory, &fToXYZD50, 9 * sizeof(float));
255 }
256
257 return sizeof(ColorSpaceHeader) + 16 * sizeof(float);
258}
259
262 this->writeToMemory(data->writable_data());
263 return data;
264}
265
267 if (length < sizeof(ColorSpaceHeader)) {
268 return nullptr;
269 }
270
272 data = SkTAddOffset<const void>(data, sizeof(ColorSpaceHeader));
273 length -= sizeof(ColorSpaceHeader);
274 if (header.fVersion != k1_Version) {
275 return nullptr;
276 }
277
278 if (length < 16 * sizeof(float)) {
279 return nullptr;
280 }
281
283 memcpy(&transferFn, data, 7 * sizeof(float));
284 data = SkTAddOffset<const void>(data, 7 * sizeof(float));
285
286 skcms_Matrix3x3 toXYZ;
287 memcpy(&toXYZ, data, 9 * sizeof(float));
288 return SkColorSpace::MakeRGB(transferFn, toXYZ);
289}
290
292 if (x == y) {
293 return true;
294 }
295
296 if (!x || !y) {
297 return false;
298 }
299
300 if (x->hash() == y->hash()) {
301 #if defined(SK_DEBUG)
302 // Do these floats function equivalently?
303 // This returns true more often than simple float comparison (NaN vs. NaN) and,
304 // also returns true more often than simple bitwise comparison (+0 vs. -0) and,
305 // even returns true more often than those two OR'd together (two different NaNs).
306 auto equiv = [](float X, float Y) {
307 return (X==Y)
308 || (std::isnan(X) && std::isnan(Y));
309 };
310
311 for (int i = 0; i < 7; i++) {
312 float X = (&x->fTransferFn.g)[i],
313 Y = (&y->fTransferFn.g)[i];
314 SkASSERTF(equiv(X,Y), "Hash collision at tf[%d], !equiv(%g,%g)\n", i, X,Y);
315 }
316 for (int r = 0; r < 3; r++)
317 for (int c = 0; c < 3; c++) {
318 float X = x->fToXYZD50.vals[r][c],
319 Y = y->fToXYZD50.vals[r][c];
320 SkASSERTF(equiv(X,Y), "Hash collision at toXYZD50[%d][%d], !equiv(%g,%g)\n", r,c, X,Y);
321 }
322 #endif
323 return true;
324 }
325 return false;
326}
static SkM44 inv(const SkM44 &m)
Definition: 3d.cpp:26
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
#define SkASSERTF(cond, fmt,...)
Definition: SkAssert.h:117
static bool is_almost_linear(const skcms_TransferFunction &coeffs)
static bool color_space_almost_equal(float a, float b)
static bool is_almost_2dot2(const skcms_TransferFunction &coeffs)
static bool is_almost_srgb(const skcms_TransferFunction &coeffs)
SkColorSpace * sk_srgb_linear_singleton()
Version
@ k0_Version
@ k1_Version
@ kCurrent_Version
SkColorSpace * sk_srgb_singleton()
static bool xyz_almost_equal(const skcms_Matrix3x3 &mA, const skcms_Matrix3x3 &mB)
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
static const SkScalar Y
Definition: StrokeBench.cpp:55
static const SkScalar X
Definition: StrokeBench.cpp:54
static SkColorSpace * Make(const skcms_TransferFunction &transferFn, const skcms_Matrix3x3 &to_xyz)
bool gammaIsLinear() const
bool gammaCloseToSRGB() const
void toProfile(skcms_ICCProfile *) const
bool isNumericalTransferFn(skcms_TransferFunction *fn) const
bool toXYZD50(skcms_Matrix3x3 *toXYZD50) const
sk_sp< SkColorSpace > makeSRGBGamma() const
void invTransferFn(skcms_TransferFunction *fn) const
static bool Equals(const SkColorSpace *, const SkColorSpace *)
void gamutTransformTo(const SkColorSpace *dst, skcms_Matrix3x3 *src_to_dst) const
sk_sp< SkColorSpace > makeLinearGamma() const
static sk_sp< SkColorSpace > MakeSRGB()
sk_sp< SkColorSpace > makeColorSpin() const
static sk_sp< SkColorSpace > Deserialize(const void *data, size_t length)
void transferFn(float gabcdef[7]) const
static sk_sp< SkColorSpace > MakeRGB(const skcms_TransferFunction &transferFn, const skcms_Matrix3x3 &toXYZ)
static sk_sp< SkColorSpace > Make(const skcms_ICCProfile &)
size_t writeToMemory(void *memory) const
sk_sp< SkData > serialize() const
bool isSRGB() const
static sk_sp< SkColorSpace > MakeSRGBLinear()
static sk_sp< SkData > MakeUninitialized(size_t length)
Definition: SkData.cpp:116
void * writable_data()
Definition: SkData.h:52
size_t length
double y
double x
uint32_t Hash32(const void *data, size_t bytes, uint32_t seed)
Definition: SkChecksum.cpp:113
static constexpr skcms_Matrix3x3 kSRGB
Definition: SkColorSpace.h:67
static constexpr skcms_TransferFunction k2Dot2
Definition: SkColorSpace.h:48
static constexpr skcms_TransferFunction kSRGB
Definition: SkColorSpace.h:45
static constexpr skcms_TransferFunction kLinear
Definition: SkColorSpace.h:51
dst
Definition: cp.py:12
bool skcms_PrimariesToXYZD50(float rx, float ry, float gx, float gy, float bx, float by, float wx, float wy, skcms_Matrix3x3 *toXYZD50)
Definition: skcms.cc:1747
bool skcms_Matrix3x3_invert(const skcms_Matrix3x3 *src, skcms_Matrix3x3 *dst)
Definition: skcms.cc:1792
skcms_TFType skcms_TransferFunction_getType(const skcms_TransferFunction *tf)
Definition: skcms.cc:183
const skcms_TransferFunction * skcms_sRGB_Inverse_TransferFunction()
Definition: skcms.cc:1591
bool skcms_TransferFunction_invert(const skcms_TransferFunction *src, skcms_TransferFunction *dst)
Definition: skcms.cc:1863
const skcms_ICCProfile * skcms_sRGB_profile()
Definition: skcms.cc:1393
bool skcms_TRCs_AreApproximateInverse(const skcms_ICCProfile *profile, const skcms_TransferFunction *inv_tf)
Definition: skcms.cc:1679
bool skcms_ApproximatelyEqualProfiles(const skcms_ICCProfile *A, const skcms_ICCProfile *B)
Definition: skcms.cc:1621
skcms_Matrix3x3 skcms_Matrix3x3_concat(const skcms_Matrix3x3 *A, const skcms_Matrix3x3 *B)
Definition: skcms.cc:1849
static void skcms_SetXYZD50(skcms_ICCProfile *p, const skcms_Matrix3x3 *m)
Definition: skcms_public.h:399
static void skcms_SetTransferFunction(skcms_ICCProfile *p, const skcms_TransferFunction *tf)
Definition: skcms_public.h:390
@ skcms_TFType_Invalid
Definition: skcms_public.h:55
@ skcms_TFType_sRGBish
Definition: skcms_public.h:56
static void skcms_Init(skcms_ICCProfile *p)
Definition: skcms_public.h:384
static const char header[]
Definition: skpbench.cpp:88
bool toXYZD50(skcms_Matrix3x3 *toXYZD50) const
float vals[3][3]
Definition: skcms_public.h:27
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
static sk_sp< SkColorFilter > spin(sk_sp< SkColorFilter > cf)