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