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

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

Issue 2386453003: WIP: Implement APNG (Closed)
Patch Set: Create m_reader on construction and remove |m_offset| Created 4 years 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 22 matching lines...) Expand all
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 "platform/image-decoders/png/PNGImageReader.h"
42 #include "png.h" 42 #include "png.h"
43 #include "wtf/PtrUtil.h"
44 #include <memory> 43 #include <memory>
45 44
46 namespace blink { 45 namespace blink {
47 46
48 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, 47 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption,
49 ColorSpaceOption colorOptions, 48 ColorSpaceOption colorOptions,
50 sk_sp<SkColorSpace> targetColorSpace, 49 sk_sp<SkColorSpace> targetColorSpace,
51 size_t maxDecodedBytes, 50 size_t maxDecodedBytes,
52 size_t offset) 51 size_t offset)
53 : ImageDecoder(alphaOption, 52 : ImageDecoder(alphaOption, colorOptions, std::move(targetColorSpace),
54 colorOptions,
55 std::move(targetColorSpace),
56 maxDecodedBytes), 53 maxDecodedBytes),
57 m_offset(offset) {} 54 m_frameCount(0),
55 m_currentFrame(0),
56 m_repetitionCount(cAnimationLoopOnce),
57 m_hasAlphaChannel(false),
58 m_currentBufferSawAlpha(false),
59 m_isParsing(false)
60 {
61 m_reader = wrapUnique(new PNGImageReader(this, offset));
scroggo_chromium 2016/12/08 13:59:09 Now that m_reader has the same lifetime as PNGImag
joostouwerling 2016/12/08 16:28:39 That's difficult since they cross reference each o
scroggo_chromium 2016/12/08 16:33:25 What about if you move PNGParseQuery to PNGImageRe
joostouwerling 2016/12/08 21:33:20 Done.
62 }
58 63
59 PNGImageDecoder::~PNGImageDecoder() {} 64 PNGImageDecoder::~PNGImageDecoder() {}
60 65
66 bool PNGImageDecoder::setFailed() {
67 // Update the frame count to make sure it reflects the most up to date number
68 // of frames there were parsed. We don't want to set the decoder to the failed
scroggo_chromium 2016/12/08 13:59:09 nit: I think this sounds better if you switch "the
joostouwerling 2016/12/08 16:28:39 Done.
69 // state if the reader was able to successfully parse some frames.
70 if (m_isParsing)
71 m_frameCount = m_reader->frameCount();
72
73 // There are three cases in which the decoder is invalidated:
74 // 1) No frames are received so far.
75 // 2) The image is not animated.
76 // 3) Decoding for the first frame fails.
77 if (m_frameCount == 0 || (m_reader->parseCompleted() && m_frameCount == 1) ||
78 (!m_isParsing && m_currentFrame == 0))
79 return ImageDecoder::setFailed();
80
81 // If decoding fails for later frames, we still want to show earlier frames,
82 // but the frame count needs to be adjusted. We do not want to shrink
83 // |m_frameBufferCache|, since clients may store pointers to later frames.
84 if (!m_isParsing) {
85 m_frameBufferCache[m_currentFrame].clearPixelData();
86 m_frameBufferCache[m_currentFrame].setStatus(ImageFrame::FrameEmpty);
87 m_frameCount = m_currentFrame;
88 }
89
90 // Set parseCompleted to true, so parse() returns early and thus won't update
91 // |m_frameCount| to the pre-failure value when it is called again.
92 m_reader->setParseCompleted(true);
93 return false;
94 }
95
96 size_t PNGImageDecoder::decodeFrameCount() {
97 parse(PNGParseQuery::PNGMetaDataQuery);
98 return m_frameCount;
99 }
100
101 void PNGImageDecoder::decode(size_t index) {
102 if (failed())
103 return;
104
105 updateAggressivePurging(index);
106
107 Vector<size_t> framesToDecode = findFramesToDecode(index);
108 for (auto i = framesToDecode.rbegin(); i != framesToDecode.rend(); i++) {
109 m_currentFrame = *i;
110 m_reader->decode(*m_data, *i);
111 if (failed() || !postDecodeProcessing(*i))
112 return;
113 }
114 }
115
116 void PNGImageDecoder::clearFrameBuffer(size_t frameIndex) {
117 m_reader->clearDecodeState(frameIndex);
118 m_frameBufferCache[frameIndex].clearPixelData();
scroggo_chromium 2016/12/08 13:59:09 For consistency with the other decoders, you might
joostouwerling 2016/12/08 16:28:39 Calling ImageDecoder::clearFrameBuffer may be the
119 }
120
121 void PNGImageDecoder::onInitFrameBuffer(size_t index) {
122 unsigned colorChannels = m_hasAlphaChannel ? 4 : 3;
123 if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(m_reader->pngPtr(),
124 m_reader->infoPtr())) {
125 m_reader->createInterlaceBuffer(colorChannels * size().width() *
126 size().height());
127 if (!m_reader->interlaceBuffer())
128 setFailed();
129 }
130 }
131
132 bool PNGImageDecoder::canReusePreviousFrameBuffer(size_t frameIndex) const {
133 DCHECK(frameIndex < m_frameBufferCache.size());
134 return m_frameBufferCache[frameIndex].getDisposalMethod() !=
135 ImageFrame::DisposeOverwritePrevious;
136 }
137
138 void PNGImageDecoder::parse(PNGParseQuery query) {
139 if (failed() || m_reader->parseCompleted())
140 return;
141
142 m_isParsing = true;
143 if (!m_reader->parse(*m_data, query) && isAllDataReceived())
144 setFailed();
145 else if (query == PNGParseQuery::PNGMetaDataQuery)
146 m_frameCount = m_reader->frameCount();
147
148 m_isParsing = false;
149 }
150
151 void PNGImageDecoder::setRepetitionCount(size_t repetitionCount) {
152 m_repetitionCount =
153 (repetitionCount == 0) ? cAnimationLoopInfinite : repetitionCount;
154 }
155
156 // This matches the existing behavior to loop once if decoding fails, but this
157 // should be changed to stick with m_repetitionCount to match other browsers.
158 // See crbug.com/267883
159 int PNGImageDecoder::repetitionCount() const {
160 if (m_reader->parseCompleted() && m_reader->frameCount() == 1)
161 return cAnimationNone;
162 return failed() ? cAnimationLoopOnce : m_repetitionCount;
163 }
164
165 // These are mapped according to:
166 // https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chu nk
167 static inline ImageFrame::DisposalMethod getDisposalMethod(
168 uint8_t disposalMethod) {
169 switch (disposalMethod) {
170 case 0:
171 return ImageFrame::DisposalMethod::DisposeKeep;
172 case 1:
173 return ImageFrame::DisposalMethod::DisposeOverwriteBgcolor;
174 case 2:
175 return ImageFrame::DisposalMethod::DisposeOverwritePrevious;
176 default:
177 return ImageFrame::DisposalMethod::DisposeNotSpecified;
178 }
179 }
180
181 // These are mapped according to:
182 // https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chu nk
183 static inline ImageFrame::AlphaBlendSource getAlphaBlend(uint8_t alphaBlend) {
184 if (alphaBlend == 1)
185 return ImageFrame::AlphaBlendSource::BlendAtopPreviousFrame;
186 return ImageFrame::AlphaBlendSource::BlendAtopBgcolor;
187 }
188
189 void PNGImageDecoder::initializeNewFrame(size_t index) {
190 const PNGImageReader::FrameInfo& frameInfo = m_reader->frameInfo(index);
191 ImageFrame* buffer = &m_frameBufferCache[index];
192
193 IntRect frameRectWithinSize =
194 intersection(frameInfo.frameRect, {IntPoint(), size()});
195 buffer->setOriginalFrameRect(frameRectWithinSize);
196 buffer->setDuration(frameInfo.duration);
197 buffer->setDisposalMethod(getDisposalMethod(frameInfo.disposalMethod));
198 buffer->setAlphaBlendSource(getAlphaBlend(frameInfo.alphaBlend));
199 buffer->setRequiredPreviousFrameIndex(
200 findRequiredPreviousFrame(index, false));
201 }
202
61 inline float pngFixedToFloat(png_fixed_point x) { 203 inline float pngFixedToFloat(png_fixed_point x) {
62 return ((float)x) * 0.00001f; 204 return ((float)x) * 0.00001f;
63 } 205 }
64 206
65 inline sk_sp<SkColorSpace> readColorSpace(png_structp png, png_infop info) { 207 inline sk_sp<SkColorSpace> readColorSpace(png_structp png, png_infop info) {
66 if (png_get_valid(png, info, PNG_INFO_sRGB)) { 208 if (png_get_valid(png, info, PNG_INFO_sRGB)) {
67 return SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); 209 return SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
68 } 210 }
69 211
70 png_charp name = nullptr; 212 png_charp name = nullptr;
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
108 // However, the current behavior matches Safari and Firefox. 250 // However, the current behavior matches Safari and Firefox.
109 } 251 }
110 } 252 }
111 253
112 return nullptr; 254 return nullptr;
113 } 255 }
114 256
115 void PNGImageDecoder::headerAvailable() { 257 void PNGImageDecoder::headerAvailable() {
116 png_structp png = m_reader->pngPtr(); 258 png_structp png = m_reader->pngPtr();
117 png_infop info = m_reader->infoPtr(); 259 png_infop info = m_reader->infoPtr();
118 png_uint_32 width = png_get_image_width(png, info);
119 png_uint_32 height = png_get_image_height(png, info);
120 260
121 // Protect against large PNGs. See http://bugzil.la/251381 for more details. 261 png_uint_32 width, height;
122 const unsigned long maxPNGSize = 1000000UL;
123 if (width > maxPNGSize || height > maxPNGSize) {
124 longjmp(JMPBUF(png), 1);
125 return;
126 }
127
128 // Set the image size now that the image header is available.
129 if (!setSize(width, height)) {
130 longjmp(JMPBUF(png), 1);
131 return;
132 }
133
134 int bitDepth, colorType, interlaceType, compressionType, filterType, channels; 262 int bitDepth, colorType, interlaceType, compressionType, filterType, channels;
135 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, 263 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType,
136 &interlaceType, &compressionType, &filterType); 264 &interlaceType, &compressionType, &filterType);
137 265
138 // The options we set here match what Mozilla does. 266 // The options we set here match what Mozilla does.
139 267
140 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. 268 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
141 if (colorType == PNG_COLOR_TYPE_PALETTE || 269 if (colorType == PNG_COLOR_TYPE_PALETTE ||
142 (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) 270 (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8))
143 png_set_expand(png); 271 png_set_expand(png);
144 272
145 png_bytep trns = 0; 273 png_bytep trns = 0;
146 int trnsCount = 0; 274 int trnsCount = 0;
147 if (png_get_valid(png, info, PNG_INFO_tRNS)) { 275 if (png_get_valid(png, info, PNG_INFO_tRNS)) {
148 png_get_tRNS(png, info, &trns, &trnsCount, 0); 276 png_get_tRNS(png, info, &trns, &trnsCount, 0);
149 png_set_expand(png); 277 png_set_expand(png);
150 } 278 }
151 279
152 if (bitDepth == 16) 280 if (bitDepth == 16)
153 png_set_strip_16(png); 281 png_set_strip_16(png);
154 282
155 if (colorType == PNG_COLOR_TYPE_GRAY || 283 if (colorType == PNG_COLOR_TYPE_GRAY ||
156 colorType == PNG_COLOR_TYPE_GRAY_ALPHA) 284 colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
157 png_set_gray_to_rgb(png); 285 png_set_gray_to_rgb(png);
158 286
159 if ((colorType & PNG_COLOR_MASK_COLOR) && !ignoresColorSpace()) { 287 // Only set the size and the color space of the image once. Since non-first
160 // We only support color profiles for color PALETTE and RGB[A] PNG. 288 // frames also use this method, we don't want them to override the size of
161 // Supporting color profiles for gray-scale images is slightly tricky, at 289 // the image to the size of their frame rect. Frames also don't specify their
162 // least using the CoreGraphics ICC library, because we expand gray-scale 290 // own color space, so we need to set it only once.
163 // images to RGB but we do not similarly transform the color profile. We'd 291 if (!isDecodedSizeAvailable()) {
164 // either need to transform the color profile or we'd need to decode into a 292 // Protect against large PNGs. See http://bugzil.la/251381 for more details.
165 // gray-scale image buffer and hand that to CoreGraphics. 293 const unsigned long maxPNGSize = 1000000UL;
166 sk_sp<SkColorSpace> colorSpace = readColorSpace(png, info); 294 if (width > maxPNGSize || height > maxPNGSize) {
167 if (colorSpace) { 295 longjmp(JMPBUF(png), 1);
168 setEmbeddedColorSpace(colorSpace); 296 return;
297 }
298
299 // Set the image size now that the image header is available.
300 if (!setSize(width, height)) {
301 longjmp(JMPBUF(png), 1);
302 return;
303 }
304
305 if ((colorType & PNG_COLOR_MASK_COLOR) && !ignoresColorSpace()) {
306 // We only support color profiles for color PALETTE and RGB[A] PNG.
307 // Supporting color profiles for gray-scale images is slightly tricky, at
308 // least using the CoreGraphics ICC library, because we expand gray-scale
309 // images to RGB but we do not similarly transform the color profile. We'd
310 // either need to transform the color profile or we'd need to decode into
311 // a
312 // gray-scale image buffer and hand that to CoreGraphics.
313 sk_sp<SkColorSpace> colorSpace = readColorSpace(png, info);
314 if (colorSpace) {
315 setEmbeddedColorSpace(colorSpace);
316 }
169 } 317 }
170 } 318 }
171 319
172 if (!hasEmbeddedColorSpace()) { 320 if (!hasEmbeddedColorSpace()) {
173 // TODO (msarett): 321 // TODO (msarett):
174 // Applying the transfer function (gamma) should be handled by 322 // Applying the transfer function (gamma) should be handled by
175 // SkColorSpaceXform. Here we always convert to a transfer function that 323 // SkColorSpaceXform. Here we always convert to a transfer function that
176 // is a 2.2 exponential. This is a little strange given that the dst 324 // is a 2.2 exponential. This is a little strange given that the dst
177 // transfer function is not necessarily a 2.2 exponential. 325 // transfer function is not necessarily a 2.2 exponential.
178 // TODO (msarett): 326 // TODO (msarett):
(...skipping 17 matching lines...) Expand all
196 344
197 // Tell libpng to send us rows for interlaced pngs. 345 // Tell libpng to send us rows for interlaced pngs.
198 if (interlaceType == PNG_INTERLACE_ADAM7) 346 if (interlaceType == PNG_INTERLACE_ADAM7)
199 png_set_interlace_handling(png); 347 png_set_interlace_handling(png);
200 348
201 // Update our info now. 349 // Update our info now.
202 png_read_update_info(png, info); 350 png_read_update_info(png, info);
203 channels = png_get_channels(png, info); 351 channels = png_get_channels(png, info);
204 ASSERT(channels == 3 || channels == 4); 352 ASSERT(channels == 3 || channels == 4);
205 353
206 m_reader->setHasAlpha(channels == 4); 354 m_hasAlphaChannel = (channels == 4);
207 355 m_currentBufferSawAlpha = false;
208 if (m_reader->decodingSizeOnly()) {
209 // If we only needed the size, halt the reader.
210 #if PNG_LIBPNG_VER_MAJOR > 1 || \
211 (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
212 // Passing '0' tells png_process_data_pause() not to cache unprocessed data.
213 m_reader->setReadOffset(m_reader->currentBufferSize() -
214 png_process_data_pause(png, 0));
215 #else
216 m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size);
217 png->buffer_size = 0;
218 #endif
219 }
220 } 356 }
221 357
222 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, 358 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer,
223 unsigned rowIndex, 359 unsigned rowIndex,
224 int) { 360 int) {
225 if (m_frameBufferCache.isEmpty()) 361 if (m_frameBufferCache.isEmpty())
226 return; 362 return;
227 363
228 // Initialize the framebuffer if needed. 364 if (!initFrameBuffer(m_currentFrame)) {
229 ImageFrame& buffer = m_frameBufferCache[0]; 365 setFailed();
230 if (buffer.getStatus() == ImageFrame::FrameEmpty) { 366 return;
231 png_structp png = m_reader->pngPtr();
232 if (!buffer.setSizeAndColorSpace(size().width(), size().height(),
233 colorSpaceForSkImages())) {
234 longjmp(JMPBUF(png), 1);
235 return;
236 }
237
238 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3;
239 if (PNG_INTERLACE_ADAM7 ==
240 png_get_interlace_type(png, m_reader->infoPtr())) {
241 m_reader->createInterlaceBuffer(colorChannels * size().width() *
242 size().height());
243 if (!m_reader->interlaceBuffer()) {
244 longjmp(JMPBUF(png), 1);
245 return;
246 }
247 }
248
249 buffer.setStatus(ImageFrame::FramePartial);
250 buffer.setHasAlpha(false);
251
252 // For PNGs, the frame always fills the entire image.
253 buffer.setOriginalFrameRect(IntRect(IntPoint(), size()));
254 } 367 }
255 368
369 // This frameRect is already clipped, so that it fits within the size of the
370 // image. This is done in initializeNewFrame() after a frameCount() call.
371 ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
372 const IntRect& frameRect = buffer.originalFrameRect();
373
256 /* libpng comments (here to explain what follows). 374 /* libpng comments (here to explain what follows).
257 * 375 *
258 * this function is called for every row in the image. If the 376 * this function is called for every row in the image. If the
259 * image is interlacing, and you turned on the interlace handler, 377 * image is interlacing, and you turned on the interlace handler,
260 * this function will be called for every row in every pass. 378 * this function will be called for every row in every pass.
261 * Some of these rows will not be changed from the previous pass. 379 * Some of these rows will not be changed from the previous pass.
262 * When the row is not changed, the new_row variable will be NULL. 380 * When the row is not changed, the new_row variable will be NULL.
263 * The rows and passes are called in order, so you don't really 381 * The rows and passes are called in order, so you don't really
264 * need the row_num and pass, but I'm supplying them because it 382 * need the row_num and pass, but I'm supplying them because it
265 * may make your life easier. 383 * may make your life easier.
266 */ 384 */
267 385
268 // Nothing to do if the row is unchanged, or the row is outside 386 // Nothing to do if the row is unchanged, or the row is outside the image
269 // the image bounds: libpng may send extra rows, ignore them to 387 // bounds. In the case that a frame presents more data than the indicated
270 // make our lives easier. 388 // frame size, ignore the extra rows and thus use the frame size as the
389 // source of truth. libpng may also send extra rows. Ignore those as well.
390 // This prevents us from trying to write outside of the image bounds.
271 if (!rowBuffer) 391 if (!rowBuffer)
272 return; 392 return;
273 int y = rowIndex; 393 int y = rowIndex + frameRect.y();
274 if (y < 0 || y >= size().height()) 394 DCHECK_GE(y, 0);
395 if (y >= size().height())
275 return; 396 return;
276 397
277 /* libpng comments (continued). 398 /* libpng comments (continued).
278 * 399 *
279 * For the non-NULL rows of interlaced images, you must call 400 * For the non-NULL rows of interlaced images, you must call
280 * png_progressive_combine_row() passing in the row and the 401 * png_progressive_combine_row() passing in the row and the
281 * old row. You can call this function for NULL rows (it will 402 * old row. You can call this function for NULL rows (it will
282 * just return) and for non-interlaced images (it just does the 403 * just return) and for non-interlaced images (it just does the
283 * memcpy for you) if it will make the code easier. Thus, you 404 * memcpy for you) if it will make the code easier. Thus, you
284 * can just do this for all cases: 405 * can just do this for all cases:
285 * 406 *
286 * png_progressive_combine_row(png_ptr, old_row, new_row); 407 * png_progressive_combine_row(png_ptr, old_row, new_row);
287 * 408 *
288 * where old_row is what was displayed for previous rows. Note 409 * where old_row is what was displayed for previous rows. Note
289 * that the first pass (pass == 0 really) will completely cover 410 * that the first pass (pass == 0 really) will completely cover
290 * the old row, so the rows do not have to be initialized. After 411 * the old row, so the rows do not have to be initialized. After
291 * the first pass (and only for interlaced images), you will have 412 * the first pass (and only for interlaced images), you will have
292 * to pass the current row, and the function will combine the 413 * to pass the current row, and the function will combine the
293 * old row and the new row. 414 * old row and the new row.
294 */ 415 */
295 416
296 bool hasAlpha = m_reader->hasAlpha(); 417 bool hasAlpha = m_hasAlphaChannel;
297 png_bytep row = rowBuffer; 418 png_bytep row = rowBuffer;
298 419
299 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { 420 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) {
300 unsigned colorChannels = hasAlpha ? 4 : 3; 421 unsigned colorChannels = hasAlpha ? 4 : 3;
301 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); 422 row = interlaceBuffer + (rowIndex * colorChannels * size().width());
302 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); 423 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer);
303 } 424 }
304 425
305 // Write the decoded row pixels to the frame buffer. The repetitive 426 // Write the decoded row pixels to the frame buffer. The repetitive
306 // form of the row write loops is for speed. 427 // form of the row write loops is for speed.
307 ImageFrame::PixelData* const dstRow = buffer.getAddr(0, y); 428 ImageFrame::PixelData* const dstRow = buffer.getAddr(frameRect.x(), y);
308 unsigned alphaMask = 255; 429 unsigned alphaMask = 255;
309 int width = size().width(); 430 int width = frameRect.width();
310 431
311 png_bytep srcPtr = row; 432 png_bytep srcPtr = row;
312 if (hasAlpha) { 433 if (hasAlpha) {
313 // Here we apply the color space transformation to the dst space. 434 // Here we apply the color space transformation to the dst space.
314 // It does not really make sense to transform to a gamma-encoded 435 // It does not really make sense to transform to a gamma-encoded
315 // space and then immediately after, perform a linear premultiply. 436 // space and then immediately after, perform a linear premultiply.
316 // Ideally we would pass kPremul_SkAlphaType to xform->apply(), 437 // Ideally we would pass kPremul_SkAlphaType to xform->apply(),
317 // instructing SkColorSpaceXform to perform the linear premultiply 438 // instructing SkColorSpaceXform to perform the linear premultiply
318 // while the pixels are a linear space. 439 // while the pixels are a linear space.
319 // We cannot do this because when we apply the gamma encoding after 440 // We cannot do this because when we apply the gamma encoding after
320 // the premultiply, we will very likely end up with valid pixels 441 // the premultiply, we will very likely end up with valid pixels
321 // where R, G, and/or B are greater than A. The legacy drawing 442 // where R, G, and/or B are greater than A. The legacy drawing
322 // pipeline does not know how to handle this. 443 // pipeline does not know how to handle this.
323 if (SkColorSpaceXform* xform = colorTransform()) { 444 if (SkColorSpaceXform* xform = colorTransform()) {
324 SkColorSpaceXform::ColorFormat colorFormat = 445 SkColorSpaceXform::ColorFormat colorFormat =
325 SkColorSpaceXform::kRGBA_8888_ColorFormat; 446 SkColorSpaceXform::kRGBA_8888_ColorFormat;
326 xform->apply(colorFormat, dstRow, colorFormat, srcPtr, size().width(), 447 xform->apply(colorFormat, dstRow, colorFormat, srcPtr, size().width(),
327 kUnpremul_SkAlphaType); 448 kUnpremul_SkAlphaType);
328 srcPtr = (png_bytep)dstRow; 449 srcPtr = (png_bytep)dstRow;
329 } 450 }
330 451
331 if (buffer.premultiplyAlpha()) { 452 if (m_frameBufferCache[m_currentFrame].getAlphaBlendSource() ==
332 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; 453 ImageFrame::BlendAtopBgcolor) {
333 dstPixel++, srcPtr += 4) { 454 if (buffer.premultiplyAlpha()) {
334 buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 455 for (auto* dstPixel = dstRow; dstPixel < dstRow + width;
335 srcPtr[3]); 456 dstPixel++, srcPtr += 4) {
336 alphaMask &= srcPtr[3]; 457 buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2],
458 srcPtr[3]);
459 alphaMask &= srcPtr[3];
460 }
461 } else {
462 for (auto* dstPixel = dstRow; dstPixel < dstRow + width;
463 dstPixel++, srcPtr += 4) {
464 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2],
465 srcPtr[3]);
466 alphaMask &= srcPtr[3];
467 }
337 } 468 }
338 } else { 469 } else {
339 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; 470 // Now, the blend method is ImageFrame::BlendAtopPreviousFrame. Since the
340 dstPixel++, srcPtr += 4) { 471 // frame data of the previous frame is copied at initFrameBuffer, we can
341 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], srcPtr[3]); 472 // blend the pixel of this frame, stored in |srcPtr|, over the previous
342 alphaMask &= srcPtr[3]; 473 // pixel stored in |dstPixel|.
474 if (buffer.premultiplyAlpha()) {
475 for (auto* dstPixel = dstRow; dstPixel < dstRow + width;
476 dstPixel++, srcPtr += 4) {
477 buffer.blendRGBAPremultiplied(dstPixel, srcPtr[0], srcPtr[1],
478 srcPtr[2], srcPtr[3]);
479 alphaMask &= srcPtr[3];
480 }
481 } else {
482 for (auto* dstPixel = dstRow; dstPixel < dstRow + width;
483 dstPixel++, srcPtr += 4) {
484 buffer.blendRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2],
485 srcPtr[3]);
486 alphaMask &= srcPtr[3];
487 }
343 } 488 }
344 } 489 }
490
491 if (alphaMask != 255 && !m_currentBufferSawAlpha)
492 m_currentBufferSawAlpha = true;
493
345 } else { 494 } else {
346 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; 495 for (auto* dstPixel = dstRow; dstPixel < dstRow + width;
347 dstPixel++, srcPtr += 3) { 496 dstPixel++, srcPtr += 3) {
348 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 255); 497 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 255);
349 } 498 }
350 499
351 // We'll apply the color space xform to opaque pixels after they have been 500 // We'll apply the color space xform to opaque pixels after they have been
352 // written to the ImageFrame, purely because SkColorSpaceXform supports 501 // written to the ImageFrame, purely because SkColorSpaceXform supports
353 // RGBA (and not RGB). 502 // RGBA (and not RGB).
354 if (SkColorSpaceXform* xform = colorTransform()) { 503 if (SkColorSpaceXform* xform = colorTransform()) {
355 xform->apply(xformColorFormat(), dstRow, xformColorFormat(), dstRow, 504 xform->apply(xformColorFormat(), dstRow, xformColorFormat(), dstRow,
356 size().width(), kOpaque_SkAlphaType); 505 size().width(), kOpaque_SkAlphaType);
357 } 506 }
358 } 507 }
359 508
360 if (alphaMask != 255 && !buffer.hasAlpha()) 509 buffer.setPixelsChanged(true);
361 buffer.setHasAlpha(true); 510 }
362 511
363 buffer.setPixelsChanged(true); 512 bool PNGImageDecoder::frameIsCompleteAtIndex(size_t index) const {
513 if (failed())
scroggo_chromium 2016/12/08 13:59:09 I just noticed that the other implementations do n
joostouwerling 2016/12/08 16:28:39 I'm not entirely sure why it was there in the firs
514 return false;
515
516 // For non-animated images, return whether the status of the frame is
517 // ImageFrame::FrameComplete through ImageDecoder::frameIsCompleteAtIndex.
518 // We only know that the frame is fully received when it is successfully
519 // decoded, since there is no explicit check whether the IEND chunk has been
520 // received.
521 if (index == 0 && m_reader->parseCompleted() && m_frameCount == 1)
522 return ImageDecoder::frameIsCompleteAtIndex(index);
523
524 // For first frames of animated images, PNGImageReader exposes whether it is
525 // fully received through firstFrameFullyReceived(). Non-first frames are
526 // reported by |m_reader| once it has parsed all data for that frame, so we
527 // can simply return if the index is below the reported frame count. We have
528 // to use |m_frameCount| here, and not |m_frameBufferCache|.size(), since
529 // |m_frameCount| gets adjusted when decoding or parsing fails, but
530 // |m_frameBufferCache| does not, as explained above at
531 // PNGImageDecoder::setFailed().
532 if (index == 0)
533 return m_reader->firstFrameFullyReceived();
534 return (index < m_frameCount);
535 }
536
537 float PNGImageDecoder::frameDurationAtIndex(size_t index) const {
538 return (index < m_frameCount ? m_frameBufferCache[index].duration() : 0);
364 } 539 }
365 540
366 void PNGImageDecoder::complete() { 541 void PNGImageDecoder::complete() {
367 if (m_frameBufferCache.isEmpty()) 542 if (m_frameBufferCache.isEmpty())
368 return; 543 return;
369 544
370 m_frameBufferCache[0].setStatus(ImageFrame::FrameComplete); 545 // We don't know for sure at this point if we've received enough pixels to
371 } 546 // completely fill the frame. This is not taken into account. The error could
547 // be in the indicated frame size, or in the frame data chunks.
548 //
549 // If the frame size is wrong, we could correct for that, but later frames
550 // may depend on this frame's size when they're initialized in
551 // initFrameBuffer(), in the case the disposal method of this frame is
552 // DisposeOverwriteBgColor. There is no informed decision that can be made
553 // about what the author intended, so we stay with the indicated frame size.
554 //
555 // In the case the data chunk is too small, there's not much we can do. This
556 // method only gets called when the frame end has been received, so the
557 // encoder indicated that all frame data is received. It could be an encoding
558 // error, or it may be an intentional file size reduction.
559 //
560 // Therefore, the frame size and pixels that aren't decoded are left as-is.
372 561
373 inline bool isComplete(const PNGImageDecoder* decoder) { 562 if (m_reader->interlaceBuffer())
374 return decoder->frameIsCompleteAtIndex(0); 563 m_reader->clearInterlaceBuffer();
375 }
376 564
377 void PNGImageDecoder::decode(bool onlySize) { 565 ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
378 if (failed()) 566 buffer.setStatus(ImageFrame::FrameComplete);
379 return;
380 567
381 if (!m_reader) 568 if (!m_currentBufferSawAlpha)
382 m_reader = makeUnique<PNGImageReader>(this, m_offset); 569 correctAlphaWhenFrameBufferSawNoAlpha(m_currentFrame);
383
384 // If we couldn't decode the image but have received all the data, decoding
385 // has failed.
386 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived())
387 setFailed();
388
389 // If decoding is done or failed, we don't need the PNGImageReader anymore.
390 if (isComplete(this) || failed())
391 m_reader.reset();
392 } 570 }
393 571
394 } // namespace blink 572 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698