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

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

Issue 2001203003: Write ICC profiles from SkColorSpace object (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: std::move Created 4 years, 7 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
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 "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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698