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 "SkTypes.h"
9
10#ifdef SK_HAS_HEIF_LIBRARY
11#include "SkCodec.h"
12#include "SkCodecPriv.h"
13#include "SkColorPriv.h"
14#include "SkColorSpace_Base.h"
15#include "SkEndian.h"
16#include "SkStream.h"
17#include "SkHeifCodec.h"
18
19#define FOURCC(c1, c2, c3, c4) \
20    ((c1) << 24 | (c2) << 16 | (c3) << 8 | (c4))
21
22bool SkHeifCodec::IsHeif(const void* buffer, size_t bytesRead) {
23    // Parse the ftyp box up to bytesRead to determine if this is HEIF.
24    // Any valid ftyp box should have at least 8 bytes.
25    if (bytesRead < 8) {
26        return false;
27    }
28
29    uint32_t* ptr = (uint32_t*)buffer;
30    uint64_t chunkSize = SkEndian_SwapBE32(ptr[0]);
31    uint32_t chunkType = SkEndian_SwapBE32(ptr[1]);
32
33    if (chunkType != FOURCC('f', 't', 'y', 'p')) {
34        return false;
35    }
36
37    off64_t offset = 8;
38    if (chunkSize == 1) {
39        // This indicates that the next 8 bytes represent the chunk size,
40        // and chunk data comes after that.
41        if (bytesRead < 16) {
42            return false;
43        }
44        auto* chunkSizePtr = SkTAddOffset<const uint64_t>(buffer, offset);
45        chunkSize = SkEndian_SwapBE64(*chunkSizePtr);
46        if (chunkSize < 16) {
47            // The smallest valid chunk is 16 bytes long in this case.
48            return false;
49        }
50        offset += 8;
51    } else if (chunkSize < 8) {
52        // The smallest valid chunk is 8 bytes long.
53        return false;
54    }
55
56    if (chunkSize > bytesRead) {
57        chunkSize = bytesRead;
58    }
59    off64_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    if (chunkDataSize < 8) {
63        return false;
64    }
65
66    uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4;
67    for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
68        if (i == 1) {
69            // Skip this index, it refers to the minorVersion,
70            // not a brand.
71            continue;
72        }
73        auto* brandPtr = SkTAddOffset<const uint32_t>(buffer, offset + 4 * i);
74        uint32_t brand = SkEndian_SwapBE32(*brandPtr);
75        if (brand == FOURCC('m', 'i', 'f', '1') || brand == FOURCC('h', 'e', 'i', 'c')) {
76            return true;
77        }
78    }
79    return false;
80}
81
82static SkCodec::Origin get_orientation(const HeifFrameInfo& frameInfo) {
83    switch (frameInfo.mRotationAngle) {
84        case 0:   return SkCodec::kTopLeft_Origin;
85        case 90:  return SkCodec::kRightTop_Origin;
86        case 180: return SkCodec::kBottomRight_Origin;
87        case 270: return SkCodec::kLeftBottom_Origin;
88    }
89    return SkCodec::kDefault_Origin;
90}
91
92struct SkHeifStreamWrapper : public HeifStream {
93    SkHeifStreamWrapper(SkStream* stream) : fStream(stream) {}
94
95    ~SkHeifStreamWrapper() override {}
96
97    size_t read(void* buffer, size_t size) override {
98        return fStream->read(buffer, size);
99    }
100
101    bool rewind() override {
102        return fStream->rewind();
103    }
104
105    bool seek(size_t position) override {
106        return fStream->seek(position);
107    }
108
109    bool hasLength() const override {
110        return fStream->hasLength();
111    }
112
113    size_t getLength() const override {
114        return fStream->getLength();
115    }
116
117private:
118    std::unique_ptr<SkStream> fStream;
119};
120
121SkCodec* SkHeifCodec::NewFromStream(SkStream* stream, Result* result) {
122    std::unique_ptr<SkStream> streamDeleter(stream);
123
124    std::unique_ptr<HeifDecoder> heifDecoder(createHeifDecoder());
125    if (heifDecoder.get() == nullptr) {
126        *result = kInternalError;
127        return nullptr;
128    }
129
130    HeifFrameInfo frameInfo;
131    if (!heifDecoder->init(new SkHeifStreamWrapper(streamDeleter.release()),
132                           &frameInfo)) {
133        *result = kInvalidInput;
134        return nullptr;
135    }
136
137    SkEncodedInfo info = SkEncodedInfo::Make(
138            SkEncodedInfo::kYUV_Color, SkEncodedInfo::kOpaque_Alpha, 8);
139
140    Origin orientation = get_orientation(frameInfo);
141
142    sk_sp<SkColorSpace> colorSpace = nullptr;
143    if ((frameInfo.mIccSize > 0) && (frameInfo.mIccData != nullptr)) {
144        SkColorSpace_Base::ICCTypeFlag iccType = SkColorSpace_Base::kRGB_ICCTypeFlag;
145        colorSpace = SkColorSpace_Base::MakeICC(
146                frameInfo.mIccData.get(), frameInfo.mIccSize, iccType);
147    }
148    if (!colorSpace) {
149        colorSpace = SkColorSpace::MakeSRGB();
150    }
151
152    *result = kSuccess;
153    return new SkHeifCodec(frameInfo.mWidth, frameInfo.mHeight,
154            info, heifDecoder.release(), std::move(colorSpace), orientation);
155}
156
157SkHeifCodec::SkHeifCodec(int width, int height, const SkEncodedInfo& info,
158        HeifDecoder* heifDecoder, sk_sp<SkColorSpace> colorSpace, Origin origin)
159    : INHERITED(width, height, info, SkColorSpaceXform::kRGBA_8888_ColorFormat,
160            nullptr, std::move(colorSpace), origin)
161    , fHeifDecoder(heifDecoder)
162    , fSwizzleSrcRow(nullptr)
163    , fColorXformSrcRow(nullptr)
164{}
165
166/*
167 * Checks if the conversion between the input image and the requested output
168 * image has been implemented
169 * Sets the output color format
170 */
171bool SkHeifCodec::setOutputColorFormat(const SkImageInfo& dstInfo) {
172    if (kUnknown_SkAlphaType == dstInfo.alphaType()) {
173        return false;
174    }
175
176    if (kOpaque_SkAlphaType != dstInfo.alphaType()) {
177        SkCodecPrintf("Warning: an opaque image should be decoded as opaque "
178                "- it is being decoded as non-opaque, which will draw slower\n");
179    }
180
181    switch (dstInfo.colorType()) {
182        case kRGBA_8888_SkColorType:
183            return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
184
185        case kBGRA_8888_SkColorType:
186            return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888);
187
188        case kRGB_565_SkColorType:
189            if (this->colorXform()) {
190                return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
191            } else {
192                return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565);
193            }
194
195        case kRGBA_F16_SkColorType:
196            SkASSERT(this->colorXform());
197
198            if (!dstInfo.colorSpace()->gammaIsLinear()) {
199                return false;
200            }
201            return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
202
203        default:
204            return false;
205    }
206}
207
208int SkHeifCodec::readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int count,
209                          const Options& opts) {
210    // When fSwizzleSrcRow is non-null, it means that we need to swizzle.  In this case,
211    // we will always decode into fSwizzlerSrcRow before swizzling into the next buffer.
212    // We can never swizzle "in place" because the swizzler may perform sampling and/or
213    // subsetting.
214    // When fColorXformSrcRow is non-null, it means that we need to color xform and that
215    // we cannot color xform "in place" (many times we can, but not when the dst is F16).
216    // In this case, we will color xform from fColorXformSrcRow into the dst.
217    uint8_t* decodeDst = (uint8_t*) dst;
218    uint32_t* swizzleDst = (uint32_t*) dst;
219    size_t decodeDstRowBytes = rowBytes;
220    size_t swizzleDstRowBytes = rowBytes;
221    int dstWidth = opts.fSubset ? opts.fSubset->width() : dstInfo.width();
222    if (fSwizzleSrcRow && fColorXformSrcRow) {
223        decodeDst = fSwizzleSrcRow;
224        swizzleDst = fColorXformSrcRow;
225        decodeDstRowBytes = 0;
226        swizzleDstRowBytes = 0;
227        dstWidth = fSwizzler->swizzleWidth();
228    } else if (fColorXformSrcRow) {
229        decodeDst = (uint8_t*) fColorXformSrcRow;
230        swizzleDst = fColorXformSrcRow;
231        decodeDstRowBytes = 0;
232        swizzleDstRowBytes = 0;
233    } else if (fSwizzleSrcRow) {
234        decodeDst = fSwizzleSrcRow;
235        decodeDstRowBytes = 0;
236        dstWidth = fSwizzler->swizzleWidth();
237    }
238
239    for (int y = 0; y < count; y++) {
240        if (!fHeifDecoder->getScanline(decodeDst)) {
241            return y;
242        }
243
244        if (fSwizzler) {
245            fSwizzler->swizzle(swizzleDst, decodeDst);
246        }
247
248        if (this->colorXform()) {
249            this->applyColorXform(dst, swizzleDst, dstWidth, kOpaque_SkAlphaType);
250            dst = SkTAddOffset<void>(dst, rowBytes);
251        }
252
253        decodeDst = SkTAddOffset<uint8_t>(decodeDst, decodeDstRowBytes);
254        swizzleDst = SkTAddOffset<uint32_t>(swizzleDst, swizzleDstRowBytes);
255    }
256
257    return count;
258}
259
260/*
261 * Performs the heif decode
262 */
263SkCodec::Result SkHeifCodec::onGetPixels(const SkImageInfo& dstInfo,
264                                         void* dst, size_t dstRowBytes,
265                                         const Options& options,
266                                         int* rowsDecoded) {
267    if (options.fSubset) {
268        // Not supporting subsets on this path for now.
269        // TODO: if the heif has tiles, we can support subset here, but
270        // need to retrieve tile config from metadata retriever first.
271        return kUnimplemented;
272    }
273
274    if (!this->initializeColorXform(dstInfo, options.fPremulBehavior)) {
275        return kInvalidConversion;
276    }
277
278    // Check if we can decode to the requested destination and set the output color space
279    if (!this->setOutputColorFormat(dstInfo)) {
280        return kInvalidConversion;
281    }
282
283    if (!fHeifDecoder->decode(&fFrameInfo)) {
284        return kInvalidInput;
285    }
286
287    fSwizzler.reset(nullptr);
288    this->allocateStorage(dstInfo);
289
290    int rows = this->readRows(dstInfo, dst, dstRowBytes, dstInfo.height(), options);
291    if (rows < dstInfo.height()) {
292        *rowsDecoded = rows;
293        return kIncompleteInput;
294    }
295
296    return kSuccess;
297}
298
299void SkHeifCodec::allocateStorage(const SkImageInfo& dstInfo) {
300    int dstWidth = dstInfo.width();
301
302    size_t swizzleBytes = 0;
303    if (fSwizzler) {
304        swizzleBytes = fFrameInfo.mBytesPerPixel * fFrameInfo.mWidth;
305        dstWidth = fSwizzler->swizzleWidth();
306        SkASSERT(!this->colorXform() || SkIsAlign4(swizzleBytes));
307    }
308
309    size_t xformBytes = 0;
310    if (this->colorXform() && (kRGBA_F16_SkColorType == dstInfo.colorType() ||
311                               kRGB_565_SkColorType == dstInfo.colorType())) {
312        xformBytes = dstWidth * sizeof(uint32_t);
313    }
314
315    size_t totalBytes = swizzleBytes + xformBytes;
316    fStorage.reset(totalBytes);
317    if (totalBytes > 0) {
318        fSwizzleSrcRow = (swizzleBytes > 0) ? fStorage.get() : nullptr;
319        fColorXformSrcRow = (xformBytes > 0) ?
320                SkTAddOffset<uint32_t>(fStorage.get(), swizzleBytes) : nullptr;
321    }
322}
323
324void SkHeifCodec::initializeSwizzler(
325        const SkImageInfo& dstInfo, const Options& options) {
326    SkEncodedInfo swizzlerInfo = this->getEncodedInfo();
327
328    SkImageInfo swizzlerDstInfo = dstInfo;
329    if (this->colorXform()) {
330        // The color xform will be expecting RGBA 8888 input.
331        swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_8888_SkColorType);
332    }
333
334    fSwizzler.reset(SkSwizzler::CreateSwizzler(swizzlerInfo, nullptr,
335            swizzlerDstInfo, options, nullptr, true));
336    SkASSERT(fSwizzler);
337}
338
339SkSampler* SkHeifCodec::getSampler(bool createIfNecessary) {
340    if (!createIfNecessary || fSwizzler) {
341        SkASSERT(!fSwizzler || (fSwizzleSrcRow && fStorage.get() == fSwizzleSrcRow));
342        return fSwizzler.get();
343    }
344
345    this->initializeSwizzler(this->dstInfo(), this->options());
346    this->allocateStorage(this->dstInfo());
347    return fSwizzler.get();
348}
349
350SkCodec::Result SkHeifCodec::onStartScanlineDecode(
351        const SkImageInfo& dstInfo, const Options& options) {
352    if (!this->initializeColorXform(dstInfo, options.fPremulBehavior)) {
353        return kInvalidConversion;
354    }
355
356    // Check if we can decode to the requested destination and set the output color space
357    if (!this->setOutputColorFormat(dstInfo)) {
358        return kInvalidConversion;
359    }
360
361    // TODO: For now, just decode the whole thing even when there is a subset.
362    // If the heif image has tiles, we could potentially do this much faster,
363    // but the tile configuration needs to be retrieved from the metadata.
364    if (!fHeifDecoder->decode(&fFrameInfo)) {
365        return kInvalidInput;
366    }
367
368    if (options.fSubset) {
369        this->initializeSwizzler(dstInfo, options);
370    } else {
371        fSwizzler.reset(nullptr);
372    }
373
374    this->allocateStorage(dstInfo);
375
376    return kSuccess;
377}
378
379int SkHeifCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {
380    return this->readRows(this->dstInfo(), dst, dstRowBytes, count, this->options());
381}
382
383bool SkHeifCodec::onSkipScanlines(int count) {
384    return count == (int) fHeifDecoder->skipScanlines(count);
385}
386
387#endif // SK_HAS_HEIF_LIBRARY
388