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 |
48 | 51 |
49 #if PNG_LIBPNG_VER_MAJOR > 1 || \ | 52 #if PNG_LIBPNG_VER_MAJOR > 1 || \ |
50 (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4) | 53 (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4) |
51 #define JMPBUF(png_ptr) png_jmpbuf(png_ptr) | 54 #define JMPBUF(png_ptr) png_jmpbuf(png_ptr) |
52 #else | 55 #else |
53 #define JMPBUF(png_ptr) png_ptr->jmpbuf | 56 #define JMPBUF(png_ptr) png_ptr->jmpbuf |
54 #endif | 57 #endif |
55 | 58 |
56 namespace { | 59 namespace { |
57 | 60 |
(...skipping 28 matching lines...) Expand all Loading... |
86 USING_FAST_MALLOC(PNGImageReader); | 89 USING_FAST_MALLOC(PNGImageReader); |
87 WTF_MAKE_NONCOPYABLE(PNGImageReader); | 90 WTF_MAKE_NONCOPYABLE(PNGImageReader); |
88 | 91 |
89 public: | 92 public: |
90 PNGImageReader(PNGImageDecoder* decoder, size_t readOffset) | 93 PNGImageReader(PNGImageDecoder* decoder, size_t readOffset) |
91 : m_decoder(decoder), | 94 : m_decoder(decoder), |
92 m_readOffset(readOffset), | 95 m_readOffset(readOffset), |
93 m_currentBufferSize(0), | 96 m_currentBufferSize(0), |
94 m_decodingSizeOnly(false), | 97 m_decodingSizeOnly(false), |
95 m_hasAlpha(false) | 98 m_hasAlpha(false) |
| 99 #if USE(QCMSLIB) |
| 100 , |
| 101 m_rowBuffer() |
| 102 #endif |
96 { | 103 { |
97 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); | 104 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); |
98 m_info = png_create_info_struct(m_png); | 105 m_info = png_create_info_struct(m_png); |
99 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, | 106 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, |
100 pngRowAvailable, pngComplete); | 107 pngRowAvailable, pngComplete); |
101 } | 108 } |
102 | 109 |
103 ~PNGImageReader() { | 110 ~PNGImageReader() { |
104 png_destroy_read_struct(m_png ? &m_png : 0, m_info ? &m_info : 0, 0); | 111 png_destroy_read_struct(m_png ? &m_png : 0, m_info ? &m_info : 0, 0); |
105 ASSERT(!m_png && !m_info); | 112 ASSERT(!m_png && !m_info); |
(...skipping 30 matching lines...) Expand all Loading... |
136 void setReadOffset(size_t offset) { m_readOffset = offset; } | 143 void setReadOffset(size_t offset) { m_readOffset = offset; } |
137 size_t currentBufferSize() const { return m_currentBufferSize; } | 144 size_t currentBufferSize() const { return m_currentBufferSize; } |
138 bool decodingSizeOnly() const { return m_decodingSizeOnly; } | 145 bool decodingSizeOnly() const { return m_decodingSizeOnly; } |
139 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; } | 146 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; } |
140 bool hasAlpha() const { return m_hasAlpha; } | 147 bool hasAlpha() const { return m_hasAlpha; } |
141 | 148 |
142 png_bytep interlaceBuffer() const { return m_interlaceBuffer.get(); } | 149 png_bytep interlaceBuffer() const { return m_interlaceBuffer.get(); } |
143 void createInterlaceBuffer(int size) { | 150 void createInterlaceBuffer(int size) { |
144 m_interlaceBuffer = wrapArrayUnique(new png_byte[size]); | 151 m_interlaceBuffer = wrapArrayUnique(new png_byte[size]); |
145 } | 152 } |
| 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 |
146 | 159 |
147 private: | 160 private: |
148 png_structp m_png; | 161 png_structp m_png; |
149 png_infop m_info; | 162 png_infop m_info; |
150 PNGImageDecoder* m_decoder; | 163 PNGImageDecoder* m_decoder; |
151 size_t m_readOffset; | 164 size_t m_readOffset; |
152 size_t m_currentBufferSize; | 165 size_t m_currentBufferSize; |
153 bool m_decodingSizeOnly; | 166 bool m_decodingSizeOnly; |
154 bool m_hasAlpha; | 167 bool m_hasAlpha; |
155 std::unique_ptr<png_byte[]> m_interlaceBuffer; | 168 std::unique_ptr<png_byte[]> m_interlaceBuffer; |
| 169 #if USE(QCMSLIB) |
| 170 std::unique_ptr<png_byte[]> m_rowBuffer; |
| 171 #endif |
156 }; | 172 }; |
157 | 173 |
158 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, | 174 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, |
159 GammaAndColorProfileOption colorOptions, | 175 GammaAndColorProfileOption colorOptions, |
160 size_t maxDecodedBytes, | 176 size_t maxDecodedBytes, |
161 size_t offset) | 177 size_t offset) |
162 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes), | 178 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes), |
163 m_offset(offset) {} | 179 m_offset(offset) {} |
164 | 180 |
165 PNGImageDecoder::~PNGImageDecoder() {} | 181 PNGImageDecoder::~PNGImageDecoder() {} |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
208 colorType == PNG_COLOR_TYPE_GRAY_ALPHA) | 224 colorType == PNG_COLOR_TYPE_GRAY_ALPHA) |
209 png_set_gray_to_rgb(png); | 225 png_set_gray_to_rgb(png); |
210 | 226 |
211 if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) { | 227 if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) { |
212 // We only support color profiles for color PALETTE and RGB[A] PNG. | 228 // We only support color profiles for color PALETTE and RGB[A] PNG. |
213 // Supporting color profiles for gray-scale images is slightly tricky, at | 229 // Supporting color profiles for gray-scale images is slightly tricky, at |
214 // least using the CoreGraphics ICC library, because we expand gray-scale | 230 // least using the CoreGraphics ICC library, because we expand gray-scale |
215 // images to RGB but we do not similarly transform the color profile. We'd | 231 // images to RGB but we do not similarly transform the color profile. We'd |
216 // either need to transform the color profile or we'd need to decode into a | 232 // either need to transform the color profile or we'd need to decode into a |
217 // gray-scale image buffer and hand that to CoreGraphics. | 233 // gray-scale image buffer and hand that to CoreGraphics. |
| 234 bool imageHasAlpha = (colorType & PNG_COLOR_MASK_ALPHA) || trnsCount; |
218 #ifdef PNG_iCCP_SUPPORTED | 235 #ifdef PNG_iCCP_SUPPORTED |
219 if (png_get_valid(png, info, PNG_INFO_sRGB)) { | 236 if (png_get_valid(png, info, PNG_INFO_sRGB)) { |
220 setColorSpaceAndComputeTransform(nullptr, 0, true /* useSRGB */); | 237 setColorProfileAndComputeTransform(nullptr, 0, imageHasAlpha, |
| 238 true /* useSRGB */); |
221 } else { | 239 } else { |
222 char* profileName = nullptr; | 240 char* profileName = nullptr; |
223 int compressionType = 0; | 241 int compressionType = 0; |
224 #if (PNG_LIBPNG_VER < 10500) | 242 #if (PNG_LIBPNG_VER < 10500) |
225 png_charp profile = nullptr; | 243 png_charp profile = nullptr; |
226 #else | 244 #else |
227 png_bytep profile = nullptr; | 245 png_bytep profile = nullptr; |
228 #endif | 246 #endif |
229 png_uint_32 profileLength = 0; | 247 png_uint_32 profileLength = 0; |
230 if (png_get_iCCP(png, info, &profileName, &compressionType, &profile, | 248 if (png_get_iCCP(png, info, &profileName, &compressionType, &profile, |
231 &profileLength)) { | 249 &profileLength)) { |
232 setColorSpaceAndComputeTransform(reinterpret_cast<char*>(profile), | 250 setColorProfileAndComputeTransform(reinterpret_cast<char*>(profile), |
233 profileLength, false /* useSRGB */); | 251 profileLength, imageHasAlpha, |
| 252 false /* useSRGB */); |
234 } | 253 } |
235 } | 254 } |
236 #endif // PNG_iCCP_SUPPORTED | 255 #endif // PNG_iCCP_SUPPORTED |
237 } | 256 } |
238 | 257 |
239 if (!hasColorProfile()) { | 258 if (!hasColorProfile()) { |
240 // TODO (msarett): | 259 // Deal with gamma and keep it under our control. |
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 // TODO (msarett): | |
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. | |
249 const double inverseGamma = 0.45455; | 260 const double inverseGamma = 0.45455; |
250 const double defaultGamma = 2.2; | 261 const double defaultGamma = 2.2; |
251 double gamma; | 262 double gamma; |
252 if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { | 263 if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { |
253 const double maxGamma = 21474.83; | 264 const double maxGamma = 21474.83; |
254 if ((gamma <= 0.0) || (gamma > maxGamma)) { | 265 if ((gamma <= 0.0) || (gamma > maxGamma)) { |
255 gamma = inverseGamma; | 266 gamma = inverseGamma; |
256 png_set_gAMA(png, info, gamma); | 267 png_set_gAMA(png, info, gamma); |
257 } | 268 } |
258 png_set_gamma(png, defaultGamma, gamma); | 269 png_set_gamma(png, defaultGamma, gamma); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
306 if (PNG_INTERLACE_ADAM7 == | 317 if (PNG_INTERLACE_ADAM7 == |
307 png_get_interlace_type(png, m_reader->infoPtr())) { | 318 png_get_interlace_type(png, m_reader->infoPtr())) { |
308 m_reader->createInterlaceBuffer(colorChannels * size().width() * | 319 m_reader->createInterlaceBuffer(colorChannels * size().width() * |
309 size().height()); | 320 size().height()); |
310 if (!m_reader->interlaceBuffer()) { | 321 if (!m_reader->interlaceBuffer()) { |
311 longjmp(JMPBUF(png), 1); | 322 longjmp(JMPBUF(png), 1); |
312 return; | 323 return; |
313 } | 324 } |
314 } | 325 } |
315 | 326 |
| 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 |
316 buffer.setStatus(ImageFrame::FramePartial); | 336 buffer.setStatus(ImageFrame::FramePartial); |
317 buffer.setHasAlpha(false); | 337 buffer.setHasAlpha(false); |
318 | 338 |
319 // For PNGs, the frame always fills the entire image. | 339 // For PNGs, the frame always fills the entire image. |
320 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); | 340 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); |
321 } | 341 } |
322 | 342 |
323 /* libpng comments (here to explain what follows). | 343 /* libpng comments (here to explain what follows). |
324 * | 344 * |
325 * this function is called for every row in the image. If the | 345 * 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... |
362 | 382 |
363 bool hasAlpha = m_reader->hasAlpha(); | 383 bool hasAlpha = m_reader->hasAlpha(); |
364 png_bytep row = rowBuffer; | 384 png_bytep row = rowBuffer; |
365 | 385 |
366 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { | 386 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { |
367 unsigned colorChannels = hasAlpha ? 4 : 3; | 387 unsigned colorChannels = hasAlpha ? 4 : 3; |
368 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); | 388 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); |
369 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); | 389 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); |
370 } | 390 } |
371 | 391 |
| 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 |
372 // Write the decoded row pixels to the frame buffer. The repetitive | 399 // Write the decoded row pixels to the frame buffer. The repetitive |
373 // form of the row write loops is for speed. | 400 // form of the row write loops is for speed. |
374 ImageFrame::PixelData* const dstRow = buffer.getAddr(0, y); | 401 ImageFrame::PixelData* address = buffer.getAddr(0, y); |
375 unsigned alphaMask = 255; | 402 unsigned alphaMask = 255; |
376 int width = size().width(); | 403 int width = size().width(); |
377 | 404 |
378 png_bytep srcPtr = row; | 405 png_bytep pixel = row; |
379 if (hasAlpha) { | 406 if (hasAlpha) { |
380 #if USE(SKCOLORXFORM) | |
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 | |
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 (SkColorSpaceXform* xform = colorTransform()) { | |
392 SkColorSpaceXform::ColorFormat colorFormat = | |
393 SkColorSpaceXform::kRGBA_8888_ColorFormat; | |
394 xform->apply(colorFormat, dstRow, colorFormat, srcPtr, size().width(), | |
395 kUnpremul_SkAlphaType); | |
396 srcPtr = (png_bytep)dstRow; | |
397 } | |
398 #endif | |
399 | |
400 if (buffer.premultiplyAlpha()) { | 407 if (buffer.premultiplyAlpha()) { |
401 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; | 408 for (int x = 0; x < width; ++x, pixel += 4) { |
402 dstPixel++, srcPtr += 4) { | 409 buffer.setRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2], |
403 buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], | 410 pixel[3]); |
404 srcPtr[3]); | 411 alphaMask &= pixel[3]; |
405 alphaMask &= srcPtr[3]; | |
406 } | 412 } |
407 } else { | 413 } else { |
408 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; | 414 for (int x = 0; x < width; ++x, pixel += 4) { |
409 dstPixel++, srcPtr += 4) { | 415 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], pixel[3]); |
410 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], srcPtr[3]); | 416 alphaMask &= pixel[3]; |
411 alphaMask &= srcPtr[3]; | |
412 } | 417 } |
413 } | 418 } |
414 } else { | 419 } else { |
415 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; | 420 for (int x = 0; x < width; ++x, pixel += 3) { |
416 dstPixel++, srcPtr += 3) { | 421 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], 255); |
417 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 255); | |
418 } | 422 } |
419 | |
420 #if USE(SKCOLORXFORM) | |
421 // We'll apply the color space xform to opaque pixels after they have been | |
422 // written to the ImageFrame, purely because SkColorSpaceXform supports | |
423 // RGBA (and not RGB). | |
424 if (SkColorSpaceXform* xform = colorTransform()) { | |
425 xform->apply(xformColorFormat(), dstRow, xformColorFormat(), dstRow, | |
426 size().width(), kOpaque_SkAlphaType); | |
427 } | |
428 #endif | |
429 } | 423 } |
430 | 424 |
431 if (alphaMask != 255 && !buffer.hasAlpha()) | 425 if (alphaMask != 255 && !buffer.hasAlpha()) |
432 buffer.setHasAlpha(true); | 426 buffer.setHasAlpha(true); |
433 | 427 |
434 buffer.setPixelsChanged(true); | 428 buffer.setPixelsChanged(true); |
435 } | 429 } |
436 | 430 |
437 void PNGImageDecoder::complete() { | 431 void PNGImageDecoder::complete() { |
438 if (m_frameBufferCache.isEmpty()) | 432 if (m_frameBufferCache.isEmpty()) |
(...skipping 17 matching lines...) Expand all Loading... |
456 // has failed. | 450 // has failed. |
457 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) | 451 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) |
458 setFailed(); | 452 setFailed(); |
459 | 453 |
460 // If decoding is done or failed, we don't need the PNGImageReader anymore. | 454 // If decoding is done or failed, we don't need the PNGImageReader anymore. |
461 if (isComplete(this) || failed()) | 455 if (isComplete(this) || failed()) |
462 m_reader.reset(); | 456 m_reader.reset(); |
463 } | 457 } |
464 | 458 |
465 } // namespace blink | 459 } // namespace blink |
OLD | NEW |