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

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

Issue 2386453003: WIP: Implement APNG (Closed)
Patch Set: Implemented feedback on patch set 9 Created 4 years, 1 month 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 20 matching lines...) Expand all
31 * licenses (the MPL or the GPL) and not to allow others to use your 31 * licenses (the MPL or the GPL) and not to allow others to use your
32 * version of this file under the LGPL, indicate your decision by 32 * version of this file under the LGPL, indicate your decision by
33 * deletingthe provisions above and replace them with the notice and 33 * deletingthe provisions above and replace them with the notice and
34 * other provisions required by the MPL or the GPL, as the case may be. 34 * other provisions required by the MPL or the GPL, as the case may be.
35 * If you do not delete the provisions above, a recipient may use your 35 * If you do not delete the provisions above, a recipient may use your
36 * version of this file under any of the LGPL, the MPL or the GPL. 36 * version of this file under any of the LGPL, the MPL or the GPL.
37 */ 37 */
38 38
39 #include "platform/image-decoders/png/PNGImageDecoder.h" 39 #include "platform/image-decoders/png/PNGImageDecoder.h"
40 40
41 #include "platform/image-decoders/png/PNGImageReader.h"
41 #include "png.h" 42 #include "png.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 48
49 #if PNG_LIBPNG_VER_MAJOR > 1 || \ 49 #if PNG_LIBPNG_VER_MAJOR > 1 || \
50 (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4) 50 (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4)
51 #define JMPBUF(png_ptr) png_jmpbuf(png_ptr) 51 #define JMPBUF(png_ptr) png_jmpbuf(png_ptr)
52 #else 52 #else
53 #define JMPBUF(png_ptr) png_ptr->jmpbuf 53 #define JMPBUF(png_ptr) png_ptr->jmpbuf
54 #endif 54 #endif
55 55
56 namespace {
57
58 inline blink::PNGImageDecoder* imageDecoder(png_structp png) {
59 return static_cast<blink::PNGImageDecoder*>(png_get_progressive_ptr(png));
60 }
61
62 void PNGAPI pngHeaderAvailable(png_structp png, png_infop) {
63 imageDecoder(png)->headerAvailable();
64 }
65
66 void PNGAPI pngRowAvailable(png_structp png,
67 png_bytep row,
68 png_uint_32 rowIndex,
69 int state) {
70 imageDecoder(png)->rowAvailable(row, rowIndex, state);
71 }
72
73 void PNGAPI pngComplete(png_structp png, png_infop) {
74 imageDecoder(png)->complete();
75 }
76
77 void PNGAPI pngFailed(png_structp png, png_const_charp) {
78 longjmp(JMPBUF(png), 1);
79 }
80
81 } // namespace
82
83 namespace blink { 56 namespace blink {
84 57
85 class PNGImageReader final {
86 USING_FAST_MALLOC(PNGImageReader);
87 WTF_MAKE_NONCOPYABLE(PNGImageReader);
88
89 public:
90 PNGImageReader(PNGImageDecoder* decoder, size_t readOffset)
91 : m_decoder(decoder),
92 m_readOffset(readOffset),
93 m_currentBufferSize(0),
94 m_decodingSizeOnly(false),
95 m_hasAlpha(false)
96 {
97 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
98 m_info = png_create_info_struct(m_png);
99 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable,
100 pngRowAvailable, pngComplete);
101 }
102
103 ~PNGImageReader() {
104 png_destroy_read_struct(m_png ? &m_png : 0, m_info ? &m_info : 0, 0);
105 ASSERT(!m_png && !m_info);
106
107 m_readOffset = 0;
108 }
109
110 bool decode(const SegmentReader& data, bool sizeOnly) {
111 m_decodingSizeOnly = sizeOnly;
112
113 // We need to do the setjmp here. Otherwise bad things will happen.
114 if (setjmp(JMPBUF(m_png)))
115 return m_decoder->setFailed();
116
117 const char* segment;
118 while (size_t segmentLength = data.getSomeData(segment, m_readOffset)) {
119 m_readOffset += segmentLength;
120 m_currentBufferSize = m_readOffset;
121 png_process_data(m_png, m_info,
122 reinterpret_cast<png_bytep>(const_cast<char*>(segment)),
123 segmentLength);
124 if (sizeOnly ? m_decoder->isDecodedSizeAvailable()
125 : m_decoder->frameIsCompleteAtIndex(0))
126 return true;
127 }
128
129 return false;
130 }
131
132 png_structp pngPtr() const { return m_png; }
133 png_infop infoPtr() const { return m_info; }
134
135 size_t getReadOffset() const { return m_readOffset; }
136 void setReadOffset(size_t offset) { m_readOffset = offset; }
137 size_t currentBufferSize() const { return m_currentBufferSize; }
138 bool decodingSizeOnly() const { return m_decodingSizeOnly; }
139 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; }
140 bool hasAlpha() const { return m_hasAlpha; }
141
142 png_bytep interlaceBuffer() const { return m_interlaceBuffer.get(); }
143 void createInterlaceBuffer(int size) {
144 m_interlaceBuffer = wrapArrayUnique(new png_byte[size]);
145 }
146
147 private:
148 png_structp m_png;
149 png_infop m_info;
150 PNGImageDecoder* m_decoder;
151 size_t m_readOffset;
152 size_t m_currentBufferSize;
153 bool m_decodingSizeOnly;
154 bool m_hasAlpha;
155 std::unique_ptr<png_byte[]> m_interlaceBuffer;
156 };
157
158 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, 58 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption,
159 ColorSpaceOption colorOptions, 59 ColorSpaceOption colorOptions,
160 size_t maxDecodedBytes, 60 size_t maxDecodedBytes,
161 size_t offset) 61 size_t offset)
162 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes), 62 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes),
163 m_offset(offset) {} 63 m_offset(offset),
64 m_metaDataDecoded(false),
65 m_frameCount(0),
66 m_currentFrame(0),
67 m_repetitionCount(cAnimationLoopOnce) {}
164 68
165 PNGImageDecoder::~PNGImageDecoder() {} 69 PNGImageDecoder::~PNGImageDecoder() {}
166 70
71 size_t PNGImageDecoder::decodeFrameCount() {
72 if (!m_metaDataDecoded)
73 parse(PNGParseQuery::PNGMetaDataQuery);
74 return m_frameCount;
75 }
76
77 inline bool frameComplete(ImageFrame& frame) {
78 return frame.getStatus() == ImageFrame::FrameComplete;
79 }
80
81 void PNGImageDecoder::decode(size_t index) {
82 m_currentFrame = index;
83 m_reader->decode(*m_data, index);
84 }
85
86 void PNGImageDecoder::parse(PNGParseQuery query) {
87 if (failed())
88 return;
89
90 if (!m_reader)
91 m_reader = wrapUnique(new PNGImageReader(this, m_offset));
92
93 if (!m_reader->parse(*m_data, query) && isAllDataReceived())
94 setFailed();
95
96 if (query == PNGParseQuery::PNGMetaDataQuery)
97 m_frameCount = m_reader->frameCount();
98 }
99
100 void PNGImageDecoder::setRepetitionCount(size_t repetitionCount) {
101 m_repetitionCount =
102 (repetitionCount == 0) ? cAnimationLoopInfinite : repetitionCount;
103 }
104
105 // This matches the existing behavior to loop once if decoding fails, but this
106 // should be changed to stick with m_repetitionCount to match other browsers.
107 // See crbug.com/267883
108 int PNGImageDecoder::repetitionCount() const {
109 if (m_metaDataDecoded && isAllDataReceived() && m_reader->frameCount() == 1)
110 return cAnimationNone;
111 return failed() ? cAnimationLoopOnce : m_repetitionCount;
112 }
113
114 // These are mapped according to:
115 // https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chu nk
116 static inline ImageFrame::DisposalMethod getDisposalMethod(
117 uint8_t disposalMethod) {
118 switch (disposalMethod) {
119 case 0:
120 return ImageFrame::DisposalMethod::DisposeKeep;
121 case 1:
122 return ImageFrame::DisposalMethod::DisposeOverwriteBgcolor;
123 case 2:
124 return ImageFrame::DisposalMethod::DisposeOverwritePrevious;
125 default:
126 return ImageFrame::DisposalMethod::DisposeNotSpecified;
127 }
128 }
129
130 // These are mapped according to:
131 // https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chu nk
132 static inline ImageFrame::AlphaBlendSource getAlphaBlend(uint8_t alphaBlend) {
133 if (alphaBlend == 1)
134 return ImageFrame::AlphaBlendSource::BlendAtopPreviousFrame;
135 return ImageFrame::AlphaBlendSource::BlendAtopBgcolor;
136 }
137
138 void PNGImageDecoder::initializeNewFrame(size_t index) {
139 const PNGImageReader::FrameInfo& frameInfo = m_reader->frameInfo(index);
140 ImageFrame* buffer = &m_frameBufferCache[index];
141
142 IntRect frameRectWithinSize =
143 intersection(frameInfo.frameRect, {IntPoint(), size()});
144 buffer->setOriginalFrameRect(frameRectWithinSize);
145 buffer->setDuration(frameInfo.duration);
146 buffer->setDisposalMethod(getDisposalMethod(frameInfo.disposalMethod));
147 buffer->setAlphaBlendSource(getAlphaBlend(frameInfo.alphaBlend));
148 }
149
167 void PNGImageDecoder::headerAvailable() { 150 void PNGImageDecoder::headerAvailable() {
168 png_structp png = m_reader->pngPtr(); 151 png_structp png = m_reader->pngPtr();
169 png_infop info = m_reader->infoPtr(); 152 png_infop info = m_reader->infoPtr();
170 png_uint_32 width = png_get_image_width(png, info); 153 png_uint_32 width = png_get_image_width(png, info);
171 png_uint_32 height = png_get_image_height(png, info); 154 png_uint_32 height = png_get_image_height(png, info);
172 155
173 // Protect against large PNGs. See http://bugzil.la/251381 for more details. 156 // Only set the size of the image once. Since single frames also use this
174 const unsigned long maxPNGSize = 1000000UL; 157 // method, we don't want them to override the size to their frame rect.
175 if (width > maxPNGSize || height > maxPNGSize) { 158 if (!isDecodedSizeAvailable()) {
176 longjmp(JMPBUF(png), 1); 159 // Protect against large PNGs. See http://bugzil.la/251381 for more details.
177 return; 160 const unsigned long maxPNGSize = 1000000UL;
178 } 161 if (width > maxPNGSize || height > maxPNGSize) {
162 longjmp(JMPBUF(png), 1);
163 return;
164 }
179 165
180 // Set the image size now that the image header is available. 166 // Set the image size now that the image header is available.
181 if (!setSize(width, height)) { 167 if (!setSize(width, height)) {
182 longjmp(JMPBUF(png), 1); 168 longjmp(JMPBUF(png), 1);
183 return; 169 return;
170 }
184 } 171 }
185 172
186 int bitDepth, colorType, interlaceType, compressionType, filterType, channels; 173 int bitDepth, colorType, interlaceType, compressionType, filterType, channels;
187 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, 174 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType,
188 &interlaceType, &compressionType, &filterType); 175 &interlaceType, &compressionType, &filterType);
189 176
190 // The options we set here match what Mozilla does. 177 // The options we set here match what Mozilla does.
191 178
192 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. 179 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
193 if (colorType == PNG_COLOR_TYPE_PALETTE || 180 if (colorType == PNG_COLOR_TYPE_PALETTE ||
194 (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) 181 (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8))
195 png_set_expand(png); 182 png_set_expand(png);
196 183
197 png_bytep trns = 0; 184 png_bytep trns = 0;
198 int trnsCount = 0; 185 int trnsCount = 0;
199 if (png_get_valid(png, info, PNG_INFO_tRNS)) { 186 if (png_get_valid(png, info, PNG_INFO_tRNS)) {
200 png_get_tRNS(png, info, &trns, &trnsCount, 0); 187 png_get_tRNS(png, info, &trns, &trnsCount, 0);
201 png_set_expand(png); 188 png_set_expand(png);
202 } 189 }
203 190
204 if (bitDepth == 16) 191 if (bitDepth == 16)
205 png_set_strip_16(png); 192 png_set_strip_16(png);
206 193
207 if (colorType == PNG_COLOR_TYPE_GRAY || 194 if (colorType == PNG_COLOR_TYPE_GRAY ||
208 colorType == PNG_COLOR_TYPE_GRAY_ALPHA) 195 colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
209 png_set_gray_to_rgb(png); 196 png_set_gray_to_rgb(png);
210 197
211 if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreColorSpace) { 198 if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreColorSpace) {
212 // We only support color profiles for color PALETTE and RGB[A] PNG. 199 // We only support color profiles for color PALETTE and RGB[A] PNG.
213 // Supporting color profiles for gray-scale images is slightly tricky, at 200 // Supporting color profiles for gray-scale images is slightly tricky, at
214 // least using the CoreGraphics ICC library, because we expand gray-scale 201 // 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 202 // 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 203 // 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. 204 // gray-scale image buffer and hand that to CoreGraphics.
218 #ifdef PNG_iCCP_SUPPORTED 205 #ifdef PNG_iCCP_SUPPORTED
219 if (png_get_valid(png, info, PNG_INFO_sRGB)) { 206 if (png_get_valid(png, info, PNG_INFO_sRGB)) {
220 setColorSpaceAndComputeTransform( 207 setColorSpaceAndComputeTransform(
221 SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)); 208 SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named));
222 } else { 209 } else {
223 char* profileName = nullptr; 210 char* profileName = nullptr;
224 int compressionType = 0; 211 int compressionType = 0;
225 #if (PNG_LIBPNG_VER < 10500) 212 #if (PNG_LIBPNG_VER < 10500)
226 png_charp profile = nullptr; 213 png_charp profile = nullptr;
227 #else 214 #else
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 // Tell libpng to send us rows for interlaced pngs. 252 // Tell libpng to send us rows for interlaced pngs.
266 if (interlaceType == PNG_INTERLACE_ADAM7) 253 if (interlaceType == PNG_INTERLACE_ADAM7)
267 png_set_interlace_handling(png); 254 png_set_interlace_handling(png);
268 255
269 // Update our info now. 256 // Update our info now.
270 png_read_update_info(png, info); 257 png_read_update_info(png, info);
271 channels = png_get_channels(png, info); 258 channels = png_get_channels(png, info);
272 ASSERT(channels == 3 || channels == 4); 259 ASSERT(channels == 3 || channels == 4);
273 260
274 m_reader->setHasAlpha(channels == 4); 261 m_reader->setHasAlpha(channels == 4);
275
276 if (m_reader->decodingSizeOnly()) {
277 // If we only needed the size, halt the reader.
278 #if PNG_LIBPNG_VER_MAJOR > 1 || \
279 (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
280 // Passing '0' tells png_process_data_pause() not to cache unprocessed data.
281 m_reader->setReadOffset(m_reader->currentBufferSize() -
282 png_process_data_pause(png, 0));
283 #else
284 m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size);
285 png->buffer_size = 0;
286 #endif
287 }
288 } 262 }
289 263
290 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, 264 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer,
291 unsigned rowIndex, 265 unsigned rowIndex,
292 int) { 266 int) {
293 if (m_frameBufferCache.isEmpty()) 267 if (m_frameBufferCache.isEmpty())
294 return; 268 return;
295 269
296 // Initialize the framebuffer if needed. 270 // Initialize the framebuffer if needed.
297 ImageFrame& buffer = m_frameBufferCache[0]; 271 ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
298 if (buffer.getStatus() == ImageFrame::FrameEmpty) { 272 if (buffer.getStatus() == ImageFrame::FrameEmpty) {
299 png_structp png = m_reader->pngPtr(); 273 png_structp png = m_reader->pngPtr();
300 if (!buffer.setSizeAndColorSpace(size().width(), size().height(), 274 if (!buffer.setSizeAndColorSpace(size().width(), size().height(),
301 colorSpace())) { 275 colorSpace())) {
302 longjmp(JMPBUF(png), 1); 276 longjmp(JMPBUF(png), 1);
303 return; 277 return;
304 } 278 }
305 279
306 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; 280 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3;
307 if (PNG_INTERLACE_ADAM7 == 281 if (PNG_INTERLACE_ADAM7 ==
308 png_get_interlace_type(png, m_reader->infoPtr())) { 282 png_get_interlace_type(png, m_reader->infoPtr())) {
309 m_reader->createInterlaceBuffer(colorChannels * size().width() * 283 m_reader->createInterlaceBuffer(colorChannels * size().width() *
310 size().height()); 284 size().height());
311 if (!m_reader->interlaceBuffer()) { 285 if (!m_reader->interlaceBuffer()) {
312 longjmp(JMPBUF(png), 1); 286 longjmp(JMPBUF(png), 1);
313 return; 287 return;
314 } 288 }
315 } 289 }
316 290
317 buffer.setStatus(ImageFrame::FramePartial); 291 buffer.setStatus(ImageFrame::FramePartial);
318 buffer.setHasAlpha(false); 292 buffer.setHasAlpha(false);
319
320 // For PNGs, the frame always fills the entire image.
321 buffer.setOriginalFrameRect(IntRect(IntPoint(), size()));
322 } 293 }
323 294
295 // This frameRect is already clipped, so that it fits within the size of the
296 // image. This is done in initializeNewFrame() after a frameCount() call.
297 const IntRect& frameRect = buffer.originalFrameRect();
298
324 /* libpng comments (here to explain what follows). 299 /* libpng comments (here to explain what follows).
325 * 300 *
326 * this function is called for every row in the image. If the 301 * this function is called for every row in the image. If the
327 * image is interlacing, and you turned on the interlace handler, 302 * image is interlacing, and you turned on the interlace handler,
328 * this function will be called for every row in every pass. 303 * this function will be called for every row in every pass.
329 * Some of these rows will not be changed from the previous pass. 304 * Some of these rows will not be changed from the previous pass.
330 * When the row is not changed, the new_row variable will be NULL. 305 * When the row is not changed, the new_row variable will be NULL.
331 * The rows and passes are called in order, so you don't really 306 * The rows and passes are called in order, so you don't really
332 * need the row_num and pass, but I'm supplying them because it 307 * need the row_num and pass, but I'm supplying them because it
333 * may make your life easier. 308 * may make your life easier.
334 */ 309 */
335 310
336 // Nothing to do if the row is unchanged, or the row is outside 311 // Nothing to do if the row is unchanged, or the row is outside
337 // the image bounds: libpng may send extra rows, ignore them to 312 // the image bounds: libpng may send extra rows, ignore them to
338 // make our lives easier. 313 // make our lives easier.
339 if (!rowBuffer) 314 if (!rowBuffer)
340 return; 315 return;
341 int y = rowIndex; 316 int y = rowIndex + frameRect.y();
342 if (y < 0 || y >= size().height()) 317 ASSERT(y >= 0);
318 if (y >= size().height())
343 return; 319 return;
344 320
345 /* libpng comments (continued). 321 /* libpng comments (continued).
346 * 322 *
347 * For the non-NULL rows of interlaced images, you must call 323 * For the non-NULL rows of interlaced images, you must call
348 * png_progressive_combine_row() passing in the row and the 324 * png_progressive_combine_row() passing in the row and the
349 * old row. You can call this function for NULL rows (it will 325 * old row. You can call this function for NULL rows (it will
350 * just return) and for non-interlaced images (it just does the 326 * just return) and for non-interlaced images (it just does the
351 * memcpy for you) if it will make the code easier. Thus, you 327 * memcpy for you) if it will make the code easier. Thus, you
352 * can just do this for all cases: 328 * can just do this for all cases:
353 * 329 *
354 * png_progressive_combine_row(png_ptr, old_row, new_row); 330 * png_progressive_combine_row(png_ptr, old_row, new_row);
355 * 331 *
356 * where old_row is what was displayed for previous rows. Note 332 * where old_row is what was displayed for previous rows. Note
357 * that the first pass (pass == 0 really) will completely cover 333 * that the first pass (pass == 0 really) will completely cover
358 * the old row, so the rows do not have to be initialized. After 334 * the old row, so the rows do not have to be initialized. After
359 * the first pass (and only for interlaced images), you will have 335 * the first pass (and only for interlaced images), you will have
360 * to pass the current row, and the function will combine the 336 * to pass the current row, and the function will combine the
361 * old row and the new row. 337 * old row and the new row.
362 */ 338 */
363 339
364 bool hasAlpha = m_reader->hasAlpha(); 340 bool hasAlpha = m_reader->hasAlpha();
365 png_bytep row = rowBuffer; 341 png_bytep row = rowBuffer;
366 342
367 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { 343 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) {
368 unsigned colorChannels = hasAlpha ? 4 : 3; 344 unsigned colorChannels = hasAlpha ? 4 : 3;
369 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); 345 row = interlaceBuffer + (rowIndex * colorChannels * size().width());
370 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); 346 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer);
371 } 347 }
372 348
373 // Write the decoded row pixels to the frame buffer. The repetitive 349 // Write the decoded row pixels to the frame buffer. The repetitive
374 // form of the row write loops is for speed. 350 // form of the row write loops is for speed.
375 ImageFrame::PixelData* const dstRow = buffer.getAddr(0, y); 351 ImageFrame::PixelData* const dstRow = buffer.getAddr(frameRect.x(), y);
376 unsigned alphaMask = 255; 352 unsigned alphaMask = 255;
377 int width = size().width(); 353 int width = frameRect.width();
378 354
379 png_bytep srcPtr = row; 355 png_bytep srcPtr = row;
380 if (hasAlpha) { 356 if (hasAlpha) {
381 // Here we apply the color space transformation to the dst space. 357 // Here we apply the color space transformation to the dst space.
382 // It does not really make sense to transform to a gamma-encoded 358 // It does not really make sense to transform to a gamma-encoded
383 // space and then immediately after, perform a linear premultiply. 359 // space and then immediately after, perform a linear premultiply.
384 // Ideally we would pass kPremul_SkAlphaType to xform->apply(), 360 // Ideally we would pass kPremul_SkAlphaType to xform->apply(),
385 // instructing SkColorSpaceXform to perform the linear premultiply 361 // instructing SkColorSpaceXform to perform the linear premultiply
386 // while the pixels are a linear space. 362 // while the pixels are a linear space.
387 // We cannot do this because when we apply the gamma encoding after 363 // We cannot do this because when we apply the gamma encoding after
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
424 size().width(), kOpaque_SkAlphaType); 400 size().width(), kOpaque_SkAlphaType);
425 } 401 }
426 } 402 }
427 403
428 if (alphaMask != 255 && !buffer.hasAlpha()) 404 if (alphaMask != 255 && !buffer.hasAlpha())
429 buffer.setHasAlpha(true); 405 buffer.setHasAlpha(true);
430 406
431 buffer.setPixelsChanged(true); 407 buffer.setPixelsChanged(true);
432 } 408 }
433 409
410 bool PNGImageDecoder::frameIsCompleteAtIndex(size_t index) const {
411 // @TODO(joostouwerling): show complete frames even if a later frame fails.
412 if (failed())
413 return false;
414 if (index >= m_frameBufferCache.size())
415 return false;
416 if (index == 0)
417 return ImageDecoder::frameIsCompleteAtIndex(index);
418 return true;
419 }
420
421 float PNGImageDecoder::frameDurationAtIndex(size_t index) const {
422 return (index < m_frameBufferCache.size()
423 ? m_frameBufferCache[index].duration()
424 : 0);
425 }
426
427 // @TODO(joostouwerling) if necessary, do a check if all expected data has been
scroggo_chromium 2016/10/31 19:34:06 Could you add some context? This is because we art
joostouwerling 2016/11/02 18:21:53 Done.
428 // received.
434 void PNGImageDecoder::complete() { 429 void PNGImageDecoder::complete() {
435 if (m_frameBufferCache.isEmpty()) 430 if (m_frameBufferCache.isEmpty())
436 return; 431 return;
437 432 m_frameBufferCache[m_currentFrame].setStatus(ImageFrame::FrameComplete);
438 m_frameBufferCache[0].setStatus(ImageFrame::FrameComplete);
439 }
440
441 inline bool isComplete(const PNGImageDecoder* decoder) {
442 return decoder->frameIsCompleteAtIndex(0);
443 }
444
445 void PNGImageDecoder::decode(bool onlySize) {
446 if (failed())
447 return;
448
449 if (!m_reader)
450 m_reader = wrapUnique(new PNGImageReader(this, m_offset));
451
452 // If we couldn't decode the image but have received all the data, decoding
453 // has failed.
454 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived())
455 setFailed();
456
457 // If decoding is done or failed, we don't need the PNGImageReader anymore.
458 if (isComplete(this) || failed())
459 m_reader.reset();
460 } 433 }
461 434
462 } // namespace blink 435 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698