libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

encode_internal.h (23504B)


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