Coverage Report

Created: 2026-11-12 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
6.60k
               size_t width) {
36
6.60k
  size_t height = (size + width - 1) / width;  // amount of rows of output
37
6.60k
  PaddedBytes result(memory_manager);
38
6.60k
  JXL_ASSIGN_OR_RETURN(result,
39
6.60k
                       PaddedBytes::WithInitialSpace(memory_manager, size));
40
  // i = output index, j input index
41
6.60k
  size_t s = 0;
42
6.60k
  size_t j = 0;
43
173k
  for (size_t i = 0; i < size; i++) {
44
166k
    result[i] = data[j];
45
166k
    j += height;
46
166k
    if (j >= size) j = ++s;
47
166k
  }
48
49
173k
  for (size_t i = 0; i < size; i++) {
50
166k
    data[i] = result[i];
51
166k
  }
52
6.60k
  return true;
53
6.60k
}
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
30.2k
                    uint64_t* out) {
64
30.2k
  uint64_t ret = 0;
65
  // 9 bytes cover bits 0..62; the 10th byte may only contribute bit 63.
66
41.8k
  for (size_t i = 0; i < 9; ++i) {
67
41.7k
    if (*pos >= inputSize) {
68
170
      return JXL_FAILURE("DecodeVarInt: truncated input");
69
170
    }
70
41.5k
    const uint8_t byte = input[(*pos)++];
71
41.5k
    ret |= static_cast<uint64_t>(byte & 0x7F) << (7 * i);
72
41.5k
    if ((byte & 0x80) == 0) {
73
29.9k
      *out = ret;
74
29.9k
      return true;
75
29.9k
    }
76
41.5k
  }
77
65
  if (*pos >= inputSize) {
78
8
    return JXL_FAILURE("DecodeVarInt: truncated input (10th byte)");
79
8
  }
80
57
  const uint8_t byte = input[(*pos)++];
81
57
  if ((byte & 0x80) != 0) {
82
23
    return JXL_FAILURE("DecodeVarInt: varint exceeds 10 bytes");
83
23
  }
84
34
  if ((byte & 0x7E) != 0) {
85
5
    return JXL_FAILURE("DecodeVarInt: value exceeds 2^64 - 1");
86
5
  }
87
29
  ret |= static_cast<uint64_t>(byte & 0x01) << 63;
88
29
  *out = ret;
89
29
  return true;
90
34
}
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
1.80k
Status CheckPreamble(const PaddedBytes& data, size_t enc_size) {
97
1.80k
  const uint8_t* enc = data.data();
98
1.80k
  size_t size = data.size();
99
1.80k
  size_t pos = 0;
100
1.80k
  uint64_t osize;
101
1.80k
  JXL_RETURN_IF_ERROR(DecodeVarInt(enc, size, &pos, &osize));
102
1.79k
  JXL_RETURN_IF_ERROR(CheckIs32Bit(osize));
103
1.78k
  uint64_t csize;
104
1.78k
  JXL_RETURN_IF_ERROR(DecodeVarInt(enc, size, &pos, &csize));
105
1.77k
  JXL_RETURN_IF_ERROR(CheckIs32Bit(csize));
106
1.77k
  JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, csize, size));
107
  // We expect that UnpredictICC inflates input, not the other way round.
108
1.74k
  if (osize + 65536 < enc_size) return JXL_FAILURE("Malformed ICC");
109
110
  // NB(eustas): 64 MiB ICC should be enough for everything!?
111
1.71k
  const size_t output_limit = 1 << 28;
112
1.71k
  if (output_limit && osize > output_limit) {
113
6
    return JXL_FAILURE("Decoded ICC is too large");
114
6
  }
115
1.70k
  return true;
116
1.71k
}
117
118
// Decodes the result of PredictICC back to a valid ICC profile.
119
1.64k
Status UnpredictICC(const uint8_t* enc, size_t size, PaddedBytes* result) {
120
1.64k
  if (!result->empty()) return JXL_FAILURE("result must be empty initially");
121
1.64k
  JxlMemoryManager* memory_manager = result->memory_manager();
122
1.64k
  size_t pos = 0;
123
1.64k
  uint64_t osize;
124
1.64k
  JXL_RETURN_IF_ERROR(DecodeVarInt(enc, size, &pos, &osize));  // Output size
125
1.60k
  JXL_RETURN_IF_ERROR(CheckIs32Bit(osize));
126
1.59k
  uint64_t csize;
127
1.59k
  JXL_RETURN_IF_ERROR(DecodeVarInt(enc, size, &pos, &csize));  // Commands size
128
  // Every command is translated to at least one byte.
129
1.56k
  JXL_RETURN_IF_ERROR(CheckIs32Bit(csize));
130
1.55k
  size_t cpos = pos;  // pos in commands stream
131
1.55k
  JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, csize, size));
132
1.53k
  size_t commands_end = cpos + csize;
133
1.53k
  pos = commands_end;  // pos in data stream
134
135
  // Header
136
1.53k
  PaddedBytes header{memory_manager};
137
1.53k
  JXL_RETURN_IF_ERROR(header.append(ICCInitialHeaderPrediction(osize)));
138
147k
  for (size_t i = 0; i <= kICCHeaderSize; i++) {
139
147k
    if (result->size() == osize) {
140
394
      if (cpos != commands_end) return JXL_FAILURE("Not all commands used");
141
231
      if (pos != size) return JXL_FAILURE("Not all data used");
142
23
      return true;  // Valid end
143
231
    }
144
147k
    if (i == kICCHeaderSize) break;  // Done
145
146k
    ICCPredictHeader(result->data(), result->size(), header.data(), i);
146
146k
    if (pos >= size) return JXL_FAILURE("Out of bounds");
147
146k
    JXL_RETURN_IF_ERROR(result->push_back(enc[pos++] + header[i]));
148
146k
  }
149
1.11k
  if (cpos >= commands_end) return JXL_FAILURE("Out of bounds");
150
151
  // Tag list
152
1.10k
  uint64_t numtags;
153
1.10k
  JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &numtags));
154
155
1.10k
  if (numtags != 0) {
156
877
    numtags--;
157
877
    JXL_RETURN_IF_ERROR(CheckIs32Bit(numtags));
158
865
    JXL_RETURN_IF_ERROR(AppendUint32(numtags, result));
159
865
    uint64_t prevtagstart = kICCHeaderSize + numtags * 12;
160
865
    uint64_t prevtagsize = 0;
161
22.9k
    for (;;) {
162
22.9k
      if (result->size() > osize) return JXL_FAILURE("Invalid result size");
163
22.8k
      if (cpos > commands_end) return JXL_FAILURE("Out of bounds");
164
22.8k
      if (cpos == commands_end) break;  // Valid end
165
22.8k
      uint8_t command = enc[cpos++];
166
22.8k
      uint8_t tagcode = command & 63;
167
22.8k
      Tag tag;
168
22.8k
      if (tagcode == 0) {
169
631
        break;
170
22.1k
      } else if (tagcode == kCommandTagUnknown) {
171
2.93k
        JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, 4, size));
172
2.93k
        tag = DecodeKeyword(enc, size, pos);
173
2.93k
        pos += 4;
174
19.2k
      } else if (tagcode == kCommandTagTRC) {
175
3.09k
        tag = kRtrcTag;
176
16.1k
      } else if (tagcode == kCommandTagXYZ) {
177
4.28k
        tag = kRxyzTag;
178
11.8k
      } else {
179
11.8k
        if (tagcode - kCommandTagStringFirst >= kNumTagStrings) {
180
37
          return JXL_FAILURE("Unknown tagcode");
181
37
        }
182
11.8k
        tag = *kTagStrings[tagcode - kCommandTagStringFirst];
183
11.8k
      }
184
22.1k
      JXL_RETURN_IF_ERROR(AppendKeyword(tag, result));
185
186
22.1k
      uint64_t tagstart;
187
22.1k
      uint64_t tagsize = prevtagsize;
188
22.1k
      if (tag == kRxyzTag || tag == kGxyzTag || tag == kBxyzTag ||
189
14.1k
          tag == kKxyzTag || tag == kWtptTag || tag == kBkptTag ||
190
12.5k
          tag == kLumiTag) {
191
12.5k
        tagsize = 20;
192
12.5k
      }
193
194
22.1k
      if (command & kFlagBitOffset) {
195
3.37k
        JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &tagstart));
196
18.7k
      } else {
197
18.7k
        JXL_RETURN_IF_ERROR(CheckIs32Bit(prevtagstart));
198
18.7k
        tagstart = prevtagstart + prevtagsize;
199
18.7k
      }
200
22.0k
      JXL_RETURN_IF_ERROR(CheckIs32Bit(tagstart));
201
22.0k
      JXL_RETURN_IF_ERROR(AppendUint32(tagstart, result));
202
22.0k
      if (command & kFlagBitSize) {
203
5.71k
        JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &tagsize));
204
5.71k
      }
205
22.0k
      JXL_RETURN_IF_ERROR(CheckIs32Bit(tagsize));
206
22.0k
      JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result));
207
22.0k
      prevtagstart = tagstart;
208
22.0k
      prevtagsize = tagsize;
209
210
22.0k
      if (tagcode == kCommandTagTRC) {
211
3.08k
        JXL_RETURN_IF_ERROR(AppendKeyword(kGtrcTag, result));
212
3.08k
        JXL_RETURN_IF_ERROR(AppendUint32(tagstart, result));
213
3.08k
        JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result));
214
3.08k
        JXL_RETURN_IF_ERROR(AppendKeyword(kBtrcTag, result));
215
3.08k
        JXL_RETURN_IF_ERROR(AppendUint32(tagstart, result));
216
3.08k
        JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result));
217
3.08k
      }
218
219
22.0k
      if (tagcode == kCommandTagXYZ) {
220
4.27k
        JXL_RETURN_IF_ERROR(CheckIs32Bit(tagstart + tagsize * 2));
221
4.27k
        JXL_RETURN_IF_ERROR(AppendKeyword(kGxyzTag, result));
222
4.27k
        JXL_RETURN_IF_ERROR(AppendUint32(tagstart + tagsize, result));
223
4.27k
        JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result));
224
4.27k
        JXL_RETURN_IF_ERROR(AppendKeyword(kBxyzTag, result));
225
4.27k
        JXL_RETURN_IF_ERROR(AppendUint32(tagstart + tagsize * 2, result));
226
4.27k
        JXL_RETURN_IF_ERROR(AppendUint32(tagsize, result));
227
4.27k
      }
228
22.0k
    }
229
865
  }
230
231
  // Main Content
232
18.7k
  for (;;) {
233
18.7k
    if (result->size() > osize) return JXL_FAILURE("Invalid result size");
234
18.7k
    if (cpos > commands_end) return JXL_FAILURE("Out of bounds");
235
18.7k
    if (cpos == commands_end) break;  // Valid end
236
18.3k
    uint8_t command = enc[cpos++];
237
18.3k
    if (command == kCommandInsert) {
238
3.33k
      uint64_t num;
239
3.33k
      JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &num));
240
3.32k
      JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, num, size));
241
45.7k
      for (size_t i = 0; i < num; i++) {
242
42.4k
        JXL_RETURN_IF_ERROR(result->push_back(enc[pos++]));
243
42.4k
      }
244
14.9k
    } else if (command == kCommandShuffle2 || command == kCommandShuffle4) {
245
5.05k
      uint64_t num;
246
5.05k
      JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &num));
247
5.02k
      JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, num, size));
248
4.99k
      PaddedBytes shuffled(memory_manager);
249
4.99k
      JXL_ASSIGN_OR_RETURN(shuffled,
250
4.99k
                           PaddedBytes::WithInitialSpace(memory_manager, num));
251
88.5k
      for (size_t i = 0; i < num; i++) {
252
83.5k
        shuffled[i] = enc[pos + i];
253
83.5k
      }
254
4.99k
      if (command == kCommandShuffle2) {
255
3.73k
        JXL_RETURN_IF_ERROR(Shuffle(memory_manager, shuffled.data(), num, 2));
256
3.73k
      } else if (command == kCommandShuffle4) {
257
1.26k
        JXL_RETURN_IF_ERROR(Shuffle(memory_manager, shuffled.data(), num, 4));
258
1.26k
      }
259
88.5k
      for (size_t i = 0; i < num; i++) {
260
83.5k
        JXL_RETURN_IF_ERROR(result->push_back(shuffled[i]));
261
83.5k
        pos++;
262
83.5k
      }
263
9.91k
    } else if (command == kCommandPredict) {
264
4.09k
      JXL_RETURN_IF_ERROR(CheckOutOfBounds(cpos, 2, commands_end));
265
4.07k
      uint8_t flags = enc[cpos++];
266
267
4.07k
      size_t width = (flags & 3) + 1;
268
4.07k
      if (width == 3) return JXL_FAILURE("Invalid width");
269
270
4.06k
      int order = (flags & 12) >> 2;
271
4.06k
      if (order == 3) return JXL_FAILURE("Invalid order");
272
273
4.06k
      uint64_t stride = width;
274
4.06k
      if (flags & 16) {
275
786
        JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &stride));
276
782
        if (stride < width) {
277
5
          return JXL_FAILURE("Invalid stride");
278
5
        }
279
782
      }
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
4.05k
      if (result->empty() || ((result->size() - 1u) >> 2u) < stride) {
286
40
        return JXL_FAILURE("Invalid stride");
287
40
      }
288
289
4.01k
      uint64_t num;
290
4.01k
      JXL_RETURN_IF_ERROR(DecodeVarInt(enc, commands_end, &cpos, &num));  // in bytes
291
4.00k
      JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, num, size));
292
293
3.97k
      PaddedBytes shuffled(memory_manager);
294
3.97k
      JXL_ASSIGN_OR_RETURN(shuffled,
295
3.97k
                           PaddedBytes::WithInitialSpace(memory_manager, num));
296
297
110k
      for (size_t i = 0; i < num; i++) {
298
106k
        shuffled[i] = enc[pos + i];
299
106k
      }
300
3.97k
      if (width > 1) {
301
1.60k
        JXL_RETURN_IF_ERROR(
302
1.60k
            Shuffle(memory_manager, shuffled.data(), num, width));
303
1.60k
      }
304
305
3.97k
      size_t start = result->size();
306
110k
      for (size_t i = 0; i < num; i++) {
307
106k
        uint8_t predicted = LinearPredictICCValue(result->data(), start, i,
308
106k
                                                  stride, width, order);
309
106k
        JXL_RETURN_IF_ERROR(result->push_back(predicted + shuffled[i]));
310
106k
      }
311
3.97k
      pos += num;
312
5.82k
    } else if (command == kCommandXYZ) {
313
1.64k
      JXL_RETURN_IF_ERROR(AppendKeyword(kXyz_Tag, result));
314
8.22k
      for (int i = 0; i < 4; i++) {
315
6.58k
        JXL_RETURN_IF_ERROR(result->push_back(0));
316
6.58k
      }
317
1.64k
      JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, 12, size));
318
21.2k
      for (size_t i = 0; i < 12; i++) {
319
19.6k
        JXL_RETURN_IF_ERROR(result->push_back(enc[pos++]));
320
19.6k
      }
321
4.18k
    } else if (command >= kCommandTypeStartFirst &&
322
4.06k
               command < kCommandTypeStartFirst + kNumTypeStrings) {
323
3.93k
      JXL_RETURN_IF_ERROR(AppendKeyword(
324
3.93k
          *kTypeStrings[command - kCommandTypeStartFirst], result));
325
19.6k
      for (size_t i = 0; i < 4; i++) {
326
15.7k
        JXL_RETURN_IF_ERROR(result->push_back(0));
327
15.7k
      }
328
3.93k
    } else {
329
249
      return JXL_FAILURE("Unknown command");
330
249
    }
331
18.3k
  }
332
333
449
  if (pos != size) return JXL_FAILURE("Not all data used");
334
320
  if (result->size() != osize) return JXL_FAILURE("Invalid result size");
335
336
315
  return true;
337
320
}
338
339
4.67k
Status ICCReader::Init(BitReader* reader) {
340
4.67k
  JXL_RETURN_IF_ERROR(CheckEOI(reader));
341
4.67k
  JxlMemoryManager* memory_manager = decompressed_.memory_manager();
342
4.67k
  used_bits_base_ = reader->TotalBitsConsumed();
343
4.67k
  if (bits_to_skip_ == 0) {
344
4.08k
    enc_size_ = U64Coder::Read(reader);
345
4.08k
    if (enc_size_ > 268435456) {
346
      // Avoid too large memory allocation for invalid file.
347
116
      return JXL_FAILURE("Too large encoded profile");
348
116
    }
349
3.97k
    JXL_RETURN_IF_ERROR(DecodeHistograms(
350
3.97k
        memory_manager, reader, kNumICCContexts, &code_, &context_map_));
351
6.06k
    JXL_ASSIGN_OR_RETURN(ans_reader_, ANSSymbolReader::Create(&code_, reader));
352
6.06k
    i_ = 0;
353
6.06k
    JXL_RETURN_IF_ERROR(
354
6.06k
        decompressed_.resize(std::min<size_t>(i_ + 0x400, enc_size_)));
355
8.71k
    for (; i_ < std::min<size_t>(2, enc_size_); i_++) {
356
5.68k
      decompressed_[i_] = ans_reader_.ReadHybridUint(
357
5.68k
          ICCANSContext(i_, i_ > 0 ? decompressed_[i_ - 1] : 0,
358
5.68k
                        i_ > 1 ? decompressed_[i_ - 2] : 0),
359
5.68k
          reader, context_map_);
360
5.68k
    }
361
3.03k
    if (enc_size_ > kPreambleSize) {
362
47.5k
      for (; i_ < kPreambleSize; i_++) {
363
45.0k
        decompressed_[i_] = ans_reader_.ReadHybridUint(
364
45.0k
            ICCANSContext(i_, decompressed_[i_ - 1], decompressed_[i_ - 2]),
365
45.0k
            reader, context_map_);
366
45.0k
      }
367
2.50k
      JXL_RETURN_IF_ERROR(CheckEOI(reader));
368
1.80k
      JXL_RETURN_IF_ERROR(CheckPreamble(decompressed_, enc_size_));
369
1.80k
    }
370
2.23k
    bits_to_skip_ = reader->TotalBitsConsumed() - used_bits_base_;
371
2.23k
  } else {
372
589
    reader->SkipBits(bits_to_skip_);
373
589
  }
374
2.82k
  return true;
375
4.67k
}
376
377
2.28k
Status ICCReader::Process(BitReader* reader, PaddedBytes* icc) {
378
2.28k
  auto checkpoint = jxl::make_unique<ANSSymbolReader::Checkpoint>();
379
2.28k
  size_t saved_i = 0;
380
16.6k
  auto save = [&]() {
381
16.6k
    ans_reader_.Save(checkpoint.get());
382
16.6k
    bits_to_skip_ = reader->TotalBitsConsumed() - used_bits_base_;
383
16.6k
    saved_i = i_;
384
16.6k
  };
385
2.28k
  save();
386
16.6k
  auto check_and_restore = [&]() -> Status {
387
16.6k
    Status status = CheckEOI(reader);
388
16.6k
    if (!status) {
389
      // not enough bytes.
390
638
      ans_reader_.Restore(*checkpoint);
391
638
      i_ = saved_i;
392
638
      return status;
393
638
    }
394
16.0k
    return true;
395
16.6k
  };
396
7.95M
  for (; i_ < enc_size_; i_++) {
397
7.95M
    if (i_ % ANSSymbolReader::kMaxCheckpointInterval == 0 && i_ > 0) {
398
14.6k
      JXL_RETURN_IF_ERROR(check_and_restore());
399
14.3k
      save();
400
14.3k
      if ((i_ > 0) && (((i_ & 0xFFFF) == 0))) {
401
81
        float used_bytes =
402
81
            (reader->TotalBitsConsumed() - used_bits_base_) / 8.0f;
403
81
        if (i_ > used_bytes * 256) return JXL_FAILURE("Corrupted stream");
404
81
      }
405
14.3k
      JXL_RETURN_IF_ERROR(
406
14.3k
          decompressed_.resize(std::min<size_t>(i_ + 0x400, enc_size_)));
407
14.3k
    }
408
7.95M
    JXL_ENSURE(i_ >= 2);
409
7.95M
    decompressed_[i_] = ans_reader_.ReadHybridUint(
410
7.95M
        ICCANSContext(i_, decompressed_[i_ - 1], decompressed_[i_ - 2]), reader,
411
7.95M
        context_map_);
412
7.95M
  }
413
1.97k
  JXL_RETURN_IF_ERROR(check_and_restore());
414
1.64k
  bits_to_skip_ = reader->TotalBitsConsumed() - used_bits_base_;
415
1.64k
  if (!ans_reader_.CheckANSFinalState()) {
416
0
    return JXL_FAILURE("Corrupted ICC profile");
417
0
  }
418
419
1.64k
  icc->clear();
420
1.64k
  return UnpredictICC(decompressed_.data(), decompressed_.size(), icc);
421
1.64k
}
422
423
23.8k
Status ICCReader::CheckEOI(BitReader* reader) {
424
23.8k
  if (reader->AllReadsWithinBounds()) return true;
425
1.33k
  return JXL_NOT_ENOUGH_BYTES("Not enough bytes for reading ICC profile");
426
23.8k
}
427
428
}  // namespace jxl