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