tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

encode_internal.h (24237B)


      1 /* Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 *
      3 * Use of this source code is governed by a BSD-style
      4 * license that can be found in the LICENSE file.
      5 */
      6 
      7 #ifndef LIB_JXL_ENCODE_INTERNAL_H_
      8 #define LIB_JXL_ENCODE_INTERNAL_H_
      9 
     10 #include <jxl/cms_interface.h>
     11 #include <jxl/codestream_header.h>
     12 #include <jxl/encode.h>
     13 #include <jxl/memory_manager.h>
     14 #include <jxl/types.h>
     15 
     16 #include <algorithm>
     17 #include <array>
     18 #include <cstddef>
     19 #include <cstdint>
     20 #include <cstring>
     21 #include <functional>
     22 #include <map>
     23 #include <memory>
     24 #include <string>
     25 #include <utility>
     26 #include <vector>
     27 
     28 #include "lib/jxl/base/c_callback_support.h"
     29 #include "lib/jxl/base/common.h"
     30 #include "lib/jxl/base/compiler_specific.h"
     31 #include "lib/jxl/base/data_parallel.h"
     32 #include "lib/jxl/base/status.h"
     33 #include "lib/jxl/enc_fast_lossless.h"
     34 #include "lib/jxl/enc_params.h"
     35 #include "lib/jxl/image_metadata.h"
     36 #include "lib/jxl/jpeg/jpeg_data.h"
     37 #include "lib/jxl/memory_manager_internal.h"
     38 #include "lib/jxl/padded_bytes.h"
     39 
     40 namespace jxl {
     41 
     42 /* Frame index box 'jxli' will start with Varint() for
     43 NF: has type Varint(): number of frames listed in the index.
     44 TNUM: has type u32: numerator of tick unit.
     45 TDEN: has type u32: denominator of tick unit. Value 0 means the file is
     46 ill-formed. per frame i listed: OFFi: has type Varint(): offset of start byte of
     47 this frame compared to start byte of previous frame from this index in the JPEG
     48 XL codestream. For the first frame, this is the offset from the first byte of
     49 the JPEG XL codestream. Ti: has type Varint(): duration in ticks between the
     50 start of this frame and the start of the next frame in the index. If this is the
     51 last frame in the index, this is the duration in ticks between the start of this
     52 frame and the end of the stream. A tick lasts TNUM / TDEN seconds. Fi: has type
     53 Varint(): amount of frames the next frame in the index occurs after this frame.
     54 If this is the last frame in the index, this is the amount of frames after this
     55 frame in the remainder of the stream. Only frames that are presented by the
     56 decoder are counted for this purpose, this excludes frames that are not intended
     57 for display but for compositing with other frames, such as frames that aren't
     58 the last frame with a duration of 0 ticks.
     59 
     60 All the frames listed in jxli are keyframes and the first frame is
     61 present in the list.
     62 There shall be either zero or one Frame Index boxes in a JPEG XL file.
     63 The offsets OFFi per frame are given as bytes in the codestream, not as
     64 bytes in the file format using the box structure. This means if JPEG XL Partial
     65 Codestream boxes are used, the offset is counted within the concatenated
     66 codestream, bytes from box headers or non-codestream boxes are not counted.
     67 */
     68 
     69 typedef struct JxlEncoderFrameIndexBoxEntryStruct {
     70  bool to_be_indexed;
     71  uint32_t duration;
     72  uint64_t OFFi;
     73 } JxlEncoderFrameIndexBoxEntry;
     74 
     75 typedef struct JxlEncoderFrameIndexBoxStruct {
     76  // We always need to record the first frame entry, so presence of the
     77  // first entry alone is not an indication if it was requested to be
     78  // stored.
     79  bool index_box_requested_through_api = false;
     80 
     81  int64_t NF() const { return entries.size(); }
     82  bool StoreFrameIndexBox() {
     83    for (auto e : entries) {
     84      if (e.to_be_indexed) {
     85        return true;
     86      }
     87    }
     88    return false;
     89  }
     90  int32_t TNUM = 1;
     91  int32_t TDEN = 1000;
     92 
     93  std::vector<JxlEncoderFrameIndexBoxEntry> entries;
     94 
     95  // That way we can ensure that every index box will have the first frame.
     96  // If the API user decides to mark it as an indexed frame, we call
     97  // the AddFrame again, this time with requested.
     98  void AddFrame(uint64_t OFFi, uint32_t duration, bool to_be_indexed) {
     99    // We call AddFrame to every frame.
    100    // Recording the first frame is required by the standard.
    101    // Knowing the last frame is required, since the last indexed frame
    102    // needs to know how many frames until the end.
    103    // To be able to tell how many frames there are between each index
    104    // entry we just record every frame here.
    105    if (entries.size() == 1) {
    106      if (OFFi == entries[0].OFFi) {
    107        // API use for the first frame, let's clear the already recorded first
    108        // frame.
    109        entries.clear();
    110      }
    111    }
    112    JxlEncoderFrameIndexBoxEntry e;
    113    e.to_be_indexed = to_be_indexed;
    114    e.OFFi = OFFi;
    115    e.duration = duration;
    116    entries.push_back(e);
    117  }
    118 } JxlEncoderFrameIndexBox;
    119 
    120 // The encoder options (such as quality, compression speed, ...) for a single
    121 // frame, but not encoder-wide options such as box-related options.
    122 typedef struct JxlEncoderFrameSettingsValuesStruct {
    123  // lossless is a separate setting from cparams because it is a combination
    124  // setting that overrides multiple settings inside of cparams.
    125  bool lossless;
    126  CompressParams cparams;
    127  JxlFrameHeader header;
    128  std::vector<JxlBlendInfo> extra_channel_blend_info;
    129  std::string frame_name;
    130  JxlBitDepth image_bit_depth;
    131  bool frame_index_box = false;
    132  jxl::AuxOut* aux_out = nullptr;
    133 } JxlEncoderFrameSettingsValues;
    134 
    135 typedef std::array<uint8_t, 4> BoxType;
    136 
    137 // Utility function that makes a BoxType from a string literal. The string must
    138 // have 4 characters, a 5th null termination character is optional.
    139 constexpr BoxType MakeBoxType(const char* type) {
    140  return BoxType(
    141      {{static_cast<uint8_t>(type[0]), static_cast<uint8_t>(type[1]),
    142        static_cast<uint8_t>(type[2]), static_cast<uint8_t>(type[3])}});
    143 }
    144 
    145 constexpr std::array<unsigned char, 32> kContainerHeader = {
    146    0,   0,   0, 0xc, 'J',  'X', 'L', ' ', 0xd, 0xa, 0x87,
    147    0xa, 0,   0, 0,   0x14, 'f', 't', 'y', 'p', 'j', 'x',
    148    'l', ' ', 0, 0,   0,    0,   'j', 'x', 'l', ' '};
    149 
    150 constexpr std::array<unsigned char, 8> kLevelBoxHeader = {0,   0,   0,   0x9,
    151                                                          'j', 'x', 'l', 'l'};
    152 
    153 static JXL_INLINE size_t BitsPerChannel(JxlDataType data_type) {
    154  switch (data_type) {
    155    case JXL_TYPE_UINT8:
    156      return 8;
    157    case JXL_TYPE_UINT16:
    158      return 16;
    159    case JXL_TYPE_FLOAT:
    160      return 32;
    161    case JXL_TYPE_FLOAT16:
    162      return 16;
    163    default:
    164      return 0;  // signals unhandled JxlDataType
    165  }
    166 }
    167 
    168 static JXL_INLINE size_t BytesPerPixel(JxlPixelFormat format) {
    169  return format.num_channels * BitsPerChannel(format.data_type) / 8;
    170 }
    171 
    172 using ScopedInputBuffer =
    173    std::unique_ptr<const void, std::function<void(const void*)>>;
    174 
    175 static JXL_INLINE ScopedInputBuffer
    176 GetColorBuffer(JxlChunkedFrameInputSource& input, size_t xpos, size_t ypos,
    177               size_t xsize, size_t ysize, size_t* row_offset) {
    178  return ScopedInputBuffer(
    179      input.get_color_channel_data_at(input.opaque, xpos, ypos, xsize, ysize,
    180                                      row_offset),
    181      [&input](const void* p) { input.release_buffer(input.opaque, p); });
    182 }
    183 
    184 static JXL_INLINE ScopedInputBuffer GetExtraChannelBuffer(
    185    JxlChunkedFrameInputSource& input, size_t ec_index, size_t xpos,
    186    size_t ypos, size_t xsize, size_t ysize, size_t* row_offset) {
    187  return ScopedInputBuffer(
    188      input.get_extra_channel_data_at(input.opaque, ec_index, xpos, ypos, xsize,
    189                                      ysize, row_offset),
    190      [&input](const void* p) { input.release_buffer(input.opaque, p); });
    191 }
    192 
    193 // Common adapter for an existing frame input source or a whole-image input
    194 // buffer or a parsed JPEG file.
    195 class JxlEncoderChunkedFrameAdapter {
    196 public:
    197  JxlEncoderChunkedFrameAdapter(size_t xs, size_t ys, size_t num_extra_channels)
    198      : xsize(xs), ysize(ys), channels_(1 + num_extra_channels) {}
    199 
    200  void SetInputSource(JxlChunkedFrameInputSource input_source) {
    201    input_source_ = input_source;
    202    has_input_source_ = true;
    203  }
    204 
    205  bool SetFromBuffer(size_t channel, const uint8_t* buffer, size_t size,
    206                     JxlPixelFormat format) {
    207    if (channel >= channels_.size()) return false;
    208    if (!channels_[channel].SetFromBuffer(buffer, size, format, xsize, ysize)) {
    209      return false;
    210    }
    211    if (channel > 0) channels_[channel].CopyBuffer();
    212    return true;
    213  }
    214 
    215  void SetJPEGData(std::unique_ptr<jpeg::JPEGData> jpeg_data) {
    216    jpeg_data_ = std::move(jpeg_data);
    217  }
    218 
    219  // NB: after TakeJPEGData it will return false!
    220  bool IsJPEG() const { return jpeg_data_ != nullptr; }
    221 
    222  std::unique_ptr<jpeg::JPEGData> TakeJPEGData() {
    223    return std::move(jpeg_data_);
    224  }
    225 
    226  JxlChunkedFrameInputSource GetInputSource() {
    227    if (has_input_source_) {
    228      return input_source_;
    229    }
    230    return JxlChunkedFrameInputSource{
    231        this,
    232        METHOD_TO_C_CALLBACK(
    233            &JxlEncoderChunkedFrameAdapter::GetColorChannelsPixelFormat),
    234        METHOD_TO_C_CALLBACK(
    235            &JxlEncoderChunkedFrameAdapter::GetColorChannelDataAt),
    236        METHOD_TO_C_CALLBACK(
    237            &JxlEncoderChunkedFrameAdapter::GetExtraChannelPixelFormat),
    238        METHOD_TO_C_CALLBACK(
    239            &JxlEncoderChunkedFrameAdapter::GetExtraChannelDataAt),
    240        METHOD_TO_C_CALLBACK(
    241            &JxlEncoderChunkedFrameAdapter::ReleaseCurrentData)};
    242  }
    243 
    244  bool CopyBuffers() {
    245    if (has_input_source_) {
    246      JxlPixelFormat format{4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
    247      input_source_.get_color_channels_pixel_format(input_source_.opaque,
    248                                                    &format);
    249      size_t row_offset;
    250      {
    251        auto buffer =
    252            GetColorBuffer(input_source_, 0, 0, xsize, ysize, &row_offset);
    253        if (!buffer) return false;
    254        channels_[0].CopyFromBuffer(buffer.get(), format, xsize, ysize,
    255                                    row_offset);
    256      }
    257      for (size_t ec = 0; ec + 1 < channels_.size(); ++ec) {
    258        input_source_.get_extra_channel_pixel_format(input_source_.opaque, ec,
    259                                                     &format);
    260        auto buffer = GetExtraChannelBuffer(input_source_, ec, 0, 0, xsize,
    261                                            ysize, &row_offset);
    262        if (!buffer) continue;
    263        channels_[1 + ec].CopyFromBuffer(buffer.get(), format, xsize, ysize,
    264                                         row_offset);
    265      }
    266      has_input_source_ = false;
    267    } else {
    268      channels_[0].CopyBuffer();
    269    }
    270    return true;
    271  }
    272 
    273  bool StreamingInput() const { return has_input_source_; }
    274 
    275  const size_t xsize;
    276  const size_t ysize;
    277 
    278 private:
    279  void GetColorChannelsPixelFormat(JxlPixelFormat* pixel_format) {
    280    *pixel_format = channels_[0].format_;
    281  }
    282 
    283  const void* GetColorChannelDataAt(size_t xpos, size_t ypos, size_t xsize,
    284                                    size_t ysize, size_t* row_offset) {
    285    return channels_[0].GetDataAt(xpos, ypos, xsize, ysize, row_offset);
    286  }
    287 
    288  void GetExtraChannelPixelFormat(size_t ec_index,
    289                                  JxlPixelFormat* pixel_format) {
    290    JXL_DASSERT(1 + ec_index < channels_.size());
    291    *pixel_format = channels_[1 + ec_index].format_;
    292  }
    293 
    294  const void* GetExtraChannelDataAt(size_t ec_index, size_t xpos, size_t ypos,
    295                                    size_t xsize, size_t ysize,
    296                                    size_t* row_offset) {
    297    JXL_DASSERT(1 + ec_index < channels_.size());
    298    return channels_[1 + ec_index].GetDataAt(xpos, ypos, xsize, ysize,
    299                                             row_offset);
    300  }
    301 
    302  void ReleaseCurrentData(const void* buffer) {
    303    // No dynamic memory is allocated in GetColorChannelDataAt or
    304    // GetExtraChannelDataAt. Therefore, no cleanup is required here.
    305  }
    306 
    307  JxlChunkedFrameInputSource input_source_ = {};
    308  bool has_input_source_ = false;
    309  std::unique_ptr<jpeg::JPEGData> jpeg_data_;
    310  struct Channel {
    311    const uint8_t* buffer_ = nullptr;
    312    size_t buffer_size_;
    313    JxlPixelFormat format_;
    314    size_t xsize_;
    315    size_t ysize_;
    316    size_t bytes_per_pixel_;
    317    size_t stride_;
    318    std::vector<uint8_t> copy_;
    319 
    320    void SetFormatAndDimensions(JxlPixelFormat format, size_t xsize,
    321                                size_t ysize) {
    322      format_ = format;
    323      xsize_ = xsize;
    324      ysize_ = ysize;
    325      bytes_per_pixel_ = BytesPerPixel(format_);
    326      const size_t last_row_size = xsize_ * bytes_per_pixel_;
    327      const size_t align = format_.align;
    328      stride_ = (align > 1 ? jxl::DivCeil(last_row_size, align) * align
    329                           : last_row_size);
    330    }
    331 
    332    bool SetFromBuffer(const uint8_t* buffer, size_t size,
    333                       JxlPixelFormat format, size_t xsize, size_t ysize) {
    334      SetFormatAndDimensions(format, xsize, ysize);
    335      buffer_ = buffer;
    336      buffer_size_ = size;
    337      const size_t min_buffer_size =
    338          stride_ * (ysize_ - 1) + xsize_ * bytes_per_pixel_;
    339      return min_buffer_size <= size;
    340    }
    341 
    342    void CopyFromBuffer(const void* buffer, JxlPixelFormat format, size_t xsize,
    343                        size_t ysize, size_t row_offset) {
    344      SetFormatAndDimensions(format, xsize, ysize);
    345      buffer_ = nullptr;
    346      copy_.resize(ysize * stride_);
    347      for (size_t y = 0; y < ysize; ++y) {
    348        memcpy(copy_.data() + y * stride_,
    349               reinterpret_cast<const uint8_t*>(buffer) + y * row_offset,
    350               stride_);
    351      }
    352    }
    353 
    354    void CopyBuffer() {
    355      if (buffer_) {
    356        copy_ = std::vector<uint8_t>(buffer_, buffer_ + buffer_size_);
    357        buffer_ = nullptr;
    358      }
    359    }
    360 
    361    const void* GetDataAt(size_t xpos, size_t ypos, size_t xsize, size_t ysize,
    362                          size_t* row_offset) const {
    363      const uint8_t* buffer = copy_.empty() ? buffer_ : copy_.data();
    364      JXL_DASSERT(ypos + ysize <= ysize_);
    365      JXL_DASSERT(xpos + xsize <= xsize_);
    366      JXL_DASSERT(buffer);
    367      *row_offset = stride_;
    368      return buffer + ypos * stride_ + xpos * bytes_per_pixel_;
    369    }
    370  };
    371  std::vector<Channel> channels_;
    372 };
    373 
    374 struct JxlEncoderQueuedFrame {
    375  JxlEncoderFrameSettingsValues option_values;
    376  JxlEncoderChunkedFrameAdapter frame_data;
    377  std::vector<uint8_t> ec_initialized;
    378 };
    379 
    380 struct JxlEncoderQueuedBox {
    381  BoxType type;
    382  std::vector<uint8_t> contents;
    383  bool compress_box;
    384 };
    385 
    386 using FJXLFrameUniquePtr =
    387    std::unique_ptr<JxlFastLosslessFrameState,
    388                    decltype(&JxlFastLosslessFreeFrameState)>;
    389 
    390 // Either a frame, or a box, not both.
    391 // Can also be a FJXL frame.
    392 struct JxlEncoderQueuedInput {
    393  explicit JxlEncoderQueuedInput(const JxlMemoryManager& memory_manager)
    394      : frame(nullptr, jxl::MemoryManagerDeleteHelper(&memory_manager)),
    395        box(nullptr, jxl::MemoryManagerDeleteHelper(&memory_manager)) {}
    396  MemoryManagerUniquePtr<JxlEncoderQueuedFrame> frame;
    397  MemoryManagerUniquePtr<JxlEncoderQueuedBox> box;
    398  FJXLFrameUniquePtr fast_lossless_frame = {nullptr,
    399                                            JxlFastLosslessFreeFrameState};
    400 };
    401 
    402 static constexpr size_t kSmallBoxHeaderSize = 8;
    403 static constexpr size_t kLargeBoxHeaderSize = 16;
    404 static constexpr size_t kLargeBoxContentSizeThreshold =
    405    0x100000000ull - kSmallBoxHeaderSize;
    406 
    407 size_t WriteBoxHeader(const jxl::BoxType& type, size_t size, bool unbounded,
    408                      bool force_large_box, uint8_t* output);
    409 
    410 // Appends a JXL container box header with given type, size, and unbounded
    411 // properties to output.
    412 template <typename T>
    413 void AppendBoxHeader(const jxl::BoxType& type, size_t size, bool unbounded,
    414                     T* output) {
    415  size_t current_size = output->size();
    416  output->resize(current_size + kLargeBoxHeaderSize);
    417  size_t header_size =
    418      WriteBoxHeader(type, size, unbounded, /*force_large_box=*/false,
    419                     output->data() + current_size);
    420  output->resize(current_size + header_size);
    421 }
    422 
    423 }  // namespace jxl
    424 
    425 class JxlOutputProcessorBuffer;
    426 
    427 class JxlEncoderOutputProcessorWrapper {
    428  friend class JxlOutputProcessorBuffer;
    429 
    430 public:
    431  explicit JxlEncoderOutputProcessorWrapper(JxlMemoryManager* memory_manager)
    432      : memory_manager_(memory_manager) {}
    433  JxlEncoderOutputProcessorWrapper(JxlMemoryManager* memory_manager,
    434                                   JxlEncoderOutputProcessor processor)
    435      : memory_manager_(memory_manager),
    436        external_output_processor_(
    437            jxl::make_unique<JxlEncoderOutputProcessor>(processor)) {}
    438 
    439  bool HasAvailOut() const { return avail_out_ != nullptr; }
    440 
    441  // Caller can never overwrite a previously-written buffer. Asking for a buffer
    442  // with `min_size` such that `position + min_size` overlaps with a
    443  // previously-written buffer is invalid.
    444  jxl::StatusOr<JxlOutputProcessorBuffer> GetBuffer(size_t min_size,
    445                                                    size_t requested_size = 0);
    446 
    447  jxl::Status Seek(size_t pos);
    448 
    449  jxl::Status SetFinalizedPosition();
    450 
    451  size_t CurrentPosition() const { return position_; }
    452 
    453  jxl::Status SetAvailOut(uint8_t** next_out, size_t* avail_out);
    454 
    455  bool WasStopRequested() const { return stop_requested_; }
    456  bool OutputProcessorSet() const {
    457    return external_output_processor_ != nullptr;
    458  }
    459  bool HasOutputToWrite() const {
    460    return output_position_ < finalized_position_;
    461  }
    462 
    463  jxl::Status CopyOutput(std::vector<uint8_t>& output, uint8_t* next_out,
    464                         size_t& avail_out);
    465 
    466 private:
    467  jxl::Status ReleaseBuffer(size_t bytes_used);
    468 
    469  // Tries to write all the bytes up to the finalized position.
    470  jxl::Status FlushOutput();
    471 
    472  bool AppendBufferToExternalProcessor(void* data, size_t count);
    473 
    474  struct InternalBuffer {
    475    explicit InternalBuffer(JxlMemoryManager* memory_manager)
    476        : owned_data(memory_manager) {
    477      JXL_DASSERT(memory_manager != nullptr);
    478    }
    479    // Bytes in the range `[output_position_ - start_of_the_buffer,
    480    // written_bytes)` need to be flushed out.
    481    size_t written_bytes = 0;
    482    // If data has been buffered, it is stored in `owned_data`.
    483    jxl::PaddedBytes owned_data;
    484  };
    485 
    486  // Invariant: `internal_buffers_` does not contain chunks that are entirely
    487  // below the output position.
    488  std::map<size_t, InternalBuffer> internal_buffers_;
    489 
    490  uint8_t** next_out_ = nullptr;
    491  size_t* avail_out_ = nullptr;
    492  // Where the next GetBuffer call will write bytes to.
    493  size_t position_ = 0;
    494  // The position of the last SetFinalizedPosition call.
    495  size_t finalized_position_ = 0;
    496  // Either the position of the `external_output_processor_` or the position
    497  // `next_out_` points to.
    498  size_t output_position_ = 0;
    499 
    500  bool stop_requested_ = false;
    501  bool has_buffer_ = false;
    502 
    503  JxlMemoryManager* memory_manager_;
    504  std::unique_ptr<JxlEncoderOutputProcessor> external_output_processor_;
    505 };
    506 
    507 class JxlOutputProcessorBuffer {
    508 public:
    509  size_t size() const { return size_; };
    510  uint8_t* data() { return data_; }
    511 
    512  JxlOutputProcessorBuffer(uint8_t* buffer, size_t size, size_t bytes_used,
    513                           JxlEncoderOutputProcessorWrapper* wrapper)
    514      : data_(buffer),
    515        size_(size),
    516        bytes_used_(bytes_used),
    517        wrapper_(wrapper) {}
    518  ~JxlOutputProcessorBuffer() {
    519    jxl::Status result = release();
    520    (void)result;
    521    JXL_DASSERT(result);
    522  }
    523 
    524  JxlOutputProcessorBuffer(const JxlOutputProcessorBuffer&) = delete;
    525  JxlOutputProcessorBuffer(JxlOutputProcessorBuffer&& other) noexcept
    526      : JxlOutputProcessorBuffer(other.data_, other.size_, other.bytes_used_,
    527                                 other.wrapper_) {
    528    other.data_ = nullptr;
    529    other.size_ = 0;
    530  }
    531 
    532  jxl::Status advance(size_t count) {
    533    JXL_ENSURE(count <= size_);
    534    data_ += count;
    535    size_ -= count;
    536    bytes_used_ += count;
    537    return true;
    538  }
    539 
    540  jxl::Status release() {
    541    jxl::Status result = jxl::OkStatus();
    542    if (this->data_) {
    543      result = wrapper_->ReleaseBuffer(bytes_used_);
    544    }
    545    data_ = nullptr;
    546    size_ = 0;
    547    return result;
    548  }
    549 
    550  jxl::Status append(const void* data, size_t count) {
    551    memcpy(data_, data, count);
    552    JXL_RETURN_IF_ERROR(advance(count));
    553    return true;
    554  }
    555 
    556  template <typename T>
    557  jxl::Status append(const T& data) {
    558    static_assert(sizeof(*std::begin(data)) == 1, "Cannot append non-bytes");
    559    JXL_RETURN_IF_ERROR(
    560        append(&*std::begin(data), std::end(data) - std::begin(data)));
    561    return true;
    562  }
    563 
    564  JxlOutputProcessorBuffer& operator=(const JxlOutputProcessorBuffer&) = delete;
    565  JxlOutputProcessorBuffer& operator=(
    566      JxlOutputProcessorBuffer&& other) noexcept {
    567    data_ = other.data_;
    568    size_ = other.size_;
    569    wrapper_ = other.wrapper_;
    570    return *this;
    571  }
    572 
    573 private:
    574  uint8_t* data_;
    575  size_t size_;
    576  size_t bytes_used_;
    577  JxlEncoderOutputProcessorWrapper* wrapper_;
    578 };
    579 
    580 template <typename T>
    581 jxl::Status AppendData(JxlEncoderOutputProcessorWrapper& output_processor,
    582                       const T& data) {
    583  size_t size = std::end(data) - std::begin(data);
    584  size_t written = 0;
    585  while (written < size) {
    586    JXL_ASSIGN_OR_RETURN(auto buffer,
    587                         output_processor.GetBuffer(1, size - written));
    588    size_t n = std::min(size - written, buffer.size());
    589    JXL_RETURN_IF_ERROR(buffer.append(data.data() + written, n));
    590    written += n;
    591  }
    592  return jxl::OkStatus();
    593 }
    594 
    595 // Internal use only struct, can only be initialized correctly by
    596 // JxlEncoderCreate.
    597 struct JxlEncoderStruct {
    598  JxlEncoderStruct() : output_processor(&memory_manager) {}
    599  JxlMemoryManager memory_manager;
    600  jxl::MemoryManagerUniquePtr<jxl::ThreadPool> thread_pool{
    601      nullptr, jxl::MemoryManagerDeleteHelper(&memory_manager)};
    602  std::vector<jxl::MemoryManagerUniquePtr<JxlEncoderFrameSettings>>
    603      encoder_options;
    604 
    605  size_t num_queued_frames;
    606  size_t num_queued_boxes;
    607  std::vector<jxl::JxlEncoderQueuedInput> input_queue;
    608  JxlEncoderOutputProcessorWrapper output_processor;
    609 
    610  // How many codestream bytes have been written, i.e.,
    611  // content of jxlc and jxlp boxes. Frame index box jxli
    612  // requires position indices to point to codestream bytes,
    613  // so we need to keep track of the total of flushed or queue
    614  // codestream bytes. These bytes may be in a single jxlc box
    615  // or across multiple jxlp boxes.
    616  size_t codestream_bytes_written_end_of_frame;
    617  jxl::JxlEncoderFrameIndexBox frame_index_box;
    618 
    619  JxlCmsInterface cms;
    620  bool cms_set;
    621 
    622  // Force using the container even if not needed
    623  bool use_container;
    624  // User declared they will add metadata boxes
    625  bool use_boxes;
    626 
    627  // TODO(lode): move level into jxl::CompressParams since some C++
    628  // implementation decisions should be based on it: level 10 allows more
    629  // features to be used.
    630  bool store_jpeg_metadata;
    631  int32_t codestream_level;
    632  jxl::CodecMetadata metadata;
    633  std::vector<uint8_t> jpeg_metadata;
    634 
    635  jxl::CompressParams last_used_cparams;
    636  JxlBasicInfo basic_info;
    637 
    638  JxlEncoderError error = JxlEncoderError::JXL_ENC_ERR_OK;
    639 
    640  // Encoder wrote a jxlp (partial codestream) box, so any next codestream
    641  // parts must also be written in jxlp boxes, a single jxlc box cannot be
    642  // used. The counter is used for the 4-byte jxlp box index header.
    643  size_t jxlp_counter;
    644 
    645  // Wrote any output at all, so wrote the data before the first user added
    646  // frame or box, such as signature, basic info, ICC profile or jpeg
    647  // reconstruction box.
    648  bool wrote_bytes;
    649 
    650  bool frames_closed;
    651  bool boxes_closed;
    652  bool basic_info_set;
    653  bool color_encoding_set;
    654  bool intensity_target_set;
    655  bool allow_expert_options = false;
    656  int brotli_effort = -1;
    657 
    658  // Takes the first frame in the input_queue, encodes it, and appends
    659  // the bytes to the output_byte_queue.
    660  jxl::Status ProcessOneEnqueuedInput();
    661 
    662  bool MustUseContainer() const {
    663    return use_container || (codestream_level != 5 && codestream_level != -1) ||
    664           store_jpeg_metadata || use_boxes;
    665  }
    666 
    667  // `write_box` must never seek before the position the output wrapper was at
    668  // the moment of the call, and must leave the output wrapper such that its
    669  // position is one byte past the end of the written box.
    670  template <typename WriteBox>
    671  jxl::Status AppendBox(const jxl::BoxType& type, bool unbounded,
    672                        size_t box_max_size, const WriteBox& write_box);
    673 
    674  template <typename BoxContents>
    675  jxl::Status AppendBoxWithContents(const jxl::BoxType& type,
    676                                    const BoxContents& contents);
    677 };
    678 
    679 struct JxlEncoderFrameSettingsStruct {
    680  JxlEncoder* enc;
    681  jxl::JxlEncoderFrameSettingsValues values;
    682 };
    683 
    684 struct JxlEncoderStatsStruct {
    685  std::unique_ptr<jxl::AuxOut> aux_out;
    686 };
    687 
    688 #endif  // LIB_JXL_ENCODE_INTERNAL_H_