Add SkCodec methods for individual frames

Add a version of getFrameInfo that returns information about a single
frame, allowing a client to skip creating the entire vector.

Add getFrameCount, for determining the number of frames in the image.

Reimplement std::vector<FrameInfo> getFrameInfo with the new methods.

Updates to the test:
- getFrameInfo(size_t, FrameInfo*) fails before parsing
- Test both versions of getFrameInfo
- Recreate the codec between tests, to test parsing

Change-Id: I77c19087f2f8dcf2c536d80167b18ad1ca96ae94
Reviewed-on: https://skia-review.googlesource.com/13190
Reviewed-by: Matt Sarett <msarett@google.com>
Reviewed-by: Mike Reed <reed@google.com>
Reviewed-by: Chris Blume <cblume@google.com>
Commit-Queue: Leon Scroggins <scroggo@google.com>
diff --git a/tests/CodecAnimTest.cpp b/tests/CodecAnimTest.cpp
index dea4adb..02253ef 100644
--- a/tests/CodecAnimTest.cpp
+++ b/tests/CodecAnimTest.cpp
@@ -94,20 +94,25 @@
     #undef kUnpremul
 
     for (const auto& rec : gRecs) {
-        std::unique_ptr<SkStream> stream(GetResourceAsStream(rec.fName));
-        if (!stream) {
+        sk_sp<SkData> data(GetResourceAsData(rec.fName));
+        if (!data) {
             // Useful error statement, but sometimes people run tests without
             // resources, and they do not want to see these messages.
             //ERRORF(r, "Missing resources? Could not find '%s'", rec.fName);
             continue;
         }
 
-        std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
+        std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
         if (!codec) {
             ERRORF(r, "Failed to create an SkCodec from '%s'", rec.fName);
             continue;
         }
 
+        {
+            SkCodec::FrameInfo frameInfo;
+            REPORTER_ASSERT(r, !codec->getFrameInfo(0, &frameInfo));
+        }
+
         const int repetitionCount = codec->getRepetitionCount();
         if (repetitionCount != rec.fRepetitionCount) {
             ERRORF(r, "%s repetition count does not match! expected: %i\tactual: %i",
@@ -115,112 +120,153 @@
         }
 
         const size_t expected = rec.fFrameCount;
-        const auto frameInfos = codec->getFrameInfo();
-        // getFrameInfo returns empty set for non-animated.
-        const size_t frameCount = frameInfos.size() == 0 ? 1 : frameInfos.size();
-        if (frameCount != expected) {
-            ERRORF(r, "'%s' expected frame count: %i\tactual: %i", rec.fName, expected, frameCount);
-            continue;
-        }
-
         if (rec.fRequiredFrames.size() + 1 != expected) {
             ERRORF(r, "'%s' has wrong number entries in fRequiredFrames; expected: %i\tactual: %i",
                    rec.fName, expected, rec.fRequiredFrames.size() + 1);
             continue;
         }
 
-        if (1 == frameCount) {
-            continue;
-        }
-
-        auto to_string = [](SkAlphaType type) {
-            switch (type) {
-                case kUnpremul_SkAlphaType:
-                    return "unpremul";
-                case kOpaque_SkAlphaType:
-                    return "opaque";
-                default:
-                    return "other";
-            }
-        };
-        // From here on, we are only concerned with animated images.
-        REPORTER_ASSERT(r, frameInfos[0].fRequiredFrame == SkCodec::kNone);
-        REPORTER_ASSERT(r, frameInfos[0].fAlphaType == codec->getInfo().alphaType());
-        for (size_t i = 1; i < frameCount; i++) {
-            if (rec.fRequiredFrames[i-1] != frameInfos[i].fRequiredFrame) {
-                ERRORF(r, "%s's frame %i has wrong dependency! expected: %i\tactual: %i",
-                       rec.fName, i, rec.fRequiredFrames[i-1], frameInfos[i].fRequiredFrame);
-            }
-            auto expectedAlpha = rec.fAlphaTypes[i-1];
-            auto alpha = frameInfos[i].fAlphaType;
-            if (expectedAlpha != alpha) {
-                ERRORF(r, "%s's frame %i has wrong alpha type! expected: %s\tactual: %s",
-                       rec.fName, i, to_string(expectedAlpha), to_string(alpha));
-            }
-        }
-
-        // Compare decoding in two ways:
-        // 1. Provide the frame that a frame depends on, so the codec just has to blend.
-        //    (in the array cachedFrames)
-        // 2. Do not provide the frame that a frame depends on, so the codec has to decode all the
-        //    way back to a key-frame. (in a local variable uncachedFrame)
-        // The two should look the same.
-        std::vector<SkBitmap> cachedFrames(frameCount);
-        const auto& info = codec->getInfo().makeColorType(kN32_SkColorType);
-
-        auto decode = [&](SkBitmap* bm, bool cached, size_t index) {
-            bm->allocPixels(info);
-            if (cached) {
-                // First copy the pixels from the cached frame
-                const size_t requiredFrame = frameInfos[index].fRequiredFrame;
-                if (requiredFrame != SkCodec::kNone) {
-                    const bool success = cachedFrames[requiredFrame].copyTo(bm);
-                    REPORTER_ASSERT(r, success);
-                }
-            }
-            SkCodec::Options opts;
-            opts.fFrameIndex = index;
-            opts.fHasPriorFrame = cached;
-            const SkCodec::Result result = codec->getPixels(info, bm->getPixels(), bm->rowBytes(),
-                                                            &opts, nullptr, nullptr);
-            REPORTER_ASSERT(r, result == SkCodec::kSuccess);
-        };
-
-        for (size_t i = 0; i < frameCount; i++) {
-            SkBitmap& cachedFrame = cachedFrames[i];
-            decode(&cachedFrame, true, i);
-            SkBitmap uncachedFrame;
-            decode(&uncachedFrame, false, i);
-
-            // Now verify they're equal.
-            const size_t rowLen = info.bytesPerPixel() * info.width();
-            for (int y = 0; y < info.height(); y++) {
-                const void* cachedAddr = cachedFrame.getAddr(0, y);
-                SkASSERT(cachedAddr != nullptr);
-                const void* uncachedAddr = uncachedFrame.getAddr(0, y);
-                SkASSERT(uncachedAddr != nullptr);
-                const bool lineMatches = memcmp(cachedAddr, uncachedAddr, rowLen) == 0;
-                if (!lineMatches) {
-                    SkString name = SkStringPrintf("cached_%i", i);
-                    write_bm(name.c_str(), cachedFrame);
-                    name = SkStringPrintf("uncached_%i", i);
-                    write_bm(name.c_str(), uncachedFrame);
-                    ERRORF(r, "%s's frame %i is different depending on caching!", rec.fName, i);
-                    break;
-                }
-            }
-        }
-
         if (rec.fDurations.size() != expected) {
             ERRORF(r, "'%s' has wrong number entries in fDurations; expected: %i\tactual: %i",
                    rec.fName, expected, rec.fDurations.size());
             continue;
         }
 
-        for (size_t i = 0; i < frameCount; i++) {
-            if (rec.fDurations[i] != frameInfos[i].fDuration) {
-                ERRORF(r, "%s frame %i's durations do not match! expected: %i\tactual: %i",
-                       rec.fName, i, rec.fDurations[i], frameInfos[i].fDuration);
+        enum class TestMode {
+            kVector,
+            kIndividual,
+        };
+
+        for (auto mode : { TestMode::kVector, TestMode::kIndividual }) {
+            // Re-create the codec to reset state and test parsing.
+            codec.reset(SkCodec::NewFromData(data));
+
+            size_t frameCount;
+            std::vector<SkCodec::FrameInfo> frameInfos;
+            switch (mode) {
+                case TestMode::kVector:
+                    frameInfos = codec->getFrameInfo();
+                    // getFrameInfo returns empty set for non-animated.
+                    frameCount = frameInfos.empty() ? 1 : frameInfos.size();
+                    break;
+                case TestMode::kIndividual:
+                    frameCount = codec->getFrameCount();
+                    break;
+            }
+
+            if (frameCount != expected) {
+                ERRORF(r, "'%s' expected frame count: %i\tactual: %i",
+                       rec.fName, expected, frameCount);
+                continue;
+            }
+
+            // From here on, we are only concerned with animated images.
+            if (1 == frameCount) {
+                continue;
+            }
+
+            for (size_t i = 0; i < frameCount; i++) {
+                SkCodec::FrameInfo frameInfo;
+                switch (mode) {
+                    case TestMode::kVector:
+                        frameInfo = frameInfos[i];
+                        break;
+                    case TestMode::kIndividual:
+                        REPORTER_ASSERT(r, codec->getFrameInfo(i, nullptr));
+                        REPORTER_ASSERT(r, codec->getFrameInfo(i, &frameInfo));
+                        break;
+                }
+
+                if (rec.fDurations[i] != frameInfo.fDuration) {
+                    ERRORF(r, "%s frame %i's durations do not match! expected: %i\tactual: %i",
+                           rec.fName, i, rec.fDurations[i], frameInfo.fDuration);
+                }
+
+                if (0 == i) {
+                    REPORTER_ASSERT(r, frameInfo.fRequiredFrame == SkCodec::kNone);
+                    REPORTER_ASSERT(r, frameInfo.fAlphaType == codec->getInfo().alphaType());
+                    continue;
+                }
+
+                if (rec.fRequiredFrames[i-1] != frameInfo.fRequiredFrame) {
+                    ERRORF(r, "%s's frame %i has wrong dependency! expected: %i\tactual: %i",
+                           rec.fName, i, rec.fRequiredFrames[i-1], frameInfo.fRequiredFrame);
+                }
+
+                auto to_string = [](SkAlphaType type) {
+                    switch (type) {
+                        case kUnpremul_SkAlphaType:
+                            return "unpremul";
+                        case kOpaque_SkAlphaType:
+                            return "opaque";
+                        default:
+                            return "other";
+                    }
+                };
+
+                auto expectedAlpha = rec.fAlphaTypes[i-1];
+                auto alpha = frameInfo.fAlphaType;
+                if (expectedAlpha != alpha) {
+                    ERRORF(r, "%s's frame %i has wrong alpha type! expected: %s\tactual: %s",
+                           rec.fName, i, to_string(expectedAlpha), to_string(alpha));
+                }
+            }
+
+            if (TestMode::kIndividual == mode) {
+                // No need to test decoding twice.
+                return;
+            }
+
+            // Compare decoding in two ways:
+            // 1. Provide the frame that a frame depends on, so the codec just has to blend.
+            //    (in the array cachedFrames)
+            // 2. Do not provide the frame that a frame depends on, so the codec has to decode
+            //    all the way back to a key-frame. (in a local variable uncachedFrame)
+            // The two should look the same.
+            std::vector<SkBitmap> cachedFrames(frameCount);
+            const auto& info = codec->getInfo().makeColorType(kN32_SkColorType);
+
+            auto decode = [&](SkBitmap* bm, bool cached, size_t index) {
+                bm->allocPixels(info);
+                if (cached) {
+                    // First copy the pixels from the cached frame
+                    const size_t requiredFrame = frameInfos[index].fRequiredFrame;
+                    if (requiredFrame != SkCodec::kNone) {
+                        const bool success = cachedFrames[requiredFrame].copyTo(bm);
+                        REPORTER_ASSERT(r, success);
+                    }
+                }
+                SkCodec::Options opts;
+                opts.fFrameIndex = index;
+                opts.fHasPriorFrame = cached;
+                auto result = codec->getPixels(info, bm->getPixels(), bm->rowBytes(),
+                                               &opts, nullptr, nullptr);
+                REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+            };
+
+            for (size_t i = 0; i < frameCount; i++) {
+                SkBitmap& cachedFrame = cachedFrames[i];
+                decode(&cachedFrame, true, i);
+                SkBitmap uncachedFrame;
+                decode(&uncachedFrame, false, i);
+
+                // Now verify they're equal.
+                const size_t rowLen = info.bytesPerPixel() * info.width();
+                for (int y = 0; y < info.height(); y++) {
+                    const void* cachedAddr = cachedFrame.getAddr(0, y);
+                    SkASSERT(cachedAddr != nullptr);
+                    const void* uncachedAddr = uncachedFrame.getAddr(0, y);
+                    SkASSERT(uncachedAddr != nullptr);
+                    const bool lineMatches = memcmp(cachedAddr, uncachedAddr, rowLen) == 0;
+                    if (!lineMatches) {
+                        SkString name = SkStringPrintf("cached_%i", i);
+                        write_bm(name.c_str(), cachedFrame);
+                        name = SkStringPrintf("uncached_%i", i);
+                        write_bm(name.c_str(), uncachedFrame);
+                        ERRORF(r, "%s's frame %i is different depending on caching!", rec.fName, i);
+                        break;
+                    }
+                }
             }
         }
     }