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_