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_