1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2015 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkCodec.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkCodecAnimation.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkPixmapUtils.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorPriv.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorType.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h" // IWYU pragma: keep
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skcms/skcms.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkNoDestructor.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkCodecPriv.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkFrameHolder.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkPixmapUtilsPriv.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkSampler.h"
29*c8dee2aaSAndroid Build Coastguard Worker
30*c8dee2aaSAndroid Build Coastguard Worker #include <string>
31*c8dee2aaSAndroid Build Coastguard Worker #include <string_view>
32*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
33*c8dee2aaSAndroid Build Coastguard Worker
34*c8dee2aaSAndroid Build Coastguard Worker #if !defined(SK_DISABLE_LEGACY_INIT_DECODERS)
35*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkOnce.h"
36*c8dee2aaSAndroid Build Coastguard Worker
37*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_AVIF)
38*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkAvifDecoder.h"
39*c8dee2aaSAndroid Build Coastguard Worker #endif
40*c8dee2aaSAndroid Build Coastguard Worker
41*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_BMP)
42*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkBmpDecoder.h"
43*c8dee2aaSAndroid Build Coastguard Worker #endif
44*c8dee2aaSAndroid Build Coastguard Worker
45*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_GIF) || defined(SK_HAS_WUFFS_LIBRARY)
46*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkGifDecoder.h"
47*c8dee2aaSAndroid Build Coastguard Worker #endif
48*c8dee2aaSAndroid Build Coastguard Worker
49*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_HAS_HEIF_LIBRARY)
50*c8dee2aaSAndroid Build Coastguard Worker #include "include/android/SkHeifDecoder.h"
51*c8dee2aaSAndroid Build Coastguard Worker #endif
52*c8dee2aaSAndroid Build Coastguard Worker
53*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_ICO)
54*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkIcoDecoder.h"
55*c8dee2aaSAndroid Build Coastguard Worker #endif
56*c8dee2aaSAndroid Build Coastguard Worker
57*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_JPEG)
58*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkJpegDecoder.h"
59*c8dee2aaSAndroid Build Coastguard Worker #endif
60*c8dee2aaSAndroid Build Coastguard Worker
61*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_JPEGXL)
62*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkJpegxlDecoder.h"
63*c8dee2aaSAndroid Build Coastguard Worker #endif
64*c8dee2aaSAndroid Build Coastguard Worker
65*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_PNG)
66*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkPngDecoder.h"
67*c8dee2aaSAndroid Build Coastguard Worker #endif
68*c8dee2aaSAndroid Build Coastguard Worker
69*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_RAW)
70*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkRawDecoder.h"
71*c8dee2aaSAndroid Build Coastguard Worker #endif
72*c8dee2aaSAndroid Build Coastguard Worker
73*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_WBMP)
74*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkWbmpDecoder.h"
75*c8dee2aaSAndroid Build Coastguard Worker #endif
76*c8dee2aaSAndroid Build Coastguard Worker
77*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_WEBP)
78*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkWebpDecoder.h"
79*c8dee2aaSAndroid Build Coastguard Worker #endif
80*c8dee2aaSAndroid Build Coastguard Worker #endif // !defined(SK_DISABLE_LEGACY_INIT_DECODERS)
81*c8dee2aaSAndroid Build Coastguard Worker
82*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
83*c8dee2aaSAndroid Build Coastguard Worker #include "cutils/properties.h"
84*c8dee2aaSAndroid Build Coastguard Worker #endif // defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
85*c8dee2aaSAndroid Build Coastguard Worker
86*c8dee2aaSAndroid Build Coastguard Worker namespace SkCodecs {
87*c8dee2aaSAndroid Build Coastguard Worker // A static variable inside a function avoids a static initializer.
88*c8dee2aaSAndroid Build Coastguard Worker // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/static_initializers.md#removing-static-initializers
get_decoders_for_editing()89*c8dee2aaSAndroid Build Coastguard Worker static std::vector<Decoder>* get_decoders_for_editing() {
90*c8dee2aaSAndroid Build Coastguard Worker static SkNoDestructor<std::vector<Decoder>> decoders;
91*c8dee2aaSAndroid Build Coastguard Worker #if !defined(SK_DISABLE_LEGACY_INIT_DECODERS)
92*c8dee2aaSAndroid Build Coastguard Worker static SkOnce once;
93*c8dee2aaSAndroid Build Coastguard Worker once([] {
94*c8dee2aaSAndroid Build Coastguard Worker if (decoders->empty()) {
95*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_PNG)
96*c8dee2aaSAndroid Build Coastguard Worker decoders->push_back(SkPngDecoder::Decoder());
97*c8dee2aaSAndroid Build Coastguard Worker #endif
98*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_JPEG)
99*c8dee2aaSAndroid Build Coastguard Worker decoders->push_back(SkJpegDecoder::Decoder());
100*c8dee2aaSAndroid Build Coastguard Worker #endif
101*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_WEBP)
102*c8dee2aaSAndroid Build Coastguard Worker decoders->push_back(SkWebpDecoder::Decoder());
103*c8dee2aaSAndroid Build Coastguard Worker #endif
104*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_GIF) || defined(SK_HAS_WUFFS_LIBRARY)
105*c8dee2aaSAndroid Build Coastguard Worker decoders->push_back(SkGifDecoder::Decoder());
106*c8dee2aaSAndroid Build Coastguard Worker #endif
107*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_ICO)
108*c8dee2aaSAndroid Build Coastguard Worker decoders->push_back(SkIcoDecoder::Decoder());
109*c8dee2aaSAndroid Build Coastguard Worker #endif
110*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_BMP)
111*c8dee2aaSAndroid Build Coastguard Worker decoders->push_back(SkBmpDecoder::Decoder());
112*c8dee2aaSAndroid Build Coastguard Worker #endif
113*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_WBMP)
114*c8dee2aaSAndroid Build Coastguard Worker decoders->push_back(SkWbmpDecoder::Decoder());
115*c8dee2aaSAndroid Build Coastguard Worker #endif
116*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_AVIF)
117*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
118*c8dee2aaSAndroid Build Coastguard Worker // Register CrabbyAvif based SkAvifDecoder on the Android framework
119*c8dee2aaSAndroid Build Coastguard Worker // if it is allowed. Otherwise Android framework will use
120*c8dee2aaSAndroid Build Coastguard Worker // SkHeifDecoder for decoding AVIF.
121*c8dee2aaSAndroid Build Coastguard Worker // TODO: Codec registration for the Android framework has to be
122*c8dee2aaSAndroid Build Coastguard Worker // moved outside of skia and this logic has to be moved there.
123*c8dee2aaSAndroid Build Coastguard Worker if (property_get_int32("media.avif.crabbyavif", 0) != 0) {
124*c8dee2aaSAndroid Build Coastguard Worker decoders->push_back(SkAvifDecoder::CrabbyAvif::Decoder());
125*c8dee2aaSAndroid Build Coastguard Worker }
126*c8dee2aaSAndroid Build Coastguard Worker #else
127*c8dee2aaSAndroid Build Coastguard Worker decoders->push_back(SkAvifDecoder::LibAvif::Decoder());
128*c8dee2aaSAndroid Build Coastguard Worker #endif
129*c8dee2aaSAndroid Build Coastguard Worker #endif
130*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_JPEGXL)
131*c8dee2aaSAndroid Build Coastguard Worker decoders->push_back(SkJpegxlDecoder::Decoder());
132*c8dee2aaSAndroid Build Coastguard Worker #endif
133*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_HAS_HEIF_LIBRARY)
134*c8dee2aaSAndroid Build Coastguard Worker decoders->push_back(SkHeifDecoder::Decoder());
135*c8dee2aaSAndroid Build Coastguard Worker #endif
136*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_RAW)
137*c8dee2aaSAndroid Build Coastguard Worker decoders->push_back(SkRawDecoder::Decoder());
138*c8dee2aaSAndroid Build Coastguard Worker #endif
139*c8dee2aaSAndroid Build Coastguard Worker }
140*c8dee2aaSAndroid Build Coastguard Worker });
141*c8dee2aaSAndroid Build Coastguard Worker #endif // !defined(SK_DISABLE_LEGACY_INIT_DECODERS)
142*c8dee2aaSAndroid Build Coastguard Worker return decoders.get();
143*c8dee2aaSAndroid Build Coastguard Worker }
144*c8dee2aaSAndroid Build Coastguard Worker
get_decoders()145*c8dee2aaSAndroid Build Coastguard Worker const std::vector<Decoder>& get_decoders() {
146*c8dee2aaSAndroid Build Coastguard Worker auto decoders = get_decoders_for_editing();
147*c8dee2aaSAndroid Build Coastguard Worker return *decoders;
148*c8dee2aaSAndroid Build Coastguard Worker }
149*c8dee2aaSAndroid Build Coastguard Worker
Register(Decoder d)150*c8dee2aaSAndroid Build Coastguard Worker void Register(Decoder d) {
151*c8dee2aaSAndroid Build Coastguard Worker auto decoders = get_decoders_for_editing();
152*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; i < decoders->size(); i++) {
153*c8dee2aaSAndroid Build Coastguard Worker if ((*decoders)[i].id == d.id) {
154*c8dee2aaSAndroid Build Coastguard Worker (*decoders)[i] = d;
155*c8dee2aaSAndroid Build Coastguard Worker return;
156*c8dee2aaSAndroid Build Coastguard Worker }
157*c8dee2aaSAndroid Build Coastguard Worker }
158*c8dee2aaSAndroid Build Coastguard Worker decoders->push_back(d);
159*c8dee2aaSAndroid Build Coastguard Worker }
160*c8dee2aaSAndroid Build Coastguard Worker
HasDecoder(std::string_view id)161*c8dee2aaSAndroid Build Coastguard Worker bool HasDecoder(std::string_view id) {
162*c8dee2aaSAndroid Build Coastguard Worker for (const SkCodecs::Decoder& decoder : get_decoders()) {
163*c8dee2aaSAndroid Build Coastguard Worker if (decoder.id == id) {
164*c8dee2aaSAndroid Build Coastguard Worker return true;
165*c8dee2aaSAndroid Build Coastguard Worker }
166*c8dee2aaSAndroid Build Coastguard Worker }
167*c8dee2aaSAndroid Build Coastguard Worker return false;
168*c8dee2aaSAndroid Build Coastguard Worker }
169*c8dee2aaSAndroid Build Coastguard Worker
170*c8dee2aaSAndroid Build Coastguard Worker } // namespace SkCodecs
171*c8dee2aaSAndroid Build Coastguard Worker
MakeFromStream(std::unique_ptr<SkStream> stream,Result * outResult,SkPngChunkReader * chunkReader,SelectionPolicy selectionPolicy)172*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkCodec> SkCodec::MakeFromStream(
173*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkStream> stream, Result* outResult,
174*c8dee2aaSAndroid Build Coastguard Worker SkPngChunkReader* chunkReader, SelectionPolicy selectionPolicy) {
175*c8dee2aaSAndroid Build Coastguard Worker return MakeFromStream(std::move(stream), SkCodecs::get_decoders(), outResult,
176*c8dee2aaSAndroid Build Coastguard Worker chunkReader, selectionPolicy);
177*c8dee2aaSAndroid Build Coastguard Worker }
MakeFromStream(std::unique_ptr<SkStream> stream,SkSpan<const SkCodecs::Decoder> decoders,Result * outResult,SkPngChunkReader * chunkReader,SelectionPolicy selectionPolicy)178*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkCodec> SkCodec::MakeFromStream(
179*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkStream> stream, SkSpan<const SkCodecs::Decoder> decoders,
180*c8dee2aaSAndroid Build Coastguard Worker Result* outResult, SkPngChunkReader* chunkReader, SelectionPolicy selectionPolicy) {
181*c8dee2aaSAndroid Build Coastguard Worker Result resultStorage;
182*c8dee2aaSAndroid Build Coastguard Worker if (!outResult) {
183*c8dee2aaSAndroid Build Coastguard Worker outResult = &resultStorage;
184*c8dee2aaSAndroid Build Coastguard Worker }
185*c8dee2aaSAndroid Build Coastguard Worker
186*c8dee2aaSAndroid Build Coastguard Worker if (!stream) {
187*c8dee2aaSAndroid Build Coastguard Worker *outResult = kInvalidInput;
188*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
189*c8dee2aaSAndroid Build Coastguard Worker }
190*c8dee2aaSAndroid Build Coastguard Worker
191*c8dee2aaSAndroid Build Coastguard Worker if (selectionPolicy != SelectionPolicy::kPreferStillImage
192*c8dee2aaSAndroid Build Coastguard Worker && selectionPolicy != SelectionPolicy::kPreferAnimation) {
193*c8dee2aaSAndroid Build Coastguard Worker *outResult = kInvalidParameters;
194*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
195*c8dee2aaSAndroid Build Coastguard Worker }
196*c8dee2aaSAndroid Build Coastguard Worker
197*c8dee2aaSAndroid Build Coastguard Worker constexpr size_t bytesToRead = MinBufferedBytesNeeded();
198*c8dee2aaSAndroid Build Coastguard Worker
199*c8dee2aaSAndroid Build Coastguard Worker char buffer[bytesToRead];
200*c8dee2aaSAndroid Build Coastguard Worker size_t bytesRead = stream->peek(buffer, bytesToRead);
201*c8dee2aaSAndroid Build Coastguard Worker
202*c8dee2aaSAndroid Build Coastguard Worker // It is also possible to have a complete image less than bytesToRead bytes
203*c8dee2aaSAndroid Build Coastguard Worker // (e.g. a 1 x 1 wbmp), meaning peek() would return less than bytesToRead.
204*c8dee2aaSAndroid Build Coastguard Worker // Assume that if bytesRead < bytesToRead, but > 0, the stream is shorter
205*c8dee2aaSAndroid Build Coastguard Worker // than bytesToRead, so pass that directly to the decoder.
206*c8dee2aaSAndroid Build Coastguard Worker // It also is possible the stream uses too small a buffer for peeking, but
207*c8dee2aaSAndroid Build Coastguard Worker // we trust the caller to use a large enough buffer.
208*c8dee2aaSAndroid Build Coastguard Worker
209*c8dee2aaSAndroid Build Coastguard Worker if (0 == bytesRead) {
210*c8dee2aaSAndroid Build Coastguard Worker // TODO: After implementing peek in CreateJavaOutputStreamAdaptor.cpp, this
211*c8dee2aaSAndroid Build Coastguard Worker // printf could be useful to notice failures.
212*c8dee2aaSAndroid Build Coastguard Worker // SkCodecPrintf("Encoded image data failed to peek!\n");
213*c8dee2aaSAndroid Build Coastguard Worker
214*c8dee2aaSAndroid Build Coastguard Worker // It is possible the stream does not support peeking, but does support
215*c8dee2aaSAndroid Build Coastguard Worker // rewinding.
216*c8dee2aaSAndroid Build Coastguard Worker // Attempt to read() and pass the actual amount read to the decoder.
217*c8dee2aaSAndroid Build Coastguard Worker bytesRead = stream->read(buffer, bytesToRead);
218*c8dee2aaSAndroid Build Coastguard Worker if (!stream->rewind()) {
219*c8dee2aaSAndroid Build Coastguard Worker SkCodecPrintf("Encoded image data could not peek or rewind to determine format!\n");
220*c8dee2aaSAndroid Build Coastguard Worker *outResult = kCouldNotRewind;
221*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
222*c8dee2aaSAndroid Build Coastguard Worker }
223*c8dee2aaSAndroid Build Coastguard Worker }
224*c8dee2aaSAndroid Build Coastguard Worker
225*c8dee2aaSAndroid Build Coastguard Worker SkCodecs::MakeFromStreamCallback rawFallback = nullptr;
226*c8dee2aaSAndroid Build Coastguard Worker for (const SkCodecs::Decoder& proc : decoders) {
227*c8dee2aaSAndroid Build Coastguard Worker if (proc.isFormat(buffer, bytesRead)) {
228*c8dee2aaSAndroid Build Coastguard Worker // Some formats are special, since we want to be able to provide an extra parameter.
229*c8dee2aaSAndroid Build Coastguard Worker if (proc.id == "png") {
230*c8dee2aaSAndroid Build Coastguard Worker return proc.makeFromStream(std::move(stream), outResult, chunkReader);
231*c8dee2aaSAndroid Build Coastguard Worker } else if (proc.id == "heif" || proc.id == "gif") {
232*c8dee2aaSAndroid Build Coastguard Worker return proc.makeFromStream(std::move(stream), outResult, &selectionPolicy);
233*c8dee2aaSAndroid Build Coastguard Worker } else if (proc.id == "raw") {
234*c8dee2aaSAndroid Build Coastguard Worker rawFallback = proc.makeFromStream;
235*c8dee2aaSAndroid Build Coastguard Worker continue;
236*c8dee2aaSAndroid Build Coastguard Worker }
237*c8dee2aaSAndroid Build Coastguard Worker return proc.makeFromStream(std::move(stream), outResult, nullptr);
238*c8dee2aaSAndroid Build Coastguard Worker }
239*c8dee2aaSAndroid Build Coastguard Worker }
240*c8dee2aaSAndroid Build Coastguard Worker if (rawFallback != nullptr) {
241*c8dee2aaSAndroid Build Coastguard Worker // Fallback to raw.
242*c8dee2aaSAndroid Build Coastguard Worker return rawFallback(std::move(stream), outResult, nullptr);
243*c8dee2aaSAndroid Build Coastguard Worker }
244*c8dee2aaSAndroid Build Coastguard Worker
245*c8dee2aaSAndroid Build Coastguard Worker if (bytesRead < bytesToRead) {
246*c8dee2aaSAndroid Build Coastguard Worker *outResult = kIncompleteInput;
247*c8dee2aaSAndroid Build Coastguard Worker } else {
248*c8dee2aaSAndroid Build Coastguard Worker *outResult = kUnimplemented;
249*c8dee2aaSAndroid Build Coastguard Worker }
250*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
251*c8dee2aaSAndroid Build Coastguard Worker }
252*c8dee2aaSAndroid Build Coastguard Worker
MakeFromData(sk_sp<SkData> data,SkPngChunkReader * reader)253*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkCodec> SkCodec::MakeFromData(sk_sp<SkData> data, SkPngChunkReader* reader) {
254*c8dee2aaSAndroid Build Coastguard Worker return MakeFromData(std::move(data), SkCodecs::get_decoders(), reader);
255*c8dee2aaSAndroid Build Coastguard Worker }
MakeFromData(sk_sp<SkData> data,SkSpan<const SkCodecs::Decoder> decoders,SkPngChunkReader * reader)256*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkCodec> SkCodec::MakeFromData(sk_sp<SkData> data,
257*c8dee2aaSAndroid Build Coastguard Worker SkSpan<const SkCodecs::Decoder> decoders,
258*c8dee2aaSAndroid Build Coastguard Worker SkPngChunkReader* reader) {
259*c8dee2aaSAndroid Build Coastguard Worker if (!data) {
260*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
261*c8dee2aaSAndroid Build Coastguard Worker }
262*c8dee2aaSAndroid Build Coastguard Worker return MakeFromStream(SkMemoryStream::Make(std::move(data)), decoders, nullptr, reader);
263*c8dee2aaSAndroid Build Coastguard Worker }
264*c8dee2aaSAndroid Build Coastguard Worker
SkCodec(SkEncodedInfo && info,XformFormat srcFormat,std::unique_ptr<SkStream> stream,SkEncodedOrigin origin)265*c8dee2aaSAndroid Build Coastguard Worker SkCodec::SkCodec(SkEncodedInfo&& info,
266*c8dee2aaSAndroid Build Coastguard Worker XformFormat srcFormat,
267*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkStream> stream,
268*c8dee2aaSAndroid Build Coastguard Worker SkEncodedOrigin origin)
269*c8dee2aaSAndroid Build Coastguard Worker : fEncodedInfo(std::move(info))
270*c8dee2aaSAndroid Build Coastguard Worker , fSrcXformFormat(srcFormat)
271*c8dee2aaSAndroid Build Coastguard Worker , fStream(std::move(stream))
272*c8dee2aaSAndroid Build Coastguard Worker , fOrigin(origin)
273*c8dee2aaSAndroid Build Coastguard Worker , fDstInfo()
274*c8dee2aaSAndroid Build Coastguard Worker , fOptions() {}
275*c8dee2aaSAndroid Build Coastguard Worker
~SkCodec()276*c8dee2aaSAndroid Build Coastguard Worker SkCodec::~SkCodec() {}
277*c8dee2aaSAndroid Build Coastguard Worker
setSrcXformFormat(XformFormat pixelFormat)278*c8dee2aaSAndroid Build Coastguard Worker void SkCodec::setSrcXformFormat(XformFormat pixelFormat) {
279*c8dee2aaSAndroid Build Coastguard Worker fSrcXformFormat = pixelFormat;
280*c8dee2aaSAndroid Build Coastguard Worker }
281*c8dee2aaSAndroid Build Coastguard Worker
queryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes & supportedDataTypes,SkYUVAPixmapInfo * yuvaPixmapInfo) const282*c8dee2aaSAndroid Build Coastguard Worker bool SkCodec::queryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& supportedDataTypes,
283*c8dee2aaSAndroid Build Coastguard Worker SkYUVAPixmapInfo* yuvaPixmapInfo) const {
284*c8dee2aaSAndroid Build Coastguard Worker if (!yuvaPixmapInfo) {
285*c8dee2aaSAndroid Build Coastguard Worker return false;
286*c8dee2aaSAndroid Build Coastguard Worker }
287*c8dee2aaSAndroid Build Coastguard Worker return this->onQueryYUVAInfo(supportedDataTypes, yuvaPixmapInfo) &&
288*c8dee2aaSAndroid Build Coastguard Worker yuvaPixmapInfo->isSupported(supportedDataTypes);
289*c8dee2aaSAndroid Build Coastguard Worker }
290*c8dee2aaSAndroid Build Coastguard Worker
getYUVAPlanes(const SkYUVAPixmaps & yuvaPixmaps)291*c8dee2aaSAndroid Build Coastguard Worker SkCodec::Result SkCodec::getYUVAPlanes(const SkYUVAPixmaps& yuvaPixmaps) {
292*c8dee2aaSAndroid Build Coastguard Worker if (!yuvaPixmaps.isValid()) {
293*c8dee2aaSAndroid Build Coastguard Worker return kInvalidInput;
294*c8dee2aaSAndroid Build Coastguard Worker }
295*c8dee2aaSAndroid Build Coastguard Worker if (!this->rewindIfNeeded()) {
296*c8dee2aaSAndroid Build Coastguard Worker return kCouldNotRewind;
297*c8dee2aaSAndroid Build Coastguard Worker }
298*c8dee2aaSAndroid Build Coastguard Worker return this->onGetYUVAPlanes(yuvaPixmaps);
299*c8dee2aaSAndroid Build Coastguard Worker }
300*c8dee2aaSAndroid Build Coastguard Worker
conversionSupported(const SkImageInfo & dst,bool srcIsOpaque,bool needsColorXform)301*c8dee2aaSAndroid Build Coastguard Worker bool SkCodec::conversionSupported(const SkImageInfo& dst, bool srcIsOpaque, bool needsColorXform) {
302*c8dee2aaSAndroid Build Coastguard Worker if (!valid_alpha(dst.alphaType(), srcIsOpaque)) {
303*c8dee2aaSAndroid Build Coastguard Worker return false;
304*c8dee2aaSAndroid Build Coastguard Worker }
305*c8dee2aaSAndroid Build Coastguard Worker
306*c8dee2aaSAndroid Build Coastguard Worker switch (dst.colorType()) {
307*c8dee2aaSAndroid Build Coastguard Worker case kRGBA_8888_SkColorType:
308*c8dee2aaSAndroid Build Coastguard Worker case kBGRA_8888_SkColorType:
309*c8dee2aaSAndroid Build Coastguard Worker case kRGBA_F16_SkColorType:
310*c8dee2aaSAndroid Build Coastguard Worker case kBGRA_10101010_XR_SkColorType:
311*c8dee2aaSAndroid Build Coastguard Worker return true;
312*c8dee2aaSAndroid Build Coastguard Worker case kBGR_101010x_XR_SkColorType:
313*c8dee2aaSAndroid Build Coastguard Worker case kRGB_565_SkColorType:
314*c8dee2aaSAndroid Build Coastguard Worker return srcIsOpaque;
315*c8dee2aaSAndroid Build Coastguard Worker case kGray_8_SkColorType:
316*c8dee2aaSAndroid Build Coastguard Worker return SkEncodedInfo::kGray_Color == fEncodedInfo.color() && srcIsOpaque;
317*c8dee2aaSAndroid Build Coastguard Worker case kAlpha_8_SkColorType:
318*c8dee2aaSAndroid Build Coastguard Worker // conceptually we can convert anything into alpha_8, but we haven't actually coded
319*c8dee2aaSAndroid Build Coastguard Worker // all of those other conversions yet.
320*c8dee2aaSAndroid Build Coastguard Worker return SkEncodedInfo::kXAlpha_Color == fEncodedInfo.color();
321*c8dee2aaSAndroid Build Coastguard Worker default:
322*c8dee2aaSAndroid Build Coastguard Worker return false;
323*c8dee2aaSAndroid Build Coastguard Worker }
324*c8dee2aaSAndroid Build Coastguard Worker }
325*c8dee2aaSAndroid Build Coastguard Worker
rewindIfNeeded()326*c8dee2aaSAndroid Build Coastguard Worker bool SkCodec::rewindIfNeeded() {
327*c8dee2aaSAndroid Build Coastguard Worker // Store the value of fNeedsRewind so we can update it. Next read will
328*c8dee2aaSAndroid Build Coastguard Worker // require a rewind.
329*c8dee2aaSAndroid Build Coastguard Worker const bool needsRewind = fNeedsRewind;
330*c8dee2aaSAndroid Build Coastguard Worker fNeedsRewind = true;
331*c8dee2aaSAndroid Build Coastguard Worker if (!needsRewind) {
332*c8dee2aaSAndroid Build Coastguard Worker return true;
333*c8dee2aaSAndroid Build Coastguard Worker }
334*c8dee2aaSAndroid Build Coastguard Worker
335*c8dee2aaSAndroid Build Coastguard Worker // startScanlineDecode will need to be called before decoding scanlines.
336*c8dee2aaSAndroid Build Coastguard Worker fCurrScanline = -1;
337*c8dee2aaSAndroid Build Coastguard Worker // startIncrementalDecode will need to be called before incrementalDecode.
338*c8dee2aaSAndroid Build Coastguard Worker fStartedIncrementalDecode = false;
339*c8dee2aaSAndroid Build Coastguard Worker
340*c8dee2aaSAndroid Build Coastguard Worker // Some codecs do not have a stream. They may hold onto their own data or another codec.
341*c8dee2aaSAndroid Build Coastguard Worker // They must handle rewinding themselves.
342*c8dee2aaSAndroid Build Coastguard Worker if (fStream && !fStream->rewind()) {
343*c8dee2aaSAndroid Build Coastguard Worker return false;
344*c8dee2aaSAndroid Build Coastguard Worker }
345*c8dee2aaSAndroid Build Coastguard Worker
346*c8dee2aaSAndroid Build Coastguard Worker return this->onRewind();
347*c8dee2aaSAndroid Build Coastguard Worker }
348*c8dee2aaSAndroid Build Coastguard Worker
frame_rect_on_screen(SkIRect frameRect,const SkIRect & screenRect)349*c8dee2aaSAndroid Build Coastguard Worker static SkIRect frame_rect_on_screen(SkIRect frameRect,
350*c8dee2aaSAndroid Build Coastguard Worker const SkIRect& screenRect) {
351*c8dee2aaSAndroid Build Coastguard Worker if (!frameRect.intersect(screenRect)) {
352*c8dee2aaSAndroid Build Coastguard Worker return SkIRect::MakeEmpty();
353*c8dee2aaSAndroid Build Coastguard Worker }
354*c8dee2aaSAndroid Build Coastguard Worker
355*c8dee2aaSAndroid Build Coastguard Worker return frameRect;
356*c8dee2aaSAndroid Build Coastguard Worker }
357*c8dee2aaSAndroid Build Coastguard Worker
zero_rect(const SkImageInfo & dstInfo,void * pixels,size_t rowBytes,SkISize srcDimensions,SkIRect prevRect)358*c8dee2aaSAndroid Build Coastguard Worker bool zero_rect(const SkImageInfo& dstInfo, void* pixels, size_t rowBytes,
359*c8dee2aaSAndroid Build Coastguard Worker SkISize srcDimensions, SkIRect prevRect) {
360*c8dee2aaSAndroid Build Coastguard Worker const auto dimensions = dstInfo.dimensions();
361*c8dee2aaSAndroid Build Coastguard Worker if (dimensions != srcDimensions) {
362*c8dee2aaSAndroid Build Coastguard Worker SkRect src = SkRect::Make(srcDimensions);
363*c8dee2aaSAndroid Build Coastguard Worker SkRect dst = SkRect::Make(dimensions);
364*c8dee2aaSAndroid Build Coastguard Worker SkMatrix map = SkMatrix::RectToRect(src, dst);
365*c8dee2aaSAndroid Build Coastguard Worker SkRect asRect = SkRect::Make(prevRect);
366*c8dee2aaSAndroid Build Coastguard Worker if (!map.mapRect(&asRect)) {
367*c8dee2aaSAndroid Build Coastguard Worker return false;
368*c8dee2aaSAndroid Build Coastguard Worker }
369*c8dee2aaSAndroid Build Coastguard Worker asRect.roundOut(&prevRect);
370*c8dee2aaSAndroid Build Coastguard Worker }
371*c8dee2aaSAndroid Build Coastguard Worker
372*c8dee2aaSAndroid Build Coastguard Worker if (!prevRect.intersect(SkIRect::MakeSize(dimensions))) {
373*c8dee2aaSAndroid Build Coastguard Worker // Nothing to zero, due to scaling or bad frame rect.
374*c8dee2aaSAndroid Build Coastguard Worker return true;
375*c8dee2aaSAndroid Build Coastguard Worker }
376*c8dee2aaSAndroid Build Coastguard Worker
377*c8dee2aaSAndroid Build Coastguard Worker const SkImageInfo info = dstInfo.makeDimensions(prevRect.size());
378*c8dee2aaSAndroid Build Coastguard Worker const size_t bpp = dstInfo.bytesPerPixel();
379*c8dee2aaSAndroid Build Coastguard Worker const size_t offset = prevRect.x() * bpp + prevRect.y() * rowBytes;
380*c8dee2aaSAndroid Build Coastguard Worker void* eraseDst = SkTAddOffset<void>(pixels, offset);
381*c8dee2aaSAndroid Build Coastguard Worker SkSampler::Fill(info, eraseDst, rowBytes, SkCodec::kNo_ZeroInitialized);
382*c8dee2aaSAndroid Build Coastguard Worker return true;
383*c8dee2aaSAndroid Build Coastguard Worker }
384*c8dee2aaSAndroid Build Coastguard Worker
handleFrameIndex(const SkImageInfo & info,void * pixels,size_t rowBytes,const Options & options,GetPixelsCallback getPixelsFn)385*c8dee2aaSAndroid Build Coastguard Worker SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels, size_t rowBytes,
386*c8dee2aaSAndroid Build Coastguard Worker const Options& options, GetPixelsCallback getPixelsFn) {
387*c8dee2aaSAndroid Build Coastguard Worker if (getPixelsFn) {
388*c8dee2aaSAndroid Build Coastguard Worker // If a callback is used, it handles the frame index, so calls from this SkCodec
389*c8dee2aaSAndroid Build Coastguard Worker // should always short-circuit in the else case below.
390*c8dee2aaSAndroid Build Coastguard Worker fUsingCallbackForHandleFrameIndex = true;
391*c8dee2aaSAndroid Build Coastguard Worker } else if (fUsingCallbackForHandleFrameIndex) {
392*c8dee2aaSAndroid Build Coastguard Worker return kSuccess;
393*c8dee2aaSAndroid Build Coastguard Worker }
394*c8dee2aaSAndroid Build Coastguard Worker
395*c8dee2aaSAndroid Build Coastguard Worker if (!this->rewindIfNeeded()) {
396*c8dee2aaSAndroid Build Coastguard Worker return kCouldNotRewind;
397*c8dee2aaSAndroid Build Coastguard Worker }
398*c8dee2aaSAndroid Build Coastguard Worker
399*c8dee2aaSAndroid Build Coastguard Worker const int index = options.fFrameIndex;
400*c8dee2aaSAndroid Build Coastguard Worker if (0 == index) {
401*c8dee2aaSAndroid Build Coastguard Worker return this->initializeColorXform(info, fEncodedInfo.alpha(), fEncodedInfo.opaque())
402*c8dee2aaSAndroid Build Coastguard Worker ? kSuccess : kInvalidConversion;
403*c8dee2aaSAndroid Build Coastguard Worker }
404*c8dee2aaSAndroid Build Coastguard Worker
405*c8dee2aaSAndroid Build Coastguard Worker if (index < 0) {
406*c8dee2aaSAndroid Build Coastguard Worker return kInvalidParameters;
407*c8dee2aaSAndroid Build Coastguard Worker }
408*c8dee2aaSAndroid Build Coastguard Worker
409*c8dee2aaSAndroid Build Coastguard Worker if (options.fSubset) {
410*c8dee2aaSAndroid Build Coastguard Worker // If we add support for this, we need to update the code that zeroes
411*c8dee2aaSAndroid Build Coastguard Worker // a kRestoreBGColor frame.
412*c8dee2aaSAndroid Build Coastguard Worker return kInvalidParameters;
413*c8dee2aaSAndroid Build Coastguard Worker }
414*c8dee2aaSAndroid Build Coastguard Worker
415*c8dee2aaSAndroid Build Coastguard Worker if (index >= this->onGetFrameCount()) {
416*c8dee2aaSAndroid Build Coastguard Worker return kIncompleteInput;
417*c8dee2aaSAndroid Build Coastguard Worker }
418*c8dee2aaSAndroid Build Coastguard Worker
419*c8dee2aaSAndroid Build Coastguard Worker const auto* frameHolder = this->getFrameHolder();
420*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(frameHolder);
421*c8dee2aaSAndroid Build Coastguard Worker
422*c8dee2aaSAndroid Build Coastguard Worker const auto* frame = frameHolder->getFrame(index);
423*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(frame);
424*c8dee2aaSAndroid Build Coastguard Worker
425*c8dee2aaSAndroid Build Coastguard Worker const int requiredFrame = frame->getRequiredFrame();
426*c8dee2aaSAndroid Build Coastguard Worker if (requiredFrame != kNoFrame) {
427*c8dee2aaSAndroid Build Coastguard Worker // Decode earlier frame if necessary
428*c8dee2aaSAndroid Build Coastguard Worker const SkFrame* preppedFrame = nullptr;
429*c8dee2aaSAndroid Build Coastguard Worker if (options.fPriorFrame == kNoFrame) {
430*c8dee2aaSAndroid Build Coastguard Worker Result result = kInternalError;
431*c8dee2aaSAndroid Build Coastguard Worker // getPixelsFn will be set when things like SkAndroidCodec are calling this function.
432*c8dee2aaSAndroid Build Coastguard Worker // Thus, we call the provided function when recursively decoding previous frames,
433*c8dee2aaSAndroid Build Coastguard Worker // but only when necessary (i.e. there is a required frame).
434*c8dee2aaSAndroid Build Coastguard Worker if (getPixelsFn) {
435*c8dee2aaSAndroid Build Coastguard Worker result = getPixelsFn(info, pixels, rowBytes, options, requiredFrame);
436*c8dee2aaSAndroid Build Coastguard Worker } else {
437*c8dee2aaSAndroid Build Coastguard Worker Options prevFrameOptions(options);
438*c8dee2aaSAndroid Build Coastguard Worker prevFrameOptions.fFrameIndex = requiredFrame;
439*c8dee2aaSAndroid Build Coastguard Worker result = this->getPixels(info, pixels, rowBytes, &prevFrameOptions);
440*c8dee2aaSAndroid Build Coastguard Worker }
441*c8dee2aaSAndroid Build Coastguard Worker if (result != kSuccess) {
442*c8dee2aaSAndroid Build Coastguard Worker return result;
443*c8dee2aaSAndroid Build Coastguard Worker }
444*c8dee2aaSAndroid Build Coastguard Worker preppedFrame = frameHolder->getFrame(requiredFrame);
445*c8dee2aaSAndroid Build Coastguard Worker } else {
446*c8dee2aaSAndroid Build Coastguard Worker // Check for a valid frame as a starting point. Alternatively, we could
447*c8dee2aaSAndroid Build Coastguard Worker // treat an invalid frame as not providing one, but rejecting it will
448*c8dee2aaSAndroid Build Coastguard Worker // make it easier to catch the mistake.
449*c8dee2aaSAndroid Build Coastguard Worker if (options.fPriorFrame < requiredFrame || options.fPriorFrame >= index) {
450*c8dee2aaSAndroid Build Coastguard Worker return kInvalidParameters;
451*c8dee2aaSAndroid Build Coastguard Worker }
452*c8dee2aaSAndroid Build Coastguard Worker preppedFrame = frameHolder->getFrame(options.fPriorFrame);
453*c8dee2aaSAndroid Build Coastguard Worker }
454*c8dee2aaSAndroid Build Coastguard Worker
455*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(preppedFrame);
456*c8dee2aaSAndroid Build Coastguard Worker switch (preppedFrame->getDisposalMethod()) {
457*c8dee2aaSAndroid Build Coastguard Worker case SkCodecAnimation::DisposalMethod::kRestorePrevious:
458*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(options.fPriorFrame != kNoFrame);
459*c8dee2aaSAndroid Build Coastguard Worker return kInvalidParameters;
460*c8dee2aaSAndroid Build Coastguard Worker case SkCodecAnimation::DisposalMethod::kRestoreBGColor:
461*c8dee2aaSAndroid Build Coastguard Worker // If a frame after the required frame is provided, there is no
462*c8dee2aaSAndroid Build Coastguard Worker // need to clear, since it must be covered by the desired frame.
463*c8dee2aaSAndroid Build Coastguard Worker // FIXME: If the required frame is kRestoreBGColor, we don't actually need to decode
464*c8dee2aaSAndroid Build Coastguard Worker // it, since we'll just clear it to transparent. Instead, we could decode *its*
465*c8dee2aaSAndroid Build Coastguard Worker // required frame and then clear.
466*c8dee2aaSAndroid Build Coastguard Worker if (preppedFrame->frameId() == requiredFrame) {
467*c8dee2aaSAndroid Build Coastguard Worker SkIRect preppedRect = preppedFrame->frameRect();
468*c8dee2aaSAndroid Build Coastguard Worker if (!zero_rect(info, pixels, rowBytes, this->dimensions(), preppedRect)) {
469*c8dee2aaSAndroid Build Coastguard Worker return kInternalError;
470*c8dee2aaSAndroid Build Coastguard Worker }
471*c8dee2aaSAndroid Build Coastguard Worker }
472*c8dee2aaSAndroid Build Coastguard Worker break;
473*c8dee2aaSAndroid Build Coastguard Worker default:
474*c8dee2aaSAndroid Build Coastguard Worker break;
475*c8dee2aaSAndroid Build Coastguard Worker }
476*c8dee2aaSAndroid Build Coastguard Worker }
477*c8dee2aaSAndroid Build Coastguard Worker
478*c8dee2aaSAndroid Build Coastguard Worker return this->initializeColorXform(info, frame->reportedAlpha(), !frame->hasAlpha())
479*c8dee2aaSAndroid Build Coastguard Worker ? kSuccess : kInvalidConversion;
480*c8dee2aaSAndroid Build Coastguard Worker }
481*c8dee2aaSAndroid Build Coastguard Worker
getPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const Options * options)482*c8dee2aaSAndroid Build Coastguard Worker SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
483*c8dee2aaSAndroid Build Coastguard Worker const Options* options) {
484*c8dee2aaSAndroid Build Coastguard Worker if (kUnknown_SkColorType == info.colorType()) {
485*c8dee2aaSAndroid Build Coastguard Worker return kInvalidConversion;
486*c8dee2aaSAndroid Build Coastguard Worker }
487*c8dee2aaSAndroid Build Coastguard Worker if (nullptr == pixels) {
488*c8dee2aaSAndroid Build Coastguard Worker return kInvalidParameters;
489*c8dee2aaSAndroid Build Coastguard Worker }
490*c8dee2aaSAndroid Build Coastguard Worker if (rowBytes < info.minRowBytes()) {
491*c8dee2aaSAndroid Build Coastguard Worker return kInvalidParameters;
492*c8dee2aaSAndroid Build Coastguard Worker }
493*c8dee2aaSAndroid Build Coastguard Worker
494*c8dee2aaSAndroid Build Coastguard Worker // Default options.
495*c8dee2aaSAndroid Build Coastguard Worker Options optsStorage;
496*c8dee2aaSAndroid Build Coastguard Worker if (nullptr == options) {
497*c8dee2aaSAndroid Build Coastguard Worker options = &optsStorage;
498*c8dee2aaSAndroid Build Coastguard Worker } else {
499*c8dee2aaSAndroid Build Coastguard Worker if (options->fSubset) {
500*c8dee2aaSAndroid Build Coastguard Worker SkIRect subset(*options->fSubset);
501*c8dee2aaSAndroid Build Coastguard Worker if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) {
502*c8dee2aaSAndroid Build Coastguard Worker // FIXME: How to differentiate between not supporting subset at all
503*c8dee2aaSAndroid Build Coastguard Worker // and not supporting this particular subset?
504*c8dee2aaSAndroid Build Coastguard Worker return kUnimplemented;
505*c8dee2aaSAndroid Build Coastguard Worker }
506*c8dee2aaSAndroid Build Coastguard Worker }
507*c8dee2aaSAndroid Build Coastguard Worker }
508*c8dee2aaSAndroid Build Coastguard Worker
509*c8dee2aaSAndroid Build Coastguard Worker const Result frameIndexResult = this->handleFrameIndex(info, pixels, rowBytes,
510*c8dee2aaSAndroid Build Coastguard Worker *options);
511*c8dee2aaSAndroid Build Coastguard Worker if (frameIndexResult != kSuccess) {
512*c8dee2aaSAndroid Build Coastguard Worker return frameIndexResult;
513*c8dee2aaSAndroid Build Coastguard Worker }
514*c8dee2aaSAndroid Build Coastguard Worker
515*c8dee2aaSAndroid Build Coastguard Worker // FIXME: Support subsets somehow? Note that this works for SkWebpCodec
516*c8dee2aaSAndroid Build Coastguard Worker // because it supports arbitrary scaling/subset combinations.
517*c8dee2aaSAndroid Build Coastguard Worker if (!this->dimensionsSupported(info.dimensions())) {
518*c8dee2aaSAndroid Build Coastguard Worker return kInvalidScale;
519*c8dee2aaSAndroid Build Coastguard Worker }
520*c8dee2aaSAndroid Build Coastguard Worker
521*c8dee2aaSAndroid Build Coastguard Worker fDstInfo = info;
522*c8dee2aaSAndroid Build Coastguard Worker fOptions = *options;
523*c8dee2aaSAndroid Build Coastguard Worker
524*c8dee2aaSAndroid Build Coastguard Worker // On an incomplete decode, the subclass will specify the number of scanlines that it decoded
525*c8dee2aaSAndroid Build Coastguard Worker // successfully.
526*c8dee2aaSAndroid Build Coastguard Worker int rowsDecoded = 0;
527*c8dee2aaSAndroid Build Coastguard Worker const Result result = this->onGetPixels(info, pixels, rowBytes, *options, &rowsDecoded);
528*c8dee2aaSAndroid Build Coastguard Worker
529*c8dee2aaSAndroid Build Coastguard Worker // A return value of kIncompleteInput indicates a truncated image stream.
530*c8dee2aaSAndroid Build Coastguard Worker // In this case, we will fill any uninitialized memory with a default value.
531*c8dee2aaSAndroid Build Coastguard Worker // Some subclasses will take care of filling any uninitialized memory on
532*c8dee2aaSAndroid Build Coastguard Worker // their own. They indicate that all of the memory has been filled by
533*c8dee2aaSAndroid Build Coastguard Worker // setting rowsDecoded equal to the height.
534*c8dee2aaSAndroid Build Coastguard Worker if ((kIncompleteInput == result || kErrorInInput == result) && rowsDecoded != info.height()) {
535*c8dee2aaSAndroid Build Coastguard Worker // FIXME: (skbug.com/5772) fillIncompleteImage will fill using the swizzler's width, unless
536*c8dee2aaSAndroid Build Coastguard Worker // there is a subset. In that case, it will use the width of the subset. From here, the
537*c8dee2aaSAndroid Build Coastguard Worker // subset will only be non-null in the case of SkWebpCodec, but it treats the subset
538*c8dee2aaSAndroid Build Coastguard Worker // differenty from the other codecs, and it needs to use the width specified by the info.
539*c8dee2aaSAndroid Build Coastguard Worker // Set the subset to null so SkWebpCodec uses the correct width.
540*c8dee2aaSAndroid Build Coastguard Worker fOptions.fSubset = nullptr;
541*c8dee2aaSAndroid Build Coastguard Worker this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitialized, info.height(),
542*c8dee2aaSAndroid Build Coastguard Worker rowsDecoded);
543*c8dee2aaSAndroid Build Coastguard Worker }
544*c8dee2aaSAndroid Build Coastguard Worker
545*c8dee2aaSAndroid Build Coastguard Worker return result;
546*c8dee2aaSAndroid Build Coastguard Worker }
547*c8dee2aaSAndroid Build Coastguard Worker
getImage(const SkImageInfo & info,const Options * options)548*c8dee2aaSAndroid Build Coastguard Worker std::tuple<sk_sp<SkImage>, SkCodec::Result> SkCodec::getImage(const SkImageInfo& info,
549*c8dee2aaSAndroid Build Coastguard Worker const Options* options) {
550*c8dee2aaSAndroid Build Coastguard Worker SkBitmap bm;
551*c8dee2aaSAndroid Build Coastguard Worker if (!bm.tryAllocPixels(info)) {
552*c8dee2aaSAndroid Build Coastguard Worker return {nullptr, kInternalError};
553*c8dee2aaSAndroid Build Coastguard Worker }
554*c8dee2aaSAndroid Build Coastguard Worker
555*c8dee2aaSAndroid Build Coastguard Worker Result result;
556*c8dee2aaSAndroid Build Coastguard Worker auto decode = [this, options, &result](const SkPixmap& pm) {
557*c8dee2aaSAndroid Build Coastguard Worker result = this->getPixels(pm, options);
558*c8dee2aaSAndroid Build Coastguard Worker switch (result) {
559*c8dee2aaSAndroid Build Coastguard Worker case SkCodec::kSuccess:
560*c8dee2aaSAndroid Build Coastguard Worker case SkCodec::kIncompleteInput:
561*c8dee2aaSAndroid Build Coastguard Worker case SkCodec::kErrorInInput:
562*c8dee2aaSAndroid Build Coastguard Worker return true;
563*c8dee2aaSAndroid Build Coastguard Worker default:
564*c8dee2aaSAndroid Build Coastguard Worker return false;
565*c8dee2aaSAndroid Build Coastguard Worker }
566*c8dee2aaSAndroid Build Coastguard Worker };
567*c8dee2aaSAndroid Build Coastguard Worker
568*c8dee2aaSAndroid Build Coastguard Worker // If the codec reports this image is rotated, we will decode it into
569*c8dee2aaSAndroid Build Coastguard Worker // a temporary buffer, then copy it (rotated) into the pixmap belonging
570*c8dee2aaSAndroid Build Coastguard Worker // to bm that we allocated above. If the image is not rotated, we will
571*c8dee2aaSAndroid Build Coastguard Worker // decode straight into that allocated pixmap.
572*c8dee2aaSAndroid Build Coastguard Worker if (!SkPixmapUtils::Orient(bm.pixmap(), this->getOrigin(), decode)) {
573*c8dee2aaSAndroid Build Coastguard Worker return {nullptr, result};
574*c8dee2aaSAndroid Build Coastguard Worker }
575*c8dee2aaSAndroid Build Coastguard Worker // Setting the bitmap to be immutable saves us from having to copy it.
576*c8dee2aaSAndroid Build Coastguard Worker bm.setImmutable();
577*c8dee2aaSAndroid Build Coastguard Worker return {SkImages::RasterFromBitmap(bm), kSuccess};
578*c8dee2aaSAndroid Build Coastguard Worker }
579*c8dee2aaSAndroid Build Coastguard Worker
getImage()580*c8dee2aaSAndroid Build Coastguard Worker std::tuple<sk_sp<SkImage>, SkCodec::Result> SkCodec::getImage() {
581*c8dee2aaSAndroid Build Coastguard Worker // If the codec reports that it is rotated, we need to rotate the image info
582*c8dee2aaSAndroid Build Coastguard Worker // it says it is, so the output is what the user wants.
583*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo info = this->getInfo();
584*c8dee2aaSAndroid Build Coastguard Worker if (SkEncodedOriginSwapsWidthHeight(this->getOrigin())) {
585*c8dee2aaSAndroid Build Coastguard Worker info = SkPixmapUtils::SwapWidthHeight(info);
586*c8dee2aaSAndroid Build Coastguard Worker }
587*c8dee2aaSAndroid Build Coastguard Worker return this->getImage(info, nullptr);
588*c8dee2aaSAndroid Build Coastguard Worker }
589*c8dee2aaSAndroid Build Coastguard Worker
startIncrementalDecode(const SkImageInfo & info,void * pixels,size_t rowBytes,const SkCodec::Options * options)590*c8dee2aaSAndroid Build Coastguard Worker SkCodec::Result SkCodec::startIncrementalDecode(const SkImageInfo& info, void* pixels,
591*c8dee2aaSAndroid Build Coastguard Worker size_t rowBytes, const SkCodec::Options* options) {
592*c8dee2aaSAndroid Build Coastguard Worker fStartedIncrementalDecode = false;
593*c8dee2aaSAndroid Build Coastguard Worker
594*c8dee2aaSAndroid Build Coastguard Worker if (kUnknown_SkColorType == info.colorType()) {
595*c8dee2aaSAndroid Build Coastguard Worker return kInvalidConversion;
596*c8dee2aaSAndroid Build Coastguard Worker }
597*c8dee2aaSAndroid Build Coastguard Worker if (nullptr == pixels) {
598*c8dee2aaSAndroid Build Coastguard Worker return kInvalidParameters;
599*c8dee2aaSAndroid Build Coastguard Worker }
600*c8dee2aaSAndroid Build Coastguard Worker
601*c8dee2aaSAndroid Build Coastguard Worker // Set options.
602*c8dee2aaSAndroid Build Coastguard Worker Options optsStorage;
603*c8dee2aaSAndroid Build Coastguard Worker if (nullptr == options) {
604*c8dee2aaSAndroid Build Coastguard Worker options = &optsStorage;
605*c8dee2aaSAndroid Build Coastguard Worker } else {
606*c8dee2aaSAndroid Build Coastguard Worker if (options->fSubset) {
607*c8dee2aaSAndroid Build Coastguard Worker SkIRect size = SkIRect::MakeSize(info.dimensions());
608*c8dee2aaSAndroid Build Coastguard Worker if (!size.contains(*options->fSubset)) {
609*c8dee2aaSAndroid Build Coastguard Worker return kInvalidParameters;
610*c8dee2aaSAndroid Build Coastguard Worker }
611*c8dee2aaSAndroid Build Coastguard Worker
612*c8dee2aaSAndroid Build Coastguard Worker const int top = options->fSubset->top();
613*c8dee2aaSAndroid Build Coastguard Worker const int bottom = options->fSubset->bottom();
614*c8dee2aaSAndroid Build Coastguard Worker if (top < 0 || top >= info.height() || top >= bottom || bottom > info.height()) {
615*c8dee2aaSAndroid Build Coastguard Worker return kInvalidParameters;
616*c8dee2aaSAndroid Build Coastguard Worker }
617*c8dee2aaSAndroid Build Coastguard Worker }
618*c8dee2aaSAndroid Build Coastguard Worker }
619*c8dee2aaSAndroid Build Coastguard Worker
620*c8dee2aaSAndroid Build Coastguard Worker const Result frameIndexResult = this->handleFrameIndex(info, pixels, rowBytes,
621*c8dee2aaSAndroid Build Coastguard Worker *options);
622*c8dee2aaSAndroid Build Coastguard Worker if (frameIndexResult != kSuccess) {
623*c8dee2aaSAndroid Build Coastguard Worker return frameIndexResult;
624*c8dee2aaSAndroid Build Coastguard Worker }
625*c8dee2aaSAndroid Build Coastguard Worker
626*c8dee2aaSAndroid Build Coastguard Worker if (!this->dimensionsSupported(info.dimensions())) {
627*c8dee2aaSAndroid Build Coastguard Worker return kInvalidScale;
628*c8dee2aaSAndroid Build Coastguard Worker }
629*c8dee2aaSAndroid Build Coastguard Worker
630*c8dee2aaSAndroid Build Coastguard Worker fDstInfo = info;
631*c8dee2aaSAndroid Build Coastguard Worker fOptions = *options;
632*c8dee2aaSAndroid Build Coastguard Worker
633*c8dee2aaSAndroid Build Coastguard Worker const Result result = this->onStartIncrementalDecode(info, pixels, rowBytes, fOptions);
634*c8dee2aaSAndroid Build Coastguard Worker if (kSuccess == result) {
635*c8dee2aaSAndroid Build Coastguard Worker fStartedIncrementalDecode = true;
636*c8dee2aaSAndroid Build Coastguard Worker } else if (kUnimplemented == result) {
637*c8dee2aaSAndroid Build Coastguard Worker // FIXME: This is temporarily necessary, until we transition SkCodec
638*c8dee2aaSAndroid Build Coastguard Worker // implementations from scanline decoding to incremental decoding.
639*c8dee2aaSAndroid Build Coastguard Worker // SkAndroidCodec will first attempt to use incremental decoding, but
640*c8dee2aaSAndroid Build Coastguard Worker // will fall back to scanline decoding if incremental returns
641*c8dee2aaSAndroid Build Coastguard Worker // kUnimplemented. rewindIfNeeded(), above, set fNeedsRewind to true
642*c8dee2aaSAndroid Build Coastguard Worker // (after potentially rewinding), but we do not want the next call to
643*c8dee2aaSAndroid Build Coastguard Worker // startScanlineDecode() to do a rewind.
644*c8dee2aaSAndroid Build Coastguard Worker fNeedsRewind = false;
645*c8dee2aaSAndroid Build Coastguard Worker }
646*c8dee2aaSAndroid Build Coastguard Worker return result;
647*c8dee2aaSAndroid Build Coastguard Worker }
648*c8dee2aaSAndroid Build Coastguard Worker
649*c8dee2aaSAndroid Build Coastguard Worker
startScanlineDecode(const SkImageInfo & info,const SkCodec::Options * options)650*c8dee2aaSAndroid Build Coastguard Worker SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& info,
651*c8dee2aaSAndroid Build Coastguard Worker const SkCodec::Options* options) {
652*c8dee2aaSAndroid Build Coastguard Worker // Reset fCurrScanline in case of failure.
653*c8dee2aaSAndroid Build Coastguard Worker fCurrScanline = -1;
654*c8dee2aaSAndroid Build Coastguard Worker
655*c8dee2aaSAndroid Build Coastguard Worker // Set options.
656*c8dee2aaSAndroid Build Coastguard Worker Options optsStorage;
657*c8dee2aaSAndroid Build Coastguard Worker if (nullptr == options) {
658*c8dee2aaSAndroid Build Coastguard Worker options = &optsStorage;
659*c8dee2aaSAndroid Build Coastguard Worker } else if (options->fSubset) {
660*c8dee2aaSAndroid Build Coastguard Worker SkIRect size = SkIRect::MakeSize(info.dimensions());
661*c8dee2aaSAndroid Build Coastguard Worker if (!size.contains(*options->fSubset)) {
662*c8dee2aaSAndroid Build Coastguard Worker return kInvalidInput;
663*c8dee2aaSAndroid Build Coastguard Worker }
664*c8dee2aaSAndroid Build Coastguard Worker
665*c8dee2aaSAndroid Build Coastguard Worker // We only support subsetting in the x-dimension for scanline decoder.
666*c8dee2aaSAndroid Build Coastguard Worker // Subsetting in the y-dimension can be accomplished using skipScanlines().
667*c8dee2aaSAndroid Build Coastguard Worker if (options->fSubset->top() != 0 || options->fSubset->height() != info.height()) {
668*c8dee2aaSAndroid Build Coastguard Worker return kInvalidInput;
669*c8dee2aaSAndroid Build Coastguard Worker }
670*c8dee2aaSAndroid Build Coastguard Worker }
671*c8dee2aaSAndroid Build Coastguard Worker
672*c8dee2aaSAndroid Build Coastguard Worker // Scanline decoding only supports decoding the first frame.
673*c8dee2aaSAndroid Build Coastguard Worker if (options->fFrameIndex != 0) {
674*c8dee2aaSAndroid Build Coastguard Worker return kUnimplemented;
675*c8dee2aaSAndroid Build Coastguard Worker }
676*c8dee2aaSAndroid Build Coastguard Worker
677*c8dee2aaSAndroid Build Coastguard Worker // The void* dst and rowbytes in handleFrameIndex or only used for decoding prior
678*c8dee2aaSAndroid Build Coastguard Worker // frames, which is not supported here anyway, so it is safe to pass nullptr/0.
679*c8dee2aaSAndroid Build Coastguard Worker const Result frameIndexResult = this->handleFrameIndex(info, nullptr, 0, *options);
680*c8dee2aaSAndroid Build Coastguard Worker if (frameIndexResult != kSuccess) {
681*c8dee2aaSAndroid Build Coastguard Worker return frameIndexResult;
682*c8dee2aaSAndroid Build Coastguard Worker }
683*c8dee2aaSAndroid Build Coastguard Worker
684*c8dee2aaSAndroid Build Coastguard Worker // FIXME: Support subsets somehow?
685*c8dee2aaSAndroid Build Coastguard Worker if (!this->dimensionsSupported(info.dimensions())) {
686*c8dee2aaSAndroid Build Coastguard Worker return kInvalidScale;
687*c8dee2aaSAndroid Build Coastguard Worker }
688*c8dee2aaSAndroid Build Coastguard Worker
689*c8dee2aaSAndroid Build Coastguard Worker const Result result = this->onStartScanlineDecode(info, *options);
690*c8dee2aaSAndroid Build Coastguard Worker if (result != SkCodec::kSuccess) {
691*c8dee2aaSAndroid Build Coastguard Worker return result;
692*c8dee2aaSAndroid Build Coastguard Worker }
693*c8dee2aaSAndroid Build Coastguard Worker
694*c8dee2aaSAndroid Build Coastguard Worker // FIXME: See startIncrementalDecode. That method set fNeedsRewind to false
695*c8dee2aaSAndroid Build Coastguard Worker // so that when onStartScanlineDecode calls rewindIfNeeded it would not
696*c8dee2aaSAndroid Build Coastguard Worker // rewind. But it also relies on that call to rewindIfNeeded to set
697*c8dee2aaSAndroid Build Coastguard Worker // fNeedsRewind to true for future decodes. When
698*c8dee2aaSAndroid Build Coastguard Worker // fUsingCallbackForHandleFrameIndex is true, that call to rewindIfNeeded is
699*c8dee2aaSAndroid Build Coastguard Worker // skipped, so this method sets it back to true.
700*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fUsingCallbackForHandleFrameIndex || fNeedsRewind);
701*c8dee2aaSAndroid Build Coastguard Worker fNeedsRewind = true;
702*c8dee2aaSAndroid Build Coastguard Worker
703*c8dee2aaSAndroid Build Coastguard Worker fCurrScanline = 0;
704*c8dee2aaSAndroid Build Coastguard Worker fDstInfo = info;
705*c8dee2aaSAndroid Build Coastguard Worker fOptions = *options;
706*c8dee2aaSAndroid Build Coastguard Worker return kSuccess;
707*c8dee2aaSAndroid Build Coastguard Worker }
708*c8dee2aaSAndroid Build Coastguard Worker
getScanlines(void * dst,int countLines,size_t rowBytes)709*c8dee2aaSAndroid Build Coastguard Worker int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) {
710*c8dee2aaSAndroid Build Coastguard Worker if (fCurrScanline < 0) {
711*c8dee2aaSAndroid Build Coastguard Worker return 0;
712*c8dee2aaSAndroid Build Coastguard Worker }
713*c8dee2aaSAndroid Build Coastguard Worker
714*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fDstInfo.isEmpty());
715*c8dee2aaSAndroid Build Coastguard Worker if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) {
716*c8dee2aaSAndroid Build Coastguard Worker return 0;
717*c8dee2aaSAndroid Build Coastguard Worker }
718*c8dee2aaSAndroid Build Coastguard Worker
719*c8dee2aaSAndroid Build Coastguard Worker const int linesDecoded = this->onGetScanlines(dst, countLines, rowBytes);
720*c8dee2aaSAndroid Build Coastguard Worker if (linesDecoded < countLines) {
721*c8dee2aaSAndroid Build Coastguard Worker this->fillIncompleteImage(this->dstInfo(), dst, rowBytes, this->options().fZeroInitialized,
722*c8dee2aaSAndroid Build Coastguard Worker countLines, linesDecoded);
723*c8dee2aaSAndroid Build Coastguard Worker }
724*c8dee2aaSAndroid Build Coastguard Worker fCurrScanline += countLines;
725*c8dee2aaSAndroid Build Coastguard Worker return linesDecoded;
726*c8dee2aaSAndroid Build Coastguard Worker }
727*c8dee2aaSAndroid Build Coastguard Worker
skipScanlines(int countLines)728*c8dee2aaSAndroid Build Coastguard Worker bool SkCodec::skipScanlines(int countLines) {
729*c8dee2aaSAndroid Build Coastguard Worker if (fCurrScanline < 0) {
730*c8dee2aaSAndroid Build Coastguard Worker return false;
731*c8dee2aaSAndroid Build Coastguard Worker }
732*c8dee2aaSAndroid Build Coastguard Worker
733*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fDstInfo.isEmpty());
734*c8dee2aaSAndroid Build Coastguard Worker if (countLines < 0 || fCurrScanline + countLines > fDstInfo.height()) {
735*c8dee2aaSAndroid Build Coastguard Worker // Arguably, we could just skip the scanlines which are remaining,
736*c8dee2aaSAndroid Build Coastguard Worker // and return true. We choose to return false so the client
737*c8dee2aaSAndroid Build Coastguard Worker // can catch their bug.
738*c8dee2aaSAndroid Build Coastguard Worker return false;
739*c8dee2aaSAndroid Build Coastguard Worker }
740*c8dee2aaSAndroid Build Coastguard Worker
741*c8dee2aaSAndroid Build Coastguard Worker bool result = this->onSkipScanlines(countLines);
742*c8dee2aaSAndroid Build Coastguard Worker fCurrScanline += countLines;
743*c8dee2aaSAndroid Build Coastguard Worker return result;
744*c8dee2aaSAndroid Build Coastguard Worker }
745*c8dee2aaSAndroid Build Coastguard Worker
outputScanline(int inputScanline) const746*c8dee2aaSAndroid Build Coastguard Worker int SkCodec::outputScanline(int inputScanline) const {
747*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(0 <= inputScanline && inputScanline < fEncodedInfo.height());
748*c8dee2aaSAndroid Build Coastguard Worker return this->onOutputScanline(inputScanline);
749*c8dee2aaSAndroid Build Coastguard Worker }
750*c8dee2aaSAndroid Build Coastguard Worker
onOutputScanline(int inputScanline) const751*c8dee2aaSAndroid Build Coastguard Worker int SkCodec::onOutputScanline(int inputScanline) const {
752*c8dee2aaSAndroid Build Coastguard Worker switch (this->getScanlineOrder()) {
753*c8dee2aaSAndroid Build Coastguard Worker case kTopDown_SkScanlineOrder:
754*c8dee2aaSAndroid Build Coastguard Worker return inputScanline;
755*c8dee2aaSAndroid Build Coastguard Worker case kBottomUp_SkScanlineOrder:
756*c8dee2aaSAndroid Build Coastguard Worker return fEncodedInfo.height() - inputScanline - 1;
757*c8dee2aaSAndroid Build Coastguard Worker default:
758*c8dee2aaSAndroid Build Coastguard Worker // This case indicates an interlaced gif and is implemented by SkGifCodec.
759*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(false);
760*c8dee2aaSAndroid Build Coastguard Worker return 0;
761*c8dee2aaSAndroid Build Coastguard Worker }
762*c8dee2aaSAndroid Build Coastguard Worker }
763*c8dee2aaSAndroid Build Coastguard Worker
fillIncompleteImage(const SkImageInfo & info,void * dst,size_t rowBytes,ZeroInitialized zeroInit,int linesRequested,int linesDecoded)764*c8dee2aaSAndroid Build Coastguard Worker void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t rowBytes,
765*c8dee2aaSAndroid Build Coastguard Worker ZeroInitialized zeroInit, int linesRequested, int linesDecoded) {
766*c8dee2aaSAndroid Build Coastguard Worker if (kYes_ZeroInitialized == zeroInit) {
767*c8dee2aaSAndroid Build Coastguard Worker return;
768*c8dee2aaSAndroid Build Coastguard Worker }
769*c8dee2aaSAndroid Build Coastguard Worker
770*c8dee2aaSAndroid Build Coastguard Worker const int linesRemaining = linesRequested - linesDecoded;
771*c8dee2aaSAndroid Build Coastguard Worker SkSampler* sampler = this->getSampler(false);
772*c8dee2aaSAndroid Build Coastguard Worker
773*c8dee2aaSAndroid Build Coastguard Worker const int fillWidth = sampler ? sampler->fillWidth() :
774*c8dee2aaSAndroid Build Coastguard Worker fOptions.fSubset ? fOptions.fSubset->width() :
775*c8dee2aaSAndroid Build Coastguard Worker info.width() ;
776*c8dee2aaSAndroid Build Coastguard Worker void* fillDst = this->getScanlineOrder() == kBottomUp_SkScanlineOrder ? dst :
777*c8dee2aaSAndroid Build Coastguard Worker SkTAddOffset<void>(dst, linesDecoded * rowBytes);
778*c8dee2aaSAndroid Build Coastguard Worker const auto fillInfo = info.makeWH(fillWidth, linesRemaining);
779*c8dee2aaSAndroid Build Coastguard Worker SkSampler::Fill(fillInfo, fillDst, rowBytes, kNo_ZeroInitialized);
780*c8dee2aaSAndroid Build Coastguard Worker }
781*c8dee2aaSAndroid Build Coastguard Worker
sk_select_xform_format(SkColorType colorType,bool forColorTable,skcms_PixelFormat * outFormat)782*c8dee2aaSAndroid Build Coastguard Worker bool sk_select_xform_format(SkColorType colorType, bool forColorTable,
783*c8dee2aaSAndroid Build Coastguard Worker skcms_PixelFormat* outFormat) {
784*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(outFormat);
785*c8dee2aaSAndroid Build Coastguard Worker
786*c8dee2aaSAndroid Build Coastguard Worker switch (colorType) {
787*c8dee2aaSAndroid Build Coastguard Worker case kRGBA_8888_SkColorType:
788*c8dee2aaSAndroid Build Coastguard Worker *outFormat = skcms_PixelFormat_RGBA_8888;
789*c8dee2aaSAndroid Build Coastguard Worker break;
790*c8dee2aaSAndroid Build Coastguard Worker case kBGRA_8888_SkColorType:
791*c8dee2aaSAndroid Build Coastguard Worker *outFormat = skcms_PixelFormat_BGRA_8888;
792*c8dee2aaSAndroid Build Coastguard Worker break;
793*c8dee2aaSAndroid Build Coastguard Worker case kRGB_565_SkColorType:
794*c8dee2aaSAndroid Build Coastguard Worker if (forColorTable) {
795*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_PMCOLOR_IS_RGBA)
796*c8dee2aaSAndroid Build Coastguard Worker *outFormat = skcms_PixelFormat_RGBA_8888;
797*c8dee2aaSAndroid Build Coastguard Worker #else
798*c8dee2aaSAndroid Build Coastguard Worker *outFormat = skcms_PixelFormat_BGRA_8888;
799*c8dee2aaSAndroid Build Coastguard Worker #endif
800*c8dee2aaSAndroid Build Coastguard Worker break;
801*c8dee2aaSAndroid Build Coastguard Worker }
802*c8dee2aaSAndroid Build Coastguard Worker *outFormat = skcms_PixelFormat_BGR_565;
803*c8dee2aaSAndroid Build Coastguard Worker break;
804*c8dee2aaSAndroid Build Coastguard Worker case kRGBA_F16_SkColorType:
805*c8dee2aaSAndroid Build Coastguard Worker *outFormat = skcms_PixelFormat_RGBA_hhhh;
806*c8dee2aaSAndroid Build Coastguard Worker break;
807*c8dee2aaSAndroid Build Coastguard Worker case kBGR_101010x_XR_SkColorType:
808*c8dee2aaSAndroid Build Coastguard Worker *outFormat = skcms_PixelFormat_BGR_101010x_XR;
809*c8dee2aaSAndroid Build Coastguard Worker break;
810*c8dee2aaSAndroid Build Coastguard Worker case kGray_8_SkColorType:
811*c8dee2aaSAndroid Build Coastguard Worker *outFormat = skcms_PixelFormat_G_8;
812*c8dee2aaSAndroid Build Coastguard Worker break;
813*c8dee2aaSAndroid Build Coastguard Worker default:
814*c8dee2aaSAndroid Build Coastguard Worker return false;
815*c8dee2aaSAndroid Build Coastguard Worker }
816*c8dee2aaSAndroid Build Coastguard Worker return true;
817*c8dee2aaSAndroid Build Coastguard Worker }
818*c8dee2aaSAndroid Build Coastguard Worker
initializeColorXform(const SkImageInfo & dstInfo,SkEncodedInfo::Alpha encodedAlpha,bool srcIsOpaque)819*c8dee2aaSAndroid Build Coastguard Worker bool SkCodec::initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha encodedAlpha,
820*c8dee2aaSAndroid Build Coastguard Worker bool srcIsOpaque) {
821*c8dee2aaSAndroid Build Coastguard Worker fXformTime = kNo_XformTime;
822*c8dee2aaSAndroid Build Coastguard Worker bool needsColorXform = false;
823*c8dee2aaSAndroid Build Coastguard Worker if (this->usesColorXform()) {
824*c8dee2aaSAndroid Build Coastguard Worker if (kRGBA_F16_SkColorType == dstInfo.colorType() ||
825*c8dee2aaSAndroid Build Coastguard Worker kBGR_101010x_XR_SkColorType == dstInfo.colorType()) {
826*c8dee2aaSAndroid Build Coastguard Worker needsColorXform = true;
827*c8dee2aaSAndroid Build Coastguard Worker if (dstInfo.colorSpace()) {
828*c8dee2aaSAndroid Build Coastguard Worker dstInfo.colorSpace()->toProfile(&fDstProfile);
829*c8dee2aaSAndroid Build Coastguard Worker } else {
830*c8dee2aaSAndroid Build Coastguard Worker // Use the srcProfile to avoid conversion.
831*c8dee2aaSAndroid Build Coastguard Worker const auto* srcProfile = fEncodedInfo.profile();
832*c8dee2aaSAndroid Build Coastguard Worker fDstProfile = srcProfile ? *srcProfile : *skcms_sRGB_profile();
833*c8dee2aaSAndroid Build Coastguard Worker }
834*c8dee2aaSAndroid Build Coastguard Worker } else if (dstInfo.colorSpace()) {
835*c8dee2aaSAndroid Build Coastguard Worker dstInfo.colorSpace()->toProfile(&fDstProfile);
836*c8dee2aaSAndroid Build Coastguard Worker const auto* srcProfile = fEncodedInfo.profile();
837*c8dee2aaSAndroid Build Coastguard Worker if (!srcProfile) {
838*c8dee2aaSAndroid Build Coastguard Worker srcProfile = skcms_sRGB_profile();
839*c8dee2aaSAndroid Build Coastguard Worker }
840*c8dee2aaSAndroid Build Coastguard Worker if (!skcms_ApproximatelyEqualProfiles(srcProfile, &fDstProfile) ) {
841*c8dee2aaSAndroid Build Coastguard Worker needsColorXform = true;
842*c8dee2aaSAndroid Build Coastguard Worker }
843*c8dee2aaSAndroid Build Coastguard Worker }
844*c8dee2aaSAndroid Build Coastguard Worker }
845*c8dee2aaSAndroid Build Coastguard Worker
846*c8dee2aaSAndroid Build Coastguard Worker if (!this->conversionSupported(dstInfo, srcIsOpaque, needsColorXform)) {
847*c8dee2aaSAndroid Build Coastguard Worker return false;
848*c8dee2aaSAndroid Build Coastguard Worker }
849*c8dee2aaSAndroid Build Coastguard Worker
850*c8dee2aaSAndroid Build Coastguard Worker if (needsColorXform) {
851*c8dee2aaSAndroid Build Coastguard Worker fXformTime = SkEncodedInfo::kPalette_Color != fEncodedInfo.color()
852*c8dee2aaSAndroid Build Coastguard Worker || kRGBA_F16_SkColorType == dstInfo.colorType()
853*c8dee2aaSAndroid Build Coastguard Worker ? kDecodeRow_XformTime : kPalette_XformTime;
854*c8dee2aaSAndroid Build Coastguard Worker if (!sk_select_xform_format(dstInfo.colorType(), fXformTime == kPalette_XformTime,
855*c8dee2aaSAndroid Build Coastguard Worker &fDstXformFormat)) {
856*c8dee2aaSAndroid Build Coastguard Worker return false;
857*c8dee2aaSAndroid Build Coastguard Worker }
858*c8dee2aaSAndroid Build Coastguard Worker if (encodedAlpha == SkEncodedInfo::kUnpremul_Alpha
859*c8dee2aaSAndroid Build Coastguard Worker && dstInfo.alphaType() == kPremul_SkAlphaType) {
860*c8dee2aaSAndroid Build Coastguard Worker fDstXformAlphaFormat = skcms_AlphaFormat_PremulAsEncoded;
861*c8dee2aaSAndroid Build Coastguard Worker } else {
862*c8dee2aaSAndroid Build Coastguard Worker fDstXformAlphaFormat = skcms_AlphaFormat_Unpremul;
863*c8dee2aaSAndroid Build Coastguard Worker }
864*c8dee2aaSAndroid Build Coastguard Worker }
865*c8dee2aaSAndroid Build Coastguard Worker return true;
866*c8dee2aaSAndroid Build Coastguard Worker }
867*c8dee2aaSAndroid Build Coastguard Worker
applyColorXform(void * dst,const void * src,int count) const868*c8dee2aaSAndroid Build Coastguard Worker void SkCodec::applyColorXform(void* dst, const void* src, int count) const {
869*c8dee2aaSAndroid Build Coastguard Worker // It is okay for srcProfile to be null. This will use sRGB.
870*c8dee2aaSAndroid Build Coastguard Worker const auto* srcProfile = fEncodedInfo.profile();
871*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(skcms_Transform(src, fSrcXformFormat, skcms_AlphaFormat_Unpremul, srcProfile,
872*c8dee2aaSAndroid Build Coastguard Worker dst, fDstXformFormat, fDstXformAlphaFormat, &fDstProfile,
873*c8dee2aaSAndroid Build Coastguard Worker count));
874*c8dee2aaSAndroid Build Coastguard Worker }
875*c8dee2aaSAndroid Build Coastguard Worker
getFrameInfo()876*c8dee2aaSAndroid Build Coastguard Worker std::vector<SkCodec::FrameInfo> SkCodec::getFrameInfo() {
877*c8dee2aaSAndroid Build Coastguard Worker const int frameCount = this->getFrameCount();
878*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(frameCount >= 0);
879*c8dee2aaSAndroid Build Coastguard Worker if (frameCount <= 0) {
880*c8dee2aaSAndroid Build Coastguard Worker return std::vector<FrameInfo>{};
881*c8dee2aaSAndroid Build Coastguard Worker }
882*c8dee2aaSAndroid Build Coastguard Worker
883*c8dee2aaSAndroid Build Coastguard Worker if (frameCount == 1 && !this->onGetFrameInfo(0, nullptr)) {
884*c8dee2aaSAndroid Build Coastguard Worker // Not animated.
885*c8dee2aaSAndroid Build Coastguard Worker return std::vector<FrameInfo>{};
886*c8dee2aaSAndroid Build Coastguard Worker }
887*c8dee2aaSAndroid Build Coastguard Worker
888*c8dee2aaSAndroid Build Coastguard Worker std::vector<FrameInfo> result(frameCount);
889*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < frameCount; ++i) {
890*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(this->onGetFrameInfo(i, &result[i]));
891*c8dee2aaSAndroid Build Coastguard Worker }
892*c8dee2aaSAndroid Build Coastguard Worker return result;
893*c8dee2aaSAndroid Build Coastguard Worker }
894*c8dee2aaSAndroid Build Coastguard Worker
ResultToString(Result result)895*c8dee2aaSAndroid Build Coastguard Worker const char* SkCodec::ResultToString(Result result) {
896*c8dee2aaSAndroid Build Coastguard Worker switch (result) {
897*c8dee2aaSAndroid Build Coastguard Worker case kSuccess:
898*c8dee2aaSAndroid Build Coastguard Worker return "success";
899*c8dee2aaSAndroid Build Coastguard Worker case kIncompleteInput:
900*c8dee2aaSAndroid Build Coastguard Worker return "incomplete input";
901*c8dee2aaSAndroid Build Coastguard Worker case kErrorInInput:
902*c8dee2aaSAndroid Build Coastguard Worker return "error in input";
903*c8dee2aaSAndroid Build Coastguard Worker case kInvalidConversion:
904*c8dee2aaSAndroid Build Coastguard Worker return "invalid conversion";
905*c8dee2aaSAndroid Build Coastguard Worker case kInvalidScale:
906*c8dee2aaSAndroid Build Coastguard Worker return "invalid scale";
907*c8dee2aaSAndroid Build Coastguard Worker case kInvalidParameters:
908*c8dee2aaSAndroid Build Coastguard Worker return "invalid parameters";
909*c8dee2aaSAndroid Build Coastguard Worker case kInvalidInput:
910*c8dee2aaSAndroid Build Coastguard Worker return "invalid input";
911*c8dee2aaSAndroid Build Coastguard Worker case kCouldNotRewind:
912*c8dee2aaSAndroid Build Coastguard Worker return "could not rewind";
913*c8dee2aaSAndroid Build Coastguard Worker case kInternalError:
914*c8dee2aaSAndroid Build Coastguard Worker return "internal error";
915*c8dee2aaSAndroid Build Coastguard Worker case kUnimplemented:
916*c8dee2aaSAndroid Build Coastguard Worker return "unimplemented";
917*c8dee2aaSAndroid Build Coastguard Worker default:
918*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(false);
919*c8dee2aaSAndroid Build Coastguard Worker return "bogus result value";
920*c8dee2aaSAndroid Build Coastguard Worker }
921*c8dee2aaSAndroid Build Coastguard Worker }
922*c8dee2aaSAndroid Build Coastguard Worker
fillIn(SkCodec::FrameInfo * frameInfo,bool fullyReceived) const923*c8dee2aaSAndroid Build Coastguard Worker void SkFrame::fillIn(SkCodec::FrameInfo* frameInfo, bool fullyReceived) const {
924*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(frameInfo);
925*c8dee2aaSAndroid Build Coastguard Worker
926*c8dee2aaSAndroid Build Coastguard Worker frameInfo->fRequiredFrame = fRequiredFrame;
927*c8dee2aaSAndroid Build Coastguard Worker frameInfo->fDuration = fDuration;
928*c8dee2aaSAndroid Build Coastguard Worker frameInfo->fFullyReceived = fullyReceived;
929*c8dee2aaSAndroid Build Coastguard Worker frameInfo->fAlphaType = fHasAlpha ? kUnpremul_SkAlphaType
930*c8dee2aaSAndroid Build Coastguard Worker : kOpaque_SkAlphaType;
931*c8dee2aaSAndroid Build Coastguard Worker frameInfo->fHasAlphaWithinBounds = this->reportedAlpha() != SkEncodedInfo::kOpaque_Alpha;
932*c8dee2aaSAndroid Build Coastguard Worker frameInfo->fDisposalMethod = fDisposalMethod;
933*c8dee2aaSAndroid Build Coastguard Worker frameInfo->fBlend = fBlend;
934*c8dee2aaSAndroid Build Coastguard Worker frameInfo->fFrameRect = fRect;
935*c8dee2aaSAndroid Build Coastguard Worker }
936*c8dee2aaSAndroid Build Coastguard Worker
independent(const SkFrame & frame)937*c8dee2aaSAndroid Build Coastguard Worker static bool independent(const SkFrame& frame) {
938*c8dee2aaSAndroid Build Coastguard Worker return frame.getRequiredFrame() == SkCodec::kNoFrame;
939*c8dee2aaSAndroid Build Coastguard Worker }
940*c8dee2aaSAndroid Build Coastguard Worker
restore_bg(const SkFrame & frame)941*c8dee2aaSAndroid Build Coastguard Worker static bool restore_bg(const SkFrame& frame) {
942*c8dee2aaSAndroid Build Coastguard Worker return frame.getDisposalMethod() == SkCodecAnimation::DisposalMethod::kRestoreBGColor;
943*c8dee2aaSAndroid Build Coastguard Worker }
944*c8dee2aaSAndroid Build Coastguard Worker
945*c8dee2aaSAndroid Build Coastguard Worker // As its name suggests, this method computes a frame's alpha (e.g. completely
946*c8dee2aaSAndroid Build Coastguard Worker // opaque, unpremul, binary) and its required frame (a preceding frame that
947*c8dee2aaSAndroid Build Coastguard Worker // this frame depends on, to draw the complete image at this frame's point in
948*c8dee2aaSAndroid Build Coastguard Worker // the animation stream), and calls this frame's setter methods with that
949*c8dee2aaSAndroid Build Coastguard Worker // computed information.
950*c8dee2aaSAndroid Build Coastguard Worker //
951*c8dee2aaSAndroid Build Coastguard Worker // A required frame of kNoFrame means that this frame is independent: drawing
952*c8dee2aaSAndroid Build Coastguard Worker // the complete image at this frame's point in the animation stream does not
953*c8dee2aaSAndroid Build Coastguard Worker // require first preparing the pixel buffer based on another frame. Instead,
954*c8dee2aaSAndroid Build Coastguard Worker // drawing can start from an uninitialized pixel buffer.
955*c8dee2aaSAndroid Build Coastguard Worker //
956*c8dee2aaSAndroid Build Coastguard Worker // "Uninitialized" is from the SkCodec's caller's point of view. In the SkCodec
957*c8dee2aaSAndroid Build Coastguard Worker // implementation, for independent frames, first party Skia code (in src/codec)
958*c8dee2aaSAndroid Build Coastguard Worker // will typically fill the buffer with a uniform background color (e.g.
959*c8dee2aaSAndroid Build Coastguard Worker // transparent black) before calling into third party codec-specific code (e.g.
960*c8dee2aaSAndroid Build Coastguard Worker // libjpeg or libpng). Pixels outside of the frame's rect will remain this
961*c8dee2aaSAndroid Build Coastguard Worker // background color after drawing this frame. For incomplete decodes, pixels
962*c8dee2aaSAndroid Build Coastguard Worker // inside that rect may be (at least temporarily) set to that background color.
963*c8dee2aaSAndroid Build Coastguard Worker // In an incremental decode, later passes may then overwrite that background
964*c8dee2aaSAndroid Build Coastguard Worker // color.
965*c8dee2aaSAndroid Build Coastguard Worker //
966*c8dee2aaSAndroid Build Coastguard Worker // Determining kNoFrame or otherwise involves testing a number of conditions
967*c8dee2aaSAndroid Build Coastguard Worker // sequentially. The first satisfied condition results in setting the required
968*c8dee2aaSAndroid Build Coastguard Worker // frame to kNoFrame (an "INDx" condition) or to a non-negative frame number (a
969*c8dee2aaSAndroid Build Coastguard Worker // "DEPx" condition), and the function returning early. Those "INDx" and "DEPx"
970*c8dee2aaSAndroid Build Coastguard Worker // labels also map to comments in the function body.
971*c8dee2aaSAndroid Build Coastguard Worker //
972*c8dee2aaSAndroid Build Coastguard Worker // - IND1: this frame is the first frame.
973*c8dee2aaSAndroid Build Coastguard Worker // - IND2: this frame fills out the whole image, and it is completely opaque
974*c8dee2aaSAndroid Build Coastguard Worker // or it overwrites (not blends with) the previous frame.
975*c8dee2aaSAndroid Build Coastguard Worker // - IND3: all preceding frames' disposals are kRestorePrevious.
976*c8dee2aaSAndroid Build Coastguard Worker // - IND4: the prevFrame's disposal is kRestoreBGColor, and it fills out the
977*c8dee2aaSAndroid Build Coastguard Worker // whole image or it is itself otherwise independent.
978*c8dee2aaSAndroid Build Coastguard Worker // - DEP5: this frame reports alpha (it is not completely opaque) and it
979*c8dee2aaSAndroid Build Coastguard Worker // blends with (not overwrites) the previous frame.
980*c8dee2aaSAndroid Build Coastguard Worker // - IND6: this frame's rect covers the rects of all preceding frames back to
981*c8dee2aaSAndroid Build Coastguard Worker // and including the most recent independent frame before this frame.
982*c8dee2aaSAndroid Build Coastguard Worker // - DEP7: unconditional.
983*c8dee2aaSAndroid Build Coastguard Worker //
984*c8dee2aaSAndroid Build Coastguard Worker // The "prevFrame" variable initially points to the previous frame (also known
985*c8dee2aaSAndroid Build Coastguard Worker // as the prior frame), but that variable may iterate further backwards over
986*c8dee2aaSAndroid Build Coastguard Worker // the course of this computation.
setAlphaAndRequiredFrame(SkFrame * frame)987*c8dee2aaSAndroid Build Coastguard Worker void SkFrameHolder::setAlphaAndRequiredFrame(SkFrame* frame) {
988*c8dee2aaSAndroid Build Coastguard Worker const bool reportsAlpha = frame->reportedAlpha() != SkEncodedInfo::kOpaque_Alpha;
989*c8dee2aaSAndroid Build Coastguard Worker const auto screenRect = SkIRect::MakeWH(fScreenWidth, fScreenHeight);
990*c8dee2aaSAndroid Build Coastguard Worker const auto frameRect = frame_rect_on_screen(frame->frameRect(), screenRect);
991*c8dee2aaSAndroid Build Coastguard Worker
992*c8dee2aaSAndroid Build Coastguard Worker const int i = frame->frameId();
993*c8dee2aaSAndroid Build Coastguard Worker if (0 == i) {
994*c8dee2aaSAndroid Build Coastguard Worker frame->setHasAlpha(reportsAlpha || frameRect != screenRect);
995*c8dee2aaSAndroid Build Coastguard Worker frame->setRequiredFrame(SkCodec::kNoFrame); // IND1
996*c8dee2aaSAndroid Build Coastguard Worker return;
997*c8dee2aaSAndroid Build Coastguard Worker }
998*c8dee2aaSAndroid Build Coastguard Worker
999*c8dee2aaSAndroid Build Coastguard Worker
1000*c8dee2aaSAndroid Build Coastguard Worker const bool blendWithPrevFrame = frame->getBlend() == SkCodecAnimation::Blend::kSrcOver;
1001*c8dee2aaSAndroid Build Coastguard Worker if ((!reportsAlpha || !blendWithPrevFrame) && frameRect == screenRect) {
1002*c8dee2aaSAndroid Build Coastguard Worker frame->setHasAlpha(reportsAlpha);
1003*c8dee2aaSAndroid Build Coastguard Worker frame->setRequiredFrame(SkCodec::kNoFrame); // IND2
1004*c8dee2aaSAndroid Build Coastguard Worker return;
1005*c8dee2aaSAndroid Build Coastguard Worker }
1006*c8dee2aaSAndroid Build Coastguard Worker
1007*c8dee2aaSAndroid Build Coastguard Worker const SkFrame* prevFrame = this->getFrame(i-1);
1008*c8dee2aaSAndroid Build Coastguard Worker while (prevFrame->getDisposalMethod() == SkCodecAnimation::DisposalMethod::kRestorePrevious) {
1009*c8dee2aaSAndroid Build Coastguard Worker const int prevId = prevFrame->frameId();
1010*c8dee2aaSAndroid Build Coastguard Worker if (0 == prevId) {
1011*c8dee2aaSAndroid Build Coastguard Worker frame->setHasAlpha(true);
1012*c8dee2aaSAndroid Build Coastguard Worker frame->setRequiredFrame(SkCodec::kNoFrame); // IND3
1013*c8dee2aaSAndroid Build Coastguard Worker return;
1014*c8dee2aaSAndroid Build Coastguard Worker }
1015*c8dee2aaSAndroid Build Coastguard Worker
1016*c8dee2aaSAndroid Build Coastguard Worker prevFrame = this->getFrame(prevId - 1);
1017*c8dee2aaSAndroid Build Coastguard Worker }
1018*c8dee2aaSAndroid Build Coastguard Worker
1019*c8dee2aaSAndroid Build Coastguard Worker const bool clearPrevFrame = restore_bg(*prevFrame);
1020*c8dee2aaSAndroid Build Coastguard Worker auto prevFrameRect = frame_rect_on_screen(prevFrame->frameRect(), screenRect);
1021*c8dee2aaSAndroid Build Coastguard Worker
1022*c8dee2aaSAndroid Build Coastguard Worker if (clearPrevFrame) {
1023*c8dee2aaSAndroid Build Coastguard Worker if (prevFrameRect == screenRect || independent(*prevFrame)) {
1024*c8dee2aaSAndroid Build Coastguard Worker frame->setHasAlpha(true);
1025*c8dee2aaSAndroid Build Coastguard Worker frame->setRequiredFrame(SkCodec::kNoFrame); // IND4
1026*c8dee2aaSAndroid Build Coastguard Worker return;
1027*c8dee2aaSAndroid Build Coastguard Worker }
1028*c8dee2aaSAndroid Build Coastguard Worker }
1029*c8dee2aaSAndroid Build Coastguard Worker
1030*c8dee2aaSAndroid Build Coastguard Worker if (reportsAlpha && blendWithPrevFrame) {
1031*c8dee2aaSAndroid Build Coastguard Worker // Note: We could be more aggressive here. If prevFrame clears
1032*c8dee2aaSAndroid Build Coastguard Worker // to background color and covers its required frame (and that
1033*c8dee2aaSAndroid Build Coastguard Worker // frame is independent), prevFrame could be marked independent.
1034*c8dee2aaSAndroid Build Coastguard Worker // Would this extra complexity be worth it?
1035*c8dee2aaSAndroid Build Coastguard Worker frame->setRequiredFrame(prevFrame->frameId()); // DEP5
1036*c8dee2aaSAndroid Build Coastguard Worker frame->setHasAlpha(prevFrame->hasAlpha() || clearPrevFrame);
1037*c8dee2aaSAndroid Build Coastguard Worker return;
1038*c8dee2aaSAndroid Build Coastguard Worker }
1039*c8dee2aaSAndroid Build Coastguard Worker
1040*c8dee2aaSAndroid Build Coastguard Worker while (frameRect.contains(prevFrameRect)) {
1041*c8dee2aaSAndroid Build Coastguard Worker const int prevRequiredFrame = prevFrame->getRequiredFrame();
1042*c8dee2aaSAndroid Build Coastguard Worker if (prevRequiredFrame == SkCodec::kNoFrame) {
1043*c8dee2aaSAndroid Build Coastguard Worker frame->setRequiredFrame(SkCodec::kNoFrame); // IND6
1044*c8dee2aaSAndroid Build Coastguard Worker frame->setHasAlpha(true);
1045*c8dee2aaSAndroid Build Coastguard Worker return;
1046*c8dee2aaSAndroid Build Coastguard Worker }
1047*c8dee2aaSAndroid Build Coastguard Worker
1048*c8dee2aaSAndroid Build Coastguard Worker prevFrame = this->getFrame(prevRequiredFrame);
1049*c8dee2aaSAndroid Build Coastguard Worker prevFrameRect = frame_rect_on_screen(prevFrame->frameRect(), screenRect);
1050*c8dee2aaSAndroid Build Coastguard Worker }
1051*c8dee2aaSAndroid Build Coastguard Worker
1052*c8dee2aaSAndroid Build Coastguard Worker frame->setRequiredFrame(prevFrame->frameId()); // DEP7
1053*c8dee2aaSAndroid Build Coastguard Worker if (restore_bg(*prevFrame)) {
1054*c8dee2aaSAndroid Build Coastguard Worker frame->setHasAlpha(true);
1055*c8dee2aaSAndroid Build Coastguard Worker return;
1056*c8dee2aaSAndroid Build Coastguard Worker }
1057*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(prevFrame->getDisposalMethod() == SkCodecAnimation::DisposalMethod::kKeep);
1058*c8dee2aaSAndroid Build Coastguard Worker frame->setHasAlpha(prevFrame->hasAlpha() || (reportsAlpha && !blendWithPrevFrame));
1059*c8dee2aaSAndroid Build Coastguard Worker }
1060*c8dee2aaSAndroid Build Coastguard Worker
getEncodedData() const1061*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkStream> SkCodec::getEncodedData() const {
1062*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fStream);
1063*c8dee2aaSAndroid Build Coastguard Worker if (!fStream) {
1064*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
1065*c8dee2aaSAndroid Build Coastguard Worker }
1066*c8dee2aaSAndroid Build Coastguard Worker return fStream->duplicate();
1067*c8dee2aaSAndroid Build Coastguard Worker }
1068