- // Let's resize our buffer now to the correct width/height and then
- // initialize portions of it if needed.
- RGBA32Array& bytes = buffer.bytes();
-
- // If the disposal method of the previous frame said to stick around, then we need
- // to copy that frame into our frame. We also dont want to have any impact on
- // anything outside our frame's rect, so if we don't overlay the entire image,
- // then also composite with the previous frame.
- if (previousBuffer && (compositeWithPreviousFrame || isSubRect)) {
- bytes = previousBuffer->bytes();
- buffer.ensureHeight(m_size.height());
- buffer.setHasAlpha(previousBuffer->hasAlpha());
- }
- else // Resize to the width and height of the image.
- bytes.resize(m_size.width() * m_size.height());
-
- if (isSubRect) {
- // We need to go ahead and initialize the first frame to make sure
- // that areas outside the subrect start off transparent.
- if (!previousBuffer) {
- bytes.fill(0);
- buffer.setHasAlpha(true);
- } else if (!compositeWithPreviousFrame) {
- // Now this is an interesting case. In the case where we fill
- // the entire image, we effectively do a full clear of the image (and thus
- // don't have to initialize anything in our buffer).
- //
- // However in the case where we only fill a piece of the image, two problems occur:
- // (1) We need to wipe out the area occupied by the previous frame, which
- // could also have been a subrect.
- // (2) Anything outside the previous frame's rect *and* outside our current
- // frame's rect should be left alone.
- // We have handled (2) by just initializing our buffer from the previous frame.
- // Our subrect will correctly overwrite the previous frame's contents as we
- // decode rows. However that still leaves the problem of having to wipe out
- // the area occupied by the previous frame that does not overlap with
- // the new frame.
- if (previousBuffer->rect() != frameRect) {
- // We have to restore the entire previous subframe with the first frame's contents.
- RGBA32Buffer* firstBuffer = &m_frameBufferCache[0];
- bool sawAlpha = buffer.hasAlpha();
- IntRect prevRect = previousBuffer->rect();
- unsigned end = prevRect.y() + prevRect.height();
-
- // Given that we allocate buffers to be the same size as previous buffers,
- // I think this assert should be valid.
- ASSERT(IntRect(IntPoint(0,0), m_size).contains(firstBuffer->rect()));
-
- for (unsigned i = prevRect.y(); i < end; i++) {
- unsigned* curr = buffer.bytes().data() + (i * m_size.width() + prevRect.x());
- unsigned* orig = firstBuffer->bytes().data() + (i * m_size.width() + prevRect.x());
- unsigned* end = curr + prevRect.width();
- unsigned* origEnd = orig + firstBuffer->rect().width();
-
- while (curr != end && orig != origEnd) {
- if (!sawAlpha) {
- sawAlpha = true;
- buffer.setHasAlpha(true);
- }
- *curr++ = *orig++;
- }
- }
+ if (frameIndex == 0) {
+ // This is the first frame, so we're not relying on any previous data.
+ prepEmptyFrameBuffer(buffer);
+ } else {
+ // The starting state for this frame depends on the previous frame's
+ // disposal method.
+ //
+ // Frames that use the DisposeOverwritePrevious method are effectively
+ // no-ops in terms of changing the starting state of a frame compared to
+ // the starting state of the previous frame, so skip over them. (If the
+ // first frame specifies this method, it will get treated like
+ // DisposeOverwriteBgcolor below and reset to a completely empty image.)
+ const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex];
+ RGBA32Buffer::FrameDisposalMethod prevMethod =
+ prevBuffer->disposalMethod();
+ while ((frameIndex > 0) &&
+ (prevMethod == RGBA32Buffer::DisposeOverwritePrevious)) {
+ prevBuffer = &m_frameBufferCache[--frameIndex];
+ prevMethod = prevBuffer->disposalMethod();
+ }
+
+ if ((prevMethod == RGBA32Buffer::DisposeNotSpecified) ||
+ (prevMethod == RGBA32Buffer::DisposeKeep)) {
+ // Preserve the last frame as the starting state for this frame.
+ buffer->bytes() = prevBuffer->bytes();
+ } else {
+ // We want to clear the previous frame to transparent, without
+ // affecting pixels in the image outside of the frame.
+ const IntRect& prevRect = prevBuffer->rect();
+ if ((frameIndex == 0) ||
+ prevRect.contains(IntRect(IntPoint(0, 0), m_size))) {
+ // Clearing the first frame, or a frame the size of the whole
+ // image, results in a completely empty image.
+ prepEmptyFrameBuffer(buffer);
+ } else {
+ // Copy the whole previous buffer, then clear just its frame.
+ buffer->bytes() = prevBuffer->bytes();
+ for (int y = prevRect.y(); y < prevRect.bottom(); ++y) {
+ unsigned* const currentRow =
+ buffer->bytes().data() + (y * m_size.width());
+ for (int x = prevRect.x(); x < prevRect.right(); ++x)
+ buffer->setRGBA(*(currentRow + x), 0, 0, 0, 0);
+ }
+ if ((prevRect.width() > 0) && (prevRect.height() > 0))
+ buffer->setHasAlpha(true);