blob: d749931de1ffac3664f3a44d52e7be07e23dd5cb [file] [log] [blame]
msarett6a738212016-03-04 13:27:35 -08001/*
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
8#include "Resources.h"
9#include "SkCodec.h"
10#include "SkColorSpace.h"
msarett8cc20912016-05-23 09:29:29 -070011#include "SkColorSpace_Base.h"
msarett6a738212016-03-04 13:27:35 -080012#include "Test.h"
13
msarett53add952016-03-07 17:25:12 -080014#include "png.h"
15
reed50d3b572016-05-03 12:13:21 -070016static bool almost_equal(float a, float b) {
17 return SkTAbs(a - b) < 0.001f;
18}
19
20static void test_space(skiatest::Reporter* r, SkColorSpace* space,
21 const float red[], const float green[], const float blue[],
msarett600c7372016-09-07 12:03:53 -070022 const SkGammaNamed expectedGamma) {
msarettbb9f7742016-05-17 09:31:20 -070023
msarett9d15dab2016-08-24 07:36:06 -070024 REPORTER_ASSERT(r, nullptr != space);
msarett600c7372016-09-07 12:03:53 -070025 REPORTER_ASSERT(r, expectedGamma == as_CSB(space)->gammaNamed());
msarettbb9f7742016-05-17 09:31:20 -070026
brianosman971cd492016-09-08 10:10:11 -070027 const SkMatrix44& mat = space->toXYZD50();
reed50d3b572016-05-03 12:13:21 -070028 const float src[] = {
29 1, 0, 0, 1,
30 0, 1, 0, 1,
31 0, 0, 1, 1,
32 };
brianosmande68d6c2016-09-09 10:36:17 -070033 const float* ref[3] = { red, green, blue };
reed50d3b572016-05-03 12:13:21 -070034 float dst[4];
35 for (int i = 0; i < 3; ++i) {
36 mat.mapScalars(&src[i*4], dst);
brianosmande68d6c2016-09-09 10:36:17 -070037 REPORTER_ASSERT(r, almost_equal(ref[i][0], dst[0]));
38 REPORTER_ASSERT(r, almost_equal(ref[i][1], dst[1]));
39 REPORTER_ASSERT(r, almost_equal(ref[i][2], dst[2]));
reed50d3b572016-05-03 12:13:21 -070040 }
41}
42
msarett9d15dab2016-08-24 07:36:06 -070043static void test_path(skiatest::Reporter* r, const char* path,
44 const float red[], const float green[], const float blue[],
msarett600c7372016-09-07 12:03:53 -070045 const SkGammaNamed expectedGamma) {
bungemanf93d7112016-09-16 06:24:20 -070046 SkAutoTDelete<SkStream> stream(GetResourceAsStream(path));
msarett0e6274f2016-03-21 08:04:40 -070047 REPORTER_ASSERT(r, nullptr != stream);
Brian Salomond2100f22016-03-25 17:02:20 -040048 if (!stream) {
49 return;
50 }
msarett0e6274f2016-03-21 08:04:40 -070051
52 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
53 REPORTER_ASSERT(r, nullptr != codec);
Brian Salomond2100f22016-03-25 17:02:20 -040054 if (!codec) {
55 return;
56 }
msarett0e6274f2016-03-21 08:04:40 -070057
msarett530c8442016-07-21 11:57:49 -070058 SkColorSpace* colorSpace = codec->getInfo().colorSpace();
msarett9d15dab2016-08-24 07:36:06 -070059 test_space(r, colorSpace, red, green, blue, expectedGamma);
60}
61
brianosmande68d6c2016-09-09 10:36:17 -070062static constexpr float g_sRGB_XYZ[]{
63 0.4358f, 0.3853f, 0.1430f, // Rx, Gx, Bx
64 0.2224f, 0.7170f, 0.0606f, // Ry, Gy, Gz
65 0.0139f, 0.0971f, 0.7139f, // Rz, Gz, Bz
66};
67
68static constexpr float g_sRGB_R[]{ 0.4358f, 0.2224f, 0.0139f };
69static constexpr float g_sRGB_G[]{ 0.3853f, 0.7170f, 0.0971f };
70static constexpr float g_sRGB_B[]{ 0.1430f, 0.0606f, 0.7139f };
msarett9d15dab2016-08-24 07:36:06 -070071
72DEF_TEST(ColorSpace_sRGB, r) {
73 test_space(r, SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named).get(),
brianosmande68d6c2016-09-09 10:36:17 -070074 g_sRGB_R, g_sRGB_G, g_sRGB_B, kSRGB_SkGammaNamed);
msarett9d15dab2016-08-24 07:36:06 -070075
76}
77
78DEF_TEST(ColorSpaceParseICCProfiles, r) {
79
80#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
brianosmande68d6c2016-09-09 10:36:17 -070081 test_path(r, "color_wheel_with_profile.png", g_sRGB_R, g_sRGB_G, g_sRGB_B,
msarett600c7372016-09-07 12:03:53 -070082 kSRGB_SkGammaNamed);
msarett9d15dab2016-08-24 07:36:06 -070083#endif
msarett0e6274f2016-03-21 08:04:40 -070084
reed50d3b572016-05-03 12:13:21 -070085 const float red[] = { 0.385117f, 0.716904f, 0.0970612f };
86 const float green[] = { 0.143051f, 0.0606079f, 0.713913f };
87 const float blue[] = { 0.436035f, 0.222488f, 0.013916f };
msarett600c7372016-09-07 12:03:53 -070088 test_path(r, "icc-v2-gbr.jpg", red, green, blue, k2Dot2Curve_SkGammaNamed);
msarett9d15dab2016-08-24 07:36:06 -070089
90 test_path(r, "webp-color-profile-crash.webp",
msarett600c7372016-09-07 12:03:53 -070091 red, green, blue, kNonStandard_SkGammaNamed);
msarett9d15dab2016-08-24 07:36:06 -070092 test_path(r, "webp-color-profile-lossless.webp",
msarett600c7372016-09-07 12:03:53 -070093 red, green, blue, kNonStandard_SkGammaNamed);
msarett9d15dab2016-08-24 07:36:06 -070094 test_path(r, "webp-color-profile-lossy.webp",
msarett600c7372016-09-07 12:03:53 -070095 red, green, blue, kNonStandard_SkGammaNamed);
msarett9d15dab2016-08-24 07:36:06 -070096 test_path(r, "webp-color-profile-lossy-alpha.webp",
msarett600c7372016-09-07 12:03:53 -070097 red, green, blue, kNonStandard_SkGammaNamed);
msarett0e6274f2016-03-21 08:04:40 -070098}
msarett02125d12016-05-03 14:24:47 -070099
100DEF_TEST(ColorSpaceSRGBCompare, r) {
101 // Create an sRGB color space by name
102 sk_sp<SkColorSpace> namedColorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
103
104 // Create an sRGB color space by value
105 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor);
msarettc1a3e242016-06-23 12:42:29 -0700106 srgbToxyzD50.set3x3RowMajorf(g_sRGB_XYZ);
msarett48ba2b82016-09-07 18:55:49 -0700107 sk_sp<SkColorSpace> rgbColorSpace =
108 SkColorSpace::NewRGB(SkColorSpace::kSRGB_RenderTargetGamma, srgbToxyzD50);
msarettab926f02016-05-25 08:53:40 -0700109 REPORTER_ASSERT(r, rgbColorSpace == namedColorSpace);
msarett02125d12016-05-03 14:24:47 -0700110
111 // Change a single value from the sRGB matrix
112 srgbToxyzD50.set(2, 2, 0.5f);
msarett48ba2b82016-09-07 18:55:49 -0700113 sk_sp<SkColorSpace> strangeColorSpace =
114 SkColorSpace::NewRGB(SkColorSpace::kSRGB_RenderTargetGamma, srgbToxyzD50);
msarett02125d12016-05-03 14:24:47 -0700115 REPORTER_ASSERT(r, strangeColorSpace != namedColorSpace);
116}
msarettab926f02016-05-25 08:53:40 -0700117
brianosmaneec83042016-09-27 15:11:47 -0700118DEF_TEST(ColorSpaceSRGBLinearCompare, r) {
119 // Create the linear sRGB color space by name
120 sk_sp<SkColorSpace> namedColorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGBLinear_Named);
121
122 // Create the linear sRGB color space via the sRGB color space's makeLinearGamma()
123 sk_sp<SkColorSpace> viaSrgbColorSpace =
124 SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)->makeLinearGamma();
125 REPORTER_ASSERT(r, namedColorSpace == viaSrgbColorSpace);
126
127 // Create a linear sRGB color space by value
128 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor);
129 srgbToxyzD50.set3x3RowMajorf(g_sRGB_XYZ);
130 sk_sp<SkColorSpace> rgbColorSpace =
131 SkColorSpace::NewRGB(SkColorSpace::kLinear_RenderTargetGamma, srgbToxyzD50);
132 REPORTER_ASSERT(r, rgbColorSpace == namedColorSpace);
133
134 // Change a single value from the sRGB matrix
135 srgbToxyzD50.set(2, 2, 0.5f);
136 sk_sp<SkColorSpace> strangeColorSpace =
137 SkColorSpace::NewRGB(SkColorSpace::kLinear_RenderTargetGamma, srgbToxyzD50);
138 REPORTER_ASSERT(r, strangeColorSpace != namedColorSpace);
139}
140
msarettc213f0d2016-08-01 14:23:32 -0700141class ColorSpaceTest {
142public:
143 static sk_sp<SkData> WriteToICC(SkColorSpace* space) {
144 return as_CSB(space)->writeToICC();
145 }
146};
147
msarettab926f02016-05-25 08:53:40 -0700148DEF_TEST(ColorSpaceWriteICC, r) {
149 // Test writing a new ICC profile
150 sk_sp<SkColorSpace> namedColorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
msarettc213f0d2016-08-01 14:23:32 -0700151 sk_sp<SkData> namedData = ColorSpaceTest::WriteToICC(namedColorSpace.get());
msarettab926f02016-05-25 08:53:40 -0700152 sk_sp<SkColorSpace> iccColorSpace = SkColorSpace::NewICC(namedData->data(), namedData->size());
brianosmande68d6c2016-09-09 10:36:17 -0700153 test_space(r, iccColorSpace.get(), g_sRGB_R, g_sRGB_G, g_sRGB_B, k2Dot2Curve_SkGammaNamed);
msarettc4ce6b52016-06-16 07:37:41 -0700154 // FIXME (msarett): Test disabled. sRGB profiles are written approximately as 2.2f curves.
155 // REPORTER_ASSERT(r, iccColorSpace == namedColorSpace);
msarettab926f02016-05-25 08:53:40 -0700156
157 // Test saving the original ICC data
158 sk_sp<SkData> monitorData = SkData::MakeFromFileName(
msaretta714bc32016-07-29 08:58:33 -0700159 GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str());
msarettab926f02016-05-25 08:53:40 -0700160 REPORTER_ASSERT(r, monitorData);
161 if (!monitorData) {
162 return;
163 }
164 sk_sp<SkColorSpace> monitorSpace = SkColorSpace::NewICC(monitorData->data(),
165 monitorData->size());
msarettc213f0d2016-08-01 14:23:32 -0700166 sk_sp<SkData> newMonitorData = ColorSpaceTest::WriteToICC(monitorSpace.get());
msarettab926f02016-05-25 08:53:40 -0700167 sk_sp<SkColorSpace> newMonitorSpace = SkColorSpace::NewICC(newMonitorData->data(),
168 newMonitorData->size());
brianosman971cd492016-09-08 10:10:11 -0700169 REPORTER_ASSERT(r, monitorSpace->toXYZD50() == newMonitorSpace->toXYZD50());
msarett600c7372016-09-07 12:03:53 -0700170 REPORTER_ASSERT(r, as_CSB(monitorSpace)->gammaNamed() == as_CSB(newMonitorSpace)->gammaNamed());
msarettab926f02016-05-25 08:53:40 -0700171}
reedfbce71f2016-06-02 12:40:22 -0700172
173DEF_TEST(ColorSpace_Named, r) {
174 const struct {
175 SkColorSpace::Named fNamed;
brianosmaneec83042016-09-27 15:11:47 -0700176 SkGammaNamed fExpectedGamma;
reedfbce71f2016-06-02 12:40:22 -0700177 } recs[] {
brianosmaneec83042016-09-27 15:11:47 -0700178 { SkColorSpace::kSRGB_Named, kSRGB_SkGammaNamed },
179 { SkColorSpace::kAdobeRGB_Named, k2Dot2Curve_SkGammaNamed },
180 { SkColorSpace::kSRGBLinear_Named, kLinear_SkGammaNamed },
reedfbce71f2016-06-02 12:40:22 -0700181 };
182
183 for (auto rec : recs) {
184 auto cs = SkColorSpace::NewNamed(rec.fNamed);
msarett47ae5c52016-09-06 10:01:48 -0700185 REPORTER_ASSERT(r, cs);
reedfbce71f2016-06-02 12:40:22 -0700186 if (cs) {
brianosmaneec83042016-09-27 15:11:47 -0700187 REPORTER_ASSERT(r, rec.fExpectedGamma == as_CSB(cs)->gammaNamed());
reedfbce71f2016-06-02 12:40:22 -0700188 }
189 }
190
191 SkImageInfo info = SkImageInfo::MakeS32(10, 10, kPremul_SkAlphaType);
reeddabe5d32016-06-21 10:28:14 -0700192 REPORTER_ASSERT(r, info.gammaCloseToSRGB());
reedfbce71f2016-06-02 12:40:22 -0700193}
msarett111a42d2016-06-22 08:18:54 -0700194
195static void test_serialize(skiatest::Reporter* r, SkColorSpace* space, bool isNamed) {
msaretta0605bf2016-07-28 10:47:50 -0700196 sk_sp<SkData> data1 = space->serialize();
197
198 size_t bytes = space->writeToMemory(nullptr);
199 sk_sp<SkData> data2 = SkData::MakeUninitialized(bytes);
200 space->writeToMemory(data2->writable_data());
201
202 sk_sp<SkColorSpace> newSpace1 = SkColorSpace::Deserialize(data1->data(), data1->size());
203 sk_sp<SkColorSpace> newSpace2 = SkColorSpace::Deserialize(data2->data(), data2->size());
msarett111a42d2016-06-22 08:18:54 -0700204
205 if (isNamed) {
msaretta0605bf2016-07-28 10:47:50 -0700206 REPORTER_ASSERT(r, space == newSpace1.get());
207 REPORTER_ASSERT(r, space == newSpace2.get());
msarett111a42d2016-06-22 08:18:54 -0700208 } else {
msarettd9015a42016-08-22 12:29:31 -0700209 REPORTER_ASSERT(r, SkColorSpace::Equals(space, newSpace1.get()));
210 REPORTER_ASSERT(r, SkColorSpace::Equals(space, newSpace2.get()));
msarett111a42d2016-06-22 08:18:54 -0700211 }
212}
213
214DEF_TEST(ColorSpace_Serialize, r) {
215 test_serialize(r, SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named).get(), true);
216 test_serialize(r, SkColorSpace::NewNamed(SkColorSpace::kAdobeRGB_Named).get(), true);
brianosmaneec83042016-09-27 15:11:47 -0700217 test_serialize(r, SkColorSpace::NewNamed(SkColorSpace::kSRGBLinear_Named).get(), true);
msarett111a42d2016-06-22 08:18:54 -0700218
219 sk_sp<SkData> monitorData = SkData::MakeFromFileName(
msaretta714bc32016-07-29 08:58:33 -0700220 GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str());
221 test_serialize(r, SkColorSpace::NewICC(monitorData->data(), monitorData->size()).get(), false);
222 monitorData = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str());
223 test_serialize(r, SkColorSpace::NewICC(monitorData->data(), monitorData->size()).get(), false);
224 monitorData = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str());
225 test_serialize(r, SkColorSpace::NewICC(monitorData->data(), monitorData->size()).get(), false);
226 monitorData = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str());
msarett111a42d2016-06-22 08:18:54 -0700227 test_serialize(r, SkColorSpace::NewICC(monitorData->data(), monitorData->size()).get(), false);
msarettd9015a42016-08-22 12:29:31 -0700228
229 const float gammas[] = { 1.1f, 1.2f, 1.7f, };
230 SkMatrix44 toXYZ(SkMatrix44::kIdentity_Constructor);
231 test_serialize(r, SkColorSpace_Base::NewRGB(gammas, toXYZ).get(), false);
msarett111a42d2016-06-22 08:18:54 -0700232}
233
msarettabbd6d52016-08-01 09:43:08 -0700234DEF_TEST(ColorSpace_Equals, r) {
235 sk_sp<SkColorSpace> srgb = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
236 sk_sp<SkColorSpace> adobe = SkColorSpace::NewNamed(SkColorSpace::kAdobeRGB_Named);
237 sk_sp<SkData> data = SkData::MakeFromFileName(
238 GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str());
239 sk_sp<SkColorSpace> z30 = SkColorSpace::NewICC(data->data(), data->size());
240 data = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str());
241 sk_sp<SkColorSpace> z32 = SkColorSpace::NewICC(data->data(), data->size());
242 data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str());
243 sk_sp<SkColorSpace> upperLeft = SkColorSpace::NewICC(data->data(), data->size());
244 data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str());
245 sk_sp<SkColorSpace> upperRight = SkColorSpace::NewICC(data->data(), data->size());
msarettd9015a42016-08-22 12:29:31 -0700246 const float gammas1[] = { 1.1f, 1.2f, 1.3f, };
247 const float gammas2[] = { 1.1f, 1.2f, 1.7f, };
248 SkMatrix44 toXYZ(SkMatrix44::kIdentity_Constructor);
249 sk_sp<SkColorSpace> rgb1 = SkColorSpace_Base::NewRGB(gammas1, toXYZ);
250 sk_sp<SkColorSpace> rgb2 = SkColorSpace_Base::NewRGB(gammas2, toXYZ);
251 sk_sp<SkColorSpace> rgb3 = SkColorSpace_Base::NewRGB(gammas1, toXYZ);
msarettabbd6d52016-08-01 09:43:08 -0700252
253 REPORTER_ASSERT(r, SkColorSpace::Equals(nullptr, nullptr));
254 REPORTER_ASSERT(r, SkColorSpace::Equals(srgb.get(), srgb.get()));
255 REPORTER_ASSERT(r, SkColorSpace::Equals(adobe.get(), adobe.get()));
256 REPORTER_ASSERT(r, SkColorSpace::Equals(z30.get(), z30.get()));
257 REPORTER_ASSERT(r, SkColorSpace::Equals(z32.get(), z32.get()));
258 REPORTER_ASSERT(r, SkColorSpace::Equals(upperLeft.get(), upperLeft.get()));
259 REPORTER_ASSERT(r, SkColorSpace::Equals(upperRight.get(), upperRight.get()));
msarettd9015a42016-08-22 12:29:31 -0700260 REPORTER_ASSERT(r, SkColorSpace::Equals(rgb1.get(), rgb1.get()));
261 REPORTER_ASSERT(r, SkColorSpace::Equals(rgb1.get(), rgb3.get()));
msarettabbd6d52016-08-01 09:43:08 -0700262
263 REPORTER_ASSERT(r, !SkColorSpace::Equals(nullptr, srgb.get()));
264 REPORTER_ASSERT(r, !SkColorSpace::Equals(srgb.get(), nullptr));
265 REPORTER_ASSERT(r, !SkColorSpace::Equals(adobe.get(), srgb.get()));
266 REPORTER_ASSERT(r, !SkColorSpace::Equals(z30.get(), srgb.get()));
267 REPORTER_ASSERT(r, !SkColorSpace::Equals(z32.get(), z30.get()));
268 REPORTER_ASSERT(r, !SkColorSpace::Equals(upperLeft.get(), srgb.get()));
269 REPORTER_ASSERT(r, !SkColorSpace::Equals(upperLeft.get(), upperRight.get()));
270 REPORTER_ASSERT(r, !SkColorSpace::Equals(z30.get(), upperRight.get()));
271 REPORTER_ASSERT(r, !SkColorSpace::Equals(upperRight.get(), adobe.get()));
msarettd9015a42016-08-22 12:29:31 -0700272 REPORTER_ASSERT(r, !SkColorSpace::Equals(rgb1.get(), rgb2.get()));
msarettabbd6d52016-08-01 09:43:08 -0700273}