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