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

Side by Side Diff: third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp

Issue 2426723005: Use SkColorSpaceXform to handle color conversions in decoders (Closed)
Patch Set: Refactor a bit for clarity Created 4 years, 2 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 (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
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
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
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
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 // TODO (msarett):
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.
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
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
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* const dstRow = 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 png_bytep srcPtr = row;
406 if (hasAlpha) { 379 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
407 if (buffer.premultiplyAlpha()) { 400 if (buffer.premultiplyAlpha()) {
408 for (int x = 0; x < width; ++x, pixel += 4) { 401 for (auto *dstPixel = dstRow; dstPixel < dstRow + width;
409 buffer.setRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2], 402 dstPixel++, srcPtr += 4) {
410 pixel[3]); 403 buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2],
411 alphaMask &= pixel[3]; 404 srcPtr[3]);
405 alphaMask &= srcPtr[3];
412 } 406 }
413 } else { 407 } else {
414 for (int x = 0; x < width; ++x, pixel += 4) { 408 for (auto *dstPixel = dstRow; dstPixel < dstRow + width;
scroggo_chromium 2016/10/20 14:10:28 nit: * goes next to the type: auto* dstPixel
415 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], pixel[3]); 409 dstPixel++, srcPtr += 4) {
416 alphaMask &= pixel[3]; 410 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], srcPtr[3]);
411 alphaMask &= srcPtr[3];
417 } 412 }
418 } 413 }
419 } else { 414 } else {
420 for (int x = 0; x < width; ++x, pixel += 3) { 415 for (auto *dstPixel = dstRow; dstPixel < dstRow + width;
421 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], 255); 416 dstPixel++, srcPtr += 3) {
417 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 255);
422 } 418 }
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
423 } 429 }
424 430
425 if (alphaMask != 255 && !buffer.hasAlpha()) 431 if (alphaMask != 255 && !buffer.hasAlpha())
426 buffer.setHasAlpha(true); 432 buffer.setHasAlpha(true);
427 433
428 buffer.setPixelsChanged(true); 434 buffer.setPixelsChanged(true);
429 } 435 }
430 436
431 void PNGImageDecoder::complete() { 437 void PNGImageDecoder::complete() {
432 if (m_frameBufferCache.isEmpty()) 438 if (m_frameBufferCache.isEmpty())
(...skipping 17 matching lines...) Expand all
450 // has failed. 456 // has failed.
451 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) 457 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived())
452 setFailed(); 458 setFailed();
453 459
454 // If decoding is done or failed, we don't need the PNGImageReader anymore. 460 // If decoding is done or failed, we don't need the PNGImageReader anymore.
455 if (isComplete(this) || failed()) 461 if (isComplete(this) || failed())
456 m_reader.reset(); 462 m_reader.reset();
457 } 463 }
458 464
459 } // namespace blink 465 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698