Flutter Engine
The Flutter Engine
ColorSpaceTest.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
10#include "include/core/SkData.h"
17#include "modules/skcms/skcms.h"
19#include "tests/Test.h"
20#include "tools/Resources.h"
21
22#include <climits>
23#include <cstring>
24#include <memory>
25#include <utility>
26
27#include <png.h>
28
29static bool almost_equal(float a, float b) {
30 return SkTAbs(a - b) < 0.001f;
31}
32
34 const float red[], const float green[], const float blue[],
35 bool expectSRGB = false) {
36
37 REPORTER_ASSERT(r, nullptr != space);
38 REPORTER_ASSERT(r, expectSRGB == space->gammaCloseToSRGB());
39
41 space->toXYZD50(&mat);
42 const float* ref[3] = { red, green, blue };
43 for (int i = 0; i < 3; ++i) {
44 REPORTER_ASSERT(r, almost_equal(ref[i][0], mat.vals[0][i]));
45 REPORTER_ASSERT(r, almost_equal(ref[i][1], mat.vals[1][i]));
46 REPORTER_ASSERT(r, almost_equal(ref[i][2], mat.vals[2][i]));
47 }
48}
49
50static void test_path(skiatest::Reporter* r, const char* path,
51 const float red[], const float green[], const float blue[],
52 bool expectSRGB = false) {
53 std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
54 REPORTER_ASSERT(r, nullptr != stream);
55 if (!stream) {
56 return;
57 }
58
59 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
60 REPORTER_ASSERT(r, nullptr != codec);
61 if (!codec) {
62 return;
63 }
64
65 auto colorSpace = codec->getInfo().refColorSpace();
66 test_space(r, colorSpace.get(), red, green, blue, expectSRGB);
67}
68
69static constexpr float g_sRGB_R[]{ 0.4358f, 0.2224f, 0.0139f };
70static constexpr float g_sRGB_G[]{ 0.3853f, 0.7170f, 0.0971f };
71static constexpr float g_sRGB_B[]{ 0.1430f, 0.0606f, 0.7139f };
72
73DEF_TEST(ColorSpace_sRGB, r) {
75
76}
77
78DEF_TEST(ColorSpaceParseICCProfiles, r) {
79
80#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
81 test_path(r, "images/color_wheel_with_profile.png", g_sRGB_R, g_sRGB_G, g_sRGB_B, true);
82#endif
83
84 const float red[] = { 0.385117f, 0.716904f, 0.0970612f };
85 const float green[] = { 0.143051f, 0.0606079f, 0.713913f };
86 const float blue[] = { 0.436035f, 0.222488f, 0.013916f };
87 test_path(r, "images/icc-v2-gbr.jpg", red, green, blue);
88
89 test_path(r, "images/webp-color-profile-crash.webp",
90 red, green, blue);
91 test_path(r, "images/webp-color-profile-lossless.webp",
92 red, green, blue);
93 test_path(r, "images/webp-color-profile-lossy.webp",
94 red, green, blue);
95 test_path(r, "images/webp-color-profile-lossy-alpha.webp",
96 red, green, blue);
97}
98
99static void test_serialize(skiatest::Reporter* r, const sk_sp<SkColorSpace>& space, bool isNamed) {
100 sk_sp<SkData> data1 = space->serialize();
101
102 size_t bytes = space->writeToMemory(nullptr);
104 space->writeToMemory(data2->writable_data());
105
106 // Handy for creating some seed data for fuzzing
107#if defined(SK_DUMP_TO_DISK)
108 static int counter = 0;
109 SkFILEWStream outFile(SkStringPrintf("/tmp/colorspace_%d.icc", counter).c_str());
110 counter += 1;
111 REPORTER_ASSERT(r, outFile.write(data2->data(), data2->size()));
112 outFile.flush();
113 outFile.fsync();
114#endif
115
116 sk_sp<SkColorSpace> newSpace1 = SkColorSpace::Deserialize(data1->data(), data1->size());
117 sk_sp<SkColorSpace> newSpace2 = SkColorSpace::Deserialize(data2->data(), data2->size());
118
119 if (isNamed) {
120 REPORTER_ASSERT(r, space.get() == newSpace1.get());
121 REPORTER_ASSERT(r, space.get() == newSpace2.get());
122 } else {
123 REPORTER_ASSERT(r, SkColorSpace::Equals(space.get(), newSpace1.get()));
124 REPORTER_ASSERT(r, SkColorSpace::Equals(space.get(), newSpace2.get()));
125 }
126}
127
128DEF_TEST(ColorSpace_Serialize, r) {
131
132 auto test = [&](const char* path) {
134
136 REPORTER_ASSERT(r, skcms_Parse(data->data(), data->size(), &profile));
137
139 REPORTER_ASSERT(r, space);
140
141 test_serialize(r, space, false);
142 };
143 test("icc_profiles/HP_ZR30w.icc");
144 test("icc_profiles/HP_Z32x.icc");
145
147 fn.a = 1.0f;
148 fn.b = 0.0f;
149 fn.c = 1.0f;
150 fn.d = 0.5f;
151 fn.e = 0.0f;
152 fn.f = 0.0f;
153 fn.g = 1.0f;
154 skcms_Matrix3x3 toXYZ = {{
155 { 1, 0, 0 },
156 { 0, 1, 0 },
157 { 0, 0, 1 },
158 }};
159 test_serialize(r, SkColorSpace::MakeRGB(fn, toXYZ), false);
160}
161
162DEF_TEST(ColorSpace_Equals, r) {
164
165 auto parse = [&](const char* path) {
167
169 REPORTER_ASSERT(r, skcms_Parse(data->data(), data->size(), &profile));
170
172 REPORTER_ASSERT(r, space);
173
174 return space;
175 };
176 sk_sp<SkColorSpace> z30 = parse("icc_profiles/HP_ZR30w.icc");
177 sk_sp<SkColorSpace> z32 = parse("icc_profiles/HP_Z32x.icc");
178
180 fn.a = 1.0f;
181 fn.b = 0.0f;
182 fn.c = 1.0f;
183 fn.d = 0.5f;
184 fn.e = 0.0f;
185 fn.f = 0.0f;
186 fn.g = 1.0f;
187 skcms_Matrix3x3 toXYZ = {{
188 { 1, 0, 0 },
189 { 0, 1, 0 },
190 { 0, 0, 1 },
191 }};
193
194 REPORTER_ASSERT(r, SkColorSpace::Equals(nullptr, nullptr));
195 REPORTER_ASSERT(r, SkColorSpace::Equals(srgb.get(), srgb.get()));
198 REPORTER_ASSERT(r, SkColorSpace::Equals(rgb4.get(), rgb4.get()));
199
200 REPORTER_ASSERT(r, !SkColorSpace::Equals(nullptr, srgb.get()));
201 REPORTER_ASSERT(r, !SkColorSpace::Equals(srgb.get(), nullptr));
202 REPORTER_ASSERT(r, !SkColorSpace::Equals(z30.get(), srgb.get()));
203 REPORTER_ASSERT(r, !SkColorSpace::Equals(z32.get(), z30.get()));
204 REPORTER_ASSERT(r, !SkColorSpace::Equals(z30.get(), rgb4.get()));
205 REPORTER_ASSERT(r, !SkColorSpace::Equals(srgb.get(), rgb4.get()));
206}
207
208static inline bool matrix_almost_equal(const skcms_Matrix3x3& a, const skcms_Matrix3x3& b) {
209 for (int r = 0; r < 3; ++r) {
210 for (int c = 0; c < 3; ++c) {
211 if (!almost_equal(a.vals[r][c], b.vals[r][c])) {
212 return false;
213 }
214 }
215 }
216 return true;
217}
218
219static inline void check_primaries(skiatest::Reporter* r, const SkColorSpacePrimaries& primaries,
220 const skcms_Matrix3x3& reference) {
221 skcms_Matrix3x3 toXYZ;
222 bool result = primaries.toXYZD50(&toXYZ);
224 REPORTER_ASSERT(r, matrix_almost_equal(toXYZ, reference));
225}
226
227DEF_TEST(ColorSpace_Primaries, r) {
228 // sRGB primaries (D65)
229 skcms_Matrix3x3 srgbToXYZ;
231 0.64f, 0.33f,
232 0.30f, 0.60f,
233 0.15f, 0.06f,
234 0.3127f, 0.3290f,
235 &srgbToXYZ);
237
240
241 // ProPhoto (D50)
242 SkColorSpacePrimaries proPhoto;
243 proPhoto.fRX = 0.7347f;
244 proPhoto.fRY = 0.2653f;
245 proPhoto.fGX = 0.1596f;
246 proPhoto.fGY = 0.8404f;
247 proPhoto.fBX = 0.0366f;
248 proPhoto.fBY = 0.0001f;
249 proPhoto.fWX = 0.34567f;
250 proPhoto.fWY = 0.35850f;
251 skcms_Matrix3x3 proToXYZ = {{
252 { 0.7976749f, 0.1351917f, 0.0313534f },
253 { 0.2880402f, 0.7118741f, 0.0000857f },
254 { 0.0000000f, 0.0000000f, 0.8252100f },
255 }};
256 check_primaries(r, proPhoto, proToXYZ);
257
258 // NTSC (C)
260 ntsc.fRX = 0.67f;
261 ntsc.fRY = 0.33f;
262 ntsc.fGX = 0.21f;
263 ntsc.fGY = 0.71f;
264 ntsc.fBX = 0.14f;
265 ntsc.fBY = 0.08f;
266 ntsc.fWX = 0.31006f;
267 ntsc.fWY = 0.31616f;
268 skcms_Matrix3x3 ntscToXYZ = {{
269 { 0.6343706f, 0.1852204f, 0.1446290f },
270 { 0.3109496f, 0.5915984f, 0.0974520f },
271 { -0.0011817f, 0.0555518f, 0.7708399f }
272 }};
273 check_primaries(r, ntsc, ntscToXYZ);
274
275 // DCI P3 (D65)
277 p3.fRX = 0.680f;
278 p3.fRY = 0.320f;
279 p3.fGX = 0.265f;
280 p3.fGY = 0.690f;
281 p3.fBX = 0.150f;
282 p3.fBY = 0.060f;
283 p3.fWX = 0.3127f;
284 p3.fWY = 0.3290f;
286 skcms_Matrix3x3 reference;
287 SkAssertResult(space->toXYZD50(&reference));
288 check_primaries(r, p3, reference);
289
290 // Rec 2020 (D65)
292 rec2020.fRX = 0.708f;
293 rec2020.fRY = 0.292f;
294 rec2020.fGX = 0.170f;
295 rec2020.fGY = 0.797f;
296 rec2020.fBX = 0.131f;
297 rec2020.fBY = 0.046f;
298 rec2020.fWX = 0.3127f;
299 rec2020.fWY = 0.3290f;
301 SkAssertResult(space->toXYZD50(&reference));
302 check_primaries(r, rec2020, reference);
303}
304
305DEF_TEST(ColorSpace_MatrixHash, r) {
307
309 fn.a = 1.0f;
310 fn.b = 0.0f;
311 fn.c = 0.0f;
312 fn.d = 0.0f;
313 fn.e = 0.0f;
314 fn.f = 0.0f;
315 fn.g = 3.0f;
316
318
319 REPORTER_ASSERT(r, srgb->toXYZD50Hash() == strange->toXYZD50Hash());
320}
321
322DEF_TEST(ColorSpace_IsSRGB, r) {
324
326 fn.a = 1.0f;
327 fn.b = 0.0f;
328 fn.c = 0.0f;
329 fn.d = 0.0f;
330 fn.e = 0.0f;
331 fn.f = 0.0f;
332 fn.g = 2.2f;
334
335 REPORTER_ASSERT(r, srgb0->isSRGB());
336 REPORTER_ASSERT(r, !twoDotTwo->isSRGB());
337}
338
339DEF_TEST(ColorSpace_skcms_IsSRGB, r) {
341 REPORTER_ASSERT(r, srgb->isSRGB());
342}
343
344DEF_TEST(ColorSpace_skcms_sRGB_exact, r) {
347
348 REPORTER_ASSERT(r, 0 == memcmp(&profile, skcms_sRGB_profile(), sizeof(skcms_ICCProfile)));
349}
350
351DEF_TEST(ColorSpace_classifyUnderflow, r) {
352 // crbug.com/1016183
354 fn.a = 1.0f;
355 fn.b = 0.0f;
356 fn.c = 0.0f;
357 fn.d = 0.0f;
358 fn.e = 0.0f;
359 fn.f = 0.0f;
360 fn.g = INT_MIN;
362 REPORTER_ASSERT(r, bad == nullptr);
363}
364
365DEF_TEST(ColorSpace_equiv, r) {
368
369 // Previously a NaN anywhere in the tf or gamut would trip up Equals(),
370 // making us think we'd hit a hash collision where we hadn't.
371 gamut.vals[1][1] = SK_FloatNaN;
372
373 // There's a quick pointer comparison in SkColorSpace::Equals() we want to get past.
375 y = SkColorSpace::MakeRGB(tf, gamut);
376 REPORTER_ASSERT(r, x && y);
377 REPORTER_ASSERT(r, x.get() != y.get());
378
379 // Most important to test in debug mode that we don't SkASSERT().
380 REPORTER_ASSERT(r, SkColorSpace::Equals(x.get(), y.get()));
381}
static void test_space(skiatest::Reporter *r, SkColorSpace *space, const float red[], const float green[], const float blue[], bool expectSRGB=false)
static constexpr float g_sRGB_G[]
static bool matrix_almost_equal(const skcms_Matrix3x3 &a, const skcms_Matrix3x3 &b)
static constexpr float g_sRGB_R[]
static constexpr float g_sRGB_B[]
static void test_path(skiatest::Reporter *r, const char *path, const float red[], const float green[], const float blue[], bool expectSRGB=false)
static bool almost_equal(float a, float b)
DEF_TEST(ColorSpace_sRGB, r)
static void check_primaries(skiatest::Reporter *r, const SkColorSpacePrimaries &primaries, const skcms_Matrix3x3 &reference)
static void test_serialize(skiatest::Reporter *r, const sk_sp< SkColorSpace > &space, bool isNamed)
#define test(name)
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
static sk_sp< SkColorSpace > rec2020()
std::unique_ptr< SkStreamAsset > GetResourceAsStream(const char *resource, bool useFileStream)
Definition: Resources.cpp:31
sk_sp< SkData > GetResourceAsData(const char *resource)
Definition: Resources.cpp:42
SkColorSpace * sk_srgb_singleton()
constexpr float SK_FloatNaN
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
static T SkTAbs(T value)
Definition: SkTemplates.h:43
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
static std::unique_ptr< SkCodec > MakeFromStream(std::unique_ptr< SkStream >, SkSpan< const SkCodecs::Decoder > decoders, Result *=nullptr, SkPngChunkReader *=nullptr, SelectionPolicy selectionPolicy=SelectionPolicy::kPreferStillImage)
Definition: SkCodec.cpp:163
bool gammaCloseToSRGB() const
void toProfile(skcms_ICCProfile *) const
bool toXYZD50(skcms_Matrix3x3 *toXYZD50) const
static bool Equals(const SkColorSpace *, const SkColorSpace *)
static sk_sp< SkColorSpace > MakeSRGB()
static sk_sp< SkColorSpace > Deserialize(const void *data, size_t length)
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
uint32_t toXYZD50Hash() const
Definition: SkColorSpace.h:161
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
const void * data() const
Definition: SkData.h:37
void * writable_data()
Definition: SkData.h:52
size_t size() const
Definition: SkData.h:30
void fsync()
Definition: SkStream.cpp:449
bool write(const void *buffer, size_t size) override
Definition: SkStream.cpp:426
void flush() override
Definition: SkStream.cpp:442
T * get() const
Definition: SkRefCnt.h:303
static bool b
struct MyStruct a[10]
GAsyncResult * result
double y
double x
static constexpr skcms_Matrix3x3 kSRGB
Definition: SkColorSpace.h:67
static constexpr skcms_Matrix3x3 kRec2020
Definition: SkColorSpace.h:93
static constexpr skcms_Matrix3x3 kDisplayP3
Definition: SkColorSpace.h:87
static constexpr skcms_TransferFunction kSRGB
Definition: SkColorSpace.h:45
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
def parse(repo_root, recipes_cfg_path)
Definition: recipes.py:56
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
const skcms_ICCProfile * skcms_sRGB_profile()
Definition: skcms.cc:1393
static bool skcms_Parse(const void *buf, size_t len, skcms_ICCProfile *profile)
Definition: skcms_public.h:245
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