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