/src/libjxl/lib/jxl/jpeg/jpeg_data.cc
Line | Count | Source |
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 | 1.60M | Status VisitMarker(uint8_t* marker, Visitor* visitor, JPEGInfo* info) { |
46 | 1.60M | uint32_t marker32 = *marker - 0xc0; |
47 | 1.60M | JXL_RETURN_IF_ERROR(visitor->Bits(6, 0x00, &marker32)); |
48 | 1.59M | *marker = marker32 + 0xc0; |
49 | 1.59M | if ((*marker & 0xf0) == 0xe0) { |
50 | 280k | info->num_app_markers++; |
51 | 280k | } |
52 | 1.59M | if (*marker == 0xfe) { |
53 | 85.7k | info->num_com_markers++; |
54 | 85.7k | } |
55 | 1.59M | if (*marker == 0xda) { |
56 | 77.8k | info->num_scans++; |
57 | 77.8k | } |
58 | | // We use a fake 0xff marker to signal intermarker data. |
59 | 1.59M | if (*marker == 0xff) { |
60 | 285k | info->num_intermarker++; |
61 | 285k | } |
62 | 1.59M | if (*marker == 0xdd) { |
63 | 25.3k | info->has_dri = true; |
64 | 25.3k | } |
65 | 1.59M | return true; |
66 | 1.60M | } |
67 | | |
68 | | } // namespace |
69 | | |
70 | 17.2k | Status JPEGData::VisitFields(Visitor* visitor) { |
71 | 17.2k | bool is_gray = components.size() == 1; |
72 | 17.2k | JXL_RETURN_IF_ERROR(visitor->Bool(false, &is_gray)); |
73 | 16.9k | if (visitor->IsReading()) { |
74 | 16.9k | components.resize(is_gray ? 1 : 3); |
75 | 16.9k | } |
76 | 16.9k | JPEGInfo info; |
77 | 16.9k | if (visitor->IsReading()) { |
78 | 16.9k | uint8_t marker = 0xc0; |
79 | 1.60M | do { |
80 | 1.60M | JXL_RETURN_IF_ERROR(VisitMarker(&marker, visitor, &info)); |
81 | 1.59M | marker_order.push_back(marker); |
82 | 1.59M | if (marker_order.size() > 16384) { |
83 | 1 | return JXL_FAILURE("Too many markers: %" PRIuS "\n", |
84 | 1 | marker_order.size()); |
85 | 1 | } |
86 | 1.59M | } while (marker != 0xd9); |
87 | 16.9k | } 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 | 12.8k | if (visitor->IsReading()) { |
102 | 12.8k | app_data.resize(info.num_app_markers); |
103 | 12.8k | app_marker_type.resize(info.num_app_markers); |
104 | 12.8k | com_data.resize(info.num_com_markers); |
105 | 12.8k | scan_info.resize(info.num_scans); |
106 | 12.8k | } |
107 | 12.8k | JXL_ENSURE(app_data.size() == info.num_app_markers); |
108 | 12.8k | JXL_ENSURE(app_marker_type.size() == info.num_app_markers); |
109 | 12.8k | JXL_ENSURE(com_data.size() == info.num_com_markers); |
110 | 12.8k | JXL_ENSURE(scan_info.size() == info.num_scans); |
111 | 29.3k | for (size_t i = 0; i < app_data.size(); i++) { |
112 | 17.4k | auto& app = app_data[i]; |
113 | | // Encodes up to 8 different values. |
114 | 17.4k | JXL_RETURN_IF_ERROR( |
115 | 17.4k | visitor->U32(Val(0), Val(1), BitsOffset(1, 2), BitsOffset(2, 4), 0, |
116 | 17.4k | reinterpret_cast<uint32_t*>(&app_marker_type[i]))); |
117 | 17.3k | if (app_marker_type[i] != AppMarkerType::kUnknown && |
118 | 9.48k | app_marker_type[i] != AppMarkerType::kICC && |
119 | 4.41k | app_marker_type[i] != AppMarkerType::kExif && |
120 | 2.05k | app_marker_type[i] != AppMarkerType::kXMP) { |
121 | 21 | return JXL_FAILURE("Unknown app marker type %u", |
122 | 21 | static_cast<uint32_t>(app_marker_type[i])); |
123 | 21 | } |
124 | 17.3k | uint32_t len = app.size() - 1; |
125 | 17.3k | JXL_RETURN_IF_ERROR(visitor->Bits(16, 0, &len)); |
126 | 16.5k | if (visitor->IsReading()) app.resize(len + 1); |
127 | 16.5k | if (app.size() < 3) { |
128 | 3 | return JXL_FAILURE("Invalid marker size: %" PRIuS "\n", app.size()); |
129 | 3 | } |
130 | 16.5k | } |
131 | 11.8k | for (auto& com : com_data) { |
132 | 5.61k | uint32_t len = com.size() - 1; |
133 | 5.61k | JXL_RETURN_IF_ERROR(visitor->Bits(16, 0, &len)); |
134 | 5.43k | if (visitor->IsReading()) com.resize(len + 1); |
135 | 5.43k | if (com.size() < 3) { |
136 | 4 | return JXL_FAILURE("Invalid marker size: %" PRIuS "\n", com.size()); |
137 | 4 | } |
138 | 5.43k | } |
139 | | |
140 | 11.6k | uint32_t num_quant_tables = quant.size(); |
141 | 11.6k | JXL_RETURN_IF_ERROR( |
142 | 11.6k | visitor->U32(Val(1), Val(2), Val(3), Val(4), 2, &num_quant_tables)); |
143 | 11.5k | if (num_quant_tables == 4) { |
144 | 18 | return JXL_FAILURE("Invalid number of quant tables"); |
145 | 18 | } |
146 | 11.5k | if (visitor->IsReading()) { |
147 | 11.5k | quant.resize(num_quant_tables); |
148 | 11.5k | } |
149 | 29.9k | for (size_t i = 0; i < num_quant_tables; i++) { |
150 | 18.6k | 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 | 18.6k | JXL_RETURN_IF_ERROR(visitor->Bits(1, 0, &quant[i].precision)); |
155 | 18.6k | JXL_RETURN_IF_ERROR(visitor->Bits(2, i, &quant[i].index)); |
156 | 18.4k | JXL_RETURN_IF_ERROR(visitor->Bool(true, &quant[i].is_last)); |
157 | 18.4k | } |
158 | | |
159 | 11.2k | JPEGComponentType component_type = |
160 | 11.2k | components.size() == 1 && components[0].id == 1 ? JPEGComponentType::kGray |
161 | 11.2k | : components.size() == 3 && components[0].id == 1 && |
162 | 0 | components[1].id == 2 && components[2].id == 3 |
163 | 11.2k | ? JPEGComponentType::kYCbCr |
164 | 11.2k | : components.size() == 3 && components[0].id == 'R' && |
165 | 0 | components[1].id == 'G' && components[2].id == 'B' |
166 | 11.2k | ? JPEGComponentType::kRGB |
167 | 11.2k | : JPEGComponentType::kCustom; |
168 | 11.2k | JXL_RETURN_IF_ERROR( |
169 | 11.2k | visitor->Bits(2, JPEGComponentType::kYCbCr, |
170 | 11.2k | reinterpret_cast<uint32_t*>(&component_type))); |
171 | 11.2k | uint32_t num_components; |
172 | 11.2k | if (component_type == JPEGComponentType::kGray) { |
173 | 7.13k | num_components = 1; |
174 | 7.13k | } else if (component_type != JPEGComponentType::kCustom) { |
175 | 3.14k | num_components = 3; |
176 | 3.14k | } else { |
177 | 944 | num_components = components.size(); |
178 | 944 | JXL_RETURN_IF_ERROR( |
179 | 944 | visitor->U32(Val(1), Val(2), Val(3), Val(4), 3, &num_components)); |
180 | 926 | if (num_components != 1 && num_components != 3) { |
181 | 4 | return JXL_FAILURE("Invalid number of components: %u", num_components); |
182 | 4 | } |
183 | 926 | } |
184 | 11.1k | if (visitor->IsReading()) { |
185 | 11.1k | components.resize(num_components); |
186 | 11.1k | } |
187 | 11.1k | if (component_type == JPEGComponentType::kCustom) { |
188 | 2.14k | for (auto& component : components) { |
189 | 2.14k | JXL_RETURN_IF_ERROR(visitor->Bits(8, 0, &component.id)); |
190 | 2.14k | } |
191 | 10.2k | } else if (component_type == JPEGComponentType::kGray) { |
192 | 7.13k | components[0].id = 1; |
193 | 7.13k | } else if (component_type == JPEGComponentType::kRGB) { |
194 | 2.14k | components[0].id = 'R'; |
195 | 2.14k | components[1].id = 'G'; |
196 | 2.14k | components[2].id = 'B'; |
197 | 2.14k | } else { |
198 | 994 | components[0].id = 1; |
199 | 994 | components[1].id = 2; |
200 | 994 | components[2].id = 3; |
201 | 994 | } |
202 | 11.1k | size_t used_tables = 0; |
203 | 29.6k | for (size_t i = 0; i < components.size(); i++) { |
204 | 18.5k | JXL_RETURN_IF_ERROR(visitor->Bits(2, 0, &components[i].quant_idx)); |
205 | 18.4k | if (components[i].quant_idx >= quant.size()) { |
206 | 14 | return JXL_FAILURE("Invalid quant table for component %" PRIuS ": %u\n", |
207 | 14 | i, components[i].quant_idx); |
208 | 14 | } |
209 | 18.4k | used_tables |= 1U << components[i].quant_idx; |
210 | 18.4k | } |
211 | 28.8k | for (size_t i = 0; i < quant.size(); i++) { |
212 | 17.8k | if (used_tables & (1 << i)) continue; |
213 | 2.29k | 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 | 148k | for (size_t j = 0; j < 64; j++) { |
216 | 146k | if (quant[i].values[j] != quant[i - 1].values[j]) { |
217 | 0 | return JXL_FAILURE("Non-trivial unused quant table"); |
218 | 0 | } |
219 | 146k | } |
220 | 2.29k | } |
221 | | |
222 | 11.0k | uint32_t num_huff = huffman_code.size(); |
223 | 11.0k | JXL_RETURN_IF_ERROR(visitor->U32(Val(4), BitsOffset(3, 2), BitsOffset(4, 10), |
224 | 11.0k | BitsOffset(6, 26), 4, &num_huff)); |
225 | 10.8k | if (visitor->IsReading()) { |
226 | 10.8k | huffman_code.resize(num_huff); |
227 | 10.8k | } |
228 | 45.6k | for (JPEGHuffmanCode& hc : huffman_code) { |
229 | 45.6k | bool is_ac = ((hc.slot_id >> 4) != 0); |
230 | 45.6k | uint32_t id = hc.slot_id & 0xF; |
231 | 45.6k | JXL_RETURN_IF_ERROR(visitor->Bool(false, &is_ac)); |
232 | 45.6k | JXL_RETURN_IF_ERROR(visitor->Bits(2, 0, &id)); |
233 | 45.4k | hc.slot_id = (static_cast<uint32_t>(is_ac) << 4) | id; |
234 | 45.4k | JXL_RETURN_IF_ERROR(visitor->Bool(true, &hc.is_last)); |
235 | 45.2k | size_t num_symbols = 0; |
236 | 781k | for (size_t i = 0; i <= 16; i++) { |
237 | 740k | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), Val(1), BitsOffset(3, 2), |
238 | 740k | Bits(8), 0, &hc.counts[i])); |
239 | 736k | num_symbols += hc.counts[i]; |
240 | 736k | } |
241 | 41.7k | 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 | 34.3k | continue; |
245 | 34.3k | } |
246 | 7.34k | if (num_symbols > hc.values.size()) { |
247 | 25 | return JXL_FAILURE("Huffman code too large (%" PRIuS ")", num_symbols); |
248 | 25 | } |
249 | | // Presence flags for 4 * 64 + 1 values. |
250 | 7.32k | uint64_t value_slots[5] = {}; |
251 | 103k | for (size_t i = 0; i < num_symbols; i++) { |
252 | | // Goes up to 256, included. Might have the same symbol appear twice... |
253 | 97.6k | JXL_RETURN_IF_ERROR(visitor->U32(Bits(2), BitsOffset(2, 4), |
254 | 97.6k | BitsOffset(4, 8), BitsOffset(8, 1), 0, |
255 | 97.6k | &hc.values[i])); |
256 | 96.2k | value_slots[hc.values[i] >> 6] |= static_cast<uint64_t>(1) |
257 | 96.2k | << (hc.values[i] & 0x3F); |
258 | 96.2k | } |
259 | 5.93k | if (hc.values[num_symbols - 1] != kJpegHuffmanAlphabetSize) { |
260 | 19 | return JXL_FAILURE("Missing EOI symbol"); |
261 | 19 | } |
262 | | // Last element, denoting EOI, have to be 1 after the loop. |
263 | 5.91k | JXL_ENSURE(value_slots[4] == 1); |
264 | 5.91k | size_t num_values = 1; |
265 | 29.5k | for (size_t i = 0; i < 4; ++i) num_values += hwy::PopCount(value_slots[i]); |
266 | 5.91k | if (num_values != num_symbols) { |
267 | 4 | return JXL_FAILURE("Duplicate Huffman symbols"); |
268 | 4 | } |
269 | 5.90k | if (!is_ac) { |
270 | 3.14k | bool only_dc = ((value_slots[0] >> kJpegDCAlphabetSize) | value_slots[1] | |
271 | 3.14k | value_slots[2] | value_slots[3]) == 0; |
272 | 3.14k | if (!only_dc) return JXL_FAILURE("Huffman symbols out of DC range"); |
273 | 3.14k | } |
274 | 5.90k | } |
275 | | |
276 | 10.8k | for (auto& scan : scan_info) { |
277 | 10.8k | JXL_RETURN_IF_ERROR( |
278 | 10.8k | visitor->U32(Val(1), Val(2), Val(3), Val(4), 1, &scan.num_components)); |
279 | 10.7k | if (scan.num_components >= 4) { |
280 | 2 | return JXL_FAILURE("Invalid number of components in SOS marker"); |
281 | 2 | } |
282 | 10.7k | JXL_RETURN_IF_ERROR(visitor->Bits(6, 0, &scan.Ss)); |
283 | 10.6k | JXL_RETURN_IF_ERROR(visitor->Bits(6, 63, &scan.Se)); |
284 | 10.4k | JXL_RETURN_IF_ERROR(visitor->Bits(4, 0, &scan.Al)); |
285 | 10.3k | JXL_RETURN_IF_ERROR(visitor->Bits(4, 0, &scan.Ah)); |
286 | 24.3k | for (size_t i = 0; i < scan.num_components; i++) { |
287 | 14.2k | JXL_RETURN_IF_ERROR(visitor->Bits(2, 0, &scan.components[i].comp_idx)); |
288 | 14.2k | if (scan.components[i].comp_idx >= components.size()) { |
289 | 19 | return JXL_FAILURE("Invalid component idx in SOS marker"); |
290 | 19 | } |
291 | 14.2k | JXL_RETURN_IF_ERROR(visitor->Bits(2, 0, &scan.components[i].ac_tbl_idx)); |
292 | 14.1k | JXL_RETURN_IF_ERROR(visitor->Bits(2, 0, &scan.components[i].dc_tbl_idx)); |
293 | 14.1k | } |
294 | | // TODO(veluca): actually set and use this value. |
295 | 10.0k | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), Val(1), Val(2), BitsOffset(3, 3), |
296 | 10.0k | kMaxNumPasses - 1, |
297 | 10.0k | &scan.last_needed_pass)); |
298 | 10.0k | } |
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 | 4.58k | if (info.has_dri) { |
303 | 1.02k | JXL_RETURN_IF_ERROR(visitor->Bits(16, 0, &restart_interval)); |
304 | 1.02k | } |
305 | | |
306 | 6.58k | for (auto& scan : scan_info) { |
307 | 6.58k | uint32_t num_reset_points = scan.reset_points.size(); |
308 | 6.58k | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), BitsOffset(2, 1), BitsOffset(4, 4), |
309 | 6.58k | BitsOffset(16, 20), 0, &num_reset_points)); |
310 | 6.53k | if (visitor->IsReading()) { |
311 | 6.53k | scan.reset_points.resize(num_reset_points); |
312 | 6.53k | } |
313 | 6.53k | int last_block_idx = -1; |
314 | 61.8k | for (auto& block_idx : scan.reset_points) { |
315 | 61.8k | block_idx -= last_block_idx + 1; |
316 | 61.8k | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), BitsOffset(3, 1), |
317 | 61.8k | BitsOffset(5, 9), BitsOffset(28, 41), 0, |
318 | 61.8k | &block_idx)); |
319 | 61.5k | block_idx += last_block_idx + 1; |
320 | 61.5k | 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 | 4 | return JXL_FAILURE("Invalid block ID: %u", block_idx); |
324 | 4 | } |
325 | 61.5k | last_block_idx = block_idx; |
326 | 61.5k | } |
327 | | |
328 | 6.16k | uint32_t num_extra_zero_runs = scan.extra_zero_runs.size(); |
329 | 6.16k | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), BitsOffset(2, 1), BitsOffset(4, 4), |
330 | 6.16k | BitsOffset(16, 20), 0, |
331 | 6.16k | &num_extra_zero_runs)); |
332 | 6.08k | if (visitor->IsReading()) { |
333 | 6.08k | scan.extra_zero_runs.resize(num_extra_zero_runs); |
334 | 6.08k | } |
335 | 6.08k | last_block_idx = -1; |
336 | 41.1k | for (auto& extra_zero_run : scan.extra_zero_runs) { |
337 | 41.1k | uint32_t& block_idx = extra_zero_run.block_idx; |
338 | 41.1k | JXL_RETURN_IF_ERROR(visitor->U32(Val(1), BitsOffset(2, 2), |
339 | 41.1k | BitsOffset(4, 5), BitsOffset(8, 20), 1, |
340 | 41.1k | &extra_zero_run.num_extra_zero_runs)); |
341 | 40.9k | block_idx -= last_block_idx + 1; |
342 | 40.9k | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), BitsOffset(3, 1), |
343 | 40.9k | BitsOffset(5, 9), BitsOffset(28, 41), 0, |
344 | 40.9k | &block_idx)); |
345 | 40.5k | block_idx += last_block_idx + 1; |
346 | 40.5k | if (block_idx > (3u << 26)) { |
347 | 13 | return JXL_FAILURE("Invalid block ID: %u", block_idx); |
348 | 13 | } |
349 | 40.5k | last_block_idx = block_idx; |
350 | 40.5k | } |
351 | 6.08k | } |
352 | 3.39k | std::vector<uint32_t> inter_marker_data_sizes; |
353 | 3.39k | inter_marker_data_sizes.reserve(info.num_intermarker); |
354 | 130k | for (size_t i = 0; i < info.num_intermarker; ++i) { |
355 | 128k | uint32_t len = visitor->IsReading() ? 0 : inter_marker_data[i].size(); |
356 | 128k | JXL_RETURN_IF_ERROR(visitor->Bits(16, 0, &len)); |
357 | 127k | if (visitor->IsReading()) inter_marker_data_sizes.emplace_back(len); |
358 | 127k | } |
359 | 2.75k | uint32_t tail_data_len = tail_data.size(); |
360 | 2.75k | 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 | 2.75k | JXL_RETURN_IF_ERROR(visitor->U32(Val(0), BitsOffset(8, 1), |
365 | 2.75k | BitsOffset(16, 257), BitsOffset(22, 65793), |
366 | 2.75k | 0, &tail_data_len)); |
367 | | |
368 | 2.68k | JXL_RETURN_IF_ERROR(visitor->Bool(false, &has_zero_padding_bit)); |
369 | 2.66k | if (has_zero_padding_bit) { |
370 | 1.95k | uint32_t nbit = padding_bits.size(); |
371 | 1.95k | JXL_RETURN_IF_ERROR(visitor->Bits(24, 0, &nbit)); |
372 | 1.86k | if (visitor->IsReading()) { |
373 | 1.86k | JXL_RETURN_IF_ERROR(CheckHasEnoughBits(visitor, nbit)); |
374 | 91 | padding_bits.reserve(std::min<uint32_t>(1024u, nbit)); |
375 | 22.6M | for (uint32_t i = 0; i < nbit; i++) { |
376 | 22.6M | bool bbit = false; |
377 | 22.6M | JXL_RETURN_IF_ERROR(visitor->Bool(false, &bbit)); |
378 | 22.6M | padding_bits.push_back(TO_JXL_BOOL(bbit)); |
379 | 22.6M | } |
380 | 91 | } 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 | 1.86k | } |
388 | | |
389 | 798 | { |
390 | 798 | size_t dht_index = 0; |
391 | 798 | size_t scan_index = 0; |
392 | 798 | bool is_progressive = false; |
393 | 798 | bool ac_ok[kMaxHuffmanTables] = {false}; |
394 | 798 | bool dc_ok[kMaxHuffmanTables] = {false}; |
395 | 66.0k | for (uint8_t marker : marker_order) { |
396 | 66.0k | if (marker == 0xC2) { |
397 | 677 | is_progressive = true; |
398 | 65.3k | } else if (marker == 0xC4) { |
399 | 1.84k | for (; dht_index < huffman_code.size();) { |
400 | 1.36k | const JPEGHuffmanCode& huff = huffman_code[dht_index++]; |
401 | 1.36k | size_t index = huff.slot_id; |
402 | 1.36k | if (index & 0x10) { |
403 | 550 | index -= 0x10; |
404 | 550 | ac_ok[index] = true; |
405 | 816 | } else { |
406 | 816 | dc_ok[index] = true; |
407 | 816 | } |
408 | 1.36k | if (huff.is_last) break; |
409 | 1.36k | } |
410 | 64.0k | } else if (marker == 0xDA) { |
411 | 1.26k | const JPEGScanInfo& si = scan_info[scan_index++]; |
412 | 3.15k | for (size_t i = 0; i < si.num_components; ++i) { |
413 | 1.94k | const JPEGComponentScanInfo& csi = si.components[i]; |
414 | 1.94k | size_t dc_tbl_idx = csi.dc_tbl_idx; |
415 | 1.94k | size_t ac_tbl_idx = csi.ac_tbl_idx; |
416 | 1.94k | bool want_dc = !is_progressive || (si.Ss == 0); |
417 | 1.94k | if (want_dc && !dc_ok[dc_tbl_idx]) { |
418 | 27 | return JXL_FAILURE("DC Huffman table used before defined"); |
419 | 27 | } |
420 | 1.91k | bool want_ac = !is_progressive || (si.Ss != 0) || (si.Se != 0); |
421 | 1.91k | if (want_ac && !ac_ok[ac_tbl_idx]) { |
422 | 20 | return JXL_FAILURE("AC Huffman table used before defined"); |
423 | 20 | } |
424 | 1.91k | } |
425 | 1.26k | } |
426 | 66.0k | } |
427 | 798 | } |
428 | | |
429 | | // Apply postponed actions. |
430 | 751 | if (visitor->IsReading()) { |
431 | 751 | tail_data.resize(tail_data_len); |
432 | 751 | JXL_ENSURE(inter_marker_data_sizes.size() == info.num_intermarker); |
433 | 751 | inter_marker_data.reserve(info.num_intermarker); |
434 | 58.2k | for (size_t i = 0; i < info.num_intermarker; ++i) { |
435 | 57.5k | inter_marker_data.emplace_back(inter_marker_data_sizes[i]); |
436 | 57.5k | } |
437 | 751 | } |
438 | | |
439 | 751 | return true; |
440 | 751 | } |
441 | | |
442 | | #endif // JPEGXL_ENABLE_TRANSCODE_JPEG |
443 | | |
444 | | void JPEGData::CalculateMcuSize(const JPEGScanInfo& scan, int* MCUs_per_row, |
445 | 302 | int* MCU_rows) const { |
446 | 302 | const bool is_interleaved = (scan.num_components > 1); |
447 | 302 | 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 sampling factor, consequently MCU is always represented with single |
452 | | // block. |
453 | 302 | const int h_group = is_interleaved ? 1 : base_component.h_samp_factor; |
454 | 302 | const int v_group = is_interleaved ? 1 : base_component.v_samp_factor; |
455 | 302 | int max_h_samp_factor = 1; |
456 | 302 | int max_v_samp_factor = 1; |
457 | 302 | for (const auto& c : components) { |
458 | 302 | max_h_samp_factor = std::max(c.h_samp_factor, max_h_samp_factor); |
459 | 302 | max_v_samp_factor = std::max(c.v_samp_factor, max_v_samp_factor); |
460 | 302 | } |
461 | 302 | *MCUs_per_row = DivCeil(width * h_group, 8 * max_h_samp_factor); |
462 | 302 | *MCU_rows = DivCeil(height * v_group, 8 * max_v_samp_factor); |
463 | 302 | } |
464 | | |
465 | | #if JPEGXL_ENABLE_TRANSCODE_JPEG |
466 | | |
467 | | Status SetJPEGDataFromICC(const std::vector<uint8_t>& icc, |
468 | 396 | jpeg::JPEGData* jpeg_data) { |
469 | 396 | size_t icc_pos = 0; |
470 | 791 | for (size_t i = 0; i < jpeg_data->app_data.size(); i++) { |
471 | 395 | if (jpeg_data->app_marker_type[i] != jpeg::AppMarkerType::kICC) { |
472 | 395 | continue; |
473 | 395 | } |
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 | 396 | if (icc_pos != icc.size() && icc_pos != 0) { |
486 | 0 | return JXL_FAILURE("ICC length is more than APP markers"); |
487 | 0 | } |
488 | 396 | return true; |
489 | 396 | } |
490 | | |
491 | | #endif // JPEGXL_ENABLE_TRANSCODE_JPEG |
492 | | |
493 | | } // namespace jpeg |
494 | | } // namespace jxl |