Make SkCodec more flexible about its required frame

SkCodec sets fRequiredFrame to be the earliest possible frame that a
given frame can depend on. e.g.

- Frame A fills the screen, Keep
- Frame B does not cover A, Keep
- Frame C covers B but not A, and is opaque

Frame C can depend on either A or B. SkCodec already reports that C
depends on A. This CL allows a client of SkCodec to use either A or
B to create C.

Also expose the DisposalMethod. Since any frame between A and C can
be used to create C except for DisposePrevious frames, the client
needs to be able to know the disposal method so they do not try to
use such a frame to create C.

Further, the disposal method can be used to give the client a better
idea whether they will continue to need a frame. (e.g. if frame i is
DisposePrevious and depends on i-1, the client may not want to steal
i-1 to create i, since i+1 may also depend on i-1.)

TODO: Share code for decoding prior frames between GIF and WEBP

Change-Id: I91a5ae22ba3d8dfbe0bde833fa67ae3da0d81ed6
Reviewed-on: https://skia-review.googlesource.com/13722
Reviewed-by: Mike Reed <reed@google.com>
Reviewed-by: Chris Blume <cblume@chromium.org>
Reviewed-by: Matt Sarett <msarett@google.com>
Commit-Queue: Leon Scroggins <scroggo@google.com>
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
index 21d45da..f702a3a 100644
--- a/src/codec/SkWebpCodec.cpp
+++ b/src/codec/SkWebpCodec.cpp
@@ -7,6 +7,8 @@
 
 #include "SkBitmap.h"
 #include "SkCanvas.h"
+#include "SkCodecAnimation.h"
+#include "SkCodecAnimationPriv.h"
 #include "SkCodecPriv.h"
 #include "SkColorSpaceXform.h"
 #include "SkRasterPipeline.h"
@@ -253,8 +255,8 @@
         Frame* frame = fFrameHolder.appendNewFrame(iter.has_alpha);
         frame->setXYWH(iter.x_offset, iter.y_offset, iter.width, iter.height);
         frame->setDisposalMethod(iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ?
-                SkCodecAnimation::RestoreBGColor_DisposalMethod :
-                SkCodecAnimation::Keep_DisposalMethod);
+                SkCodecAnimation::DisposalMethod::kRestoreBGColor :
+                SkCodecAnimation::DisposalMethod::kKeep);
         frame->setDuration(iter.duration);
         if (WEBP_MUX_BLEND != iter.blend_method) {
             frame->setBlend(SkCodecAnimation::Blend::kBG);
@@ -292,6 +294,7 @@
         // animated image.
         frameInfo->fFullyReceived = true;
         frameInfo->fAlphaType = alpha_type(frame->hasAlpha());
+        frameInfo->fDisposalMethod = frame->getDisposalMethod();
     }
 
     return true;
@@ -437,9 +440,15 @@
             SkSampler::Fill(dstInfo, dst, rowBytes, 0, options.fZeroInitialized);
         }
     } else {
-        if (!options.fHasPriorFrame) {
+        // FIXME: Share with GIF
+        if (options.fPriorFrame != kNone) {
+            if (options.fPriorFrame < requiredFrame || options.fPriorFrame >= index) {
+                return kInvalidParameters;
+            }
+        } else {
             Options prevFrameOpts(options);
             prevFrameOpts.fFrameIndex = requiredFrame;
+            prevFrameOpts.fPriorFrame = kNone;
             const auto result = this->getPixels(dstInfo, dst, rowBytes, &prevFrameOpts,
                                                 nullptr, nullptr);
             switch (result) {
@@ -452,16 +461,20 @@
             }
         }
 
-        // Dispose bg color
-        const Frame* priorFrame = fFrameHolder.frame(requiredFrame);
-        if (priorFrame->getDisposalMethod() == SkCodecAnimation::RestoreBGColor_DisposalMethod) {
-            // FIXME: If we add support for scaling/subsets, this rectangle needs to be adjusted.
-            const auto priorRect = priorFrame->frameRect();
-            const auto info = dstInfo.makeWH(priorRect.width(), priorRect.height());
-            const size_t bpp = SkColorTypeBytesPerPixel(dstInfo.colorType());
-            const size_t offset = priorRect.x() * bpp + priorRect.y() * rowBytes;
-            auto* eraseDst = SkTAddOffset<void>(dst, offset);
-            SkSampler::Fill(info, eraseDst, rowBytes, 0, kNo_ZeroInitialized);
+        if (options.fPriorFrame == requiredFrame || options.fPriorFrame == kNone) {
+            // Dispose bg color
+            const Frame* priorFrame = fFrameHolder.frame(requiredFrame);
+            if (priorFrame->getDisposalMethod()
+                    == SkCodecAnimation::DisposalMethod::kRestoreBGColor) {
+                // FIXME: If we add support for scaling/subsets, this rectangle needs to be
+                // adjusted.
+                const auto priorRect = priorFrame->frameRect();
+                const auto info = dstInfo.makeWH(priorRect.width(), priorRect.height());
+                const size_t bpp = SkColorTypeBytesPerPixel(dstInfo.colorType());
+                const size_t offset = priorRect.x() * bpp + priorRect.y() * rowBytes;
+                auto* eraseDst = SkTAddOffset<void>(dst, offset);
+                SkSampler::Fill(info, eraseDst, rowBytes, 0, kNo_ZeroInitialized);
+            }
         }
     }