Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(31)

Side by Side Diff: src/core/SkColorSpace.cpp

Issue 2001203003: Write ICC profiles from SkColorSpace object (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Rebase Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | src/core/SkColorSpace_Base.h » ('j') | tests/ColorSpaceTest.cpp » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2016 Google Inc. 2 * Copyright 2016 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkColorSpace.h" 8 #include "SkColorSpace.h"
9 #include "SkColorSpace_Base.h" 9 #include "SkColorSpace_Base.h"
10 #include "SkEndian.h"
10 #include "SkOnce.h" 11 #include "SkOnce.h"
11 12
12 static bool color_space_almost_equal(float a, float b) { 13 static bool color_space_almost_equal(float a, float b) {
13 return SkTAbs(a - b) < 0.01f; 14 return SkTAbs(a - b) < 0.01f;
14 } 15 }
15 16
16 //////////////////////////////////////////////////////////////////////////////// ////////////////// 17 //////////////////////////////////////////////////////////////////////////////// //////////////////
17 18
18 SkColorSpace::SkColorSpace(GammaNamed gammaNamed, const SkMatrix44& toXYZD50, Na med named) 19 SkColorSpace::SkColorSpace(GammaNamed gammaNamed, const SkMatrix44& toXYZD50, Na med named)
19 : fGammaNamed(kNonStandard_GammaNamed) 20 : fGammaNamed(kNonStandard_GammaNamed)
20 , fToXYZD50(toXYZD50) 21 , fToXYZD50(toXYZD50)
21 , fNamed(named) 22 , fNamed(named)
22 {} 23 {}
23 24
24 SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkGammas> gammas, const SkMatrix44& t oXYZD50, 25 SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkGammas> gammas, const SkMatrix44& t oXYZD50,
25 Named named) 26 Named named, sk_sp<SkData> profileData)
26 : INHERITED(kNonStandard_GammaNamed, toXYZD50, named) 27 : INHERITED(kNonStandard_GammaNamed, toXYZD50, named)
27 , fGammas(std::move(gammas)) 28 , fGammas(std::move(gammas))
29 , fProfileData(std::move(profileData))
28 {} 30 {}
29 31
30 SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkGammas> gammas, GammaNamed gammaNam ed, 32 SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkGammas> gammas, GammaNamed gammaNam ed,
31 const SkMatrix44& toXYZD50, Named named) 33 const SkMatrix44& toXYZD50, Named named,
34 sk_sp<SkData> profileData)
32 : INHERITED(gammaNamed, toXYZD50, named) 35 : INHERITED(gammaNamed, toXYZD50, named)
33 , fGammas(std::move(gammas)) 36 , fGammas(std::move(gammas))
37 , fProfileData(std::move(profileData))
34 {} 38 {}
35 39
36 SkColorSpace_Base::SkColorSpace_Base(SkColorLookUpTable* colorLUT, sk_sp<SkGamma s> gammas, 40 SkColorSpace_Base::SkColorSpace_Base(SkColorLookUpTable* colorLUT, sk_sp<SkGamma s> gammas,
37 const SkMatrix44& toXYZD50) 41 const SkMatrix44& toXYZD50, sk_sp<SkData> p rofileData)
38 : INHERITED(kNonStandard_GammaNamed, toXYZD50, kUnknown_Named) 42 : INHERITED(kNonStandard_GammaNamed, toXYZD50, kUnknown_Named)
39 , fColorLUT(colorLUT) 43 , fColorLUT(colorLUT)
40 , fGammas(std::move(gammas)) 44 , fGammas(std::move(gammas))
45 , fProfileData(std::move(profileData))
41 {} 46 {}
42 47
43 const float gSRGB_toXYZD50[] { 48 static constexpr float gSRGB_toXYZD50[] {
44 0.4358f, 0.2224f, 0.0139f, // * R 49 0.4358f, 0.2224f, 0.0139f, // * R
45 0.3853f, 0.7170f, 0.0971f, // * G 50 0.3853f, 0.7170f, 0.0971f, // * G
46 0.1430f, 0.0606f, 0.7139f, // * B 51 0.1430f, 0.0606f, 0.7139f, // * B
47 }; 52 };
48 53
49 const float gAdobeRGB_toXYZD50[] { 54 static constexpr float gAdobeRGB_toXYZD50[] {
50 0.6098f, 0.3111f, 0.0195f, // * R 55 0.6098f, 0.3111f, 0.0195f, // * R
51 0.2052f, 0.6257f, 0.0609f, // * G 56 0.2052f, 0.6257f, 0.0609f, // * G
52 0.1492f, 0.0632f, 0.7448f, // * B 57 0.1492f, 0.0632f, 0.7448f, // * B
53 }; 58 };
54 59
55 /** 60 /**
56 * Checks if our toXYZ matrix is a close match to a known color gamut. 61 * Checks if our toXYZ matrix is a close match to a known color gamut.
57 * 62 *
58 * @param toXYZD50 transformation matrix deduced from profile data 63 * @param toXYZD50 transformation matrix deduced from profile data
59 * @param standard 3x3 canonical transformation matrix 64 * @param standard 3x3 canonical transformation matrix
(...skipping 16 matching lines...) Expand all
76 color_space_almost_equal(toXYZD50.getFloat(3, 2), 0.0f) && 81 color_space_almost_equal(toXYZD50.getFloat(3, 2), 0.0f) &&
77 color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f); 82 color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f);
78 } 83 }
79 84
80 static SkOnce g2Dot2CurveGammasOnce; 85 static SkOnce g2Dot2CurveGammasOnce;
81 static SkGammas* g2Dot2CurveGammas; 86 static SkGammas* g2Dot2CurveGammas;
82 static SkOnce gLinearGammasOnce; 87 static SkOnce gLinearGammasOnce;
83 static SkGammas* gLinearGammas; 88 static SkGammas* gLinearGammas;
84 89
85 sk_sp<SkColorSpace> SkColorSpace::NewRGB(const float gammaVals[3], const SkMatri x44& toXYZD50) { 90 sk_sp<SkColorSpace> SkColorSpace::NewRGB(const float gammaVals[3], const SkMatri x44& toXYZD50) {
91 return SkColorSpace_Base::NewRGB(gammaVals, toXYZD50, nullptr);
92 }
93
94 sk_sp<SkColorSpace> SkColorSpace_Base::NewRGB(const float gammaVals[3], const Sk Matrix44& toXYZD50,
95 sk_sp<SkData> profileData) {
86 sk_sp<SkGammas> gammas = nullptr; 96 sk_sp<SkGammas> gammas = nullptr;
87 GammaNamed gammaNamed = kNonStandard_GammaNamed; 97 GammaNamed gammaNamed = kNonStandard_GammaNamed;
88 98
89 // Check if we really have sRGB or Adobe RGB 99 // Check if we really have sRGB or Adobe RGB
90 if (color_space_almost_equal(2.2f, gammaVals[0]) && 100 if (color_space_almost_equal(2.2f, gammaVals[0]) &&
91 color_space_almost_equal(2.2f, gammaVals[1]) && 101 color_space_almost_equal(2.2f, gammaVals[1]) &&
92 color_space_almost_equal(2.2f, gammaVals[2])) 102 color_space_almost_equal(2.2f, gammaVals[2]))
93 { 103 {
94 g2Dot2CurveGammasOnce([] { 104 g2Dot2CurveGammasOnce([] {
95 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); 105 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f);
(...skipping 13 matching lines...) Expand all
109 gLinearGammasOnce([] { 119 gLinearGammasOnce([] {
110 gLinearGammas = new SkGammas(1.0f, 1.0f, 1.0f); 120 gLinearGammas = new SkGammas(1.0f, 1.0f, 1.0f);
111 }); 121 });
112 gammas = sk_ref_sp(gLinearGammas); 122 gammas = sk_ref_sp(gLinearGammas);
113 gammaNamed = kLinear_GammaNamed; 123 gammaNamed = kLinear_GammaNamed;
114 } 124 }
115 125
116 if (!gammas) { 126 if (!gammas) {
117 gammas = sk_sp<SkGammas>(new SkGammas(gammaVals[0], gammaVals[1], gammaV als[2])); 127 gammas = sk_sp<SkGammas>(new SkGammas(gammaVals[0], gammaVals[1], gammaV als[2]));
118 } 128 }
119 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, gammaNamed, toXYZD5 0, kUnknown_Named)); 129 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, gammaNamed, toXYZD5 0, kUnknown_Named,
130 std::move(profileData)));
120 } 131 }
121 132
122 sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) { 133 sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) {
123 static SkOnce sRGBOnce; 134 static SkOnce sRGBOnce;
124 static SkColorSpace* sRGB; 135 static SkColorSpace* sRGB;
125 static SkOnce adobeRGBOnce; 136 static SkOnce adobeRGBOnce;
126 static SkColorSpace* adobeRGB; 137 static SkColorSpace* adobeRGB;
127 138
128 switch (named) { 139 switch (named) {
129 case kSRGB_Named: { 140 case kSRGB_Named: {
130 g2Dot2CurveGammasOnce([] { 141 g2Dot2CurveGammasOnce([] {
131 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); 142 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f);
132 }); 143 });
133 144
134 sRGBOnce([] { 145 sRGBOnce([] {
135 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor); 146 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor);
136 srgbToxyzD50.set3x3ColMajorf(gSRGB_toXYZD50); 147 srgbToxyzD50.set3x3ColMajorf(gSRGB_toXYZD50);
137 sRGB = new SkColorSpace_Base(sk_ref_sp(g2Dot2CurveGammas), k2Dot 2Curve_GammaNamed, 148 sRGB = new SkColorSpace_Base(sk_ref_sp(g2Dot2CurveGammas), k2Dot 2Curve_GammaNamed,
138 srgbToxyzD50, kSRGB_Named); 149 srgbToxyzD50, kSRGB_Named, nullptr) ;
139 }); 150 });
140 return sk_ref_sp(sRGB); 151 return sk_ref_sp(sRGB);
141 } 152 }
142 case kAdobeRGB_Named: { 153 case kAdobeRGB_Named: {
143 g2Dot2CurveGammasOnce([] { 154 g2Dot2CurveGammasOnce([] {
144 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); 155 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f);
145 }); 156 });
146 157
147 adobeRGBOnce([] { 158 adobeRGBOnce([] {
148 SkMatrix44 adobergbToxyzD50(SkMatrix44::kUninitialized_Construct or); 159 SkMatrix44 adobergbToxyzD50(SkMatrix44::kUninitialized_Construct or);
149 adobergbToxyzD50.set3x3ColMajorf(gAdobeRGB_toXYZD50); 160 adobergbToxyzD50.set3x3ColMajorf(gAdobeRGB_toXYZD50);
150 adobeRGB = new SkColorSpace_Base(sk_ref_sp(g2Dot2CurveGammas), 161 adobeRGB = new SkColorSpace_Base(sk_ref_sp(g2Dot2CurveGammas),
151 k2Dot2Curve_GammaNamed, adoberg bToxyzD50, 162 k2Dot2Curve_GammaNamed, adoberg bToxyzD50,
152 kAdobeRGB_Named); 163 kAdobeRGB_Named, nullptr);
153 }); 164 });
154 return sk_ref_sp(adobeRGB); 165 return sk_ref_sp(adobeRGB);
155 } 166 }
156 default: 167 default:
157 break; 168 break;
158 } 169 }
159 return nullptr; 170 return nullptr;
160 } 171 }
161 172
162 //////////////////////////////////////////////////////////////////////////////// /////////////////// 173 //////////////////////////////////////////////////////////////////////////////// ///////////////////
(...skipping 25 matching lines...) Expand all
188 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; 199 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
189 } 200 }
190 201
191 static int32_t read_big_endian_int(const uint8_t* ptr) { 202 static int32_t read_big_endian_int(const uint8_t* ptr) {
192 return (int32_t) read_big_endian_uint(ptr); 203 return (int32_t) read_big_endian_uint(ptr);
193 } 204 }
194 205
195 // This is equal to the header size according to the ICC specification (128) 206 // This is equal to the header size according to the ICC specification (128)
196 // plus the size of the tag count (4). We include the tag count since we 207 // plus the size of the tag count (4). We include the tag count since we
197 // always require it to be present anyway. 208 // always require it to be present anyway.
198 static const size_t kICCHeaderSize = 132; 209 static constexpr size_t kICCHeaderSize = 132;
199 210
200 // Contains a signature (4), offset (4), and size (4). 211 // Contains a signature (4), offset (4), and size (4).
201 static const size_t kICCTagTableEntrySize = 12; 212 static constexpr size_t kICCTagTableEntrySize = 12;
202 213
203 static const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' '); 214 static constexpr uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' ' );
215 static constexpr uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r' );
216 static constexpr uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r' );
217 static constexpr uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r' );
218 static constexpr uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' ' );
219 static constexpr uint32_t kACSP_Signature = SkSetFourByteTag('a', 'c', 's', 'p' );
204 220
205 struct ICCProfileHeader { 221 struct ICCProfileHeader {
206 uint32_t fSize; 222 uint32_t fSize;
207 223
208 // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.). 224 // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.).
209 // We're always going to use this one. 225 // We're always going to use this one.
210 uint32_t fCMMType_ignored; 226 uint32_t fCMMType_ignored;
211 227
212 uint32_t fVersion; 228 uint32_t fVersion;
213 uint32_t fProfileClass; 229 uint32_t fProfileClass;
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
259 275
260 bool valid() const { 276 bool valid() const {
261 return_if_false(fSize >= kICCHeaderSize, "Size is too small"); 277 return_if_false(fSize >= kICCHeaderSize, "Size is too small");
262 278
263 uint8_t majorVersion = fVersion >> 24; 279 uint8_t majorVersion = fVersion >> 24;
264 return_if_false(majorVersion <= 4, "Unsupported version"); 280 return_if_false(majorVersion <= 4, "Unsupported version");
265 281
266 // These are the three basic classes of profiles that we might expect to see embedded 282 // These are the three basic classes of profiles that we might expect to see embedded
267 // in images. Four additional classes exist, but they generally are use d as a convenient 283 // in images. Four additional classes exist, but they generally are use d as a convenient
268 // way for CMMs to store calculated transforms. 284 // way for CMMs to store calculated transforms.
269 const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r');
270 const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r');
271 const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r');
272 return_if_false(fProfileClass == kDisplay_Profile || 285 return_if_false(fProfileClass == kDisplay_Profile ||
273 fProfileClass == kInput_Profile || 286 fProfileClass == kInput_Profile ||
274 fProfileClass == kOutput_Profile, 287 fProfileClass == kOutput_Profile,
275 "Unsupported profile"); 288 "Unsupported profile");
276 289
277 // TODO (msarett): 290 // TODO (msarett):
278 // All the profiles we've tested so far use RGB as the input color space . 291 // All the profiles we've tested so far use RGB as the input color space .
279 return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space"); 292 return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space");
280 293
281 // TODO (msarett): 294 // TODO (msarett):
282 // All the profiles we've tested so far use XYZ as the profile connectio n space. 295 // All the profiles we've tested so far use XYZ as the profile connectio n space.
283 const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' ');
284 return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space"); 296 return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space");
285 297
286 return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "Bad signature"); 298 return_if_false(fSignature == kACSP_Signature, "Bad signature");
287 299
288 // TODO (msarett): 300 // TODO (msarett):
289 // Should we treat different rendering intents differently? 301 // Should we treat different rendering intents differently?
290 // Valid rendering intents include kPerceptual (0), kRelative (1), 302 // Valid rendering intents include kPerceptual (0), kRelative (1),
291 // kSaturation (2), and kAbsolute (3). 303 // kSaturation (2), and kAbsolute (3).
292 return_if_false(fRenderingIntent <= 3, "Bad rendering intent"); 304 return_if_false(fRenderingIntent <= 3, "Bad rendering intent");
293 305
294 return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0 ]), 0.96420f) && 306 return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0 ]), 0.96420f) &&
295 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1 ]), 1.00000f) && 307 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1 ]), 1.00000f) &&
296 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2 ]), 0.82491f), 308 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2 ]), 0.82491f),
(...skipping 29 matching lines...) Expand all
326 static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature ) { 338 static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature ) {
327 for (int i = 0; i < count; ++i) { 339 for (int i = 0; i < count; ++i) {
328 if (tags[i].fSignature == signature) { 340 if (tags[i].fSignature == signature) {
329 return &tags[i]; 341 return &tags[i];
330 } 342 }
331 } 343 }
332 return nullptr; 344 return nullptr;
333 } 345 }
334 }; 346 };
335 347
336 static const uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z'); 348 static constexpr uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z');
337 static const uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z'); 349 static constexpr uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z');
338 static const uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z'); 350 static constexpr uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z');
339 static const uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C'); 351 static constexpr uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C');
340 static const uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C'); 352 static constexpr uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C');
341 static const uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C'); 353 static constexpr uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C');
342 static const uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0'); 354 static constexpr uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0');
343 355
344 bool load_xyz(float dst[3], const uint8_t* src, size_t len) { 356 bool load_xyz(float dst[3], const uint8_t* src, size_t len) {
345 if (len < 20) { 357 if (len < 20) {
346 SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len); 358 SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len);
347 return false; 359 return false;
348 } 360 }
349 361
350 dst[0] = SkFixedToFloat(read_big_endian_int(src + 8)); 362 dst[0] = SkFixedToFloat(read_big_endian_int(src + 8));
351 dst[1] = SkFixedToFloat(read_big_endian_int(src + 12)); 363 dst[1] = SkFixedToFloat(read_big_endian_int(src + 12));
352 dst[2] = SkFixedToFloat(read_big_endian_int(src + 16)); 364 dst[2] = SkFixedToFloat(read_big_endian_int(src + 16));
353 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]); 365 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]);
354 return true; 366 return true;
355 } 367 }
356 368
357 static const uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v'); 369 static constexpr uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', ' v');
358 static const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a'); 370 static constexpr uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', ' a');
359 371
360 bool load_gammas(SkGammaCurve* gammas, uint32_t numGammas, const uint8_t* src, s ize_t len) { 372 bool load_gammas(SkGammaCurve* gammas, uint32_t numGammas, const uint8_t* src, s ize_t len) {
361 for (uint32_t i = 0; i < numGammas; i++) { 373 for (uint32_t i = 0; i < numGammas; i++) {
362 if (len < 12) { 374 if (len < 12) {
363 // FIXME (msarett): 375 // FIXME (msarett):
364 // We could potentially return false here after correctly parsing *s ome* of the 376 // We could potentially return false here after correctly parsing *s ome* of the
365 // gammas correctly. Should we somehow try to indicate a partial su ccess? 377 // gammas correctly. Should we somehow try to indicate a partial su ccess?
366 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); 378 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
367 return false; 379 return false;
368 } 380 }
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
556 } 568 }
557 569
558 src += tagBytes; 570 src += tagBytes;
559 len -= tagBytes; 571 len -= tagBytes;
560 } 572 }
561 } 573 }
562 574
563 return true; 575 return true;
564 } 576 }
565 577
566 static const uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' '); 578 static constexpr uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' ');
567 579
568 bool load_color_lut(SkColorLookUpTable* colorLUT, uint32_t inputChannels, uint32 _t outputChannels, 580 bool load_color_lut(SkColorLookUpTable* colorLUT, uint32_t inputChannels, uint32 _t outputChannels,
569 const uint8_t* src, size_t len) { 581 const uint8_t* src, size_t len) {
570 if (len < 20) { 582 if (len < 20) {
571 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); 583 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len);
572 return false; 584 return false;
573 } 585 }
574 586
575 SkASSERT(inputChannels <= SkColorLookUpTable::kMaxChannels && 3 == outputCha nnels); 587 SkASSERT(inputChannels <= SkColorLookUpTable::kMaxChannels && 3 == outputCha nnels);
576 colorLUT->fInputChannels = inputChannels; 588 colorLUT->fInputChannels = inputChannels;
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
698 uint32_t offsetToMatrix = read_big_endian_int(src + 16); 710 uint32_t offsetToMatrix = read_big_endian_int(src + 16);
699 if (0 != offsetToMatrix && offsetToMatrix < len) { 711 if (0 != offsetToMatrix && offsetToMatrix < len) {
700 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) { 712 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) {
701 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); 713 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n");
702 } 714 }
703 } 715 }
704 716
705 return true; 717 return true;
706 } 718 }
707 719
708 sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* base, size_t len) { 720 sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* input, size_t len) {
709 const uint8_t* ptr = (const uint8_t*) base;
710
711 if (len < kICCHeaderSize) { 721 if (len < kICCHeaderSize) {
712 return_null("Data is not large enough to contain an ICC profile"); 722 return_null("Data is not large enough to contain an ICC profile");
713 } 723 }
714 724
725 // Create our own copy of the input.
726 void* memory = sk_malloc_throw(len);
727 memcpy(memory, input, len);
728 sk_sp<SkData> data = SkData::MakeFromMalloc(memory, len);
729 const void* base = data->data();
730 const uint8_t* ptr = (const uint8_t*) base;
731
715 // Read the ICC profile header and check to make sure that it is valid. 732 // Read the ICC profile header and check to make sure that it is valid.
716 ICCProfileHeader header; 733 ICCProfileHeader header;
717 header.init(ptr, len); 734 header.init(ptr, len);
718 if (!header.valid()) { 735 if (!header.valid()) {
719 return nullptr; 736 return nullptr;
720 } 737 }
721 738
722 // Adjust ptr and len before reading the tags. 739 // Adjust ptr and len before reading the tags.
723 if (len < header.fSize) { 740 if (len < header.fSize) {
724 SkColorSpacePrintf("ICC profile might be truncated.\n"); 741 SkColorSpacePrintf("ICC profile might be truncated.\n");
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
786 std::move(curves[2]))); 803 std::move(curves[2])));
787 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); 804 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
788 mat.set3x3ColMajorf(toXYZ); 805 mat.set3x3ColMajorf(toXYZ);
789 if (gammas->isValues()) { 806 if (gammas->isValues()) {
790 // When we have values, take advantage of the NewFromRGB ini tializer. 807 // When we have values, take advantage of the NewFromRGB ini tializer.
791 // This allows us to check for canonical sRGB and Adobe RGB. 808 // This allows us to check for canonical sRGB and Adobe RGB.
792 float gammaVals[3]; 809 float gammaVals[3];
793 gammaVals[0] = gammas->fRed.fValue; 810 gammaVals[0] = gammas->fRed.fValue;
794 gammaVals[1] = gammas->fGreen.fValue; 811 gammaVals[1] = gammas->fGreen.fValue;
795 gammaVals[2] = gammas->fBlue.fValue; 812 gammaVals[2] = gammas->fBlue.fValue;
796 return SkColorSpace::NewRGB(gammaVals, mat); 813 return SkColorSpace_Base::NewRGB(gammaVals, mat, std::move(d ata));
797 } else { 814 } else {
798 return sk_sp<SkColorSpace>(new SkColorSpace_Base(std::move(g ammas), mat, 815 return sk_sp<SkColorSpace>(new SkColorSpace_Base(std::move(g ammas), mat,
799 kUnknown_Na med)); 816 kUnknown_Na med,
817 std::move(d ata)));
800 } 818 }
801 } 819 }
802 820
803 // Recognize color profile specified by A2B0 tag. 821 // Recognize color profile specified by A2B0 tag.
804 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); 822 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0);
805 if (a2b0) { 823 if (a2b0) {
806 SkAutoTDelete<SkColorLookUpTable> colorLUT(new SkColorLookUpTabl e()); 824 SkAutoTDelete<SkColorLookUpTable> colorLUT(new SkColorLookUpTabl e());
807 SkGammaCurve curves[3]; 825 SkGammaCurve curves[3];
808 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); 826 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor);
809 if (!load_a2b0(colorLUT, curves, &toXYZ, a2b0->addr((const uint8 _t*) base), 827 if (!load_a2b0(colorLUT, curves, &toXYZ, a2b0->addr((const uint8 _t*) base),
810 a2b0->fLength)) { 828 a2b0->fLength)) {
811 return_null("Failed to parse A2B0 tag"); 829 return_null("Failed to parse A2B0 tag");
812 } 830 }
813 831
814 sk_sp<SkGammas> gammas(new SkGammas(std::move(curves[0]), std::m ove(curves[1]), 832 sk_sp<SkGammas> gammas(new SkGammas(std::move(curves[0]), std::m ove(curves[1]),
815 std::move(curves[2]))); 833 std::move(curves[2])));
816 if (colorLUT->fTable) { 834 if (colorLUT->fTable) {
817 return sk_sp<SkColorSpace>(new SkColorSpace_Base(colorLUT.re lease(), 835 return sk_sp<SkColorSpace>(new SkColorSpace_Base(colorLUT.re lease(),
818 std::move(g ammas), toXYZ)); 836 std::move(g ammas), toXYZ,
837 std::move(d ata)));
819 } else if (gammas->isValues()) { 838 } else if (gammas->isValues()) {
820 // When we have values, take advantage of the NewFromRGB ini tializer. 839 // When we have values, take advantage of the NewFromRGB ini tializer.
821 // This allows us to check for canonical sRGB and Adobe RGB. 840 // This allows us to check for canonical sRGB and Adobe RGB.
822 float gammaVals[3]; 841 float gammaVals[3];
823 gammaVals[0] = gammas->fRed.fValue; 842 gammaVals[0] = gammas->fRed.fValue;
824 gammaVals[1] = gammas->fGreen.fValue; 843 gammaVals[1] = gammas->fGreen.fValue;
825 gammaVals[2] = gammas->fBlue.fValue; 844 gammaVals[2] = gammas->fBlue.fValue;
826 return SkColorSpace::NewRGB(gammaVals, toXYZ); 845 return SkColorSpace_Base::NewRGB(gammaVals, toXYZ, std::move (data));
827 } else { 846 } else {
828 return sk_sp<SkColorSpace>(new SkColorSpace_Base(std::move(g ammas), toXYZ, 847 return sk_sp<SkColorSpace>(new SkColorSpace_Base(std::move(g ammas), toXYZ,
829 kUnknown_Na med)); 848 kUnknown_Na med,
849 std::move(d ata)));
830 } 850 }
831 } 851 }
832 } 852 }
833 default: 853 default:
834 break; 854 break;
835 } 855 }
836 856
837 return_null("ICC profile contains unsupported colorspace"); 857 return_null("ICC profile contains unsupported colorspace");
838 } 858 }
859
860 //////////////////////////////////////////////////////////////////////////////// ///////////////////
861
862 // We will write a profile with the minimum nine required tags.
863 static constexpr uint32_t kICCNumEntries = 9;
864
865 static constexpr uint32_t kTAG_desc = SkSetFourByteTag('d', 'e', 's', 'c');
866 static constexpr uint32_t kTAG_desc_Bytes = 12;
867 static constexpr uint32_t kTAG_desc_Offset = kICCHeaderSize + kICCNumEntries*kIC CTagTableEntrySize;
868
869 static constexpr uint32_t kTAG_XYZ_Bytes = 20;
870 static constexpr uint32_t kTAG_rXYZ_Offset = kTAG_desc_Offset + kTAG_desc_Bytes;
871 static constexpr uint32_t kTAG_gXYZ_Offset = kTAG_rXYZ_Offset + kTAG_XYZ_Bytes;
872 static constexpr uint32_t kTAG_bXYZ_Offset = kTAG_gXYZ_Offset + kTAG_XYZ_Bytes;
873
874 static constexpr uint32_t kTAG_TRC_Bytes = 14;
875 static constexpr uint32_t kTAG_rTRC_Offset = kTAG_bXYZ_Offset + kTAG_XYZ_Bytes;
876 static constexpr uint32_t kTAG_gTRC_Offset = kTAG_rTRC_Offset + SkAlign4(kTAG_TR C_Bytes);
877 static constexpr uint32_t kTAG_bTRC_Offset = kTAG_gTRC_Offset + SkAlign4(kTAG_TR C_Bytes);
878
879 static constexpr uint32_t kTAG_wtpt = SkSetFourByteTag('w', 't', 'p', 't');
880 static constexpr uint32_t kTAG_wtpt_Offset = kTAG_bTRC_Offset + SkAlign4(kTAG_TR C_Bytes);
881
882 static constexpr uint32_t kTAG_cprt = SkSetFourByteTag('c', 'p', 'r', 't');
883 static constexpr uint32_t kTAG_cprt_Bytes = 12;
884 static constexpr uint32_t kTAG_cprt_Offset = kTAG_wtpt_Offset + kTAG_XYZ_Bytes;
885
886 static constexpr uint32_t kICCProfileSize = kTAG_cprt_Offset + kTAG_cprt_Bytes;
887
888 static constexpr uint32_t gICCHeader[kICCHeaderSize / 4] {
889 SkEndian_SwapBE32(kICCProfileSize), // Size of the profile
890 0, // Preferred CMM type (ignored)
891 SkEndian_SwapBE32(0x02100000), // Version 2.1
892 SkEndian_SwapBE32(kDisplay_Profile), // Display device profile
893 SkEndian_SwapBE32(kRGB_ColorSpace), // RGB input color space
894 SkEndian_SwapBE32(kXYZ_PCSSpace), // XYZ profile connection space
895 0, 0, 0, // Date and time (ignored)
896 SkEndian_SwapBE32(kACSP_Signature), // Profile signature
897 0, // Platform target (ignored)
898 0x00000000, // Flags: not embedded, can be used ind ependently
899 0, // Device manufacturer (ignored)
900 0, // Device model (ignored)
901 0, 0, // Device attributes (ignored)
902 SkEndian_SwapBE32(1), // Relative colorimetric rendering inte nt
903 SkEndian_SwapBE32(0x0000f6d6), // D50 standard illuminant (X)
904 SkEndian_SwapBE32(0x00010000), // D50 standard illuminant (Y)
905 SkEndian_SwapBE32(0x0000d32d), // D50 standard illuminant (Z)
906 0, // Profile creator (ignored)
907 0, 0, 0, 0, // Profile id checksum (ignored)
908 0, 0, 0, 0, 0, 0, 0, // Reserved (ignored)
909 SkEndian_SwapBE32(kICCNumEntries), // Number of tags
910 };
911
912 static constexpr uint32_t gICCTagTable[3 * kICCNumEntries] {
913 // Profile description
914 SkEndian_SwapBE32(kTAG_desc),
915 SkEndian_SwapBE32(kTAG_desc_Offset),
916 SkEndian_SwapBE32(kTAG_desc_Bytes),
917
918 // rXYZ
919 SkEndian_SwapBE32(kTAG_rXYZ),
920 SkEndian_SwapBE32(kTAG_rXYZ_Offset),
921 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
922
923 // gXYZ
924 SkEndian_SwapBE32(kTAG_gXYZ),
925 SkEndian_SwapBE32(kTAG_gXYZ_Offset),
926 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
927
928 // bXYZ
929 SkEndian_SwapBE32(kTAG_bXYZ),
930 SkEndian_SwapBE32(kTAG_bXYZ_Offset),
931 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
932
933 // rTRC
934 SkEndian_SwapBE32(kTAG_rTRC),
935 SkEndian_SwapBE32(kTAG_rTRC_Offset),
936 SkEndian_SwapBE32(kTAG_TRC_Bytes),
937
938 // gTRC
939 SkEndian_SwapBE32(kTAG_gTRC),
940 SkEndian_SwapBE32(kTAG_gTRC_Offset),
941 SkEndian_SwapBE32(kTAG_TRC_Bytes),
942
943 // bTRC
944 SkEndian_SwapBE32(kTAG_bTRC),
945 SkEndian_SwapBE32(kTAG_bTRC_Offset),
946 SkEndian_SwapBE32(kTAG_TRC_Bytes),
947
948 // White point
949 SkEndian_SwapBE32(kTAG_wtpt),
950 SkEndian_SwapBE32(kTAG_wtpt_Offset),
951 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
952
953 // Copyright
954 SkEndian_SwapBE32(kTAG_cprt),
955 SkEndian_SwapBE32(kTAG_cprt_Offset),
956 SkEndian_SwapBE32(kTAG_cprt_Bytes),
957 };
958
959 static constexpr uint32_t kTAG_TextType = SkSetFourByteTag('m', 'l', 'u', 'c');
960 static constexpr uint32_t gEmptyTextTag[3] {
961 SkEndian_SwapBE32(kTAG_TextType), // Type signature
962 0, // Reserved
963 0, // Zero records
964 };
965
966 static void write_xyz_tag(uint32_t* ptr, const SkMatrix44& toXYZ, int row) {
967 ptr[0] = SkEndian_SwapBE32(kXYZ_PCSSpace);
968 ptr[1] = 0;
969 ptr[2] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 0)));
970 ptr[3] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 1)));
971 ptr[4] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 2)));
972 }
973
974 static void write_trc_tag(uint32_t* ptr, float value) {
975 ptr[0] = SkEndian_SwapBE32(kTAG_CurveType);
976 ptr[1] = 0;
977
978 // Gamma will be specified with a single value.
979 ptr[2] = SkEndian_SwapBE32(1);
980
981 // Convert gamma to 16-bit fixed point.
982 uint16_t* ptr16 = (uint16_t*) (ptr + 3);
983 ptr16[0] = SkEndian_SwapBE16((uint16_t) (value * 256.0f));
984
985 // Pad tag with zero.
986 ptr16[1] = 0;
987 }
988
989 sk_sp<SkData> SkColorSpace_Base::writeToICC() const {
990 // Return if this object was created from a profile, or if we have already s erialized
991 // the profile.
992 if (fProfileData) {
993 return fProfileData;
994 }
995
996 // The client may create an SkColorSpace using an SkMatrix44, but currently we only
997 // support writing profiles with 3x3 matrices.
998 // TODO (msarett): Fix this!
999 if (0.0f != fToXYZD50.getFloat(3, 0) || 0.0f != fToXYZD50.getFloat(3, 1) ||
1000 0.0f != fToXYZD50.getFloat(3, 2) || 0.0f != fToXYZD50.getFloat(0, 3) ||
1001 0.0f != fToXYZD50.getFloat(1, 3) || 0.0f != fToXYZD50.getFloat(2, 3))
1002 {
1003 return nullptr;
1004 }
1005
1006 SkAutoMalloc profile(kICCProfileSize);
1007 uint8_t* ptr = (uint8_t*) profile.get();
1008
1009 // Write profile header
1010 memcpy(ptr, gICCHeader, sizeof(gICCHeader));
1011 ptr += sizeof(gICCHeader);
1012
1013 // Write tag table
1014 memcpy(ptr, gICCTagTable, sizeof(gICCTagTable));
1015 ptr += sizeof(gICCTagTable);
1016
1017 // Write profile description tag
1018 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag));
1019 ptr += sizeof(gEmptyTextTag);
1020
1021 // Write XYZ tags
1022 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 0);
1023 ptr += kTAG_XYZ_Bytes;
1024 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 1);
1025 ptr += kTAG_XYZ_Bytes;
1026 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 2);
1027 ptr += kTAG_XYZ_Bytes;
1028
1029 // Write TRC tags
1030 SkASSERT(as_CSB(this)->fGammas->fRed.isValue());
1031 write_trc_tag((uint32_t*) ptr, as_CSB(this)->fGammas->fRed.fValue);
1032 ptr += SkAlign4(kTAG_TRC_Bytes);
1033 SkASSERT(as_CSB(this)->fGammas->fGreen.isValue());
1034 write_trc_tag((uint32_t*) ptr, as_CSB(this)->fGammas->fGreen.fValue);
1035 ptr += SkAlign4(kTAG_TRC_Bytes);
1036 SkASSERT(as_CSB(this)->fGammas->fBlue.isValue());
1037 write_trc_tag((uint32_t*) ptr, as_CSB(this)->fGammas->fBlue.fValue);
1038 ptr += SkAlign4(kTAG_TRC_Bytes);
1039
1040 // Write white point tag
1041 uint32_t* ptr32 = (uint32_t*) ptr;
1042 ptr32[0] = SkEndian_SwapBE32(kXYZ_PCSSpace);
1043 ptr32[1] = 0;
1044 // TODO (msarett): These values correspond to the D65 white point. This may not always be
1045 // correct.
1046 ptr32[2] = SkEndian_SwapBE32(0x0000f351);
1047 ptr32[3] = SkEndian_SwapBE32(0x00010000);
1048 ptr32[4] = SkEndian_SwapBE32(0x000116cc);
1049 ptr += kTAG_XYZ_Bytes;
1050
1051 // Write copyright tag
1052 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag));
1053
1054 // TODO (msarett): Should we try to hold onto the data so we can return imme diately if
1055 // the client calls again?
1056 return SkData::MakeFromMalloc(profile.release(), kICCProfileSize);
1057 }
OLDNEW
« no previous file with comments | « no previous file | src/core/SkColorSpace_Base.h » ('j') | tests/ColorSpaceTest.cpp » ('J')

Powered by Google App Engine
This is Rietveld 408576698