Flutter Engine
The Flutter Engine
SkAvifCodec.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2022 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
9
15#include "include/core/SkSize.h"
18#include "modules/skcms/skcms.h"
20
21#include <cstdint>
22#include <cstring>
23#include <utility>
24
25#include "avif/avif.h"
26
27void AvifDecoderDeleter::operator()(avifDecoder* decoder) const {
28 if (decoder != nullptr) {
29 avifDecoderDestroy(decoder);
30 }
31}
32
33bool SkAvifCodec::IsAvif(const void* buffer, size_t bytesRead) {
34 avifROData avifData = {static_cast<const uint8_t*>(buffer), bytesRead};
35 bool isAvif = avifPeekCompatibleFileType(&avifData) == AVIF_TRUE;
36 if (isAvif) return true;
37 // Peeking sometimes fails if the ftyp box is too large. Check the signature
38 // just to be sure.
39 const char* bytes = static_cast<const char*>(buffer);
40 isAvif = bytesRead >= 12 && !memcmp(&bytes[4], "ftyp", 4) &&
41 (!memcmp(&bytes[8], "avif", 4) || !memcmp(&bytes[8], "avis", 4));
42 return isAvif;
43}
44
45std::unique_ptr<SkCodec> SkAvifCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
46 Result* result) {
48 if (!stream) {
50 return nullptr;
51 }
52 AvifDecoder avifDecoder(avifDecoderCreate());
53 if (avifDecoder == nullptr) {
55 return nullptr;
56 }
57 avifDecoder->ignoreXMP = AVIF_TRUE;
58 avifDecoder->ignoreExif = AVIF_TRUE;
59 avifDecoder->allowProgressive = AVIF_FALSE;
60 avifDecoder->allowIncremental = AVIF_FALSE;
61 avifDecoder->strictFlags = AVIF_STRICT_DISABLED;
62 // TODO(vigneshv): Enable threading based on number of CPU cores available.
63 avifDecoder->maxThreads = 1;
64
65 // libavif needs a contiguous data buffer.
66 sk_sp<SkData> data = nullptr;
67 if (stream->getMemoryBase()) {
68 // It is safe to make without copy because we'll hold onto the stream.
70 } else {
72 // If we are forced to copy the stream to a data, we can go ahead and
73 // delete the stream.
74 stream.reset(nullptr);
75 }
76
77 avifResult res = avifDecoderSetIOMemory(avifDecoder.get(), data->bytes(), data->size());
78 if (res != AVIF_RESULT_OK) {
80 return nullptr;
81 }
82
83 res = avifDecoderParse(avifDecoder.get());
84 if (res != AVIF_RESULT_OK) {
86 return nullptr;
87 }
88
89 std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr;
90 // TODO(vigneshv): Get ICC Profile from the avif decoder.
91
92 const int bitsPerComponent = avifDecoder->image->depth > 8 ? 16 : 8;
95 if (avifDecoder->alphaPresent) {
98 } else {
101 }
102 SkEncodedInfo info = SkEncodedInfo::Make(avifDecoder->image->width,
103 avifDecoder->image->height,
104 color,
105 alpha,
106 bitsPerComponent,
107 std::move(profile),
108 avifDecoder->image->depth);
109 bool animation = avifDecoder->imageCount > 1;
110 *result = kSuccess;
111 return std::unique_ptr<SkCodec>(new SkAvifCodec(std::move(info),
112 std::move(stream),
113 std::move(data),
114 std::move(avifDecoder),
116 animation));
117}
118
119SkAvifCodec::SkAvifCodec(SkEncodedInfo&& info,
120 std::unique_ptr<SkStream> stream,
122 AvifDecoder avifDecoder,
123 SkEncodedOrigin origin,
124 bool useAnimation)
125 : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, std::move(stream), origin)
126 , fData(std::move(data))
127 , fAvifDecoder(std::move(avifDecoder))
128 , fUseAnimation(useAnimation) {}
129
131 if (!fUseAnimation) {
132 return 1;
133 }
134
135 if (fFrameHolder.size() == 0) {
136 if (fAvifDecoder->imageCount <= 1) {
137 fUseAnimation = false;
138 return 1;
139 }
140 fFrameHolder.reserve(fAvifDecoder->imageCount);
141 for (int i = 0; i < fAvifDecoder->imageCount; i++) {
142 Frame* frame = fFrameHolder.appendNewFrame(fAvifDecoder->alphaPresent == AVIF_TRUE);
143 frame->setXYWH(0, 0, fAvifDecoder->image->width, fAvifDecoder->image->height);
145 avifImageTiming timing;
146 avifDecoderNthImageTiming(fAvifDecoder.get(), i, &timing);
147 frame->setDuration(timing.duration * 1000);
148 frame->setRequiredFrame(SkCodec::kNoFrame);
149 frame->setHasAlpha(fAvifDecoder->alphaPresent == AVIF_TRUE);
150 }
151 }
152
153 return fFrameHolder.size();
154}
155
156const SkFrame* SkAvifCodec::FrameHolder::onGetFrame(int i) const {
157 return static_cast<const SkFrame*>(this->frame(i));
158}
159
160SkAvifCodec::Frame* SkAvifCodec::FrameHolder::appendNewFrame(bool hasAlpha) {
161 const int i = this->size();
162 fFrames.emplace_back(i,
164 return &fFrames[i];
165}
166
167const SkAvifCodec::Frame* SkAvifCodec::FrameHolder::frame(int i) const {
168 SkASSERT(i >= 0 && i < this->size());
169 return &fFrames[i];
170}
171
172bool SkAvifCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const {
173 if (i >= fFrameHolder.size()) {
174 return false;
175 }
176
177 const Frame* frame = fFrameHolder.frame(i);
178 if (!frame) {
179 return false;
180 }
181
182 if (frameInfo) {
183 frame->fillIn(frameInfo, true);
184 }
185
186 return true;
187}
188
190
192 void* dst,
193 size_t dstRowBytes,
194 const Options& options,
195 int* rowsDecoded) {
196 if (options.fSubset) {
197 return kUnimplemented;
198 }
199
200 const SkColorType dstColorType = dstInfo.colorType();
201 if (dstColorType != kRGBA_8888_SkColorType && dstColorType != kRGBA_F16_SkColorType) {
202 // TODO(vigneshv): Check if more color types need to be supported.
203 // Currently android supports at least RGB565 and BGRA8888 which is not
204 // supported here.
205 return kUnimplemented;
206 }
207
208 avifResult result = avifDecoderNthImage(fAvifDecoder.get(), options.fFrameIndex);
209 if (result != AVIF_RESULT_OK) {
210 return kInvalidInput;
211 }
212
213 if (this->dimensions() != dstInfo.dimensions()) {
214 result = avifImageScale(
215 fAvifDecoder->image, dstInfo.width(), dstInfo.height(), &fAvifDecoder->diag);
216 if (result != AVIF_RESULT_OK) {
217 return kInvalidInput;
218 }
219 }
220
221 avifRGBImage rgbImage;
222 avifRGBImageSetDefaults(&rgbImage, fAvifDecoder->image);
223
224 if (dstColorType == kRGBA_8888_SkColorType) {
225 rgbImage.depth = 8;
226 } else if (dstColorType == kRGBA_F16_SkColorType) {
227 rgbImage.depth = 16;
228 rgbImage.isFloat = AVIF_TRUE;
229 }
230
231 rgbImage.pixels = static_cast<uint8_t*>(dst);
232 rgbImage.rowBytes = dstRowBytes;
233 rgbImage.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_FASTEST;
234
235 result = avifImageYUVToRGB(fAvifDecoder->image, &rgbImage);
236 if (result != AVIF_RESULT_OK) {
237 return kInvalidInput;
238 }
239
240 *rowsDecoded = fAvifDecoder->image->height;
241 return kSuccess;
242}
243
244namespace SkAvifDecoder {
245bool IsAvif(const void* data, size_t len) {
247}
248
249std::unique_ptr<SkCodec> Decode(std::unique_ptr<SkStream> stream,
250 SkCodec::Result* outResult,
252 SkCodec::Result resultStorage;
253 if (!outResult) {
254 outResult = &resultStorage;
255 }
256 return SkAvifCodec::MakeFromStream(std::move(stream), outResult);
257}
258
259std::unique_ptr<SkCodec> Decode(sk_sp<SkData> data,
260 SkCodec::Result* outResult,
262 if (!data) {
263 if (outResult) {
264 *outResult = SkCodec::kInvalidInput;
265 }
266 return nullptr;
267 }
268 return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr);
269}
270} // namespace SkAvifDecoder
const char * options
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
#define SkASSERT(cond)
Definition: SkAssert.h:116
std::unique_ptr< avifDecoder, AvifDecoderDeleter > AvifDecoder
Definition: SkAvifCodec.h:30
SkColorType
Definition: SkColorType.h:19
@ kRGBA_F16_SkColorType
pixel with half floats for red, green, blue, alpha;
Definition: SkColorType.h:38
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
SkEncodedOrigin
@ kDefault_SkEncodedOrigin
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
sk_sp< SkData > SkCopyStreamToData(SkStream *stream)
Definition: SkStream.cpp:937
int onGetRepetitionCount() override
static std::unique_ptr< SkCodec > MakeFromStream(std::unique_ptr< SkStream >, Result *)
Definition: SkAvifCodec.cpp:45
static bool IsAvif(const void *, size_t)
Definition: SkAvifCodec.cpp:33
bool onGetFrameInfo(int, FrameInfo *) const override
int onGetFrameCount() override
Result onGetPixels(const SkImageInfo &dstInfo, void *dst, size_t dstRowBytes, const Options &options, int *rowsDecoded) override
SkISize dimensions() const
Definition: SkCodec.h:230
const SkImageInfo & dstInfo() const
Definition: SkCodec.h:878
SkStream * stream()
Definition: SkCodec.h:865
Result
Definition: SkCodec.h:76
@ kInvalidInput
Definition: SkCodec.h:109
@ kInternalError
Definition: SkCodec.h:118
@ kUnimplemented
Definition: SkCodec.h:123
@ kSuccess
Definition: SkCodec.h:80
static constexpr int kRepetitionCountInfinite
Definition: SkCodec.h:759
static constexpr int kNoFrame
Definition: SkCodec.h:650
const Options & options() const
Definition: SkCodec.h:880
static sk_sp< SkData > MakeWithoutCopy(const void *data, size_t length)
Definition: SkData.h:116
static std::unique_ptr< SkMemoryStream > Make(sk_sp< SkData > data)
Definition: SkStream.cpp:314
virtual size_t getLength() const
Definition: SkStream.h:137
virtual const void * getMemoryBase()
Definition: SkStream.h:141
DlColor color
double frame
Definition: examples.cpp:31
GAsyncResult * result
SK_API bool IsAvif(const void *, size_t)
SK_API std::unique_ptr< SkCodec > Decode(std::unique_ptr< SkStream >, SkCodec::Result *, SkCodecs::DecodeContext=nullptr)
void * DecodeContext
Definition: SkCodec.h:1047
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
Definition: ref_ptr.h:256
@ skcms_PixelFormat_RGBA_8888
Definition: skcms_public.h:287
void operator()(avifDecoder *decoder) const
Definition: SkAvifCodec.cpp:27
const SkIRect * fSubset
Definition: SkCodec.h:347
static SkEncodedInfo Make(int width, int height, Color color, Alpha alpha, int bitsPerComponent)
SkISize dimensions() const
Definition: SkImageInfo.h:421
int width() const
Definition: SkImageInfo.h:365
SkColorType colorType() const
Definition: SkImageInfo.h:373
int height() const
Definition: SkImageInfo.h:371
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63