summaryrefslogtreecommitdiffstats
path: root/third_party/jpeg-xl/lib/jxl/encode_internal.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/jpeg-xl/lib/jxl/encode_internal.h
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/jpeg-xl/lib/jxl/encode_internal.h')
-rw-r--r--third_party/jpeg-xl/lib/jxl/encode_internal.h669
1 files changed, 669 insertions, 0 deletions
diff --git a/third_party/jpeg-xl/lib/jxl/encode_internal.h b/third_party/jpeg-xl/lib/jxl/encode_internal.h
new file mode 100644
index 0000000000..e89993f253
--- /dev/null
+++ b/third_party/jpeg-xl/lib/jxl/encode_internal.h
@@ -0,0 +1,669 @@
+/* Copyright (c) the JPEG XL Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#ifndef LIB_JXL_ENCODE_INTERNAL_H_
+#define LIB_JXL_ENCODE_INTERNAL_H_
+
+#include <jxl/cms_interface.h>
+#include <jxl/codestream_header.h>
+#include <jxl/encode.h>
+#include <jxl/memory_manager.h>
+#include <jxl/types.h>
+
+#include <algorithm>
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "lib/jxl/base/c_callback_support.h"
+#include "lib/jxl/base/common.h"
+#include "lib/jxl/base/compiler_specific.h"
+#include "lib/jxl/base/data_parallel.h"
+#include "lib/jxl/base/status.h"
+#include "lib/jxl/enc_aux_out.h"
+#include "lib/jxl/enc_fast_lossless.h"
+#include "lib/jxl/enc_params.h"
+#include "lib/jxl/image_metadata.h"
+#include "lib/jxl/jpeg/jpeg_data.h"
+#include "lib/jxl/memory_manager_internal.h"
+#include "lib/jxl/padded_bytes.h"
+
+namespace jxl {
+
+/* Frame index box 'jxli' will start with Varint() for
+NF: has type Varint(): number of frames listed in the index.
+TNUM: has type u32: numerator of tick unit.
+TDEN: has type u32: denominator of tick unit. Value 0 means the file is
+ill-formed. per frame i listed: OFFi: has type Varint(): offset of start byte of
+this frame compared to start byte of previous frame from this index in the JPEG
+XL codestream. For the first frame, this is the offset from the first byte of
+the JPEG XL codestream. Ti: has type Varint(): duration in ticks between the
+start of this frame and the start of the next frame in the index. If this is the
+last frame in the index, this is the duration in ticks between the start of this
+frame and the end of the stream. A tick lasts TNUM / TDEN seconds. Fi: has type
+Varint(): amount of frames the next frame in the index occurs after this frame.
+If this is the last frame in the index, this is the amount of frames after this
+frame in the remainder of the stream. Only frames that are presented by the
+decoder are counted for this purpose, this excludes frames that are not intended
+for display but for compositing with other frames, such as frames that aren't
+the last frame with a duration of 0 ticks.
+
+All the frames listed in jxli are keyframes and the first frame is
+present in the list.
+There shall be either zero or one Frame Index boxes in a JPEG XL file.
+The offsets OFFi per frame are given as bytes in the codestream, not as
+bytes in the file format using the box structure. This means if JPEG XL Partial
+Codestream boxes are used, the offset is counted within the concatenated
+codestream, bytes from box headers or non-codestream boxes are not counted.
+*/
+
+typedef struct JxlEncoderFrameIndexBoxEntryStruct {
+ bool to_be_indexed;
+ uint32_t duration;
+ uint64_t OFFi;
+} JxlEncoderFrameIndexBoxEntry;
+
+typedef struct JxlEncoderFrameIndexBoxStruct {
+ // We always need to record the first frame entry, so presence of the
+ // first entry alone is not an indication if it was requested to be
+ // stored.
+ bool index_box_requested_through_api = false;
+
+ int64_t NF() const { return entries.size(); }
+ bool StoreFrameIndexBox() {
+ for (auto e : entries) {
+ if (e.to_be_indexed) {
+ return true;
+ }
+ }
+ return false;
+ }
+ int32_t TNUM = 1;
+ int32_t TDEN = 1000;
+
+ std::vector<JxlEncoderFrameIndexBoxEntry> entries;
+
+ // That way we can ensure that every index box will have the first frame.
+ // If the API user decides to mark it as an indexed frame, we call
+ // the AddFrame again, this time with requested.
+ void AddFrame(uint64_t OFFi, uint32_t duration, bool to_be_indexed) {
+ // We call AddFrame to every frame.
+ // Recording the first frame is required by the standard.
+ // Knowing the last frame is required, since the last indexed frame
+ // needs to know how many frames until the end.
+ // To be able to tell how many frames there are between each index
+ // entry we just record every frame here.
+ if (entries.size() == 1) {
+ if (OFFi == entries[0].OFFi) {
+ // API use for the first frame, let's clear the already recorded first
+ // frame.
+ entries.clear();
+ }
+ }
+ JxlEncoderFrameIndexBoxEntry e;
+ e.to_be_indexed = to_be_indexed;
+ e.OFFi = OFFi;
+ e.duration = duration;
+ entries.push_back(e);
+ }
+} JxlEncoderFrameIndexBox;
+
+// The encoder options (such as quality, compression speed, ...) for a single
+// frame, but not encoder-wide options such as box-related options.
+typedef struct JxlEncoderFrameSettingsValuesStruct {
+ // lossless is a separate setting from cparams because it is a combination
+ // setting that overrides multiple settings inside of cparams.
+ bool lossless;
+ CompressParams cparams;
+ JxlFrameHeader header;
+ std::vector<JxlBlendInfo> extra_channel_blend_info;
+ std::string frame_name;
+ JxlBitDepth image_bit_depth;
+ bool frame_index_box = false;
+ jxl::AuxOut* aux_out = nullptr;
+} JxlEncoderFrameSettingsValues;
+
+typedef std::array<uint8_t, 4> BoxType;
+
+// Utility function that makes a BoxType from a string literal. The string must
+// have 4 characters, a 5th null termination character is optional.
+constexpr BoxType MakeBoxType(const char* type) {
+ return BoxType(
+ {{static_cast<uint8_t>(type[0]), static_cast<uint8_t>(type[1]),
+ static_cast<uint8_t>(type[2]), static_cast<uint8_t>(type[3])}});
+}
+
+constexpr std::array<unsigned char, 32> kContainerHeader = {
+ 0, 0, 0, 0xc, 'J', 'X', 'L', ' ', 0xd, 0xa, 0x87,
+ 0xa, 0, 0, 0, 0x14, 'f', 't', 'y', 'p', 'j', 'x',
+ 'l', ' ', 0, 0, 0, 0, 'j', 'x', 'l', ' '};
+
+constexpr std::array<unsigned char, 8> kLevelBoxHeader = {0, 0, 0, 0x9,
+ 'j', 'x', 'l', 'l'};
+
+static JXL_INLINE size_t BitsPerChannel(JxlDataType data_type) {
+ switch (data_type) {
+ case JXL_TYPE_UINT8:
+ return 8;
+ case JXL_TYPE_UINT16:
+ return 16;
+ case JXL_TYPE_FLOAT:
+ return 32;
+ case JXL_TYPE_FLOAT16:
+ return 16;
+ default:
+ return 0; // signals unhandled JxlDataType
+ }
+}
+
+static JXL_INLINE size_t BytesPerPixel(JxlPixelFormat format) {
+ return format.num_channels * BitsPerChannel(format.data_type) / 8;
+}
+
+using ScopedInputBuffer =
+ std::unique_ptr<const void, std::function<void(const void*)>>;
+
+static JXL_INLINE ScopedInputBuffer
+GetColorBuffer(JxlChunkedFrameInputSource& input, size_t xpos, size_t ypos,
+ size_t xsize, size_t ysize, size_t* row_offset) {
+ return ScopedInputBuffer(
+ input.get_color_channel_data_at(input.opaque, xpos, ypos, xsize, ysize,
+ row_offset),
+ [&input](const void* p) { input.release_buffer(input.opaque, p); });
+}
+
+static JXL_INLINE ScopedInputBuffer GetExtraChannelBuffer(
+ JxlChunkedFrameInputSource& input, size_t ec_index, size_t xpos,
+ size_t ypos, size_t xsize, size_t ysize, size_t* row_offset) {
+ return ScopedInputBuffer(
+ input.get_extra_channel_data_at(input.opaque, ec_index, xpos, ypos, xsize,
+ ysize, row_offset),
+ [&input](const void* p) { input.release_buffer(input.opaque, p); });
+}
+
+// Common adapter for an existing frame input source or a whole-image input
+// buffer or a parsed JPEG file.
+class JxlEncoderChunkedFrameAdapter {
+ public:
+ JxlEncoderChunkedFrameAdapter(size_t xs, size_t ys, size_t num_extra_channels)
+ : xsize(xs), ysize(ys), channels_(1 + num_extra_channels) {}
+
+ void SetInputSource(JxlChunkedFrameInputSource input_source) {
+ input_source_ = input_source;
+ has_input_source_ = true;
+ }
+
+ bool SetFromBuffer(size_t channel, const uint8_t* buffer, size_t size,
+ JxlPixelFormat format) {
+ if (channel >= channels_.size()) return false;
+ if (!channels_[channel].SetFromBuffer(buffer, size, format, xsize, ysize)) {
+ return false;
+ }
+ if (channel > 0) channels_[channel].CopyBuffer();
+ return true;
+ }
+
+ // TODO(szabadka) Move instead of copy.
+ void SetJPEGData(const jpeg::JPEGData jpeg_data) {
+ jpeg_data_ = jpeg_data;
+ has_jpeg_data_ = true;
+ }
+ bool IsJPEG() const { return has_jpeg_data_; }
+
+ jpeg::JPEGData&& TakeJPEGData() {
+ JXL_ASSERT(has_jpeg_data_);
+ return std::move(jpeg_data_);
+ }
+
+ JxlChunkedFrameInputSource GetInputSource() {
+ if (has_input_source_) {
+ return input_source_;
+ }
+ return JxlChunkedFrameInputSource{
+ this,
+ METHOD_TO_C_CALLBACK(
+ &JxlEncoderChunkedFrameAdapter::GetColorChannelsPixelFormat),
+ METHOD_TO_C_CALLBACK(
+ &JxlEncoderChunkedFrameAdapter::GetColorChannelDataAt),
+ METHOD_TO_C_CALLBACK(
+ &JxlEncoderChunkedFrameAdapter::GetExtraChannelPixelFormat),
+ METHOD_TO_C_CALLBACK(
+ &JxlEncoderChunkedFrameAdapter::GetExtraChannelDataAt),
+ METHOD_TO_C_CALLBACK(
+ &JxlEncoderChunkedFrameAdapter::ReleaseCurrentData)};
+ }
+
+ bool CopyBuffers() {
+ if (has_input_source_) {
+ JxlPixelFormat format{4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
+ input_source_.get_color_channels_pixel_format(input_source_.opaque,
+ &format);
+ size_t row_offset;
+ {
+ auto buffer =
+ GetColorBuffer(input_source_, 0, 0, xsize, ysize, &row_offset);
+ if (!buffer) return false;
+ channels_[0].CopyFromBuffer(buffer.get(), format, xsize, ysize,
+ row_offset);
+ }
+ for (size_t ec = 0; ec + 1 < channels_.size(); ++ec) {
+ input_source_.get_extra_channel_pixel_format(input_source_.opaque, ec,
+ &format);
+ auto buffer = GetExtraChannelBuffer(input_source_, ec, 0, 0, xsize,
+ ysize, &row_offset);
+ if (!buffer) continue;
+ channels_[1 + ec].CopyFromBuffer(buffer.get(), format, xsize, ysize,
+ row_offset);
+ }
+ has_input_source_ = false;
+ } else {
+ channels_[0].CopyBuffer();
+ }
+ return true;
+ }
+
+ bool StreamingInput() const { return has_input_source_; }
+
+ const size_t xsize;
+ const size_t ysize;
+
+ private:
+ void GetColorChannelsPixelFormat(JxlPixelFormat* pixel_format) {
+ *pixel_format = channels_[0].format_;
+ }
+
+ const void* GetColorChannelDataAt(size_t xpos, size_t ypos, size_t xsize,
+ size_t ysize, size_t* row_offset) {
+ return channels_[0].GetDataAt(xpos, ypos, xsize, ysize, row_offset);
+ }
+
+ void GetExtraChannelPixelFormat(size_t ec_index,
+ JxlPixelFormat* pixel_format) {
+ JXL_ASSERT(1 + ec_index < channels_.size());
+ *pixel_format = channels_[1 + ec_index].format_;
+ }
+
+ const void* GetExtraChannelDataAt(size_t ec_index, size_t xpos, size_t ypos,
+ size_t xsize, size_t ysize,
+ size_t* row_offset) {
+ JXL_ASSERT(1 + ec_index < channels_.size());
+ return channels_[1 + ec_index].GetDataAt(xpos, ypos, xsize, ysize,
+ row_offset);
+ }
+
+ void ReleaseCurrentData(const void* buffer) {
+ // No dynamic memory is allocated in GetColorChannelDataAt or
+ // GetExtraChannelDataAt. Therefore, no cleanup is required here.
+ }
+
+ JxlChunkedFrameInputSource input_source_ = {};
+ bool has_input_source_ = false;
+ jpeg::JPEGData jpeg_data_;
+ bool has_jpeg_data_ = false;
+ struct Channel {
+ const uint8_t* buffer_ = nullptr;
+ size_t buffer_size_;
+ JxlPixelFormat format_;
+ size_t xsize_;
+ size_t ysize_;
+ size_t bytes_per_pixel_;
+ size_t stride_;
+ std::vector<uint8_t> copy_;
+
+ void SetFormatAndDimensions(JxlPixelFormat format, size_t xsize,
+ size_t ysize) {
+ format_ = format;
+ xsize_ = xsize;
+ ysize_ = ysize;
+ bytes_per_pixel_ = BytesPerPixel(format_);
+ const size_t last_row_size = xsize_ * bytes_per_pixel_;
+ const size_t align = format_.align;
+ stride_ = (align > 1 ? jxl::DivCeil(last_row_size, align) * align
+ : last_row_size);
+ }
+
+ bool SetFromBuffer(const uint8_t* buffer, size_t size,
+ JxlPixelFormat format, size_t xsize, size_t ysize) {
+ SetFormatAndDimensions(format, xsize, ysize);
+ buffer_ = buffer;
+ buffer_size_ = size;
+ const size_t min_buffer_size =
+ stride_ * (ysize_ - 1) + xsize_ * bytes_per_pixel_;
+ return min_buffer_size <= size;
+ }
+
+ void CopyFromBuffer(const void* buffer, JxlPixelFormat format, size_t xsize,
+ size_t ysize, size_t row_offset) {
+ SetFormatAndDimensions(format, xsize, ysize);
+ buffer_ = nullptr;
+ copy_.resize(ysize * stride_);
+ for (size_t y = 0; y < ysize; ++y) {
+ memcpy(copy_.data() + y * stride_,
+ reinterpret_cast<const uint8_t*>(buffer) + y * row_offset,
+ stride_);
+ }
+ }
+
+ void CopyBuffer() {
+ if (buffer_) {
+ copy_ = std::vector<uint8_t>(buffer_, buffer_ + buffer_size_);
+ buffer_ = nullptr;
+ }
+ }
+
+ const void* GetDataAt(size_t xpos, size_t ypos, size_t xsize, size_t ysize,
+ size_t* row_offset) const {
+ const uint8_t* buffer = copy_.empty() ? buffer_ : copy_.data();
+ JXL_ASSERT(ypos + ysize <= ysize_);
+ JXL_ASSERT(xpos + xsize <= xsize_);
+ JXL_ASSERT(buffer);
+ *row_offset = stride_;
+ return buffer + ypos * stride_ + xpos * bytes_per_pixel_;
+ }
+ };
+ std::vector<Channel> channels_;
+};
+
+struct JxlEncoderQueuedFrame {
+ JxlEncoderFrameSettingsValues option_values;
+ JxlEncoderChunkedFrameAdapter frame_data;
+ std::vector<uint8_t> ec_initialized;
+};
+
+struct JxlEncoderQueuedBox {
+ BoxType type;
+ std::vector<uint8_t> contents;
+ bool compress_box;
+};
+
+using FJXLFrameUniquePtr =
+ std::unique_ptr<JxlFastLosslessFrameState,
+ decltype(&JxlFastLosslessFreeFrameState)>;
+
+// Either a frame, or a box, not both.
+// Can also be a FJXL frame.
+struct JxlEncoderQueuedInput {
+ explicit JxlEncoderQueuedInput(const JxlMemoryManager& memory_manager)
+ : frame(nullptr, jxl::MemoryManagerDeleteHelper(&memory_manager)),
+ box(nullptr, jxl::MemoryManagerDeleteHelper(&memory_manager)) {}
+ MemoryManagerUniquePtr<JxlEncoderQueuedFrame> frame;
+ MemoryManagerUniquePtr<JxlEncoderQueuedBox> box;
+ FJXLFrameUniquePtr fast_lossless_frame = {nullptr,
+ JxlFastLosslessFreeFrameState};
+};
+
+static constexpr size_t kSmallBoxHeaderSize = 8;
+static constexpr size_t kLargeBoxHeaderSize = 16;
+static constexpr size_t kLargeBoxContentSizeThreshold =
+ 0x100000000ull - kSmallBoxHeaderSize;
+
+size_t WriteBoxHeader(const jxl::BoxType& type, size_t size, bool unbounded,
+ bool force_large_box, uint8_t* output);
+
+// Appends a JXL container box header with given type, size, and unbounded
+// properties to output.
+template <typename T>
+void AppendBoxHeader(const jxl::BoxType& type, size_t size, bool unbounded,
+ T* output) {
+ size_t current_size = output->size();
+ output->resize(current_size + kLargeBoxHeaderSize);
+ size_t header_size =
+ WriteBoxHeader(type, size, unbounded, /*force_large_box=*/false,
+ output->data() + current_size);
+ output->resize(current_size + header_size);
+}
+
+} // namespace jxl
+
+class JxlOutputProcessorBuffer;
+
+class JxlEncoderOutputProcessorWrapper {
+ friend class JxlOutputProcessorBuffer;
+
+ public:
+ JxlEncoderOutputProcessorWrapper() = default;
+ explicit JxlEncoderOutputProcessorWrapper(JxlEncoderOutputProcessor processor)
+ : external_output_processor_(
+ jxl::make_unique<JxlEncoderOutputProcessor>(processor)) {}
+
+ bool HasAvailOut() const { return avail_out_ != nullptr; }
+
+ // Caller can never overwrite a previously-written buffer. Asking for a buffer
+ // with `min_size` such that `position + min_size` overlaps with a
+ // previously-written buffer is invalid.
+ jxl::StatusOr<JxlOutputProcessorBuffer> GetBuffer(size_t min_size,
+ size_t requested_size = 0);
+
+ void Seek(size_t pos);
+
+ void SetFinalizedPosition();
+
+ size_t CurrentPosition() const { return position_; }
+
+ bool SetAvailOut(uint8_t** next_out, size_t* avail_out);
+
+ bool WasStopRequested() const { return stop_requested_; }
+ bool OutputProcessorSet() const {
+ return external_output_processor_ != nullptr;
+ }
+ bool HasOutputToWrite() const {
+ return output_position_ < finalized_position_;
+ }
+
+ void CopyOutput(std::vector<uint8_t>& output, uint8_t* next_out,
+ size_t& avail_out);
+
+ private:
+ void ReleaseBuffer(size_t bytes_used);
+
+ // Tries to write all the bytes up to the finalized position.
+ void FlushOutput();
+
+ bool AppendBufferToExternalProcessor(void* data, size_t count);
+
+ struct InternalBuffer {
+ // Bytes in the range `[output_position_ - start_of_the_buffer,
+ // written_bytes)` need to be flushed out.
+ size_t written_bytes = 0;
+ // If data has been buffered, it is stored in `owned_data`.
+ jxl::PaddedBytes owned_data;
+ };
+
+ // Invariant: `internal_buffers_` does not contain chunks that are entirely
+ // below the output position.
+ std::map<size_t, InternalBuffer> internal_buffers_;
+
+ uint8_t** next_out_ = nullptr;
+ size_t* avail_out_ = nullptr;
+ // Where the next GetBuffer call will write bytes to.
+ size_t position_ = 0;
+ // The position of the last SetFinalizedPosition call.
+ size_t finalized_position_ = 0;
+ // Either the position of the `external_output_processor_` or the position
+ // `next_out_` points to.
+ size_t output_position_ = 0;
+
+ bool stop_requested_ = false;
+ bool has_buffer_ = false;
+
+ std::unique_ptr<JxlEncoderOutputProcessor> external_output_processor_;
+};
+
+class JxlOutputProcessorBuffer {
+ public:
+ size_t size() const { return size_; };
+ uint8_t* data() { return data_; }
+
+ JxlOutputProcessorBuffer(uint8_t* buffer, size_t size, size_t bytes_used,
+ JxlEncoderOutputProcessorWrapper* wrapper)
+ : data_(buffer),
+ size_(size),
+ bytes_used_(bytes_used),
+ wrapper_(wrapper) {}
+ ~JxlOutputProcessorBuffer() { release(); }
+
+ JxlOutputProcessorBuffer(const JxlOutputProcessorBuffer&) = delete;
+ JxlOutputProcessorBuffer(JxlOutputProcessorBuffer&& other) noexcept
+ : JxlOutputProcessorBuffer(other.data_, other.size_, other.bytes_used_,
+ other.wrapper_) {
+ other.data_ = nullptr;
+ other.size_ = 0;
+ }
+
+ void advance(size_t count) {
+ JXL_ASSERT(count <= size_);
+ data_ += count;
+ size_ -= count;
+ bytes_used_ += count;
+ }
+
+ void release() {
+ if (this->data_) {
+ wrapper_->ReleaseBuffer(bytes_used_);
+ }
+ data_ = nullptr;
+ size_ = 0;
+ }
+
+ void append(const void* data, size_t count) {
+ memcpy(data_, data, count);
+ advance(count);
+ }
+
+ template <typename T>
+ void append(const T& data) {
+ static_assert(sizeof(*std::begin(data)) == 1, "Cannot append non-bytes");
+ append(&*std::begin(data), std::end(data) - std::begin(data));
+ }
+
+ JxlOutputProcessorBuffer& operator=(const JxlOutputProcessorBuffer&) = delete;
+ JxlOutputProcessorBuffer& operator=(
+ JxlOutputProcessorBuffer&& other) noexcept {
+ data_ = other.data_;
+ size_ = other.size_;
+ wrapper_ = other.wrapper_;
+ return *this;
+ }
+
+ private:
+ uint8_t* data_;
+ size_t size_;
+ size_t bytes_used_;
+ JxlEncoderOutputProcessorWrapper* wrapper_;
+};
+
+template <typename T>
+jxl::Status AppendData(JxlEncoderOutputProcessorWrapper& output_processor,
+ const T& data) {
+ size_t size = std::end(data) - std::begin(data);
+ size_t written = 0;
+ while (written < size) {
+ JXL_ASSIGN_OR_RETURN(auto buffer,
+ output_processor.GetBuffer(1, size - written));
+ size_t n = std::min(size - written, buffer.size());
+ buffer.append(data.data() + written, n);
+ written += n;
+ }
+ return jxl::OkStatus();
+}
+
+// Internal use only struct, can only be initialized correctly by
+// JxlEncoderCreate.
+struct JxlEncoderStruct {
+ JxlEncoderError error = JxlEncoderError::JXL_ENC_ERR_OK;
+ JxlMemoryManager memory_manager;
+ jxl::MemoryManagerUniquePtr<jxl::ThreadPool> thread_pool{
+ nullptr, jxl::MemoryManagerDeleteHelper(&memory_manager)};
+ JxlCmsInterface cms;
+ bool cms_set;
+ std::vector<jxl::MemoryManagerUniquePtr<JxlEncoderFrameSettings>>
+ encoder_options;
+
+ size_t num_queued_frames;
+ size_t num_queued_boxes;
+ std::vector<jxl::JxlEncoderQueuedInput> input_queue;
+ JxlEncoderOutputProcessorWrapper output_processor;
+
+ // How many codestream bytes have been written, i.e.,
+ // content of jxlc and jxlp boxes. Frame index box jxli
+ // requires position indices to point to codestream bytes,
+ // so we need to keep track of the total of flushed or queue
+ // codestream bytes. These bytes may be in a single jxlc box
+ // or across multiple jxlp boxes.
+ size_t codestream_bytes_written_end_of_frame;
+ jxl::JxlEncoderFrameIndexBox frame_index_box;
+
+ // Force using the container even if not needed
+ bool use_container;
+ // User declared they will add metadata boxes
+ bool use_boxes;
+
+ // TODO(lode): move level into jxl::CompressParams since some C++
+ // implementation decisions should be based on it: level 10 allows more
+ // features to be used.
+ int32_t codestream_level;
+ bool store_jpeg_metadata;
+ jxl::CodecMetadata metadata;
+ std::vector<uint8_t> jpeg_metadata;
+
+ // Wrote any output at all, so wrote the data before the first user added
+ // frame or box, such as signature, basic info, ICC profile or jpeg
+ // reconstruction box.
+ bool wrote_bytes;
+ jxl::CompressParams last_used_cparams;
+ JxlBasicInfo basic_info;
+
+ // Encoder wrote a jxlp (partial codestream) box, so any next codestream
+ // parts must also be written in jxlp boxes, a single jxlc box cannot be
+ // used. The counter is used for the 4-byte jxlp box index header.
+ size_t jxlp_counter;
+
+ bool frames_closed;
+ bool boxes_closed;
+ bool basic_info_set;
+ bool color_encoding_set;
+ bool intensity_target_set;
+ bool allow_expert_options = false;
+ int brotli_effort = -1;
+
+ // Takes the first frame in the input_queue, encodes it, and appends
+ // the bytes to the output_byte_queue.
+ jxl::Status ProcessOneEnqueuedInput();
+
+ bool MustUseContainer() const {
+ return use_container || (codestream_level != 5 && codestream_level != -1) ||
+ store_jpeg_metadata || use_boxes;
+ }
+
+ // `write_box` must never seek before the position the output wrapper was at
+ // the moment of the call, and must leave the output wrapper such that its
+ // position is one byte past the end of the written box.
+ template <typename WriteBox>
+ jxl::Status AppendBox(const jxl::BoxType& type, bool unbounded,
+ size_t box_max_size, const WriteBox& write_box);
+
+ template <typename BoxContents>
+ jxl::Status AppendBoxWithContents(const jxl::BoxType& type,
+ const BoxContents& contents);
+};
+
+struct JxlEncoderFrameSettingsStruct {
+ JxlEncoder* enc;
+ jxl::JxlEncoderFrameSettingsValues values;
+};
+
+struct JxlEncoderStatsStruct {
+ jxl::AuxOut aux_out;
+};
+
+#endif // LIB_JXL_ENCODE_INTERNAL_H_