jpegr codec: Implement scanline decoding and sampling

Add functionality needed for scanline decoding, region decoding and
sampling.

Test: atest BitmapFactoryTest#testDecodeJpegr BitmapRegionDecoderTest#testJpegr
Change-Id: I1557d9ee4a45802700b241f05fddaf50168512cb
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/631162
Commit-Queue: Fyodor Kyslov <kyslov@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/client_utils/android/BitmapRegionDecoder.cpp b/client_utils/android/BitmapRegionDecoder.cpp
index c5262b8..067a76f 100644
--- a/client_utils/android/BitmapRegionDecoder.cpp
+++ b/client_utils/android/BitmapRegionDecoder.cpp
@@ -23,6 +23,9 @@
 
     switch (codec->getEncodedFormat()) {
         case SkEncodedImageFormat::kJPEG:
+#ifdef SK_CODEC_DECODES_JPEGR
+        case SkEncodedImageFormat::kJPEGR:
+#endif
         case SkEncodedImageFormat::kPNG:
         case SkEncodedImageFormat::kWEBP:
         case SkEncodedImageFormat::kHEIF:
diff --git a/src/codec/SkAndroidCodec.cpp b/src/codec/SkAndroidCodec.cpp
index 11eceaa..dd4d582 100644
--- a/src/codec/SkAndroidCodec.cpp
+++ b/src/codec/SkAndroidCodec.cpp
@@ -107,6 +107,9 @@
 #ifndef SK_CODEC_DECODES_AVIF
         case SkEncodedImageFormat::kAVIF:
 #endif
+#ifdef SK_CODEC_DECODES_JPEGR
+        case SkEncodedImageFormat::kJPEGR:
+#endif
             return std::make_unique<SkSampledCodec>(codec.release());
 #ifdef SK_HAS_WUFFS_LIBRARY
         case SkEncodedImageFormat::kGIF:
@@ -120,9 +123,6 @@
 #ifdef SK_CODEC_DECODES_AVIF
         case SkEncodedImageFormat::kAVIF:
 #endif
-#ifdef SK_CODEC_DECODES_JPEGR
-        case SkEncodedImageFormat::kJPEGR:
-#endif
 #if defined(SK_CODEC_DECODES_WEBP) || defined(SK_CODEC_DECODES_RAW) || \
         defined(SK_HAS_WUFFS_LIBRARY) || defined(SK_CODEC_DECODES_AVIF)
             return std::make_unique<SkAndroidCodecAdapter>(codec.release());
diff --git a/src/codec/SkJpegRCodec.cpp b/src/codec/SkJpegRCodec.cpp
index 5532f5b..96bdc46 100644
--- a/src/codec/SkJpegRCodec.cpp
+++ b/src/codec/SkJpegRCodec.cpp
@@ -24,22 +24,22 @@
 #include "include/core/SkStream.h"
 #include "include/core/SkTypes.h"
 #include "include/core/SkYUVAInfo.h"
-#include "include/private/base/SkTemplates.h"
 #include "include/private/base/SkMalloc.h"
+#include "include/private/base/SkTemplates.h"
 #include "include/private/base/SkTo.h"
 #include "modules/skcms/skcms.h"
 #include "src/codec/SkCodecPriv.h"
 #include "src/codec/SkParseEncodedOrigin.h"
+#include "src/codec/SkSwizzler.h"
 #include "src/core/SkStreamPriv.h"
 
-static SkEncodedOrigin get_orientation(const std::vector<uint8_t> &exifData) {
+static SkEncodedOrigin get_orientation(const std::vector<uint8_t>& exifData) {
     SkEncodedOrigin orientation = kDefault_SkEncodedOrigin;
     if (exifData.size() > 6) {
         constexpr size_t kOffset = 6;
         const size_t exifSize = exifData.size();
-        const uint8_t *data = exifData.data();
-        SkParseEncodedOrigin(data + kOffset, exifSize - kOffset,
-                &orientation);
+        const uint8_t* data = exifData.data();
+        SkParseEncodedOrigin(data + kOffset, exifSize - kOffset, &orientation);
     }
     return orientation;
 }
@@ -135,6 +135,7 @@
 
         // JPEGR always report 10-bit color depth
         const uint8_t colorDepth = 10;
+        const int bitsPerComponent = 8;
 
         // iccSkData will outlive iccData as it is passed to profile, hence we need a copy
         sk_sp<SkData> iccSkData = SkData::MakeWithCopy(iccData.data(), iccData.size());
@@ -145,8 +146,9 @@
                                                  jpegRInfo.height,
                                                  SkEncodedInfo::kRGBA_Color,
                                                  SkEncodedInfo::kOpaque_Alpha,
-                                                 colorDepth,
-                                                 std::move(profile));
+                                                 bitsPerComponent,
+                                                 std::move(profile),
+                                                 colorDepth);
 
         SkEncodedOrigin orientation = get_orientation(exifData);
 
@@ -216,6 +218,128 @@
     }
 }
 
+void SkJpegRCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options) {
+    const int srcBPP = 4;
+
+    fSwizzler = SkSwizzler::MakeSimple(srcBPP, dstInfo, options);
+    SkASSERT(fSwizzler);
+}
+
+void SkJpegRCodec::allocateStorage(const SkImageInfo& dstInfo) {
+    int dstWidth = dstInfo.width();
+
+    size_t swizzleBytes = 0;
+    const SkEncodedInfo& encodedInfo = this->getEncodedInfo();
+    if (fSwizzler) {
+        const int srcBPP = encodedInfo.bitsPerPixel() / 8;
+        swizzleBytes = srcBPP * encodedInfo.width();
+        dstWidth = fSwizzler->swizzleWidth();
+        SkASSERT(!this->colorXform() || SkIsAlign4(swizzleBytes));
+    }
+
+    size_t totalBytes = swizzleBytes;
+    fStorage.reset(totalBytes);
+    if (totalBytes > 0) {
+        fSwizzleSrcRow = (swizzleBytes > 0) ? fStorage.get() : nullptr;
+    }
+}
+
+SkSampler* SkJpegRCodec::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 SkJpegRCodec::decodeImage(const SkImageInfo& dstInfo, void* dst) {
+    const SkColorType dstColorType = dstInfo.colorType();
+    if (dstColorType != kRGBA_1010102_SkColorType && dstColorType != kRGBA_8888_SkColorType) {
+        // We only support RGBA1010102 and RGBA8888 colors
+        return kUnimplemented;
+    }
+
+    const bool decodeSDR = (dstColorType == kRGBA_8888_SkColorType);
+    jpegr_compressed_struct compressedImage;
+    jpegr_uncompressed_struct decompressedImage;
+    compressedImage.data = (void*)fData->data();
+    compressedImage.length = fData->size();
+
+    // Decode to intermediate buffer
+    if (dst == nullptr) {
+        const SkEncodedInfo& encodedInfo = this->getEncodedInfo();
+        fDecodedImage.reset(encodedInfo.width() * encodedInfo.height() *
+                            encodedInfo.bitsPerPixel() / 8);
+        decompressedImage.data = fDecodedImage.get();
+    } else {
+        decompressedImage.data = dst;
+    }
+
+    if (fRecoveryMap->decodeJPEGR(&compressedImage, &decompressedImage, nullptr, decodeSDR) != 0) {
+        return kInternalError;
+    }
+    return kSuccess;
+}
+
+SkCodec::Result SkJpegRCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
+                                                    const Options& options) {
+    // We need to decode the whole image.
+    // Decode it and put to a temporary storage
+    SkCodec::Result result = kSuccess;
+    if ((result = this->decodeImage(dstInfo, nullptr)) != kSuccess) {
+        return result;
+    }
+
+    if (options.fSubset) {
+        this->initializeSwizzler(dstInfo, options);
+    } else {
+        fSwizzler.reset(nullptr);
+    }
+
+    this->allocateStorage(dstInfo);
+
+    return kSuccess;
+}
+
+int SkJpegRCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {
+    uint8_t* decodeDst = (uint8_t*)dst;
+    uint32_t* swizzleDst = (uint32_t*)dst;
+    size_t decodeDstRowBytes = dstRowBytes;
+    size_t swizzleDstRowBytes = dstRowBytes;
+    int dstWidth =
+            this->options().fSubset ? this->options().fSubset->width() : this->dstInfo().width();
+    if (fDecodedImage.get() == nullptr) {
+        return 0;
+    }
+    if (fSwizzleSrcRow) {
+        decodeDst = fSwizzleSrcRow;
+        decodeDstRowBytes = 0;
+        dstWidth = fSwizzler->swizzleWidth();
+    }
+    const int currentLine = this->nextScanline();
+    const int decodeWidth = this->dstInfo().width();
+    const int bpp = this->getEncodedInfo().bitsPerPixel() / 8;
+    const int decodeStride = decodeWidth * bpp;
+
+    for (int y = 0; y < count; y++) {
+        memcpy(decodeDst, fDecodedImage.get() + decodeStride * (y + currentLine), decodeStride);
+
+        if (fSwizzler) {
+            fSwizzler->swizzle(swizzleDst, decodeDst);
+        }
+
+        decodeDst = SkTAddOffset<uint8_t>(decodeDst, decodeDstRowBytes);
+        swizzleDst = SkTAddOffset<uint32_t>(swizzleDst, swizzleDstRowBytes);
+    }
+
+    return count;
+}
+
+bool SkJpegRCodec::onSkipScanlines(int count) { return true; }
+
 /*
  * Performs the JpegR decode
  */
@@ -227,12 +351,6 @@
     if (options.fSubset) {
         return kUnimplemented;
     }
-    const SkColorType dstColorType = dstInfo.colorType();
-    if (dstColorType != kRGBA_1010102_SkColorType && dstColorType != kRGBA_8888_SkColorType) {
-        // We only support RGBA1010102 and RGBA8888 colors
-        return kUnimplemented;
-    }
-
     if (this->dimensions() != dstInfo.dimensions()) {
         // No Scaling
         return kUnimplemented;
@@ -243,16 +361,9 @@
         return kUnimplemented;
     }
 
-    const bool decodeSDR = (dstColorType == kRGBA_8888_SkColorType);
-
-    jpegr_compressed_struct compressedImage;
-    jpegr_uncompressed_struct decompressedImage;
-    compressedImage.data = (void*)fData->data();
-    compressedImage.length = fData->size();
-
-    decompressedImage.data = dst;
-    if (fRecoveryMap->decodeJPEGR(&compressedImage, &decompressedImage, nullptr, decodeSDR) != 0) {
-        return kInternalError;
+    SkCodec::Result result = kSuccess;
+    if ((result = this->decodeImage(dstInfo, dst)) != kSuccess) {
+        return result;
     }
 
     *rowsDecoded = dstInfo.height();
diff --git a/src/codec/SkJpegRCodec.h b/src/codec/SkJpegRCodec.h
index a3ab47a..bc36cee 100644
--- a/src/codec/SkJpegRCodec.h
+++ b/src/codec/SkJpegRCodec.h
@@ -32,6 +32,7 @@
 #include <memory>
 
 class SkStream;
+class SkSwizzler;
 struct SkImageInfo;
 
 /*
@@ -109,9 +110,29 @@
                  SkEncodedOrigin origin,
                  sk_sp<SkData> data);
 
+    void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options);
+    void allocateStorage(const SkImageInfo& dstInfo);
+
+    // If "dst" is nullptr decoded image will be placed to fDecodedImage
+    Result decodeImage(const SkImageInfo& dstInfo, void* dst);
+
+    /*
+     * 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<RecoveryMap> fRecoveryMap;
     sk_sp<SkData> fData;
 
+    skia_private::AutoTMalloc<uint8_t> fStorage;
+    skia_private::AutoTMalloc<uint8_t> fDecodedImage;
+    uint8_t* fSwizzleSrcRow = nullptr;
+
+    std::unique_ptr<SkSwizzler> fSwizzler;
+
 #endif  // SK_CODEC_DECODES_JPEGR
 };