blob: 10675112b760e1aa875022d785e45ac55f52de86 [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"
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
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
136 SkEncodedInfo info = SkEncodedInfo::Make(
137 SkEncodedInfo::kYUV_Color, SkEncodedInfo::kOpaque_Alpha, 8);
138
139 Origin orientation = get_orientation(frameInfo);
140
141 sk_sp<SkColorSpace> colorSpace = nullptr;
142 if ((frameInfo.mIccSize > 0) && (frameInfo.mIccData != nullptr)) {
143 SkColorSpace_Base::ICCTypeFlag iccType = SkColorSpace_Base::kRGB_ICCTypeFlag;
144 colorSpace = SkColorSpace_Base::MakeICC(
145 frameInfo.mIccData.get(), frameInfo.mIccSize, iccType);
146 }
147 if (!colorSpace) {
148 colorSpace = SkColorSpace::MakeSRGB();
149 }
150
151 *result = kSuccess;
152 return std::unique_ptr<SkCodec>(new SkHeifCodec(frameInfo.mWidth, frameInfo.mHeight,
153 info, heifDecoder.release(), std::move(colorSpace), orientation));
154}
155
156SkHeifCodec::SkHeifCodec(int width, int height, const SkEncodedInfo& info,
157 HeifDecoder* heifDecoder, sk_sp<SkColorSpace> colorSpace, Origin origin)
158 : INHERITED(width, height, info, SkColorSpaceXform::kRGBA_8888_ColorFormat,
159 nullptr, std::move(colorSpace), origin)
160 , fHeifDecoder(heifDecoder)
161 , fSwizzleSrcRow(nullptr)
162 , fColorXformSrcRow(nullptr)
163{}
164
165/*
166 * Checks if the conversion between the input image and the requested output
167 * image has been implemented
168 * Sets the output color format
169 */
170bool SkHeifCodec::setOutputColorFormat(const SkImageInfo& dstInfo) {
171 if (kUnknown_SkAlphaType == dstInfo.alphaType()) {
172 return false;
173 }
174
175 if (kOpaque_SkAlphaType != dstInfo.alphaType()) {
176 SkCodecPrintf("Warning: an opaque image should be decoded as opaque "
177 "- it is being decoded as non-opaque, which will draw slower\n");
178 }
179
180 switch (dstInfo.colorType()) {
181 case kRGBA_8888_SkColorType:
182 return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
183
184 case kBGRA_8888_SkColorType:
185 return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888);
186
187 case kRGB_565_SkColorType:
188 if (this->colorXform()) {
189 return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
190 } else {
191 return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565);
192 }
193
194 case kRGBA_F16_SkColorType:
195 SkASSERT(this->colorXform());
196
197 if (!dstInfo.colorSpace()->gammaIsLinear()) {
198 return false;
199 }
200 return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
201
202 default:
203 return false;
204 }
205}
206
207int SkHeifCodec::readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int count,
208 const Options& opts) {
209 // When fSwizzleSrcRow is non-null, it means that we need to swizzle. In this case,
210 // we will always decode into fSwizzlerSrcRow before swizzling into the next buffer.
211 // We can never swizzle "in place" because the swizzler may perform sampling and/or
212 // subsetting.
213 // When fColorXformSrcRow is non-null, it means that we need to color xform and that
214 // we cannot color xform "in place" (many times we can, but not when the dst is F16).
215 // In this case, we will color xform from fColorXformSrcRow into the dst.
216 uint8_t* decodeDst = (uint8_t*) dst;
217 uint32_t* swizzleDst = (uint32_t*) dst;
218 size_t decodeDstRowBytes = rowBytes;
219 size_t swizzleDstRowBytes = rowBytes;
220 int dstWidth = opts.fSubset ? opts.fSubset->width() : dstInfo.width();
221 if (fSwizzleSrcRow && fColorXformSrcRow) {
222 decodeDst = fSwizzleSrcRow;
223 swizzleDst = fColorXformSrcRow;
224 decodeDstRowBytes = 0;
225 swizzleDstRowBytes = 0;
226 dstWidth = fSwizzler->swizzleWidth();
227 } else if (fColorXformSrcRow) {
228 decodeDst = (uint8_t*) fColorXformSrcRow;
229 swizzleDst = fColorXformSrcRow;
230 decodeDstRowBytes = 0;
231 swizzleDstRowBytes = 0;
232 } else if (fSwizzleSrcRow) {
233 decodeDst = fSwizzleSrcRow;
234 decodeDstRowBytes = 0;
235 dstWidth = fSwizzler->swizzleWidth();
236 }
237
238 for (int y = 0; y < count; y++) {
239 if (!fHeifDecoder->getScanline(decodeDst)) {
240 return y;
241 }
242
243 if (fSwizzler) {
244 fSwizzler->swizzle(swizzleDst, decodeDst);
245 }
246
247 if (this->colorXform()) {
248 this->applyColorXform(dst, swizzleDst, dstWidth, kOpaque_SkAlphaType);
249 dst = SkTAddOffset<void>(dst, rowBytes);
250 }
251
252 decodeDst = SkTAddOffset<uint8_t>(decodeDst, decodeDstRowBytes);
253 swizzleDst = SkTAddOffset<uint32_t>(swizzleDst, swizzleDstRowBytes);
254 }
255
256 return count;
257}
258
259/*
260 * Performs the heif decode
261 */
262SkCodec::Result SkHeifCodec::onGetPixels(const SkImageInfo& dstInfo,
263 void* dst, size_t dstRowBytes,
264 const Options& options,
265 int* rowsDecoded) {
266 if (options.fSubset) {
267 // Not supporting subsets on this path for now.
268 // TODO: if the heif has tiles, we can support subset here, but
269 // need to retrieve tile config from metadata retriever first.
270 return kUnimplemented;
271 }
272
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400273 // Check if we can decode to the requested destination and set the output color space
274 if (!this->setOutputColorFormat(dstInfo)) {
275 return kInvalidConversion;
276 }
277
278 if (!fHeifDecoder->decode(&fFrameInfo)) {
279 return kInvalidInput;
280 }
281
Chong Zhang722b3a72017-08-23 11:17:36 -0700282 fSwizzler.reset(nullptr);
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400283 this->allocateStorage(dstInfo);
284
285 int rows = this->readRows(dstInfo, dst, dstRowBytes, dstInfo.height(), options);
286 if (rows < dstInfo.height()) {
287 *rowsDecoded = rows;
288 return kIncompleteInput;
289 }
290
291 return kSuccess;
292}
293
294void SkHeifCodec::allocateStorage(const SkImageInfo& dstInfo) {
295 int dstWidth = dstInfo.width();
296
297 size_t swizzleBytes = 0;
298 if (fSwizzler) {
299 swizzleBytes = fFrameInfo.mBytesPerPixel * fFrameInfo.mWidth;
300 dstWidth = fSwizzler->swizzleWidth();
301 SkASSERT(!this->colorXform() || SkIsAlign4(swizzleBytes));
302 }
303
304 size_t xformBytes = 0;
305 if (this->colorXform() && (kRGBA_F16_SkColorType == dstInfo.colorType() ||
306 kRGB_565_SkColorType == dstInfo.colorType())) {
307 xformBytes = dstWidth * sizeof(uint32_t);
308 }
309
310 size_t totalBytes = swizzleBytes + xformBytes;
311 fStorage.reset(totalBytes);
312 if (totalBytes > 0) {
313 fSwizzleSrcRow = (swizzleBytes > 0) ? fStorage.get() : nullptr;
314 fColorXformSrcRow = (xformBytes > 0) ?
315 SkTAddOffset<uint32_t>(fStorage.get(), swizzleBytes) : nullptr;
316 }
317}
318
319void SkHeifCodec::initializeSwizzler(
320 const SkImageInfo& dstInfo, const Options& options) {
321 SkEncodedInfo swizzlerInfo = this->getEncodedInfo();
322
323 SkImageInfo swizzlerDstInfo = dstInfo;
324 if (this->colorXform()) {
325 // The color xform will be expecting RGBA 8888 input.
326 swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_8888_SkColorType);
327 }
328
329 fSwizzler.reset(SkSwizzler::CreateSwizzler(swizzlerInfo, nullptr,
330 swizzlerDstInfo, options, nullptr, true));
331 SkASSERT(fSwizzler);
332}
333
334SkSampler* SkHeifCodec::getSampler(bool createIfNecessary) {
335 if (!createIfNecessary || fSwizzler) {
336 SkASSERT(!fSwizzler || (fSwizzleSrcRow && fStorage.get() == fSwizzleSrcRow));
337 return fSwizzler.get();
338 }
339
340 this->initializeSwizzler(this->dstInfo(), this->options());
341 this->allocateStorage(this->dstInfo());
342 return fSwizzler.get();
343}
344
345SkCodec::Result SkHeifCodec::onStartScanlineDecode(
346 const SkImageInfo& dstInfo, const Options& options) {
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400347 // Check if we can decode to the requested destination and set the output color space
348 if (!this->setOutputColorFormat(dstInfo)) {
349 return kInvalidConversion;
350 }
351
352 // TODO: For now, just decode the whole thing even when there is a subset.
353 // If the heif image has tiles, we could potentially do this much faster,
354 // but the tile configuration needs to be retrieved from the metadata.
355 if (!fHeifDecoder->decode(&fFrameInfo)) {
356 return kInvalidInput;
357 }
358
Chong Zhang722b3a72017-08-23 11:17:36 -0700359 if (options.fSubset) {
360 this->initializeSwizzler(dstInfo, options);
361 } else {
362 fSwizzler.reset(nullptr);
363 }
364
Leon Scroggins III04be2b52017-08-17 15:13:20 -0400365 this->allocateStorage(dstInfo);
366
367 return kSuccess;
368}
369
370int SkHeifCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {
371 return this->readRows(this->dstInfo(), dst, dstRowBytes, count, this->options());
372}
373
374bool SkHeifCodec::onSkipScanlines(int count) {
375 return count == (int) fHeifDecoder->skipScanlines(count);
376}
377
378#endif // SK_HAS_HEIF_LIBRARY