Coverage Report

Created: 2026-11-12 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/jxl/dec_frame.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/dec_frame.h"
7
8
#include <jxl/decode.h>
9
#include <jxl/memory_manager.h>
10
#include <jxl/types.h>
11
12
#include <algorithm>
13
#include <cstddef>
14
#include <cstdint>
15
#include <cstdlib>
16
#include <memory>
17
#include <utility>
18
#include <vector>
19
20
#include "lib/jxl/ac_context.h"
21
#include "lib/jxl/ac_strategy.h"
22
#include "lib/jxl/base/bits.h"
23
#include "lib/jxl/base/common.h"
24
#include "lib/jxl/base/compiler_specific.h"
25
#include "lib/jxl/base/data_parallel.h"
26
#include "lib/jxl/base/printf_macros.h"
27
#include "lib/jxl/base/rect.h"
28
#include "lib/jxl/base/span.h"
29
#include "lib/jxl/base/status.h"
30
#include "lib/jxl/chroma_from_luma.h"
31
#include "lib/jxl/coeff_order.h"
32
#include "lib/jxl/coeff_order_fwd.h"
33
#include "lib/jxl/common.h"  // kMaxNumPasses
34
#include "lib/jxl/compressed_dc.h"
35
#include "lib/jxl/dct_util.h"
36
#include "lib/jxl/dec_ans.h"
37
#include "lib/jxl/dec_bit_reader.h"
38
#include "lib/jxl/dec_cache.h"
39
#include "lib/jxl/dec_group.h"
40
#include "lib/jxl/dec_modular.h"
41
#include "lib/jxl/dec_noise.h"
42
#include "lib/jxl/dec_patch_dictionary.h"
43
#include "lib/jxl/entropy_coder.h"
44
#include "lib/jxl/epf.h"
45
#include "lib/jxl/fields.h"
46
#include "lib/jxl/frame_dimensions.h"
47
#include "lib/jxl/frame_header.h"
48
#include "lib/jxl/image_bundle.h"
49
#include "lib/jxl/image_metadata.h"
50
#include "lib/jxl/image_ops.h"
51
#include "lib/jxl/jpeg/jpeg_data.h"
52
#include "lib/jxl/loop_filter.h"
53
#include "lib/jxl/passes_state.h"
54
#include "lib/jxl/quant_weights.h"
55
#include "lib/jxl/quantizer.h"
56
#include "lib/jxl/render_pipeline/render_pipeline.h"
57
#include "lib/jxl/splines.h"
58
#include "lib/jxl/toc.h"
59
60
namespace jxl {
61
62
namespace {
63
Status DecodeGlobalDCInfo(BitReader* reader, bool is_jpeg,
64
8.96k
                          PassesDecoderState* state, ThreadPool* pool) {
65
8.96k
  JXL_RETURN_IF_ERROR(state->shared_storage.quantizer.Decode(reader));
66
67
8.95k
  JXL_RETURN_IF_ERROR(DecodeBlockCtxMap(state->memory_manager(), reader,
68
8.95k
                                        &state->shared_storage.block_ctx_map));
69
70
8.87k
  JXL_RETURN_IF_ERROR(state->shared_storage.cmap.DecodeDC(reader));
71
72
  // Pre-compute info for decoding a group.
73
8.84k
  if (is_jpeg) {
74
0
    state->shared_storage.quantizer.ClearDCMul();  // Don't dequant DC
75
0
  }
76
77
8.84k
  state->shared_storage.ac_strategy.FillInvalid();
78
8.84k
  return true;
79
8.87k
}
80
}  // namespace
81
82
Status DecodeFrame(PassesDecoderState* dec_state, ThreadPool* JXL_RESTRICT pool,
83
                   const uint8_t* next_in, size_t avail_in,
84
                   FrameHeader* frame_header, ImageBundle* decoded,
85
                   const CodecMetadata& metadata,
86
793
                   bool use_slow_rendering_pipeline) {
87
793
  FrameDecoder frame_decoder(dec_state, metadata, pool,
88
793
                             use_slow_rendering_pipeline);
89
90
793
  BitReader reader(Bytes(next_in, avail_in));
91
793
  JXL_RETURN_IF_ERROR(frame_decoder.InitFrame(&reader, decoded,
92
793
                                              /*is_preview=*/false));
93
793
  JXL_RETURN_IF_ERROR(frame_decoder.InitFrameOutput());
94
793
  if (frame_header) {
95
0
    *frame_header = frame_decoder.GetFrameHeader();
96
0
  }
97
98
793
  JXL_RETURN_IF_ERROR(reader.AllReadsWithinBounds());
99
793
  size_t header_bytes = reader.TotalBitsConsumed() / kBitsPerByte;
100
793
  JXL_RETURN_IF_ERROR(reader.Close());
101
102
793
  size_t processed_bytes = header_bytes;
103
793
  Status close_ok = true;
104
793
  std::vector<std::unique_ptr<BitReader>> section_readers;
105
793
  {
106
793
    std::vector<std::unique_ptr<BitReaderScopedCloser>> section_closers;
107
793
    std::vector<FrameDecoder::SectionInfo> section_info;
108
793
    std::vector<FrameDecoder::SectionStatus> section_status;
109
793
    size_t pos = header_bytes;
110
793
    size_t index = 0;
111
793
    for (auto toc_entry : frame_decoder.Toc()) {
112
793
      JXL_RETURN_IF_ERROR(pos + toc_entry.size <= avail_in);
113
793
      auto br = make_unique<BitReader>(Bytes(next_in + pos, toc_entry.size));
114
793
      section_info.emplace_back(
115
793
          FrameDecoder::SectionInfo{br.get(), toc_entry.id, index++});
116
793
      section_closers.emplace_back(
117
793
          make_unique<BitReaderScopedCloser>(*br, close_ok));
118
793
      section_readers.emplace_back(std::move(br));
119
793
      pos += toc_entry.size;
120
793
    }
121
793
    section_status.resize(section_info.size());
122
793
    JXL_RETURN_IF_ERROR(frame_decoder.ProcessSections(
123
793
        section_info.data(), section_info.size(), section_status.data()));
124
1.58k
    for (size_t i = 0; i < section_status.size(); i++) {
125
793
      JXL_RETURN_IF_ERROR(section_status[i] == FrameDecoder::kDone);
126
793
      processed_bytes += frame_decoder.Toc()[i].size;
127
793
    }
128
793
  }
129
793
  JXL_RETURN_IF_ERROR(close_ok);
130
793
  JXL_RETURN_IF_ERROR(frame_decoder.FinalizeFrame());
131
793
  decoded->SetDecodedBytes(processed_bytes);
132
793
  return true;
133
793
}
134
135
Status FrameDecoder::InitFrame(BitReader* JXL_RESTRICT br, ImageBundle* decoded,
136
60.2k
                               bool is_preview) {
137
60.2k
  decoded_ = decoded;
138
60.2k
  JXL_ENSURE(is_finalized_);
139
60.2k
  JxlMemoryManager* memory_manager = decoded_->memory_manager();
140
141
  // Reset the dequantization matrices to their default values.
142
60.2k
  dec_state_->shared_storage.matrices = DequantMatrices();
143
144
60.2k
  frame_header_.nonserialized_is_preview = is_preview;
145
60.2k
  JXL_ENSURE(frame_header_.nonserialized_metadata != nullptr);
146
60.2k
  JXL_RETURN_IF_ERROR(ReadFrameHeader(br, &frame_header_));
147
56.0k
  frame_dim_ = frame_header_.ToFrameDimensions();
148
56.0k
  JXL_DEBUG_V(2, "FrameHeader: %s", frame_header_.DebugString().c_str());
149
150
56.0k
  const size_t num_passes = frame_header_.passes.num_passes;
151
56.0k
  const size_t num_groups = frame_dim_.num_groups;
152
153
  // If the previous frame was not a kRegularFrame, `decoded` may have different
154
  // dimensions; must reset to avoid errors.
155
56.0k
  decoded->RemoveColor();
156
56.0k
  decoded->ClearExtraChannels();
157
158
56.0k
  decoded->duration = frame_header_.animation_frame.duration;
159
160
56.0k
  if (!frame_header_.nonserialized_is_preview &&
161
55.8k
      (frame_header_.is_last || frame_header_.animation_frame.duration > 0) &&
162
10.6k
      (frame_header_.frame_type == kRegularFrame ||
163
10.6k
       frame_header_.frame_type == kSkipProgressive)) {
164
10.6k
    ++dec_state_->visible_frame_index;
165
10.6k
    dec_state_->nonvisible_frame_index = 0;
166
45.4k
  } else {
167
45.4k
    ++dec_state_->nonvisible_frame_index;
168
45.4k
  }
169
170
  // Read TOC.
171
56.0k
  const size_t toc_entries =
172
56.0k
      NumTocEntries(num_groups, frame_dim_.num_dc_groups, num_passes);
173
56.0k
  std::vector<uint32_t> sizes;
174
56.0k
  std::vector<coeff_order_t> permutation;
175
56.0k
  JXL_RETURN_IF_ERROR(
176
56.0k
      ReadToc(memory_manager, toc_entries, br, &sizes, &permutation));
177
50.5k
  bool have_permutation = !permutation.empty();
178
50.5k
  toc_.resize(toc_entries);
179
50.5k
  section_sizes_sum_ = 0;
180
181k
  for (size_t i = 0; i < toc_entries; ++i) {
181
130k
    toc_[i].size = sizes[i];
182
130k
    size_t index = have_permutation ? permutation[i] : i;
183
130k
    toc_[index].id = i;
184
130k
    if (section_sizes_sum_ + toc_[i].size < section_sizes_sum_) {
185
0
      return JXL_FAILURE("group offset overflow");
186
0
    }
187
130k
    section_sizes_sum_ += toc_[i].size;
188
130k
  }
189
190
50.5k
  if (JXL_DEBUG_V_LEVEL >= 3) {
191
0
    for (size_t i = 0; i < toc_entries; ++i) {
192
0
      JXL_DEBUG_V(3, "TOC entry %" PRIuS " size %" PRIuS " id %" PRIuS "", i,
193
0
                  toc_[i].size, toc_[i].id);
194
0
    }
195
0
  }
196
197
50.5k
  JXL_ENSURE((br->TotalBitsConsumed() % kBitsPerByte) == 0);
198
50.5k
  const size_t group_codes_begin = br->TotalBitsConsumed() / kBitsPerByte;
199
50.5k
  JXL_ENSURE(!toc_.empty());
200
201
  // Overflow check.
202
50.5k
  if (group_codes_begin + section_sizes_sum_ < group_codes_begin) {
203
0
    return JXL_FAILURE("Invalid group codes");
204
0
  }
205
50.5k
  return true;
206
50.5k
}
207
208
42.2k
Status FrameDecoder::InitFrameOutput() {
209
42.2k
  JXL_RETURN_IF_ERROR(
210
42.2k
      InitializePassesSharedState(frame_header_, &dec_state_->shared_storage));
211
42.2k
  JXL_RETURN_IF_ERROR(dec_state_->Init(frame_header_));
212
42.2k
  modular_frame_decoder_.Init(frame_dim_);
213
214
42.2k
  if (decoded_->IsJPEG()) {
215
0
    if (frame_header_.encoding == FrameEncoding::kModular) {
216
0
      return JXL_FAILURE("Cannot output JPEG from Modular");
217
0
    }
218
0
    jpeg::JPEGData* jpeg_data = decoded_->jpeg_data.get();
219
0
    size_t num_components = jpeg_data->components.size();
220
0
    if (num_components != 1 && num_components != 3) {
221
0
      return JXL_FAILURE("Invalid number of components");
222
0
    }
223
0
    if (frame_header_.nonserialized_metadata->m.xyb_encoded) {
224
0
      return JXL_FAILURE("Cannot decode to JPEG an XYB image");
225
0
    }
226
0
    bool is_gray = (num_components == 1);
227
0
    auto jpeg_c_map = JpegOrder(ColorTransform::kYCbCr, is_gray);
228
0
    if (frame_dim_.xsize > static_cast<size_t>(jpeg::kMaxDimPixels) ||
229
0
        frame_dim_.ysize > static_cast<size_t>(jpeg::kMaxDimPixels)) {
230
0
      return JXL_FAILURE("Frame dimensions too large for JPEG reconstruction");
231
0
    }
232
0
    decoded_->jpeg_data->width = static_cast<int>(frame_dim_.xsize);
233
0
    decoded_->jpeg_data->height = static_cast<int>(frame_dim_.ysize);
234
0
    for (size_t c = 0; c < 3; ++c) {
235
0
      if (is_gray && c != 1) continue;
236
0
      auto& component = jpeg_data->components[jpeg_c_map[c]];
237
0
      component.width_in_blocks =
238
0
          frame_dim_.xsize_blocks >> frame_header_.chroma_subsampling.HShift(c);
239
0
      component.height_in_blocks =
240
0
          frame_dim_.ysize_blocks >> frame_header_.chroma_subsampling.VShift(c);
241
0
      component.h_samp_factor =
242
0
          1 << frame_header_.chroma_subsampling.RawHShift(c);
243
0
      component.v_samp_factor =
244
0
          1 << frame_header_.chroma_subsampling.RawVShift(c);
245
0
      size_t num_blocks;
246
0
      if (!SafeMul(static_cast<size_t>(component.width_in_blocks),
247
0
                   static_cast<size_t>(component.height_in_blocks),
248
0
                   num_blocks)) {
249
0
        return JXL_FAILURE("JPEG component block count overflow");
250
0
      }
251
0
      size_t num_coeffs;
252
0
      if (!SafeMul(num_blocks, jxl::kDCTBlockSize, num_coeffs)) {
253
0
        return JXL_FAILURE("JPEG component coefficient count overflow");
254
0
      }
255
0
      component.coeffs.resize(num_coeffs);
256
0
    }
257
0
  }
258
259
  // Clear the state.
260
42.2k
  decoded_dc_global_ = false;
261
42.2k
  decoded_ac_global_ = false;
262
42.2k
  is_finalized_ = false;
263
42.2k
  finalized_dc_ = false;
264
42.2k
  num_sections_done_ = 0;
265
42.2k
  decoded_dc_groups_.clear();
266
42.2k
  decoded_dc_groups_.resize(frame_dim_.num_dc_groups);
267
42.2k
  decoded_passes_per_ac_group_.clear();
268
42.2k
  decoded_passes_per_ac_group_.resize(frame_dim_.num_groups, 0);
269
42.2k
  processed_section_.clear();
270
42.2k
  processed_section_.resize(toc_.size());
271
42.2k
  allocated_ = false;
272
42.2k
  return true;
273
42.2k
}
274
275
41.5k
Status FrameDecoder::ProcessDCGlobal(BitReader* br) {
276
41.5k
  PassesSharedState& shared = dec_state_->shared_storage;
277
41.5k
  JxlMemoryManager* memory_manager = shared.memory_manager;
278
41.5k
  if (frame_header_.flags & FrameHeader::kPatches) {
279
1.17k
    bool uses_extra_channels;
280
1.17k
    JXL_RETURN_IF_ERROR(shared.image_features.patches.Decode(
281
1.17k
        memory_manager, br, frame_dim_.xsize_padded, frame_dim_.ysize_padded,
282
1.17k
        shared.metadata->m.num_extra_channels, &uses_extra_channels));
283
1.09k
    if (frame_header_.upsampling != 1) {
284
629
      for (size_t ecups : frame_header_.extra_channel_upsampling) {
285
88
        if (ecups != frame_header_.upsampling) {
286
3
          return JXL_FAILURE(
287
3
              "Cannot use extra channels in patches if color channels are "
288
3
              "subsampled differently from extra channels");
289
3
        }
290
88
      }
291
629
    }
292
40.3k
  } else {
293
40.3k
    shared.image_features.patches.Clear();
294
40.3k
  }
295
41.4k
  shared.image_features.splines.Clear();
296
41.4k
  if (frame_header_.flags & FrameHeader::kSplines) {
297
3.21k
    JXL_RETURN_IF_ERROR(shared.image_features.splines.Decode(
298
3.21k
        br, frame_dim_.xsize * frame_dim_.ysize));
299
3.21k
  }
300
41.2k
  if (frame_header_.flags & FrameHeader::kNoise) {
301
6.32k
    JXL_RETURN_IF_ERROR(DecodeNoise(br, &shared.image_features.noise_params));
302
6.32k
  }
303
41.2k
  JXL_RETURN_IF_ERROR(dec_state_->shared_storage.matrices.DecodeDC(br));
304
305
40.6k
  if (frame_header_.encoding == FrameEncoding::kVarDCT) {
306
8.96k
    JXL_RETURN_IF_ERROR(
307
8.96k
        jxl::DecodeGlobalDCInfo(br, decoded_->IsJPEG(), dec_state_, pool_));
308
8.96k
  }
309
  // Splines' draw cache uses the color correlation map.
310
40.5k
  if (frame_header_.flags & FrameHeader::kSplines) {
311
2.94k
    JXL_RETURN_IF_ERROR(shared.image_features.splines.InitializeDrawCache(
312
2.94k
        frame_dim_.xsize_upsampled, frame_dim_.ysize_upsampled,
313
2.94k
        dec_state_->shared->cmap.base()));
314
2.94k
  }
315
40.4k
  Status dec_status = modular_frame_decoder_.DecodeGlobalInfo(
316
40.4k
      br, frame_header_, /*allow_truncated_group=*/false);
317
40.4k
  if (dec_status.IsFatalError()) return dec_status;
318
37.6k
  if (dec_status) {
319
37.4k
    decoded_dc_global_ = true;
320
37.4k
  }
321
37.6k
  return dec_status;
322
40.4k
}
323
324
37.6k
Status FrameDecoder::ProcessDCGroup(size_t dc_group_id, BitReader* br) {
325
37.6k
  const size_t gx = dc_group_id % frame_dim_.xsize_dc_groups;
326
37.6k
  const size_t gy = dc_group_id / frame_dim_.xsize_dc_groups;
327
37.6k
  if (frame_header_.encoding == FrameEncoding::kVarDCT &&
328
8.71k
      !(frame_header_.flags & FrameHeader::kUseDcFrame)) {
329
8.70k
    JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeVarDCTDC(
330
8.70k
        frame_header_, dc_group_id, br, dec_state_));
331
8.70k
  }
332
36.1k
  const Rect mrect(gx * frame_dim_.dc_group_dim, gy * frame_dim_.dc_group_dim,
333
36.1k
                   frame_dim_.dc_group_dim, frame_dim_.dc_group_dim);
334
36.1k
  JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeGroup(
335
36.1k
      frame_header_, mrect, br, 3, 1000,
336
36.1k
      ModularStreamId::ModularDC(dc_group_id),
337
36.1k
      /*zerofill=*/false, nullptr, nullptr,
338
36.1k
      /*allow_truncated=*/false));
339
36.1k
  if (frame_header_.encoding == FrameEncoding::kVarDCT) {
340
7.27k
    JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeAcMetadata(
341
7.27k
        frame_header_, dc_group_id, br, dec_state_));
342
7.27k
  }
343
34.6k
  decoded_dc_groups_[dc_group_id] = JXL_TRUE;
344
34.6k
  return true;
345
36.1k
}
346
347
34.3k
Status FrameDecoder::FinalizeDC() {
348
  // Do Adaptive DC smoothing if enabled. This *must* happen between all the
349
  // ProcessDCGroup and ProcessACGroup.
350
34.3k
  JxlMemoryManager* memory_manager = dec_state_->memory_manager();
351
34.3k
  if (frame_header_.encoding == FrameEncoding::kVarDCT &&
352
5.75k
      !(frame_header_.flags & FrameHeader::kSkipAdaptiveDCSmoothing) &&
353
5.15k
      !(frame_header_.flags & FrameHeader::kUseDcFrame) &&
354
      // Skip smoothing when reconstructing to JPEG.
355
5.14k
      !decoded_->IsJPEG()) {
356
5.14k
    JXL_RETURN_IF_ERROR(AdaptiveDCSmoothing(
357
5.14k
        memory_manager, dec_state_->shared->quantizer.MulDC(),
358
5.14k
        &dec_state_->shared_storage.dc_storage,
359
5.14k
        frame_header_.chroma_subsampling, pool_));
360
5.14k
  }
361
34.3k
  if (frame_header_.encoding == FrameEncoding::kModular &&
362
28.5k
      frame_header_.loop_filter.epf_iters > 0) {
363
10.4k
    FillImage(kInvSigmaNum / frame_header_.loop_filter.epf_sigma_for_modular,
364
10.4k
              &dec_state_->sigma);
365
10.4k
  }
366
367
34.3k
  finalized_dc_ = true;
368
34.3k
  return true;
369
34.3k
}
370
371
34.3k
Status FrameDecoder::AllocateOutput() {
372
34.3k
  if (allocated_) return true;
373
34.3k
  modular_frame_decoder_.MaybeDropFullImage();
374
34.3k
  decoded_->origin = frame_header_.frame_origin;
375
34.3k
  JXL_RETURN_IF_ERROR(
376
34.3k
      dec_state_->InitForAC(frame_header_.passes.num_passes, nullptr));
377
34.3k
  allocated_ = true;
378
34.3k
  return true;
379
34.3k
}
380
381
34.2k
Status FrameDecoder::ProcessACGlobal(BitReader* br) {
382
34.2k
  JXL_ENSURE(finalized_dc_);
383
34.2k
  JxlMemoryManager* memory_manager = dec_state_->memory_manager();
384
385
  // Decode AC group.
386
34.2k
  if (frame_header_.encoding == FrameEncoding::kVarDCT) {
387
5.73k
    JXL_RETURN_IF_ERROR(dec_state_->shared_storage.matrices.Decode(
388
5.73k
        memory_manager, br, &modular_frame_decoder_));
389
5.20k
    JXL_RETURN_IF_ERROR(dec_state_->shared_storage.matrices.EnsureComputed(
390
5.20k
        memory_manager, dec_state_->used_acs));
391
392
5.14k
    size_t num_histo_bits =
393
5.14k
        CeilLog2Nonzero(dec_state_->shared->frame_dim.num_groups);
394
5.14k
    dec_state_->shared_storage.num_histograms =
395
5.14k
        1 + br->ReadBits(num_histo_bits);
396
397
5.14k
    JXL_DEBUG_V(3,
398
5.14k
                "Processing AC global with %d passes and %" PRIuS
399
5.14k
                " sets of histograms",
400
5.14k
                frame_header_.passes.num_passes,
401
5.14k
                dec_state_->shared_storage.num_histograms);
402
403
5.14k
    dec_state_->code.resize(kMaxNumPasses);
404
5.14k
    dec_state_->context_map.resize(kMaxNumPasses);
405
    // Read coefficient orders and histograms.
406
5.14k
    size_t max_num_bits_ac = 0;
407
10.2k
    for (size_t i = 0; i < frame_header_.passes.num_passes; i++) {
408
5.32k
      uint16_t used_orders = U32Coder::Read(kOrderEnc, br);
409
5.32k
      JXL_RETURN_IF_ERROR(DecodeCoeffOrders(
410
5.32k
          memory_manager, used_orders, dec_state_->used_acs,
411
5.32k
          &dec_state_->shared_storage
412
5.32k
               .coeff_orders[i * dec_state_->shared_storage.coeff_order_size],
413
5.32k
          br));
414
5.23k
      size_t num_contexts =
415
5.23k
          dec_state_->shared->num_histograms *
416
5.23k
          dec_state_->shared_storage.block_ctx_map.NumACContexts();
417
5.23k
      JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, num_contexts,
418
5.23k
                                           &dec_state_->code[i],
419
5.23k
                                           &dec_state_->context_map[i]));
420
      // Add extra values to enable the cheat in hot loop of DecodeACVarBlock.
421
5.14k
      dec_state_->context_map[i].resize(
422
5.14k
          num_contexts + kZeroDensityContextLimit - kZeroDensityContextCount);
423
5.14k
      max_num_bits_ac =
424
5.14k
          std::max(max_num_bits_ac, dec_state_->code[i].max_num_bits);
425
5.14k
    }
426
4.96k
    max_num_bits_ac += CeilLog2Nonzero(frame_header_.passes.num_passes);
427
    // 16-bit buffer for decoding to JPEG are not implemented.
428
    // TODO(veluca): figure out the exact limit - 16 should still work with
429
    // 16-bit buffers, but we are excluding it for safety.
430
4.96k
    bool use_16_bit = max_num_bits_ac < 16 && !decoded_->IsJPEG();
431
4.96k
    bool store = frame_header_.passes.num_passes > 1;
432
4.96k
    size_t xs = store ? kGroupDim * kGroupDim : 0;
433
4.96k
    size_t ys = store ? frame_dim_.num_groups : 0;
434
4.96k
    if (use_16_bit) {
435
4.22k
      JXL_ASSIGN_OR_RETURN(dec_state_->coefficients,
436
4.22k
                           ACImageT<int16_t>::Make(memory_manager, xs, ys));
437
4.22k
    } else {
438
738
      JXL_ASSIGN_OR_RETURN(dec_state_->coefficients,
439
738
                           ACImageT<int32_t>::Make(memory_manager, xs, ys));
440
738
    }
441
4.96k
    if (store) {
442
26
      dec_state_->coefficients->ZeroFill();
443
26
    }
444
4.96k
  }
445
446
  // Set JPEG decoding data.
447
33.4k
  if (decoded_->IsJPEG()) {
448
0
    decoded_->color_transform = frame_header_.color_transform;
449
0
    decoded_->chroma_subsampling = frame_header_.chroma_subsampling;
450
0
    const std::vector<QuantEncoding>& qe =
451
0
        dec_state_->shared_storage.matrices.encodings();
452
0
    if (qe.empty() || qe[0].mode != QuantEncoding::Mode::kQuantModeRAW ||
453
0
        std::abs(qe[0].qraw.qtable_den - 1.f / (8 * 255)) > 1e-8f) {
454
0
      return JXL_FAILURE(
455
0
          "Quantization table is not a JPEG quantization table.");
456
0
    }
457
0
    jpeg::JPEGData* jpeg_data = decoded_->jpeg_data.get();
458
0
    size_t num_components = jpeg_data->components.size();
459
0
    bool is_gray = (num_components == 1);
460
0
    JXL_ENSURE(frame_header_.color_transform != ColorTransform::kXYB);
461
0
    auto jpeg_c_map = JpegOrder(frame_header_.color_transform, is_gray);
462
0
    size_t qt_set = 0;
463
0
    JXL_ENSURE(num_components <= 3);
464
0
    JXL_ENSURE(qe[0].qraw.qtable->size() == 3 * 8 * 8);
465
0
    int* qtable = qe[0].qraw.qtable->data();
466
0
    for (size_t c = 0; c < num_components; c++) {
467
      // TODO(eustas): why 1-st quant table for gray?
468
0
      size_t quant_c = is_gray ? 1 : c;
469
0
      size_t qpos = jpeg_data->components[jpeg_c_map[c]].quant_idx;
470
0
      JXL_ENSURE(qpos != jpeg_data->quant.size());
471
0
      qt_set |= 1 << qpos;
472
0
      for (size_t x = 0; x < 8; x++) {
473
0
        for (size_t y = 0; y < 8; y++) {
474
0
          jpeg_data->quant[qpos].values[x * 8 + y] =
475
0
              qtable[quant_c * 64 + y * 8 + x];
476
0
        }
477
0
      }
478
0
    }
479
0
    for (size_t i = 0; i < jpeg_data->quant.size(); i++) {
480
0
      if (qt_set & (1 << i)) continue;
481
0
      if (i == 0) return JXL_FAILURE("First quant table unused.");
482
      // Unused quant table is set to copy of previous quant table
483
0
      for (size_t j = 0; j < 64; j++) {
484
0
        jpeg_data->quant[i].values[j] = jpeg_data->quant[i - 1].values[j];
485
0
      }
486
0
    }
487
0
  }
488
33.4k
  decoded_ac_global_ = true;
489
33.4k
  return true;
490
33.4k
}
491
492
Status FrameDecoder::ProcessACGroup(size_t ac_group_id, PassesReaders& br,
493
                                    size_t num_passes, size_t thread,
494
36.2k
                                    bool force_draw, bool dc_only) {
495
36.2k
  size_t group_dim = frame_dim_.group_dim;
496
36.2k
  const size_t gx = ac_group_id % frame_dim_.xsize_groups;
497
36.2k
  const size_t gy = ac_group_id / frame_dim_.xsize_groups;
498
36.2k
  const size_t x = gx * group_dim;
499
36.2k
  const size_t y = gy * group_dim;
500
36.2k
  JxlMemoryManager* memory_manager = dec_state_->memory_manager();
501
36.2k
  JXL_DEBUG_V(3,
502
36.2k
              "Processing AC group %" PRIuS "(%" PRIuS ",%" PRIuS
503
36.2k
              ") group_dim: %" PRIuS " decoded passes: %u new passes: %" PRIuS,
504
36.2k
              ac_group_id, gx, gy, group_dim,
505
36.2k
              decoded_passes_per_ac_group_[ac_group_id], num_passes);
506
507
36.2k
  RenderPipelineInput render_pipeline_input =
508
36.2k
      dec_state_->render_pipeline->GetInputBuffers(ac_group_id, thread);
509
510
36.2k
  bool should_run_pipeline = true;
511
512
36.2k
  if (frame_header_.encoding == FrameEncoding::kVarDCT) {
513
5.66k
    JXL_RETURN_IF_ERROR(group_dec_caches_[thread].InitOnce(
514
5.66k
        memory_manager, frame_header_.passes.num_passes, dec_state_->used_acs));
515
5.66k
    JXL_RETURN_IF_ERROR(DecodeGroup(
516
5.66k
        frame_header_, br.data(), num_passes, ac_group_id, dec_state_,
517
5.66k
        &group_dec_caches_[thread], thread, render_pipeline_input,
518
5.66k
        decoded_->jpeg_data.get(), decoded_passes_per_ac_group_[ac_group_id],
519
5.66k
        force_draw, dc_only, &should_run_pipeline));
520
5.66k
  }
521
522
  // don't limit to image dimensions here (is done in DecodeGroup)
523
35.8k
  const Rect mrect(x, y, group_dim, group_dim);
524
35.8k
  bool modular_ready = false;
525
35.8k
  size_t pass0 = decoded_passes_per_ac_group_[ac_group_id];
526
35.8k
  size_t pass1 =
527
35.8k
      force_draw ? frame_header_.passes.num_passes : pass0 + num_passes;
528
74.3k
  for (size_t i = pass0; i < pass1; ++i) {
529
38.8k
    int minShift;
530
38.8k
    int maxShift;
531
38.8k
    frame_header_.passes.GetDownsamplingBracket(i, minShift, maxShift);
532
38.8k
    bool modular_pass_ready = true;
533
38.8k
    JXL_DEBUG_V(2, "Decoding modular in group %d pass %d",
534
38.8k
                static_cast<int>(ac_group_id), static_cast<int>(i));
535
38.8k
    if (i < pass0 + num_passes) {  // i.e. i - pass0 < num_passes
536
38.8k
      BitReader* r = br[i - pass0];
537
38.8k
      JXL_ENSURE(r);
538
38.8k
      JXL_DEBUG_V(2, "Bit reader position: %" PRIuS " / %" PRIuS,
539
38.8k
                  r->TotalBitsConsumed(), r->TotalBytes() * kBitsPerByte);
540
38.8k
      JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeGroup(
541
38.8k
          frame_header_, mrect, r, minShift, maxShift,
542
38.8k
          ModularStreamId::ModularAC(ac_group_id, i),
543
38.8k
          /*zerofill=*/false, dec_state_, &render_pipeline_input,
544
38.8k
          /*allow_truncated=*/false, &modular_pass_ready));
545
38.8k
    } else {
546
0
      JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeGroup(
547
0
          frame_header_, mrect, nullptr, minShift, maxShift,
548
0
          ModularStreamId::ModularAC(ac_group_id, i), /*zerofill=*/true,
549
0
          dec_state_, &render_pipeline_input,
550
0
          /*allow_truncated=*/false, &modular_pass_ready));
551
0
    }
552
38.5k
    if (modular_pass_ready) modular_ready = true;
553
38.5k
  }
554
35.5k
  decoded_passes_per_ac_group_[ac_group_id] += num_passes;
555
556
35.5k
  if ((frame_header_.flags & FrameHeader::kNoise) != 0) {
557
7.91k
    PrepareNoiseInput(*dec_state_, frame_dim_, frame_header_, ac_group_id,
558
7.91k
                      thread);
559
7.91k
  }
560
561
35.5k
  if (!modular_frame_decoder_.UsesFullImage() && !decoded_->IsJPEG()) {
562
6.63k
    if (should_run_pipeline && modular_ready) {
563
6.21k
      JXL_RETURN_IF_ERROR(render_pipeline_input.Done());
564
6.21k
    } else if (force_draw) {
565
0
      return JXL_FAILURE("Modular group decoding failed.");
566
0
    }
567
6.63k
  }
568
35.5k
  return true;
569
35.5k
}
570
571
void FrameDecoder::MarkSections(const SectionInfo* sections, size_t num,
572
33.3k
                                const SectionStatus* section_status) {
573
33.3k
  num_sections_done_ += num;
574
82.1k
  for (size_t i = 0; i < num; i++) {
575
48.7k
    if (section_status[i] != SectionStatus::kDone) {
576
2.26k
      processed_section_[sections[i].id] = JXL_FALSE;
577
2.26k
      num_sections_done_--;
578
2.26k
    }
579
48.7k
  }
580
33.3k
}
581
582
Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
583
43.1k
                                     SectionStatus* section_status) {
584
43.1k
  if (num == 0) return true;  // Nothing to process
585
41.6k
  std::fill(section_status, section_status + num, SectionStatus::kSkipped);
586
41.6k
  size_t dc_global_sec = num;
587
41.6k
  size_t ac_global_sec = num;
588
41.6k
  std::vector<size_t> dc_group_sec(frame_dim_.num_dc_groups, num);
589
41.6k
  std::vector<std::vector<size_t>> ac_group_sec(
590
41.6k
      frame_dim_.num_groups,
591
41.6k
      std::vector<size_t>(frame_header_.passes.num_passes, num));
592
  // This keeps track of the number of ac passes we want to process during this
593
  // call of ProcessSections.
594
41.6k
  std::vector<size_t> desired_num_ac_passes(frame_dim_.num_groups);
595
41.6k
  bool single_section =
596
41.6k
      frame_dim_.num_groups == 1 && frame_header_.passes.num_passes == 1;
597
41.6k
  if (single_section) {
598
36.6k
    JXL_ENSURE(num == 1);
599
36.6k
    JXL_ENSURE(sections[0].id == 0);
600
36.6k
    if (processed_section_[0] == JXL_FALSE) {
601
36.6k
      processed_section_[0] = JXL_TRUE;
602
36.6k
      ac_group_sec[0].resize(1);
603
36.6k
      dc_global_sec = ac_global_sec = dc_group_sec[0] = ac_group_sec[0][0] = 0;
604
36.6k
      desired_num_ac_passes[0] = 1;
605
36.6k
    } else {
606
0
      section_status[0] = SectionStatus::kDuplicate;
607
0
    }
608
36.6k
  } else {
609
4.98k
    size_t ac_global_index = frame_dim_.num_dc_groups + 1;
610
32.8k
    for (size_t i = 0; i < num; i++) {
611
27.8k
      JXL_ENSURE(sections[i].id < processed_section_.size());
612
27.8k
      if (processed_section_[sections[i].id]) {
613
0
        section_status[i] = SectionStatus::kDuplicate;
614
0
        continue;
615
0
      }
616
27.8k
      if (sections[i].id == 0) {
617
4.92k
        dc_global_sec = i;
618
22.9k
      } else if (sections[i].id < ac_global_index) {
619
5.28k
        dc_group_sec[sections[i].id - 1] = i;
620
17.6k
      } else if (sections[i].id == ac_global_index) {
621
3.70k
        ac_global_sec = i;
622
13.9k
      } else {
623
13.9k
        size_t ac_idx = sections[i].id - ac_global_index - 1;
624
13.9k
        size_t acg = ac_idx % frame_dim_.num_groups;
625
13.9k
        size_t acp = ac_idx / frame_dim_.num_groups;
626
13.9k
        if (acp >= frame_header_.passes.num_passes) {
627
0
          return JXL_FAILURE("Invalid section ID");
628
0
        }
629
13.9k
        ac_group_sec[acg][acp] = i;
630
13.9k
      }
631
27.8k
      processed_section_[sections[i].id] = JXL_TRUE;
632
27.8k
    }
633
    // Count number of new passes per group.
634
38.4k
    for (size_t g = 0; g < ac_group_sec.size(); g++) {
635
33.4k
      size_t j = 0;
636
47.0k
      for (; j + decoded_passes_per_ac_group_[g] <
637
47.0k
             frame_header_.passes.num_passes;
638
39.8k
           j++) {
639
39.8k
        if (ac_group_sec[g][j + decoded_passes_per_ac_group_[g]] == num) {
640
26.2k
          break;
641
26.2k
        }
642
39.8k
      }
643
33.4k
      desired_num_ac_passes[g] = j;
644
33.4k
    }
645
4.98k
  }
646
41.6k
  if (dc_global_sec != num) {
647
41.5k
    Status dc_global_status = ProcessDCGlobal(sections[dc_global_sec].br);
648
41.5k
    if (dc_global_status.IsFatalError()) return dc_global_status;
649
37.7k
    if (dc_global_status) {
650
37.4k
      section_status[dc_global_sec] = SectionStatus::kDone;
651
37.4k
    } else {
652
354
      section_status[dc_global_sec] = SectionStatus::kPartial;
653
354
    }
654
37.7k
  }
655
656
37.8k
  if (decoded_dc_global_) {
657
37.4k
    const auto process_section = [this, &dc_group_sec, &num, &sections,
658
37.4k
                                  &section_status](size_t i,
659
38.1k
                                                   size_t thread) -> Status {
660
38.1k
      if (dc_group_sec[i] != num) {
661
37.6k
        JXL_RETURN_IF_ERROR(ProcessDCGroup(i, sections[dc_group_sec[i]].br));
662
34.6k
        section_status[dc_group_sec[i]] = SectionStatus::kDone;
663
34.6k
      }
664
35.1k
      return true;
665
38.1k
    };
666
37.4k
    JXL_RETURN_IF_ERROR(RunOnPool(pool_, 0, dc_group_sec.size(),
667
37.4k
                                  ThreadPool::NoInit, process_section,
668
37.4k
                                  "DecodeDCGroup"));
669
37.4k
  }
670
671
34.8k
  if (!HasDcGroupToDecode() && !finalized_dc_) {
672
34.3k
    PassesDecoderState::PipelineOptions pipeline_options;
673
34.3k
    pipeline_options.use_slow_render_pipeline = use_slow_rendering_pipeline_;
674
34.3k
    pipeline_options.coalescing = coalescing_;
675
34.3k
    pipeline_options.render_spotcolors = render_spotcolors_;
676
34.3k
    pipeline_options.render_noise = true;
677
34.3k
    JXL_RETURN_IF_ERROR(dec_state_->PreparePipeline(
678
34.3k
        frame_header_, &frame_header_.nonserialized_metadata->m, decoded_,
679
34.3k
        pipeline_options));
680
34.3k
    JXL_RETURN_IF_ERROR(FinalizeDC());
681
34.3k
    JXL_RETURN_IF_ERROR(AllocateOutput());
682
34.3k
    if (progressive_detail_ >= JxlProgressiveDetail::kDC) {
683
0
      MarkSections(sections, num, section_status);
684
0
      return true;
685
0
    }
686
34.3k
  }
687
688
34.8k
  if (finalized_dc_ && ac_global_sec != num && !decoded_ac_global_) {
689
34.2k
    JXL_RETURN_IF_ERROR(ProcessACGlobal(sections[ac_global_sec].br));
690
33.4k
    section_status[ac_global_sec] = SectionStatus::kDone;
691
33.4k
  }
692
693
34.0k
  if (progressive_detail_ >= JxlProgressiveDetail::kLastPasses) {
694
    // Mark that we only want the next progression pass.
695
0
    size_t target_complete_passes = NextNumPassesToPause();
696
0
    for (size_t i = 0; i < ac_group_sec.size(); i++) {
697
0
      desired_num_ac_passes[i] =
698
0
          std::min(desired_num_ac_passes[i],
699
0
                   target_complete_passes - decoded_passes_per_ac_group_[i]);
700
0
    }
701
0
  }
702
703
34.0k
  if (decoded_ac_global_) {
704
    // Mark all the AC groups that we received as not complete yet.
705
74.1k
    for (size_t i = 0; i < ac_group_sec.size(); i++) {
706
40.6k
      if (desired_num_ac_passes[i] != 0) {
707
36.4k
        dec_state_->render_pipeline->ClearDone(i);
708
36.4k
      }
709
40.6k
    }
710
711
33.5k
    const auto prepare_storage = [this](size_t num_threads) -> Status {
712
33.5k
      JXL_RETURN_IF_ERROR(
713
33.5k
          PrepareStorage(num_threads, decoded_passes_per_ac_group_.size()));
714
33.4k
      return true;
715
33.5k
    };
716
33.5k
    const auto process_group = [this, &ac_group_sec, &desired_num_ac_passes,
717
33.5k
                                &num, &sections, &section_status](
718
38.6k
                                   size_t g, size_t thread) -> Status {
719
38.6k
      if (desired_num_ac_passes[g] == 0) {
720
        // no new AC pass, nothing to do
721
2.37k
        return true;
722
2.37k
      }
723
36.2k
      size_t first_pass = decoded_passes_per_ac_group_[g];
724
36.2k
      PassesReaders readers = {};
725
75.4k
      for (size_t i = 0; i < desired_num_ac_passes[g]; i++) {
726
39.2k
        JXL_ENSURE(ac_group_sec[g][first_pass + i] != num);
727
39.2k
        readers[i] = sections[ac_group_sec[g][first_pass + i]].br;
728
39.2k
      }
729
36.2k
      JXL_RETURN_IF_ERROR(ProcessACGroup(
730
36.2k
          g, readers, desired_num_ac_passes[g], GetStorageLocation(thread, g),
731
36.2k
          /*force_draw=*/false, /*dc_only=*/false));
732
74.0k
      for (size_t i = 0; i < desired_num_ac_passes[g]; i++) {
733
38.5k
        section_status[ac_group_sec[g][first_pass + i]] = SectionStatus::kDone;
734
38.5k
      }
735
35.5k
      return true;
736
36.2k
    };
737
33.5k
    JXL_RETURN_IF_ERROR(RunOnPool(pool_, 0, ac_group_sec.size(),
738
33.5k
                                  prepare_storage, process_group,
739
33.5k
                                  "DecodeGroup"));
740
33.5k
  }
741
742
33.3k
  MarkSections(sections, num, section_status);
743
33.3k
  return true;
744
34.0k
}
745
746
0
Status FrameDecoder::Flush() {
747
0
  bool has_blending = frame_header_.blending_info.mode != BlendMode::kReplace ||
748
0
                      frame_header_.custom_size_or_origin;
749
0
  for (const auto& blending_info_ec :
750
0
       frame_header_.extra_channel_blending_info) {
751
0
    if (blending_info_ec.mode != BlendMode::kReplace) has_blending = true;
752
0
  }
753
  // No early Flush() if blending is enabled.
754
0
  if (has_blending && !is_finalized_) {
755
0
    return false;
756
0
  }
757
  // No early Flush() - nothing to do - if the frame is a kSkipProgressive
758
  // frame.
759
0
  if (frame_header_.frame_type == FrameType::kSkipProgressive &&
760
0
      !is_finalized_) {
761
0
    return true;
762
0
  }
763
0
  if (decoded_->IsJPEG()) {
764
    // Nothing to do.
765
0
    return true;
766
0
  }
767
0
  JXL_RETURN_IF_ERROR(AllocateOutput());
768
769
0
  uint32_t completely_decoded_ac_pass = *std::min_element(
770
0
      decoded_passes_per_ac_group_.begin(), decoded_passes_per_ac_group_.end());
771
0
  if (completely_decoded_ac_pass < frame_header_.passes.num_passes) {
772
    // We don't have all AC yet: force a draw of all the missing areas.
773
    // Mark all sections as not complete.
774
0
    for (size_t i = 0; i < decoded_passes_per_ac_group_.size(); i++) {
775
0
      if (decoded_passes_per_ac_group_[i] < frame_header_.passes.num_passes) {
776
0
        dec_state_->render_pipeline->ClearDone(i);
777
0
      }
778
0
    }
779
0
    const auto prepare_storage = [this](const size_t num_threads) -> Status {
780
0
      JXL_RETURN_IF_ERROR(
781
0
          PrepareStorage(num_threads, decoded_passes_per_ac_group_.size()));
782
0
      return true;
783
0
    };
784
0
    const auto process_group = [this](const uint32_t g,
785
0
                                      size_t thread) -> Status {
786
0
      if (decoded_passes_per_ac_group_[g] == frame_header_.passes.num_passes) {
787
        // This group was drawn already, nothing to do.
788
0
        return true;
789
0
      }
790
0
      PassesReaders readers = {};
791
0
      JXL_RETURN_IF_ERROR(ProcessACGroup(
792
0
          g, readers, /*num_passes=*/0, GetStorageLocation(thread, g),
793
0
          /*force_draw=*/true, /*dc_only=*/!decoded_ac_global_));
794
0
      return true;
795
0
    };
796
0
    JXL_RETURN_IF_ERROR(RunOnPool(pool_, 0, decoded_passes_per_ac_group_.size(),
797
0
                                  prepare_storage, process_group,
798
0
                                  "ForceDrawGroup"));
799
0
  }
800
801
  // undo global modular transforms and copy int pixel buffers to float ones
802
0
  JXL_RETURN_IF_ERROR(modular_frame_decoder_.FinalizeDecoding(
803
0
      frame_header_, dec_state_, pool_, is_finalized_));
804
805
0
  return true;
806
0
}
807
808
49.6k
int FrameDecoder::SavedAs(const FrameHeader& header) {
809
49.6k
  if (header.frame_type == FrameType::kDCFrame) {
810
    // bits 16, 32, 64, 128 for DC level
811
12.5k
    return 16 << (header.dc_level - 1);
812
37.0k
  } else if (header.CanBeReferenced()) {
813
    // bits 1, 2, 4 and 8 for the references
814
30.9k
    return 1 << header.save_as_reference;
815
30.9k
  }
816
817
6.13k
  return 0;
818
49.6k
}
819
820
31.2k
bool FrameDecoder::HasEverything() const {
821
31.2k
  if (!decoded_dc_global_) return false;
822
31.2k
  if (!decoded_ac_global_) return false;
823
31.2k
  if (HasDcGroupToDecode()) return false;
824
33.1k
  for (const auto& nb_passes : decoded_passes_per_ac_group_) {
825
33.1k
    if (nb_passes < frame_header_.passes.num_passes) return false;
826
33.1k
  }
827
31.2k
  return true;
828
31.2k
}
829
830
31.2k
int FrameDecoder::References() const {
831
31.2k
  if (is_finalized_) {
832
0
    return 0;
833
0
  }
834
31.2k
  if (!HasEverything()) return 0;
835
836
31.2k
  int result = 0;
837
838
  // Blending
839
31.2k
  if (frame_header_.frame_type == FrameType::kRegularFrame ||
840
17.7k
      frame_header_.frame_type == FrameType::kSkipProgressive) {
841
17.7k
    bool cropped = frame_header_.custom_size_or_origin;
842
17.7k
    if (cropped || frame_header_.blending_info.mode != BlendMode::kReplace) {
843
7.55k
      result |= (1 << frame_header_.blending_info.source);
844
7.55k
    }
845
17.7k
    const auto& extra = frame_header_.extra_channel_blending_info;
846
17.7k
    for (const auto& ecbi : extra) {
847
8.39k
      if (cropped || ecbi.mode != BlendMode::kReplace) {
848
4.33k
        result |= (1 << ecbi.source);
849
4.33k
      }
850
8.39k
    }
851
17.7k
  }
852
853
  // Patches
854
31.2k
  if (frame_header_.flags & FrameHeader::kPatches) {
855
800
    result |= dec_state_->shared->image_features.patches.GetReferences();
856
800
  }
857
858
  // DC Level
859
31.2k
  if (frame_header_.flags & FrameHeader::kUseDcFrame) {
860
    // Reads from the next dc level
861
27
    int dc_level = frame_header_.dc_level + 1;
862
    // bits 16, 32, 64, 128 for DC level
863
27
    result |= (16 << (dc_level - 1));
864
27
  }
865
866
31.2k
  return result;
867
31.2k
}
868
869
32.0k
Status FrameDecoder::FinalizeFrame() {
870
32.0k
  if (is_finalized_) {
871
0
    return JXL_FAILURE("FinalizeFrame called multiple times");
872
0
  }
873
32.0k
  is_finalized_ = true;
874
32.0k
  if (decoded_->IsJPEG()) {
875
    // Nothing to do.
876
0
    return true;
877
0
  }
878
879
  // undo global modular transforms and copy int pixel buffers to float ones
880
32.0k
  JXL_RETURN_IF_ERROR(
881
32.0k
      modular_frame_decoder_.FinalizeDecoding(frame_header_, dec_state_, pool_,
882
32.0k
                                              /*inplace=*/true));
883
884
32.0k
  if (frame_header_.CanBeReferenced()) {
885
18.2k
    auto& info = dec_state_->shared_storage
886
18.2k
                     .reference_frames[frame_header_.save_as_reference];
887
18.2k
    *info.frame = std::move(dec_state_->frame_storage_for_referencing);
888
18.2k
    info.ib_is_in_xyb = frame_header_.save_before_color_transform;
889
18.2k
  }
890
32.0k
  return true;
891
32.0k
}
892
893
}  // namespace jxl