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