/src/libjxl/lib/jxl/icc_codec.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/icc_codec.h" |
7 | | |
8 | | #include <jxl/memory_manager.h> |
9 | | |
10 | | #include <algorithm> |
11 | | #include <cstddef> |
12 | | #include <cstdint> |
13 | | |
14 | | #include "lib/jxl/base/common.h" |
15 | | #include "lib/jxl/base/status.h" |
16 | | #include "lib/jxl/dec_ans.h" |
17 | | #include "lib/jxl/dec_bit_reader.h" |
18 | | #include "lib/jxl/fields.h" |
19 | | #include "lib/jxl/icc_codec_common.h" |
20 | | #include "lib/jxl/padded_bytes.h" |
21 | | |
22 | | namespace jxl { |
23 | | namespace { |
24 | | |
25 | | // Shuffles or interleaves bytes, for example with width 2, turns "ABCDabcd" |
26 | | // into "AaBbCcDd". Transposes a matrix of ceil(size / width) columns and |
27 | | // width rows. There are size elements, size may be < width * height, if so the |
28 | | // last elements of the rightmost column are missing, the missing spots are |
29 | | // transposed along with the filled spots, and the result has the missing |
30 | | // elements at the end of the bottom row. The input is the input matrix in |
31 | | // scanline order but with missing elements skipped (which may occur in multiple |
32 | | // locations), the output is the result matrix in scanline order (with |
33 | | // no need to skip missing elements as they are past the end of the data). |
34 | | Status Shuffle(JxlMemoryManager* memory_manager, uint8_t* data, size_t size, |
35 | 2.42k | size_t width) { |
36 | 2.42k | size_t height = (size + width - 1) / width; // amount of rows of output |
37 | 2.42k | PaddedBytes result(memory_manager); |
38 | 2.42k | JXL_ASSIGN_OR_RETURN(result, |
39 | 2.42k | PaddedBytes::WithInitialSpace(memory_manager, size)); |
40 | | // i = output index, j input index |
41 | 2.42k | size_t s = 0; |
42 | 2.42k | size_t j = 0; |
43 | 30.2k | for (size_t i = 0; i < size; i++) { |
44 | 27.8k | result[i] = data[j]; |
45 | 27.8k | j += height; |
46 | 27.8k | if (j >= size) j = ++s; |
47 | 27.8k | } |
48 | | |
49 | 30.2k | for (size_t i = 0; i < size; i++) { |
50 | 27.8k | data[i] = result[i]; |
51 | 27.8k | } |
52 | 2.42k | return true; |
53 | 2.42k | } |
54 | | |
55 | | // Two base-128 varints at up to 10 bytes each. |
56 | | constexpr size_t kPreambleSize = 20; |
57 | | |
58 | | // Decodes a base-128 unsigned varint into *out, advancing *pos by the exact |
59 | | // number of bytes consumed. Returns an error if the input is truncated, if |
60 | | // the terminator (top-bit-clear byte) is not seen within 10 bytes, or if the |
61 | | // 10th byte encodes a value that does not fit in a uint64_t. |
62 | | Status DecodeVarInt(const uint8_t* input, size_t inputSize, size_t* pos, |
63 | 41.9k | uint64_t* out) { |
64 | 41.9k | uint64_t ret = 0; |
65 | | // 9 bytes cover bits 0..62; the 10th byte may only contribute bit 63. |
66 | 47.9k | for (size_t i = 0; i < 9; ++i) { |
67 | 47.8k | if (*pos >= inputSize) { |
68 | 10.8k | return JXL_FAILURE("DecodeVarInt: truncated input"); |
69 | 10.8k | } |
70 | 37.0k | const uint8_t byte = input[(*pos)++]; |
71 | 37.0k | ret |= static_cast<uint64_t>(byte & 0x7F) << (7 * i); |
72 | 37.0k | if ((byte & 0x80) == 0) { |
73 | 31.0k | *out = ret; |
74 | 31.0k | return true; |
75 | 31.0k | } |
76 | 37.0k | } |
77 | 72 | if (*pos >= inputSize) { |
78 | 0 | return JXL_FAILURE("DecodeVarInt: truncated input (10th byte)"); |
79 | 0 | } |
80 | 72 | const uint8_t byte = input[(*pos)++]; |
81 | 72 | if ((byte & 0x80) != 0) { |
82 | 63 | return JXL_FAILURE("DecodeVarInt: varint exceeds 10 bytes"); |
83 | 63 | } |
84 | 9 | if ((byte & 0x7E) != 0) { |
85 | 3 | return JXL_FAILURE("DecodeVarInt: value exceeds 2^64 - 1"); |
86 | 3 | } |
87 | 6 | ret |= static_cast<uint64_t>(byte & 0x01) << 63; |
88 | 6 | *out = ret; |
89 | 6 | return true; |
90 | 9 | } |
91 | | |
92 | | } // namespace |
93 | | |
94 | | // Mimics the beginning of UnpredictICC for quick validity check. |
95 | | // At least kPreambleSize bytes of data should be valid at invocation time. |
96 | 6.35k | Status CheckPreamble(const PaddedBytes& data, size_t enc_size) { |
97 | 6.35k | const uint8_t* enc = data.data(); |
98 | 6.35k | size_t size = data.size(); |
99 | 6.35k | size_t pos = 0; |
100 | 6.35k | uint64_t osize; |
101 | 6.35k | JXL_RETURN_IF_ERROR(DecodeVarInt(enc, size, &pos, &osize)); |
102 | 6.32k | JXL_RETURN_IF_ERROR(CheckIs32Bit(osize)); |
103 | 6.31k | uint64_t csize; |
104 | 6.31k | JXL_RETURN_IF_ERROR(DecodeVarInt(enc, size, &pos, &csize)); |
105 | 6.31k | JXL_RETURN_IF_ERROR(CheckIs32Bit(csize)); |
106 | 6.30k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, csize, size)); |
107 | | // We expect that UnpredictICC inflates input, not the other way round. |
108 | 6.27k | if (osize + 65536 < enc_size) return JXL_FAILURE("Malformed ICC"); |
109 | | |
110 | | // NB(eustas): 64 MiB ICC should be enough for everything!? |
111 | 6.18k | const size_t output_limit = 1 << 28; |
112 | 6.18k | if (output_limit && osize > output_limit) { |
113 | 7 | return JXL_FAILURE("Decoded ICC is too large"); |
114 | 7 | } |
115 | 6.17k | return true; |
116 | 6.18k | } |
117 | | |
118 | | // Decodes the result of PredictICC back to a valid ICC profile. |
119 | 14.2k | Status UnpredictICC(const uint8_t* enc, size_t size, PaddedBytes* result) { |
120 | 14.2k | if (!result->empty()) return JXL_FAILURE("result must be empty initially"); |
121 | 14.2k | JxlMemoryManager* memory_manager = result->memory_manager(); |
122 | 14.2k | size_t pos = 0; |
123 | 14.2k | uint64_t osize; |
124 | 14.2k | JXL_RETURN_IF_ERROR(DecodeVarInt(enc, size, &pos, &osize)); // Output size |
125 | 3.65k | JXL_RETURN_IF_ERROR(CheckIs32Bit(osize)); |
126 | 3.64k | uint64_t csize; |
127 | 3.64k | JXL_RETURN_IF_ERROR(DecodeVarInt(enc, size, &pos, &csize)); // Commands size |
128 | | // Every command is translated to at least one byte. |
129 | 3.57k | JXL_RETURN_IF_ERROR(CheckIs32Bit(csize)); |
130 | 3.56k | size_t cpos = pos; // pos in commands stream |
131 | 3.56k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, csize, size)); |
132 | 2.36k | size_t commands_end = cpos + csize; |
133 | 2.36k | pos = commands_end; // pos in data stream |
134 | | |
135 | | // Header |
136 | 2.36k | PaddedBytes header{memory_manager}; |
137 | 2.36k | JXL_RETURN_IF_ERROR(header.append(ICCInitialHeaderPrediction(osize))); |
138 | 109k | for (size_t i = 0; i <= kICCHeaderSize; i++) { |
139 | 109k | if (result->size() == osize) { |
140 | 1.46k | if (cpos != commands_end) return JXL_FAILURE("Not all commands used"); |
141 | 1.25k | if (pos != size) return JXL_FAILURE("Not all data used"); |
142 | 34 | return true; // Valid end |
143 | 1.25k | } |
144 | 108k | if (i == kICCHeaderSize) break; // Done |
145 | 107k | ICCPredictHeader(result->data(), result->size(), header.data(), i); |
146 | 107k | if (pos >= size) return JXL_FAILURE("Out of bounds"); |
147 | 107k | JXL_RETURN_IF_ERROR(result->push_back(enc[pos++] + header[i])); |
148 | 107k | } |
149 | 801 | if (cpos >= commands_end) return JXL_FAILURE("Out of bounds"); |
150 | | |
151 | | // Tag list |
152 | 795 | uint64_t numtags; |
153 | 795 | JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &numtags)); |
154 | | |
155 | 782 | if (numtags != 0) { |
156 | 609 | numtags--; |
157 | 609 | JXL_RETURN_IF_ERROR(CheckIs32Bit(numtags)); |
158 | 603 | JXL_RETURN_IF_ERROR(AppendUint32(numtags, result)); |
159 | 603 | uint64_t prevtagstart = kICCHeaderSize + numtags * 12; |
160 | 603 | uint64_t prevtagsize = 0; |
161 | 11.1k | for (;;) { |
162 | 11.1k | if (result->size() > osize) return JXL_FAILURE("Invalid result size"); |
163 | 11.1k | if (cpos > commands_end) return JXL_FAILURE("Out of bounds"); |
164 | 11.1k | if (cpos == commands_end) break; // Valid end |
165 | 10.9k | uint8_t command = enc[cpos++]; |
166 | 10.9k | uint8_t tagcode = command & 63; |
167 | 10.9k | Tag tag; |
168 | 10.9k | if (tagcode == 0) { |
169 | 336 | break; |
170 | 10.6k | } else if (tagcode == kCommandTagUnknown) { |
171 | 551 | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, 4, size)); |
172 | 541 | tag = DecodeKeyword(enc, size, pos); |
173 | 541 | pos += 4; |
174 | 10.0k | } else if (tagcode == kCommandTagTRC) { |
175 | 984 | tag = kRtrcTag; |
176 | 9.11k | } else if (tagcode == kCommandTagXYZ) { |
177 | 668 | tag = kRxyzTag; |
178 | 8.44k | } else { |
179 | 8.44k | if (tagcode - kCommandTagStringFirst >= kNumTagStrings) { |
180 | 53 | return JXL_FAILURE("Unknown tagcode"); |
181 | 53 | } |
182 | 8.39k | tag = *kTagStrings[tagcode - kCommandTagStringFirst]; |
183 | 8.39k | } |
184 | 10.5k | JXL_RETURN_IF_ERROR(AppendKeyword(tag, result)); |
185 | | |
186 | 10.5k | uint64_t tagstart; |
187 | 10.5k | uint64_t tagsize = prevtagsize; |
188 | 10.5k | if (tag == kRxyzTag || tag == kGxyzTag || tag == kBxyzTag || |
189 | 7.94k | tag == kKxyzTag || tag == kWtptTag || tag == kBkptTag || |
190 | 6.12k | tag == kLumiTag) { |
191 | 6.12k | tagsize = 20; |
192 | 6.12k | } |
193 | | |
194 | 10.5k | if (command & kFlagBitOffset) { |
195 | 2.25k | JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &tagstart)); |
196 | 8.33k | } else { |
197 | 8.33k | JXL_RETURN_IF_ERROR(CheckIs32Bit(prevtagstart)); |
198 | 8.33k | tagstart = prevtagstart + prevtagsize; |
199 | 8.33k | } |
200 | 10.5k | JXL_RETURN_IF_ERROR(CheckIs32Bit(tagstart)); |
201 | 10.5k | JXL_RETURN_IF_ERROR(AppendUint32(tagstart, result)); |
202 | 10.5k | if (command & kFlagBitSize) { |
203 | 2.30k | JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &tagsize)); |
204 | 2.30k | } |
205 | 10.5k | JXL_RETURN_IF_ERROR(CheckIs32Bit(tagsize)); |
206 | 10.5k | JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result)); |
207 | 10.5k | prevtagstart = tagstart; |
208 | 10.5k | prevtagsize = tagsize; |
209 | | |
210 | 10.5k | if (tagcode == kCommandTagTRC) { |
211 | 977 | JXL_RETURN_IF_ERROR(AppendKeyword(kGtrcTag, result)); |
212 | 977 | JXL_RETURN_IF_ERROR(AppendUint32(tagstart, result)); |
213 | 977 | JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result)); |
214 | 977 | JXL_RETURN_IF_ERROR(AppendKeyword(kBtrcTag, result)); |
215 | 977 | JXL_RETURN_IF_ERROR(AppendUint32(tagstart, result)); |
216 | 977 | JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result)); |
217 | 977 | } |
218 | | |
219 | 10.5k | if (tagcode == kCommandTagXYZ) { |
220 | 664 | JXL_RETURN_IF_ERROR(CheckIs32Bit(tagstart + tagsize * 2)); |
221 | 663 | JXL_RETURN_IF_ERROR(AppendKeyword(kGxyzTag, result)); |
222 | 663 | JXL_RETURN_IF_ERROR(AppendUint32(tagstart + tagsize, result)); |
223 | 663 | JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result)); |
224 | 663 | JXL_RETURN_IF_ERROR(AppendKeyword(kBxyzTag, result)); |
225 | 663 | JXL_RETURN_IF_ERROR(AppendUint32(tagstart + tagsize * 2, result)); |
226 | 663 | JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result)); |
227 | 663 | } |
228 | 10.5k | } |
229 | 603 | } |
230 | | |
231 | | // Main Content |
232 | 8.23k | for (;;) { |
233 | 8.23k | if (result->size() > osize) return JXL_FAILURE("Invalid result size"); |
234 | 8.23k | if (cpos > commands_end) return JXL_FAILURE("Out of bounds"); |
235 | 8.23k | if (cpos == commands_end) break; // Valid end |
236 | 7.99k | uint8_t command = enc[cpos++]; |
237 | 7.99k | if (command == kCommandInsert) { |
238 | 1.51k | uint64_t num; |
239 | 1.51k | JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &num)); |
240 | 1.49k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, num, size)); |
241 | 370k | for (size_t i = 0; i < num; i++) { |
242 | 368k | JXL_RETURN_IF_ERROR(result->push_back(enc[pos++])); |
243 | 368k | } |
244 | 6.48k | } else if (command == kCommandShuffle2 || command == kCommandShuffle4) { |
245 | 1.54k | uint64_t num; |
246 | 1.54k | JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &num)); |
247 | 1.52k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, num, size)); |
248 | 1.49k | PaddedBytes shuffled(memory_manager); |
249 | 1.49k | JXL_ASSIGN_OR_RETURN(shuffled, |
250 | 1.49k | PaddedBytes::WithInitialSpace(memory_manager, num)); |
251 | 21.5k | for (size_t i = 0; i < num; i++) { |
252 | 20.0k | shuffled[i] = enc[pos + i]; |
253 | 20.0k | } |
254 | 1.49k | if (command == kCommandShuffle2) { |
255 | 757 | JXL_RETURN_IF_ERROR(Shuffle(memory_manager, shuffled.data(), num, 2)); |
256 | 757 | } else if (command == kCommandShuffle4) { |
257 | 737 | JXL_RETURN_IF_ERROR(Shuffle(memory_manager, shuffled.data(), num, 4)); |
258 | 737 | } |
259 | 21.5k | for (size_t i = 0; i < num; i++) { |
260 | 20.0k | JXL_RETURN_IF_ERROR(result->push_back(shuffled[i])); |
261 | 20.0k | pos++; |
262 | 20.0k | } |
263 | 4.94k | } else if (command == kCommandPredict) { |
264 | 2.32k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(cpos, 2, commands_end)); |
265 | 2.30k | uint8_t flags = enc[cpos++]; |
266 | | |
267 | 2.30k | size_t width = (flags & 3) + 1; |
268 | 2.30k | if (width == 3) return JXL_FAILURE("Invalid width"); |
269 | | |
270 | 2.29k | int order = (flags & 12) >> 2; |
271 | 2.29k | if (order == 3) return JXL_FAILURE("Invalid order"); |
272 | | |
273 | 2.29k | uint64_t stride = width; |
274 | 2.29k | if (flags & 16) { |
275 | 697 | JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &stride)); |
276 | 693 | if (stride < width) { |
277 | 3 | return JXL_FAILURE("Invalid stride"); |
278 | 3 | } |
279 | 693 | } |
280 | | // If stride * 4 >= result->size(), return failure. The check |
281 | | // "size == 0 || ((size - 1) >> 2) < stride" corresponds to |
282 | | // "stride * 4 >= size", but does not suffer from integer overflow. |
283 | | // This check is more strict than necessary but follows the specification |
284 | | // and the encoder should ensure this is followed. |
285 | 2.28k | if (result->empty() || ((result->size() - 1u) >> 2u) < stride) { |
286 | 34 | return JXL_FAILURE("Invalid stride"); |
287 | 34 | } |
288 | | |
289 | 2.25k | uint64_t num; |
290 | 2.25k | JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &num)); // in bytes |
291 | 2.24k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, num, size)); |
292 | | |
293 | 2.22k | PaddedBytes shuffled(memory_manager); |
294 | 2.22k | JXL_ASSIGN_OR_RETURN(shuffled, |
295 | 2.22k | PaddedBytes::WithInitialSpace(memory_manager, num)); |
296 | | |
297 | 26.2k | for (size_t i = 0; i < num; i++) { |
298 | 24.0k | shuffled[i] = enc[pos + i]; |
299 | 24.0k | } |
300 | 2.22k | if (width > 1) { |
301 | 926 | JXL_RETURN_IF_ERROR( |
302 | 926 | Shuffle(memory_manager, shuffled.data(), num, width)); |
303 | 926 | } |
304 | | |
305 | 2.22k | size_t start = result->size(); |
306 | 26.2k | for (size_t i = 0; i < num; i++) { |
307 | 24.0k | uint8_t predicted = LinearPredictICCValue(result->data(), start, i, |
308 | 24.0k | stride, width, order); |
309 | 24.0k | JXL_RETURN_IF_ERROR(result->push_back(predicted + shuffled[i])); |
310 | 24.0k | } |
311 | 2.22k | pos += num; |
312 | 2.62k | } else if (command == kCommandXYZ) { |
313 | 879 | JXL_RETURN_IF_ERROR(AppendKeyword(kXyz_Tag, result)); |
314 | 4.39k | for (int i = 0; i < 4; i++) { |
315 | 3.51k | JXL_RETURN_IF_ERROR(result->push_back(0)); |
316 | 3.51k | } |
317 | 879 | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, 12, size)); |
318 | 11.2k | for (size_t i = 0; i < 12; i++) { |
319 | 10.4k | JXL_RETURN_IF_ERROR(result->push_back(enc[pos++])); |
320 | 10.4k | } |
321 | 1.74k | } else if (command >= kCommandTypeStartFirst && |
322 | 1.66k | command < kCommandTypeStartFirst + kNumTypeStrings) { |
323 | 1.55k | JXL_RETURN_IF_ERROR(AppendKeyword( |
324 | 1.55k | *kTypeStrings[command - kCommandTypeStartFirst], result)); |
325 | 7.77k | for (size_t i = 0; i < 4; i++) { |
326 | 6.22k | JXL_RETURN_IF_ERROR(result->push_back(0)); |
327 | 6.22k | } |
328 | 1.55k | } else { |
329 | 190 | return JXL_FAILURE("Unknown command"); |
330 | 190 | } |
331 | 7.99k | } |
332 | | |
333 | 232 | if (pos != size) return JXL_FAILURE("Not all data used"); |
334 | 70 | if (result->size() != osize) return JXL_FAILURE("Invalid result size"); |
335 | | |
336 | 66 | return true; |
337 | 70 | } |
338 | | |
339 | 52.6k | Status ICCReader::Init(BitReader* reader) { |
340 | 52.6k | JXL_RETURN_IF_ERROR(CheckEOI(reader)); |
341 | 52.6k | JxlMemoryManager* memory_manager = decompressed_.memory_manager(); |
342 | 52.6k | used_bits_base_ = reader->TotalBitsConsumed(); |
343 | 52.6k | if (bits_to_skip_ == 0) { |
344 | 52.5k | enc_size_ = U64Coder::Read(reader); |
345 | 52.5k | if (enc_size_ > 268435456) { |
346 | | // Avoid too large memory allocation for invalid file. |
347 | 301 | return JXL_FAILURE("Too large encoded profile"); |
348 | 301 | } |
349 | 52.2k | JXL_RETURN_IF_ERROR(DecodeHistograms( |
350 | 52.2k | memory_manager, reader, kNumICCContexts, &code_, &context_map_)); |
351 | 51.4k | JXL_ASSIGN_OR_RETURN(ans_reader_, ANSSymbolReader::Create(&code_, reader)); |
352 | 51.4k | i_ = 0; |
353 | 51.4k | JXL_RETURN_IF_ERROR( |
354 | 51.4k | decompressed_.resize(std::min<size_t>(i_ + 0x400, enc_size_))); |
355 | 42.4k | for (; i_ < std::min<size_t>(2, enc_size_); i_++) { |
356 | 16.7k | decompressed_[i_] = ans_reader_.ReadHybridUint( |
357 | 16.7k | ICCANSContext(i_, i_ > 0 ? decompressed_[i_ - 1] : 0, |
358 | 16.7k | i_ > 1 ? decompressed_[i_ - 2] : 0), |
359 | 16.7k | reader, context_map_); |
360 | 16.7k | } |
361 | 25.7k | if (enc_size_ > kPreambleSize) { |
362 | 128k | for (; i_ < kPreambleSize; i_++) { |
363 | 121k | decompressed_[i_] = ans_reader_.ReadHybridUint( |
364 | 121k | ICCANSContext(i_, decompressed_[i_ - 1], decompressed_[i_ - 2]), |
365 | 121k | reader, context_map_); |
366 | 121k | } |
367 | 6.75k | JXL_RETURN_IF_ERROR(CheckEOI(reader)); |
368 | 6.35k | JXL_RETURN_IF_ERROR(CheckPreamble(decompressed_, enc_size_)); |
369 | 6.35k | } |
370 | 25.1k | bits_to_skip_ = reader->TotalBitsConsumed() - used_bits_base_; |
371 | 25.1k | } else { |
372 | 110 | reader->SkipBits(bits_to_skip_); |
373 | 110 | } |
374 | 25.2k | return true; |
375 | 52.6k | } |
376 | | |
377 | 18.4k | Status ICCReader::Process(BitReader* reader, PaddedBytes* icc) { |
378 | 18.4k | auto checkpoint = jxl::make_unique<ANSSymbolReader::Checkpoint>(); |
379 | 18.4k | size_t saved_i = 0; |
380 | 48.9k | auto save = [&]() { |
381 | 48.9k | ans_reader_.Save(checkpoint.get()); |
382 | 48.9k | bits_to_skip_ = reader->TotalBitsConsumed() - used_bits_base_; |
383 | 48.9k | saved_i = i_; |
384 | 48.9k | }; |
385 | 18.4k | save(); |
386 | 48.9k | auto check_and_restore = [&]() -> Status { |
387 | 48.9k | Status status = CheckEOI(reader); |
388 | 48.9k | if (!status) { |
389 | | // not enough bytes. |
390 | 4.15k | ans_reader_.Restore(*checkpoint); |
391 | 4.15k | i_ = saved_i; |
392 | 4.15k | return status; |
393 | 4.15k | } |
394 | 44.7k | return true; |
395 | 48.9k | }; |
396 | 17.3M | for (; i_ < enc_size_; i_++) { |
397 | 17.3M | if (i_ % ANSSymbolReader::kMaxCheckpointInterval == 0 && i_ > 0) { |
398 | 31.4k | JXL_RETURN_IF_ERROR(check_and_restore()); |
399 | 30.4k | save(); |
400 | 30.4k | if ((i_ > 0) && (((i_ & 0xFFFF) == 0))) { |
401 | 164 | float used_bytes = |
402 | 164 | (reader->TotalBitsConsumed() - used_bits_base_) / 8.0f; |
403 | 164 | if (i_ > used_bytes * 256) return JXL_FAILURE("Corrupted stream"); |
404 | 164 | } |
405 | 30.4k | JXL_RETURN_IF_ERROR( |
406 | 30.4k | decompressed_.resize(std::min<size_t>(i_ + 0x400, enc_size_))); |
407 | 30.4k | } |
408 | 17.2M | JXL_ENSURE(i_ >= 2); |
409 | 17.2M | decompressed_[i_] = ans_reader_.ReadHybridUint( |
410 | 17.2M | ICCANSContext(i_, decompressed_[i_ - 1], decompressed_[i_ - 2]), reader, |
411 | 17.2M | context_map_); |
412 | 17.2M | } |
413 | 17.5k | JXL_RETURN_IF_ERROR(check_and_restore()); |
414 | 14.2k | bits_to_skip_ = reader->TotalBitsConsumed() - used_bits_base_; |
415 | 14.2k | if (!ans_reader_.CheckANSFinalState()) { |
416 | 0 | return JXL_FAILURE("Corrupted ICC profile"); |
417 | 0 | } |
418 | | |
419 | 14.2k | icc->clear(); |
420 | 14.2k | return UnpredictICC(decompressed_.data(), decompressed_.size(), icc); |
421 | 14.2k | } |
422 | | |
423 | 108k | Status ICCReader::CheckEOI(BitReader* reader) { |
424 | 108k | if (reader->AllReadsWithinBounds()) return true; |
425 | 4.55k | return JXL_NOT_ENOUGH_BYTES("Not enough bytes for reading ICC profile"); |
426 | 108k | } |
427 | | |
428 | | } // namespace jxl |