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

Side by Side Diff: Source/core/platform/image-decoders/gif/GIFImageDecoder.cpp

Issue 15350006: Decode GIF image frames on demand. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 7 years, 7 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. All rights reserved. 2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
50 return; 50 return;
51 51
52 ImageDecoder::setData(data, allDataReceived); 52 ImageDecoder::setData(data, allDataReceived);
53 if (m_reader) 53 if (m_reader)
54 m_reader->setData(data); 54 m_reader->setData(data);
55 } 55 }
56 56
57 bool GIFImageDecoder::isSizeAvailable() 57 bool GIFImageDecoder::isSizeAvailable()
58 { 58 {
59 if (!ImageDecoder::isSizeAvailable()) 59 if (!ImageDecoder::isSizeAvailable())
60 decode(0, GIFSizeQuery); 60 parse(GIFSizeQuery);
61 61
62 return ImageDecoder::isSizeAvailable(); 62 return ImageDecoder::isSizeAvailable();
63 } 63 }
64 64
65 bool GIFImageDecoder::setSize(unsigned width, unsigned height) 65 bool GIFImageDecoder::setSize(unsigned width, unsigned height)
66 { 66 {
67 if (ImageDecoder::isSizeAvailable() && size() == IntSize(width, height)) 67 if (ImageDecoder::isSizeAvailable() && size() == IntSize(width, height))
68 return true; 68 return true;
69 69
70 if (!ImageDecoder::setSize(width, height)) 70 if (!ImageDecoder::setSize(width, height))
71 return false; 71 return false;
72 72
73 prepareScaleDataIfNecessary(); 73 prepareScaleDataIfNecessary();
74 return true; 74 return true;
75 } 75 }
76 76
77 size_t GIFImageDecoder::frameCount() 77 size_t GIFImageDecoder::frameCount()
78 { 78 {
79 decode(std::numeric_limits<unsigned>::max(), GIFFrameCountQuery); 79 parse(GIFFrameCountQuery);
80 return m_frameBufferCache.size(); 80 return m_frameBufferCache.size();
81 } 81 }
82 82
83 int GIFImageDecoder::repetitionCount() const 83 int GIFImageDecoder::repetitionCount() const
84 { 84 {
85 // This value can arrive at any point in the image data stream. Most GIFs 85 // This value can arrive at any point in the image data stream. Most GIFs
86 // in the wild declare it near the beginning of the file, so it usually is 86 // in the wild declare it near the beginning of the file, so it usually is
87 // set by the time we've decoded the size, but (depending on the GIF and the 87 // set by the time we've decoded the size, but (depending on the GIF and the
88 // packets sent back by the webserver) not always. If the reader hasn't 88 // packets sent back by the webserver) not always. If the reader hasn't
89 // seen a loop count yet, it will return cLoopCountNotSeen, in which case we 89 // seen a loop count yet, it will return cLoopCountNotSeen, in which case we
(...skipping 24 matching lines...) Expand all
114 } 114 }
115 115
116 ImageFrame* GIFImageDecoder::frameBufferAtIndex(size_t index) 116 ImageFrame* GIFImageDecoder::frameBufferAtIndex(size_t index)
117 { 117 {
118 if (index >= frameCount()) 118 if (index >= frameCount())
119 return 0; 119 return 0;
120 120
121 ImageFrame& frame = m_frameBufferCache[index]; 121 ImageFrame& frame = m_frameBufferCache[index];
122 if (frame.status() != ImageFrame::FrameComplete) { 122 if (frame.status() != ImageFrame::FrameComplete) {
123 PlatformInstrumentation::willDecodeImage("GIF"); 123 PlatformInstrumentation::willDecodeImage("GIF");
124 decode(index + 1, GIFFullQuery); 124 decode(index);
125 PlatformInstrumentation::didDecodeImage(); 125 PlatformInstrumentation::didDecodeImage();
126 } 126 }
127 return &frame; 127 return &frame;
128 } 128 }
129 129
130 bool GIFImageDecoder::frameIsCompleteAtIndex(size_t index) const 130 bool GIFImageDecoder::frameIsCompleteAtIndex(size_t index) const
131 { 131 {
132 return m_reader && (index < m_reader->imagesCount()) && m_reader->frameConte xt(index)->isComplete(); 132 return m_reader && (index < m_reader->imagesCount()) && m_reader->frameConte xt(index)->isComplete();
133 } 133 }
134 134
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
182 // has a disposal method other than DisposeOverwritePrevious, stop 182 // has a disposal method other than DisposeOverwritePrevious, stop
183 // scanning, as we'll only need this frame when decoding the next one. 183 // scanning, as we'll only need this frame when decoding the next one.
184 Vector<ImageFrame>::iterator i(end); 184 Vector<ImageFrame>::iterator i(end);
185 for (; (i != m_frameBufferCache.begin()) && ((i->status() == ImageFrame::Fra meEmpty) || (i->disposalMethod() == ImageFrame::DisposeOverwritePrevious)); --i) { 185 for (; (i != m_frameBufferCache.begin()) && ((i->status() == ImageFrame::Fra meEmpty) || (i->disposalMethod() == ImageFrame::DisposeOverwritePrevious)); --i) {
186 if ((i->status() == ImageFrame::FrameComplete) && (i != end)) 186 if ((i->status() == ImageFrame::FrameComplete) && (i != end))
187 i->clearPixelData(); 187 i->clearPixelData();
188 } 188 }
189 189
190 // Now |i| holds the last frame we need to preserve; clear prior frames. 190 // Now |i| holds the last frame we need to preserve; clear prior frames.
191 for (Vector<ImageFrame>::iterator j(m_frameBufferCache.begin()); j != i; ++j ) { 191 for (Vector<ImageFrame>::iterator j(m_frameBufferCache.begin()); j != i; ++j ) {
192 ASSERT(j->status() != ImageFrame::FramePartial); 192 ASSERT(j->status() != ImageFrame::FramePartial);
Alpha Left Google 2013/05/18 00:39:35 This condition can fail now with this change. Bec
Xianzhu 2013/05/20 04:30:59 Done.
193 if (j->status() != ImageFrame::FrameEmpty) 193 if (j->status() != ImageFrame::FrameEmpty)
194 j->clearPixelData(); 194 j->clearPixelData();
195 } 195 }
196 } 196 }
197 197
198 bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, const Vector<unsigned char>& rowBuffer, size_t width, size_t rowNumber, unsigned repeatCount, bool wri teTransparentPixels) 198 bool GIFImageDecoder::haveDecodedRow(size_t frameIndex, const Vector<unsigned ch ar>& rowBuffer, size_t width, size_t rowNumber, unsigned repeatCount, bool write TransparentPixels)
199 { 199 {
200 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); 200 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex);
201 // The pixel data and coordinates supplied to us are relative to the frame's 201 // The pixel data and coordinates supplied to us are relative to the frame's
202 // origin within the entire image size, i.e. 202 // origin within the entire image size, i.e.
203 // (frameContext->xOffset, frameContext->yOffset). There is no guarantee 203 // (frameContext->xOffset, frameContext->yOffset). There is no guarantee
204 // that width == (size().width() - frameContext->xOffset), so 204 // that width == (size().width() - frameContext->xOffset), so
205 // we must ensure we don't run off the end of either the source data or the 205 // we must ensure we don't run off the end of either the source data or the
206 // row's X-coordinates. 206 // row's X-coordinates.
207 int xBegin = upperBoundScaledX(frameContext->xOffset); 207 int xBegin = upperBoundScaledX(frameContext->xOffset);
208 int yBegin = upperBoundScaledY(frameContext->yOffset + rowNumber); 208 int yBegin = upperBoundScaledY(frameContext->yOffset + rowNumber);
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
251 ++currentAddress; 251 ++currentAddress;
252 } 252 }
253 253
254 // Tell the frame to copy the row data if need be. 254 // Tell the frame to copy the row data if need be.
255 if (repeatCount > 1) 255 if (repeatCount > 1)
256 buffer.copyRowNTimes(xBegin, xEnd, yBegin, yEnd); 256 buffer.copyRowNTimes(xBegin, xEnd, yBegin, yEnd);
257 257
258 return true; 258 return true;
259 } 259 }
260 260
261 bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, ImageFrame::FrameDisposalMethod disposalMethod) 261 bool GIFImageDecoder::frameComplete(size_t frameIndex, unsigned frameDuration, I mageFrame::FrameDisposalMethod disposalMethod)
262 { 262 {
263 // Initialize the frame if necessary. Some GIFs insert do-nothing frames, 263 // Initialize the frame if necessary. Some GIFs insert do-nothing frames,
264 // in which case we never reach haveDecodedRow() before getting here. 264 // in which case we never reach haveDecodedRow() before getting here.
265 ImageFrame& buffer = m_frameBufferCache[frameIndex]; 265 ImageFrame& buffer = m_frameBufferCache[frameIndex];
266 if ((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameInd ex)) 266 if ((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameInd ex))
267 return false; // initFrameBuffer() has already called setFailed(). 267 return false; // initFrameBuffer() has already called setFailed().
268 268
269 buffer.setStatus(ImageFrame::FrameComplete); 269 buffer.setStatus(ImageFrame::FrameComplete);
270 buffer.setDuration(frameDuration); 270 buffer.setDuration(frameDuration);
271 buffer.setDisposalMethod(disposalMethod); 271 buffer.setDisposalMethod(disposalMethod);
(...skipping 25 matching lines...) Expand all
297 // it had no alpha, and its rect is contained in the current frame's 297 // it had no alpha, and its rect is contained in the current frame's
298 // rect, we know the current frame has no alpha. 298 // rect, we know the current frame has no alpha.
299 if ((prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgc olor) && !prevBuffer->hasAlpha() && buffer.originalFrameRect().contains(prevBuff er->originalFrameRect())) 299 if ((prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgc olor) && !prevBuffer->hasAlpha() && buffer.originalFrameRect().contains(prevBuff er->originalFrameRect()))
300 buffer.setHasAlpha(false); 300 buffer.setHasAlpha(false);
301 } 301 }
302 } 302 }
303 303
304 return true; 304 return true;
305 } 305 }
306 306
307 void GIFImageDecoder::gifComplete() 307 void GIFImageDecoder::gifComplete()
Alpha Left Google 2013/05/18 00:39:35 I don't think this method is needed any more. Now
Xianzhu 2013/05/20 04:30:59 Done.
308 { 308 {
309 // Cache the repetition count, which is now as authoritative as it's ever 309 // Cache the repetition count, which is now as authoritative as it's ever
310 // going to be. 310 // going to be.
311 repetitionCount(); 311 repetitionCount();
312 } 312 }
313 313
314 void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query) 314 void GIFImageDecoder::parse(GIFParseQuery query)
315 { 315 {
316 if (failed()) 316 if (failed())
317 return; 317 return;
318 318
319 if (!m_reader) { 319 if (!m_reader) {
320 m_reader = adoptPtr(new GIFImageReader(this)); 320 m_reader = adoptPtr(new GIFImageReader(this));
321 m_reader->setData(m_data); 321 m_reader->setData(m_data);
322 } 322 }
323 323
324 if (query == GIFSizeQuery) { 324 if (!m_reader->parse(query)) {
325 if (!m_reader->decode(GIFSizeQuery, haltAtFrame))
326 setFailed();
327 return;
328 }
329
330 if (!m_reader->decode(GIFFrameCountQuery, haltAtFrame)) {
331 setFailed(); 325 setFailed();
332 return; 326 return;
333 } 327 }
334 328
335 const size_t oldSize = m_frameBufferCache.size(); 329 const size_t oldSize = m_frameBufferCache.size();
336 m_frameBufferCache.resize(m_reader->imagesCount()); 330 m_frameBufferCache.resize(m_reader->imagesCount());
337 for (size_t i = oldSize; i < m_reader->imagesCount(); ++i) 331 for (size_t i = oldSize; i < m_reader->imagesCount(); ++i)
338 m_frameBufferCache[i].setPremultiplyAlpha(m_premultiplyAlpha); 332 m_frameBufferCache[i].setPremultiplyAlpha(m_premultiplyAlpha);
333 }
339 334
340 if (query == GIFFrameCountQuery) 335 void GIFImageDecoder::decode(size_t frameIndex)
336 {
337 parse(GIFFrameCountQuery);
338
339 if (failed())
341 return; 340 return;
342 341
343 if (!m_reader->decode(GIFFullQuery, haltAtFrame)) { 342 BitVector frames(frameIndex);
Alpha Left Google 2013/05/18 00:39:35 Instead of a BitVector what about a vector of fram
Xianzhu 2013/05/20 04:30:59 Done.
343 getFramesToDecode(frameIndex, &frames);
344
345 if (!m_reader->decode(frames)) {
Alpha Left Google 2013/05/18 00:39:35 I'd prefer to call m_reader->decode(...) multiple
Xianzhu 2013/05/20 04:30:59 Done.
344 setFailed(); 346 setFailed();
345 return; 347 return;
346 } 348 }
347 349
348 // It is also a fatal error if all data is received and we have decoded all 350 // It is also a fatal error if all data is received and we have decoded all
349 // frames available but the file is truncated. 351 // frames available but the file is truncated.
350 if (haltAtFrame >= m_frameBufferCache.size() && isAllDataReceived() && m_rea der && !m_reader->parseCompleted()) 352 if (frameIndex >= m_frameBufferCache.size() - 1 && isAllDataReceived() && m_ reader && !m_reader->parseCompleted())
351 setFailed(); 353 setFailed();
Alpha Left Google 2013/05/18 00:39:35 The reason for calling setFailed() here is to stop
352 } 354 }
353 355
354 bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) 356 void GIFImageDecoder::getFramesToDecode(size_t frameIndex, BitVector* frames)
357 {
358 while (m_frameBufferCache[frameIndex].status() != ImageFrame::FrameComplete) {
359 frames->set(frameIndex);
360 frameIndex = getPreviousRequiredFrame(frameIndex);
361 if (frameIndex == notFound)
362 break;
363 }
364 }
365
366 size_t GIFImageDecoder::getPreviousRequiredFrame(size_t frameIndex)
Alpha Left Google 2013/05/18 00:39:35 I see this method will be called multiple times fo
Xianzhu 2013/05/20 04:30:59 Done.
367 {
368 if (!frameIndex) {
369 // The first frame doesn't rely on any previous data.
370 return notFound;
371 }
372
373 // The starting state for this frame depends on the previous frame's
374 // disposal method.
375 //
376 // Frames that use the DisposeOverwritePrevious method are effectively
377 // no-ops in terms of changing the starting state of a frame compared to
378 // the starting state of the previous frame, so skip over them. (If the
379 // first frame specifies this method, it will get treated like
380 // DisposeOverwriteBgcolor below and reset to a completely empty image.)
381 size_t prevFrame = frameIndex - 1;
382 const ImageFrame* prevBuffer = &m_frameBufferCache[prevFrame];
383 ImageFrame::FrameDisposalMethod prevMethod = prevBuffer->disposalMethod();
384 while (prevFrame && (prevMethod == ImageFrame::DisposeOverwritePrevious)) {
385 prevBuffer = &m_frameBufferCache[--prevFrame];
386 prevMethod = prevBuffer->disposalMethod();
387 }
388
389 if (prevMethod == ImageFrame::DisposeNotSpecified || prevMethod == ImageFram e::DisposeKeep) {
390 // prevFrame will be used as the starting state for this frame.
391 return prevFrame;
392 }
393
394 ASSERT(prevMethod == ImageFrame::DisposeOverwriteBgcolor);
395 const IntRect& prevRect = prevBuffer->originalFrameRect();
396 const IntSize& bufferSize = scaledSize();
397 if (!frameIndex || prevRect.contains(IntRect(IntPoint(), scaledSize()))) {
398 // The first frame or a frame covering the whole image will be cleared
399 // to background, so this frame doesn't rely on any previous data.
400 return notFound;
401 }
402
403 // prevFrame only clears part of the image to background, so it contributes
404 // to the starting state of this frame.
405 return prevFrame;
406 }
407
408 bool GIFImageDecoder::initFrameBuffer(size_t frameIndex)
355 { 409 {
356 // Initialize the frame rect in our buffer. 410 // Initialize the frame rect in our buffer.
357 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); 411 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex);
358 IntRect frameRect(frameContext->xOffset, frameContext->yOffset, frameContext ->width, frameContext->height); 412 IntRect frameRect(frameContext->xOffset, frameContext->yOffset, frameContext ->width, frameContext->height);
359 413
360 // Make sure the frameRect doesn't extend outside the buffer. 414 // Make sure the frameRect doesn't extend outside the buffer.
361 if (frameRect.maxX() > size().width()) 415 if (frameRect.maxX() > size().width())
362 frameRect.setWidth(size().width() - frameContext->xOffset); 416 frameRect.setWidth(size().width() - frameContext->xOffset);
363 if (frameRect.maxY() > size().height()) 417 if (frameRect.maxY() > size().height())
364 frameRect.setHeight(size().height() - frameContext->yOffset); 418 frameRect.setHeight(size().height() - frameContext->yOffset);
365 419
366 ImageFrame* const buffer = &m_frameBufferCache[frameIndex]; 420 ImageFrame* const buffer = &m_frameBufferCache[frameIndex];
367 int left = upperBoundScaledX(frameRect.x()); 421 int left = upperBoundScaledX(frameRect.x());
368 int right = lowerBoundScaledX(frameRect.maxX(), left); 422 int right = lowerBoundScaledX(frameRect.maxX(), left);
369 int top = upperBoundScaledY(frameRect.y()); 423 int top = upperBoundScaledY(frameRect.y());
370 int bottom = lowerBoundScaledY(frameRect.maxY(), top); 424 int bottom = lowerBoundScaledY(frameRect.maxY(), top);
371 buffer->setOriginalFrameRect(IntRect(left, top, right - left, bottom - top)) ; 425 buffer->setOriginalFrameRect(IntRect(left, top, right - left, bottom - top)) ;
372 426
373 if (!frameIndex) { 427 size_t previousRequiredFrame = getPreviousRequiredFrame(frameIndex);
374 // This is the first frame, so we're not relying on any previous data. 428 if (previousRequiredFrame == notFound) {
429 // This frame doesn't rely on any previous data.
375 if (!buffer->setSize(scaledSize().width(), scaledSize().height())) 430 if (!buffer->setSize(scaledSize().width(), scaledSize().height()))
376 return setFailed(); 431 return setFailed();
377 } else { 432 } else {
378 // The starting state for this frame depends on the previous frame's 433 const ImageFrame* prevBuffer = &m_frameBufferCache[previousRequiredFrame ];
379 // disposal method.
380 //
381 // Frames that use the DisposeOverwritePrevious method are effectively
382 // no-ops in terms of changing the starting state of a frame compared to
383 // the starting state of the previous frame, so skip over them. (If the
384 // first frame specifies this method, it will get treated like
385 // DisposeOverwriteBgcolor below and reset to a completely empty image.)
386 const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex];
387 ImageFrame::FrameDisposalMethod prevMethod = prevBuffer->disposalMethod( );
388 while (frameIndex && (prevMethod == ImageFrame::DisposeOverwritePrevious )) {
389 prevBuffer = &m_frameBufferCache[--frameIndex];
390 prevMethod = prevBuffer->disposalMethod();
391 }
392 ASSERT(prevBuffer->status() == ImageFrame::FrameComplete); 434 ASSERT(prevBuffer->status() == ImageFrame::FrameComplete);
393 435
394 if ((prevMethod == ImageFrame::DisposeNotSpecified) || (prevMethod == Im ageFrame::DisposeKeep)) { 436 // Preserve the last frame as the starting state for this frame.
395 // Preserve the last frame as the starting state for this frame. 437 if (!buffer->copyBitmapData(*prevBuffer))
396 if (!buffer->copyBitmapData(*prevBuffer)) 438 return setFailed();
397 return setFailed(); 439
398 } else { 440 if (prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) {
399 // We want to clear the previous frame to transparent, without 441 // We want to clear the previous frame to transparent, without
400 // affecting pixels in the image outside of the frame. 442 // affecting pixels in the image outside of the frame.
401 const IntRect& prevRect = prevBuffer->originalFrameRect(); 443 const IntRect& prevRect = prevBuffer->originalFrameRect();
402 const IntSize& bufferSize = scaledSize(); 444 for (int y = prevRect.y(); y < prevRect.maxY(); ++y) {
403 if (!frameIndex || prevRect.contains(IntRect(IntPoint(), scaledSize( )))) { 445 for (int x = prevRect.x(); x < prevRect.maxX(); ++x)
404 // Clearing the first frame, or a frame the size of the whole 446 buffer->setRGBA(x, y, 0, 0, 0, 0);
405 // image, results in a completely empty image.
406 if (!buffer->setSize(bufferSize.width(), bufferSize.height()))
407 return setFailed();
408 } else {
409 // Copy the whole previous buffer, then clear just its frame.
410 if (!buffer->copyBitmapData(*prevBuffer))
411 return setFailed();
412 for (int y = prevRect.y(); y < prevRect.maxY(); ++y) {
413 for (int x = prevRect.x(); x < prevRect.maxX(); ++x)
414 buffer->setRGBA(x, y, 0, 0, 0, 0);
415 }
416 if ((prevRect.width() > 0) && (prevRect.height() > 0))
417 buffer->setHasAlpha(true);
418 } 447 }
448 if ((prevRect.width() > 0) && (prevRect.height() > 0))
449 buffer->setHasAlpha(true);
419 } 450 }
420 } 451 }
421 452
422 // Update our status to be partially complete. 453 // Update our status to be partially complete.
423 buffer->setStatus(ImageFrame::FramePartial); 454 buffer->setStatus(ImageFrame::FramePartial);
424 455
425 // Reset the alpha pixel tracker for this frame. 456 // Reset the alpha pixel tracker for this frame.
426 m_currentBufferSawAlpha = false; 457 m_currentBufferSawAlpha = false;
427 return true; 458 return true;
428 } 459 }
429 460
430 } // namespace WebCore 461 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698