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

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

Issue 1707033002: parse icc profiles Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 4 years, 9 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 | « src/core/SkColorSpace.h ('k') | tests/ColorSpaceTest.cpp » ('j') | no next file with comments »
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 "SkAtomics.h" 8 #include "SkAtomics.h"
9 #include "SkColorSpace.h" 9 #include "SkColorSpace.h"
10 10
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
107 if (!SkFloatIsFinite(gamma.fVec[i]) || gamma.fVec[i] < 0) { 107 if (!SkFloatIsFinite(gamma.fVec[i]) || gamma.fVec[i] < 0) {
108 return nullptr; 108 return nullptr;
109 } 109 }
110 for (int j = 0; j < 3; ++j) { 110 for (int j = 0; j < 3; ++j) {
111 if (!SkFloatIsFinite(toXYZD50.fMat[3*i + j])) { 111 if (!SkFloatIsFinite(toXYZD50.fMat[3*i + j])) {
112 return nullptr; 112 return nullptr;
113 } 113 }
114 } 114 }
115 } 115 }
116 116
117 #if 0
117 // check the matrix for invertibility 118 // check the matrix for invertibility
118 float d = det(toXYZD50); 119 float d = det(toXYZD50);
119 if (!SkFloatIsFinite(d) || !SkFloatIsFinite(1 / d)) { 120 if (!SkFloatIsFinite(d) || !SkFloatIsFinite(1 / d)) {
120 return nullptr; 121 return nullptr;
121 } 122 }
123 #endif
122 124
123 return new SkColorSpace(toXYZD50, gamma, kUnknown_Named); 125 return new SkColorSpace(toXYZD50, gamma, kUnknown_Named);
124 } 126 }
125 127
126 void SkColorSpace::dump() const { 128 void SkColorSpace::dump() const {
127 fToXYZD50.dump(); 129 fToXYZD50.dump();
128 fGamma.dump(); 130 fGamma.dump();
129 } 131 }
130 132
131 //////////////////////////////////////////////////////////////////////////////// ////////////////// 133 //////////////////////////////////////////////////////////////////////////////// //////////////////
(...skipping 18 matching lines...) Expand all
150 return new SkColorSpace(gDevice_toXYZD50, gDevice_gamma, kDevice_Nam ed); 152 return new SkColorSpace(gDevice_toXYZD50, gDevice_gamma, kDevice_Nam ed);
151 case kSRGB_Named: 153 case kSRGB_Named:
152 return new SkColorSpace(gSRGB_toXYZD50, gSRGB_gamma, kSRGB_Named); 154 return new SkColorSpace(gSRGB_toXYZD50, gSRGB_gamma, kSRGB_Named);
153 default: 155 default:
154 break; 156 break;
155 } 157 }
156 return nullptr; 158 return nullptr;
157 } 159 }
158 160
159 //////////////////////////////////////////////////////////////////////////////// /////////////////// 161 //////////////////////////////////////////////////////////////////////////////// ///////////////////
162 #include "SkEndian.h"
163 #include "SkStream.h"
164
165 #define return_if_false(pred, msg) \
166 do { if (!(pred)) { SkDebugf("parsing icc profile: %s\n", msg); return false ; } } while (0)
167
168 #define return_null(msg) do { SkDebugf("ICC Profile: %s\n", (msg)); return nu llptr; } while (0)
169
170 const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' ');
171 const uint32_t kGray_ColorSpace = SkSetFourByteTag('G', 'R', 'A', 'Y');
172
173 const uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z');
174 const uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z');
175 const uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z');
176
177 const uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C');
178 const uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C');
179 const uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C');
180
181 struct ICCProfileHeader {
182 uint32_t fSize;
183 uint32_t fCMMType;
184 uint32_t fVersion;
185 uint32_t fClassProfile;
186 uint32_t fColorSpace;
187 uint32_t fPCS;
188 uint32_t fCreated[3];
189 uint32_t fSignature;
190 uint32_t fPlatformTarget_ignored;
191 uint32_t fFlags_ignored;
192 uint32_t fManufacturer_ignored;
193 uint32_t fDeviceModel_ignored;
194 uint32_t fDeviceAttributes_ignored[2];
195 uint32_t fRenderingIntent;
196 uint32_t fIlluminantXYZ_ignored[3];
197 uint32_t fCreator_ignored;
198
199 uint32_t fReserved_ignored[11];
200
201 bool init(const uint32_t data[], size_t length) {
202 SkASSERT(128 == sizeof(*this));
203 if (length < 128) {
204 return false;
205 }
206 uint32_t* ptr = &fSize;
207 for (int i = 0; i < 32; ++i) {
208 ptr[i] = SkEndian_SwapBE32(data[i]);
209 }
210 return true;
211 }
212
213 bool valid() const {
214 return_if_false(fSize > 128, "size too small");
215
216 uint8_t version[4];
217 memcpy(version, &fVersion, sizeof(version));
218 return_if_false((version[3] == 2 || version[3] == 4) && version[0] == 0 && version[1] == 0,
219 "unsupported version");
220
221 const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r ');
222 const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r ');
223 const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r ');
224 return_if_false(fClassProfile == kDisplay_Profile ||
225 fClassProfile == kInput_Profile ||
226 fClassProfile == kOutput_Profile,
227 "unsupported class profile");
228
229 return_if_false(fColorSpace == kRGB_ColorSpace ||
230 fColorSpace == kGray_ColorSpace,
231 "unsupported class profile");
232
233 const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' ');
234 return_if_false(fPCS == kXYZ_PCSSpace,
235 "unsupported class profile");
236
237 return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "bad signature");
238
239 enum ICCIntents {
240 kPerceptual = 0,
241 kRelativeColormetric = 1,
242 kSaturation = 2,
243 kAbsoluteColorMetric = 3,
244 };
245 return_if_false(fRenderingIntent >= 0 || fRenderingIntent <= 3, "bad ren dering intent");
246
247 return true;
248 }
249 };
250
251 struct ICCTag {
252 uint32_t fSignature;
253 uint32_t fOffset;
254 uint32_t fLength;
255
256 const uint32_t* init(const uint32_t data[]) {
257 fSignature = SkEndian_SwapBE32(data[0]);
258 fOffset = SkEndian_SwapBE32(data[1]);
259 fLength = SkEndian_SwapBE32(data[2]);
260 return data + 3;
261 }
262
263 const uint32_t* addr(const uint32_t* base) const {
264 SkASSERT(SkIsAlign4(fOffset));
265 return base + (fOffset >> 2);
266 }
267
268 static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature ) {
269 for (int i = 0; i < count; ++i) {
270 if (tags[i].fSignature == signature) {
271 return &tags[i];
272 }
273 }
274 return nullptr;
275 }
276 };
277
278 void load_3x(float dst[3], const uint32_t data[]) {
279 uint32_t tag = SkEndian_SwapBE32(data[0]);
280 dst[0] = SkFixedToFloat(SkEndian_SwapBE32(data[2]));
281 dst[1] = SkFixedToFloat(SkEndian_SwapBE32(data[2]));
282 dst[2] = SkFixedToFloat(SkEndian_SwapBE32(data[2]));
283 SkDebugf("3x tag %08X %g %g %g\n", tag, dst[0], dst[1], dst[2]);
284 }
285
286 const uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v');
287 const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a');
288
289 static float load_gamma(const uint32_t data[]) {
290 uint32_t type = SkEndian_SwapBE32(data[0]);
291 switch (type) {
292 case kTAG_CurveType: {
293 int count = SkEndian_SwapBE32(data[2]);
294 const uint16_t* table = (const uint16_t*)&data[3];
295 if (1 == count) { // table entry is the exponent (bias 256)
296 unsigned value = SkEndian_SwapBE16(table[0]);
297 float gamma = value / 256.0f;
298 SkDebugf("gamma %d %g\n", value, gamma);
299 return gamma;
300 }
301 // general interp table (ingored for now)
302 for (int i = 0; i < count; ++i) {
303 unsigned value = SkEndian_SwapBE16(table[i]);
304 SkDebugf("curve[%d] %d\n", i, value);
305 }
306 return 2.2f;
307 } break;
308 case kTAG_ParaCurveType: {
309 SkDebugf("parametric curve\n");
310 return 2.2f;
311 } break;
312 default:
313 return 1;
314 }
315 }
316
317 SkColorSpace* SkColorSpace::NewICCProfile32(const uint32_t base[], size_t length ) {
318 const uint32_t storedSize = SkEndian_SwapBE32(base[0]);
319 if (storedSize < 128+4) {
320 return nullptr; // too small
321 }
322 if (length > storedSize) {
323 length = storedSize;
324 }
325
326 ICCProfileHeader header;
327 if (!header.init(base, length) || !header.valid()) {
328 return nullptr;
329 }
330
331 const uint32_t* data = base + (128/4);
332 int tagCount = SkEndian_SwapBE32(*data++);
333 if (tagCount < 0 || tagCount > 100) {
334 return nullptr;
335 }
336
337 SkDebugf("%d tags\n", tagCount);
338 SkAutoTArray<ICCTag> tags(tagCount);
339 for (int i = 0; i < tagCount; ++i) {
340 data = tags[i].init(data);
341 SkDebugf("[%d] %c%c%c%c %d %d\n", i,
342 (tags[i].fSignature >> 24) & 0xFF, (tags[i].fSignature >> 16) & 0xFF,
343 (tags[i].fSignature >> 8) & 0xFF, (tags[i].fSignature >> 0) & 0xFF,
344 tags[i].fOffset, tags[i].fLength);
345 }
346
347 SkFloat3x3 toXYZ;
348 SkFloat3 gamma {{ 1, 1, 1 }};
349
350 // Load up our matrix
351 switch (header.fColorSpace) {
352 case kRGB_ColorSpace: {
353 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ);
354 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ);
355 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ);
356 if (!r || !g || !b) {
357 return_null("needed rgb tags for XYZ space");
358 }
359 load_3x(&toXYZ.fMat[0], r->addr(base));
360 load_3x(&toXYZ.fMat[3], g->addr(base));
361 load_3x(&toXYZ.fMat[6], b->addr(base));
362
363 r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC);
364 g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC);
365 b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC);
366 if (r) {
367 gamma.fVec[0] = load_gamma(r->addr(base));
368 }
369 if (g) {
370 gamma.fVec[1] = load_gamma(g->addr(base));
371 }
372 if (b) {
373 gamma.fVec[2] = load_gamma(b->addr(base));
374 }
375 return SkColorSpace::NewRGB(toXYZ, gamma);
376 }
377 default:
378 break;
379 }
380 return_null("unsupported colorspace");
381 }
382
383 SkColorSpace* SkColorSpace::NewICCProfile(const void* ptr, size_t length) {
384 const uint32_t* data;
385 SkAutoTArray<uint32_t> storage;
386
387 if ((intptr_t)ptr & 3) { // not 4-byte aligned
388 storage.reset(SkAlign4(length) >> 2);
389 memcpy(storage.get(), ptr, length);
390 data = storage.get();
391 } else {
392 data = (const uint32_t*)ptr;
393 }
394 return NewICCProfile32(data, length);
395 }
396
397 //////////////////////////////////////////////////////////////////////////////// ///////////////////
398 //////////////////////////////////////////////////////////////////////////////// ///////////////////
160 399
161 SkColorSpace::Result SkColorSpace::Concat(const SkColorSpace* src, const SkColor Space* dst, 400 SkColorSpace::Result SkColorSpace::Concat(const SkColorSpace* src, const SkColor Space* dst,
162 SkFloat3x3* result) { 401 SkFloat3x3* result) {
163 if (!src || !dst || (src->named() == kDevice_Named) || (src->named() == dst- >named())) { 402 if (!src || !dst || (src->named() == kDevice_Named) || (src->named() == dst- >named())) {
164 if (result) { 403 if (result) {
165 *result = {{ 1, 0, 0, 0, 1, 0, 0, 0, 1 }}; 404 *result = {{ 1, 0, 0, 0, 1, 0, 0, 0, 1 }};
166 } 405 }
167 return kIdentity_Result; 406 return kIdentity_Result;
168 } 407 }
169 if (result) { 408 if (result) {
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
236 } 475 }
237 476
238 // D65 white point of Rec. 709 [8] are: 477 // D65 white point of Rec. 709 [8] are:
239 // 478 //
240 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 479 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890
241 // 480 //
242 // R G B white 481 // R G B white
243 // x 0.640 0.300 0.150 0.3127 482 // x 0.640 0.300 0.150 0.3127
244 // y 0.330 0.600 0.060 0.3290 483 // y 0.330 0.600 0.060 0.3290
245 // z 0.030 0.100 0.790 0.3582 484 // z 0.030 0.100 0.790 0.3582
OLDNEW
« no previous file with comments | « src/core/SkColorSpace.h ('k') | tests/ColorSpaceTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698