• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SkAnimCodecPlayer(std::unique_ptr<SkCodec> codec)17 SkAnimCodecPlayer::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 
~SkAnimCodecPlayer()39 SkAnimCodecPlayer::~SkAnimCodecPlayer() {}
40 
dimensions() const41 SkISize 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 
getFrameAt(int index)52 sk_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 
getFrame()116 sk_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 
seek(uint32_t msec)124 bool 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