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