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

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: Response to comments 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* address = buffer.getAddr(0, y);
scroggo_chromium 2016/10/20 12:45:04 The variables "address" and "addr" could be easy t
msarett 2016/10/20 13:20:52 Done.
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 pixel = row;
406 if (hasAlpha) { 379 if (hasAlpha) {
380 #if USE(SKCOLORXFORM)
381 // TODO (msarett):
382 // Here we apply the color space transformation to the dst space.
383 // It does not really make sense to transform to a gamma-encoded
384 // space and then immediately after, perform a linear premultiply.
385 // Ideally we would pass kPremul_SkAlphaType to xform->apply(),
386 // instructing SkColorSpaceXform to perform the linear premultiply
387 // while the pixels are a linear space.
388 // We cannot do this because when we apply the gamma encoding after
scroggo_chromium 2016/10/20 12:45:04 Is there an actual TODO here? Or should is this co
msarett 2016/10/20 13:20:52 It's the latter. I'll drop the TODO.
389 // the premultiply, we will very likely end up with valid pixels
390 // where R, G, and/or B are greater than A. The legacy drawing
391 // pipeline does not know how to handle this.
392 if (SkColorSpaceXform* xform = colorTransform()) {
393 SkColorSpaceXform::ColorFormat colorFormat =
394 SkColorSpaceXform::kRGBA_8888_ColorFormat;
395 xform->apply(colorFormat, address, colorFormat, row, size().width(),
396 kUnpremul_SkAlphaType);
397 pixel = (png_bytep)address;
398 }
399 #endif
400
407 if (buffer.premultiplyAlpha()) { 401 if (buffer.premultiplyAlpha()) {
408 for (int x = 0; x < width; ++x, pixel += 4) { 402 for (int x = 0; x < width; ++x, pixel += 4) {
409 buffer.setRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2], 403 buffer.setRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2],
410 pixel[3]); 404 pixel[3]);
411 alphaMask &= pixel[3]; 405 alphaMask &= pixel[3];
412 } 406 }
413 } else { 407 } else {
414 for (int x = 0; x < width; ++x, pixel += 4) { 408 for (int x = 0; x < width; ++x, pixel += 4) {
415 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], pixel[3]); 409 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], pixel[3]);
416 alphaMask &= pixel[3]; 410 alphaMask &= pixel[3];
417 } 411 }
418 } 412 }
419 } else { 413 } else {
414 ImageFrame::PixelData* addr = address;
scroggo_chromium 2016/10/20 12:45:04 Another way to distinguish addr from address - you
msarett 2016/10/20 13:20:52 Changed the style of the loop. Using dstRow and d
420 for (int x = 0; x < width; ++x, pixel += 3) { 415 for (int x = 0; x < width; ++x, pixel += 3) {
421 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], 255); 416 buffer.setRGBARaw(addr++, pixel[0], pixel[1], pixel[2], 255);
422 } 417 }
418
419 #if USE(SKCOLORXFORM)
420 // We'll apply the color space xform to opaque pixels after they have been
421 // written to the ImageFrame, purely because SkColorSpaceXform supports
422 // RGBA (and not RGB).
423 if (SkColorSpaceXform* xform = colorTransform()) {
424 xform->apply(xformColorFormat(), address, xformColorFormat(), address,
425 size().width(), kOpaque_SkAlphaType);
426 }
427 #endif
423 } 428 }
424 429
425 if (alphaMask != 255 && !buffer.hasAlpha()) 430 if (alphaMask != 255 && !buffer.hasAlpha())
426 buffer.setHasAlpha(true); 431 buffer.setHasAlpha(true);
427 432
428 buffer.setPixelsChanged(true); 433 buffer.setPixelsChanged(true);
429 } 434 }
430 435
431 void PNGImageDecoder::complete() { 436 void PNGImageDecoder::complete() {
432 if (m_frameBufferCache.isEmpty()) 437 if (m_frameBufferCache.isEmpty())
(...skipping 17 matching lines...) Expand all
450 // has failed. 455 // has failed.
451 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) 456 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived())
452 setFailed(); 457 setFailed();
453 458
454 // If decoding is done or failed, we don't need the PNGImageReader anymore. 459 // If decoding is done or failed, we don't need the PNGImageReader anymore.
455 if (isComplete(this) || failed()) 460 if (isComplete(this) || failed())
456 m_reader.reset(); 461 m_reader.reset();
457 } 462 }
458 463
459 } // namespace blink 464 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698