decode_to_jpeg.cc (6564B)
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 #include "lib/jxl/decode_to_jpeg.h" 7 8 #include <jxl/decode.h> 9 10 #include <algorithm> 11 #include <cstddef> 12 #include <cstdint> 13 #include <cstring> 14 15 #include "lib/jxl/base/span.h" 16 #include "lib/jxl/base/status.h" 17 #include "lib/jxl/common.h" // JPEGXL_ENABLE_TRANSCODE_JPEG 18 #include "lib/jxl/jpeg/dec_jpeg_data.h" 19 #include "lib/jxl/jpeg/jpeg_data.h" 20 21 namespace jxl { 22 23 #if JPEGXL_ENABLE_TRANSCODE_JPEG 24 25 JxlDecoderStatus JxlToJpegDecoder::Process(const uint8_t** next_in, 26 size_t* avail_in) { 27 if (!inside_box_) { 28 JXL_WARNING( 29 "processing of JPEG reconstruction data outside JPEG reconstruction " 30 "box"); 31 return JXL_DEC_ERROR; 32 } 33 Span<const uint8_t> to_decode; 34 if (box_until_eof_) { 35 // Until EOF means consume all data. 36 to_decode = Bytes(*next_in, *avail_in); 37 *next_in += *avail_in; 38 *avail_in = 0; 39 } else { 40 // Defined size means consume min(available, needed). 41 size_t avail_recon_in = 42 std::min<size_t>(*avail_in, box_size_ - buffer_.size()); 43 to_decode = Bytes(*next_in, avail_recon_in); 44 *next_in += avail_recon_in; 45 *avail_in -= avail_recon_in; 46 } 47 bool old_data_exists = !buffer_.empty(); 48 if (old_data_exists) { 49 // Append incoming data to buffer if we already had data in the buffer. 50 buffer_.insert(buffer_.end(), to_decode.data(), 51 to_decode.data() + to_decode.size()); 52 to_decode = Bytes(buffer_.data(), buffer_.size()); 53 } 54 if (!box_until_eof_ && to_decode.size() > box_size_) { 55 JXL_WARNING("JPEG reconstruction data to decode larger than expected"); 56 return JXL_DEC_ERROR; 57 } 58 if (box_until_eof_ || to_decode.size() == box_size_) { 59 // If undefined size, or the right size, try to decode. 60 jpeg_data_ = make_unique<jpeg::JPEGData>(); 61 const auto status = jpeg::DecodeJPEGData(to_decode, jpeg_data_.get()); 62 if (status.IsFatalError()) return JXL_DEC_ERROR; 63 if (status) { 64 // Successful decoding, emit event after updating state to track that we 65 // are no longer parsing JPEG reconstruction data. 66 inside_box_ = false; 67 return JXL_DEC_JPEG_RECONSTRUCTION; 68 } 69 if (box_until_eof_) { 70 // Unsuccessful decoding and undefined size, assume incomplete data. Copy 71 // the data if we haven't already. 72 if (!old_data_exists) { 73 buffer_.insert(buffer_.end(), to_decode.data(), 74 to_decode.data() + to_decode.size()); 75 } 76 } else { 77 // Unsuccessful decoding of correct amount of data, assume error. 78 return JXL_DEC_ERROR; 79 } 80 } else { 81 // Not enough data, copy the data if we haven't already. 82 if (!old_data_exists) { 83 buffer_.insert(buffer_.end(), to_decode.data(), 84 to_decode.data() + to_decode.size()); 85 } 86 } 87 return JXL_DEC_NEED_MORE_INPUT; 88 } 89 90 size_t JxlToJpegDecoder::NumExifMarkers(const jpeg::JPEGData& jpeg_data) { 91 size_t num = 0; 92 for (size_t i = 0; i < jpeg_data.app_data.size(); ++i) { 93 if (jpeg_data.app_marker_type[i] == jxl::jpeg::AppMarkerType::kExif) { 94 num++; 95 } 96 } 97 return num; 98 } 99 100 size_t JxlToJpegDecoder::NumXmpMarkers(const jpeg::JPEGData& jpeg_data) { 101 size_t num = 0; 102 for (size_t i = 0; i < jpeg_data.app_data.size(); ++i) { 103 if (jpeg_data.app_marker_type[i] == jxl::jpeg::AppMarkerType::kXMP) { 104 num++; 105 } 106 } 107 return num; 108 } 109 110 JxlDecoderStatus JxlToJpegDecoder::ExifBoxContentSize( 111 const jpeg::JPEGData& jpeg_data, size_t* size) { 112 for (size_t i = 0; i < jpeg_data.app_data.size(); ++i) { 113 if (jpeg_data.app_marker_type[i] == jxl::jpeg::AppMarkerType::kExif) { 114 if (jpeg_data.app_data[i].size() < 3 + sizeof(jpeg::kExifTag)) { 115 // too small for app marker header 116 return JXL_DEC_ERROR; 117 } 118 // The first 4 bytes are the TIFF header from the box contents, and are 119 // not included in the JPEG 120 *size = jpeg_data.app_data[i].size() + 4 - 3 - sizeof(jpeg::kExifTag); 121 return JXL_DEC_SUCCESS; 122 } 123 } 124 return JXL_DEC_ERROR; 125 } 126 127 JxlDecoderStatus JxlToJpegDecoder::XmlBoxContentSize( 128 const jpeg::JPEGData& jpeg_data, size_t* size) { 129 for (size_t i = 0; i < jpeg_data.app_data.size(); ++i) { 130 if (jpeg_data.app_marker_type[i] == jxl::jpeg::AppMarkerType::kXMP) { 131 if (jpeg_data.app_data[i].size() < 3 + sizeof(jpeg::kXMPTag)) { 132 // too small for app marker header 133 return JXL_DEC_ERROR; 134 } 135 *size = jpeg_data.app_data[i].size() - 3 - sizeof(jpeg::kXMPTag); 136 return JXL_DEC_SUCCESS; 137 } 138 } 139 return JXL_DEC_ERROR; 140 } 141 142 JxlDecoderStatus JxlToJpegDecoder::SetExif(const uint8_t* data, size_t size, 143 jpeg::JPEGData* jpeg_data) { 144 for (size_t i = 0; i < jpeg_data->app_data.size(); ++i) { 145 if (jpeg_data->app_marker_type[i] == jxl::jpeg::AppMarkerType::kExif) { 146 if (jpeg_data->app_data[i].size() != 147 size + 3 + sizeof(jpeg::kExifTag) - 4) 148 return JXL_DEC_ERROR; 149 // The first 9 bytes are used for JPEG marker header. 150 jpeg_data->app_data[i][0] = 0xE1; 151 // The second and third byte are already filled in correctly 152 memcpy(jpeg_data->app_data[i].data() + 3, jpeg::kExifTag, 153 sizeof(jpeg::kExifTag)); 154 // The first 4 bytes are the TIFF header from the box contents, and are 155 // not included in the JPEG 156 memcpy(jpeg_data->app_data[i].data() + 3 + sizeof(jpeg::kExifTag), 157 data + 4, size - 4); 158 return JXL_DEC_SUCCESS; 159 } 160 } 161 return JXL_DEC_ERROR; 162 } 163 JxlDecoderStatus JxlToJpegDecoder::SetXmp(const uint8_t* data, size_t size, 164 jpeg::JPEGData* jpeg_data) { 165 for (size_t i = 0; i < jpeg_data->app_data.size(); ++i) { 166 if (jpeg_data->app_marker_type[i] == jxl::jpeg::AppMarkerType::kXMP) { 167 if (jpeg_data->app_data[i].size() != size + 3 + sizeof(jpeg::kXMPTag)) 168 return JXL_DEC_ERROR; 169 // The first 9 bytes are used for JPEG marker header. 170 jpeg_data->app_data[i][0] = 0xE1; 171 // The second and third byte are already filled in correctly 172 memcpy(jpeg_data->app_data[i].data() + 3, jpeg::kXMPTag, 173 sizeof(jpeg::kXMPTag)); 174 memcpy(jpeg_data->app_data[i].data() + 3 + sizeof(jpeg::kXMPTag), data, 175 size); 176 return JXL_DEC_SUCCESS; 177 } 178 } 179 return JXL_DEC_ERROR; 180 } 181 182 #endif // JPEGXL_ENABLE_TRANSCODE_JPEG 183 184 } // namespace jxl