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