/src/libjxl/lib/jxl/jpeg/jpeg_data.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/jpeg/jpeg_data.h" |
7 | | |
8 | | #include <jxl/types.h> |
9 | | |
10 | | #include <algorithm> |
11 | | #include <cstddef> |
12 | | #include <cstdint> |
13 | | #include <cstring> |
14 | | #include <hwy/base.h> |
15 | | #include <vector> |
16 | | |
17 | | #include "lib/jxl/base/common.h" |
18 | | #include "lib/jxl/base/printf_macros.h" |
19 | | #include "lib/jxl/base/status.h" |
20 | | #include "lib/jxl/common.h" // kMaxNumPasses, JPEGXL_ENABLE_TRANSCODE_JPEG |
21 | | #include "lib/jxl/field_encodings.h" |
22 | | #include "lib/jxl/fields.h" |
23 | | |
24 | | namespace jxl { |
25 | | namespace jpeg { |
26 | | |
27 | | #if JPEGXL_ENABLE_TRANSCODE_JPEG |
28 | | |
29 | | namespace { |
30 | | enum JPEGComponentType : uint32_t { |
31 | | kGray = 0, |
32 | | kYCbCr = 1, |
33 | | kRGB = 2, |
34 | | kCustom = 3, |
35 | | }; |
36 | | |
37 | | struct JPEGInfo { |
38 | | size_t num_app_markers = 0; |
39 | | size_t num_com_markers = 0; |
40 | | size_t num_scans = 0; |
41 | | size_t num_intermarker = 0; |
42 | | bool has_dri = false; |
43 | | }; |
44 | | |
45 | 0 | Status VisitMarker(uint8_t* marker, Visitor* visitor, JPEGInfo* info) { |
46 | 0 | uint32_t marker32 = *marker - 0xc0; |
47 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(6, 0x00, &marker32)); |
48 | 0 | *marker = marker32 + 0xc0; |
49 | 0 | if ((*marker & 0xf0) == 0xe0) { |
50 | 0 | info->num_app_markers++; |
51 | 0 | } |
52 | 0 | if (*marker == 0xfe) { |
53 | 0 | info->num_com_markers++; |
54 | 0 | } |
55 | 0 | if (*marker == 0xda) { |
56 | 0 | info->num_scans++; |
57 | 0 | } |
58 | | // We use a fake 0xff marker to signal intermarker data. |
59 | 0 | if (*marker == 0xff) { |
60 | 0 | info->num_intermarker++; |
61 | 0 | } |
62 | 0 | if (*marker == 0xdd) { |
63 | 0 | info->has_dri = true; |
64 | 0 | } |
65 | 0 | return true; |
66 | 0 | } |
67 | | |
68 | | } // namespace |
69 | | |
70 | 0 | Status JPEGData::VisitFields(Visitor* visitor) { |
71 | 0 | bool is_gray = components.size() == 1; |
72 | 0 | JXL_RETURN_IF_ERROR(visitor->Bool(false, &is_gray)); |
73 | 0 | if (visitor->IsReading()) { |
74 | 0 | components.resize(is_gray ? 1 : 3); |
75 | 0 | } |
76 | 0 | JPEGInfo info; |
77 | 0 | if (visitor->IsReading()) { |
78 | 0 | uint8_t marker = 0xc0; |
79 | 0 | do { |
80 | 0 | JXL_RETURN_IF_ERROR(VisitMarker(&marker, visitor, &info)); |
81 | 0 | marker_order.push_back(marker); |
82 | 0 | if (marker_order.size() > 16384) { |
83 | 0 | return JXL_FAILURE("Too many markers: %" PRIuS "\n", |
84 | 0 | marker_order.size()); |
85 | 0 | } |
86 | 0 | } while (marker != 0xd9); |
87 | 0 | } else { |
88 | 0 | if (marker_order.size() > 16384) { |
89 | 0 | return JXL_FAILURE("Too many markers: %" PRIuS "\n", marker_order.size()); |
90 | 0 | } |
91 | 0 | for (uint8_t& marker : marker_order) { |
92 | 0 | JXL_RETURN_IF_ERROR(VisitMarker(&marker, visitor, &info)); |
93 | 0 | } |
94 | 0 | if (!marker_order.empty()) { |
95 | | // Last marker should always be EOI marker. |
96 | 0 | JXL_ENSURE(marker_order.back() == 0xd9); |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | | // Size of the APP and COM markers. |
101 | 0 | if (visitor->IsReading()) { |
102 | 0 | app_data.resize(info.num_app_markers); |
103 | 0 | app_marker_type.resize(info.num_app_markers); |
104 | 0 | com_data.resize(info.num_com_markers); |
105 | 0 | scan_info.resize(info.num_scans); |
106 | 0 | } |
107 | 0 | JXL_ENSURE(app_data.size() == info.num_app_markers); |
108 | 0 | JXL_ENSURE(app_marker_type.size() == info.num_app_markers); |
109 | 0 | JXL_ENSURE(com_data.size() == info.num_com_markers); |
110 | 0 | JXL_ENSURE(scan_info.size() == info.num_scans); |
111 | 0 | for (size_t i = 0; i < app_data.size(); i++) { |
112 | 0 | auto& app = app_data[i]; |
113 | | // Encodes up to 8 different values. |
114 | 0 | JXL_RETURN_IF_ERROR( |
115 | 0 | visitor->U32(Val(0), Val(1), BitsOffset(1, 2), BitsOffset(2, 4), 0, |
116 | 0 | reinterpret_cast<uint32_t*>(&app_marker_type[i]))); |
117 | 0 | if (app_marker_type[i] != AppMarkerType::kUnknown && |
118 | 0 | app_marker_type[i] != AppMarkerType::kICC && |
119 | 0 | app_marker_type[i] != AppMarkerType::kExif && |
120 | 0 | app_marker_type[i] != AppMarkerType::kXMP) { |
121 | 0 | return JXL_FAILURE("Unknown app marker type %u", |
122 | 0 | static_cast<uint32_t>(app_marker_type[i])); |
123 | 0 | } |
124 | 0 | uint32_t len = app.size() - 1; |
125 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(16, 0, &len)); |
126 | 0 | if (visitor->IsReading()) app.resize(len + 1); |
127 | 0 | if (app.size() < 3) { |
128 | 0 | return JXL_FAILURE("Invalid marker size: %" PRIuS "\n", app.size()); |
129 | 0 | } |
130 | 0 | } |
131 | 0 | for (auto& com : com_data) { |
132 | 0 | uint32_t len = com.size() - 1; |
133 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(16, 0, &len)); |
134 | 0 | if (visitor->IsReading()) com.resize(len + 1); |
135 | 0 | if (com.size() < 3) { |
136 | 0 | return JXL_FAILURE("Invalid marker size: %" PRIuS "\n", com.size()); |
137 | 0 | } |
138 | 0 | } |
139 | | |
140 | 0 | uint32_t num_quant_tables = quant.size(); |
141 | 0 | JXL_RETURN_IF_ERROR( |
142 | 0 | visitor->U32(Val(1), Val(2), Val(3), Val(4), 2, &num_quant_tables)); |
143 | 0 | if (num_quant_tables == 4) { |
144 | 0 | return JXL_FAILURE("Invalid number of quant tables"); |
145 | 0 | } |
146 | 0 | if (visitor->IsReading()) { |
147 | 0 | quant.resize(num_quant_tables); |
148 | 0 | } |
149 | 0 | for (size_t i = 0; i < num_quant_tables; i++) { |
150 | 0 | if (quant[i].precision > 1) { |
151 | 0 | return JXL_FAILURE( |
152 | 0 | "Quant tables with more than 16 bits are not supported"); |
153 | 0 | } |
154 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(1, 0, &quant[i].precision)); |
155 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(2, i, &quant[i].index)); |
156 | 0 | JXL_RETURN_IF_ERROR(visitor->Bool(true, &quant[i].is_last)); |
157 | 0 | } |
158 | | |
159 | 0 | JPEGComponentType component_type = |
160 | 0 | components.size() == 1 && components[0].id == 1 ? JPEGComponentType::kGray |
161 | 0 | : components.size() == 3 && components[0].id == 1 && |
162 | 0 | components[1].id == 2 && components[2].id == 3 |
163 | 0 | ? JPEGComponentType::kYCbCr |
164 | 0 | : components.size() == 3 && components[0].id == 'R' && |
165 | 0 | components[1].id == 'G' && components[2].id == 'B' |
166 | 0 | ? JPEGComponentType::kRGB |
167 | 0 | : JPEGComponentType::kCustom; |
168 | 0 | JXL_RETURN_IF_ERROR( |
169 | 0 | visitor->Bits(2, JPEGComponentType::kYCbCr, |
170 | 0 | reinterpret_cast<uint32_t*>(&component_type))); |
171 | 0 | uint32_t num_components; |
172 | 0 | if (component_type == JPEGComponentType::kGray) { |
173 | 0 | num_components = 1; |
174 | 0 | } else if (component_type != JPEGComponentType::kCustom) { |
175 | 0 | num_components = 3; |
176 | 0 | } else { |
177 | 0 | num_components = components.size(); |
178 | 0 | JXL_RETURN_IF_ERROR( |
179 | 0 | visitor->U32(Val(1), Val(2), Val(3), Val(4), 3, &num_components)); |
180 | 0 | if (num_components != 1 && num_components != 3) { |
181 | 0 | return JXL_FAILURE("Invalid number of components: %u", num_components); |
182 | 0 | } |
183 | 0 | } |
184 | 0 | if (visitor->IsReading()) { |
185 | 0 | components.resize(num_components); |
186 | 0 | } |
187 | 0 | if (component_type == JPEGComponentType::kCustom) { |
188 | 0 | for (auto& component : components) { |
189 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(8, 0, &component.id)); |
190 | 0 | } |
191 | 0 | } else if (component_type == JPEGComponentType::kGray) { |
192 | 0 | components[0].id = 1; |
193 | 0 | } else if (component_type == JPEGComponentType::kRGB) { |
194 | 0 | components[0].id = 'R'; |
195 | 0 | components[1].id = 'G'; |
196 | 0 | components[2].id = 'B'; |
197 | 0 | } else { |
198 | 0 | components[0].id = 1; |
199 | 0 | components[1].id = 2; |
200 | 0 | components[2].id = 3; |
201 | 0 | } |
202 | 0 | size_t used_tables = 0; |
203 | 0 | for (size_t i = 0; i < components.size(); i++) { |
204 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(2, 0, &components[i].quant_idx)); |
205 | 0 | if (components[i].quant_idx >= quant.size()) { |
206 | 0 | return JXL_FAILURE("Invalid quant table for component %" PRIuS ": %u\n", |
207 | 0 | i, components[i].quant_idx); |
208 | 0 | } |
209 | 0 | used_tables |= 1U << components[i].quant_idx; |
210 | 0 | } |
211 | 0 | for (size_t i = 0; i < quant.size(); i++) { |
212 | 0 | if (used_tables & (1 << i)) continue; |
213 | 0 | if (i == 0) return JXL_FAILURE("First quant table unused."); |
214 | | // Unused quant table has to be set to copy of previous quant table |
215 | 0 | for (size_t j = 0; j < 64; j++) { |
216 | 0 | if (quant[i].values[j] != quant[i - 1].values[j]) { |
217 | 0 | return JXL_FAILURE("Non-trivial unused quant table"); |
218 | 0 | } |
219 | 0 | } |
220 | 0 | } |
221 | | |
222 | 0 | uint32_t num_huff = huffman_code.size(); |
223 | 0 | JXL_RETURN_IF_ERROR(visitor->U32(Val(4), BitsOffset(3, 2), BitsOffset(4, 10), |
224 | 0 | BitsOffset(6, 26), 4, &num_huff)); |
225 | 0 | if (visitor->IsReading()) { |
226 | 0 | huffman_code.resize(num_huff); |
227 | 0 | } |
228 | 0 | for (JPEGHuffmanCode& hc : huffman_code) { |
229 | 0 | bool is_ac = ((hc.slot_id >> 4) != 0); |
230 | 0 | uint32_t id = hc.slot_id & 0xF; |
231 | 0 | JXL_RETURN_IF_ERROR(visitor->Bool(false, &is_ac)); |
232 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(2, 0, &id)); |
233 | 0 | hc.slot_id = (static_cast<uint32_t>(is_ac) << 4) | id; |
234 | 0 | JXL_RETURN_IF_ERROR(visitor->Bool(true, &hc.is_last)); |
235 | 0 | size_t num_symbols = 0; |
236 | 0 | for (size_t i = 0; i <= 16; i++) { |
237 | 0 | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), Val(1), BitsOffset(3, 2), |
238 | 0 | Bits(8), 0, &hc.counts[i])); |
239 | 0 | num_symbols += hc.counts[i]; |
240 | 0 | } |
241 | 0 | if (num_symbols == 0) { |
242 | | // Actually, at least 2 symbols are required, since one of them is EOI. |
243 | | // This case is used to represent an empty DHT marker. |
244 | 0 | continue; |
245 | 0 | } |
246 | 0 | if (num_symbols > hc.values.size()) { |
247 | 0 | return JXL_FAILURE("Huffman code too large (%" PRIuS ")", num_symbols); |
248 | 0 | } |
249 | | // Presence flags for 4 * 64 + 1 values. |
250 | 0 | uint64_t value_slots[5] = {}; |
251 | 0 | for (size_t i = 0; i < num_symbols; i++) { |
252 | | // Goes up to 256, included. Might have the same symbol appear twice... |
253 | 0 | JXL_RETURN_IF_ERROR(visitor->U32(Bits(2), BitsOffset(2, 4), |
254 | 0 | BitsOffset(4, 8), BitsOffset(8, 1), 0, |
255 | 0 | &hc.values[i])); |
256 | 0 | value_slots[hc.values[i] >> 6] |= static_cast<uint64_t>(1) |
257 | 0 | << (hc.values[i] & 0x3F); |
258 | 0 | } |
259 | 0 | if (hc.values[num_symbols - 1] != kJpegHuffmanAlphabetSize) { |
260 | 0 | return JXL_FAILURE("Missing EOI symbol"); |
261 | 0 | } |
262 | | // Last element, denoting EOI, have to be 1 after the loop. |
263 | 0 | JXL_ENSURE(value_slots[4] == 1); |
264 | 0 | size_t num_values = 1; |
265 | 0 | for (size_t i = 0; i < 4; ++i) num_values += hwy::PopCount(value_slots[i]); |
266 | 0 | if (num_values != num_symbols) { |
267 | 0 | return JXL_FAILURE("Duplicate Huffman symbols"); |
268 | 0 | } |
269 | 0 | if (!is_ac) { |
270 | 0 | bool only_dc = ((value_slots[0] >> kJpegDCAlphabetSize) | value_slots[1] | |
271 | 0 | value_slots[2] | value_slots[3]) == 0; |
272 | 0 | if (!only_dc) return JXL_FAILURE("Huffman symbols out of DC range"); |
273 | 0 | } |
274 | 0 | } |
275 | | |
276 | 0 | for (auto& scan : scan_info) { |
277 | 0 | JXL_RETURN_IF_ERROR( |
278 | 0 | visitor->U32(Val(1), Val(2), Val(3), Val(4), 1, &scan.num_components)); |
279 | 0 | if (scan.num_components >= 4) { |
280 | 0 | return JXL_FAILURE("Invalid number of components in SOS marker"); |
281 | 0 | } |
282 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(6, 0, &scan.Ss)); |
283 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(6, 63, &scan.Se)); |
284 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(4, 0, &scan.Al)); |
285 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(4, 0, &scan.Ah)); |
286 | 0 | for (size_t i = 0; i < scan.num_components; i++) { |
287 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(2, 0, &scan.components[i].comp_idx)); |
288 | 0 | if (scan.components[i].comp_idx >= components.size()) { |
289 | 0 | return JXL_FAILURE("Invalid component idx in SOS marker"); |
290 | 0 | } |
291 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(2, 0, &scan.components[i].ac_tbl_idx)); |
292 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(2, 0, &scan.components[i].dc_tbl_idx)); |
293 | 0 | } |
294 | | // TODO(veluca): actually set and use this value. |
295 | 0 | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), Val(1), Val(2), BitsOffset(3, 3), |
296 | 0 | kMaxNumPasses - 1, |
297 | 0 | &scan.last_needed_pass)); |
298 | 0 | } |
299 | | |
300 | | // From here on, this is data that is not strictly necessary to get a valid |
301 | | // JPEG, but necessary for bit-exact JPEG reconstruction. |
302 | 0 | if (info.has_dri) { |
303 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(16, 0, &restart_interval)); |
304 | 0 | } |
305 | | |
306 | 0 | for (auto& scan : scan_info) { |
307 | 0 | uint32_t num_reset_points = scan.reset_points.size(); |
308 | 0 | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), BitsOffset(2, 1), BitsOffset(4, 4), |
309 | 0 | BitsOffset(16, 20), 0, &num_reset_points)); |
310 | 0 | if (visitor->IsReading()) { |
311 | 0 | scan.reset_points.resize(num_reset_points); |
312 | 0 | } |
313 | 0 | int last_block_idx = -1; |
314 | 0 | for (auto& block_idx : scan.reset_points) { |
315 | 0 | block_idx -= last_block_idx + 1; |
316 | 0 | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), BitsOffset(3, 1), |
317 | 0 | BitsOffset(5, 9), BitsOffset(28, 41), 0, |
318 | 0 | &block_idx)); |
319 | 0 | block_idx += last_block_idx + 1; |
320 | 0 | if (block_idx >= (3u << 26)) { |
321 | | // At most 8K x 8K x num_channels blocks are possible in a JPEG. |
322 | | // So valid block indices are below 3 * 2^26. |
323 | 0 | return JXL_FAILURE("Invalid block ID: %u", block_idx); |
324 | 0 | } |
325 | 0 | last_block_idx = block_idx; |
326 | 0 | } |
327 | | |
328 | 0 | uint32_t num_extra_zero_runs = scan.extra_zero_runs.size(); |
329 | 0 | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), BitsOffset(2, 1), BitsOffset(4, 4), |
330 | 0 | BitsOffset(16, 20), 0, |
331 | 0 | &num_extra_zero_runs)); |
332 | 0 | if (visitor->IsReading()) { |
333 | 0 | scan.extra_zero_runs.resize(num_extra_zero_runs); |
334 | 0 | } |
335 | 0 | last_block_idx = -1; |
336 | 0 | for (auto& extra_zero_run : scan.extra_zero_runs) { |
337 | 0 | uint32_t& block_idx = extra_zero_run.block_idx; |
338 | 0 | JXL_RETURN_IF_ERROR(visitor->U32(Val(1), BitsOffset(2, 2), |
339 | 0 | BitsOffset(4, 5), BitsOffset(8, 20), 1, |
340 | 0 | &extra_zero_run.num_extra_zero_runs)); |
341 | 0 | block_idx -= last_block_idx + 1; |
342 | 0 | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), BitsOffset(3, 1), |
343 | 0 | BitsOffset(5, 9), BitsOffset(28, 41), 0, |
344 | 0 | &block_idx)); |
345 | 0 | block_idx += last_block_idx + 1; |
346 | 0 | if (block_idx > (3u << 26)) { |
347 | 0 | return JXL_FAILURE("Invalid block ID: %u", block_idx); |
348 | 0 | } |
349 | 0 | last_block_idx = block_idx; |
350 | 0 | } |
351 | 0 | } |
352 | 0 | std::vector<uint32_t> inter_marker_data_sizes; |
353 | 0 | inter_marker_data_sizes.reserve(info.num_intermarker); |
354 | 0 | for (size_t i = 0; i < info.num_intermarker; ++i) { |
355 | 0 | uint32_t len = visitor->IsReading() ? 0 : inter_marker_data[i].size(); |
356 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(16, 0, &len)); |
357 | 0 | if (visitor->IsReading()) inter_marker_data_sizes.emplace_back(len); |
358 | 0 | } |
359 | 0 | uint32_t tail_data_len = tail_data.size(); |
360 | 0 | if (!visitor->IsReading() && tail_data_len > 4260096) { |
361 | 0 | return JXL_FAILURE("Tail data too large (max size = 4260096, size = %u)", |
362 | 0 | tail_data_len); |
363 | 0 | } |
364 | 0 | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), BitsOffset(8, 1), |
365 | 0 | BitsOffset(16, 257), BitsOffset(22, 65793), |
366 | 0 | 0, &tail_data_len)); |
367 | | |
368 | 0 | JXL_RETURN_IF_ERROR(visitor->Bool(false, &has_zero_padding_bit)); |
369 | 0 | if (has_zero_padding_bit) { |
370 | 0 | uint32_t nbit = padding_bits.size(); |
371 | 0 | JXL_RETURN_IF_ERROR(visitor->Bits(24, 0, &nbit)); |
372 | 0 | if (visitor->IsReading()) { |
373 | 0 | JXL_RETURN_IF_ERROR(CheckHasEnoughBits(visitor, nbit)); |
374 | 0 | padding_bits.reserve(std::min<uint32_t>(1024u, nbit)); |
375 | 0 | for (uint32_t i = 0; i < nbit; i++) { |
376 | 0 | bool bbit = false; |
377 | 0 | JXL_RETURN_IF_ERROR(visitor->Bool(false, &bbit)); |
378 | 0 | padding_bits.push_back(TO_JXL_BOOL(bbit)); |
379 | 0 | } |
380 | 0 | } else { |
381 | 0 | for (uint8_t& bit : padding_bits) { |
382 | 0 | bool bbit = FROM_JXL_BOOL(bit); |
383 | 0 | JXL_RETURN_IF_ERROR(visitor->Bool(false, &bbit)); |
384 | 0 | bit = TO_JXL_BOOL(bbit); |
385 | 0 | } |
386 | 0 | } |
387 | 0 | } |
388 | | |
389 | 0 | { |
390 | 0 | size_t dht_index = 0; |
391 | 0 | size_t scan_index = 0; |
392 | 0 | bool is_progressive = false; |
393 | 0 | bool ac_ok[kMaxHuffmanTables] = {false}; |
394 | 0 | bool dc_ok[kMaxHuffmanTables] = {false}; |
395 | 0 | for (uint8_t marker : marker_order) { |
396 | 0 | if (marker == 0xC2) { |
397 | 0 | is_progressive = true; |
398 | 0 | } else if (marker == 0xC4) { |
399 | 0 | for (; dht_index < huffman_code.size();) { |
400 | 0 | const JPEGHuffmanCode& huff = huffman_code[dht_index++]; |
401 | 0 | size_t index = huff.slot_id; |
402 | 0 | if (index & 0x10) { |
403 | 0 | index -= 0x10; |
404 | 0 | ac_ok[index] = true; |
405 | 0 | } else { |
406 | 0 | dc_ok[index] = true; |
407 | 0 | } |
408 | 0 | if (huff.is_last) break; |
409 | 0 | } |
410 | 0 | } else if (marker == 0xDA) { |
411 | 0 | const JPEGScanInfo& si = scan_info[scan_index++]; |
412 | 0 | for (size_t i = 0; i < si.num_components; ++i) { |
413 | 0 | const JPEGComponentScanInfo& csi = si.components[i]; |
414 | 0 | size_t dc_tbl_idx = csi.dc_tbl_idx; |
415 | 0 | size_t ac_tbl_idx = csi.ac_tbl_idx; |
416 | 0 | bool want_dc = !is_progressive || (si.Ss == 0); |
417 | 0 | if (want_dc && !dc_ok[dc_tbl_idx]) { |
418 | 0 | return JXL_FAILURE("DC Huffman table used before defined"); |
419 | 0 | } |
420 | 0 | bool want_ac = !is_progressive || (si.Ss != 0) || (si.Se != 0); |
421 | 0 | if (want_ac && !ac_ok[ac_tbl_idx]) { |
422 | 0 | return JXL_FAILURE("AC Huffman table used before defined"); |
423 | 0 | } |
424 | 0 | } |
425 | 0 | } |
426 | 0 | } |
427 | 0 | } |
428 | | |
429 | | // Apply postponed actions. |
430 | 0 | if (visitor->IsReading()) { |
431 | 0 | tail_data.resize(tail_data_len); |
432 | 0 | JXL_ENSURE(inter_marker_data_sizes.size() == info.num_intermarker); |
433 | 0 | inter_marker_data.reserve(info.num_intermarker); |
434 | 0 | for (size_t i = 0; i < info.num_intermarker; ++i) { |
435 | 0 | inter_marker_data.emplace_back(inter_marker_data_sizes[i]); |
436 | 0 | } |
437 | 0 | } |
438 | | |
439 | 0 | return true; |
440 | 0 | } |
441 | | |
442 | | #endif // JPEGXL_ENABLE_TRANSCODE_JPEG |
443 | | |
444 | | void JPEGData::CalculateMcuSize(const JPEGScanInfo& scan, int* MCUs_per_row, |
445 | 0 | int* MCU_rows) const { |
446 | 0 | const bool is_interleaved = (scan.num_components > 1); |
447 | 0 | const JPEGComponent& base_component = components[scan.components[0].comp_idx]; |
448 | | // h_group / v_group act as numerators for converting number of blocks to |
449 | | // number of MCU. In interleaved mode it is 1, so MCU is represented with |
450 | | // max_*_samp_factor blocks. In non-interleaved mode we choose numerator to |
451 | | // be the samping factor, consequently MCU is always represented with single |
452 | | // block. |
453 | 0 | const int h_group = is_interleaved ? 1 : base_component.h_samp_factor; |
454 | 0 | const int v_group = is_interleaved ? 1 : base_component.v_samp_factor; |
455 | 0 | int max_h_samp_factor = 1; |
456 | 0 | int max_v_samp_factor = 1; |
457 | 0 | for (const auto& c : components) { |
458 | 0 | max_h_samp_factor = std::max(c.h_samp_factor, max_h_samp_factor); |
459 | 0 | max_v_samp_factor = std::max(c.v_samp_factor, max_v_samp_factor); |
460 | 0 | } |
461 | 0 | *MCUs_per_row = DivCeil(width * h_group, 8 * max_h_samp_factor); |
462 | 0 | *MCU_rows = DivCeil(height * v_group, 8 * max_v_samp_factor); |
463 | 0 | } |
464 | | |
465 | | #if JPEGXL_ENABLE_TRANSCODE_JPEG |
466 | | |
467 | | Status SetJPEGDataFromICC(const std::vector<uint8_t>& icc, |
468 | 0 | jpeg::JPEGData* jpeg_data) { |
469 | 0 | size_t icc_pos = 0; |
470 | 0 | for (size_t i = 0; i < jpeg_data->app_data.size(); i++) { |
471 | 0 | if (jpeg_data->app_marker_type[i] != jpeg::AppMarkerType::kICC) { |
472 | 0 | continue; |
473 | 0 | } |
474 | 0 | size_t len = jpeg_data->app_data[i].size() - 17; |
475 | 0 | if (icc_pos + len > icc.size()) { |
476 | 0 | return JXL_FAILURE( |
477 | 0 | "ICC length is less than APP markers: requested %" PRIuS |
478 | 0 | " more bytes, " |
479 | 0 | "%" PRIuS " available", |
480 | 0 | len, icc.size() - icc_pos); |
481 | 0 | } |
482 | 0 | memcpy(&jpeg_data->app_data[i][17], icc.data() + icc_pos, len); |
483 | 0 | icc_pos += len; |
484 | 0 | } |
485 | 0 | if (icc_pos != icc.size() && icc_pos != 0) { |
486 | 0 | return JXL_FAILURE("ICC length is more than APP markers"); |
487 | 0 | } |
488 | 0 | return true; |
489 | 0 | } |
490 | | |
491 | | #endif // JPEGXL_ENABLE_TRANSCODE_JPEG |
492 | | |
493 | | } // namespace jpeg |
494 | | } // namespace jxl |