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

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

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

Powered by Google App Engine
This is Rietveld 408576698