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

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

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

Powered by Google App Engine
This is Rietveld 408576698