OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2006 Apple Computer, Inc. | 2 * Copyright (C) 2006 Apple Computer, Inc. |
3 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. | 3 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. |
4 * | 4 * |
5 * Portions are Copyright (C) 2001 mozilla.org | 5 * Portions are Copyright (C) 2001 mozilla.org |
6 * | 6 * |
7 * Other contributors: | 7 * Other contributors: |
8 * Stuart Parmenter <stuart@mozilla.com> | 8 * Stuart Parmenter <stuart@mozilla.com> |
9 * | 9 * |
10 * This library is free software; you can redistribute it and/or | 10 * This library is free software; you can redistribute it and/or |
(...skipping 27 matching lines...) Expand all Loading... | |
38 | 38 |
39 #include "platform/image-decoders/png/PNGImageDecoder.h" | 39 #include "platform/image-decoders/png/PNGImageDecoder.h" |
40 | 40 |
41 #include "png.h" | 41 #include "png.h" |
42 #include "wtf/PtrUtil.h" | 42 #include "wtf/PtrUtil.h" |
43 #include <memory> | 43 #include <memory> |
44 | 44 |
45 #if !defined(PNG_LIBPNG_VER_MAJOR) || !defined(PNG_LIBPNG_VER_MINOR) | 45 #if !defined(PNG_LIBPNG_VER_MAJOR) || !defined(PNG_LIBPNG_VER_MINOR) |
46 #error version error: compile against a versioned libpng. | 46 #error version error: compile against a versioned libpng. |
47 #endif | 47 #endif |
48 #if USE(QCMSLIB) | |
49 #include "qcms.h" | |
50 #endif | |
51 | 48 |
52 #if PNG_LIBPNG_VER_MAJOR > 1 || \ | 49 #if PNG_LIBPNG_VER_MAJOR > 1 || \ |
53 (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4) | 50 (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4) |
54 #define JMPBUF(png_ptr) png_jmpbuf(png_ptr) | 51 #define JMPBUF(png_ptr) png_jmpbuf(png_ptr) |
55 #else | 52 #else |
56 #define JMPBUF(png_ptr) png_ptr->jmpbuf | 53 #define JMPBUF(png_ptr) png_ptr->jmpbuf |
57 #endif | 54 #endif |
58 | 55 |
59 namespace { | 56 namespace { |
60 | 57 |
(...skipping 28 matching lines...) Expand all Loading... | |
89 USING_FAST_MALLOC(PNGImageReader); | 86 USING_FAST_MALLOC(PNGImageReader); |
90 WTF_MAKE_NONCOPYABLE(PNGImageReader); | 87 WTF_MAKE_NONCOPYABLE(PNGImageReader); |
91 | 88 |
92 public: | 89 public: |
93 PNGImageReader(PNGImageDecoder* decoder, size_t readOffset) | 90 PNGImageReader(PNGImageDecoder* decoder, size_t readOffset) |
94 : m_decoder(decoder), | 91 : m_decoder(decoder), |
95 m_readOffset(readOffset), | 92 m_readOffset(readOffset), |
96 m_currentBufferSize(0), | 93 m_currentBufferSize(0), |
97 m_decodingSizeOnly(false), | 94 m_decodingSizeOnly(false), |
98 m_hasAlpha(false) | 95 m_hasAlpha(false) |
99 #if USE(QCMSLIB) | |
100 , | |
101 m_rowBuffer() | |
102 #endif | |
103 { | 96 { |
104 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); | 97 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); |
105 m_info = png_create_info_struct(m_png); | 98 m_info = png_create_info_struct(m_png); |
106 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, | 99 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, |
107 pngRowAvailable, pngComplete); | 100 pngRowAvailable, pngComplete); |
108 } | 101 } |
109 | 102 |
110 ~PNGImageReader() { | 103 ~PNGImageReader() { |
111 png_destroy_read_struct(m_png ? &m_png : 0, m_info ? &m_info : 0, 0); | 104 png_destroy_read_struct(m_png ? &m_png : 0, m_info ? &m_info : 0, 0); |
112 ASSERT(!m_png && !m_info); | 105 ASSERT(!m_png && !m_info); |
(...skipping 30 matching lines...) Expand all Loading... | |
143 void setReadOffset(size_t offset) { m_readOffset = offset; } | 136 void setReadOffset(size_t offset) { m_readOffset = offset; } |
144 size_t currentBufferSize() const { return m_currentBufferSize; } | 137 size_t currentBufferSize() const { return m_currentBufferSize; } |
145 bool decodingSizeOnly() const { return m_decodingSizeOnly; } | 138 bool decodingSizeOnly() const { return m_decodingSizeOnly; } |
146 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; } | 139 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; } |
147 bool hasAlpha() const { return m_hasAlpha; } | 140 bool hasAlpha() const { return m_hasAlpha; } |
148 | 141 |
149 png_bytep interlaceBuffer() const { return m_interlaceBuffer.get(); } | 142 png_bytep interlaceBuffer() const { return m_interlaceBuffer.get(); } |
150 void createInterlaceBuffer(int size) { | 143 void createInterlaceBuffer(int size) { |
151 m_interlaceBuffer = wrapArrayUnique(new png_byte[size]); | 144 m_interlaceBuffer = wrapArrayUnique(new png_byte[size]); |
152 } | 145 } |
153 #if USE(QCMSLIB) | |
154 png_bytep rowBuffer() const { return m_rowBuffer.get(); } | |
155 void createRowBuffer(int size) { | |
156 m_rowBuffer = wrapArrayUnique(new png_byte[size]); | |
157 } | |
158 #endif | |
159 | 146 |
160 private: | 147 private: |
161 png_structp m_png; | 148 png_structp m_png; |
162 png_infop m_info; | 149 png_infop m_info; |
163 PNGImageDecoder* m_decoder; | 150 PNGImageDecoder* m_decoder; |
164 size_t m_readOffset; | 151 size_t m_readOffset; |
165 size_t m_currentBufferSize; | 152 size_t m_currentBufferSize; |
166 bool m_decodingSizeOnly; | 153 bool m_decodingSizeOnly; |
167 bool m_hasAlpha; | 154 bool m_hasAlpha; |
168 std::unique_ptr<png_byte[]> m_interlaceBuffer; | 155 std::unique_ptr<png_byte[]> m_interlaceBuffer; |
169 #if USE(QCMSLIB) | |
170 std::unique_ptr<png_byte[]> m_rowBuffer; | |
171 #endif | |
172 }; | 156 }; |
173 | 157 |
174 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, | 158 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, |
175 GammaAndColorProfileOption colorOptions, | 159 GammaAndColorProfileOption colorOptions, |
176 size_t maxDecodedBytes, | 160 size_t maxDecodedBytes, |
177 size_t offset) | 161 size_t offset) |
178 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes), | 162 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes), |
179 m_offset(offset) {} | 163 m_offset(offset) {} |
180 | 164 |
181 PNGImageDecoder::~PNGImageDecoder() {} | 165 PNGImageDecoder::~PNGImageDecoder() {} |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
224 colorType == PNG_COLOR_TYPE_GRAY_ALPHA) | 208 colorType == PNG_COLOR_TYPE_GRAY_ALPHA) |
225 png_set_gray_to_rgb(png); | 209 png_set_gray_to_rgb(png); |
226 | 210 |
227 if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) { | 211 if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) { |
228 // We only support color profiles for color PALETTE and RGB[A] PNG. | 212 // We only support color profiles for color PALETTE and RGB[A] PNG. |
229 // Supporting color profiles for gray-scale images is slightly tricky, at | 213 // Supporting color profiles for gray-scale images is slightly tricky, at |
230 // least using the CoreGraphics ICC library, because we expand gray-scale | 214 // least using the CoreGraphics ICC library, because we expand gray-scale |
231 // images to RGB but we do not similarly transform the color profile. We'd | 215 // images to RGB but we do not similarly transform the color profile. We'd |
232 // either need to transform the color profile or we'd need to decode into a | 216 // either need to transform the color profile or we'd need to decode into a |
233 // gray-scale image buffer and hand that to CoreGraphics. | 217 // gray-scale image buffer and hand that to CoreGraphics. |
234 bool imageHasAlpha = (colorType & PNG_COLOR_MASK_ALPHA) || trnsCount; | |
235 #ifdef PNG_iCCP_SUPPORTED | 218 #ifdef PNG_iCCP_SUPPORTED |
236 if (png_get_valid(png, info, PNG_INFO_sRGB)) { | 219 if (png_get_valid(png, info, PNG_INFO_sRGB)) { |
237 setColorProfileAndComputeTransform(nullptr, 0, imageHasAlpha, | 220 setColorSpaceAndComputeTransform(nullptr, 0, true /* useSRGB */); |
238 true /* useSRGB */); | |
239 } else { | 221 } else { |
240 char* profileName = nullptr; | 222 char* profileName = nullptr; |
241 int compressionType = 0; | 223 int compressionType = 0; |
242 #if (PNG_LIBPNG_VER < 10500) | 224 #if (PNG_LIBPNG_VER < 10500) |
243 png_charp profile = nullptr; | 225 png_charp profile = nullptr; |
244 #else | 226 #else |
245 png_bytep profile = nullptr; | 227 png_bytep profile = nullptr; |
246 #endif | 228 #endif |
247 png_uint_32 profileLength = 0; | 229 png_uint_32 profileLength = 0; |
248 if (png_get_iCCP(png, info, &profileName, &compressionType, &profile, | 230 if (png_get_iCCP(png, info, &profileName, &compressionType, &profile, |
249 &profileLength)) { | 231 &profileLength)) { |
250 setColorProfileAndComputeTransform(reinterpret_cast<char*>(profile), | 232 setColorSpaceAndComputeTransform(reinterpret_cast<char*>(profile), |
251 profileLength, imageHasAlpha, | 233 profileLength, false /* useSRGB */); |
252 false /* useSRGB */); | |
253 } | 234 } |
254 } | 235 } |
255 #endif // PNG_iCCP_SUPPORTED | 236 #endif // PNG_iCCP_SUPPORTED |
256 } | 237 } |
257 | 238 |
258 if (!hasColorProfile()) { | 239 if (!hasColorProfile()) { |
259 // Deal with gamma and keep it under our control. | 240 // FIXME: |
scroggo_chromium
2016/10/19 18:26:25
nit: I think this should be:
TODO (msarett):
Sam
msarett
2016/10/19 19:55:43
Done.
| |
241 // Applying the transfer function (gamma) should be handled by | |
242 // SkColorSpaceXform. Here we always convert to a transfer function that | |
243 // is a 2.2 exponential. This is a little strange given that the dst | |
244 // transfer function is not necessarily a 2.2 exponential. | |
245 // FIXME: | |
246 // Often, PNGs that specify their transfer function with the gAMA tag will | |
247 // also specify their gamut with the cHRM tag. We should read this tag | |
248 // and do a full color space transformation if it is present. | |
260 const double inverseGamma = 0.45455; | 249 const double inverseGamma = 0.45455; |
261 const double defaultGamma = 2.2; | 250 const double defaultGamma = 2.2; |
262 double gamma; | 251 double gamma; |
263 if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { | 252 if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { |
264 const double maxGamma = 21474.83; | 253 const double maxGamma = 21474.83; |
265 if ((gamma <= 0.0) || (gamma > maxGamma)) { | 254 if ((gamma <= 0.0) || (gamma > maxGamma)) { |
266 gamma = inverseGamma; | 255 gamma = inverseGamma; |
267 png_set_gAMA(png, info, gamma); | 256 png_set_gAMA(png, info, gamma); |
268 } | 257 } |
269 png_set_gamma(png, defaultGamma, gamma); | 258 png_set_gamma(png, defaultGamma, gamma); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
317 if (PNG_INTERLACE_ADAM7 == | 306 if (PNG_INTERLACE_ADAM7 == |
318 png_get_interlace_type(png, m_reader->infoPtr())) { | 307 png_get_interlace_type(png, m_reader->infoPtr())) { |
319 m_reader->createInterlaceBuffer(colorChannels * size().width() * | 308 m_reader->createInterlaceBuffer(colorChannels * size().width() * |
320 size().height()); | 309 size().height()); |
321 if (!m_reader->interlaceBuffer()) { | 310 if (!m_reader->interlaceBuffer()) { |
322 longjmp(JMPBUF(png), 1); | 311 longjmp(JMPBUF(png), 1); |
323 return; | 312 return; |
324 } | 313 } |
325 } | 314 } |
326 | 315 |
327 #if USE(QCMSLIB) | |
328 if (colorTransform()) { | |
329 m_reader->createRowBuffer(colorChannels * size().width()); | |
330 if (!m_reader->rowBuffer()) { | |
331 longjmp(JMPBUF(png), 1); | |
332 return; | |
333 } | |
334 } | |
335 #endif | |
336 buffer.setStatus(ImageFrame::FramePartial); | 316 buffer.setStatus(ImageFrame::FramePartial); |
337 buffer.setHasAlpha(false); | 317 buffer.setHasAlpha(false); |
338 | 318 |
339 // For PNGs, the frame always fills the entire image. | 319 // For PNGs, the frame always fills the entire image. |
340 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); | 320 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); |
341 } | 321 } |
342 | 322 |
343 /* libpng comments (here to explain what follows). | 323 /* libpng comments (here to explain what follows). |
344 * | 324 * |
345 * this function is called for every row in the image. If the | 325 * this function is called for every row in the image. If the |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
382 | 362 |
383 bool hasAlpha = m_reader->hasAlpha(); | 363 bool hasAlpha = m_reader->hasAlpha(); |
384 png_bytep row = rowBuffer; | 364 png_bytep row = rowBuffer; |
385 | 365 |
386 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { | 366 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { |
387 unsigned colorChannels = hasAlpha ? 4 : 3; | 367 unsigned colorChannels = hasAlpha ? 4 : 3; |
388 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); | 368 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); |
389 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); | 369 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); |
390 } | 370 } |
391 | 371 |
392 #if USE(QCMSLIB) | |
393 if (qcms_transform* transform = colorTransform()) { | |
394 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width()); | |
395 row = m_reader->rowBuffer(); | |
396 } | |
397 #endif | |
398 | |
399 // Write the decoded row pixels to the frame buffer. The repetitive | 372 // Write the decoded row pixels to the frame buffer. The repetitive |
400 // form of the row write loops is for speed. | 373 // form of the row write loops is for speed. |
401 ImageFrame::PixelData* address = buffer.getAddr(0, y); | 374 ImageFrame::PixelData* address = buffer.getAddr(0, y); |
402 unsigned alphaMask = 255; | 375 unsigned alphaMask = 255; |
403 int width = size().width(); | 376 int width = size().width(); |
404 | 377 |
405 png_bytep pixel = row; | 378 SkColorSpaceXform* xform = colorTransform(); |
406 if (hasAlpha) { | 379 if (hasAlpha) { |
380 // FIXME: | |
381 // Here we apply the color space transformation to the dst space. | |
382 // It does not really make sense to transform to a gamma-encoded | |
383 // space and then immediately after, perform a linear premultiply. | |
384 // Ideally we would pass kPremul_SkAlphaType to xform->apply(), | |
385 // instructing SkColorSpaceXform to perform the linear premultiply | |
386 // while the pixels are a linear space. | |
387 // We cannot do this because, when we apply the gamma encoding after | |
scroggo_chromium
2016/10/19 18:26:25
nit: No need for comma in between "because" and "w
msarett
2016/10/19 19:55:43
Done.
| |
388 // the premultiply, we will very likely end up with valid pixels | |
389 // where R, G, and/or B are greater than A. The legacy drawing | |
390 // pipeline does not know how to handle this. | |
391 if (xform) { | |
392 SkColorSpaceXform::ColorFormat colorFormat = | |
393 SkColorSpaceXform::kRGBA_8888_ColorFormat; | |
394 xform->apply(colorFormat, address, colorFormat, row, size().width(), | |
395 kUnpremul_SkAlphaType); | |
396 } | |
397 | |
398 uint8_t* pixel = (uint8_t*)address; | |
407 if (buffer.premultiplyAlpha()) { | 399 if (buffer.premultiplyAlpha()) { |
408 for (int x = 0; x < width; ++x, pixel += 4) { | 400 for (int x = 0; x < width; ++x, pixel += 4) { |
409 buffer.setRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2], | 401 buffer.setRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2], |
410 pixel[3]); | 402 pixel[3]); |
411 alphaMask &= pixel[3]; | 403 alphaMask &= pixel[3]; |
412 } | 404 } |
413 } else { | 405 } else { |
414 for (int x = 0; x < width; ++x, pixel += 4) { | 406 for (int x = 0; x < width; ++x, pixel += 4) { |
415 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], pixel[3]); | 407 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], pixel[3]); |
416 alphaMask &= pixel[3]; | 408 alphaMask &= pixel[3]; |
417 } | 409 } |
418 } | 410 } |
419 } else { | 411 } else { |
412 uint8_t* pixel = row; | |
420 for (int x = 0; x < width; ++x, pixel += 3) { | 413 for (int x = 0; x < width; ++x, pixel += 3) { |
421 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], 255); | 414 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], 255); |
422 } | 415 } |
416 | |
417 // We'll apply the color space xform to opaque pixels after they have been | |
418 // written the ImageFrame, purely because SkColorSpaceXform supports RGBA | |
scroggo_chromium
2016/10/19 18:26:25
nit: to* the ImageFrame
msarett
2016/10/19 19:55:43
Done.
| |
419 // (and not RGB). | |
420 if (xform) { | |
421 xform->apply(xformColorFormat(), address, xformColorFormat(), row, | |
422 size().width(), kOpaque_SkAlphaType); | |
423 } | |
423 } | 424 } |
424 | 425 |
425 if (alphaMask != 255 && !buffer.hasAlpha()) | 426 if (alphaMask != 255 && !buffer.hasAlpha()) |
426 buffer.setHasAlpha(true); | 427 buffer.setHasAlpha(true); |
427 | 428 |
428 buffer.setPixelsChanged(true); | 429 buffer.setPixelsChanged(true); |
429 } | 430 } |
430 | 431 |
431 void PNGImageDecoder::complete() { | 432 void PNGImageDecoder::complete() { |
432 if (m_frameBufferCache.isEmpty()) | 433 if (m_frameBufferCache.isEmpty()) |
(...skipping 17 matching lines...) Expand all Loading... | |
450 // has failed. | 451 // has failed. |
451 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) | 452 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) |
452 setFailed(); | 453 setFailed(); |
453 | 454 |
454 // If decoding is done or failed, we don't need the PNGImageReader anymore. | 455 // If decoding is done or failed, we don't need the PNGImageReader anymore. |
455 if (isComplete(this) || failed()) | 456 if (isComplete(this) || failed()) |
456 m_reader.reset(); | 457 m_reader.reset(); |
457 } | 458 } |
458 | 459 |
459 } // namespace blink | 460 } // namespace blink |
OLD | NEW |