Coverage Report

Created: 2021-08-22 09:07

/src/skia/src/codec/SkHeifCodec.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2017 Google Inc.
3
 *
4
 * Use of this source code is governed by a BSD-style license that can be
5
 * found in the LICENSE file.
6
 */
7
8
#include "include/core/SkTypes.h"
9
10
#ifdef SK_HAS_HEIF_LIBRARY
11
#include "include/codec/SkCodec.h"
12
#include "include/core/SkStream.h"
13
#include "include/private/SkColorData.h"
14
#include "src/codec/SkCodecPriv.h"
15
#include "src/codec/SkHeifCodec.h"
16
#include "src/core/SkEndian.h"
17
18
#define FOURCC(c1, c2, c3, c4) \
19
12.6k
    ((c1) << 24 | (c2) << 16 | (c3) << 8 | (c4))
20
21
bool SkHeifCodec::IsSupported(const void* buffer, size_t bytesRead,
22
9.86k
                              SkEncodedImageFormat* format) {
23
    // Parse the ftyp box up to bytesRead to determine if this is HEIF or AVIF.
24
    // Any valid ftyp box should have at least 8 bytes.
25
9.86k
    if (bytesRead < 8) {
26
506
        return false;
27
506
    }
28
29
9.36k
    uint32_t* ptr = (uint32_t*)buffer;
30
9.36k
    uint64_t chunkSize = SkEndian_SwapBE32(ptr[0]);
31
9.36k
    uint32_t chunkType = SkEndian_SwapBE32(ptr[1]);
32
33
9.36k
    if (chunkType != FOURCC('f', 't', 'y', 'p')) {
34
9.26k
        return false;
35
9.26k
    }
36
37
92
    int64_t offset = 8;
38
92
    if (chunkSize == 1) {
39
        // This indicates that the next 8 bytes represent the chunk size,
40
        // and chunk data comes after that.
41
76
        if (bytesRead < 16) {
42
0
            return false;
43
0
        }
44
76
        auto* chunkSizePtr = SkTAddOffset<const uint64_t>(buffer, offset);
45
76
        chunkSize = SkEndian_SwapBE64(*chunkSizePtr);
46
76
        if (chunkSize < 16) {
47
            // The smallest valid chunk is 16 bytes long in this case.
48
1
            return false;
49
1
        }
50
75
        offset += 8;
51
16
    } else if (chunkSize < 8) {
52
        // The smallest valid chunk is 8 bytes long.
53
0
        return false;
54
0
    }
55
56
91
    if (chunkSize > bytesRead) {
57
91
        chunkSize = bytesRead;
58
91
    }
59
91
    int64_t chunkDataSize = chunkSize - offset;
60
    // It should at least have major brand (4-byte) and minor version (4-bytes).
61
    // The rest of the chunk (if any) is a list of (4-byte) compatible brands.
62
91
    if (chunkDataSize < 8) {
63
0
        return false;
64
0
    }
65
66
91
    uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4;
67
91
    bool isHeif = false;
68
482
    for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
69
392
        if (i == 1) {
70
            // Skip this index, it refers to the minorVersion,
71
            // not a brand.
72
90
            continue;
73
90
        }
74
302
        auto* brandPtr = SkTAddOffset<const uint32_t>(buffer, offset + 4 * i);
75
302
        uint32_t brand = SkEndian_SwapBE32(*brandPtr);
76
302
        if (brand == FOURCC('m', 'i', 'f', '1') || brand == FOURCC('h', 'e', 'i', 'c')
77
302
         || brand == FOURCC('m', 's', 'f', '1') || brand == FOURCC('h', 'e', 'v', 'c')
78
302
         || brand == FOURCC('a', 'v', 'i', 'f') || brand == FOURCC('a', 'v', 'i', 's')) {
79
            // AVIF files could have "mif1" as the major brand. So we cannot
80
            // distinguish whether the image is AVIF or HEIC just based on the
81
            // "mif1" brand. So wait until we see a specific avif brand to
82
            // determine whether it is AVIF or HEIC.
83
1
            isHeif = true;
84
1
            if (brand == FOURCC('a', 'v', 'i', 'f')
85
1
              || brand == FOURCC('a', 'v', 'i', 's')) {
86
1
                if (format != nullptr) {
87
1
                    *format = SkEncodedImageFormat::kAVIF;
88
1
                }
89
1
                return true;
90
1
            }
91
1
        }
92
302
    }
93
90
    if (isHeif) {
94
0
        if (format != nullptr) {
95
0
            *format = SkEncodedImageFormat::kHEIF;
96
0
        }
97
0
        return true;
98
0
    }
99
90
    return false;
100
90
}
101
102
0
static SkEncodedOrigin get_orientation(const HeifFrameInfo& frameInfo) {
103
0
    switch (frameInfo.mRotationAngle) {
104
0
        case 0:   return kTopLeft_SkEncodedOrigin;
105
0
        case 90:  return kRightTop_SkEncodedOrigin;
106
0
        case 180: return kBottomRight_SkEncodedOrigin;
107
0
        case 270: return kLeftBottom_SkEncodedOrigin;
108
0
    }
109
0
    return kDefault_SkEncodedOrigin;
110
0
}
111
112
struct SkHeifStreamWrapper : public HeifStream {
113
1
    SkHeifStreamWrapper(SkStream* stream) : fStream(stream) {}
114
115
1
    ~SkHeifStreamWrapper() override {}
116
117
0
    size_t read(void* buffer, size_t size) override {
118
0
        return fStream->read(buffer, size);
119
0
    }
120
121
0
    bool rewind() override {
122
0
        return fStream->rewind();
123
0
    }
124
125
0
    bool seek(size_t position) override {
126
0
        return fStream->seek(position);
127
0
    }
128
129
0
    bool hasLength() const override {
130
0
        return fStream->hasLength();
131
0
    }
132
133
0
    size_t getLength() const override {
134
0
        return fStream->getLength();
135
0
    }
136
137
private:
138
    std::unique_ptr<SkStream> fStream;
139
};
140
141
0
static void releaseProc(const void* ptr, void* context) {
142
0
    delete reinterpret_cast<std::vector<uint8_t>*>(context);
143
0
}
144
145
std::unique_ptr<SkCodec> SkHeifCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
146
1
        SkCodec::SelectionPolicy selectionPolicy, SkEncodedImageFormat format, Result* result) {
147
1
    std::unique_ptr<HeifDecoder> heifDecoder(createHeifDecoder());
148
1
    if (heifDecoder == nullptr) {
149
0
        *result = kInternalError;
150
0
        return nullptr;
151
0
    }
152
153
1
    HeifFrameInfo heifInfo;
154
1
    if (!heifDecoder->init(new SkHeifStreamWrapper(stream.release()), &heifInfo)) {
155
1
        *result = kInvalidInput;
156
1
        return nullptr;
157
1
    }
158
159
0
    size_t frameCount = 1;
160
0
    if (selectionPolicy == SkCodec::SelectionPolicy::kPreferAnimation) {
161
0
        HeifFrameInfo sequenceInfo;
162
0
        if (heifDecoder->getSequenceInfo(&sequenceInfo, &frameCount) &&
163
0
                frameCount > 1) {
164
0
            heifInfo = std::move(sequenceInfo);
165
0
        }
166
0
    }
167
168
0
    std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr;
169
0
    if (heifInfo.mIccData.size() > 0) {
170
0
        auto iccData = new std::vector<uint8_t>(std::move(heifInfo.mIccData));
171
0
        auto icc = SkData::MakeWithProc(iccData->data(), iccData->size(), releaseProc, iccData);
172
0
        profile = SkEncodedInfo::ICCProfile::Make(std::move(icc));
173
0
    }
174
0
    if (profile && profile->profile()->data_color_space != skcms_Signature_RGB) {
175
        // This will result in sRGB.
176
0
        profile = nullptr;
177
0
    }
178
179
0
    SkEncodedInfo info = SkEncodedInfo::Make(heifInfo.mWidth, heifInfo.mHeight,
180
0
            SkEncodedInfo::kYUV_Color, SkEncodedInfo::kOpaque_Alpha, 8, std::move(profile));
181
0
    SkEncodedOrigin orientation = get_orientation(heifInfo);
182
183
0
    *result = kSuccess;
184
0
    return std::unique_ptr<SkCodec>(new SkHeifCodec(
185
0
            std::move(info), heifDecoder.release(), orientation, frameCount > 1, format));
186
0
}
187
188
SkHeifCodec::SkHeifCodec(
189
        SkEncodedInfo&& info,
190
        HeifDecoder* heifDecoder,
191
        SkEncodedOrigin origin,
192
        bool useAnimation,
193
        SkEncodedImageFormat format)
194
    : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, nullptr, origin)
195
    , fHeifDecoder(heifDecoder)
196
    , fSwizzleSrcRow(nullptr)
197
    , fColorXformSrcRow(nullptr)
198
    , fUseAnimation(useAnimation)
199
    , fFormat(format)
200
0
{}
201
202
bool SkHeifCodec::conversionSupported(const SkImageInfo& dstInfo, bool srcIsOpaque,
203
0
                                      bool needsColorXform) {
204
0
    SkASSERT(srcIsOpaque);
205
206
0
    if (kUnknown_SkAlphaType == dstInfo.alphaType()) {
207
0
        return false;
208
0
    }
209
210
0
    if (kOpaque_SkAlphaType != dstInfo.alphaType()) {
211
0
        SkCodecPrintf("Warning: an opaque image should be decoded as opaque "
212
0
                "- it is being decoded as non-opaque, which will draw slower\n");
213
0
    }
214
215
0
    switch (dstInfo.colorType()) {
216
0
        case kRGBA_8888_SkColorType:
217
0
            return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
218
219
0
        case kBGRA_8888_SkColorType:
220
0
            return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888);
221
222
0
        case kRGB_565_SkColorType:
223
0
            if (needsColorXform) {
224
0
                return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
225
0
            } else {
226
0
                return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565);
227
0
            }
228
229
0
        case kRGBA_F16_SkColorType:
230
0
            SkASSERT(needsColorXform);
231
0
            return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
232
233
0
        default:
234
0
            return false;
235
0
    }
236
0
}
Unexecuted instantiation: SkHeifCodec::conversionSupported(SkImageInfo const&, bool, bool)
Unexecuted instantiation: SkHeifCodec::conversionSupported(SkImageInfo const&, bool, bool)
237
238
int SkHeifCodec::readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int count,
239
0
                          const Options& opts) {
240
    // When fSwizzleSrcRow is non-null, it means that we need to swizzle.  In this case,
241
    // we will always decode into fSwizzlerSrcRow before swizzling into the next buffer.
242
    // We can never swizzle "in place" because the swizzler may perform sampling and/or
243
    // subsetting.
244
    // When fColorXformSrcRow is non-null, it means that we need to color xform and that
245
    // we cannot color xform "in place" (many times we can, but not when the dst is F16).
246
    // In this case, we will color xform from fColorXformSrcRow into the dst.
247
0
    uint8_t* decodeDst = (uint8_t*) dst;
248
0
    uint32_t* swizzleDst = (uint32_t*) dst;
249
0
    size_t decodeDstRowBytes = rowBytes;
250
0
    size_t swizzleDstRowBytes = rowBytes;
251
0
    int dstWidth = opts.fSubset ? opts.fSubset->width() : dstInfo.width();
252
0
    if (fSwizzleSrcRow && fColorXformSrcRow) {
253
0
        decodeDst = fSwizzleSrcRow;
254
0
        swizzleDst = fColorXformSrcRow;
255
0
        decodeDstRowBytes = 0;
256
0
        swizzleDstRowBytes = 0;
257
0
        dstWidth = fSwizzler->swizzleWidth();
258
0
    } else if (fColorXformSrcRow) {
259
0
        decodeDst = (uint8_t*) fColorXformSrcRow;
260
0
        swizzleDst = fColorXformSrcRow;
261
0
        decodeDstRowBytes = 0;
262
0
        swizzleDstRowBytes = 0;
263
0
    } else if (fSwizzleSrcRow) {
264
0
        decodeDst = fSwizzleSrcRow;
265
0
        decodeDstRowBytes = 0;
266
0
        dstWidth = fSwizzler->swizzleWidth();
267
0
    }
268
269
0
    for (int y = 0; y < count; y++) {
270
0
        if (!fHeifDecoder->getScanline(decodeDst)) {
271
0
            return y;
272
0
        }
273
274
0
        if (fSwizzler) {
275
0
            fSwizzler->swizzle(swizzleDst, decodeDst);
276
0
        }
277
278
0
        if (this->colorXform()) {
279
0
            this->applyColorXform(dst, swizzleDst, dstWidth);
280
0
            dst = SkTAddOffset<void>(dst, rowBytes);
281
0
        }
282
283
0
        decodeDst = SkTAddOffset<uint8_t>(decodeDst, decodeDstRowBytes);
284
0
        swizzleDst = SkTAddOffset<uint32_t>(swizzleDst, swizzleDstRowBytes);
285
0
    }
286
287
0
    return count;
288
0
}
289
290
0
int SkHeifCodec::onGetFrameCount() {
291
0
    if (!fUseAnimation) {
292
0
        return 1;
293
0
    }
294
295
0
    if (fFrameHolder.size() == 0) {
296
0
        size_t frameCount;
297
0
        HeifFrameInfo frameInfo;
298
0
        if (!fHeifDecoder->getSequenceInfo(&frameInfo, &frameCount)
299
0
                || frameCount <= 1) {
300
0
            fUseAnimation = false;
301
0
            return 1;
302
0
        }
303
0
        fFrameHolder.reserve(frameCount);
304
0
        for (size_t i = 0; i < frameCount; i++) {
305
0
            Frame* frame = fFrameHolder.appendNewFrame();
306
0
            frame->setXYWH(0, 0, frameInfo.mWidth, frameInfo.mHeight);
307
0
            frame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kKeep);
308
            // Currently we don't know the duration until the frame is actually
309
            // decoded (onGetFrameInfo is also called before frame is decoded).
310
            // For now, fill it base on the value reported for the sequence.
311
0
            frame->setDuration(frameInfo.mDurationUs / 1000);
312
0
            frame->setRequiredFrame(SkCodec::kNoFrame);
313
0
            frame->setHasAlpha(false);
314
0
        }
315
0
    }
316
317
0
    return fFrameHolder.size();
318
0
}
319
320
0
const SkFrame* SkHeifCodec::FrameHolder::onGetFrame(int i) const {
321
0
    return static_cast<const SkFrame*>(this->frame(i));
322
0
}
323
324
0
SkHeifCodec::Frame* SkHeifCodec::FrameHolder::appendNewFrame() {
325
0
    const int i = this->size();
326
0
    fFrames.emplace_back(i); // TODO: need to handle frame duration here
327
0
    return &fFrames[i];
328
0
}
329
330
0
const SkHeifCodec::Frame* SkHeifCodec::FrameHolder::frame(int i) const {
331
0
    SkASSERT(i >= 0 && i < this->size());
332
0
    return &fFrames[i];
333
0
}
Unexecuted instantiation: SkHeifCodec::FrameHolder::frame(int) const
Unexecuted instantiation: SkHeifCodec::FrameHolder::frame(int) const
334
335
0
SkHeifCodec::Frame* SkHeifCodec::FrameHolder::editFrameAt(int i) {
336
0
    SkASSERT(i >= 0 && i < this->size());
337
0
    return &fFrames[i];
338
0
}
Unexecuted instantiation: SkHeifCodec::FrameHolder::editFrameAt(int)
Unexecuted instantiation: SkHeifCodec::FrameHolder::editFrameAt(int)
339
340
0
bool SkHeifCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const {
341
0
    if (i >= fFrameHolder.size()) {
342
0
        return false;
343
0
    }
344
345
0
    const Frame* frame = fFrameHolder.frame(i);
346
0
    if (!frame) {
347
0
        return false;
348
0
    }
349
350
0
    if (frameInfo) {
351
0
        frame->fillIn(frameInfo, true);
352
0
    }
353
354
0
    return true;
355
0
}
356
357
0
int SkHeifCodec::onGetRepetitionCount() {
358
0
    return kRepetitionCountInfinite;
359
0
}
360
361
/*
362
 * Performs the heif decode
363
 */
364
SkCodec::Result SkHeifCodec::onGetPixels(const SkImageInfo& dstInfo,
365
                                         void* dst, size_t dstRowBytes,
366
                                         const Options& options,
367
0
                                         int* rowsDecoded) {
368
0
    if (options.fSubset) {
369
        // Not supporting subsets on this path for now.
370
        // TODO: if the heif has tiles, we can support subset here, but
371
        // need to retrieve tile config from metadata retriever first.
372
0
        return kUnimplemented;
373
0
    }
374
375
0
    bool success;
376
0
    if (fUseAnimation) {
377
0
        success = fHeifDecoder->decodeSequence(options.fFrameIndex, &fFrameInfo);
378
0
        fFrameHolder.editFrameAt(options.fFrameIndex)->setDuration(
379
0
                fFrameInfo.mDurationUs / 1000);
380
0
    } else {
381
0
        success = fHeifDecoder->decode(&fFrameInfo);
382
0
    }
383
384
0
    if (!success) {
385
0
        return kInvalidInput;
386
0
    }
387
388
0
    fSwizzler.reset(nullptr);
389
0
    this->allocateStorage(dstInfo);
390
391
0
    int rows = this->readRows(dstInfo, dst, dstRowBytes, dstInfo.height(), options);
392
0
    if (rows < dstInfo.height()) {
393
0
        *rowsDecoded = rows;
394
0
        return kIncompleteInput;
395
0
    }
396
397
0
    return kSuccess;
398
0
}
399
400
0
void SkHeifCodec::allocateStorage(const SkImageInfo& dstInfo) {
401
0
    int dstWidth = dstInfo.width();
402
403
0
    size_t swizzleBytes = 0;
404
0
    if (fSwizzler) {
405
0
        swizzleBytes = fFrameInfo.mBytesPerPixel * fFrameInfo.mWidth;
406
0
        dstWidth = fSwizzler->swizzleWidth();
407
0
        SkASSERT(!this->colorXform() || SkIsAlign4(swizzleBytes));
408
0
    }
409
410
0
    size_t xformBytes = 0;
411
0
    if (this->colorXform() && (kRGBA_F16_SkColorType == dstInfo.colorType() ||
412
0
                               kRGB_565_SkColorType == dstInfo.colorType())) {
413
0
        xformBytes = dstWidth * sizeof(uint32_t);
414
0
    }
415
416
0
    size_t totalBytes = swizzleBytes + xformBytes;
417
0
    fStorage.reset(totalBytes);
418
0
    if (totalBytes > 0) {
419
0
        fSwizzleSrcRow = (swizzleBytes > 0) ? fStorage.get() : nullptr;
420
0
        fColorXformSrcRow = (xformBytes > 0) ?
421
0
                SkTAddOffset<uint32_t>(fStorage.get(), swizzleBytes) : nullptr;
422
0
    }
423
0
}
Unexecuted instantiation: SkHeifCodec::allocateStorage(SkImageInfo const&)
Unexecuted instantiation: SkHeifCodec::allocateStorage(SkImageInfo const&)
424
425
void SkHeifCodec::initializeSwizzler(
426
0
        const SkImageInfo& dstInfo, const Options& options) {
427
0
    SkImageInfo swizzlerDstInfo = dstInfo;
428
0
    if (this->colorXform()) {
429
        // The color xform will be expecting RGBA 8888 input.
430
0
        swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_8888_SkColorType);
431
0
    }
432
433
0
    int srcBPP = 4;
434
0
    if (dstInfo.colorType() == kRGB_565_SkColorType && !this->colorXform()) {
435
0
        srcBPP = 2;
436
0
    }
437
438
0
    fSwizzler = SkSwizzler::MakeSimple(srcBPP, swizzlerDstInfo, options);
439
0
    SkASSERT(fSwizzler);
440
0
}
Unexecuted instantiation: SkHeifCodec::initializeSwizzler(SkImageInfo const&, SkCodec::Options const&)
Unexecuted instantiation: SkHeifCodec::initializeSwizzler(SkImageInfo const&, SkCodec::Options const&)
441
442
0
SkSampler* SkHeifCodec::getSampler(bool createIfNecessary) {
443
0
    if (!createIfNecessary || fSwizzler) {
444
0
        SkASSERT(!fSwizzler || (fSwizzleSrcRow && fStorage.get() == fSwizzleSrcRow));
445
0
        return fSwizzler.get();
446
0
    }
447
448
0
    this->initializeSwizzler(this->dstInfo(), this->options());
449
0
    this->allocateStorage(this->dstInfo());
450
0
    return fSwizzler.get();
451
0
}
Unexecuted instantiation: SkHeifCodec::getSampler(bool)
Unexecuted instantiation: SkHeifCodec::getSampler(bool)
452
453
0
bool SkHeifCodec::onRewind() {
454
0
    fSwizzler.reset(nullptr);
455
0
    fSwizzleSrcRow = nullptr;
456
0
    fColorXformSrcRow = nullptr;
457
0
    fStorage.reset();
458
459
0
    return true;
460
0
}
461
462
SkCodec::Result SkHeifCodec::onStartScanlineDecode(
463
0
        const SkImageInfo& dstInfo, const Options& options) {
464
    // TODO: For now, just decode the whole thing even when there is a subset.
465
    // If the heif image has tiles, we could potentially do this much faster,
466
    // but the tile configuration needs to be retrieved from the metadata.
467
0
    if (!fHeifDecoder->decode(&fFrameInfo)) {
468
0
        return kInvalidInput;
469
0
    }
470
471
0
    if (options.fSubset) {
472
0
        this->initializeSwizzler(dstInfo, options);
473
0
    } else {
474
0
        fSwizzler.reset(nullptr);
475
0
    }
476
477
0
    this->allocateStorage(dstInfo);
478
479
0
    return kSuccess;
480
0
}
481
482
0
int SkHeifCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {
483
0
    return this->readRows(this->dstInfo(), dst, dstRowBytes, count, this->options());
484
0
}
485
486
0
bool SkHeifCodec::onSkipScanlines(int count) {
487
0
    return count == (int) fHeifDecoder->skipScanlines(count);
488
0
}
489
490
#endif // SK_HAS_HEIF_LIBRARY