blob: dcbfabc56cdc7572859e7866ea4bd52e9fd10f3b [file] [log] [blame]
Leon Scroggins III04be2b52017-08-17 15:13:20 -04001/*
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"
Cary Clarka4083c92017-09-15 11:59:23 -040013#include "SkColorData.h"
Leon Scroggins III04be2b52017-08-17 15:13:20 -040014#include "SkEndian.h"
15#include "SkStream.h"
16#include "SkHeifCodec.h"
17
18#define FOURCC(c1, c2, c3, c4) \
19 ((c1) << 24 | (c2) << 16 | (c3) << 8 | (c4))
20
21bool SkHeifCodec::IsHeif(const void* buffer, size_t bytesRead) {
22 // Parse the ftyp box up to bytesRead to determine if this is HEIF.
23 // Any valid ftyp box should have at least 8 bytes.
24 if (bytesRead < 8) {
25 return false;
26 }
27
28 uint32_t* ptr = (uint32_t*)buffer;
29 uint64_t chunkSize = SkEndian_SwapBE32(ptr[0]);
30 uint32_t chunkType = SkEndian_SwapBE32(ptr[1]);
31
32 if (chunkType != FOURCC('f', 't', 'y', 'p')) {
33 return false;
34 }
35
Mike Klein329d5042017-10-20 13:48:55 -040036 int64_t offset = 8;
Leon Scroggins III04be2b52017-08-17 15:13:20 -040037 if (chunkSize == 1) {
38 // This indicates that the next 8 bytes represent the chunk size,
39 // and chunk data comes after that.
40 if (bytesRead < 16) {
41 return false;
42 }
43 auto* chunkSizePtr = SkTAddOffset<const uint64_t>(buffer, offset);
44 chunkSize = SkEndian_SwapBE64(*chunkSizePtr);
45 if (chunkSize < 16) {
46 // The smallest valid chunk is 16 bytes long in this case.
47 return false;
48 }
49 offset += 8;
50 } else if (chunkSize < 8) {
51 // The smallest valid chunk is 8 bytes long.
52 return false;
53 }
54
55 if (chunkSize > bytesRead) {
56 chunkSize = bytesRead;
57 }
Mike Klein329d5042017-10-20 13:48:55 -040058 int64_t chunkDataSize = chunkSize - offset;
Leon Scroggins III04be2b52017-08-17 15:13:20 -040059 // It should at least have major brand (4-byte) and minor version (4-bytes).
60 // The rest of the chunk (if any) is a list of (4-byte) compatible brands.
61 if (chunkDataSize < 8) {
62 return false;
63 }
64
65 uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4;
66 for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
67 if (i == 1) {
68 // Skip this index, it refers to the minorVersion,
69 // not a brand.
70 continue;
71 }
72 auto* brandPtr = SkTAddOffset<const uint32_t>(buffer, offset + 4 * i);
73 uint32_t brand = SkEndian_SwapBE32(*brandPtr);
Chong Zhang3c235932017-10-03 13:16:06 -070074 if (brand == FOURCC('m', 'i', 'f', '1') || brand == FOURCC('h', 'e', 'i', 'c')
75 || brand == FOURCC('m', 's', 'f', '1') || brand == FOURCC('h', 'e', 'v', 'c')) {
Leon Scroggins III04be2b52017-08-17 15:13:20 -040076 return true;
77 }
78 }
79 return false;
80}
81
Mike Klein32a0a632017-10-19 08:33:12 -040082static SkEncodedOrigin get_orientation(const HeifFrameInfo& frameInfo) {
Leon Scroggins III04be2b52017-08-17 15:13:20 -040083 switch (frameInfo.mRotationAngle) {
Mike Klein32a0a632017-10-19 08:33:12 -040084 case 0: return kTopLeft_SkEncodedOrigin;
85 case 90: return kRightTop_SkEncodedOrigin;
86 case 180: return kBottomRight_SkEncodedOrigin;
87 case 270: return kLeftBottom_SkEncodedOrigin;
Leon Scroggins III04be2b52017-08-17 15:13:20 -040088 }
Mike Klein32a0a632017-10-19 08:33:12 -040089 return kDefault_SkEncodedOrigin;
Leon Scroggins III04be2b52017-08-17 15:13:20 -040090}
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
121std::unique_ptr<SkCodec> SkHeifCodec::MakeFromStream(
122 std::unique_ptr<SkStream> stream, Result* result) {
123 std::unique_ptr<HeifDecoder> heifDecoder(createHeifDecoder());
124 if (heifDecoder.get() == nullptr) {
125 *result = kInternalError;
126 return nullptr;
127 }
128
129 HeifFrameInfo frameInfo;
130 if (!heifDecoder->init(new SkHeifStreamWrapper(stream.release()),
131 &frameInfo)) {
132 *result = kInvalidInput;
133 return nullptr;
134 }
135
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400136 std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr;
137 if ((frameInfo.mIccSize > 0) && (frameInfo.mIccData != nullptr)) {
138 // FIXME: Would it be possible to use MakeWithoutCopy?
139 auto icc = SkData::MakeWithCopy(frameInfo.mIccData.get(), frameInfo.mIccSize);
140 profile = SkEncodedInfo::ICCProfile::Make(std::move(icc));
141 }
Leon Scroggins III5dd47e42018-09-27 15:26:48 -0400142 if (profile && profile->profile()->data_color_space != skcms_Signature_RGB) {
143 // This will result in sRGB.
144 profile = nullptr;
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400145 }
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400146
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400147 SkEncodedInfo info = SkEncodedInfo::Make(frameInfo.mWidth, frameInfo.mHeight,
148 SkEncodedInfo::kYUV_Color, SkEncodedInfo::kOpaque_Alpha, 8, std::move(profile));
Mike Klein32a0a632017-10-19 08:33:12 -0400149 SkEncodedOrigin orientation = get_orientation(frameInfo);
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400150
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400151 *result = kSuccess;
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400152 return std::unique_ptr<SkCodec>(new SkHeifCodec(std::move(info), heifDecoder.release(),
153 orientation));
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400154}
155
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400156SkHeifCodec::SkHeifCodec(SkEncodedInfo&& info, HeifDecoder* heifDecoder, SkEncodedOrigin origin)
157 : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, nullptr, origin)
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400158 , fHeifDecoder(heifDecoder)
159 , fSwizzleSrcRow(nullptr)
160 , fColorXformSrcRow(nullptr)
161{}
162
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400163
Leon Scroggins III712476e2018-10-03 15:47:00 -0400164bool SkHeifCodec::conversionSupported(const SkImageInfo& dstInfo, bool srcIsOpaque,
165 bool needsColorXform) {
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400166 SkASSERT(srcIsOpaque);
167
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400168 if (kUnknown_SkAlphaType == dstInfo.alphaType()) {
169 return false;
170 }
171
172 if (kOpaque_SkAlphaType != dstInfo.alphaType()) {
173 SkCodecPrintf("Warning: an opaque image should be decoded as opaque "
174 "- it is being decoded as non-opaque, which will draw slower\n");
175 }
176
177 switch (dstInfo.colorType()) {
178 case kRGBA_8888_SkColorType:
179 return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
180
181 case kBGRA_8888_SkColorType:
182 return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888);
183
184 case kRGB_565_SkColorType:
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400185 if (needsColorXform) {
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400186 return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
187 } else {
188 return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565);
189 }
190
191 case kRGBA_F16_SkColorType:
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400192 SkASSERT(needsColorXform);
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400193 return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
194
195 default:
196 return false;
197 }
198}
199
200int SkHeifCodec::readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int count,
201 const Options& opts) {
202 // When fSwizzleSrcRow is non-null, it means that we need to swizzle. In this case,
203 // we will always decode into fSwizzlerSrcRow before swizzling into the next buffer.
204 // We can never swizzle "in place" because the swizzler may perform sampling and/or
205 // subsetting.
206 // When fColorXformSrcRow is non-null, it means that we need to color xform and that
207 // we cannot color xform "in place" (many times we can, but not when the dst is F16).
208 // In this case, we will color xform from fColorXformSrcRow into the dst.
209 uint8_t* decodeDst = (uint8_t*) dst;
210 uint32_t* swizzleDst = (uint32_t*) dst;
211 size_t decodeDstRowBytes = rowBytes;
212 size_t swizzleDstRowBytes = rowBytes;
213 int dstWidth = opts.fSubset ? opts.fSubset->width() : dstInfo.width();
214 if (fSwizzleSrcRow && fColorXformSrcRow) {
215 decodeDst = fSwizzleSrcRow;
216 swizzleDst = fColorXformSrcRow;
217 decodeDstRowBytes = 0;
218 swizzleDstRowBytes = 0;
219 dstWidth = fSwizzler->swizzleWidth();
220 } else if (fColorXformSrcRow) {
221 decodeDst = (uint8_t*) fColorXformSrcRow;
222 swizzleDst = fColorXformSrcRow;
223 decodeDstRowBytes = 0;
224 swizzleDstRowBytes = 0;
225 } else if (fSwizzleSrcRow) {
226 decodeDst = fSwizzleSrcRow;
227 decodeDstRowBytes = 0;
228 dstWidth = fSwizzler->swizzleWidth();
229 }
230
231 for (int y = 0; y < count; y++) {
232 if (!fHeifDecoder->getScanline(decodeDst)) {
233 return y;
234 }
235
236 if (fSwizzler) {
237 fSwizzler->swizzle(swizzleDst, decodeDst);
238 }
239
240 if (this->colorXform()) {
Leon Scroggins III36f7e322018-08-27 11:55:46 -0400241 this->applyColorXform(dst, swizzleDst, dstWidth);
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400242 dst = SkTAddOffset<void>(dst, rowBytes);
243 }
244
245 decodeDst = SkTAddOffset<uint8_t>(decodeDst, decodeDstRowBytes);
246 swizzleDst = SkTAddOffset<uint32_t>(swizzleDst, swizzleDstRowBytes);
247 }
248
249 return count;
250}
251
252/*
253 * Performs the heif decode
254 */
255SkCodec::Result SkHeifCodec::onGetPixels(const SkImageInfo& dstInfo,
256 void* dst, size_t dstRowBytes,
257 const Options& options,
258 int* rowsDecoded) {
259 if (options.fSubset) {
260 // Not supporting subsets on this path for now.
261 // TODO: if the heif has tiles, we can support subset here, but
262 // need to retrieve tile config from metadata retriever first.
263 return kUnimplemented;
264 }
265
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400266 if (!fHeifDecoder->decode(&fFrameInfo)) {
267 return kInvalidInput;
268 }
269
Chong Zhang722b3a72017-08-23 11:17:36 -0700270 fSwizzler.reset(nullptr);
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400271 this->allocateStorage(dstInfo);
272
273 int rows = this->readRows(dstInfo, dst, dstRowBytes, dstInfo.height(), options);
274 if (rows < dstInfo.height()) {
275 *rowsDecoded = rows;
276 return kIncompleteInput;
277 }
278
279 return kSuccess;
280}
281
282void SkHeifCodec::allocateStorage(const SkImageInfo& dstInfo) {
283 int dstWidth = dstInfo.width();
284
285 size_t swizzleBytes = 0;
286 if (fSwizzler) {
287 swizzleBytes = fFrameInfo.mBytesPerPixel * fFrameInfo.mWidth;
288 dstWidth = fSwizzler->swizzleWidth();
289 SkASSERT(!this->colorXform() || SkIsAlign4(swizzleBytes));
290 }
291
292 size_t xformBytes = 0;
293 if (this->colorXform() && (kRGBA_F16_SkColorType == dstInfo.colorType() ||
294 kRGB_565_SkColorType == dstInfo.colorType())) {
295 xformBytes = dstWidth * sizeof(uint32_t);
296 }
297
298 size_t totalBytes = swizzleBytes + xformBytes;
299 fStorage.reset(totalBytes);
300 if (totalBytes > 0) {
301 fSwizzleSrcRow = (swizzleBytes > 0) ? fStorage.get() : nullptr;
302 fColorXformSrcRow = (xformBytes > 0) ?
303 SkTAddOffset<uint32_t>(fStorage.get(), swizzleBytes) : nullptr;
304 }
305}
306
307void SkHeifCodec::initializeSwizzler(
308 const SkImageInfo& dstInfo, const Options& options) {
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400309 SkImageInfo swizzlerDstInfo = dstInfo;
310 if (this->colorXform()) {
311 // The color xform will be expecting RGBA 8888 input.
312 swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_8888_SkColorType);
313 }
314
Leon Scroggins III65f4aea2018-10-24 12:17:22 -0400315 int srcBPP = 4;
316 if (dstInfo.colorType() == kRGB_565_SkColorType && !this->colorXform()) {
317 srcBPP = 2;
318 }
319
320 fSwizzler = SkSwizzler::MakeSimple(srcBPP, swizzlerDstInfo, options);
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400321 SkASSERT(fSwizzler);
322}
323
324SkSampler* SkHeifCodec::getSampler(bool createIfNecessary) {
325 if (!createIfNecessary || fSwizzler) {
326 SkASSERT(!fSwizzler || (fSwizzleSrcRow && fStorage.get() == fSwizzleSrcRow));
327 return fSwizzler.get();
328 }
329
330 this->initializeSwizzler(this->dstInfo(), this->options());
331 this->allocateStorage(this->dstInfo());
332 return fSwizzler.get();
333}
334
335SkCodec::Result SkHeifCodec::onStartScanlineDecode(
336 const SkImageInfo& dstInfo, const Options& options) {
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400337 // TODO: For now, just decode the whole thing even when there is a subset.
338 // If the heif image has tiles, we could potentially do this much faster,
339 // but the tile configuration needs to be retrieved from the metadata.
340 if (!fHeifDecoder->decode(&fFrameInfo)) {
341 return kInvalidInput;
342 }
343
Chong Zhang722b3a72017-08-23 11:17:36 -0700344 if (options.fSubset) {
345 this->initializeSwizzler(dstInfo, options);
346 } else {
347 fSwizzler.reset(nullptr);
348 }
349
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400350 this->allocateStorage(dstInfo);
351
352 return kSuccess;
353}
354
355int SkHeifCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {
356 return this->readRows(this->dstInfo(), dst, dstRowBytes, count, this->options());
357}
358
359bool SkHeifCodec::onSkipScanlines(int count) {
360 return count == (int) fHeifDecoder->skipScanlines(count);
361}
362
363#endif // SK_HAS_HEIF_LIBRARY