/src/libjxl/lib/jxl/icc_codec.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/icc_codec.h" |
7 | | |
8 | | #include <jxl/memory_manager.h> |
9 | | |
10 | | #include <cstdint> |
11 | | |
12 | | #include "lib/jxl/base/status.h" |
13 | | #include "lib/jxl/dec_ans.h" |
14 | | #include "lib/jxl/fields.h" |
15 | | #include "lib/jxl/icc_codec_common.h" |
16 | | #include "lib/jxl/padded_bytes.h" |
17 | | |
18 | | namespace jxl { |
19 | | namespace { |
20 | | |
21 | | // Shuffles or interleaves bytes, for example with width 2, turns "ABCDabcd" |
22 | | // into "AaBbCcDd". Transposes a matrix of ceil(size / width) columns and |
23 | | // width rows. There are size elements, size may be < width * height, if so the |
24 | | // last elements of the rightmost column are missing, the missing spots are |
25 | | // transposed along with the filled spots, and the result has the missing |
26 | | // elements at the end of the bottom row. The input is the input matrix in |
27 | | // scanline order but with missing elements skipped (which may occur in multiple |
28 | | // locations), the output is the result matrix in scanline order (with |
29 | | // no need to skip missing elements as they are past the end of the data). |
30 | | Status Shuffle(JxlMemoryManager* memory_manager, uint8_t* data, size_t size, |
31 | 25.7k | size_t width) { |
32 | 25.7k | size_t height = (size + width - 1) / width; // amount of rows of output |
33 | 25.7k | PaddedBytes result(memory_manager); |
34 | 25.7k | JXL_ASSIGN_OR_RETURN(result, |
35 | 25.7k | PaddedBytes::WithInitialSpace(memory_manager, size)); |
36 | | // i = output index, j input index |
37 | 25.7k | size_t s = 0; |
38 | 25.7k | size_t j = 0; |
39 | 1.12M | for (size_t i = 0; i < size; i++) { |
40 | 1.10M | result[i] = data[j]; |
41 | 1.10M | j += height; |
42 | 1.10M | if (j >= size) j = ++s; |
43 | 1.10M | } |
44 | | |
45 | 1.12M | for (size_t i = 0; i < size; i++) { |
46 | 1.10M | data[i] = result[i]; |
47 | 1.10M | } |
48 | 25.7k | return true; |
49 | 25.7k | } |
50 | | |
51 | | // TODO(eustas): should be 20, or even 18, once DecodeVarInt is improved; |
52 | | // currently DecodeVarInt does not signal the errors, and marks |
53 | | // 11 bytes as used even if only 10 are used (and 9 is enough for |
54 | | // 63-bit values). |
55 | | constexpr const size_t kPreambleSize = 22; // enough for reading 2 VarInts |
56 | | |
57 | 132k | uint64_t DecodeVarInt(const uint8_t* input, size_t inputSize, size_t* pos) { |
58 | 132k | size_t i; |
59 | 132k | uint64_t ret = 0; |
60 | 166k | for (i = 0; *pos + i < inputSize && i < 10; ++i) { |
61 | 166k | ret |= static_cast<uint64_t>(input[*pos + i] & 127) |
62 | 166k | << static_cast<uint64_t>(7 * i); |
63 | | // If the next-byte flag is not set, stop |
64 | 166k | if ((input[*pos + i] & 128) == 0) break; |
65 | 166k | } |
66 | | // TODO(user): Return a decoding error if i == 10. |
67 | 132k | *pos += i + 1; |
68 | 132k | return ret; |
69 | 132k | } |
70 | | |
71 | | } // namespace |
72 | | |
73 | | // Mimics the beginning of UnpredictICC for quick validity check. |
74 | | // At least kPreambleSize bytes of data should be valid at invocation time. |
75 | 6.53k | Status CheckPreamble(const PaddedBytes& data, size_t enc_size) { |
76 | 6.53k | const uint8_t* enc = data.data(); |
77 | 6.53k | size_t size = data.size(); |
78 | 6.53k | size_t pos = 0; |
79 | 6.53k | uint64_t osize = DecodeVarInt(enc, size, &pos); |
80 | 6.53k | JXL_RETURN_IF_ERROR(CheckIs32Bit(osize)); |
81 | 6.50k | if (pos >= size) return JXL_FAILURE("Out of bounds"); |
82 | 6.50k | uint64_t csize = DecodeVarInt(enc, size, &pos); |
83 | 6.50k | JXL_RETURN_IF_ERROR(CheckIs32Bit(csize)); |
84 | 6.49k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, csize, size)); |
85 | | // We expect that UnpredictICC inflates input, not the other way round. |
86 | 6.48k | if (osize + 65536 < enc_size) return JXL_FAILURE("Malformed ICC"); |
87 | | |
88 | | // NB(eustas): 64 MiB ICC should be enough for everything!? |
89 | 6.46k | const size_t output_limit = 1 << 28; |
90 | 6.46k | if (output_limit && osize > output_limit) { |
91 | 4 | return JXL_FAILURE("Decoded ICC is too large"); |
92 | 4 | } |
93 | 6.46k | return true; |
94 | 6.46k | } |
95 | | |
96 | | // Decodes the result of PredictICC back to a valid ICC profile. |
97 | 6.41k | Status UnpredictICC(const uint8_t* enc, size_t size, PaddedBytes* result) { |
98 | 6.41k | if (!result->empty()) return JXL_FAILURE("result must be empty initially"); |
99 | 6.41k | JxlMemoryManager* memory_manager = result->memory_manager(); |
100 | 6.41k | size_t pos = 0; |
101 | | // TODO(lode): technically speaking we need to check that the entire varint |
102 | | // decoding never goes out of bounds, not just the first byte. This requires |
103 | | // a DecodeVarInt function that returns an error code. It is safe to use |
104 | | // DecodeVarInt with out of bounds values, it silently returns, but the |
105 | | // specification requires an error. Idem for all DecodeVarInt below. |
106 | 6.41k | if (pos >= size) return JXL_FAILURE("Out of bounds"); |
107 | 6.41k | uint64_t osize = DecodeVarInt(enc, size, &pos); // Output size |
108 | 6.41k | JXL_RETURN_IF_ERROR(CheckIs32Bit(osize)); |
109 | 6.39k | if (pos >= size) return JXL_FAILURE("Out of bounds"); |
110 | 6.39k | uint64_t csize = DecodeVarInt(enc, size, &pos); // Commands size |
111 | | // Every command is translated to at least on byte. |
112 | 6.39k | JXL_RETURN_IF_ERROR(CheckIs32Bit(csize)); |
113 | 6.39k | size_t cpos = pos; // pos in commands stream |
114 | 6.39k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, csize, size)); |
115 | 6.37k | size_t commands_end = cpos + csize; |
116 | 6.37k | pos = commands_end; // pos in data stream |
117 | | |
118 | | // Header |
119 | 6.37k | PaddedBytes header{memory_manager}; |
120 | 6.37k | JXL_RETURN_IF_ERROR(header.append(ICCInitialHeaderPrediction(osize))); |
121 | 798k | for (size_t i = 0; i <= kICCHeaderSize; i++) { |
122 | 798k | if (result->size() == osize) { |
123 | 189 | if (cpos != commands_end) return JXL_FAILURE("Not all commands used"); |
124 | 152 | if (pos != size) return JXL_FAILURE("Not all data used"); |
125 | 70 | return true; // Valid end |
126 | 152 | } |
127 | 797k | if (i == kICCHeaderSize) break; // Done |
128 | 791k | ICCPredictHeader(result->data(), result->size(), header.data(), i); |
129 | 791k | if (pos >= size) return JXL_FAILURE("Out of bounds"); |
130 | 791k | JXL_RETURN_IF_ERROR(result->push_back(enc[pos++] + header[i])); |
131 | 791k | } |
132 | 6.17k | if (cpos >= commands_end) return JXL_FAILURE("Out of bounds"); |
133 | | |
134 | | // Tag list |
135 | 6.17k | uint64_t numtags = DecodeVarInt(enc, size, &cpos); |
136 | | |
137 | 6.17k | if (numtags != 0) { |
138 | 5.98k | numtags--; |
139 | 5.98k | JXL_RETURN_IF_ERROR(CheckIs32Bit(numtags)); |
140 | 5.91k | JXL_RETURN_IF_ERROR(AppendUint32(numtags, result)); |
141 | 5.91k | uint64_t prevtagstart = kICCHeaderSize + numtags * 12; |
142 | 5.91k | uint64_t prevtagsize = 0; |
143 | 72.5k | for (;;) { |
144 | 72.5k | if (result->size() > osize) return JXL_FAILURE("Invalid result size"); |
145 | 72.5k | if (cpos > commands_end) return JXL_FAILURE("Out of bounds"); |
146 | 72.5k | if (cpos == commands_end) break; // Valid end |
147 | 72.5k | uint8_t command = enc[cpos++]; |
148 | 72.5k | uint8_t tagcode = command & 63; |
149 | 72.5k | Tag tag; |
150 | 72.5k | if (tagcode == 0) { |
151 | 5.80k | break; |
152 | 66.7k | } else if (tagcode == kCommandTagUnknown) { |
153 | 3.86k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, 4, size)); |
154 | 3.86k | tag = DecodeKeyword(enc, size, pos); |
155 | 3.86k | pos += 4; |
156 | 62.8k | } else if (tagcode == kCommandTagTRC) { |
157 | 5.65k | tag = kRtrcTag; |
158 | 57.2k | } else if (tagcode == kCommandTagXYZ) { |
159 | 558 | tag = kRxyzTag; |
160 | 56.6k | } else { |
161 | 56.6k | if (tagcode - kCommandTagStringFirst >= kNumTagStrings) { |
162 | 24 | return JXL_FAILURE("Unknown tagcode"); |
163 | 24 | } |
164 | 56.6k | tag = *kTagStrings[tagcode - kCommandTagStringFirst]; |
165 | 56.6k | } |
166 | 66.7k | JXL_RETURN_IF_ERROR(AppendKeyword(tag, result)); |
167 | | |
168 | 66.7k | uint64_t tagstart; |
169 | 66.7k | uint64_t tagsize = prevtagsize; |
170 | 66.7k | if (tag == kRxyzTag || tag == kGxyzTag || tag == kBxyzTag || |
171 | 66.7k | tag == kKxyzTag || tag == kWtptTag || tag == kBkptTag || |
172 | 66.7k | tag == kLumiTag) { |
173 | 24.8k | tagsize = 20; |
174 | 24.8k | } |
175 | | |
176 | 66.7k | if (command & kFlagBitOffset) { |
177 | 16.1k | if (cpos >= commands_end) return JXL_FAILURE("Out of bounds"); |
178 | 16.1k | tagstart = DecodeVarInt(enc, size, &cpos); |
179 | 50.5k | } else { |
180 | 50.5k | JXL_RETURN_IF_ERROR(CheckIs32Bit(prevtagstart)); |
181 | 50.5k | tagstart = prevtagstart + prevtagsize; |
182 | 50.5k | } |
183 | 66.6k | JXL_RETURN_IF_ERROR(CheckIs32Bit(tagstart)); |
184 | 66.6k | JXL_RETURN_IF_ERROR(AppendUint32(tagstart, result)); |
185 | 66.6k | if (command & kFlagBitSize) { |
186 | 34.6k | if (cpos >= commands_end) return JXL_FAILURE("Out of bounds"); |
187 | 34.6k | tagsize = DecodeVarInt(enc, size, &cpos); |
188 | 34.6k | } |
189 | 66.6k | JXL_RETURN_IF_ERROR(CheckIs32Bit(tagsize)); |
190 | 66.6k | JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result)); |
191 | 66.6k | prevtagstart = tagstart; |
192 | 66.6k | prevtagsize = tagsize; |
193 | | |
194 | 66.6k | if (tagcode == kCommandTagTRC) { |
195 | 5.65k | JXL_RETURN_IF_ERROR(AppendKeyword(kGtrcTag, result)); |
196 | 5.65k | JXL_RETURN_IF_ERROR(AppendUint32(tagstart, result)); |
197 | 5.65k | JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result)); |
198 | 5.65k | JXL_RETURN_IF_ERROR(AppendKeyword(kBtrcTag, result)); |
199 | 5.65k | JXL_RETURN_IF_ERROR(AppendUint32(tagstart, result)); |
200 | 5.65k | JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result)); |
201 | 5.65k | } |
202 | | |
203 | 66.6k | if (tagcode == kCommandTagXYZ) { |
204 | 558 | JXL_RETURN_IF_ERROR(CheckIs32Bit(tagstart + tagsize * 2)); |
205 | 557 | JXL_RETURN_IF_ERROR(AppendKeyword(kGxyzTag, result)); |
206 | 557 | JXL_RETURN_IF_ERROR(AppendUint32(tagstart + tagsize, result)); |
207 | 557 | JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result)); |
208 | 557 | JXL_RETURN_IF_ERROR(AppendKeyword(kBxyzTag, result)); |
209 | 557 | JXL_RETURN_IF_ERROR(AppendUint32(tagstart + tagsize * 2, result)); |
210 | 557 | JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result)); |
211 | 557 | } |
212 | 66.6k | } |
213 | 5.91k | } |
214 | | |
215 | | // Main Content |
216 | 114k | for (;;) { |
217 | 114k | if (result->size() > osize) return JXL_FAILURE("Invalid result size"); |
218 | 114k | if (cpos > commands_end) return JXL_FAILURE("Out of bounds"); |
219 | 114k | if (cpos == commands_end) break; // Valid end |
220 | 108k | uint8_t command = enc[cpos++]; |
221 | 108k | if (command == kCommandInsert) { |
222 | 22.7k | if (cpos >= commands_end) return JXL_FAILURE("Out of bounds"); |
223 | 22.7k | uint64_t num = DecodeVarInt(enc, size, &cpos); |
224 | 22.7k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, num, size)); |
225 | 13.1M | for (size_t i = 0; i < num; i++) { |
226 | 13.1M | JXL_RETURN_IF_ERROR(result->push_back(enc[pos++])); |
227 | 13.1M | } |
228 | 85.5k | } else if (command == kCommandShuffle2 || command == kCommandShuffle4) { |
229 | 24.9k | if (cpos >= commands_end) return JXL_FAILURE("Out of bounds"); |
230 | 24.9k | uint64_t num = DecodeVarInt(enc, size, &cpos); |
231 | 24.9k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, num, size)); |
232 | 24.9k | PaddedBytes shuffled(memory_manager); |
233 | 24.9k | JXL_ASSIGN_OR_RETURN(shuffled, |
234 | 24.9k | PaddedBytes::WithInitialSpace(memory_manager, num)); |
235 | 1.11M | for (size_t i = 0; i < num; i++) { |
236 | 1.08M | shuffled[i] = enc[pos + i]; |
237 | 1.08M | } |
238 | 24.9k | if (command == kCommandShuffle2) { |
239 | 22.7k | JXL_RETURN_IF_ERROR(Shuffle(memory_manager, shuffled.data(), num, 2)); |
240 | 22.7k | } else if (command == kCommandShuffle4) { |
241 | 2.13k | JXL_RETURN_IF_ERROR(Shuffle(memory_manager, shuffled.data(), num, 4)); |
242 | 2.13k | } |
243 | 1.11M | for (size_t i = 0; i < num; i++) { |
244 | 1.08M | JXL_RETURN_IF_ERROR(result->push_back(shuffled[i])); |
245 | 1.08M | pos++; |
246 | 1.08M | } |
247 | 60.5k | } else if (command == kCommandPredict) { |
248 | 2.25k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(cpos, 2, commands_end)); |
249 | 2.25k | uint8_t flags = enc[cpos++]; |
250 | | |
251 | 2.25k | size_t width = (flags & 3) + 1; |
252 | 2.25k | if (width == 3) return JXL_FAILURE("Invalid width"); |
253 | | |
254 | 2.24k | int order = (flags & 12) >> 2; |
255 | 2.24k | if (order == 3) return JXL_FAILURE("Invalid order"); |
256 | | |
257 | 2.24k | uint64_t stride = width; |
258 | 2.24k | if (flags & 16) { |
259 | 327 | if (cpos >= commands_end) return JXL_FAILURE("Out of bounds"); |
260 | 327 | stride = DecodeVarInt(enc, size, &cpos); |
261 | 327 | if (stride < width) { |
262 | 1 | return JXL_FAILURE("Invalid stride"); |
263 | 1 | } |
264 | 327 | } |
265 | | // If stride * 4 >= result->size(), return failure. The check |
266 | | // "size == 0 || ((size - 1) >> 2) < stride" corresponds to |
267 | | // "stride * 4 >= size", but does not suffer from integer overflow. |
268 | | // This check is more strict than necessary but follows the specification |
269 | | // and the encoder should ensure this is followed. |
270 | 2.24k | if (result->empty() || ((result->size() - 1u) >> 2u) < stride) { |
271 | 77 | return JXL_FAILURE("Invalid stride"); |
272 | 77 | } |
273 | | |
274 | 2.17k | if (cpos >= commands_end) return JXL_FAILURE("Out of bounds"); |
275 | 2.16k | uint64_t num = DecodeVarInt(enc, size, &cpos); // in bytes |
276 | 2.16k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, num, size)); |
277 | | |
278 | 2.11k | PaddedBytes shuffled(memory_manager); |
279 | 2.11k | JXL_ASSIGN_OR_RETURN(shuffled, |
280 | 2.11k | PaddedBytes::WithInitialSpace(memory_manager, num)); |
281 | | |
282 | 22.6k | for (size_t i = 0; i < num; i++) { |
283 | 20.5k | shuffled[i] = enc[pos + i]; |
284 | 20.5k | } |
285 | 2.11k | if (width > 1) { |
286 | 775 | JXL_RETURN_IF_ERROR( |
287 | 775 | Shuffle(memory_manager, shuffled.data(), num, width)); |
288 | 775 | } |
289 | | |
290 | 2.11k | size_t start = result->size(); |
291 | 22.6k | for (size_t i = 0; i < num; i++) { |
292 | 20.5k | uint8_t predicted = LinearPredictICCValue(result->data(), start, i, |
293 | 20.5k | stride, width, order); |
294 | 20.5k | JXL_RETURN_IF_ERROR(result->push_back(predicted + shuffled[i])); |
295 | 20.5k | } |
296 | 2.11k | pos += num; |
297 | 58.3k | } else if (command == kCommandXYZ) { |
298 | 22.5k | JXL_RETURN_IF_ERROR(AppendKeyword(kXyz_Tag, result)); |
299 | 112k | for (int i = 0; i < 4; i++) { |
300 | 90.0k | JXL_RETURN_IF_ERROR(result->push_back(0)); |
301 | 90.0k | } |
302 | 22.5k | JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, 12, size)); |
303 | 292k | for (size_t i = 0; i < 12; i++) { |
304 | 270k | JXL_RETURN_IF_ERROR(result->push_back(enc[pos++])); |
305 | 270k | } |
306 | 35.8k | } else if (command >= kCommandTypeStartFirst && |
307 | 35.8k | command < kCommandTypeStartFirst + kNumTypeStrings) { |
308 | 35.7k | JXL_RETURN_IF_ERROR(AppendKeyword( |
309 | 35.7k | *kTypeStrings[command - kCommandTypeStartFirst], result)); |
310 | 178k | for (size_t i = 0; i < 4; i++) { |
311 | 143k | JXL_RETURN_IF_ERROR(result->push_back(0)); |
312 | 143k | } |
313 | 35.7k | } else { |
314 | 58 | return JXL_FAILURE("Unknown command"); |
315 | 58 | } |
316 | 108k | } |
317 | | |
318 | 5.75k | if (pos != size) return JXL_FAILURE("Not all data used"); |
319 | 5.70k | if (result->size() != osize) return JXL_FAILURE("Invalid result size"); |
320 | | |
321 | 5.69k | return true; |
322 | 5.70k | } |
323 | | |
324 | 15.4k | Status ICCReader::Init(BitReader* reader) { |
325 | 15.4k | JXL_RETURN_IF_ERROR(CheckEOI(reader)); |
326 | 15.4k | JxlMemoryManager* memory_manager = decompressed_.memory_manager(); |
327 | 15.4k | used_bits_base_ = reader->TotalBitsConsumed(); |
328 | 15.4k | if (bits_to_skip_ == 0) { |
329 | 14.1k | enc_size_ = U64Coder::Read(reader); |
330 | 14.1k | if (enc_size_ > 268435456) { |
331 | | // Avoid too large memory allocation for invalid file. |
332 | 143 | return JXL_FAILURE("Too large encoded profile"); |
333 | 143 | } |
334 | 14.0k | JXL_RETURN_IF_ERROR(DecodeHistograms( |
335 | 14.0k | memory_manager, reader, kNumICCContexts, &code_, &context_map_)); |
336 | 18.5k | JXL_ASSIGN_OR_RETURN(ans_reader_, ANSSymbolReader::Create(&code_, reader)); |
337 | 18.5k | i_ = 0; |
338 | 18.5k | JXL_RETURN_IF_ERROR( |
339 | 18.5k | decompressed_.resize(std::min<size_t>(i_ + 0x400, enc_size_))); |
340 | 27.6k | for (; i_ < std::min<size_t>(2, enc_size_); i_++) { |
341 | 18.3k | decompressed_[i_] = ans_reader_.ReadHybridUint( |
342 | 18.3k | ICCANSContext(i_, i_ > 0 ? decompressed_[i_ - 1] : 0, |
343 | 18.3k | i_ > 1 ? decompressed_[i_ - 2] : 0), |
344 | 18.3k | reader, context_map_); |
345 | 18.3k | } |
346 | 9.27k | if (enc_size_ > kPreambleSize) { |
347 | 189k | for (; i_ < kPreambleSize; i_++) { |
348 | 180k | decompressed_[i_] = ans_reader_.ReadHybridUint( |
349 | 180k | ICCANSContext(i_, decompressed_[i_ - 1], decompressed_[i_ - 2]), |
350 | 180k | reader, context_map_); |
351 | 180k | } |
352 | 9.02k | JXL_RETURN_IF_ERROR(CheckEOI(reader)); |
353 | 6.53k | JXL_RETURN_IF_ERROR(CheckPreamble(decompressed_, enc_size_)); |
354 | 6.53k | } |
355 | 6.70k | bits_to_skip_ = reader->TotalBitsConsumed() - used_bits_base_; |
356 | 6.70k | } else { |
357 | 1.29k | reader->SkipBits(bits_to_skip_); |
358 | 1.29k | } |
359 | 8.00k | return true; |
360 | 15.4k | } |
361 | | |
362 | 7.47k | Status ICCReader::Process(BitReader* reader, PaddedBytes* icc) { |
363 | 7.47k | ANSSymbolReader::Checkpoint checkpoint; |
364 | 7.47k | size_t saved_i = 0; |
365 | 52.6k | auto save = [&]() { |
366 | 52.6k | ans_reader_.Save(&checkpoint); |
367 | 52.6k | bits_to_skip_ = reader->TotalBitsConsumed() - used_bits_base_; |
368 | 52.6k | saved_i = i_; |
369 | 52.6k | }; |
370 | 7.47k | save(); |
371 | 52.6k | auto check_and_restore = [&]() { |
372 | 52.6k | Status status = CheckEOI(reader); |
373 | 52.6k | if (!status) { |
374 | | // not enough bytes. |
375 | 1.05k | ans_reader_.Restore(checkpoint); |
376 | 1.05k | i_ = saved_i; |
377 | 1.05k | return status; |
378 | 1.05k | } |
379 | 51.6k | return Status(true); |
380 | 52.6k | }; |
381 | 25.2M | for (; i_ < enc_size_; i_++) { |
382 | 25.1M | if (i_ % ANSSymbolReader::kMaxCheckpointInterval == 0 && i_ > 0) { |
383 | 45.7k | JXL_RETURN_IF_ERROR(check_and_restore()); |
384 | 45.1k | save(); |
385 | 45.1k | if ((i_ > 0) && (((i_ & 0xFFFF) == 0))) { |
386 | 222 | float used_bytes = |
387 | 222 | (reader->TotalBitsConsumed() - used_bits_base_) / 8.0f; |
388 | 222 | if (i_ > used_bytes * 256) return JXL_FAILURE("Corrupted stream"); |
389 | 222 | } |
390 | 45.1k | JXL_RETURN_IF_ERROR( |
391 | 45.1k | decompressed_.resize(std::min<size_t>(i_ + 0x400, enc_size_))); |
392 | 45.1k | } |
393 | 25.1M | JXL_ENSURE(i_ >= 2); |
394 | 25.1M | decompressed_[i_] = ans_reader_.ReadHybridUint( |
395 | 25.1M | ICCANSContext(i_, decompressed_[i_ - 1], decompressed_[i_ - 2]), reader, |
396 | 25.1M | context_map_); |
397 | 25.1M | } |
398 | 6.96k | JXL_RETURN_IF_ERROR(check_and_restore()); |
399 | 6.41k | bits_to_skip_ = reader->TotalBitsConsumed() - used_bits_base_; |
400 | 6.41k | if (!ans_reader_.CheckANSFinalState()) { |
401 | 0 | return JXL_FAILURE("Corrupted ICC profile"); |
402 | 0 | } |
403 | | |
404 | 6.41k | icc->clear(); |
405 | 6.41k | return UnpredictICC(decompressed_.data(), decompressed_.size(), icc); |
406 | 6.41k | } |
407 | | |
408 | 77.1k | Status ICCReader::CheckEOI(BitReader* reader) { |
409 | 77.1k | if (reader->AllReadsWithinBounds()) return true; |
410 | 3.55k | return JXL_STATUS(StatusCode::kNotEnoughBytes, |
411 | 77.1k | "Not enough bytes for reading ICC profile"); |
412 | 77.1k | } |
413 | | |
414 | | } // namespace jxl |