1/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/codec/SkCodec.h"
9#include "include/core/SkCanvas.h"
10#include "include/core/SkData.h"
11#include "include/core/SkImage.h"
12#include "include/utils/SkAnimCodecPlayer.h"
13#include "src/codec/SkCodecImageGenerator.h"
14#include "src/core/SkPixmapPriv.h"
15#include <algorithm>
16
17SkAnimCodecPlayer::SkAnimCodecPlayer(std::unique_ptr<SkCodec> codec) : fCodec(std::move(codec)) {
18    fImageInfo = fCodec->getInfo();
19    fFrameInfos = fCodec->getFrameInfo();
20    fImages.resize(fFrameInfos.size());
21
22    // change the interpretation of fDuration to a end-time for that frame
23    size_t dur = 0;
24    for (auto& f : fFrameInfos) {
25        dur += f.fDuration;
26        f.fDuration = dur;
27    }
28    fTotalDuration = dur;
29
30    if (!fTotalDuration) {
31        // Static image -- may or may not have returned a single frame info.
32        fFrameInfos.clear();
33        fImages.clear();
34        fImages.push_back(SkImage::MakeFromGenerator(
35                              SkCodecImageGenerator::MakeFromCodec(std::move(fCodec))));
36    }
37}
38
39SkAnimCodecPlayer::~SkAnimCodecPlayer() {}
40
41SkISize SkAnimCodecPlayer::dimensions() const {
42    if (!fCodec) {
43        auto image = fImages.front();
44        return image ? image->dimensions() : SkISize::MakeEmpty();
45    }
46    if (SkEncodedOriginSwapsWidthHeight(fCodec->getOrigin())) {
47        return { fImageInfo.height(), fImageInfo.width() };
48    }
49    return { fImageInfo.width(), fImageInfo.height() };
50}
51
52sk_sp<SkImage> SkAnimCodecPlayer::getFrameAt(int index) {
53    SkASSERT((unsigned)index < fFrameInfos.size());
54
55    if (fImages[index]) {
56        return fImages[index];
57    }
58
59    size_t rb = fImageInfo.minRowBytes();
60    size_t size = fImageInfo.computeByteSize(rb);
61    auto data = SkData::MakeUninitialized(size);
62
63    SkCodec::Options opts;
64    opts.fFrameIndex = index;
65
66    const auto origin = fCodec->getOrigin();
67    const auto orientedDims = this->dimensions();
68    const auto originMatrix = SkEncodedOriginToMatrix(origin, orientedDims.width(),
69                                                              orientedDims.height());
70
71    SkPaint paint;
72    paint.setBlendMode(SkBlendMode::kSrc);
73
74    auto imageInfo = fImageInfo;
75    if (fFrameInfos[index].fAlphaType != kOpaque_SkAlphaType && imageInfo.isOpaque()) {
76        imageInfo = imageInfo.makeAlphaType(kPremul_SkAlphaType);
77    }
78    const int requiredFrame = fFrameInfos[index].fRequiredFrame;
79    if (requiredFrame != SkCodec::kNoFrame && fImages[requiredFrame]) {
80        auto requiredImage = fImages[requiredFrame];
81        auto canvas = SkCanvas::MakeRasterDirect(imageInfo, data->writable_data(), rb);
82        if (origin != kDefault_SkEncodedOrigin) {
83            // The required frame is stored after applying the origin. Undo that,
84            // because the codec decodes prior to applying the origin.
85            // FIXME: Another approach would be to decode the frame's delta on top
86            // of transparent black, and then draw that through the origin matrix
87            // onto the required frame. To do that, SkCodec needs to expose the
88            // rectangle of the delta and the blend mode, so we can handle
89            // kRestoreBGColor frames and Blend::kSrc.
90            SkMatrix inverse;
91            SkAssertResult(originMatrix.invert(&inverse));
92            canvas->concat(inverse);
93        }
94        canvas->drawImage(requiredImage, 0, 0, SkSamplingOptions(), &paint);
95        opts.fPriorFrame = requiredFrame;
96    }
97
98    if (SkCodec::kSuccess != fCodec->getPixels(imageInfo, data->writable_data(), rb, &opts)) {
99        return nullptr;
100    }
101
102    auto image = SkImage::MakeRasterData(imageInfo, std::move(data), rb);
103    if (origin != kDefault_SkEncodedOrigin) {
104        imageInfo = imageInfo.makeDimensions(orientedDims);
105        rb = imageInfo.minRowBytes();
106        size = imageInfo.computeByteSize(rb);
107        data = SkData::MakeUninitialized(size);
108        auto canvas = SkCanvas::MakeRasterDirect(imageInfo, data->writable_data(), rb);
109        canvas->concat(originMatrix);
110        canvas->drawImage(image, 0, 0, SkSamplingOptions(), &paint);
111        image = SkImage::MakeRasterData(imageInfo, std::move(data), rb);
112    }
113    return fImages[index] = image;
114}
115
116sk_sp<SkImage> SkAnimCodecPlayer::getFrame() {
117    SkASSERT(fTotalDuration > 0 || fImages.size() == 1);
118
119    return fTotalDuration > 0
120        ? this->getFrameAt(fCurrIndex)
121        : fImages.front();
122}
123
124bool SkAnimCodecPlayer::seek(uint32_t msec) {
125    if (!fTotalDuration) {
126        return false;
127    }
128
129    msec %= fTotalDuration;
130
131    auto lower = std::lower_bound(fFrameInfos.begin(), fFrameInfos.end(), msec,
132                                  [](const SkCodec::FrameInfo& info, uint32_t msec) {
133                                      return (uint32_t)info.fDuration <= msec;
134                                  });
135    int prevIndex = fCurrIndex;
136    fCurrIndex = lower - fFrameInfos.begin();
137    return fCurrIndex != prevIndex;
138}
139
140
141