libjxl

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

decode_to_jpeg.h (7786B)


      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 #ifndef LIB_JXL_DECODE_TO_JPEG_H_
      7 #define LIB_JXL_DECODE_TO_JPEG_H_
      8 
      9 // JPEG XL to JPEG bytes decoder logic. The JxlToJpegDecoder class keeps track
     10 // of the decoder state needed to parse the JPEG reconstruction box and provide
     11 // the reconstructed JPEG to the output buffer.
     12 
     13 #include <jxl/decode.h>
     14 #include <stdint.h>
     15 #include <stdlib.h>
     16 
     17 #include <algorithm>
     18 #include <cstring>
     19 #include <memory>
     20 #include <utility>
     21 #include <vector>
     22 
     23 #include "lib/jxl/base/status.h"
     24 #include "lib/jxl/common.h"
     25 #include "lib/jxl/image_bundle.h"
     26 #include "lib/jxl/jpeg/jpeg_data.h"
     27 #if JPEGXL_ENABLE_TRANSCODE_JPEG
     28 #include "lib/jxl/jpeg/dec_jpeg_data_writer.h"
     29 #endif  // JPEGXL_ENABLE_TRANSCODE_JPEG
     30 
     31 namespace jxl {
     32 
     33 #if JPEGXL_ENABLE_TRANSCODE_JPEG
     34 
     35 class JxlToJpegDecoder {
     36  public:
     37   // Returns whether an output buffer is set.
     38   bool IsOutputSet() const { return next_out_ != nullptr; }
     39 
     40   // Returns whether the decoder is parsing a boxa JPEG box was parsed.
     41   bool IsParsingBox() const { return inside_box_; }
     42 
     43   // Sets the output buffer used when producing JPEG output.
     44   JxlDecoderStatus SetOutputBuffer(uint8_t* data, size_t size) {
     45     if (next_out_) return JXL_DEC_ERROR;
     46     next_out_ = data;
     47     avail_size_ = size;
     48     return JXL_DEC_SUCCESS;
     49   }
     50 
     51   // Releases the buffer set with SetOutputBuffer().
     52   size_t ReleaseOutputBuffer() {
     53     size_t result = avail_size_;
     54     next_out_ = nullptr;
     55     avail_size_ = 0;
     56     return result;
     57   }
     58 
     59   void StartBox(bool box_until_eof, size_t contents_size) {
     60     // A new box implies that we clear the buffer.
     61     buffer_.clear();
     62     inside_box_ = true;
     63     if (box_until_eof) {
     64       box_until_eof_ = true;
     65     } else {
     66       box_size_ = contents_size;
     67     }
     68   }
     69 
     70   // Consumes data from next_in/avail_in to reconstruct JPEG data.
     71   // Uses box_size_, inside_box_ and box_until_eof_ to calculate how much to
     72   // consume. Potentially stores unparsed data in buffer_.
     73   // Potentially populates jpeg_data_. Potentially updates inside_box_.
     74   // Returns JXL_DEC_JPEG_RECONSTRUCTION when finished, JXL_DEC_NEED_MORE_INPUT
     75   // if more input is needed, JXL_DEC_ERROR on parsing error.
     76   JxlDecoderStatus Process(const uint8_t** next_in, size_t* avail_in);
     77 
     78   // Returns non-owned copy of the JPEGData, only after Process finished and
     79   // the JPEGData was not yet moved to an image bundle with
     80   // SetImageBundleJpegData.
     81   jpeg::JPEGData* GetJpegData() { return jpeg_data_.get(); }
     82 
     83   // Returns how many exif or xmp app markers are present in the JPEG data. A
     84   // return value higher than 1 would require multiple exif boxes or multiple
     85   // xmp boxes in the container format, and this is not supported by the API and
     86   // considered an error. May only be called after Process returned success.
     87   static size_t NumExifMarkers(const jpeg::JPEGData& jpeg_data);
     88   static size_t NumXmpMarkers(const jpeg::JPEGData& jpeg_data);
     89 
     90   // Returns box content size for metadata, using the known data from the app
     91   // markers.
     92   static JxlDecoderStatus ExifBoxContentSize(const jpeg::JPEGData& jpeg_data,
     93                                              size_t* size);
     94   static JxlDecoderStatus XmlBoxContentSize(const jpeg::JPEGData& jpeg_data,
     95                                             size_t* size);
     96 
     97   // Returns JXL_DEC_ERROR if there is no exif/XMP marker or the data size
     98   // does not match, or this function is called before Process returned
     99   // success, JXL_DEC_SUCCESS otherwise. As input, provide the full box contents
    100   // but not the box header. In case of exif, this includes the 4-byte TIFF
    101   // header, even though it won't be copied into the JPEG.
    102   static JxlDecoderStatus SetExif(const uint8_t* data, size_t size,
    103                                   jpeg::JPEGData* jpeg_data);
    104   static JxlDecoderStatus SetXmp(const uint8_t* data, size_t size,
    105                                  jpeg::JPEGData* jpeg_data);
    106 
    107   // Sets the JpegData of the ImageBundle passed if there is anything to set.
    108   // Releases the JpegData from this decoder if set.
    109   Status SetImageBundleJpegData(ImageBundle* ib) {
    110     if (IsOutputSet() && jpeg_data_ != nullptr) {
    111       if (!jpeg::SetJPEGDataFromICC(ib->metadata()->color_encoding.ICC(),
    112                                     jpeg_data_.get())) {
    113         return false;
    114       }
    115       ib->jpeg_data = std::move(jpeg_data_);
    116     }
    117     return true;
    118   }
    119 
    120   JxlDecoderStatus WriteOutput(const jpeg::JPEGData& jpeg_data) {
    121     // Copy JPEG bytestream if desired.
    122     uint8_t* tmp_next_out = next_out_;
    123     size_t tmp_avail_size = avail_size_;
    124     auto write = [&tmp_next_out, &tmp_avail_size](const uint8_t* buf,
    125                                                   size_t len) {
    126       size_t to_write = std::min<size_t>(tmp_avail_size, len);
    127       if (to_write != 0) memcpy(tmp_next_out, buf, to_write);
    128       tmp_next_out += to_write;
    129       tmp_avail_size -= to_write;
    130       return to_write;
    131     };
    132     Status write_result = jpeg::WriteJpeg(jpeg_data, write);
    133     if (!write_result) {
    134       if (tmp_avail_size == 0) {
    135         return JXL_DEC_JPEG_NEED_MORE_OUTPUT;
    136       }
    137       return JXL_DEC_ERROR;
    138     }
    139     next_out_ = tmp_next_out;
    140     avail_size_ = tmp_avail_size;
    141     return JXL_DEC_SUCCESS;
    142   }
    143 
    144  private:
    145   // Content of the most recently parsed JPEG reconstruction box if any.
    146   std::vector<uint8_t> buffer_;
    147 
    148   // Decoded content of the most recently parsed JPEG reconstruction box is
    149   // stored here.
    150   std::unique_ptr<jpeg::JPEGData> jpeg_data_;
    151 
    152   // True if the decoder is currently reading bytes inside a JPEG reconstruction
    153   // box.
    154   bool inside_box_ = false;
    155 
    156   // True if the JPEG reconstruction box had undefined size (all remaining
    157   // bytes).
    158   bool box_until_eof_ = false;
    159   // Size of most recently parsed JPEG reconstruction box contents.
    160   size_t box_size_ = 0;
    161 
    162   // Next bytes to write JPEG reconstruction to.
    163   uint8_t* next_out_ = nullptr;
    164   // Available bytes to write JPEG reconstruction to.
    165   size_t avail_size_ = 0;
    166 };
    167 
    168 #else
    169 
    170 // Fake class that disables support for decoding JPEG XL to JPEG.
    171 class JxlToJpegDecoder {
    172  public:
    173   bool IsOutputSet() const { return false; }
    174   bool IsParsingBox() const { return false; }
    175 
    176   JxlDecoderStatus SetOutputBuffer(uint8_t* /* data */, size_t /* size */) {
    177     return JXL_DEC_ERROR;
    178   }
    179   size_t ReleaseOutputBuffer() { return 0; }
    180 
    181   void StartBox(bool /* box_until_eof */, size_t /* contents_size */) {}
    182 
    183   JxlDecoderStatus Process(const uint8_t** next_in, size_t* avail_in) {
    184     return JXL_DEC_ERROR;
    185   }
    186   jpeg::JPEGData* GetJpegData() { return nullptr; }
    187 
    188   Status SetImageBundleJpegData(ImageBundle* /* ib */) { return true; }
    189 
    190   static size_t NumExifMarkers(const jpeg::JPEGData& /*jpeg_data*/) {
    191     return 0;
    192   }
    193   static size_t NumXmpMarkers(const jpeg::JPEGData& /*jpeg_data*/) { return 0; }
    194   static size_t ExifBoxContentSize(const jpeg::JPEGData& /*jpeg_data*/,
    195                                    size_t* /*size*/) {
    196     return JXL_DEC_ERROR;
    197   }
    198   static size_t XmlBoxContentSize(const jpeg::JPEGData& /*jpeg_data*/,
    199                                   size_t* /*size*/) {
    200     return JXL_DEC_ERROR;
    201   }
    202   static JxlDecoderStatus SetExif(const uint8_t* /*data*/, size_t /*size*/,
    203                                   jpeg::JPEGData* /*jpeg_data*/) {
    204     return JXL_DEC_ERROR;
    205   }
    206   static JxlDecoderStatus SetXmp(const uint8_t* /*data*/, size_t /*size*/,
    207                                  jpeg::JPEGData* /*jpeg_data*/) {
    208     return JXL_DEC_ERROR;
    209   }
    210 
    211   JxlDecoderStatus WriteOutput(const jpeg::JPEGData& /* jpeg_data */) {
    212     return JXL_DEC_SUCCESS;
    213   }
    214 };
    215 
    216 #endif  // JPEGXL_ENABLE_TRANSCODE_JPEG
    217 
    218 }  // namespace jxl
    219 
    220 #endif  // LIB_JXL_DECODE_TO_JPEG_H_