SkColorSpace.cpp revision bbf251bf225489a0939fff6df938035a290f4d16
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 "SkColorSpace.h" 9#include "SkColorSpace_Base.h" 10#include "SkColorSpace_XYZ.h" 11#include "SkColorSpacePriv.h" 12#include "SkOnce.h" 13#include "SkPoint3.h" 14 15bool SkColorSpacePrimaries::toXYZD50(SkMatrix44* toXYZ_D50) const { 16 if (!is_zero_to_one(fRX) || !is_zero_to_one(fRY) || 17 !is_zero_to_one(fGX) || !is_zero_to_one(fGY) || 18 !is_zero_to_one(fBX) || !is_zero_to_one(fBY) || 19 !is_zero_to_one(fWX) || !is_zero_to_one(fWY)) 20 { 21 return false; 22 } 23 24 // First, we need to convert xy values (primaries) to XYZ. 25 SkMatrix primaries; 26 primaries.setAll( fRX, fGX, fBX, 27 fRY, fGY, fBY, 28 1.0f - fRX - fRY, 1.0f - fGX - fGY, 1.0f - fBX - fBY); 29 SkMatrix primariesInv; 30 if (!primaries.invert(&primariesInv)) { 31 return false; 32 } 33 34 // Assumes that Y is 1.0f. 35 SkVector3 wXYZ = SkVector3::Make(fWX / fWY, 1.0f, (1.0f - fWX - fWY) / fWY); 36 SkVector3 XYZ; 37 XYZ.fX = primariesInv[0] * wXYZ.fX + primariesInv[1] * wXYZ.fY + primariesInv[2] * wXYZ.fZ; 38 XYZ.fY = primariesInv[3] * wXYZ.fX + primariesInv[4] * wXYZ.fY + primariesInv[5] * wXYZ.fZ; 39 XYZ.fZ = primariesInv[6] * wXYZ.fX + primariesInv[7] * wXYZ.fY + primariesInv[8] * wXYZ.fZ; 40 SkMatrix toXYZ; 41 toXYZ.setAll(XYZ.fX, 0.0f, 0.0f, 42 0.0f, XYZ.fY, 0.0f, 43 0.0f, 0.0f, XYZ.fZ); 44 toXYZ.postConcat(primaries); 45 46 // Now convert toXYZ matrix to toXYZD50. 47 SkVector3 wXYZD50 = SkVector3::Make(0.96422f, 1.0f, 0.82521f); 48 49 // Calculate the chromatic adaptation matrix. We will use the Bradford method, thus 50 // the matrices below. The Bradford method is used by Adobe and is widely considered 51 // to be the best. 52 SkMatrix mA, mAInv; 53 mA.setAll(+0.8951f, +0.2664f, -0.1614f, 54 -0.7502f, +1.7135f, +0.0367f, 55 +0.0389f, -0.0685f, +1.0296f); 56 mAInv.setAll(+0.9869929f, -0.1470543f, +0.1599627f, 57 +0.4323053f, +0.5183603f, +0.0492912f, 58 -0.0085287f, +0.0400428f, +0.9684867f); 59 60 SkVector3 srcCone; 61 srcCone.fX = mA[0] * wXYZ.fX + mA[1] * wXYZ.fY + mA[2] * wXYZ.fZ; 62 srcCone.fY = mA[3] * wXYZ.fX + mA[4] * wXYZ.fY + mA[5] * wXYZ.fZ; 63 srcCone.fZ = mA[6] * wXYZ.fX + mA[7] * wXYZ.fY + mA[8] * wXYZ.fZ; 64 SkVector3 dstCone; 65 dstCone.fX = mA[0] * wXYZD50.fX + mA[1] * wXYZD50.fY + mA[2] * wXYZD50.fZ; 66 dstCone.fY = mA[3] * wXYZD50.fX + mA[4] * wXYZD50.fY + mA[5] * wXYZD50.fZ; 67 dstCone.fZ = mA[6] * wXYZD50.fX + mA[7] * wXYZD50.fY + mA[8] * wXYZD50.fZ; 68 69 SkMatrix DXToD50; 70 DXToD50.setIdentity(); 71 DXToD50[0] = dstCone.fX / srcCone.fX; 72 DXToD50[4] = dstCone.fY / srcCone.fY; 73 DXToD50[8] = dstCone.fZ / srcCone.fZ; 74 DXToD50.postConcat(mAInv); 75 DXToD50.preConcat(mA); 76 77 toXYZ.postConcat(DXToD50); 78 toXYZ_D50->set3x3(toXYZ[0], toXYZ[3], toXYZ[6], 79 toXYZ[1], toXYZ[4], toXYZ[7], 80 toXYZ[2], toXYZ[5], toXYZ[8]); 81 return true; 82} 83 84/////////////////////////////////////////////////////////////////////////////////////////////////// 85 86SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkData> profileData) 87 : fProfileData(std::move(profileData)) 88{} 89 90static constexpr float gSRGB_toXYZD50[] { 91 0.4358f, 0.3853f, 0.1430f, // Rx, Gx, Bx 92 0.2224f, 0.7170f, 0.0606f, // Ry, Gy, Gz 93 0.0139f, 0.0971f, 0.7139f, // Rz, Gz, Bz 94}; 95 96static constexpr float gAdobeRGB_toXYZD50[] { 97 0.6098f, 0.2052f, 0.1492f, // Rx, Gx, Bx 98 0.3111f, 0.6257f, 0.0632f, // Ry, Gy, By 99 0.0195f, 0.0609f, 0.7448f, // Rz, Gz, Bz 100}; 101 102/** 103 * Checks if our toXYZ matrix is a close match to a known color gamut. 104 * 105 * @param toXYZD50 transformation matrix deduced from profile data 106 * @param standard 3x3 canonical transformation matrix 107 */ 108static bool xyz_almost_equal(const SkMatrix44& toXYZD50, const float* standard) { 109 return color_space_almost_equal(toXYZD50.getFloat(0, 0), standard[0]) && 110 color_space_almost_equal(toXYZD50.getFloat(0, 1), standard[1]) && 111 color_space_almost_equal(toXYZD50.getFloat(0, 2), standard[2]) && 112 color_space_almost_equal(toXYZD50.getFloat(1, 0), standard[3]) && 113 color_space_almost_equal(toXYZD50.getFloat(1, 1), standard[4]) && 114 color_space_almost_equal(toXYZD50.getFloat(1, 2), standard[5]) && 115 color_space_almost_equal(toXYZD50.getFloat(2, 0), standard[6]) && 116 color_space_almost_equal(toXYZD50.getFloat(2, 1), standard[7]) && 117 color_space_almost_equal(toXYZD50.getFloat(2, 2), standard[8]) && 118 color_space_almost_equal(toXYZD50.getFloat(0, 3), 0.0f) && 119 color_space_almost_equal(toXYZD50.getFloat(1, 3), 0.0f) && 120 color_space_almost_equal(toXYZD50.getFloat(2, 3), 0.0f) && 121 color_space_almost_equal(toXYZD50.getFloat(3, 0), 0.0f) && 122 color_space_almost_equal(toXYZD50.getFloat(3, 1), 0.0f) && 123 color_space_almost_equal(toXYZD50.getFloat(3, 2), 0.0f) && 124 color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f); 125} 126 127sk_sp<SkColorSpace> SkColorSpace::NewRGB(const float values[3], const SkMatrix44& toXYZD50) { 128 if (0.0f > values[0] || 0.0f > values[1] || 0.0f > values[2]) { 129 return nullptr; 130 } 131 132 SkGammaNamed gammaNamed = kNonStandard_SkGammaNamed; 133 if (color_space_almost_equal(2.2f, values[0]) && 134 color_space_almost_equal(2.2f, values[1]) && 135 color_space_almost_equal(2.2f, values[2])) { 136 gammaNamed = k2Dot2Curve_SkGammaNamed; 137 } else if (color_space_almost_equal(1.0f, values[0]) && 138 color_space_almost_equal(1.0f, values[1]) && 139 color_space_almost_equal(1.0f, values[2])) { 140 gammaNamed = kLinear_SkGammaNamed; 141 } 142 143 if (kNonStandard_SkGammaNamed == gammaNamed) { 144 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas()); 145 gammas->fRedType = SkGammas::Type::kValue_Type; 146 gammas->fGreenType = SkGammas::Type::kValue_Type; 147 gammas->fBlueType = SkGammas::Type::kValue_Type; 148 gammas->fRedData.fValue = values[0]; 149 gammas->fGreenData.fValue = values[1]; 150 gammas->fBlueData.fValue = values[2]; 151 return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(kNonStandard_SkGammaNamed, 152 gammas, toXYZD50, nullptr)); 153 } 154 155 return SkColorSpace_Base::NewRGB(gammaNamed, toXYZD50); 156} 157 158sk_sp<SkColorSpace> SkColorSpace_Base::NewRGB(SkGammaNamed gammaNamed, const SkMatrix44& toXYZD50) { 159 switch (gammaNamed) { 160 case kSRGB_SkGammaNamed: 161 if (xyz_almost_equal(toXYZD50, gSRGB_toXYZD50)) { 162 return SkColorSpace::NewNamed(kSRGB_Named); 163 } 164 break; 165 case k2Dot2Curve_SkGammaNamed: 166 if (xyz_almost_equal(toXYZD50, gAdobeRGB_toXYZD50)) { 167 return SkColorSpace::NewNamed(kAdobeRGB_Named); 168 } 169 break; 170 case kLinear_SkGammaNamed: 171 if (xyz_almost_equal(toXYZD50, gSRGB_toXYZD50)) { 172 return SkColorSpace::NewNamed(kSRGBLinear_Named); 173 } 174 break; 175 case kNonStandard_SkGammaNamed: 176 // This is not allowed. 177 return nullptr; 178 default: 179 break; 180 } 181 182 return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed, toXYZD50)); 183} 184 185sk_sp<SkColorSpace> SkColorSpace::NewRGB(RenderTargetGamma gamma, const SkMatrix44& toXYZD50) { 186 switch (gamma) { 187 case kLinear_RenderTargetGamma: 188 return SkColorSpace_Base::NewRGB(kLinear_SkGammaNamed, toXYZD50); 189 case kSRGB_RenderTargetGamma: 190 return SkColorSpace_Base::NewRGB(kSRGB_SkGammaNamed, toXYZD50); 191 default: 192 return nullptr; 193 } 194} 195 196sk_sp<SkColorSpace> SkColorSpace::NewRGB(const SkColorSpaceTransferFn& coeffs, 197 const SkMatrix44& toXYZD50) { 198 if (!is_valid_transfer_fn(coeffs)) { 199 return nullptr; 200 } 201 202 if (is_almost_srgb(coeffs)) { 203 return SkColorSpace::NewRGB(kSRGB_RenderTargetGamma, toXYZD50); 204 } 205 206 if (is_almost_2dot2(coeffs)) { 207 return SkColorSpace_Base::NewRGB(k2Dot2Curve_SkGammaNamed, toXYZD50); 208 } 209 210 void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(SkColorSpaceTransferFn)); 211 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas()); 212 SkColorSpaceTransferFn* fn = SkTAddOffset<SkColorSpaceTransferFn>(memory, sizeof(SkGammas)); 213 *fn = coeffs; 214 gammas->fRedType = SkGammas::Type::kParam_Type; 215 gammas->fGreenType = SkGammas::Type::kParam_Type; 216 gammas->fBlueType = SkGammas::Type::kParam_Type; 217 218 SkGammas::Data data; 219 data.fParamOffset = 0; 220 gammas->fRedData = data; 221 gammas->fGreenData = data; 222 gammas->fBlueData = data; 223 return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(kNonStandard_SkGammaNamed, 224 std::move(gammas), toXYZD50, nullptr)); 225} 226 227static SkColorSpace* gAdobeRGB; 228static SkColorSpace* gSRGB; 229static SkColorSpace* gSRGBLinear; 230 231sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) { 232 static SkOnce sRGBOnce; 233 static SkOnce adobeRGBOnce; 234 static SkOnce sRGBLinearOnce; 235 236 switch (named) { 237 case kSRGB_Named: { 238 sRGBOnce([] { 239 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor); 240 srgbToxyzD50.set3x3RowMajorf(gSRGB_toXYZD50); 241 242 // Force the mutable type mask to be computed. This avoids races. 243 (void)srgbToxyzD50.getType(); 244 gSRGB = new SkColorSpace_XYZ(kSRGB_SkGammaNamed, srgbToxyzD50); 245 }); 246 return sk_ref_sp<SkColorSpace>(gSRGB); 247 } 248 case kAdobeRGB_Named: { 249 adobeRGBOnce([] { 250 SkMatrix44 adobergbToxyzD50(SkMatrix44::kUninitialized_Constructor); 251 adobergbToxyzD50.set3x3RowMajorf(gAdobeRGB_toXYZD50); 252 253 // Force the mutable type mask to be computed. This avoids races. 254 (void)adobergbToxyzD50.getType(); 255 gAdobeRGB = new SkColorSpace_XYZ(k2Dot2Curve_SkGammaNamed, adobergbToxyzD50); 256 }); 257 return sk_ref_sp<SkColorSpace>(gAdobeRGB); 258 } 259 case kSRGBLinear_Named: { 260 sRGBLinearOnce([] { 261 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor); 262 srgbToxyzD50.set3x3RowMajorf(gSRGB_toXYZD50); 263 264 // Force the mutable type mask to be computed. This avoids races. 265 (void)srgbToxyzD50.getType(); 266 gSRGBLinear = new SkColorSpace_XYZ(kLinear_SkGammaNamed, srgbToxyzD50); 267 }); 268 return sk_ref_sp<SkColorSpace>(gSRGBLinear); 269 } 270 default: 271 break; 272 } 273 return nullptr; 274} 275 276/////////////////////////////////////////////////////////////////////////////////////////////////// 277 278bool SkColorSpace::gammaCloseToSRGB() const { 279 return as_CSB(this)->onGammaCloseToSRGB(); 280} 281 282bool SkColorSpace::gammaIsLinear() const { 283 return as_CSB(this)->onGammaIsLinear(); 284} 285 286/////////////////////////////////////////////////////////////////////////////////////////////////// 287 288enum Version { 289 k0_Version, // Initial version, header + flags for matrix and profile 290}; 291 292struct ColorSpaceHeader { 293 /** 294 * It is only valid to set zero or one flags. 295 * Setting multiple flags is invalid. 296 */ 297 298 /** 299 * If kMatrix_Flag is set, we will write 12 floats after the header. 300 */ 301 static constexpr uint8_t kMatrix_Flag = 1 << 0; 302 303 /** 304 * If kICC_Flag is set, we will write an ICC profile after the header. 305 * The ICC profile will be written as a uint32 size, followed immediately 306 * by the data (padded to 4 bytes). 307 */ 308 static constexpr uint8_t kICC_Flag = 1 << 1; 309 310 /** 311 * If kFloatGamma_Flag is set, we will write 15 floats after the header. 312 * The first three are the gamma values, and the next twelve are the 313 * matrix. 314 */ 315 static constexpr uint8_t kFloatGamma_Flag = 1 << 2; 316 317 /** 318 * If kTransferFn_Flag is set, we will write 19 floats after the header. 319 * The first seven represent the transfer fn, and the next twelve are the 320 * matrix. 321 */ 322 static constexpr uint8_t kTransferFn_Flag = 1 << 3; 323 324 static ColorSpaceHeader Pack(Version version, uint8_t named, uint8_t gammaNamed, uint8_t flags) 325 { 326 ColorSpaceHeader header; 327 328 SkASSERT(k0_Version == version); 329 header.fVersion = (uint8_t) version; 330 331 SkASSERT(named <= SkColorSpace::kSRGBLinear_Named); 332 header.fNamed = (uint8_t) named; 333 334 SkASSERT(gammaNamed <= kNonStandard_SkGammaNamed); 335 header.fGammaNamed = (uint8_t) gammaNamed; 336 337 SkASSERT(flags <= kTransferFn_Flag); 338 header.fFlags = flags; 339 return header; 340 } 341 342 uint8_t fVersion; // Always zero 343 uint8_t fNamed; // Must be a SkColorSpace::Named 344 uint8_t fGammaNamed; // Must be a SkGammaNamed 345 uint8_t fFlags; // Some combination of the flags listed above 346}; 347 348size_t SkColorSpace::writeToMemory(void* memory) const { 349 // Start by trying the serialization fast path. If we haven't saved ICC profile data, 350 // we must have a profile that we can serialize easily. 351 if (!as_CSB(this)->fProfileData) { 352 // Profile data is mandatory for A2B0 color spaces. 353 SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(this)->type()); 354 const SkColorSpace_XYZ* thisXYZ = static_cast<const SkColorSpace_XYZ*>(this); 355 // If we have a named profile, only write the enum. 356 const SkGammaNamed gammaNamed = thisXYZ->gammaNamed(); 357 if (this == gSRGB) { 358 if (memory) { 359 *((ColorSpaceHeader*) memory) = 360 ColorSpaceHeader::Pack(k0_Version, kSRGB_Named, gammaNamed, 0); 361 } 362 return sizeof(ColorSpaceHeader); 363 } else if (this == gAdobeRGB) { 364 if (memory) { 365 *((ColorSpaceHeader*) memory) = 366 ColorSpaceHeader::Pack(k0_Version, kAdobeRGB_Named, gammaNamed, 0); 367 } 368 return sizeof(ColorSpaceHeader); 369 } else if (this == gSRGBLinear) { 370 if (memory) { 371 *((ColorSpaceHeader*) memory) = 372 ColorSpaceHeader::Pack(k0_Version, kSRGBLinear_Named, gammaNamed, 0); 373 } 374 return sizeof(ColorSpaceHeader); 375 } 376 377 // If we have a named gamma, write the enum and the matrix. 378 switch (gammaNamed) { 379 case kSRGB_SkGammaNamed: 380 case k2Dot2Curve_SkGammaNamed: 381 case kLinear_SkGammaNamed: { 382 if (memory) { 383 *((ColorSpaceHeader*) memory) = 384 ColorSpaceHeader::Pack(k0_Version, 0, gammaNamed, 385 ColorSpaceHeader::kMatrix_Flag); 386 memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader)); 387 thisXYZ->toXYZD50()->as3x4RowMajorf((float*) memory); 388 } 389 return sizeof(ColorSpaceHeader) + 12 * sizeof(float); 390 } 391 default: 392 const SkGammas* gammas = thisXYZ->gammas(); 393 SkASSERT(gammas); 394 if (gammas->isValue(0) && gammas->isValue(1) && gammas->isValue(2)) { 395 if (memory) { 396 *((ColorSpaceHeader*) memory) = 397 ColorSpaceHeader::Pack(k0_Version, 0, thisXYZ->fGammaNamed, 398 ColorSpaceHeader::kFloatGamma_Flag); 399 memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader)); 400 401 *(((float*) memory) + 0) = gammas->fRedData.fValue; 402 *(((float*) memory) + 1) = gammas->fGreenData.fValue; 403 *(((float*) memory) + 2) = gammas->fBlueData.fValue; 404 memory = SkTAddOffset<void>(memory, 3 * sizeof(float)); 405 406 thisXYZ->fToXYZD50.as3x4RowMajorf((float*) memory); 407 } 408 409 return sizeof(ColorSpaceHeader) + 15 * sizeof(float); 410 } else { 411 SkASSERT(gammas->isParametric(0)); 412 SkASSERT(gammas->isParametric(1)); 413 SkASSERT(gammas->isParametric(2)); 414 SkASSERT(gammas->data(0) == gammas->data(1)); 415 SkASSERT(gammas->data(0) == gammas->data(2)); 416 417 if (memory) { 418 *((ColorSpaceHeader*) memory) = 419 ColorSpaceHeader::Pack(k0_Version, 0, thisXYZ->fGammaNamed, 420 ColorSpaceHeader::kTransferFn_Flag); 421 memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader)); 422 423 *(((float*) memory) + 0) = gammas->params(0).fA; 424 *(((float*) memory) + 1) = gammas->params(0).fB; 425 *(((float*) memory) + 2) = gammas->params(0).fC; 426 *(((float*) memory) + 3) = gammas->params(0).fD; 427 *(((float*) memory) + 4) = gammas->params(0).fE; 428 *(((float*) memory) + 5) = gammas->params(0).fF; 429 *(((float*) memory) + 6) = gammas->params(0).fG; 430 memory = SkTAddOffset<void>(memory, 7 * sizeof(float)); 431 432 thisXYZ->fToXYZD50.as3x4RowMajorf((float*) memory); 433 } 434 435 return sizeof(ColorSpaceHeader) + 19 * sizeof(float); 436 } 437 } 438 } 439 440 // Otherwise, serialize the ICC data. 441 size_t profileSize = as_CSB(this)->fProfileData->size(); 442 if (SkAlign4(profileSize) != (uint32_t) SkAlign4(profileSize)) { 443 return 0; 444 } 445 446 if (memory) { 447 *((ColorSpaceHeader*) memory) = ColorSpaceHeader::Pack(k0_Version, 0, 448 kNonStandard_SkGammaNamed, 449 ColorSpaceHeader::kICC_Flag); 450 memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader)); 451 452 *((uint32_t*) memory) = (uint32_t) SkAlign4(profileSize); 453 memory = SkTAddOffset<void>(memory, sizeof(uint32_t)); 454 455 memcpy(memory, as_CSB(this)->fProfileData->data(), profileSize); 456 memset(SkTAddOffset<void>(memory, profileSize), 0, SkAlign4(profileSize) - profileSize); 457 } 458 return sizeof(ColorSpaceHeader) + sizeof(uint32_t) + SkAlign4(profileSize); 459} 460 461sk_sp<SkData> SkColorSpace::serialize() const { 462 size_t size = this->writeToMemory(nullptr); 463 if (0 == size) { 464 return nullptr; 465 } 466 467 sk_sp<SkData> data = SkData::MakeUninitialized(size); 468 this->writeToMemory(data->writable_data()); 469 return data; 470} 471 472sk_sp<SkColorSpace> SkColorSpace::Deserialize(const void* data, size_t length) { 473 if (length < sizeof(ColorSpaceHeader)) { 474 return nullptr; 475 } 476 477 ColorSpaceHeader header = *((const ColorSpaceHeader*) data); 478 data = SkTAddOffset<const void>(data, sizeof(ColorSpaceHeader)); 479 length -= sizeof(ColorSpaceHeader); 480 if (0 == header.fFlags) { 481 return NewNamed((Named) header.fNamed); 482 } 483 484 switch ((SkGammaNamed) header.fGammaNamed) { 485 case kSRGB_SkGammaNamed: 486 case k2Dot2Curve_SkGammaNamed: 487 case kLinear_SkGammaNamed: { 488 if (ColorSpaceHeader::kMatrix_Flag != header.fFlags || length < 12 * sizeof(float)) { 489 return nullptr; 490 } 491 492 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); 493 toXYZ.set3x4RowMajorf((const float*) data); 494 return SkColorSpace_Base::NewRGB((SkGammaNamed) header.fGammaNamed, toXYZ); 495 } 496 default: 497 break; 498 } 499 500 switch (header.fFlags) { 501 case ColorSpaceHeader::kICC_Flag: { 502 if (length < sizeof(uint32_t)) { 503 return nullptr; 504 } 505 506 uint32_t profileSize = *((uint32_t*) data); 507 data = SkTAddOffset<const void>(data, sizeof(uint32_t)); 508 length -= sizeof(uint32_t); 509 if (length < profileSize) { 510 return nullptr; 511 } 512 513 return NewICC(data, profileSize); 514 } 515 case ColorSpaceHeader::kFloatGamma_Flag: { 516 if (length < 15 * sizeof(float)) { 517 return nullptr; 518 } 519 520 float gammas[3]; 521 gammas[0] = *(((const float*) data) + 0); 522 gammas[1] = *(((const float*) data) + 1); 523 gammas[2] = *(((const float*) data) + 2); 524 data = SkTAddOffset<const void>(data, 3 * sizeof(float)); 525 526 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); 527 toXYZ.set3x4RowMajorf((const float*) data); 528 return SkColorSpace::NewRGB(gammas, toXYZ); 529 } 530 case ColorSpaceHeader::kTransferFn_Flag: { 531 if (length < 19 * sizeof(float)) { 532 return nullptr; 533 } 534 535 SkColorSpaceTransferFn transferFn; 536 transferFn.fA = *(((const float*) data) + 0); 537 transferFn.fB = *(((const float*) data) + 1); 538 transferFn.fC = *(((const float*) data) + 2); 539 transferFn.fD = *(((const float*) data) + 3); 540 transferFn.fE = *(((const float*) data) + 4); 541 transferFn.fF = *(((const float*) data) + 5); 542 transferFn.fG = *(((const float*) data) + 6); 543 data = SkTAddOffset<const void>(data, 7 * sizeof(float)); 544 545 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); 546 toXYZ.set3x4RowMajorf((const float*) data); 547 return SkColorSpace::NewRGB(transferFn, toXYZ); 548 } 549 default: 550 return nullptr; 551 } 552} 553 554bool SkColorSpace::Equals(const SkColorSpace* src, const SkColorSpace* dst) { 555 if (src == dst) { 556 return true; 557 } 558 559 if (!src || !dst) { 560 return false; 561 } 562 563 SkData* srcData = as_CSB(src)->fProfileData.get(); 564 SkData* dstData = as_CSB(dst)->fProfileData.get(); 565 if (srcData || dstData) { 566 if (srcData && dstData) { 567 return srcData->size() == dstData->size() && 568 0 == memcmp(srcData->data(), dstData->data(), srcData->size()); 569 } 570 571 return false; 572 } 573 574 // profiles are mandatory for A2B0 color spaces 575 SkASSERT(as_CSB(src)->type() == SkColorSpace_Base::Type::kXYZ); 576 const SkColorSpace_XYZ* srcXYZ = static_cast<const SkColorSpace_XYZ*>(src); 577 const SkColorSpace_XYZ* dstXYZ = static_cast<const SkColorSpace_XYZ*>(dst); 578 579 if (srcXYZ->gammaNamed() != dstXYZ->gammaNamed()) { 580 return false; 581 } 582 583 switch (srcXYZ->gammaNamed()) { 584 case kSRGB_SkGammaNamed: 585 case k2Dot2Curve_SkGammaNamed: 586 case kLinear_SkGammaNamed: 587 if (srcXYZ->toXYZD50Hash() == dstXYZ->toXYZD50Hash()) { 588 SkASSERT(*srcXYZ->toXYZD50() == *dstXYZ->toXYZD50() && "Hash collision"); 589 return true; 590 } 591 return false; 592 default: 593 // It is unlikely that we will reach this case. 594 sk_sp<SkData> serializedSrcData = src->serialize(); 595 sk_sp<SkData> serializedDstData = dst->serialize(); 596 return serializedSrcData->size() == serializedDstData->size() && 597 0 == memcmp(serializedSrcData->data(), serializedDstData->data(), 598 serializedSrcData->size()); 599 } 600} 601