Bring android/o-mr1-release up to date for HEIF

Bug: b/65262488
This cherry-picks 3 commits:
========================================================================
skia: add heif decoding support -- DO NOT MERGE

Cherry-picked from https://skia-review.googlesource.com/c/35701 in skia.

Change-Id: Id9cbe6a463384ce6a0b57a32a4ba881968fe32bb
========================================================================
Initialize the swizzler if there is a subset -- DO NOT MERGE

Reviewed-on: https://skia-review.googlesource.com/37680
Change-Id: Ic6bef3f63bcda24fc3d88ce0cdcf1216441faa40
========================================================================
Use kIgnore blend behavior when encoding JPEG

Cherry-pick from upstream bb3dc768fd57956feb3947991011052b034dcb7a

This looks to have been an oversight - encoding PNG and WEBP both use
kIgnore when called through this interface.

No-Tree-Checks: true
No-Try: true
No-Presubmit: true
Change-Id: I23ba02f979210087808ee2da026a2ca0269e0fb4
Reviewed-On: https://skia-review.googlesource.com/43261
Reviewed-By: Brian Osman <brianosman@google.com>
Commit-Queue: Leon Scroggins <scroggo@google.com>
Reviewed-on: https://skia-review.googlesource.com/53902
Reviewed-by: Derek Sollenberger <djsollen@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 2fac60e..0d8a847 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -31,6 +31,7 @@
   skia_use_piex = !is_win
   skia_use_zlib = true
   skia_use_metal = false
+  skia_use_libheif = false
 
   skia_android_serial = ""
   skia_enable_discrete_gpu = true
@@ -545,6 +546,17 @@
   }
 }
 
+optional("heif") {
+  enabled = skia_use_libheif
+  public_defines = [ "SK_HAS_HEIF_LIBRARY" ]
+
+  deps = []
+
+  sources = [
+    "src/codec/SkHeifCodec.cpp",
+  ]
+}
+
 optional("jpeg") {
   enabled = skia_use_libjpeg_turbo
   public_defines = [ "SK_HAS_JPEG_LIBRARY" ]
@@ -668,6 +680,7 @@
     ":fontmgr_fontconfig",
     ":fontmgr_fuchsia",
     ":gpu",
+    ":heif",
     ":hsw",
     ":jpeg",
     ":none",
diff --git a/gn/gn_to_bp.py b/gn/gn_to_bp.py
index 3fc30a0..b073c93 100644
--- a/gn/gn_to_bp.py
+++ b/gn/gn_to_bp.py
@@ -31,6 +31,7 @@
     'libicui18n',
     'libexpat',
     'libft2',
+    'libheif',
     'libdng_sdk',
     'libpiex',
     'libcutils',
@@ -124,6 +125,7 @@
         "libdng_sdk",
         "libexpat",
         "libft2",
+        "libheif",
         "libicui18n",
         "libicuuc",
         "libjpeg",
@@ -195,6 +197,7 @@
 gn_args = {
   'is_official_build':  'true',
   'skia_enable_tools':  'true',
+  'skia_use_libheif':   'true',
   'skia_use_vulkan':    'true',
   'target_cpu':         '"none"',
   'target_os':          '"android"',
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
index bb780f0..540f6fe 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -50,7 +50,7 @@
      *  this many bytes, or by implementing rewind() to be able to rewind()
      *  after reading this many bytes.
      */
-    static size_t MinBufferedBytesNeeded();
+    static constexpr size_t MinBufferedBytesNeeded() { return 32; }
 
     /**
      *  Error codes for various SkCodec methods.
diff --git a/include/core/SkEncodedImageFormat.h b/include/core/SkEncodedImageFormat.h
index 8f79236..c391053 100644
--- a/include/core/SkEncodedImageFormat.h
+++ b/include/core/SkEncodedImageFormat.h
@@ -28,6 +28,7 @@
     kKTX,
     kASTC,
     kDNG,
+    kHEIF,
 };
 
 #endif  // SkEncodedImageFormat_DEFINED
diff --git a/src/android/SkBitmapRegionDecoder.cpp b/src/android/SkBitmapRegionDecoder.cpp
index 8298fb5..f84e6e9 100644
--- a/src/android/SkBitmapRegionDecoder.cpp
+++ b/src/android/SkBitmapRegionDecoder.cpp
@@ -33,6 +33,7 @@
                 case SkEncodedImageFormat::kJPEG:
                 case SkEncodedImageFormat::kPNG:
                 case SkEncodedImageFormat::kWEBP:
+                case SkEncodedImageFormat::kHEIF:
                     break;
                 default:
                     return nullptr;
diff --git a/src/codec/SkAndroidCodec.cpp b/src/codec/SkAndroidCodec.cpp
index 0245ea2..e0e7bce 100644
--- a/src/codec/SkAndroidCodec.cpp
+++ b/src/codec/SkAndroidCodec.cpp
@@ -82,6 +82,9 @@
         case SkEncodedImageFormat::kGIF:
         case SkEncodedImageFormat::kBMP:
         case SkEncodedImageFormat::kWBMP:
+#ifdef SK_HAS_HEIF_LIBRARY
+        case SkEncodedImageFormat::kHEIF:
+#endif
             return new SkSampledCodec(codec.release());
 #ifdef SK_HAS_WEBP_LIBRARY
         case SkEncodedImageFormat::kWEBP:
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index 872fe2b..111aacb 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -14,6 +14,9 @@
 #include "SkFrameHolder.h"
 #include "SkGifCodec.h"
 #include "SkHalf.h"
+#ifdef SK_HAS_HEIF_LIBRARY
+#include "SkHeifCodec.h"
+#endif
 #include "SkIcoCodec.h"
 #include "SkJpegCodec.h"
 #ifdef SK_HAS_PNG_LIBRARY
@@ -41,13 +44,12 @@
     { SkIcoCodec::IsIco, SkIcoCodec::NewFromStream },
 #endif
     { SkBmpCodec::IsBmp, SkBmpCodec::NewFromStream },
-    { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewFromStream }
+    { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewFromStream },
+#ifdef SK_HAS_HEIF_LIBRARY
+    { SkHeifCodec::IsHeif, SkHeifCodec::NewFromStream },
+#endif
 };
 
-size_t SkCodec::MinBufferedBytesNeeded() {
-    return WEBP_VP8_HEADER_SIZE;
-}
-
 SkCodec* SkCodec::NewFromStream(SkStream* stream,
         Result* outResult, SkPngChunkReader* chunkReader) {
     Result resultStorage;
@@ -62,9 +64,7 @@
 
     std::unique_ptr<SkStream> streamDeleter(stream);
 
-    // 14 is enough to read all of the supported types.
-    const size_t bytesToRead = 14;
-    SkASSERT(bytesToRead <= MinBufferedBytesNeeded());
+    constexpr size_t bytesToRead = MinBufferedBytesNeeded();
 
     char buffer[bytesToRead];
     size_t bytesRead = stream->peek(buffer, bytesToRead);
diff --git a/src/codec/SkHeifCodec.cpp b/src/codec/SkHeifCodec.cpp
new file mode 100644
index 0000000..7c1f494
--- /dev/null
+++ b/src/codec/SkHeifCodec.cpp
@@ -0,0 +1,387 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#ifdef SK_HAS_HEIF_LIBRARY
+#include "SkCodec.h"
+#include "SkCodecPriv.h"
+#include "SkColorPriv.h"
+#include "SkColorSpace_Base.h"
+#include "SkEndian.h"
+#include "SkStream.h"
+#include "SkHeifCodec.h"
+
+#define FOURCC(c1, c2, c3, c4) \
+    ((c1) << 24 | (c2) << 16 | (c3) << 8 | (c4))
+
+bool SkHeifCodec::IsHeif(const void* buffer, size_t bytesRead) {
+    // Parse the ftyp box up to bytesRead to determine if this is HEIF.
+    // Any valid ftyp box should have at least 8 bytes.
+    if (bytesRead < 8) {
+        return false;
+    }
+
+    uint32_t* ptr = (uint32_t*)buffer;
+    uint64_t chunkSize = SkEndian_SwapBE32(ptr[0]);
+    uint32_t chunkType = SkEndian_SwapBE32(ptr[1]);
+
+    if (chunkType != FOURCC('f', 't', 'y', 'p')) {
+        return false;
+    }
+
+    off64_t offset = 8;
+    if (chunkSize == 1) {
+        // This indicates that the next 8 bytes represent the chunk size,
+        // and chunk data comes after that.
+        if (bytesRead < 16) {
+            return false;
+        }
+        auto* chunkSizePtr = SkTAddOffset<const uint64_t>(buffer, offset);
+        chunkSize = SkEndian_SwapBE64(*chunkSizePtr);
+        if (chunkSize < 16) {
+            // The smallest valid chunk is 16 bytes long in this case.
+            return false;
+        }
+        offset += 8;
+    } else if (chunkSize < 8) {
+        // The smallest valid chunk is 8 bytes long.
+        return false;
+    }
+
+    if (chunkSize > bytesRead) {
+        chunkSize = bytesRead;
+    }
+    off64_t chunkDataSize = chunkSize - offset;
+    // It should at least have major brand (4-byte) and minor version (4-bytes).
+    // The rest of the chunk (if any) is a list of (4-byte) compatible brands.
+    if (chunkDataSize < 8) {
+        return false;
+    }
+
+    uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4;
+    for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
+        if (i == 1) {
+            // Skip this index, it refers to the minorVersion,
+            // not a brand.
+            continue;
+        }
+        auto* brandPtr = SkTAddOffset<const uint32_t>(buffer, offset + 4 * i);
+        uint32_t brand = SkEndian_SwapBE32(*brandPtr);
+        if (brand == FOURCC('m', 'i', 'f', '1') || brand == FOURCC('h', 'e', 'i', 'c')) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static SkCodec::Origin get_orientation(const HeifFrameInfo& frameInfo) {
+    switch (frameInfo.mRotationAngle) {
+        case 0:   return SkCodec::kTopLeft_Origin;
+        case 90:  return SkCodec::kRightTop_Origin;
+        case 180: return SkCodec::kBottomRight_Origin;
+        case 270: return SkCodec::kLeftBottom_Origin;
+    }
+    return SkCodec::kDefault_Origin;
+}
+
+struct SkHeifStreamWrapper : public HeifStream {
+    SkHeifStreamWrapper(SkStream* stream) : fStream(stream) {}
+
+    ~SkHeifStreamWrapper() override {}
+
+    size_t read(void* buffer, size_t size) override {
+        return fStream->read(buffer, size);
+    }
+
+    bool rewind() override {
+        return fStream->rewind();
+    }
+
+    bool seek(size_t position) override {
+        return fStream->seek(position);
+    }
+
+    bool hasLength() const override {
+        return fStream->hasLength();
+    }
+
+    size_t getLength() const override {
+        return fStream->getLength();
+    }
+
+private:
+    std::unique_ptr<SkStream> fStream;
+};
+
+SkCodec* SkHeifCodec::NewFromStream(SkStream* stream, Result* result) {
+    std::unique_ptr<SkStream> streamDeleter(stream);
+
+    std::unique_ptr<HeifDecoder> heifDecoder(createHeifDecoder());
+    if (heifDecoder.get() == nullptr) {
+        *result = kInternalError;
+        return nullptr;
+    }
+
+    HeifFrameInfo frameInfo;
+    if (!heifDecoder->init(new SkHeifStreamWrapper(streamDeleter.release()),
+                           &frameInfo)) {
+        *result = kInvalidInput;
+        return nullptr;
+    }
+
+    SkEncodedInfo info = SkEncodedInfo::Make(
+            SkEncodedInfo::kYUV_Color, SkEncodedInfo::kOpaque_Alpha, 8);
+
+    Origin orientation = get_orientation(frameInfo);
+
+    sk_sp<SkColorSpace> colorSpace = nullptr;
+    if ((frameInfo.mIccSize > 0) && (frameInfo.mIccData != nullptr)) {
+        SkColorSpace_Base::ICCTypeFlag iccType = SkColorSpace_Base::kRGB_ICCTypeFlag;
+        colorSpace = SkColorSpace_Base::MakeICC(
+                frameInfo.mIccData.get(), frameInfo.mIccSize, iccType);
+    }
+    if (!colorSpace) {
+        colorSpace = SkColorSpace::MakeSRGB();
+    }
+
+    *result = kSuccess;
+    return new SkHeifCodec(frameInfo.mWidth, frameInfo.mHeight,
+            info, heifDecoder.release(), std::move(colorSpace), orientation);
+}
+
+SkHeifCodec::SkHeifCodec(int width, int height, const SkEncodedInfo& info,
+        HeifDecoder* heifDecoder, sk_sp<SkColorSpace> colorSpace, Origin origin)
+    : INHERITED(width, height, info, SkColorSpaceXform::kRGBA_8888_ColorFormat,
+            nullptr, std::move(colorSpace), origin)
+    , fHeifDecoder(heifDecoder)
+    , fSwizzleSrcRow(nullptr)
+    , fColorXformSrcRow(nullptr)
+{}
+
+/*
+ * Checks if the conversion between the input image and the requested output
+ * image has been implemented
+ * Sets the output color format
+ */
+bool SkHeifCodec::setOutputColorFormat(const SkImageInfo& dstInfo) {
+    if (kUnknown_SkAlphaType == dstInfo.alphaType()) {
+        return false;
+    }
+
+    if (kOpaque_SkAlphaType != dstInfo.alphaType()) {
+        SkCodecPrintf("Warning: an opaque image should be decoded as opaque "
+                "- it is being decoded as non-opaque, which will draw slower\n");
+    }
+
+    switch (dstInfo.colorType()) {
+        case kRGBA_8888_SkColorType:
+            return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
+
+        case kBGRA_8888_SkColorType:
+            return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888);
+
+        case kRGB_565_SkColorType:
+            if (this->colorXform()) {
+                return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
+            } else {
+                return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565);
+            }
+
+        case kRGBA_F16_SkColorType:
+            SkASSERT(this->colorXform());
+
+            if (!dstInfo.colorSpace()->gammaIsLinear()) {
+                return false;
+            }
+            return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
+
+        default:
+            return false;
+    }
+}
+
+int SkHeifCodec::readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int count,
+                          const Options& opts) {
+    // When fSwizzleSrcRow is non-null, it means that we need to swizzle.  In this case,
+    // we will always decode into fSwizzlerSrcRow before swizzling into the next buffer.
+    // We can never swizzle "in place" because the swizzler may perform sampling and/or
+    // subsetting.
+    // When fColorXformSrcRow is non-null, it means that we need to color xform and that
+    // we cannot color xform "in place" (many times we can, but not when the dst is F16).
+    // In this case, we will color xform from fColorXformSrcRow into the dst.
+    uint8_t* decodeDst = (uint8_t*) dst;
+    uint32_t* swizzleDst = (uint32_t*) dst;
+    size_t decodeDstRowBytes = rowBytes;
+    size_t swizzleDstRowBytes = rowBytes;
+    int dstWidth = opts.fSubset ? opts.fSubset->width() : dstInfo.width();
+    if (fSwizzleSrcRow && fColorXformSrcRow) {
+        decodeDst = fSwizzleSrcRow;
+        swizzleDst = fColorXformSrcRow;
+        decodeDstRowBytes = 0;
+        swizzleDstRowBytes = 0;
+        dstWidth = fSwizzler->swizzleWidth();
+    } else if (fColorXformSrcRow) {
+        decodeDst = (uint8_t*) fColorXformSrcRow;
+        swizzleDst = fColorXformSrcRow;
+        decodeDstRowBytes = 0;
+        swizzleDstRowBytes = 0;
+    } else if (fSwizzleSrcRow) {
+        decodeDst = fSwizzleSrcRow;
+        decodeDstRowBytes = 0;
+        dstWidth = fSwizzler->swizzleWidth();
+    }
+
+    for (int y = 0; y < count; y++) {
+        if (!fHeifDecoder->getScanline(decodeDst)) {
+            return y;
+        }
+
+        if (fSwizzler) {
+            fSwizzler->swizzle(swizzleDst, decodeDst);
+        }
+
+        if (this->colorXform()) {
+            this->applyColorXform(dst, swizzleDst, dstWidth, kOpaque_SkAlphaType);
+            dst = SkTAddOffset<void>(dst, rowBytes);
+        }
+
+        decodeDst = SkTAddOffset<uint8_t>(decodeDst, decodeDstRowBytes);
+        swizzleDst = SkTAddOffset<uint32_t>(swizzleDst, swizzleDstRowBytes);
+    }
+
+    return count;
+}
+
+/*
+ * Performs the heif decode
+ */
+SkCodec::Result SkHeifCodec::onGetPixels(const SkImageInfo& dstInfo,
+                                         void* dst, size_t dstRowBytes,
+                                         const Options& options,
+                                         int* rowsDecoded) {
+    if (options.fSubset) {
+        // Not supporting subsets on this path for now.
+        // TODO: if the heif has tiles, we can support subset here, but
+        // need to retrieve tile config from metadata retriever first.
+        return kUnimplemented;
+    }
+
+    if (!this->initializeColorXform(dstInfo, options.fPremulBehavior)) {
+        return kInvalidConversion;
+    }
+
+    // Check if we can decode to the requested destination and set the output color space
+    if (!this->setOutputColorFormat(dstInfo)) {
+        return kInvalidConversion;
+    }
+
+    if (!fHeifDecoder->decode(&fFrameInfo)) {
+        return kInvalidInput;
+    }
+
+    fSwizzler.reset(nullptr);
+    this->allocateStorage(dstInfo);
+
+    int rows = this->readRows(dstInfo, dst, dstRowBytes, dstInfo.height(), options);
+    if (rows < dstInfo.height()) {
+        *rowsDecoded = rows;
+        return kIncompleteInput;
+    }
+
+    return kSuccess;
+}
+
+void SkHeifCodec::allocateStorage(const SkImageInfo& dstInfo) {
+    int dstWidth = dstInfo.width();
+
+    size_t swizzleBytes = 0;
+    if (fSwizzler) {
+        swizzleBytes = fFrameInfo.mBytesPerPixel * fFrameInfo.mWidth;
+        dstWidth = fSwizzler->swizzleWidth();
+        SkASSERT(!this->colorXform() || SkIsAlign4(swizzleBytes));
+    }
+
+    size_t xformBytes = 0;
+    if (this->colorXform() && (kRGBA_F16_SkColorType == dstInfo.colorType() ||
+                               kRGB_565_SkColorType == dstInfo.colorType())) {
+        xformBytes = dstWidth * sizeof(uint32_t);
+    }
+
+    size_t totalBytes = swizzleBytes + xformBytes;
+    fStorage.reset(totalBytes);
+    if (totalBytes > 0) {
+        fSwizzleSrcRow = (swizzleBytes > 0) ? fStorage.get() : nullptr;
+        fColorXformSrcRow = (xformBytes > 0) ?
+                SkTAddOffset<uint32_t>(fStorage.get(), swizzleBytes) : nullptr;
+    }
+}
+
+void SkHeifCodec::initializeSwizzler(
+        const SkImageInfo& dstInfo, const Options& options) {
+    SkEncodedInfo swizzlerInfo = this->getEncodedInfo();
+
+    SkImageInfo swizzlerDstInfo = dstInfo;
+    if (this->colorXform()) {
+        // The color xform will be expecting RGBA 8888 input.
+        swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_8888_SkColorType);
+    }
+
+    fSwizzler.reset(SkSwizzler::CreateSwizzler(swizzlerInfo, nullptr,
+            swizzlerDstInfo, options, nullptr, true));
+    SkASSERT(fSwizzler);
+}
+
+SkSampler* SkHeifCodec::getSampler(bool createIfNecessary) {
+    if (!createIfNecessary || fSwizzler) {
+        SkASSERT(!fSwizzler || (fSwizzleSrcRow && fStorage.get() == fSwizzleSrcRow));
+        return fSwizzler.get();
+    }
+
+    this->initializeSwizzler(this->dstInfo(), this->options());
+    this->allocateStorage(this->dstInfo());
+    return fSwizzler.get();
+}
+
+SkCodec::Result SkHeifCodec::onStartScanlineDecode(
+        const SkImageInfo& dstInfo, const Options& options) {
+    if (!this->initializeColorXform(dstInfo, options.fPremulBehavior)) {
+        return kInvalidConversion;
+    }
+
+    // Check if we can decode to the requested destination and set the output color space
+    if (!this->setOutputColorFormat(dstInfo)) {
+        return kInvalidConversion;
+    }
+
+    // TODO: For now, just decode the whole thing even when there is a subset.
+    // If the heif image has tiles, we could potentially do this much faster,
+    // but the tile configuration needs to be retrieved from the metadata.
+    if (!fHeifDecoder->decode(&fFrameInfo)) {
+        return kInvalidInput;
+    }
+
+    if (options.fSubset) {
+        this->initializeSwizzler(dstInfo, options);
+    } else {
+        fSwizzler.reset(nullptr);
+    }
+
+    this->allocateStorage(dstInfo);
+
+    return kSuccess;
+}
+
+int SkHeifCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {
+    return this->readRows(this->dstInfo(), dst, dstRowBytes, count, this->options());
+}
+
+bool SkHeifCodec::onSkipScanlines(int count) {
+    return count == (int) fHeifDecoder->skipScanlines(count);
+}
+
+#endif // SK_HAS_HEIF_LIBRARY
diff --git a/src/codec/SkHeifCodec.h b/src/codec/SkHeifCodec.h
new file mode 100644
index 0000000..e07e402
--- /dev/null
+++ b/src/codec/SkHeifCodec.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkHeifCodec_DEFINED
+#define SkHeifCodec_DEFINED
+
+#include "SkCodec.h"
+#include "SkColorSpace.h"
+#include "SkColorSpaceXform.h"
+#include "SkImageInfo.h"
+#include "SkSwizzler.h"
+#include "SkStream.h"
+#include "HeifDecoderAPI.h"
+
+class SkHeifCodec : public SkCodec {
+public:
+    static bool IsHeif(const void*, size_t);
+
+    /*
+     * Assumes IsHeif was called and returned true.
+     * Takes ownership of the stream.
+     */
+    static SkCodec* NewFromStream(SkStream*, Result*);
+
+protected:
+
+    Result onGetPixels(
+            const SkImageInfo& dstInfo,
+            void* dst, size_t dstRowBytes,
+            const Options& options,
+            int* rowsDecoded) override;
+
+    SkEncodedImageFormat onGetEncodedFormat() const override {
+        return SkEncodedImageFormat::kHEIF;
+    }
+
+private:
+    /*
+     * Creates an instance of the decoder
+     * Called only by NewFromStream
+     */
+    SkHeifCodec(int width, int height, const SkEncodedInfo&,
+            HeifDecoder*, sk_sp<SkColorSpace>, Origin);
+
+    /*
+     * Checks if the conversion between the input image and the requested output
+     * image has been implemented.
+     *
+     * Sets the output color format.
+     */
+    bool setOutputColorFormat(const SkImageInfo& dst);
+
+    void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options);
+    void allocateStorage(const SkImageInfo& dstInfo);
+    int readRows(const SkImageInfo& dstInfo, void* dst,
+            size_t rowBytes, int count, const Options&);
+
+    /*
+     * Scanline decoding.
+     */
+    SkSampler* getSampler(bool createIfNecessary) override;
+    Result onStartScanlineDecode(const SkImageInfo& dstInfo,
+            const Options& options) override;
+    int onGetScanlines(void* dst, int count, size_t rowBytes) override;
+    bool onSkipScanlines(int count) override;
+
+    std::unique_ptr<HeifDecoder>       fHeifDecoder;
+    HeifFrameInfo                      fFrameInfo;
+    SkAutoTMalloc<uint8_t>             fStorage;
+    uint8_t*                           fSwizzleSrcRow;
+    uint32_t*                          fColorXformSrcRow;
+
+    std::unique_ptr<SkSwizzler>        fSwizzler;
+
+    typedef SkCodec INHERITED;
+};
+
+#endif // SkHeifCodec_DEFINED
diff --git a/src/codec/SkWebpCodec.h b/src/codec/SkWebpCodec.h
index e06d097..3669a02 100644
--- a/src/codec/SkWebpCodec.h
+++ b/src/codec/SkWebpCodec.h
@@ -23,8 +23,6 @@
     void WebPDemuxDelete(WebPDemuxer* dmux);
 }
 
-static const size_t WEBP_VP8_HEADER_SIZE = 30;
-
 class SkWebpCodec final : public SkCodec {
 public:
     // Assumes IsWebp was called and returned true.
diff --git a/src/images/SkImageEncoder.cpp b/src/images/SkImageEncoder.cpp
index d79741d..52ad032 100644
--- a/src/images/SkImageEncoder.cpp
+++ b/src/images/SkImageEncoder.cpp
@@ -40,6 +40,7 @@
             case SkEncodedImageFormat::kJPEG: {
                 SkJpegEncoder::Options opts;
                 opts.fQuality = quality;
+                opts.fBlendBehavior = SkTransferFunctionBehavior::kIgnore;
                 return SkJpegEncoder::Encode(dst, src, opts);
             }
             case SkEncodedImageFormat::kPNG: {
diff --git a/tests/CodecTest.cpp b/tests/CodecTest.cpp
index ac4afd1..c2043b5 100644
--- a/tests/CodecTest.cpp
+++ b/tests/CodecTest.cpp
@@ -924,8 +924,8 @@
 // wbmp images have a header that can be arbitrarily large, depending on the
 // size of the image. We cap the size at 65535, meaning we only need to look at
 // 8 bytes to determine whether we can read the image. This is important
-// because SkCodec only passes 14 bytes to SkWbmpCodec to determine whether the
-// image is a wbmp.
+// because SkCodec only passes a limited number of bytes to SkWbmpCodec to
+// determine whether the image is a wbmp.
 DEF_TEST(Codec_wbmp_max_size, r) {
     const unsigned char maxSizeWbmp[] = { 0x00, 0x00,           // Header
                                           0x83, 0xFF, 0x7F,     // W: 65535
@@ -1289,7 +1289,7 @@
             "randPixels.bmp",
             };
     for (auto file : files) {
-        SkStream* stream = LimitedRewindingStream::Make(file, 14);
+        SkStream* stream = LimitedRewindingStream::Make(file, SkCodec::MinBufferedBytesNeeded());
         if (!stream) {
             SkDebugf("Missing resources (%s). Set --resourcePath.\n", file);
             return;