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

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

Issue 2618633004: Add support for Animated PNG (Closed)
Patch Set: Respond to comments Created 3 years, 9 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 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"
42 #include "png.h"
43 #include "wtf/PtrUtil.h"
44 #include <memory>
45
46 namespace blink { 41 namespace blink {
47 42
48 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, 43 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption,
49 const ColorBehavior& colorBehavior, 44 const ColorBehavior& colorBehavior,
50 size_t maxDecodedBytes, 45 size_t maxDecodedBytes,
51 size_t offset) 46 size_t offset)
52 : ImageDecoder(alphaOption, colorBehavior, maxDecodedBytes), 47 : ImageDecoder(alphaOption, colorBehavior, maxDecodedBytes),
53 m_offset(offset) {} 48 m_offset(offset),
49 m_currentFrame(0),
50 // It would be logical to default to cAnimationNone, but BitmapImage uses
51 // that as a signal to never check again, meaning the actual count will
52 // never be respected.
53 m_repetitionCount(cAnimationLoopOnce),
54 m_hasAlphaChannel(false),
55 m_currentBufferSawAlpha(false) {}
54 56
55 PNGImageDecoder::~PNGImageDecoder() {} 57 PNGImageDecoder::~PNGImageDecoder() {}
56 58
59 bool PNGImageDecoder::setFailed() {
60 m_reader.reset();
61 return ImageDecoder::setFailed();
62 }
63
64 size_t PNGImageDecoder::decodeFrameCount() {
65 parse(ParseQuery::MetaData);
66 return failed() ? m_frameBufferCache.size() : m_reader->frameCount();
67 }
68
69 void PNGImageDecoder::decode(size_t index) {
70 parse(ParseQuery::MetaData);
71
72 if (failed())
73 return;
74
75 updateAggressivePurging(index);
76
77 Vector<size_t> framesToDecode = findFramesToDecode(index);
78 for (auto i = framesToDecode.rbegin(); i != framesToDecode.rend(); i++) {
79 m_currentFrame = *i;
80 if (!m_reader->decode(*m_data, *i)) {
81 setFailed();
82 return;
83 }
84
85 // If this returns false, we need more data to continue decoding.
86 if (!postDecodeProcessing(*i))
87 break;
88 }
89
90 // It is also a fatal error if all data is received and we have decoded all
91 // frames available but the file is truncated.
92 if (index >= m_frameBufferCache.size() - 1 && isAllDataReceived() &&
93 m_reader && !m_reader->parseCompleted())
94 setFailed();
95 }
96
97 void PNGImageDecoder::parse(ParseQuery query) {
98 if (failed() || (m_reader && m_reader->parseCompleted()))
99 return;
100
101 if (!m_reader)
102 m_reader = WTF::makeUnique<PNGImageReader>(this, m_offset);
103
104 if (!m_reader->parse(*m_data, query))
105 setFailed();
106 }
107
108 void PNGImageDecoder::clearFrameBuffer(size_t index) {
109 if (m_reader)
110 m_reader->clearDecodeState(index);
111 ImageDecoder::clearFrameBuffer(index);
112 }
113
114 bool PNGImageDecoder::canReusePreviousFrameBuffer(size_t index) const {
115 DCHECK(index < m_frameBufferCache.size());
116 return m_frameBufferCache[index].getDisposalMethod() !=
117 ImageFrame::DisposeOverwritePrevious;
118 }
119
120 void PNGImageDecoder::setRepetitionCount(int repetitionCount) {
121 m_repetitionCount = repetitionCount;
122 }
123
124 int PNGImageDecoder::repetitionCount() const {
125 return failed() ? cAnimationLoopOnce : m_repetitionCount;
126 }
127
128 void PNGImageDecoder::initializeNewFrame(size_t index) {
129 const PNGImageReader::FrameInfo& frameInfo = m_reader->frameInfo(index);
130 ImageFrame& buffer = m_frameBufferCache[index];
131
132 DCHECK(IntRect(IntPoint(), size()).contains(frameInfo.frameRect));
133 buffer.setOriginalFrameRect(frameInfo.frameRect);
134
135 buffer.setDuration(frameInfo.duration);
136 buffer.setDisposalMethod(frameInfo.disposalMethod);
137 buffer.setAlphaBlendSource(frameInfo.alphaBlend);
138
139 size_t previousFrameIndex = findRequiredPreviousFrame(index, false);
140 buffer.setRequiredPreviousFrameIndex(previousFrameIndex);
141 }
142
57 inline sk_sp<SkColorSpace> readColorSpace(png_structp png, png_infop info) { 143 inline sk_sp<SkColorSpace> readColorSpace(png_structp png, png_infop info) {
58 if (png_get_valid(png, info, PNG_INFO_sRGB)) 144 if (png_get_valid(png, info, PNG_INFO_sRGB))
59 return SkColorSpace::MakeSRGB(); 145 return SkColorSpace::MakeSRGB();
60 146
61 png_charp name; 147 png_charp name;
62 int compression; 148 int compression;
63 png_bytep profile; 149 png_bytep profile;
64 png_uint_32 length; 150 png_uint_32 length;
65 if (png_get_iCCP(png, info, &name, &compression, &profile, &length)) 151 if (png_get_iCCP(png, info, &name, &compression, &profile, &length))
66 return SkColorSpace::MakeICC(profile, length); 152 return SkColorSpace::MakeICC(profile, length);
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 fn.fG = 1.0f / pngFixedToFloat(inverseGamma); 190 fn.fG = 1.0f / pngFixedToFloat(inverseGamma);
105 fn.fA = 1.0f; 191 fn.fA = 1.0f;
106 fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f; 192 fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f;
107 193
108 return SkColorSpace::MakeRGB(fn, toXYZD50); 194 return SkColorSpace::MakeRGB(fn, toXYZD50);
109 } 195 }
110 196
111 void PNGImageDecoder::headerAvailable() { 197 void PNGImageDecoder::headerAvailable() {
112 png_structp png = m_reader->pngPtr(); 198 png_structp png = m_reader->pngPtr();
113 png_infop info = m_reader->infoPtr(); 199 png_infop info = m_reader->infoPtr();
114 png_uint_32 width = png_get_image_width(png, info);
115 png_uint_32 height = png_get_image_height(png, info);
116 200
117 // Protect against large PNGs. See http://bugzil.la/251381 for more details. 201 png_uint_32 width, height;
118 const unsigned long maxPNGSize = 1000000UL;
119 if (width > maxPNGSize || height > maxPNGSize) {
120 longjmp(JMPBUF(png), 1);
121 return;
122 }
123
124 // Set the image size now that the image header is available.
125 if (!setSize(width, height)) {
126 longjmp(JMPBUF(png), 1);
127 return;
128 }
129
130 int bitDepth, colorType, interlaceType, compressionType; 202 int bitDepth, colorType, interlaceType, compressionType;
131 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, 203 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType,
132 &interlaceType, &compressionType, nullptr); 204 &interlaceType, &compressionType, nullptr);
133 205
134 // The options we set here match what Mozilla does. 206 // The options we set here match what Mozilla does.
135 207
136 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. 208 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
137 if (colorType == PNG_COLOR_TYPE_PALETTE || 209 if (colorType == PNG_COLOR_TYPE_PALETTE ||
138 (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) 210 (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8))
139 png_set_expand(png); 211 png_set_expand(png);
140 212
141 if (png_get_valid(png, info, PNG_INFO_tRNS)) 213 if (png_get_valid(png, info, PNG_INFO_tRNS))
142 png_set_expand(png); 214 png_set_expand(png);
143 215
144 if (bitDepth == 16) 216 if (bitDepth == 16)
145 png_set_strip_16(png); 217 png_set_strip_16(png);
146 218
147 if (colorType == PNG_COLOR_TYPE_GRAY || 219 if (colorType == PNG_COLOR_TYPE_GRAY ||
148 colorType == PNG_COLOR_TYPE_GRAY_ALPHA) 220 colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
149 png_set_gray_to_rgb(png); 221 png_set_gray_to_rgb(png);
150 222
151 if ((colorType & PNG_COLOR_MASK_COLOR) && !ignoresColorSpace()) { 223 // Only set the size and the color space of the image once since non-first
152 // We only support color profiles for color PALETTE and RGB[A] PNG. 224 // frames also use this method: there is no per-frame color space, and the
153 // TODO(msarret): Add GRAY profile support, block CYMK? 225 // image size is determined from the header width and height.
154 if (sk_sp<SkColorSpace> colorSpace = readColorSpace(png, info)) 226 if (!isDecodedSizeAvailable()) {
155 setEmbeddedColorSpace(std::move(colorSpace)); 227 // Protect against large PNGs. See http://bugzil.la/251381 for more details.
228 const unsigned long maxPNGSize = 1000000UL;
229 if (width > maxPNGSize || height > maxPNGSize) {
230 longjmp(JMPBUF(png), 1);
231 return;
232 }
233
234 // Set the image size now that the image header is available.
235 if (!setSize(width, height)) {
236 longjmp(JMPBUF(png), 1);
237 return;
238 }
239
240 if ((colorType & PNG_COLOR_MASK_COLOR) && !ignoresColorSpace()) {
241 // We only support color profiles for color PALETTE and RGB[A] PNG.
242 // TODO(msarret): Add GRAY profile support, block CYMK?
243 if (sk_sp<SkColorSpace> colorSpace = readColorSpace(png, info))
244 setEmbeddedColorSpace(colorSpace);
245 }
156 } 246 }
157 247
158 if (!hasEmbeddedColorSpace()) { 248 if (!hasEmbeddedColorSpace()) {
159 const double inverseGamma = 0.45455; 249 const double inverseGamma = 0.45455;
160 const double defaultGamma = 2.2; 250 const double defaultGamma = 2.2;
161 double gamma; 251 double gamma;
162 if (!ignoresColorSpace() && png_get_gAMA(png, info, &gamma)) { 252 if (!ignoresColorSpace() && png_get_gAMA(png, info, &gamma)) {
163 const double maxGamma = 21474.83; 253 const double maxGamma = 21474.83;
164 if ((gamma <= 0.0) || (gamma > maxGamma)) { 254 if ((gamma <= 0.0) || (gamma > maxGamma)) {
165 gamma = inverseGamma; 255 gamma = inverseGamma;
166 png_set_gAMA(png, info, gamma); 256 png_set_gAMA(png, info, gamma);
167 } 257 }
168 png_set_gamma(png, defaultGamma, gamma); 258 png_set_gamma(png, defaultGamma, gamma);
169 } else { 259 } else {
170 png_set_gamma(png, defaultGamma, inverseGamma); 260 png_set_gamma(png, defaultGamma, inverseGamma);
171 } 261 }
172 } 262 }
173 263
264 DCHECK(isDecodedSizeAvailable());
265
174 // Tell libpng to send us rows for interlaced pngs. 266 // Tell libpng to send us rows for interlaced pngs.
175 if (interlaceType == PNG_INTERLACE_ADAM7) 267 if (interlaceType == PNG_INTERLACE_ADAM7)
176 png_set_interlace_handling(png); 268 png_set_interlace_handling(png);
177 269
178 // Update our info now (so we can get color channel info). 270 // Update our info now (so we can get color channel info).
179 png_read_update_info(png, info); 271 png_read_update_info(png, info);
180 272
181 int channels = png_get_channels(png, info); 273 int channels = png_get_channels(png, info);
182 DCHECK(channels == 3 || channels == 4); 274 DCHECK(channels == 3 || channels == 4);
183 m_reader->setHasAlpha(channels == 4); 275 m_hasAlphaChannel = (channels == 4);
184
185 if (m_reader->decodingSizeOnly()) {
186 // If we only needed the size, halt the reader.
187 #if PNG_LIBPNG_VER_MAJOR > 1 || \
188 (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
189 // Passing '0' tells png_process_data_pause() not to cache unprocessed data.
190 m_reader->setReadOffset(m_reader->currentBufferSize() -
191 png_process_data_pause(png, 0));
192 #else
193 m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size);
194 png->buffer_size = 0;
195 #endif
196 }
197 } 276 }
198 277
199 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, 278 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer,
200 unsigned rowIndex, 279 unsigned rowIndex,
201 int) { 280 int) {
202 if (m_frameBufferCache.isEmpty()) 281 if (m_currentFrame >= m_frameBufferCache.size())
203 return; 282 return;
204 283
205 // Initialize the framebuffer if needed. 284 ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
206 ImageFrame& buffer = m_frameBufferCache[0];
207 if (buffer.getStatus() == ImageFrame::FrameEmpty) { 285 if (buffer.getStatus() == ImageFrame::FrameEmpty) {
208 png_structp png = m_reader->pngPtr(); 286 png_structp png = m_reader->pngPtr();
209 if (!buffer.setSizeAndColorSpace(size().width(), size().height(), 287 if (!initFrameBuffer(m_currentFrame)) {
210 colorSpaceForSkImages())) {
211 longjmp(JMPBUF(png), 1); 288 longjmp(JMPBUF(png), 1);
212 return; 289 return;
213 } 290 }
214 291
215 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; 292 DCHECK_EQ(ImageFrame::FramePartial, buffer.getStatus());
293
216 if (PNG_INTERLACE_ADAM7 == 294 if (PNG_INTERLACE_ADAM7 ==
217 png_get_interlace_type(png, m_reader->infoPtr())) { 295 png_get_interlace_type(png, m_reader->infoPtr())) {
218 m_reader->createInterlaceBuffer(colorChannels * size().width() * 296 unsigned colorChannels = m_hasAlphaChannel ? 4 : 3;
219 size().height()); 297 m_reader->createInterlaceBuffer(colorChannels * size().area());
220 if (!m_reader->interlaceBuffer()) { 298 if (!m_reader->interlaceBuffer()) {
221 longjmp(JMPBUF(png), 1); 299 longjmp(JMPBUF(png), 1);
222 return; 300 return;
223 } 301 }
224 } 302 }
225 303
226 buffer.setStatus(ImageFrame::FramePartial); 304 m_currentBufferSawAlpha = false;
227 buffer.setHasAlpha(false); 305 }
228 306
229 // For PNGs, the frame always fills the entire image. 307 const IntRect& frameRect = buffer.originalFrameRect();
230 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); 308 DCHECK(IntRect(IntPoint(), size()).contains(frameRect));
231 }
232 309
233 /* libpng comments (here to explain what follows). 310 /* libpng comments (here to explain what follows).
234 * 311 *
235 * this function is called for every row in the image. If the 312 * this function is called for every row in the image. If the
236 * image is interlacing, and you turned on the interlace handler, 313 * image is interlacing, and you turned on the interlace handler,
237 * this function will be called for every row in every pass. 314 * this function will be called for every row in every pass.
238 * Some of these rows will not be changed from the previous pass. 315 * Some of these rows will not be changed from the previous pass.
239 * When the row is not changed, the new_row variable will be NULL. 316 * When the row is not changed, the new_row variable will be NULL.
240 * The rows and passes are called in order, so you don't really 317 * The rows and passes are called in order, so you don't really
241 * need the row_num and pass, but I'm supplying them because it 318 * need the row_num and pass, but I'm supplying them because it
242 * may make your life easier. 319 * may make your life easier.
243 */ 320 */
244 321
245 // Nothing to do if the row is unchanged, or the row is outside 322 // Nothing to do if the row is unchanged, or the row is outside the image
246 // the image bounds: libpng may send extra rows, ignore them to 323 // bounds. In the case that a frame presents more data than the indicated
247 // make our lives easier. 324 // frame size, ignore the extra rows and use the frame size as the source
325 // of truth. libpng can send extra rows: ignore them too, this to prevent
326 // memory writes outside of the image bounds (security).
248 if (!rowBuffer) 327 if (!rowBuffer)
249 return; 328 return;
250 int y = rowIndex; 329
251 if (y < 0 || y >= size().height()) 330 DCHECK_GT(frameRect.height(), 0);
331 if (rowIndex >= static_cast<unsigned>(frameRect.height()))
252 return; 332 return;
253 333
334 int y = rowIndex + frameRect.y();
335 if (y < 0)
336 return;
337 DCHECK_LT(y, size().height());
338
254 /* libpng comments (continued). 339 /* libpng comments (continued).
255 * 340 *
256 * For the non-NULL rows of interlaced images, you must call 341 * For the non-NULL rows of interlaced images, you must call
257 * png_progressive_combine_row() passing in the row and the 342 * png_progressive_combine_row() passing in the row and the
258 * old row. You can call this function for NULL rows (it will 343 * old row. You can call this function for NULL rows (it will
259 * just return) and for non-interlaced images (it just does the 344 * just return) and for non-interlaced images (it just does the
260 * memcpy for you) if it will make the code easier. Thus, you 345 * memcpy for you) if it will make the code easier. Thus, you
261 * can just do this for all cases: 346 * can just do this for all cases:
262 * 347 *
263 * png_progressive_combine_row(png_ptr, old_row, new_row); 348 * png_progressive_combine_row(png_ptr, old_row, new_row);
264 * 349 *
265 * where old_row is what was displayed for previous rows. Note 350 * where old_row is what was displayed for previous rows. Note
266 * that the first pass (pass == 0 really) will completely cover 351 * that the first pass (pass == 0 really) will completely cover
267 * the old row, so the rows do not have to be initialized. After 352 * the old row, so the rows do not have to be initialized. After
268 * the first pass (and only for interlaced images), you will have 353 * the first pass (and only for interlaced images), you will have
269 * to pass the current row, and the function will combine the 354 * to pass the current row, and the function will combine the
270 * old row and the new row. 355 * old row and the new row.
271 */ 356 */
272 357
273 bool hasAlpha = m_reader->hasAlpha(); 358 bool hasAlpha = m_hasAlphaChannel;
274 png_bytep row = rowBuffer; 359 png_bytep row = rowBuffer;
275 360
276 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { 361 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) {
277 unsigned colorChannels = hasAlpha ? 4 : 3; 362 unsigned colorChannels = hasAlpha ? 4 : 3;
278 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); 363 row = interlaceBuffer + (rowIndex * colorChannels * size().width());
279 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); 364 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer);
280 } 365 }
281 366
282 // Write the decoded row pixels to the frame buffer. The repetitive 367 // Write the decoded row pixels to the frame buffer. The repetitive
283 // form of the row write loops is for speed. 368 // form of the row write loops is for speed.
284 ImageFrame::PixelData* const dstRow = buffer.getAddr(0, y); 369 ImageFrame::PixelData* const dstRow = buffer.getAddr(frameRect.x(), y);
285 int width = size().width(); 370 int width = frameRect.width();
286 371
287 png_bytep srcPtr = row; 372 png_bytep srcPtr = row;
288 if (hasAlpha) { 373 if (hasAlpha) {
289 // Here we apply the color space transformation to the dst space. 374 // Here we apply the color space transformation to the dst space.
290 // It does not really make sense to transform to a gamma-encoded 375 // It does not really make sense to transform to a gamma-encoded
291 // space and then immediately after, perform a linear premultiply. 376 // space and then immediately after, perform a linear premultiply.
292 // Ideally we would pass kPremul_SkAlphaType to xform->apply(), 377 // Ideally we would pass kPremul_SkAlphaType to xform->apply(),
293 // instructing SkColorSpaceXform to perform the linear premultiply 378 // instructing SkColorSpaceXform to perform the linear premultiply
294 // while the pixels are a linear space. 379 // while the pixels are a linear space.
295 // We cannot do this because when we apply the gamma encoding after 380 // We cannot do this because when we apply the gamma encoding after
296 // the premultiply, we will very likely end up with valid pixels 381 // the premultiply, we will very likely end up with valid pixels
297 // where R, G, and/or B are greater than A. The legacy drawing 382 // where R, G, and/or B are greater than A. The legacy drawing
298 // pipeline does not know how to handle this. 383 // pipeline does not know how to handle this.
299 if (SkColorSpaceXform* xform = colorTransform()) { 384 if (SkColorSpaceXform* xform = colorTransform()) {
300 SkColorSpaceXform::ColorFormat colorFormat = 385 SkColorSpaceXform::ColorFormat colorFormat =
301 SkColorSpaceXform::kRGBA_8888_ColorFormat; 386 SkColorSpaceXform::kRGBA_8888_ColorFormat;
302 xform->apply(colorFormat, dstRow, colorFormat, srcPtr, size().width(), 387 xform->apply(colorFormat, dstRow, colorFormat, srcPtr, size().width(),
303 kUnpremul_SkAlphaType); 388 kUnpremul_SkAlphaType);
304 srcPtr = png_bytep(dstRow); 389 srcPtr = png_bytep(dstRow);
305 } 390 }
306 391
307 unsigned alphaMask = 255; 392 unsigned alphaMask = 255;
308 if (buffer.premultiplyAlpha()) { 393 if (m_frameBufferCache[m_currentFrame].getAlphaBlendSource() ==
309 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; 394 ImageFrame::BlendAtopBgcolor) {
310 srcPtr += 4, ++dstPixel) { 395 if (buffer.premultiplyAlpha()) {
311 buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 396 for (auto *dstPixel = dstRow; dstPixel < dstRow + width;
312 srcPtr[3]); 397 dstPixel++, srcPtr += 4) {
313 alphaMask &= srcPtr[3]; 398 buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2],
399 srcPtr[3]);
400 alphaMask &= srcPtr[3];
401 }
402 } else {
403 for (auto *dstPixel = dstRow; dstPixel < dstRow + width;
404 dstPixel++, srcPtr += 4) {
405 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2],
406 srcPtr[3]);
407 alphaMask &= srcPtr[3];
408 }
314 } 409 }
315 } else { 410 } else {
316 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; 411 // Now, the blend method is ImageFrame::BlendAtopPreviousFrame. Since the
317 srcPtr += 4, ++dstPixel) { 412 // frame data of the previous frame is copied at initFrameBuffer, we can
318 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], srcPtr[3]); 413 // blend the pixel of this frame, stored in |srcPtr|, over the previous
319 alphaMask &= srcPtr[3]; 414 // pixel stored in |dstPixel|.
415 if (buffer.premultiplyAlpha()) {
416 for (auto *dstPixel = dstRow; dstPixel < dstRow + width;
417 dstPixel++, srcPtr += 4) {
418 buffer.blendRGBAPremultiplied(dstPixel, srcPtr[0], srcPtr[1],
419 srcPtr[2], srcPtr[3]);
420 alphaMask &= srcPtr[3];
421 }
422 } else {
423 for (auto *dstPixel = dstRow; dstPixel < dstRow + width;
424 dstPixel++, srcPtr += 4) {
425 buffer.blendRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2],
426 srcPtr[3]);
427 alphaMask &= srcPtr[3];
428 }
320 } 429 }
321 } 430 }
322 431
323 if (alphaMask != 255 && !buffer.hasAlpha()) 432 if (alphaMask != 255)
324 buffer.setHasAlpha(true); 433 m_currentBufferSawAlpha = true;
325 434
326 } else { 435 } else {
327 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; 436 for (auto *dstPixel = dstRow; dstPixel < dstRow + width;
328 srcPtr += 3, ++dstPixel) { 437 srcPtr += 3, ++dstPixel) {
329 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 255); 438 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 255);
330 } 439 }
331 440
332 // We'll apply the color space xform to opaque pixels after they have been 441 // We'll apply the color space xform to opaque pixels after they have been
333 // written to the ImageFrame, purely because SkColorSpaceXform supports 442 // written to the ImageFrame, purely because SkColorSpaceXform supports
334 // RGBA (and not RGB). 443 // RGBA (and not RGB).
335 if (SkColorSpaceXform* xform = colorTransform()) { 444 if (SkColorSpaceXform* xform = colorTransform()) {
336 xform->apply(xformColorFormat(), dstRow, xformColorFormat(), dstRow, 445 xform->apply(xformColorFormat(), dstRow, xformColorFormat(), dstRow,
337 size().width(), kOpaque_SkAlphaType); 446 size().width(), kOpaque_SkAlphaType);
338 } 447 }
339 } 448 }
340 449
341 buffer.setPixelsChanged(true); 450 buffer.setPixelsChanged(true);
342 } 451 }
343 452
344 void PNGImageDecoder::complete() { 453 void PNGImageDecoder::frameComplete() {
345 if (m_frameBufferCache.isEmpty()) 454 if (m_currentFrame >= m_frameBufferCache.size())
346 return; 455 return;
347 456
348 m_frameBufferCache[0].setStatus(ImageFrame::FrameComplete); 457 if (m_reader->interlaceBuffer())
458 m_reader->clearInterlaceBuffer();
459
460 ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
461 if (buffer.getStatus() == ImageFrame::FrameEmpty) {
462 longjmp(JMPBUF(m_reader->pngPtr()), 1);
463 return;
464 }
465
466 if (!m_currentBufferSawAlpha)
467 correctAlphaWhenFrameBufferSawNoAlpha(m_currentFrame);
468
469 buffer.setStatus(ImageFrame::FrameComplete);
349 } 470 }
350 471
351 inline bool isComplete(const PNGImageDecoder* decoder) { 472 bool PNGImageDecoder::frameIsCompleteAtIndex(size_t index) const {
352 return decoder->frameIsCompleteAtIndex(0); 473 if (!isDecodedSizeAvailable())
474 return false;
475
476 DCHECK(!failed() && m_reader);
477
478 // For non-animated images, return whether the status of the frame is
479 // ImageFrame::FrameComplete with ImageDecoder::frameIsCompleteAtIndex.
480 // This matches the behavior of WEBPImageDecoder.
481 if (m_reader->parseCompleted() && m_reader->frameCount() == 1)
482 return ImageDecoder::frameIsCompleteAtIndex(index);
483
484 // For first frames of animated images, PNGImageReader exposes whether it is
485 // fully received through firstFrameFullyReceived().
486 if (!index)
Noel Gordon 2017/03/08 16:26:14 Seems fine. Maybe in future we should get rid of
scroggo_chromium 2017/03/08 20:53:22 Not sure I follow you. Do you mean m_reader->frame
Noel Gordon 2017/03/13 12:16:17 Not quite.
scroggo_chromium 2017/03/13 13:50:08 Done.
487 return m_reader->firstFrameFullyReceived();
488
489 // Non-first frames are reported by |m_reader| once it has parsed all data
490 // for that frame, so return whether the index is below the reported frame
491 // count.
492 return index < m_reader->frameCount();
353 } 493 }
354 494
355 void PNGImageDecoder::decode(bool onlySize) { 495 float PNGImageDecoder::frameDurationAtIndex(size_t index) const {
356 if (failed()) 496 if (index < m_frameBufferCache.size())
357 return; 497 return m_frameBufferCache[index].duration();
358 498 return 0;
359 if (!m_reader)
360 m_reader = WTF::makeUnique<PNGImageReader>(this, m_offset);
361
362 // If we couldn't decode the image but have received all the data, decoding
363 // has failed.
364 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived())
365 setFailed();
366
367 // If decoding is done or failed, we don't need the PNGImageReader anymore.
368 if (isComplete(this) || failed())
369 m_reader.reset();
370 } 499 }
371 500
372 } // namespace blink 501 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698