Chromium Code Reviews| OLD | NEW |
|---|---|
| 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, |
| 20 sk_sp<SkData> profileData) | |
| 19 : fGammaNamed(kNonStandard_GammaNamed) | 21 : fGammaNamed(kNonStandard_GammaNamed) |
| 20 , fToXYZD50(toXYZD50) | 22 , fToXYZD50(toXYZD50) |
| 21 , fNamed(named) | 23 , fNamed(named) |
| 24 , fProfileData(std::move(profileData)) | |
| 22 {} | 25 {} |
| 23 | 26 |
| 24 SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkGammas> gammas, const SkMatrix44& t oXYZD50, | 27 SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkGammas> gammas, const SkMatrix44& t oXYZD50, |
| 25 Named named) | 28 Named named, sk_sp<SkData> profileData) |
| 26 : INHERITED(kNonStandard_GammaNamed, toXYZD50, named) | 29 : INHERITED(kNonStandard_GammaNamed, toXYZD50, named, std::move(profileData) ) |
| 27 , fGammas(gammas) | 30 , fGammas(gammas) |
| 28 {} | 31 {} |
| 29 | 32 |
| 30 SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkGammas> gammas, GammaNamed gammaNam ed, | 33 SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkGammas> gammas, GammaNamed gammaNam ed, |
| 31 const SkMatrix44& toXYZD50, Named named) | 34 const SkMatrix44& toXYZD50, Named named, |
| 32 : INHERITED(gammaNamed, toXYZD50, named) | 35 sk_sp<SkData> profileData) |
| 36 : INHERITED(gammaNamed, toXYZD50, named, std::move(profileData)) | |
| 33 , fGammas(gammas) | 37 , fGammas(gammas) |
| 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, std::move(pro fileData)) |
| 39 , fColorLUT(colorLUT) | 43 , fColorLUT(colorLUT) |
| 40 , fGammas(gammas) | 44 , fGammas(gammas) |
| 41 {} | 45 {} |
| 42 | 46 |
| 43 const float gSRGB_toXYZD50[] { | 47 const float gSRGB_toXYZD50[] { |
| 44 0.4358f, 0.2224f, 0.0139f, // * R | 48 0.4358f, 0.2224f, 0.0139f, // * R |
| 45 0.3853f, 0.7170f, 0.0971f, // * G | 49 0.3853f, 0.7170f, 0.0971f, // * G |
| 46 0.1430f, 0.0606f, 0.7139f, // * B | 50 0.1430f, 0.0606f, 0.7139f, // * B |
| 47 }; | 51 }; |
| 48 | 52 |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 75 color_space_almost_equal(toXYZD50.getFloat(3, 1), 0.0f) && | 79 color_space_almost_equal(toXYZD50.getFloat(3, 1), 0.0f) && |
| 76 color_space_almost_equal(toXYZD50.getFloat(3, 2), 0.0f) && | 80 color_space_almost_equal(toXYZD50.getFloat(3, 2), 0.0f) && |
| 77 color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f); | 81 color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f); |
| 78 } | 82 } |
| 79 | 83 |
| 80 static SkOnce g2Dot2CurveGammasOnce; | 84 static SkOnce g2Dot2CurveGammasOnce; |
| 81 static SkGammas* g2Dot2CurveGammas; | 85 static SkGammas* g2Dot2CurveGammas; |
| 82 static SkOnce gLinearGammasOnce; | 86 static SkOnce gLinearGammasOnce; |
| 83 static SkGammas* gLinearGammas; | 87 static SkGammas* gLinearGammas; |
| 84 | 88 |
| 85 sk_sp<SkColorSpace> SkColorSpace::NewRGB(float gammaVals[3], const SkMatrix44& t oXYZD50) { | 89 sk_sp<SkColorSpace> SkColorSpace::NewRGB(const float gammaVals[3], const SkMatri x44& toXYZD50) { |
| 90 return SkColorSpace_Base::NewRGB(gammaVals, toXYZD50, nullptr); | |
| 91 } | |
| 92 | |
| 93 sk_sp<SkColorSpace> SkColorSpace_Base::NewRGB(const float gammaVals[3], const Sk Matrix44& toXYZD50, | |
| 94 sk_sp<SkData> profileData) { | |
| 86 sk_sp<SkGammas> gammas = nullptr; | 95 sk_sp<SkGammas> gammas = nullptr; |
| 87 GammaNamed gammaNamed = kNonStandard_GammaNamed; | 96 GammaNamed gammaNamed = kNonStandard_GammaNamed; |
| 88 | 97 |
| 89 // Check if we really have sRGB or Adobe RGB | 98 // Check if we really have sRGB or Adobe RGB |
| 90 if (color_space_almost_equal(2.2f, gammaVals[0]) && | 99 if (color_space_almost_equal(2.2f, gammaVals[0]) && |
| 91 color_space_almost_equal(2.2f, gammaVals[1]) && | 100 color_space_almost_equal(2.2f, gammaVals[1]) && |
| 92 color_space_almost_equal(2.2f, gammaVals[2])) | 101 color_space_almost_equal(2.2f, gammaVals[2])) |
| 93 { | 102 { |
| 94 g2Dot2CurveGammasOnce([] { | 103 g2Dot2CurveGammasOnce([] { |
| 95 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); | 104 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 109 gLinearGammasOnce([] { | 118 gLinearGammasOnce([] { |
| 110 gLinearGammas = new SkGammas(1.0f, 1.0f, 1.0f); | 119 gLinearGammas = new SkGammas(1.0f, 1.0f, 1.0f); |
| 111 }); | 120 }); |
| 112 gammas = sk_ref_sp(gLinearGammas); | 121 gammas = sk_ref_sp(gLinearGammas); |
| 113 gammaNamed = kLinear_GammaNamed; | 122 gammaNamed = kLinear_GammaNamed; |
| 114 } | 123 } |
| 115 | 124 |
| 116 if (!gammas) { | 125 if (!gammas) { |
| 117 gammas = sk_sp<SkGammas>(new SkGammas(gammaVals[0], gammaVals[1], gammaV als[2])); | 126 gammas = sk_sp<SkGammas>(new SkGammas(gammaVals[0], gammaVals[1], gammaV als[2])); |
| 118 } | 127 } |
| 119 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, gammaNamed, toXYZD5 0, kUnknown_Named)); | 128 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, gammaNamed, toXYZD5 0, kUnknown_Named, |
| 129 std::move(profileData))); | |
| 120 } | 130 } |
| 121 | 131 |
| 122 sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) { | 132 sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) { |
| 123 static SkOnce sRGBOnce; | 133 static SkOnce sRGBOnce; |
| 124 static SkColorSpace* sRGB; | 134 static SkColorSpace* sRGB; |
| 125 static SkOnce adobeRGBOnce; | 135 static SkOnce adobeRGBOnce; |
| 126 static SkColorSpace* adobeRGB; | 136 static SkColorSpace* adobeRGB; |
| 127 | 137 |
| 128 switch (named) { | 138 switch (named) { |
| 129 case kSRGB_Named: { | 139 case kSRGB_Named: { |
| 130 g2Dot2CurveGammasOnce([] { | 140 g2Dot2CurveGammasOnce([] { |
| 131 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); | 141 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); |
| 132 }); | 142 }); |
| 133 | 143 |
| 134 sRGBOnce([] { | 144 sRGBOnce([] { |
| 135 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor); | 145 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor); |
| 136 srgbToxyzD50.set3x3ColMajorf(gSRGB_toXYZD50); | 146 srgbToxyzD50.set3x3ColMajorf(gSRGB_toXYZD50); |
| 137 sRGB = new SkColorSpace_Base(sk_ref_sp(g2Dot2CurveGammas), k2Dot 2Curve_GammaNamed, | 147 sRGB = new SkColorSpace_Base(sk_ref_sp(g2Dot2CurveGammas), k2Dot 2Curve_GammaNamed, |
| 138 srgbToxyzD50, kSRGB_Named); | 148 srgbToxyzD50, kSRGB_Named, nullptr) ; |
| 139 }); | 149 }); |
| 140 return sk_ref_sp(sRGB); | 150 return sk_ref_sp(sRGB); |
| 141 } | 151 } |
| 142 case kAdobeRGB_Named: { | 152 case kAdobeRGB_Named: { |
| 143 g2Dot2CurveGammasOnce([] { | 153 g2Dot2CurveGammasOnce([] { |
| 144 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); | 154 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); |
| 145 }); | 155 }); |
| 146 | 156 |
| 147 adobeRGBOnce([] { | 157 adobeRGBOnce([] { |
| 148 SkMatrix44 adobergbToxyzD50(SkMatrix44::kUninitialized_Construct or); | 158 SkMatrix44 adobergbToxyzD50(SkMatrix44::kUninitialized_Construct or); |
| 149 adobergbToxyzD50.set3x3ColMajorf(gAdobeRGB_toXYZD50); | 159 adobergbToxyzD50.set3x3ColMajorf(gAdobeRGB_toXYZD50); |
| 150 adobeRGB = new SkColorSpace_Base(sk_ref_sp(g2Dot2CurveGammas), | 160 adobeRGB = new SkColorSpace_Base(sk_ref_sp(g2Dot2CurveGammas), |
| 151 k2Dot2Curve_GammaNamed, adoberg bToxyzD50, | 161 k2Dot2Curve_GammaNamed, adoberg bToxyzD50, |
| 152 kAdobeRGB_Named); | 162 kAdobeRGB_Named, nullptr); |
| 153 }); | 163 }); |
| 154 return sk_ref_sp(adobeRGB); | 164 return sk_ref_sp(adobeRGB); |
| 155 } | 165 } |
| 156 default: | 166 default: |
| 157 break; | 167 break; |
| 158 } | 168 } |
| 159 return nullptr; | 169 return nullptr; |
| 160 } | 170 } |
| 161 | 171 |
| 162 //////////////////////////////////////////////////////////////////////////////// /////////////////// | 172 //////////////////////////////////////////////////////////////////////////////// /////////////////// |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 194 | 204 |
| 195 // This is equal to the header size according to the ICC specification (128) | 205 // 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 | 206 // plus the size of the tag count (4). We include the tag count since we |
| 197 // always require it to be present anyway. | 207 // always require it to be present anyway. |
| 198 static const size_t kICCHeaderSize = 132; | 208 static const size_t kICCHeaderSize = 132; |
| 199 | 209 |
| 200 // Contains a signature (4), offset (4), and size (4). | 210 // Contains a signature (4), offset (4), and size (4). |
| 201 static const size_t kICCTagTableEntrySize = 12; | 211 static const size_t kICCTagTableEntrySize = 12; |
| 202 | 212 |
| 203 static const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' '); | 213 static const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' '); |
| 214 static const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r'); | |
|
scroggo
2016/05/24 15:57:50
Now that we don't have to worry about VS 2013, sho
msarett
2016/05/24 18:38:06
Sure sounds good to me.
| |
| 215 static const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r'); | |
| 216 static const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r'); | |
| 217 static const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' '); | |
| 218 static const uint32_t kACSP_Signature = SkSetFourByteTag('a', 'c', 's', 'p'); | |
| 204 | 219 |
| 205 struct ICCProfileHeader { | 220 struct ICCProfileHeader { |
| 206 uint32_t fSize; | 221 uint32_t fSize; |
| 207 | 222 |
| 208 // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.). | 223 // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.). |
| 209 // We're always going to use this one. | 224 // We're always going to use this one. |
| 210 uint32_t fCMMType_ignored; | 225 uint32_t fCMMType_ignored; |
| 211 | 226 |
| 212 uint32_t fVersion; | 227 uint32_t fVersion; |
| 213 uint32_t fProfileClass; | 228 uint32_t fProfileClass; |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 259 | 274 |
| 260 bool valid() const { | 275 bool valid() const { |
| 261 return_if_false(fSize >= kICCHeaderSize, "Size is too small"); | 276 return_if_false(fSize >= kICCHeaderSize, "Size is too small"); |
| 262 | 277 |
| 263 uint8_t majorVersion = fVersion >> 24; | 278 uint8_t majorVersion = fVersion >> 24; |
| 264 return_if_false(majorVersion <= 4, "Unsupported version"); | 279 return_if_false(majorVersion <= 4, "Unsupported version"); |
| 265 | 280 |
| 266 // These are the three basic classes of profiles that we might expect to see embedded | 281 // 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 | 282 // in images. Four additional classes exist, but they generally are use d as a convenient |
| 268 // way for CMMs to store calculated transforms. | 283 // 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 || | 284 return_if_false(fProfileClass == kDisplay_Profile || |
| 273 fProfileClass == kInput_Profile || | 285 fProfileClass == kInput_Profile || |
| 274 fProfileClass == kOutput_Profile, | 286 fProfileClass == kOutput_Profile, |
| 275 "Unsupported profile"); | 287 "Unsupported profile"); |
| 276 | 288 |
| 277 // TODO (msarett): | 289 // TODO (msarett): |
| 278 // All the profiles we've tested so far use RGB as the input color space . | 290 // 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"); | 291 return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space"); |
| 280 | 292 |
| 281 // TODO (msarett): | 293 // TODO (msarett): |
| 282 // All the profiles we've tested so far use XYZ as the profile connectio n space. | 294 // 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"); | 295 return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space"); |
| 285 | 296 |
| 286 return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "Bad signature"); | 297 return_if_false(fSignature == kACSP_Signature, "Bad signature"); |
| 287 | 298 |
| 288 // TODO (msarett): | 299 // TODO (msarett): |
| 289 // Should we treat different rendering intents differently? | 300 // Should we treat different rendering intents differently? |
| 290 // Valid rendering intents include kPerceptual (0), kRelative (1), | 301 // Valid rendering intents include kPerceptual (0), kRelative (1), |
| 291 // kSaturation (2), and kAbsolute (3). | 302 // kSaturation (2), and kAbsolute (3). |
| 292 return_if_false(fRenderingIntent <= 3, "Bad rendering intent"); | 303 return_if_false(fRenderingIntent <= 3, "Bad rendering intent"); |
| 293 | 304 |
| 294 return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0 ]), 0.96420f) && | 305 return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0 ]), 0.96420f) && |
| 295 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1 ]), 1.00000f) && | 306 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1 ]), 1.00000f) && |
| 296 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2 ]), 0.82491f), | 307 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2 ]), 0.82491f), |
| (...skipping 401 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 698 uint32_t offsetToMatrix = read_big_endian_int(src + 16); | 709 uint32_t offsetToMatrix = read_big_endian_int(src + 16); |
| 699 if (0 != offsetToMatrix && offsetToMatrix < len) { | 710 if (0 != offsetToMatrix && offsetToMatrix < len) { |
| 700 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) { | 711 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) { |
| 701 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); | 712 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); |
| 702 } | 713 } |
| 703 } | 714 } |
| 704 | 715 |
| 705 return true; | 716 return true; |
| 706 } | 717 } |
| 707 | 718 |
| 708 sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* base, size_t len) { | 719 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) { | 720 if (len < kICCHeaderSize) { |
| 712 return_null("Data is not large enough to contain an ICC profile"); | 721 return_null("Data is not large enough to contain an ICC profile"); |
| 713 } | 722 } |
| 714 | 723 |
| 724 // Create our own copy of the input. | |
| 725 void* memory = sk_malloc_throw(len); | |
| 726 memcpy(memory, input, len); | |
| 727 sk_sp<SkData> data = SkData::MakeFromMalloc(memory, len); | |
| 728 const void* base = data->data(); | |
| 729 const uint8_t* ptr = (const uint8_t*) base; | |
| 730 | |
| 715 // Read the ICC profile header and check to make sure that it is valid. | 731 // Read the ICC profile header and check to make sure that it is valid. |
| 716 ICCProfileHeader header; | 732 ICCProfileHeader header; |
| 717 header.init(ptr, len); | 733 header.init(ptr, len); |
| 718 if (!header.valid()) { | 734 if (!header.valid()) { |
| 719 return nullptr; | 735 return nullptr; |
| 720 } | 736 } |
| 721 | 737 |
| 722 // Adjust ptr and len before reading the tags. | 738 // Adjust ptr and len before reading the tags. |
| 723 if (len < header.fSize) { | 739 if (len < header.fSize) { |
| 724 SkColorSpacePrintf("ICC profile might be truncated.\n"); | 740 SkColorSpacePrintf("ICC profile might be truncated.\n"); |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 786 std::move(curves[2]))); | 802 std::move(curves[2]))); |
| 787 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); | 803 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); |
| 788 mat.set3x3ColMajorf(toXYZ); | 804 mat.set3x3ColMajorf(toXYZ); |
| 789 if (gammas->isValues()) { | 805 if (gammas->isValues()) { |
| 790 // When we have values, take advantage of the NewFromRGB ini tializer. | 806 // When we have values, take advantage of the NewFromRGB ini tializer. |
| 791 // This allows us to check for canonical sRGB and Adobe RGB. | 807 // This allows us to check for canonical sRGB and Adobe RGB. |
| 792 float gammaVals[3]; | 808 float gammaVals[3]; |
| 793 gammaVals[0] = gammas->fRed.fValue; | 809 gammaVals[0] = gammas->fRed.fValue; |
| 794 gammaVals[1] = gammas->fGreen.fValue; | 810 gammaVals[1] = gammas->fGreen.fValue; |
| 795 gammaVals[2] = gammas->fBlue.fValue; | 811 gammaVals[2] = gammas->fBlue.fValue; |
| 796 return SkColorSpace::NewRGB(gammaVals, mat); | 812 return SkColorSpace_Base::NewRGB(gammaVals, mat, std::move(d ata)); |
| 797 } else { | 813 } else { |
| 798 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, mat , kUnknown_Named)); | 814 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, mat , kUnknown_Named, |
| 815 std::move(d ata))); | |
| 799 } | 816 } |
| 800 } | 817 } |
| 801 | 818 |
| 802 // Recognize color profile specified by A2B0 tag. | 819 // Recognize color profile specified by A2B0 tag. |
| 803 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); | 820 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); |
| 804 if (a2b0) { | 821 if (a2b0) { |
| 805 SkAutoTDelete<SkColorLookUpTable> colorLUT(new SkColorLookUpTabl e()); | 822 SkAutoTDelete<SkColorLookUpTable> colorLUT(new SkColorLookUpTabl e()); |
| 806 SkGammaCurve curves[3]; | 823 SkGammaCurve curves[3]; |
| 807 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); | 824 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); |
| 808 if (!load_a2b0(colorLUT, curves, &toXYZ, a2b0->addr((const uint8 _t*) base), | 825 if (!load_a2b0(colorLUT, curves, &toXYZ, a2b0->addr((const uint8 _t*) base), |
| 809 a2b0->fLength)) { | 826 a2b0->fLength)) { |
| 810 return_null("Failed to parse A2B0 tag"); | 827 return_null("Failed to parse A2B0 tag"); |
| 811 } | 828 } |
| 812 | 829 |
| 813 sk_sp<SkGammas> gammas(new SkGammas(std::move(curves[0]), std::m ove(curves[1]), | 830 sk_sp<SkGammas> gammas(new SkGammas(std::move(curves[0]), std::m ove(curves[1]), |
| 814 std::move(curves[2]))); | 831 std::move(curves[2]))); |
| 815 if (colorLUT->fTable) { | 832 if (colorLUT->fTable) { |
| 816 return sk_sp<SkColorSpace>(new SkColorSpace_Base(colorLUT.re lease(), gammas, | 833 return sk_sp<SkColorSpace>(new SkColorSpace_Base(colorLUT.re lease(), gammas, |
| 817 toXYZ)); | 834 toXYZ, std: :move(data))); |
| 818 } else if (gammas->isValues()) { | 835 } else if (gammas->isValues()) { |
| 819 // When we have values, take advantage of the NewFromRGB ini tializer. | 836 // When we have values, take advantage of the NewFromRGB ini tializer. |
| 820 // This allows us to check for canonical sRGB and Adobe RGB. | 837 // This allows us to check for canonical sRGB and Adobe RGB. |
| 821 float gammaVals[3]; | 838 float gammaVals[3]; |
| 822 gammaVals[0] = gammas->fRed.fValue; | 839 gammaVals[0] = gammas->fRed.fValue; |
| 823 gammaVals[1] = gammas->fGreen.fValue; | 840 gammaVals[1] = gammas->fGreen.fValue; |
| 824 gammaVals[2] = gammas->fBlue.fValue; | 841 gammaVals[2] = gammas->fBlue.fValue; |
| 825 return SkColorSpace::NewRGB(gammaVals, toXYZ); | 842 return SkColorSpace_Base::NewRGB(gammaVals, toXYZ, std::move (data)); |
| 826 } else { | 843 } else { |
| 827 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, toX YZ, | 844 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, toX YZ, |
| 828 kUnknown_Na med)); | 845 kUnknown_Na med, |
| 846 std::move(d ata))); | |
| 829 } | 847 } |
| 830 } | 848 } |
| 831 | 849 |
| 832 } | 850 } |
| 833 default: | 851 default: |
| 834 break; | 852 break; |
| 835 } | 853 } |
| 836 | 854 |
| 837 return_null("ICC profile contains unsupported colorspace"); | 855 return_null("ICC profile contains unsupported colorspace"); |
| 838 } | 856 } |
| 857 | |
| 858 //////////////////////////////////////////////////////////////////////////////// /////////////////// | |
| 859 | |
| 860 // We will write a profile with the minimum nine required tags. | |
| 861 static const uint32_t kICCNumEntries = 9; | |
| 862 | |
| 863 static const uint32_t kTAG_desc = SkSetFourByteTag('d', 'e', 's', 'c'); | |
| 864 static const uint32_t kTAG_desc_Bytes = 12; | |
| 865 static const uint32_t kTAG_desc_Offset = kICCHeaderSize + kICCNumEntries * kICCT agTableEntrySize; | |
| 866 | |
| 867 static const uint32_t kTAG_XYZ_Bytes = 20; | |
| 868 static const uint32_t kTAG_rXYZ_Offset = kTAG_desc_Offset + kTAG_desc_Bytes; | |
| 869 static const uint32_t kTAG_gXYZ_Offset = kTAG_rXYZ_Offset + kTAG_XYZ_Bytes; | |
| 870 static const uint32_t kTAG_bXYZ_Offset = kTAG_gXYZ_Offset + kTAG_XYZ_Bytes; | |
| 871 | |
| 872 static const uint32_t kTAG_TRC_Bytes = 14; | |
| 873 static const uint32_t kTAG_rTRC_Offset = kTAG_bXYZ_Offset + kTAG_XYZ_Bytes; | |
| 874 static const uint32_t kTAG_gTRC_Offset = kTAG_rTRC_Offset + SkAlign4(kTAG_TRC_By tes); | |
| 875 static const uint32_t kTAG_bTRC_Offset = kTAG_gTRC_Offset + SkAlign4(kTAG_TRC_By tes); | |
| 876 | |
| 877 static const uint32_t kTAG_wtpt = SkSetFourByteTag('w', 't', 'p', 't'); | |
| 878 static const uint32_t kTAG_wtpt_Offset = kTAG_bTRC_Offset + SkAlign4(kTAG_TRC_By tes); | |
| 879 | |
| 880 static const uint32_t kTAG_cprt = SkSetFourByteTag('c', 'p', 'r', 't'); | |
| 881 static const uint32_t kTAG_cprt_Bytes = 12; | |
| 882 static const uint32_t kTAG_cprt_Offset = kTAG_wtpt_Offset + kTAG_XYZ_Bytes; | |
| 883 | |
| 884 static const uint32_t kICCProfileSize = kTAG_cprt_Offset + kTAG_cprt_Bytes; | |
| 885 | |
| 886 static const uint32_t gICCHeader[kICCHeaderSize / 4] { | |
| 887 SkEndian_SwapBE32(kICCProfileSize), // Size of the profile | |
| 888 0, // Preferred CMM type (ignored) | |
| 889 SkEndian_SwapBE32(0x02100000), // Version 2.1 | |
| 890 SkEndian_SwapBE32(kDisplay_Profile), // Display device profile | |
| 891 SkEndian_SwapBE32(kRGB_ColorSpace), // RGB input color space | |
| 892 SkEndian_SwapBE32(kXYZ_PCSSpace), // XYZ profile connection space | |
| 893 0, 0, 0, // Date and time (ignored) | |
| 894 SkEndian_SwapBE32(kACSP_Signature), // Profile signature | |
| 895 0, // Platform target (ignored) | |
| 896 0x00000000, // Flags: not embedded, can be used ind ependently | |
| 897 0, // Device manufacturer (ignored) | |
| 898 0, // Device model (ignored) | |
| 899 0, 0, // Device attributes (ignored) | |
| 900 SkEndian_SwapBE32(1), // Relative colorimetric rendering inte nt | |
| 901 SkEndian_SwapBE32(0x0000f6d6), // D50 standard illuminant (X) | |
| 902 SkEndian_SwapBE32(0x00010000), // D50 standard illuminant (Y) | |
| 903 SkEndian_SwapBE32(0x0000d32d), // D50 standard illuminant (Z) | |
| 904 0, // Profile creator (ignored) | |
| 905 0, 0, 0, 0, // Profile id checksum (ignored) | |
| 906 0, 0, 0, 0, 0, 0, 0, // Reserved (ignored) | |
| 907 SkEndian_SwapBE32(kICCNumEntries), // Number of tags | |
| 908 }; | |
| 909 | |
| 910 static const uint32_t gICCTagTable[3 * kICCNumEntries] { | |
| 911 // Profile description | |
| 912 SkEndian_SwapBE32(kTAG_desc), | |
| 913 SkEndian_SwapBE32(kTAG_desc_Offset), | |
| 914 SkEndian_SwapBE32(kTAG_desc_Bytes), | |
| 915 | |
| 916 // rXYZ | |
| 917 SkEndian_SwapBE32(kTAG_rXYZ), | |
| 918 SkEndian_SwapBE32(kTAG_rXYZ_Offset), | |
| 919 SkEndian_SwapBE32(kTAG_XYZ_Bytes), | |
| 920 | |
| 921 // gXYZ | |
| 922 SkEndian_SwapBE32(kTAG_gXYZ), | |
| 923 SkEndian_SwapBE32(kTAG_gXYZ_Offset), | |
| 924 SkEndian_SwapBE32(kTAG_XYZ_Bytes), | |
| 925 | |
| 926 // bXYZ | |
| 927 SkEndian_SwapBE32(kTAG_bXYZ), | |
| 928 SkEndian_SwapBE32(kTAG_bXYZ_Offset), | |
| 929 SkEndian_SwapBE32(kTAG_XYZ_Bytes), | |
| 930 | |
| 931 // rTRC | |
| 932 SkEndian_SwapBE32(kTAG_rTRC), | |
| 933 SkEndian_SwapBE32(kTAG_rTRC_Offset), | |
| 934 SkEndian_SwapBE32(kTAG_TRC_Bytes), | |
| 935 | |
| 936 // gTRC | |
| 937 SkEndian_SwapBE32(kTAG_gTRC), | |
| 938 SkEndian_SwapBE32(kTAG_gTRC_Offset), | |
| 939 SkEndian_SwapBE32(kTAG_TRC_Bytes), | |
| 940 | |
| 941 // bTRC | |
| 942 SkEndian_SwapBE32(kTAG_bTRC), | |
| 943 SkEndian_SwapBE32(kTAG_bTRC_Offset), | |
| 944 SkEndian_SwapBE32(kTAG_TRC_Bytes), | |
| 945 | |
| 946 // White point | |
| 947 SkEndian_SwapBE32(kTAG_wtpt), | |
| 948 SkEndian_SwapBE32(kTAG_wtpt_Offset), | |
| 949 SkEndian_SwapBE32(kTAG_XYZ_Bytes), | |
| 950 | |
| 951 // Copyright | |
| 952 SkEndian_SwapBE32(kTAG_cprt), | |
| 953 SkEndian_SwapBE32(kTAG_cprt_Offset), | |
| 954 SkEndian_SwapBE32(kTAG_cprt_Bytes), | |
| 955 }; | |
| 956 | |
| 957 static const uint32_t kTAG_TextType = SkSetFourByteTag('m', 'l', 'u', 'c'); | |
| 958 static const uint32_t gEmptyTextTag[3] { | |
| 959 SkEndian_SwapBE32(kTAG_TextType), // Type signature | |
| 960 0, // Reserved | |
| 961 0, // Zero records | |
| 962 }; | |
| 963 | |
| 964 static void write_xyz_tag(uint32_t* ptr, const SkMatrix44& toXYZ, int row) { | |
| 965 ptr[0] = SkEndian_SwapBE32(kXYZ_PCSSpace); | |
| 966 ptr[1] = 0; | |
| 967 ptr[2] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 0))); | |
| 968 ptr[3] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 1))); | |
| 969 ptr[4] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 2))); | |
| 970 } | |
| 971 | |
| 972 static void write_trc_tag(uint32_t* ptr, float value) { | |
| 973 ptr[0] = SkEndian_SwapBE32(kTAG_CurveType); | |
| 974 ptr[1] = 0; | |
| 975 | |
| 976 // Gamma will be specified with a single value. | |
| 977 ptr[2] = SkEndian_SwapBE32(1); | |
| 978 | |
| 979 // Convert gamma to 16-bit fixed point. | |
| 980 uint16_t* ptr16 = (uint16_t*) (ptr + 3); | |
| 981 ptr16[0] = SkEndian_SwapBE16((uint16_t) (value * 256.0f)); | |
| 982 | |
| 983 // Pad tag with zero. | |
| 984 ptr16[1] = 0; | |
| 985 } | |
| 986 | |
| 987 sk_sp<SkData> SkColorSpace::writeToICC() { | |
| 988 // Return if this object was created from a profile, or if we have already s erialized | |
| 989 // the profile. | |
| 990 if (fProfileData) { | |
| 991 return fProfileData; | |
| 992 } | |
| 993 | |
| 994 // The client may create an SkColorSpace using an SkMatrix44, but currently we only | |
| 995 // support writing profiles with 3x3 matrices. | |
| 996 // TODO (msarett): Fix this! | |
| 997 if (0.0f != fToXYZD50.getFloat(3, 0) || 0.0f != fToXYZD50.getFloat(3, 1) || | |
| 998 0.0f != fToXYZD50.getFloat(3, 2) || 0.0f != fToXYZD50.getFloat(0, 3) || | |
| 999 0.0f != fToXYZD50.getFloat(1, 3) || 0.0f != fToXYZD50.getFloat(2, 3)) | |
| 1000 { | |
| 1001 return nullptr; | |
| 1002 } | |
| 1003 | |
| 1004 void* profile = sk_malloc_throw(kICCProfileSize); | |
|
scroggo
2016/05/24 15:57:50
Should this be in an auto deleter of some kind?
msarett
2016/05/24 18:38:06
Yeah I think that's probably safer, though at the
| |
| 1005 uint8_t* ptr = (uint8_t*) profile; | |
| 1006 | |
| 1007 // Write profile header | |
| 1008 memcpy(ptr, gICCHeader, sizeof(gICCHeader)); | |
| 1009 ptr += sizeof(gICCHeader); | |
| 1010 | |
| 1011 // Write tag table | |
| 1012 memcpy(ptr, gICCTagTable, sizeof(gICCTagTable)); | |
| 1013 ptr += sizeof(gICCTagTable); | |
| 1014 | |
| 1015 // Write profile description tag | |
| 1016 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); | |
| 1017 ptr += sizeof(gEmptyTextTag); | |
| 1018 | |
| 1019 // Write XYZ tags | |
| 1020 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 0); | |
| 1021 ptr += kTAG_XYZ_Bytes; | |
| 1022 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 1); | |
| 1023 ptr += kTAG_XYZ_Bytes; | |
| 1024 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 2); | |
| 1025 ptr += kTAG_XYZ_Bytes; | |
| 1026 | |
| 1027 // Write TRC tags | |
| 1028 SkASSERT(as_CSB(this)->fGammas->fRed.isValue()); | |
| 1029 write_trc_tag((uint32_t*) ptr, as_CSB(this)->fGammas->fRed.fValue); | |
| 1030 ptr += SkAlign4(kTAG_TRC_Bytes); | |
| 1031 SkASSERT(as_CSB(this)->fGammas->fGreen.isValue()); | |
| 1032 write_trc_tag((uint32_t*) ptr, as_CSB(this)->fGammas->fGreen.fValue); | |
| 1033 ptr += SkAlign4(kTAG_TRC_Bytes); | |
| 1034 SkASSERT(as_CSB(this)->fGammas->fBlue.isValue()); | |
| 1035 write_trc_tag((uint32_t*) ptr, as_CSB(this)->fGammas->fBlue.fValue); | |
| 1036 ptr += SkAlign4(kTAG_TRC_Bytes); | |
| 1037 | |
| 1038 // Write white point tag | |
| 1039 uint32_t* ptr32 = (uint32_t*) ptr; | |
| 1040 ptr32[0] = SkEndian_SwapBE32(kXYZ_PCSSpace); | |
| 1041 ptr32[1] = 0; | |
| 1042 // TODO (msarett): These values correspond to the D65 white point. This may not always be | |
| 1043 // correct. | |
| 1044 ptr32[2] = SkEndian_SwapBE32(0x0000f351); | |
| 1045 ptr32[3] = SkEndian_SwapBE32(0x00010000); | |
| 1046 ptr32[4] = SkEndian_SwapBE32(0x000116cc); | |
| 1047 ptr += kTAG_XYZ_Bytes; | |
| 1048 | |
| 1049 // Write copyright tag | |
| 1050 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); | |
| 1051 | |
| 1052 // We choose to hold onto the profile here, in case we need to serialize aga in. | |
|
msarett
2016/05/24 15:36:58
I'm not sure if this is the right decision...
scroggo
2016/05/24 15:57:50
Add a comment? (Or leave it out for now?)
msarett
2016/05/24 18:38:06
Leaving it out for now... That allows this functi
| |
| 1053 fProfileData = std::move(SkData::MakeFromMalloc(profile, kICCProfileSize)); | |
| 1054 return fProfileData; | |
| 1055 } | |
| OLD | NEW |