OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 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 "SkBitmap.h" | 8 #include "SkBitmap.h" |
9 #include "SkCodecPriv.h" | 9 #include "SkCodecPriv.h" |
10 #include "SkColorPriv.h" | 10 #include "SkColorPriv.h" |
11 #include "SkColorSpace_Base.h" | 11 #include "SkColorSpace_Base.h" |
12 #include "SkColorTable.h" | 12 #include "SkColorTable.h" |
13 #include "SkMath.h" | 13 #include "SkMath.h" |
14 #include "SkOpts.h" | 14 #include "SkOpts.h" |
15 #include "SkPngCodec.h" | 15 #include "SkPngCodec.h" |
| 16 #include "SkPoint3.h" |
16 #include "SkSize.h" | 17 #include "SkSize.h" |
17 #include "SkStream.h" | 18 #include "SkStream.h" |
18 #include "SkSwizzler.h" | 19 #include "SkSwizzler.h" |
19 #include "SkTemplates.h" | 20 #include "SkTemplates.h" |
20 #include "SkUtils.h" | 21 #include "SkUtils.h" |
21 | 22 |
22 /////////////////////////////////////////////////////////////////////////////// | 23 /////////////////////////////////////////////////////////////////////////////// |
23 // Callback functions | 24 // Callback functions |
24 /////////////////////////////////////////////////////////////////////////////// | 25 /////////////////////////////////////////////////////////////////////////////// |
25 | 26 |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
171 // This is necessary because the gAMA chunk actually stores 1/gamma. | 172 // This is necessary because the gAMA chunk actually stores 1/gamma. |
172 return 1.0f / png_fixed_point_to_float(x); | 173 return 1.0f / png_fixed_point_to_float(x); |
173 } | 174 } |
174 | 175 |
175 static constexpr float gSRGB_toXYZD50[] { | 176 static constexpr float gSRGB_toXYZD50[] { |
176 0.4358f, 0.2224f, 0.0139f, // * R | 177 0.4358f, 0.2224f, 0.0139f, // * R |
177 0.3853f, 0.7170f, 0.0971f, // * G | 178 0.3853f, 0.7170f, 0.0971f, // * G |
178 0.1430f, 0.0606f, 0.7139f, // * B | 179 0.1430f, 0.0606f, 0.7139f, // * B |
179 }; | 180 }; |
180 | 181 |
| 182 static bool convert_to_D50(SkMatrix44* toXYZD50, float toXYZ[9], float whitePoin
t[2]) { |
| 183 float wX = whitePoint[0]; |
| 184 float wY = whitePoint[1]; |
| 185 if (wX < 0.0f || wY < 0.0f || (wX + wY > 1.0f)) { |
| 186 return false; |
| 187 } |
| 188 |
| 189 // Calculate the XYZ illuminant. Call this the src illuminant. |
| 190 float wZ = 1.0f - wX - wY; |
| 191 float scale = 1.0f / wY; |
| 192 // TODO (msarett): |
| 193 // What are common src illuminants? I'm guessing we will almost always see
D65. Should |
| 194 // we go ahead and save a precomputed D65->D50 Bradford matrix? Should we e
xit early if |
| 195 // if the src illuminant is D50? |
| 196 SkVector3 srcXYZ = SkVector3::Make(wX * scale, 1.0f, wZ * scale); |
| 197 |
| 198 // The D50 illuminant. |
| 199 SkVector3 dstXYZ = SkVector3::Make(0.96422f, 1.0f, 0.82521f); |
| 200 |
| 201 // Calculate the chromatic adaptation matrix. We will use the Bradford meth
od, thus |
| 202 // the matrices below. The Bradford method is used by Adobe and is widely c
onsidered |
| 203 // to be the best. |
| 204 // http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html |
| 205 SkMatrix mA, mAInv; |
| 206 mA.setAll(0.8951f, 0.2664f, -0.1614f, -0.7502f, 1.7135f, 0.0367f, 0.0389f, -
0.0685f, 1.0296f); |
| 207 mAInv.setAll(0.9869929f, -0.1470543f, 0.1599627f, 0.4323053f, 0.5183603f, 0.
0492912f, |
| 208 -0.0085287f, 0.0400428f, 0.9684867f); |
| 209 |
| 210 // Map illuminant into cone response domain. |
| 211 SkVector3 srcCone; |
| 212 srcCone.fX = mA[0] * srcXYZ.fX + mA[1] * srcXYZ.fY + mA[2] * srcXYZ.fZ; |
| 213 srcCone.fY = mA[3] * srcXYZ.fX + mA[4] * srcXYZ.fY + mA[5] * srcXYZ.fZ; |
| 214 srcCone.fZ = mA[6] * srcXYZ.fX + mA[7] * srcXYZ.fY + mA[8] * srcXYZ.fZ; |
| 215 SkVector3 dstCone; |
| 216 dstCone.fX = mA[0] * dstXYZ.fX + mA[1] * dstXYZ.fY + mA[2] * dstXYZ.fZ; |
| 217 dstCone.fY = mA[3] * dstXYZ.fX + mA[4] * dstXYZ.fY + mA[5] * dstXYZ.fZ; |
| 218 dstCone.fZ = mA[6] * dstXYZ.fX + mA[7] * dstXYZ.fY + mA[8] * dstXYZ.fZ; |
| 219 |
| 220 SkMatrix DXToD50; |
| 221 DXToD50.setIdentity(); |
| 222 DXToD50[0] = dstCone.fX / srcCone.fX; |
| 223 DXToD50[4] = dstCone.fY / srcCone.fY; |
| 224 DXToD50[8] = dstCone.fZ / srcCone.fZ; |
| 225 DXToD50.postConcat(mAInv); |
| 226 DXToD50.preConcat(mA); |
| 227 |
| 228 SkMatrix toXYZ3x3; |
| 229 toXYZ3x3.setAll(toXYZ[0], toXYZ[3], toXYZ[6], toXYZ[1], toXYZ[4], toXYZ[7],
toXYZ[2], toXYZ[5], |
| 230 toXYZ[8]); |
| 231 toXYZ3x3.postConcat(DXToD50); |
| 232 |
| 233 toXYZD50->set3x3(toXYZ3x3[0], toXYZ3x3[1], toXYZ3x3[2], toXYZ3x3[3], toXYZ3x
3[4], toXYZ3x3[5], |
| 234 toXYZ3x3[6], toXYZ3x3[7], toXYZ3x3[8]); |
| 235 return true; |
| 236 } |
| 237 |
181 // Returns a colorSpace object that represents any color space information in | 238 // Returns a colorSpace object that represents any color space information in |
182 // the encoded data. If the encoded data contains no color space, this will | 239 // the encoded data. If the encoded data contains no color space, this will |
183 // return NULL. | 240 // return NULL. |
184 sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) { | 241 sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) { |
185 | 242 |
186 #if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_M
INOR >= 6) | 243 #if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_M
INOR >= 6) |
187 | 244 |
188 // First check for an ICC profile | 245 // First check for an ICC profile |
189 png_bytep profile; | 246 png_bytep profile; |
190 png_uint_32 length; | 247 png_uint_32 length; |
(...skipping 15 matching lines...) Expand all Loading... |
206 | 263 |
207 // sRGB chunks also store a rendering intent: Absolute, Relative, | 264 // sRGB chunks also store a rendering intent: Absolute, Relative, |
208 // Perceptual, and Saturation. | 265 // Perceptual, and Saturation. |
209 // FIXME (msarett): Extract this information from the sRGB chunk once | 266 // FIXME (msarett): Extract this information from the sRGB chunk once |
210 // we are able to handle this information in | 267 // we are able to handle this information in |
211 // SkColorSpace. | 268 // SkColorSpace. |
212 return SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); | 269 return SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); |
213 } | 270 } |
214 | 271 |
215 // Next, check for chromaticities. | 272 // Next, check for chromaticities. |
216 png_fixed_point XYZ[9]; | 273 png_fixed_point toXYZFixed[9]; |
217 float toXYZD50[9]; | 274 float toXYZ[9]; |
| 275 png_fixed_point whitePointFixed[2]; |
| 276 float whitePoint[2]; |
218 png_fixed_point gamma; | 277 png_fixed_point gamma; |
219 float gammas[3]; | 278 float gammas[3]; |
220 if (png_get_cHRM_XYZ_fixed(png_ptr, info_ptr, &XYZ[0], &XYZ[1], &XYZ[2], &XY
Z[3], &XYZ[4], | 279 if (png_get_cHRM_XYZ_fixed(png_ptr, info_ptr, &toXYZFixed[0], &toXYZFixed[1]
, &toXYZFixed[2], |
221 &XYZ[5], &XYZ[6], &XYZ[7], &XYZ[8])) { | 280 &toXYZFixed[3], &toXYZFixed[4], &toXYZFixed[5], &
toXYZFixed[6], |
| 281 &toXYZFixed[7], &toXYZFixed[8]) && |
| 282 png_get_cHRM_fixed(png_ptr, info_ptr, &whitePointFixed[0], &whitePointFi
xed[1], nullptr, |
| 283 nullptr, nullptr, nullptr, nullptr, nullptr)) |
| 284 { |
| 285 for (int i = 0; i < 9; i++) { |
| 286 toXYZ[i] = png_fixed_point_to_float(toXYZFixed[i]); |
| 287 } |
| 288 whitePoint[0] = png_fixed_point_to_float(whitePointFixed[0]); |
| 289 whitePoint[1] = png_fixed_point_to_float(whitePointFixed[1]); |
222 | 290 |
223 // FIXME (msarett): Here we are treating XYZ values as D50 even though t
he color | 291 SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor); |
224 // temperature is unspecified. I suspect that this ass
umption | 292 if (!convert_to_D50(&toXYZD50, toXYZ, whitePoint)) { |
225 // is most often ok, but we could also calculate the co
lor | 293 toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50); |
226 // temperature (D value) and then convert the XYZ to D5
0. Maybe | |
227 // we should add a new constructor to SkColorSpace that
accepts | |
228 // XYZ with D-Unkown? | |
229 for (int i = 0; i < 9; i++) { | |
230 toXYZD50[i] = png_fixed_point_to_float(XYZ[i]); | |
231 } | 294 } |
232 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); | |
233 mat.set3x3RowMajorf(toXYZD50); | |
234 | 295 |
235 if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) { | 296 if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) { |
236 float value = png_inverted_fixed_point_to_float(gamma); | 297 float value = png_inverted_fixed_point_to_float(gamma); |
237 gammas[0] = value; | 298 gammas[0] = value; |
238 gammas[1] = value; | 299 gammas[1] = value; |
239 gammas[2] = value; | 300 gammas[2] = value; |
240 | 301 |
241 return SkColorSpace_Base::NewRGB(gammas, mat); | 302 return SkColorSpace_Base::NewRGB(gammas, toXYZD50); |
242 } | 303 } |
243 | 304 |
244 // Default to sRGB gamma if the image has color space information, | 305 // Default to sRGB gamma if the image has color space information, |
245 // but does not specify gamma. | 306 // but does not specify gamma. |
246 return SkColorSpace::NewRGB(SkColorSpace::kSRGB_GammaNamed, mat); | 307 return SkColorSpace::NewRGB(SkColorSpace::kSRGB_GammaNamed, toXYZD50); |
247 } | 308 } |
248 | 309 |
249 // Last, check for gamma. | 310 // Last, check for gamma. |
250 if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) { | 311 if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) { |
251 | 312 |
252 // Set the gammas. | 313 // Set the gammas. |
253 float value = png_inverted_fixed_point_to_float(gamma); | 314 float value = png_inverted_fixed_point_to_float(gamma); |
254 gammas[0] = value; | 315 gammas[0] = value; |
255 gammas[1] = value; | 316 gammas[1] = value; |
256 gammas[2] = value; | 317 gammas[2] = value; |
257 | 318 |
258 // Since there is no cHRM, we will guess sRGB gamut. | 319 // Since there is no cHRM, we will guess sRGB gamut. |
259 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); | 320 SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor); |
260 mat.set3x3RowMajorf(gSRGB_toXYZD50); | 321 toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50); |
261 | 322 |
262 return SkColorSpace_Base::NewRGB(gammas, mat); | 323 return SkColorSpace_Base::NewRGB(gammas, toXYZD50); |
263 } | 324 } |
264 | 325 |
265 #endif // LIBPNG >= 1.6 | 326 #endif // LIBPNG >= 1.6 |
266 | 327 |
267 // Report that there is no color space information in the PNG. SkPngCodec i
s currently | 328 // Report that there is no color space information in the PNG. SkPngCodec i
s currently |
268 // implemented to guess sRGB in this case. | 329 // implemented to guess sRGB in this case. |
269 return nullptr; | 330 return nullptr; |
270 } | 331 } |
271 | 332 |
272 static int bytes_per_pixel(int bitsPerPixel) { | 333 static int bytes_per_pixel(int bitsPerPixel) { |
(...skipping 536 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
809 SkCodec* outCodec; | 870 SkCodec* outCodec; |
810 if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) { | 871 if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) { |
811 // Codec has taken ownership of the stream. | 872 // Codec has taken ownership of the stream. |
812 SkASSERT(outCodec); | 873 SkASSERT(outCodec); |
813 streamDeleter.release(); | 874 streamDeleter.release(); |
814 return outCodec; | 875 return outCodec; |
815 } | 876 } |
816 | 877 |
817 return nullptr; | 878 return nullptr; |
818 } | 879 } |
OLD | NEW |