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

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

Issue 2436223002: Revert of Use SkColorSpaceXform to handle color conversions in decoders (Closed)
Patch Set: 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
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698