2 * Copyright 2018 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "include/utils/SkAnimCodecPlayer.h"
10 #include "include/codec/SkCodec.h"
11 #include "include/codec/SkEncodedOrigin.h"
12 #include "include/core/SkAlphaType.h"
13 #include "include/core/SkBlendMode.h"
14 #include "include/core/SkCanvas.h"
15 #include "include/core/SkData.h"
16 #include "include/core/SkImage.h"
17 #include "include/core/SkImageInfo.h"
18 #include "include/core/SkMatrix.h"
19 #include "include/core/SkPaint.h"
20 #include "include/core/SkRefCnt.h"
21 #include "include/core/SkSamplingOptions.h"
22 #include "include/core/SkSize.h"
23 #include "include/core/SkTypes.h"
24 #include "src/codec/SkCodecImageGenerator.h"
31 SkAnimCodecPlayer::SkAnimCodecPlayer(std::unique_ptr<SkCodec> codec) : fCodec(std::move(codec)) {
32 fImageInfo = fCodec->getInfo();
33 fFrameInfos = fCodec->getFrameInfo();
34 fImages.resize(fFrameInfos.size());
36 // change the interpretation of fDuration to a end-time for that frame
38 for (auto& f : fFrameInfos) {
44 if (!fTotalDuration) {
45 // Static image -- may or may not have returned a single frame info.
48 fImages.push_back(SkImage::MakeFromGenerator(
49 SkCodecImageGenerator::MakeFromCodec(std::move(fCodec))));
53 SkAnimCodecPlayer::~SkAnimCodecPlayer() {}
55 SkISize SkAnimCodecPlayer::dimensions() const {
57 auto image = fImages.front();
58 return image ? image->dimensions() : SkISize::MakeEmpty();
60 if (SkEncodedOriginSwapsWidthHeight(fCodec->getOrigin())) {
61 return { fImageInfo.height(), fImageInfo.width() };
63 return { fImageInfo.width(), fImageInfo.height() };
66 sk_sp<SkImage> SkAnimCodecPlayer::getFrameAt(int index) {
67 SkASSERT((unsigned)index < fFrameInfos.size());
70 return fImages[index];
73 size_t rb = fImageInfo.minRowBytes();
74 size_t size = fImageInfo.computeByteSize(rb);
75 auto data = SkData::MakeUninitialized(size);
77 SkCodec::Options opts;
78 opts.fFrameIndex = index;
80 const auto origin = fCodec->getOrigin();
81 const auto orientedDims = this->dimensions();
82 const auto originMatrix = SkEncodedOriginToMatrix(origin, orientedDims.width(),
83 orientedDims.height());
86 paint.setBlendMode(SkBlendMode::kSrc);
88 auto imageInfo = fImageInfo;
89 if (fFrameInfos[index].fAlphaType != kOpaque_SkAlphaType && imageInfo.isOpaque()) {
90 imageInfo = imageInfo.makeAlphaType(kPremul_SkAlphaType);
92 const int requiredFrame = fFrameInfos[index].fRequiredFrame;
93 if (requiredFrame != SkCodec::kNoFrame && fImages[requiredFrame]) {
94 auto requiredImage = fImages[requiredFrame];
95 auto canvas = SkCanvas::MakeRasterDirect(imageInfo, data->writable_data(), rb);
96 if (origin != kDefault_SkEncodedOrigin) {
97 // The required frame is stored after applying the origin. Undo that,
98 // because the codec decodes prior to applying the origin.
99 // FIXME: Another approach would be to decode the frame's delta on top
100 // of transparent black, and then draw that through the origin matrix
101 // onto the required frame. To do that, SkCodec needs to expose the
102 // rectangle of the delta and the blend mode, so we can handle
103 // kRestoreBGColor frames and Blend::kSrc.
105 SkAssertResult(originMatrix.invert(&inverse));
106 canvas->concat(inverse);
108 canvas->drawImage(requiredImage, 0, 0, SkSamplingOptions(), &paint);
109 opts.fPriorFrame = requiredFrame;
112 if (SkCodec::kSuccess != fCodec->getPixels(imageInfo, data->writable_data(), rb, &opts)) {
116 auto image = SkImage::MakeRasterData(imageInfo, std::move(data), rb);
117 if (origin != kDefault_SkEncodedOrigin) {
118 imageInfo = imageInfo.makeDimensions(orientedDims);
119 rb = imageInfo.minRowBytes();
120 size = imageInfo.computeByteSize(rb);
121 data = SkData::MakeUninitialized(size);
122 auto canvas = SkCanvas::MakeRasterDirect(imageInfo, data->writable_data(), rb);
123 canvas->concat(originMatrix);
124 canvas->drawImage(image, 0, 0, SkSamplingOptions(), &paint);
125 image = SkImage::MakeRasterData(imageInfo, std::move(data), rb);
127 return fImages[index] = image;
130 sk_sp<SkImage> SkAnimCodecPlayer::getFrame() {
131 SkASSERT(fTotalDuration > 0 || fImages.size() == 1);
133 return fTotalDuration > 0
134 ? this->getFrameAt(fCurrIndex)
138 bool SkAnimCodecPlayer::seek(uint32_t msec) {
139 if (!fTotalDuration) {
143 msec %= fTotalDuration;
145 auto lower = std::lower_bound(fFrameInfos.begin(), fFrameInfos.end(), msec,
146 [](const SkCodec::FrameInfo& info, uint32_t msec) {
147 return (uint32_t)info.fDuration <= msec;
149 int prevIndex = fCurrIndex;
150 fCurrIndex = lower - fFrameInfos.begin();
151 return fCurrIndex != prevIndex;