blob: d9a008d71cda6d4682bf240a268fe197945b2bcb [file] [log] [blame]
ccameron87b65e9b62016-12-27 10:19:071// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
ccameron290a91a2017-05-13 19:22:335#include "ui/gfx/icc_profile.h"
ccameron87b65e9b62016-12-27 10:19:076#include "base/logging.h"
7#include "testing/gtest/include/gtest/gtest.h"
8#include "ui/gfx/color_space.h"
ccameron290a91a2017-05-13 19:22:339#include "ui/gfx/skia_color_space_util.h"
ccameron83e8e9a2017-01-04 01:31:2010#include "ui/gfx/test/icc_profiles.h"
ccameron87b65e9b62016-12-27 10:19:0711
12namespace gfx {
13
ccameron87b65e9b62016-12-27 10:19:0714TEST(ICCProfile, Conversions) {
15 ICCProfile icc_profile = ICCProfileForTestingColorSpin();
16 ColorSpace color_space_from_icc_profile = icc_profile.GetColorSpace();
17
ccameron3b6fe6d2017-02-17 06:21:5818 ICCProfile icc_profile_from_color_space;
19 bool result =
20 color_space_from_icc_profile.GetICCProfile(&icc_profile_from_color_space);
21 EXPECT_TRUE(result);
ccameron87b65e9b62016-12-27 10:19:0722 EXPECT_TRUE(icc_profile == icc_profile_from_color_space);
ccameron87b65e9b62016-12-27 10:19:0723}
24
25TEST(ICCProfile, SRGB) {
ccameronfa21c642017-05-10 19:38:3626 ICCProfile icc_profile = ICCProfileForTestingSRGB();
ccameron87b65e9b62016-12-27 10:19:0727 ColorSpace color_space = ColorSpace::CreateSRGB();
msarett29c85572017-02-10 22:16:5528 sk_sp<SkColorSpace> sk_color_space = SkColorSpace::MakeSRGB();
ccameron87b65e9b62016-12-27 10:19:0729
ccameronfa21c642017-05-10 19:38:3630 // The ICC profile parser should note that this is SRGB.
Christopher Camerona4adb41b2017-07-13 06:44:2531 EXPECT_EQ(icc_profile.GetColorSpace(), ColorSpace::CreateSRGB());
ccameronfa21c642017-05-10 19:38:3632 EXPECT_EQ(icc_profile.GetColorSpace().ToSkColorSpace().get(),
33 sk_color_space.get());
34 // The parametric generating code should recognize that this is SRGB.
ccamerone35e2322017-11-02 23:33:4635 EXPECT_EQ(icc_profile.GetParametricColorSpace(), ColorSpace::CreateSRGB());
36 EXPECT_EQ(icc_profile.GetParametricColorSpace().ToSkColorSpace().get(),
ccameronfa21c642017-05-10 19:38:3637 sk_color_space.get());
38 // The generated color space should recognize that this is SRGB.
ccameron628896ee2017-02-03 09:29:1239 EXPECT_EQ(color_space.ToSkColorSpace().get(), sk_color_space.get());
ccameron87b65e9b62016-12-27 10:19:0740}
41
ccameron549738b2017-01-28 17:39:3242TEST(ICCProfile, Equality) {
43 ICCProfile spin_profile = ICCProfileForTestingColorSpin();
44 ICCProfile adobe_profile = ICCProfileForTestingAdobeRGB();
45 EXPECT_TRUE(spin_profile == spin_profile);
46 EXPECT_FALSE(spin_profile != spin_profile);
47 EXPECT_FALSE(spin_profile == adobe_profile);
48 EXPECT_TRUE(spin_profile != adobe_profile);
49
50 gfx::ColorSpace spin_space = spin_profile.GetColorSpace();
51 gfx::ColorSpace adobe_space = adobe_profile.GetColorSpace();
52 EXPECT_TRUE(spin_space == spin_space);
53 EXPECT_FALSE(spin_space != spin_space);
54 EXPECT_FALSE(spin_space == adobe_space);
55 EXPECT_TRUE(spin_space != adobe_space);
56
ccameron3b6fe6d2017-02-17 06:21:5857 ICCProfile temp;
58 bool get_icc_result = false;
59
60 get_icc_result = spin_space.GetICCProfile(&temp);
61 EXPECT_TRUE(get_icc_result);
62 EXPECT_TRUE(spin_profile == temp);
63 EXPECT_FALSE(spin_profile != temp);
64
65 get_icc_result = adobe_space.GetICCProfile(&temp);
66 EXPECT_TRUE(get_icc_result);
67 EXPECT_FALSE(spin_profile == temp);
68 EXPECT_TRUE(spin_profile != temp);
ccameron628896ee2017-02-03 09:29:1269
70 EXPECT_TRUE(!!spin_space.ToSkColorSpace());
71 EXPECT_TRUE(!!adobe_space.ToSkColorSpace());
72 EXPECT_FALSE(SkColorSpace::Equals(
73 spin_space.ToSkColorSpace().get(),
74 adobe_space.ToSkColorSpace().get()));
ccameron549738b2017-01-28 17:39:3275}
76
ccameron24c87c32017-03-14 21:50:4277TEST(ICCProfile, ParametricVersusExact) {
78 // This ICC profile has three transfer functions that differ enough that the
79 // parametric color space is considered inaccurate.
ccameron95f83c512017-03-31 20:16:1280 ICCProfile multi_tr_fn = ICCProfileForTestingNoAnalyticTrFn();
ccamerone35e2322017-11-02 23:33:4681 EXPECT_NE(multi_tr_fn.GetColorSpace(), multi_tr_fn.GetParametricColorSpace());
ccameron24c87c32017-03-14 21:50:4282
ccameron95f83c512017-03-31 20:16:1283 ICCProfile multi_tr_fn_color_space;
ccameron24c87c32017-03-14 21:50:4284 EXPECT_TRUE(
ccameron95f83c512017-03-31 20:16:1285 multi_tr_fn.GetColorSpace().GetICCProfile(&multi_tr_fn_color_space));
86 EXPECT_EQ(multi_tr_fn_color_space, multi_tr_fn);
ccameron24c87c32017-03-14 21:50:4287
ccameron95f83c512017-03-31 20:16:1288 ICCProfile multi_tr_fn_parametric_color_space;
ccamerone35e2322017-11-02 23:33:4689 EXPECT_TRUE(multi_tr_fn.GetParametricColorSpace().GetICCProfile(
90 &multi_tr_fn_parametric_color_space));
ccameron95f83c512017-03-31 20:16:1291 EXPECT_NE(multi_tr_fn_parametric_color_space, multi_tr_fn);
92
93 // This ICC profile has a transfer function with T(1) that is greater than 1
94 // in the approximation, but is still close enough to be considered accurate.
95 ICCProfile overshoot = ICCProfileForTestingOvershoot();
ccamerone35e2322017-11-02 23:33:4696 EXPECT_EQ(overshoot.GetColorSpace(), overshoot.GetParametricColorSpace());
ccameron95f83c512017-03-31 20:16:1297
98 ICCProfile overshoot_color_space;
99 EXPECT_TRUE(overshoot.GetColorSpace().GetICCProfile(&overshoot_color_space));
100 EXPECT_EQ(overshoot_color_space, overshoot);
101
102 ICCProfile overshoot_parametric_color_space;
ccamerone35e2322017-11-02 23:33:46103 EXPECT_TRUE(overshoot.GetParametricColorSpace().GetICCProfile(
104 &overshoot_parametric_color_space));
ccameron95f83c512017-03-31 20:16:12105 EXPECT_EQ(overshoot_parametric_color_space, overshoot);
ccameron24c87c32017-03-14 21:50:42106
107 // This ICC profile is precisely represented by the parametric color space.
108 ICCProfile accurate = ICCProfileForTestingAdobeRGB();
ccamerone35e2322017-11-02 23:33:46109 EXPECT_EQ(accurate.GetColorSpace(), accurate.GetParametricColorSpace());
ccameron24c87c32017-03-14 21:50:42110
111 ICCProfile accurate_color_space;
112 EXPECT_TRUE(accurate.GetColorSpace().GetICCProfile(&accurate_color_space));
113 EXPECT_EQ(accurate_color_space, accurate);
114
115 ICCProfile accurate_parametric_color_space;
ccamerone35e2322017-11-02 23:33:46116 EXPECT_TRUE(accurate.GetParametricColorSpace().GetICCProfile(
117 &accurate_parametric_color_space));
ccameron24c87c32017-03-14 21:50:42118 EXPECT_EQ(accurate_parametric_color_space, accurate);
119
ccameronb3206e22017-03-25 02:09:12120 // This ICC profile has only an A2B representation. We cannot create an
121 // SkColorSpaceXform to A2B only ICC profiles, so this should be marked
122 // as invalid.
ccameron24c87c32017-03-14 21:50:42123 ICCProfile a2b = ICCProfileForTestingA2BOnly();
ccameronb3206e22017-03-25 02:09:12124 EXPECT_FALSE(a2b.GetColorSpace().IsValid());
Christopher Cameron64a55ad2017-09-21 05:31:22125
126 // Even though it is invalid, it should not be equal to the empty constructor.
127 EXPECT_NE(a2b, gfx::ICCProfile());
ccameron24c87c32017-03-14 21:50:42128}
129
130TEST(ICCProfile, GarbageData) {
131 std::vector<char> bad_data(10 * 1024);
132 const char* bad_data_string = "deadbeef";
133 for (size_t i = 0; i < bad_data.size(); ++i)
134 bad_data[i] = bad_data_string[i % 8];
135 ICCProfile garbage_profile =
136 ICCProfile::FromData(bad_data.data(), bad_data.size());
137 EXPECT_FALSE(garbage_profile.IsValid());
138 EXPECT_FALSE(garbage_profile.GetColorSpace().IsValid());
ccamerone35e2322017-11-02 23:33:46139 EXPECT_FALSE(garbage_profile.GetParametricColorSpace().IsValid());
ccameron24c87c32017-03-14 21:50:42140
141 ICCProfile default_ctor_profile;
142 EXPECT_FALSE(default_ctor_profile.IsValid());
143 EXPECT_FALSE(default_ctor_profile.GetColorSpace().IsValid());
ccamerone35e2322017-11-02 23:33:46144 EXPECT_FALSE(default_ctor_profile.GetParametricColorSpace().IsValid());
ccameron24c87c32017-03-14 21:50:42145}
146
ccameron290a91a2017-05-13 19:22:33147TEST(ICCProfile, GenericRGB) {
148 ColorSpace icc_profile = ICCProfileForTestingGenericRGB().GetColorSpace();
149 ColorSpace color_space(ColorSpace::PrimaryID::APPLE_GENERIC_RGB,
150 ColorSpace::TransferID::GAMMA18);
151
152 SkMatrix44 icc_profile_matrix;
153 SkMatrix44 color_space_matrix;
154
155 icc_profile.GetPrimaryMatrix(&icc_profile_matrix);
156 color_space.GetPrimaryMatrix(&color_space_matrix);
157
158 SkMatrix44 eye;
159 icc_profile_matrix.invert(&eye);
160 eye.postConcat(color_space_matrix);
161 EXPECT_TRUE(SkMatrixIsApproximatelyIdentity(eye));
162}
163
Christopher Cameron64a55ad2017-09-21 05:31:22164// This tests the ICCProfile MRU cache. This cache is sloppy and should be
165// rewritten, now that some of the original design constraints have been lifted.
166// This test exists only to ensure that we are aware of behavior changes, not to
167// enforce that behavior does not change.
168// https://crbug.com/766736
169TEST(ICCProfile, ExhaustCache) {
170 // Get an ICCProfile that can't be parametrically approximated.
171 ICCProfile original = ICCProfileForTestingNoAnalyticTrFn();
172 ColorSpace original_color_space_0 = original.GetColorSpace();
173
174 // Recover the ICCProfile from its GetColorSpace. Recovery should succeed, and
175 // the ICCProfiles should be equal.
176 ICCProfile recovered_0;
177 EXPECT_TRUE(original_color_space_0.GetICCProfile(&recovered_0));
178 EXPECT_EQ(original, recovered_0);
179
180 // The GetColorSpace of the recovered version should match the original.
181 ColorSpace recovered_0_color_space = recovered_0.GetColorSpace();
182 EXPECT_EQ(recovered_0_color_space, original_color_space_0);
183
184 // Create an identical ICCProfile to the original. It should equal the
185 // original, and its GetColorSpace should equal the original.
186 ICCProfile identical_0 = ICCProfileForTestingNoAnalyticTrFn();
187 EXPECT_EQ(original, identical_0);
188 ColorSpace identical_color_space_0 = identical_0.GetColorSpace();
189 EXPECT_EQ(identical_color_space_0, original_color_space_0);
190
191 // Create 128 distinct ICC profiles. This will destroy the cached
192 // ICCProfile<->ColorSpace mapping.
193 for (size_t i = 0; i < 128; ++i) {
194 SkMatrix44 toXYZD50;
195 ColorSpace::CreateSRGB().GetPrimaryMatrix(&toXYZD50);
196 SkColorSpaceTransferFn fn;
197 fn.fA = 1;
198 fn.fB = 0;
199 fn.fC = 1;
200 fn.fD = 0;
201 fn.fE = 0;
202 fn.fF = 0;
203 fn.fG = 1.5f + i / 128.f;
204 ColorSpace color_space = ColorSpace::CreateCustom(toXYZD50, fn);
205 ICCProfile icc_profile;
206 color_space.GetICCProfile(&icc_profile);
207 }
208
209 // Recover the original ICCProfile from its GetColorSpace. Recovery should
210 // fail, because it has been pushed out of the cache.
211 ICCProfile recovered_1;
212 EXPECT_FALSE(original_color_space_0.GetICCProfile(&recovered_1));
213 EXPECT_NE(original, recovered_1);
214
215 // Create an identical ICCProfile to the original. It should equal the
216 // original, because the comparison is based on data.
217 ICCProfile identical_1 = ICCProfileForTestingNoAnalyticTrFn();
218 EXPECT_EQ(original, identical_1);
219
220 // The identical ICCProfile's GetColorSpace will not match, because the
221 // original points to the now-uncached version.
222 ColorSpace identical_1_color_space = identical_1.GetColorSpace();
223 EXPECT_NE(identical_1_color_space, original_color_space_0);
224
225 // The original ICCProfile is now orphaned because there exists a new entry
226 // with the same data.
227 ColorSpace original_color_space_2 = original.GetColorSpace();
228 ICCProfile recovered_2;
229 EXPECT_FALSE(original_color_space_2.GetICCProfile(&recovered_2));
230 EXPECT_NE(original, recovered_2);
231
232 // Blow away the cache one more time.
233 for (size_t i = 0; i < 128; ++i) {
234 SkMatrix44 toXYZD50;
235 ColorSpace::CreateSRGB().GetPrimaryMatrix(&toXYZD50);
236 SkColorSpaceTransferFn fn;
237 fn.fA = 1;
238 fn.fB = 0;
239 fn.fC = 1;
240 fn.fD = 0;
241 fn.fE = 0;
242 fn.fF = 0;
243 fn.fG = 1.5f + i / 128.f;
244 ColorSpace color_space = ColorSpace::CreateCustom(toXYZD50, fn);
245 ICCProfile icc_profile;
246 color_space.GetICCProfile(&icc_profile);
247 }
248
249 // Now the original ICCProfile can re-insert itself into the cache, with its
250 // original id.
251 ColorSpace original_color_space_3 = original.GetColorSpace();
252 ICCProfile recovered_3;
253 EXPECT_TRUE(original_color_space_3.GetICCProfile(&recovered_3));
254 EXPECT_EQ(original, recovered_3);
255}
256
ccameron87b65e9b62016-12-27 10:19:07257} // namespace gfx