Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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 {
71 data = SkCopyStreamToData(stream.get());
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,
121 sk_sp<SkData> data,
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,
163 hasAlpha ? SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha);
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) {
246 return SkAvifCodec::IsAvif(data, len);
247}
248
249std::unique_ptr<SkCodec> Decode(std::unique_ptr<SkStream> stream,
250 SkCodec::Result* outResult,
251 SkCodecs::DecodeContext) {
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,
261 SkCodecs::DecodeContext) {
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
SkColor4f color
#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,...)
sk_sp< SkData > SkCopyStreamToData(SkStream *stream)
Definition SkStream.cpp:937
int onGetRepetitionCount() override
static std::unique_ptr< SkCodec > MakeFromStream(std::unique_ptr< SkStream >, Result *)
static bool IsAvif(const void *, size_t)
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
@ 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
double frame
Definition examples.cpp:31
static const uint8_t buffer[]
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)
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
Definition ref_ptr.h:256
@ skcms_PixelFormat_RGBA_8888
void operator()(avifDecoder *decoder) const
const SkIRect * fSubset
Definition SkCodec.h:347
static SkEncodedInfo Make(int width, int height, Color color, Alpha alpha, int bitsPerComponent)
SkISize dimensions() const
int width() const
SkColorType colorType() const
int height() const