• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
8 #include "SkAutoMalloc.h"
9 #include "SkColorSpace_Base.h"
10 #include "SkColorSpace_XYZ.h"
11 #include "SkColorSpacePriv.h"
12 #include "SkColorSpaceXformPriv.h"
13 #include "SkEndian.h"
14 #include "SkFixed.h"
15 #include "SkICC.h"
16 #include "SkICCPriv.h"
17 
SkICC(sk_sp<SkColorSpace> colorSpace)18 SkICC::SkICC(sk_sp<SkColorSpace> colorSpace)
19     : fColorSpace(std::move(colorSpace))
20 {}
21 
Make(const void * ptr,size_t len)22 sk_sp<SkICC> SkICC::Make(const void* ptr, size_t len) {
23     sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeICC(ptr, len);
24     if (!colorSpace) {
25         return nullptr;
26     }
27 
28     return sk_sp<SkICC>(new SkICC(std::move(colorSpace)));
29 }
30 
toXYZD50(SkMatrix44 * toXYZD50) const31 bool SkICC::toXYZD50(SkMatrix44* toXYZD50) const {
32     const SkMatrix44* m = as_CSB(fColorSpace)->toXYZD50();
33     if (!m) {
34         return false;
35     }
36 
37     *toXYZD50 = *m;
38     return true;
39 }
40 
isNumericalTransferFn(SkColorSpaceTransferFn * coeffs) const41 bool SkICC::isNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const {
42     return as_CSB(fColorSpace)->onIsNumericalTransferFn(coeffs);
43 }
44 
45 static const int kDefaultTableSize = 512; // Arbitrary
46 
fn_to_table(float * tablePtr,const SkColorSpaceTransferFn & fn)47 void fn_to_table(float* tablePtr, const SkColorSpaceTransferFn& fn) {
48     // Y = (aX + b)^g + e  for X >= d
49     // Y = cX + f          otherwise
50     for (int i = 0; i < kDefaultTableSize; i++) {
51         float x = ((float) i) / ((float) (kDefaultTableSize - 1));
52         if (x >= fn.fD) {
53             tablePtr[i] = clamp_0_1(powf(fn.fA * x + fn.fB, fn.fG) + fn.fE);
54         } else {
55             tablePtr[i] = clamp_0_1(fn.fC * x + fn.fF);
56         }
57     }
58 }
59 
copy_to_table(float * tablePtr,const SkGammas * gammas,int index)60 void copy_to_table(float* tablePtr, const SkGammas* gammas, int index) {
61     SkASSERT(gammas->isTable(index));
62     const float* ptr = gammas->table(index);
63     const size_t bytes = gammas->tableSize(index) * sizeof(float);
64     memcpy(tablePtr, ptr, bytes);
65 }
66 
rawTransferFnData(Tables * tables) const67 bool SkICC::rawTransferFnData(Tables* tables) const {
68     if (SkColorSpace_Base::Type::kA2B == as_CSB(fColorSpace)->type()) {
69         return false;
70     }
71     SkColorSpace_XYZ* colorSpace = (SkColorSpace_XYZ*) fColorSpace.get();
72 
73     SkColorSpaceTransferFn fn;
74     if (this->isNumericalTransferFn(&fn)) {
75         tables->fStorage = SkData::MakeUninitialized(kDefaultTableSize * sizeof(float));
76         fn_to_table((float*) tables->fStorage->writable_data(), fn);
77         tables->fRed.fOffset = tables->fGreen.fOffset = tables->fBlue.fOffset = 0;
78         tables->fRed.fCount = tables->fGreen.fCount = tables->fBlue.fCount = kDefaultTableSize;
79         return true;
80     }
81 
82     const SkGammas* gammas = colorSpace->gammas();
83     SkASSERT(gammas);
84     if (gammas->data(0) == gammas->data(1) && gammas->data(0) == gammas->data(2)) {
85         SkASSERT(gammas->isTable(0));
86         tables->fStorage = SkData::MakeUninitialized(gammas->tableSize(0) * sizeof(float));
87         copy_to_table((float*) tables->fStorage->writable_data(), gammas, 0);
88         tables->fRed.fOffset = tables->fGreen.fOffset = tables->fBlue.fOffset = 0;
89         tables->fRed.fCount = tables->fGreen.fCount = tables->fBlue.fCount = gammas->tableSize(0);
90         return true;
91     }
92 
93     // Determine the storage size.
94     size_t storageSize = 0;
95     for (int i = 0; i < 3; i++) {
96         if (gammas->isTable(i)) {
97             storageSize += gammas->tableSize(i) * sizeof(float);
98         } else {
99             storageSize += kDefaultTableSize * sizeof(float);
100         }
101     }
102 
103     // Fill in the tables.
104     tables->fStorage = SkData::MakeUninitialized(storageSize);
105     float* ptr = (float*) tables->fStorage->writable_data();
106     size_t offset = 0;
107     Channel rgb[3];
108     for (int i = 0; i < 3; i++) {
109         if (gammas->isTable(i)) {
110             copy_to_table(ptr, gammas, i);
111             rgb[i].fOffset = offset;
112             rgb[i].fCount = gammas->tableSize(i);
113             offset += rgb[i].fCount * sizeof(float);
114             ptr += rgb[i].fCount;
115             continue;
116         }
117 
118         if (gammas->isNamed(i)) {
119             SkAssertResult(named_to_parametric(&fn, gammas->data(i).fNamed));
120         } else if (gammas->isValue(i)) {
121             value_to_parametric(&fn, gammas->data(i).fValue);
122         } else {
123             SkASSERT(gammas->isParametric(i));
124             fn = gammas->params(i);
125         }
126 
127         fn_to_table(ptr, fn);
128         rgb[i].fOffset = offset;
129         rgb[i].fCount = kDefaultTableSize;
130         offset += kDefaultTableSize * sizeof(float);
131         ptr += kDefaultTableSize;
132     }
133 
134     tables->fRed = rgb[0];
135     tables->fGreen = rgb[1];
136     tables->fBlue = rgb[2];
137     return true;
138 }
139 
140 ///////////////////////////////////////////////////////////////////////////////////////////////////
141 
142 // Google Skia (UTF-16)
143 static constexpr uint8_t kDescriptionTagBody[] = {
144         0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00,
145         0x53, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x61, 0x00, 0x20,
146     };
147 static_assert(SkIsAlign4(sizeof(kDescriptionTagBody)), "Description must be aligned to 4-bytes.");
148 static constexpr uint32_t kDescriptionTagHeader[7] {
149     SkEndian_SwapBE32(kTAG_TextType),                        // Type signature
150     0,                                                       // Reserved
151     SkEndian_SwapBE32(1),                                    // Number of records
152     SkEndian_SwapBE32(12),                                   // Record size (must be 12)
153     SkEndian_SwapBE32(SkSetFourByteTag('e', 'n', 'U', 'S')), // English USA
154     SkEndian_SwapBE32(sizeof(kDescriptionTagBody)),          // Length of string
155     SkEndian_SwapBE32(28),                                   // Offset of string
156 };
157 
158 static constexpr uint32_t kWhitePointTag[5] {
159     SkEndian_SwapBE32(kXYZ_PCSSpace),
160     0,
161     SkEndian_SwapBE32(0x0000f6d6), // X = 0.96420 (D50)
162     SkEndian_SwapBE32(0x00010000), // Y = 1.00000 (D50)
163     SkEndian_SwapBE32(0x0000d32d), // Z = 0.82491 (D50)
164 };
165 
166 // Google Inc. 2016 (UTF-16)
167 static constexpr uint8_t kCopyrightTagBody[] = {
168         0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00,
169         0x49, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x32, 0x00, 0x30, 0x00, 0x31,
170         0x00, 0x36,
171 };
172 static_assert(SkIsAlign4(sizeof(kCopyrightTagBody)), "Copyright must be aligned to 4-bytes.");
173 static constexpr uint32_t kCopyrightTagHeader[7] {
174     SkEndian_SwapBE32(kTAG_TextType),                        // Type signature
175     0,                                                       // Reserved
176     SkEndian_SwapBE32(1),                                    // Number of records
177     SkEndian_SwapBE32(12),                                   // Record size (must be 12)
178     SkEndian_SwapBE32(SkSetFourByteTag('e', 'n', 'U', 'S')), // English USA
179     SkEndian_SwapBE32(sizeof(kCopyrightTagBody)),            // Length of string
180     SkEndian_SwapBE32(28),                                   // Offset of string
181 };
182 
183 // We will write a profile with the minimum nine required tags.
184 static constexpr uint32_t kICCNumEntries = 9;
185 
186 static constexpr uint32_t kTAG_desc = SkSetFourByteTag('d', 'e', 's', 'c');
187 static constexpr uint32_t kTAG_desc_Bytes = sizeof(kDescriptionTagHeader) +
188                                             sizeof(kDescriptionTagBody);
189 static constexpr uint32_t kTAG_desc_Offset = kICCHeaderSize +
190                                              kICCNumEntries * kICCTagTableEntrySize;
191 
192 static constexpr uint32_t kTAG_XYZ_Bytes = 20;
193 static constexpr uint32_t kTAG_rXYZ_Offset = kTAG_desc_Offset + kTAG_desc_Bytes;
194 static constexpr uint32_t kTAG_gXYZ_Offset = kTAG_rXYZ_Offset + kTAG_XYZ_Bytes;
195 static constexpr uint32_t kTAG_bXYZ_Offset = kTAG_gXYZ_Offset + kTAG_XYZ_Bytes;
196 
197 static constexpr uint32_t kTAG_TRC_Bytes = 40;
198 static constexpr uint32_t kTAG_rTRC_Offset = kTAG_bXYZ_Offset + kTAG_XYZ_Bytes;
199 static constexpr uint32_t kTAG_gTRC_Offset = kTAG_rTRC_Offset;
200 static constexpr uint32_t kTAG_bTRC_Offset = kTAG_rTRC_Offset;
201 
202 static constexpr uint32_t kTAG_wtpt = SkSetFourByteTag('w', 't', 'p', 't');
203 static constexpr uint32_t kTAG_wtpt_Offset = kTAG_bTRC_Offset + kTAG_TRC_Bytes;
204 
205 static constexpr uint32_t kTAG_cprt = SkSetFourByteTag('c', 'p', 'r', 't');
206 static constexpr uint32_t kTAG_cprt_Bytes = sizeof(kCopyrightTagHeader) +
207                                             sizeof(kCopyrightTagBody);
208 static constexpr uint32_t kTAG_cprt_Offset = kTAG_wtpt_Offset + kTAG_XYZ_Bytes;
209 
210 static constexpr uint32_t kICCProfileSize = kTAG_cprt_Offset + kTAG_cprt_Bytes;
211 
212 static constexpr uint32_t kICCHeader[kICCHeaderSize / 4] {
213     SkEndian_SwapBE32(kICCProfileSize),  // Size of the profile
214     0,                                   // Preferred CMM type (ignored)
215     SkEndian_SwapBE32(0x02100000),       // Version 2.1
216     SkEndian_SwapBE32(kDisplay_Profile), // Display device profile
217     SkEndian_SwapBE32(kRGB_ColorSpace),  // RGB input color space
218     SkEndian_SwapBE32(kXYZ_PCSSpace),    // XYZ profile connection space
219     0, 0, 0,                             // Date and time (ignored)
220     SkEndian_SwapBE32(kACSP_Signature),  // Profile signature
221     0,                                   // Platform target (ignored)
222     0x00000000,                          // Flags: not embedded, can be used independently
223     0,                                   // Device manufacturer (ignored)
224     0,                                   // Device model (ignored)
225     0, 0,                                // Device attributes (ignored)
226     SkEndian_SwapBE32(1),                // Relative colorimetric rendering intent
227     SkEndian_SwapBE32(0x0000f6d6),       // D50 standard illuminant (X)
228     SkEndian_SwapBE32(0x00010000),       // D50 standard illuminant (Y)
229     SkEndian_SwapBE32(0x0000d32d),       // D50 standard illuminant (Z)
230     0,                                   // Profile creator (ignored)
231     0, 0, 0, 0,                          // Profile id checksum (ignored)
232     0, 0, 0, 0, 0, 0, 0,                 // Reserved (ignored)
233     SkEndian_SwapBE32(kICCNumEntries),   // Number of tags
234 };
235 
236 static constexpr uint32_t kICCTagTable[3 * kICCNumEntries] {
237     // Profile description
238     SkEndian_SwapBE32(kTAG_desc),
239     SkEndian_SwapBE32(kTAG_desc_Offset),
240     SkEndian_SwapBE32(kTAG_desc_Bytes),
241 
242     // rXYZ
243     SkEndian_SwapBE32(kTAG_rXYZ),
244     SkEndian_SwapBE32(kTAG_rXYZ_Offset),
245     SkEndian_SwapBE32(kTAG_XYZ_Bytes),
246 
247     // gXYZ
248     SkEndian_SwapBE32(kTAG_gXYZ),
249     SkEndian_SwapBE32(kTAG_gXYZ_Offset),
250     SkEndian_SwapBE32(kTAG_XYZ_Bytes),
251 
252     // bXYZ
253     SkEndian_SwapBE32(kTAG_bXYZ),
254     SkEndian_SwapBE32(kTAG_bXYZ_Offset),
255     SkEndian_SwapBE32(kTAG_XYZ_Bytes),
256 
257     // rTRC
258     SkEndian_SwapBE32(kTAG_rTRC),
259     SkEndian_SwapBE32(kTAG_rTRC_Offset),
260     SkEndian_SwapBE32(kTAG_TRC_Bytes),
261 
262     // gTRC
263     SkEndian_SwapBE32(kTAG_gTRC),
264     SkEndian_SwapBE32(kTAG_gTRC_Offset),
265     SkEndian_SwapBE32(kTAG_TRC_Bytes),
266 
267     // bTRC
268     SkEndian_SwapBE32(kTAG_bTRC),
269     SkEndian_SwapBE32(kTAG_bTRC_Offset),
270     SkEndian_SwapBE32(kTAG_TRC_Bytes),
271 
272     // White point
273     SkEndian_SwapBE32(kTAG_wtpt),
274     SkEndian_SwapBE32(kTAG_wtpt_Offset),
275     SkEndian_SwapBE32(kTAG_XYZ_Bytes),
276 
277     // Copyright
278     SkEndian_SwapBE32(kTAG_cprt),
279     SkEndian_SwapBE32(kTAG_cprt_Offset),
280     SkEndian_SwapBE32(kTAG_cprt_Bytes),
281 };
282 
write_xyz_tag(uint32_t * ptr,const SkMatrix44 & toXYZ,int col)283 static void write_xyz_tag(uint32_t* ptr, const SkMatrix44& toXYZ, int col) {
284     ptr[0] = SkEndian_SwapBE32(kXYZ_PCSSpace);
285     ptr[1] = 0;
286     ptr[2] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(0, col)));
287     ptr[3] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(1, col)));
288     ptr[4] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(2, col)));
289 }
290 
write_trc_tag(uint32_t * ptr,const SkColorSpaceTransferFn & fn)291 static void write_trc_tag(uint32_t* ptr, const SkColorSpaceTransferFn& fn) {
292     ptr[0] = SkEndian_SwapBE32(kTAG_ParaCurveType);
293     ptr[1] = 0;
294     ptr[2] = (uint32_t) (SkEndian_SwapBE16(kGABCDEF_ParaCurveType));
295     ptr[3] = SkEndian_SwapBE32(SkFloatToFixed(fn.fG));
296     ptr[4] = SkEndian_SwapBE32(SkFloatToFixed(fn.fA));
297     ptr[5] = SkEndian_SwapBE32(SkFloatToFixed(fn.fB));
298     ptr[6] = SkEndian_SwapBE32(SkFloatToFixed(fn.fC));
299     ptr[7] = SkEndian_SwapBE32(SkFloatToFixed(fn.fD));
300     ptr[8] = SkEndian_SwapBE32(SkFloatToFixed(fn.fE));
301     ptr[9] = SkEndian_SwapBE32(SkFloatToFixed(fn.fF));
302 }
303 
is_3x3(const SkMatrix44 & toXYZD50)304 static bool is_3x3(const SkMatrix44& toXYZD50) {
305     return 0.0f == toXYZD50.get(3, 0) && 0.0f == toXYZD50.get(3, 1) && 0.0f == toXYZD50.get(3, 2) &&
306            0.0f == toXYZD50.get(0, 3) && 0.0f == toXYZD50.get(1, 3) && 0.0f == toXYZD50.get(2, 3) &&
307            1.0f == toXYZD50.get(3, 3);
308 }
309 
WriteToICC(const SkColorSpaceTransferFn & fn,const SkMatrix44 & toXYZD50)310 sk_sp<SkData> SkICC::WriteToICC(const SkColorSpaceTransferFn& fn, const SkMatrix44& toXYZD50) {
311     if (!is_3x3(toXYZD50) || !is_valid_transfer_fn(fn)) {
312         return nullptr;
313     }
314 
315     SkAutoMalloc profile(kICCProfileSize);
316     uint8_t* ptr = (uint8_t*) profile.get();
317 
318     // Write profile header
319     memcpy(ptr, kICCHeader, sizeof(kICCHeader));
320     ptr += sizeof(kICCHeader);
321 
322     // Write tag table
323     memcpy(ptr, kICCTagTable, sizeof(kICCTagTable));
324     ptr += sizeof(kICCTagTable);
325 
326     // Write profile description tag
327     memcpy(ptr, kDescriptionTagHeader, sizeof(kDescriptionTagHeader));
328     ptr += sizeof(kDescriptionTagHeader);
329     memcpy(ptr, kDescriptionTagBody, sizeof(kDescriptionTagBody));
330     ptr += sizeof(kDescriptionTagBody);
331 
332     // Write XYZ tags
333     write_xyz_tag((uint32_t*) ptr, toXYZD50, 0);
334     ptr += kTAG_XYZ_Bytes;
335     write_xyz_tag((uint32_t*) ptr, toXYZD50, 1);
336     ptr += kTAG_XYZ_Bytes;
337     write_xyz_tag((uint32_t*) ptr, toXYZD50, 2);
338     ptr += kTAG_XYZ_Bytes;
339 
340     // Write TRC tag
341     write_trc_tag((uint32_t*) ptr, fn);
342     ptr += kTAG_TRC_Bytes;
343 
344     // Write white point tag (must be D50)
345     memcpy(ptr, kWhitePointTag, sizeof(kWhitePointTag));
346     ptr += sizeof(kWhitePointTag);
347 
348     // Write copyright tag
349     memcpy(ptr, kCopyrightTagHeader, sizeof(kCopyrightTagHeader));
350     ptr += sizeof(kCopyrightTagHeader);
351     memcpy(ptr, kCopyrightTagBody, sizeof(kCopyrightTagBody));
352     ptr += sizeof(kCopyrightTagBody);
353 
354     SkASSERT(kICCProfileSize == ptr - (uint8_t*) profile.get());
355     return SkData::MakeFromMalloc(profile.release(), kICCProfileSize);
356 }
357