SkJpegCodec: Add SkJpegSourceMgr

The current skjpeg_source_mgr (in SkJpegUtility.cpp/h) is effectively
two implementations (buffered vs in-memory) controlled via if
statements.

Turn these into two implementations of a pure virtual interface
SkJpegSourceMgr. The reason for this it that there will be a third
interface added for buffered non-seekable sources (which will
run an SkJpegSegmentScanner on the data as it is read).

Leave the pre-existing skjpeg_source_mgr in place because it may
be used in other places. It will be removed separately.

Bug: skia:14031
Change-Id: Iae87c0da307dfec50c95168c50486c807d335526
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/633461
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Christopher Cameron <ccameron@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 9753b60..1efe409 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1208,6 +1208,7 @@
   sources = [
     "src/codec/SkJpegCodec.cpp",
     "src/codec/SkJpegDecoderMgr.cpp",
+    "src/codec/SkJpegSourceMgr.cpp",
     "src/codec/SkJpegUtility.cpp",
   ]
   if (skia_use_jpeg_gainmaps) {
diff --git a/src/codec/BUILD.bazel b/src/codec/BUILD.bazel
index ee193ca..b623280 100644
--- a/src/codec/BUILD.bazel
+++ b/src/codec/BUILD.bazel
@@ -86,6 +86,8 @@
     "SkJpegCodec.h",
     "SkJpegDecoderMgr.cpp",
     "SkJpegDecoderMgr.h",
+    "SkJpegSourceMgr.cpp",
+    "SkJpegSourceMgr.h",
     "SkJpegUtility.cpp",
     "SkJpegUtility.h",
     "SkParseEncodedOrigin.cpp",
diff --git a/src/codec/SkJpegDecoderMgr.cpp b/src/codec/SkJpegDecoderMgr.cpp
index 0567e66..5479c16 100644
--- a/src/codec/SkJpegDecoderMgr.cpp
+++ b/src/codec/SkJpegDecoderMgr.cpp
@@ -7,12 +7,16 @@
 #include "src/codec/SkJpegDecoderMgr.h"
 
 #include "src/codec/SkCodecPriv.h"
+#include "src/codec/SkJpegSourceMgr.h"
 #include "src/codec/SkJpegUtility.h"
 
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
     #include "include/android/SkAndroidFrameworkUtils.h"
 #endif
 
+#include <cstddef>
+#include <utility>
+
 class SkStream;
 
 /*
@@ -42,6 +46,9 @@
   }
 }
 
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// JpegDecoderMgr
+
 bool JpegDecoderMgr::returnFalse(const char caller[]) {
     print_message((j_common_ptr) &fDInfo, caller);
     return false;
@@ -77,10 +84,12 @@
     }
 }
 
+SkJpegSourceMgr* JpegDecoderMgr::getSourceMgr() {
+    return fSrcMgr.fSourceMgr.get();
+}
+
 JpegDecoderMgr::JpegDecoderMgr(SkStream* stream)
-    : fSrcMgr(stream)
-    , fInit(false)
-{
+        : fSrcMgr(SkJpegSourceMgr::Make(stream)), fInit(false) {
     // Error manager must be set before any calls to libjeg in order to handle failures
     fDInfo.err = jpeg_std_error(&fErrorMgr);
     fErrorMgr.error_exit = skjpeg_err_exit;
@@ -100,3 +109,48 @@
         jpeg_destroy_decompress(&fDInfo);
     }
 }
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// JpegDecoderMgr::SourceMgr
+
+// static
+void JpegDecoderMgr::SourceMgr::InitSource(j_decompress_ptr dinfo) {
+    JpegDecoderMgr::SourceMgr* src = (JpegDecoderMgr::SourceMgr*)dinfo->src;
+    src->fSourceMgr->initSource(src->next_input_byte, src->bytes_in_buffer);
+}
+
+// static
+void JpegDecoderMgr::SourceMgr::SkipInputData(j_decompress_ptr dinfo, long num_bytes_long) {
+    JpegDecoderMgr::SourceMgr* src = (JpegDecoderMgr::SourceMgr*)dinfo->src;
+    size_t num_bytes = static_cast<size_t>(num_bytes_long);
+    if (!src->fSourceMgr->skipInputBytes(num_bytes, src->next_input_byte, src->bytes_in_buffer)) {
+        SkCodecPrintf("Failure to skip.\n");
+        src->next_input_byte = nullptr;
+        src->bytes_in_buffer = 0;
+        dinfo->err->error_exit((j_common_ptr)dinfo);
+    }
+}
+
+// static
+boolean JpegDecoderMgr::SourceMgr::FillInputBuffer(j_decompress_ptr dinfo) {
+    JpegDecoderMgr::SourceMgr* src = (JpegDecoderMgr::SourceMgr*)dinfo->src;
+    if (!src->fSourceMgr->fillInputBuffer(src->next_input_byte, src->bytes_in_buffer)) {
+        SkCodecPrintf("Failure to fill input buffer.\n");
+        src->next_input_byte = nullptr;
+        src->bytes_in_buffer = 0;
+        return false;
+    }
+    return true;
+}
+
+// static
+void JpegDecoderMgr::SourceMgr::TermSource(j_decompress_ptr dinfo) {}
+
+JpegDecoderMgr::SourceMgr::SourceMgr(std::unique_ptr<SkJpegSourceMgr> sourceMgr)
+        : fSourceMgr(std::move(sourceMgr)) {
+    init_source = JpegDecoderMgr::SourceMgr::InitSource;
+    fill_input_buffer = JpegDecoderMgr::SourceMgr::FillInputBuffer;
+    skip_input_data = JpegDecoderMgr::SourceMgr::SkipInputData;
+    resync_to_restart = jpeg_resync_to_restart;
+    term_source = JpegDecoderMgr::SourceMgr::TermSource;
+}
diff --git a/src/codec/SkJpegDecoderMgr.h b/src/codec/SkJpegDecoderMgr.h
index a2c9d2b..4d9f52d 100644
--- a/src/codec/SkJpegDecoderMgr.h
+++ b/src/codec/SkJpegDecoderMgr.h
@@ -12,12 +12,15 @@
 #include "include/private/SkEncodedInfo.h"
 #include "include/private/base/SkNoncopyable.h"
 #include "src/codec/SkJpegPriv.h"
-#include "src/codec/SkJpegUtility.h"
+#include "src/codec/SkJpegSourceMgr.h"
 
 extern "C" {
     #include "jpeglib.h"
+    #include "jmorecfg.h"
 }
 
+#include <memory>
+
 class SkStream;
 
 class JpegDecoderMgr : SkNoncopyable {
@@ -66,10 +69,23 @@
      */
     jpeg_decompress_struct* dinfo() { return &fDInfo; }
 
+    // Get the source manager.
+    SkJpegSourceMgr* getSourceMgr();
+
 private:
+    // Wrapper that calls into the full SkJpegSourceMgr interface.
+    struct SourceMgr : jpeg_source_mgr {
+        static void InitSource(j_decompress_ptr dinfo);
+        static void SkipInputData(j_decompress_ptr dinfo, long num_bytes_long);
+        static boolean FillInputBuffer(j_decompress_ptr dinfo);
+        static void TermSource(j_decompress_ptr dinfo);
+
+        SourceMgr(std::unique_ptr<SkJpegSourceMgr> mgr);
+        std::unique_ptr<SkJpegSourceMgr> fSourceMgr;
+    };
 
     jpeg_decompress_struct fDInfo;
-    skjpeg_source_mgr      fSrcMgr;
+    SourceMgr              fSrcMgr;
     skjpeg_error_mgr       fErrorMgr;
     jpeg_progress_mgr      fProgressMgr;
     bool                   fInit;
diff --git a/src/codec/SkJpegSourceMgr.cpp b/src/codec/SkJpegSourceMgr.cpp
new file mode 100644
index 0000000..6718978
--- /dev/null
+++ b/src/codec/SkJpegSourceMgr.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2023 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/codec/SkJpegSourceMgr.h"
+
+#include "include/core/SkData.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkStream.h"
+#include "src/codec/SkCodecPriv.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// SkJpegMemorySourceMgr
+
+class SkJpegMemorySourceMgr : public SkJpegSourceMgr {
+public:
+    SkJpegMemorySourceMgr(SkStream* stream) : fStream(stream) {}
+    ~SkJpegMemorySourceMgr() override {}
+
+    void initSource(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override {
+        nextInputByte = reinterpret_cast<const uint8_t*>(fStream->getMemoryBase());
+        bytesInBuffer = static_cast<size_t>(fStream->getLength());
+    }
+    bool fillInputBuffer(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override {
+        // The whole JPEG data is expected to reside in the supplied memory buffer, so any request
+        // for more data beyond the given buffer size is treated as an error.
+        SkCodecPrintf("Asked to re-fill a memory-mapped stream.\n");
+        return false;
+    }
+    bool skipInputBytes(size_t bytesToSkip,
+                        const uint8_t*& nextInputByte,
+                        size_t& bytesInBuffer) override {
+        if (bytesToSkip > bytesInBuffer) {
+            SkCodecPrintf("Asked to read past end of a memory-mapped stream.\n");
+            return false;
+        }
+        nextInputByte += bytesToSkip;
+        bytesInBuffer -= bytesToSkip;
+        return true;
+    }
+
+private:
+    SkStream* const fStream;  // unowned.
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// SkJpegBufferedSourceMgr
+
+class SkJpegBufferedSourceMgr : public SkJpegSourceMgr {
+public:
+    SkJpegBufferedSourceMgr(SkStream* stream) : fStream(stream) {}
+    ~SkJpegBufferedSourceMgr() override {}
+
+    void initSource(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override {
+        constexpr size_t kBufferSize = 1024;
+        fBuffer = SkData::MakeUninitialized(kBufferSize);
+        nextInputByte = fBuffer->bytes();
+        bytesInBuffer = 0;
+    }
+    bool fillInputBuffer(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override {
+        size_t bytesRead = fStream->read(fBuffer->writable_data(), fBuffer->size());
+        if (bytesRead == 0) {
+            // Fail if we read zero bytes (libjpeg will accept any non-zero number of bytes).
+            SkCodecPrintf("Hit end of file reading a buffered stream.\n");
+            return false;
+        }
+        nextInputByte = fBuffer->bytes();
+        bytesInBuffer = bytesRead;
+        return true;
+    }
+    bool skipInputBytes(size_t bytesToSkip,
+                        const uint8_t*& nextInputByte,
+                        size_t& bytesInBuffer) override {
+        // Skip through the already-read (or already in memory) buffer.
+        if (bytesToSkip <= bytesInBuffer) {
+            nextInputByte += bytesToSkip;
+            bytesInBuffer -= bytesToSkip;
+            return true;
+        }
+        bytesToSkip -= bytesInBuffer;
+
+        // Fail if we skip past the end of the stream.
+        if (fStream->skip(bytesToSkip) != bytesToSkip) {
+            SkCodecPrintf("Failed to skip through buffered stream.\n");
+            return false;
+        }
+
+        bytesInBuffer = 0;
+        nextInputByte = fBuffer->bytes();
+        return true;
+    }
+
+private:
+    SkStream* const fStream;  // unowned.
+    sk_sp<SkData> fBuffer;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// SkJpegSourceMgr
+
+// static
+std::unique_ptr<SkJpegSourceMgr> SkJpegSourceMgr::Make(SkStream* stream) {
+    if (stream->hasLength() && stream->getMemoryBase()) {
+        return std::make_unique<SkJpegMemorySourceMgr>(stream);
+    }
+    return std::make_unique<SkJpegBufferedSourceMgr>(stream);
+}
diff --git a/src/codec/SkJpegSourceMgr.h b/src/codec/SkJpegSourceMgr.h
new file mode 100644
index 0000000..380b023
--- /dev/null
+++ b/src/codec/SkJpegSourceMgr.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkJpegSourceMgr_codec_DEFINED
+#define SkJpegSourceMgr_codec_DEFINED
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+
+class SkStream;
+
+/*
+ * Interface to adapt an SkStream to the jpeg_source_mgr interface. This interface has different
+ * implementations for SkStreams with different capabilities.
+ */
+class SkJpegSourceMgr {
+public:
+    static std::unique_ptr<SkJpegSourceMgr> Make(SkStream* stream);
+    virtual ~SkJpegSourceMgr() {}
+
+    // Interface called by libjpeg via its jpeg_source_mgr interface.
+    virtual void initSource(const uint8_t*& nextInputByte, size_t& bytesInBuffer) = 0;
+    virtual bool fillInputBuffer(const uint8_t*& nextInputByte, size_t& bytesInBuffer) = 0;
+    virtual bool skipInputBytes(size_t bytes,
+                                const uint8_t*& nextInputByte,
+                                size_t& bytesInBuffer) = 0;
+};
+
+#endif