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 18SkICC::SkICC(sk_sp<SkColorSpace> colorSpace) 19 : fColorSpace(std::move(colorSpace)) 20{} 21 22sk_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 31bool 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 41bool SkICC::isNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const { 42 return as_CSB(fColorSpace)->onIsNumericalTransferFn(coeffs); 43} 44 45static const int kDefaultTableSize = 512; // Arbitrary 46 47void 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 60void 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 67bool 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) 143static 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 }; 147static_assert(SkIsAlign4(sizeof(kDescriptionTagBody)), "Description must be aligned to 4-bytes."); 148static 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 158static 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) 167static 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}; 172static_assert(SkIsAlign4(sizeof(kCopyrightTagBody)), "Copyright must be aligned to 4-bytes."); 173static 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. 184static constexpr uint32_t kICCNumEntries = 9; 185 186static constexpr uint32_t kTAG_desc = SkSetFourByteTag('d', 'e', 's', 'c'); 187static constexpr uint32_t kTAG_desc_Bytes = sizeof(kDescriptionTagHeader) + 188 sizeof(kDescriptionTagBody); 189static constexpr uint32_t kTAG_desc_Offset = kICCHeaderSize + 190 kICCNumEntries * kICCTagTableEntrySize; 191 192static constexpr uint32_t kTAG_XYZ_Bytes = 20; 193static constexpr uint32_t kTAG_rXYZ_Offset = kTAG_desc_Offset + kTAG_desc_Bytes; 194static constexpr uint32_t kTAG_gXYZ_Offset = kTAG_rXYZ_Offset + kTAG_XYZ_Bytes; 195static constexpr uint32_t kTAG_bXYZ_Offset = kTAG_gXYZ_Offset + kTAG_XYZ_Bytes; 196 197static constexpr uint32_t kTAG_TRC_Bytes = 40; 198static constexpr uint32_t kTAG_rTRC_Offset = kTAG_bXYZ_Offset + kTAG_XYZ_Bytes; 199static constexpr uint32_t kTAG_gTRC_Offset = kTAG_rTRC_Offset; 200static constexpr uint32_t kTAG_bTRC_Offset = kTAG_rTRC_Offset; 201 202static constexpr uint32_t kTAG_wtpt = SkSetFourByteTag('w', 't', 'p', 't'); 203static constexpr uint32_t kTAG_wtpt_Offset = kTAG_bTRC_Offset + kTAG_TRC_Bytes; 204 205static constexpr uint32_t kTAG_cprt = SkSetFourByteTag('c', 'p', 'r', 't'); 206static constexpr uint32_t kTAG_cprt_Bytes = sizeof(kCopyrightTagHeader) + 207 sizeof(kCopyrightTagBody); 208static constexpr uint32_t kTAG_cprt_Offset = kTAG_wtpt_Offset + kTAG_XYZ_Bytes; 209 210static constexpr uint32_t kICCProfileSize = kTAG_cprt_Offset + kTAG_cprt_Bytes; 211 212static 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 236static 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 283static 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 291static 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 304static 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 310sk_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