/src/libjxl/lib/jxl/frame_header.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/frame_header.h" |
7 | | |
8 | | #include <sstream> |
9 | | |
10 | | #include "lib/jxl/aux_out.h" |
11 | | #include "lib/jxl/base/printf_macros.h" |
12 | | #include "lib/jxl/base/status.h" |
13 | | #include "lib/jxl/fields.h" |
14 | | |
15 | | namespace jxl { |
16 | | |
17 | | constexpr uint8_t YCbCrChromaSubsampling::kHShift[]; |
18 | | constexpr uint8_t YCbCrChromaSubsampling::kVShift[]; |
19 | | |
20 | | static Status VisitBlendMode(Visitor* JXL_RESTRICT visitor, |
21 | 1.09M | BlendMode default_value, BlendMode* blend_mode) { |
22 | 1.09M | uint32_t encoded = static_cast<uint32_t>(*blend_mode); |
23 | | |
24 | 1.09M | JXL_QUIET_RETURN_IF_ERROR(visitor->U32( |
25 | 1.09M | Val(static_cast<uint32_t>(BlendMode::kReplace)), |
26 | 1.09M | Val(static_cast<uint32_t>(BlendMode::kAdd)), |
27 | 1.09M | Val(static_cast<uint32_t>(BlendMode::kBlend)), BitsOffset(2, 3), |
28 | 1.09M | static_cast<uint32_t>(default_value), &encoded)); |
29 | 1.09M | if (encoded > 4) { |
30 | 55 | return JXL_FAILURE("Invalid blend_mode"); |
31 | 55 | } |
32 | 1.09M | *blend_mode = static_cast<BlendMode>(encoded); |
33 | 1.09M | return true; |
34 | 1.09M | } |
35 | | |
36 | | static Status VisitFrameType(Visitor* JXL_RESTRICT visitor, |
37 | 402k | FrameType default_value, FrameType* frame_type) { |
38 | 402k | uint32_t encoded = static_cast<uint32_t>(*frame_type); |
39 | | |
40 | 402k | JXL_QUIET_RETURN_IF_ERROR( |
41 | 402k | visitor->U32(Val(static_cast<uint32_t>(FrameType::kRegularFrame)), |
42 | 402k | Val(static_cast<uint32_t>(FrameType::kDCFrame)), |
43 | 402k | Val(static_cast<uint32_t>(FrameType::kReferenceOnly)), |
44 | 402k | Val(static_cast<uint32_t>(FrameType::kSkipProgressive)), |
45 | 402k | static_cast<uint32_t>(default_value), &encoded)); |
46 | 402k | *frame_type = static_cast<FrameType>(encoded); |
47 | 402k | return true; |
48 | 402k | } |
49 | | |
50 | 869k | BlendingInfo::BlendingInfo() { Bundle::Init(this); } |
51 | | |
52 | 1.09M | Status BlendingInfo::VisitFields(Visitor* JXL_RESTRICT visitor) { |
53 | 1.09M | JXL_QUIET_RETURN_IF_ERROR( |
54 | 1.09M | VisitBlendMode(visitor, BlendMode::kReplace, &mode)); |
55 | 1.09M | if (visitor->Conditional(nonserialized_num_extra_channels > 0 && |
56 | 1.09M | (mode == BlendMode::kBlend || |
57 | 919k | mode == BlendMode::kAlphaWeightedAdd))) { |
58 | | // Up to 11 alpha channels for blending. |
59 | 919k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32( |
60 | 919k | Val(0), Val(1), Val(2), BitsOffset(3, 3), 0, &alpha_channel)); |
61 | 919k | if (visitor->IsReading() && |
62 | 919k | alpha_channel >= nonserialized_num_extra_channels) { |
63 | 17 | return JXL_FAILURE("Invalid alpha channel for blending"); |
64 | 17 | } |
65 | 919k | } |
66 | 1.09M | if (visitor->Conditional((nonserialized_num_extra_channels > 0 && |
67 | 1.09M | (mode == BlendMode::kBlend || |
68 | 159k | mode == BlendMode::kAlphaWeightedAdd)) || |
69 | 1.09M | mode == BlendMode::kMul)) { |
70 | 924k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &clamp)); |
71 | 924k | } |
72 | | // 'old' frame for blending. Only necessary if this is not a full frame, or |
73 | | // blending is not kReplace. |
74 | 1.09M | if (visitor->Conditional(mode != BlendMode::kReplace || |
75 | 1.09M | nonserialized_is_partial_frame)) { |
76 | 983k | JXL_QUIET_RETURN_IF_ERROR( |
77 | 983k | visitor->U32(Val(0), Val(1), Val(2), Val(3), 0, &source)); |
78 | 983k | } |
79 | 1.09M | return true; |
80 | 1.09M | } |
81 | | |
82 | 0 | std::string BlendingInfo::DebugString() const { |
83 | 0 | std::ostringstream os; |
84 | 0 | os << ","; |
85 | 0 | os << (mode == BlendMode::kReplace ? "Replace" |
86 | 0 | : mode == BlendMode::kAdd ? "Add" |
87 | 0 | : mode == BlendMode::kBlend ? "Blend" |
88 | 0 | : mode == BlendMode::kAlphaWeightedAdd ? "AlphaWeightedAdd" |
89 | 0 | : "Mul"); |
90 | 0 | if (nonserialized_num_extra_channels > 0 && |
91 | 0 | (mode == BlendMode::kBlend || mode == BlendMode::kAlphaWeightedAdd)) { |
92 | 0 | os << ",alpha=" << alpha_channel << ",clamp=" << clamp; |
93 | 0 | } else if (mode == BlendMode::kMul) { |
94 | 0 | os << ",clamp=" << clamp; |
95 | 0 | } |
96 | 0 | if (mode != BlendMode::kReplace || nonserialized_is_partial_frame) { |
97 | 0 | os << ",source=" << source; |
98 | 0 | } |
99 | 0 | return os.str(); |
100 | 0 | } |
101 | | |
102 | | AnimationFrame::AnimationFrame(const CodecMetadata* metadata) |
103 | 281k | : nonserialized_metadata(metadata) { |
104 | 281k | Bundle::Init(this); |
105 | 281k | } |
106 | 306k | Status AnimationFrame::VisitFields(Visitor* JXL_RESTRICT visitor) { |
107 | 306k | if (visitor->Conditional(nonserialized_metadata != nullptr && |
108 | 306k | nonserialized_metadata->m.have_animation)) { |
109 | 306k | JXL_QUIET_RETURN_IF_ERROR( |
110 | 306k | visitor->U32(Val(0), Val(1), Bits(8), Bits(32), 0, &duration)); |
111 | 306k | } |
112 | | |
113 | 306k | if (visitor->Conditional( |
114 | 306k | nonserialized_metadata != nullptr && |
115 | 306k | nonserialized_metadata->m.animation.have_timecodes)) { |
116 | 303k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(32, 0, &timecode)); |
117 | 303k | } |
118 | 305k | return true; |
119 | 306k | } |
120 | | |
121 | 383k | YCbCrChromaSubsampling::YCbCrChromaSubsampling() { Bundle::Init(this); } |
122 | 281k | Passes::Passes() { Bundle::Init(this); } |
123 | 389k | Status Passes::VisitFields(Visitor* JXL_RESTRICT visitor) { |
124 | 389k | JXL_QUIET_RETURN_IF_ERROR( |
125 | 389k | visitor->U32(Val(1), Val(2), Val(3), BitsOffset(3, 4), 1, &num_passes)); |
126 | 389k | JXL_ASSERT(num_passes <= kMaxNumPasses); // Cannot happen when reading |
127 | | |
128 | 389k | if (visitor->Conditional(num_passes != 1)) { |
129 | 319k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32( |
130 | 319k | Val(0), Val(1), Val(2), BitsOffset(1, 3), 0, &num_downsample)); |
131 | 319k | JXL_ASSERT(num_downsample <= 4); // 1,2,4,8 |
132 | 319k | if (num_downsample > num_passes) { |
133 | 29 | return JXL_FAILURE("num_downsample %u > num_passes %u", num_downsample, |
134 | 29 | num_passes); |
135 | 29 | } |
136 | | |
137 | 368k | for (uint32_t i = 0; i < num_passes - 1; i++) { |
138 | 49.6k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(2, 0, &shift[i])); |
139 | 49.6k | } |
140 | 318k | shift[num_passes - 1] = 0; |
141 | | |
142 | 330k | for (uint32_t i = 0; i < num_downsample; ++i) { |
143 | 12.1k | JXL_QUIET_RETURN_IF_ERROR( |
144 | 12.1k | visitor->U32(Val(1), Val(2), Val(4), Val(8), 1, &downsample[i])); |
145 | 12.0k | if (i > 0 && downsample[i] >= downsample[i - 1]) { |
146 | 104 | return JXL_FAILURE("downsample sequence should be decreasing"); |
147 | 104 | } |
148 | 12.0k | } |
149 | 329k | for (uint32_t i = 0; i < num_downsample; ++i) { |
150 | 11.7k | JXL_QUIET_RETURN_IF_ERROR( |
151 | 11.7k | visitor->U32(Val(0), Val(1), Val(2), Bits(3), 0, &last_pass[i])); |
152 | 11.4k | if (i > 0 && last_pass[i] <= last_pass[i - 1]) { |
153 | 17 | return JXL_FAILURE("last_pass sequence should be increasing"); |
154 | 17 | } |
155 | 11.4k | if (last_pass[i] >= num_passes) { |
156 | 18 | return JXL_FAILURE("last_pass %u >= num_passes %u", last_pass[i], |
157 | 18 | num_passes); |
158 | 18 | } |
159 | 11.4k | } |
160 | 318k | } |
161 | | |
162 | 387k | return true; |
163 | 389k | } |
164 | | |
165 | 0 | std::string Passes::DebugString() const { |
166 | 0 | std::ostringstream os; |
167 | 0 | os << "p=" << num_passes; |
168 | 0 | if (num_downsample) { |
169 | 0 | os << ",ds="; |
170 | 0 | for (uint32_t i = 0; i < num_downsample; ++i) { |
171 | 0 | os << last_pass[i] << ":" << downsample[i]; |
172 | 0 | if (i + 1 < num_downsample) os << ";"; |
173 | 0 | } |
174 | 0 | } |
175 | 0 | bool have_shifts = false; |
176 | 0 | for (uint32_t i = 0; i < num_passes; ++i) { |
177 | 0 | if (shift[i]) have_shifts = true; |
178 | 0 | } |
179 | 0 | if (have_shifts) { |
180 | 0 | os << ",shifts="; |
181 | 0 | for (uint32_t i = 0; i < num_passes; ++i) { |
182 | 0 | os << shift[i]; |
183 | 0 | if (i + 1 < num_passes) os << ";"; |
184 | 0 | } |
185 | 0 | } |
186 | 0 | return os.str(); |
187 | 0 | } |
188 | | |
189 | | FrameHeader::FrameHeader(const CodecMetadata* metadata) |
190 | 281k | : animation_frame(metadata), nonserialized_metadata(metadata) { |
191 | 281k | Bundle::Init(this); |
192 | 281k | } |
193 | | |
194 | | Status ReadFrameHeader(BitReader* JXL_RESTRICT reader, |
195 | 120k | FrameHeader* JXL_RESTRICT frame) { |
196 | 120k | return Bundle::Read(reader, frame); |
197 | 120k | } |
198 | | |
199 | | Status WriteFrameHeader(const FrameHeader& frame, |
200 | 0 | BitWriter* JXL_RESTRICT writer, AuxOut* aux_out) { |
201 | 0 | return Bundle::Write(frame, writer, kLayerHeader, aux_out); |
202 | 0 | } |
203 | | |
204 | 422k | Status FrameHeader::VisitFields(Visitor* JXL_RESTRICT visitor) { |
205 | 422k | if (visitor->AllDefault(*this, &all_default)) { |
206 | | // Overwrite all serialized fields, but not any nonserialized_*. |
207 | 20.5k | visitor->SetDefault(this); |
208 | 20.5k | return true; |
209 | 20.5k | } |
210 | | |
211 | 402k | JXL_QUIET_RETURN_IF_ERROR( |
212 | 402k | VisitFrameType(visitor, FrameType::kRegularFrame, &frame_type)); |
213 | 402k | if (visitor->IsReading() && nonserialized_is_preview && |
214 | 402k | frame_type != kRegularFrame) { |
215 | 3 | return JXL_FAILURE("Only regular frame could be a preview"); |
216 | 3 | } |
217 | | |
218 | | // FrameEncoding. |
219 | 402k | bool is_modular = (encoding == FrameEncoding::kModular); |
220 | 402k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &is_modular)); |
221 | 402k | encoding = (is_modular ? FrameEncoding::kModular : FrameEncoding::kVarDCT); |
222 | | |
223 | | // Flags |
224 | 402k | JXL_QUIET_RETURN_IF_ERROR(visitor->U64(0, &flags)); |
225 | | |
226 | | // Color transform |
227 | 399k | bool xyb_encoded = nonserialized_metadata == nullptr || |
228 | 399k | nonserialized_metadata->m.xyb_encoded; |
229 | | |
230 | 399k | if (xyb_encoded) { |
231 | 189k | color_transform = ColorTransform::kXYB; |
232 | 210k | } else { |
233 | | // Alternate if kYCbCr. |
234 | 210k | bool alternate = color_transform == ColorTransform::kYCbCr; |
235 | 210k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &alternate)); |
236 | 210k | color_transform = |
237 | 210k | (alternate ? ColorTransform::kYCbCr : ColorTransform::kNone); |
238 | 210k | } |
239 | | |
240 | | // Chroma subsampling for YCbCr, if no DC frame is used. |
241 | 399k | if (visitor->Conditional(color_transform == ColorTransform::kYCbCr && |
242 | 399k | ((flags & kUseDcFrame) == 0))) { |
243 | 313k | JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&chroma_subsampling)); |
244 | 313k | } |
245 | | |
246 | 399k | size_t num_extra_channels = |
247 | 399k | nonserialized_metadata != nullptr |
248 | 399k | ? nonserialized_metadata->m.extra_channel_info.size() |
249 | 399k | : 0; |
250 | | |
251 | | // Upsampling |
252 | 399k | if (visitor->Conditional((flags & kUseDcFrame) == 0)) { |
253 | 396k | JXL_QUIET_RETURN_IF_ERROR( |
254 | 396k | visitor->U32(Val(1), Val(2), Val(4), Val(8), 1, &upsampling)); |
255 | 394k | if (nonserialized_metadata != nullptr && |
256 | 394k | visitor->Conditional(num_extra_channels != 0)) { |
257 | 323k | const std::vector<ExtraChannelInfo>& extra_channels = |
258 | 323k | nonserialized_metadata->m.extra_channel_info; |
259 | 323k | extra_channel_upsampling.resize(extra_channels.size(), 1); |
260 | 1.14M | for (size_t i = 0; i < extra_channels.size(); ++i) { |
261 | 823k | uint32_t dim_shift = |
262 | 823k | nonserialized_metadata->m.extra_channel_info[i].dim_shift; |
263 | 823k | uint32_t& ec_upsampling = extra_channel_upsampling[i]; |
264 | 823k | ec_upsampling >>= dim_shift; |
265 | 823k | JXL_QUIET_RETURN_IF_ERROR( |
266 | 823k | visitor->U32(Val(1), Val(2), Val(4), Val(8), 1, &ec_upsampling)); |
267 | 821k | ec_upsampling <<= dim_shift; |
268 | 821k | if (ec_upsampling < upsampling) { |
269 | 57 | return JXL_FAILURE( |
270 | 57 | "EC upsampling (%u) < color upsampling (%u), which is invalid.", |
271 | 57 | ec_upsampling, upsampling); |
272 | 57 | } |
273 | 821k | if (ec_upsampling > 8) { |
274 | 9 | return JXL_FAILURE("EC upsampling too large (%u)", ec_upsampling); |
275 | 9 | } |
276 | 821k | } |
277 | 323k | } else { |
278 | 71.3k | extra_channel_upsampling.clear(); |
279 | 71.3k | } |
280 | 394k | } |
281 | | |
282 | | // Modular- or VarDCT-specific data. |
283 | 394k | if (visitor->Conditional(encoding == FrameEncoding::kModular)) { |
284 | 369k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(2, 1, &group_size_shift)); |
285 | 369k | } |
286 | 393k | if (visitor->Conditional(encoding == FrameEncoding::kVarDCT && |
287 | 393k | color_transform == ColorTransform::kXYB)) { |
288 | 313k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(3, 3, &x_qm_scale)); |
289 | 313k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(3, 2, &b_qm_scale)); |
290 | 313k | } else { |
291 | 79.5k | x_qm_scale = b_qm_scale = 2; // noop |
292 | 79.5k | } |
293 | | |
294 | | // Not useful for kPatchSource |
295 | 392k | if (visitor->Conditional(frame_type != FrameType::kReferenceOnly)) { |
296 | 389k | JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&passes)); |
297 | 389k | } |
298 | | |
299 | 390k | if (visitor->Conditional(frame_type == FrameType::kDCFrame)) { |
300 | | // Up to 4 pyramid levels - for up to 16384x downsampling. |
301 | 307k | JXL_QUIET_RETURN_IF_ERROR( |
302 | 307k | visitor->U32(Val(1), Val(2), Val(3), Val(4), 1, &dc_level)); |
303 | 307k | } |
304 | 390k | if (frame_type != FrameType::kDCFrame) { |
305 | 385k | dc_level = 0; |
306 | 385k | } |
307 | | |
308 | 390k | bool is_partial_frame = false; |
309 | 390k | if (visitor->Conditional(frame_type != FrameType::kDCFrame)) { |
310 | 385k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &custom_size_or_origin)); |
311 | 384k | if (visitor->Conditional(custom_size_or_origin)) { |
312 | 327k | const U32Enc enc(Bits(8), BitsOffset(11, 256), BitsOffset(14, 2304), |
313 | 327k | BitsOffset(30, 18688)); |
314 | | // Frame offset, only if kRegularFrame or kSkipProgressive. |
315 | 327k | if (visitor->Conditional(frame_type == FrameType::kRegularFrame || |
316 | 327k | frame_type == FrameType::kSkipProgressive)) { |
317 | 325k | uint32_t ux0 = PackSigned(frame_origin.x0); |
318 | 325k | uint32_t uy0 = PackSigned(frame_origin.y0); |
319 | 325k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(enc, 0, &ux0)); |
320 | 323k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(enc, 0, &uy0)); |
321 | 322k | frame_origin.x0 = UnpackSigned(ux0); |
322 | 322k | frame_origin.y0 = UnpackSigned(uy0); |
323 | 322k | } |
324 | | // Frame size |
325 | 324k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(enc, 0, &frame_size.xsize)); |
326 | 323k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(enc, 0, &frame_size.ysize)); |
327 | 322k | if (custom_size_or_origin && |
328 | 322k | (frame_size.xsize == 0 || frame_size.ysize == 0)) { |
329 | 31 | return JXL_FAILURE( |
330 | 31 | "Invalid crop dimensions for frame: zero width or height"); |
331 | 31 | } |
332 | 322k | int32_t image_xsize = default_xsize(); |
333 | 322k | int32_t image_ysize = default_ysize(); |
334 | 322k | if (frame_type == FrameType::kRegularFrame || |
335 | 322k | frame_type == FrameType::kSkipProgressive) { |
336 | 319k | is_partial_frame |= frame_origin.x0 > 0; |
337 | 319k | is_partial_frame |= frame_origin.y0 > 0; |
338 | 319k | is_partial_frame |= (static_cast<int32_t>(frame_size.xsize) + |
339 | 319k | frame_origin.x0) < image_xsize; |
340 | 319k | is_partial_frame |= (static_cast<int32_t>(frame_size.ysize) + |
341 | 319k | frame_origin.y0) < image_ysize; |
342 | 319k | } |
343 | 322k | } |
344 | 384k | } |
345 | | |
346 | | // Blending info, animation info and whether this is the last frame or not. |
347 | 384k | if (visitor->Conditional(frame_type == FrameType::kRegularFrame || |
348 | 384k | frame_type == FrameType::kSkipProgressive)) { |
349 | 376k | blending_info.nonserialized_num_extra_channels = num_extra_channels; |
350 | 376k | blending_info.nonserialized_is_partial_frame = is_partial_frame; |
351 | 376k | JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&blending_info)); |
352 | 374k | bool replace_all = (blending_info.mode == BlendMode::kReplace); |
353 | 374k | extra_channel_blending_info.resize(num_extra_channels); |
354 | 1.08M | for (size_t i = 0; i < num_extra_channels; i++) { |
355 | 717k | auto& ec_blending_info = extra_channel_blending_info[i]; |
356 | 717k | ec_blending_info.nonserialized_is_partial_frame = is_partial_frame; |
357 | 717k | ec_blending_info.nonserialized_num_extra_channels = num_extra_channels; |
358 | 717k | JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&ec_blending_info)); |
359 | 714k | replace_all &= (ec_blending_info.mode == BlendMode::kReplace); |
360 | 714k | } |
361 | 371k | if (visitor->IsReading() && nonserialized_is_preview) { |
362 | 9.06k | if (!replace_all || custom_size_or_origin) { |
363 | 5 | return JXL_FAILURE("Preview is not compatible with blending"); |
364 | 5 | } |
365 | 9.06k | } |
366 | 371k | if (visitor->Conditional(nonserialized_metadata != nullptr && |
367 | 371k | nonserialized_metadata->m.have_animation)) { |
368 | 306k | animation_frame.nonserialized_metadata = nonserialized_metadata; |
369 | 306k | JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&animation_frame)); |
370 | 306k | } |
371 | 370k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(true, &is_last)); |
372 | 370k | } |
373 | 378k | if (frame_type != FrameType::kRegularFrame) { |
374 | 11.6k | is_last = false; |
375 | 11.6k | } |
376 | | |
377 | | // ID of that can be used to refer to this frame. 0 for a non-zero-duration |
378 | | // frame means that it will not be referenced. Not necessary for the last |
379 | | // frame. |
380 | 378k | if (visitor->Conditional(frame_type != kDCFrame && !is_last)) { |
381 | 364k | JXL_QUIET_RETURN_IF_ERROR( |
382 | 364k | visitor->U32(Val(0), Val(1), Val(2), Val(3), 0, &save_as_reference)); |
383 | 364k | } |
384 | | |
385 | | // If this frame is not blended on another frame post-color-transform, it may |
386 | | // be stored for being referenced either before or after the color transform. |
387 | | // If it is blended post-color-transform, it must be blended after. It must |
388 | | // also be blended after if this is a kRegular frame that does not cover the |
389 | | // full frame, as samples outside the partial region are from a |
390 | | // post-color-transform frame. |
391 | 376k | if (frame_type != FrameType::kDCFrame) { |
392 | 371k | if (visitor->Conditional(CanBeReferenced() && |
393 | 371k | blending_info.mode == BlendMode::kReplace && |
394 | 371k | !is_partial_frame && |
395 | 371k | (frame_type == FrameType::kRegularFrame || |
396 | 328k | frame_type == FrameType::kSkipProgressive))) { |
397 | 328k | JXL_QUIET_RETURN_IF_ERROR( |
398 | 328k | visitor->Bool(false, &save_before_color_transform)); |
399 | 328k | } else if (visitor->Conditional(frame_type == FrameType::kReferenceOnly)) { |
400 | 2.70k | JXL_QUIET_RETURN_IF_ERROR( |
401 | 2.70k | visitor->Bool(true, &save_before_color_transform)); |
402 | 2.68k | if (!save_before_color_transform && |
403 | 2.68k | (frame_size.xsize < nonserialized_metadata->xsize() || |
404 | 1.47k | frame_size.ysize < nonserialized_metadata->ysize() || |
405 | 1.47k | frame_origin.x0 != 0 || frame_origin.y0 != 0)) { |
406 | 62 | return JXL_FAILURE( |
407 | 62 | "non-patch reference frame with invalid crop: %" PRIuS "x%" PRIuS |
408 | 62 | "%+d%+d", |
409 | 62 | static_cast<size_t>(frame_size.xsize), |
410 | 62 | static_cast<size_t>(frame_size.ysize), |
411 | 62 | static_cast<int>(frame_origin.x0), |
412 | 62 | static_cast<int>(frame_origin.y0)); |
413 | 62 | } |
414 | 2.68k | } |
415 | 371k | } else { |
416 | 5.53k | save_before_color_transform = true; |
417 | 5.53k | } |
418 | | |
419 | 376k | JXL_QUIET_RETURN_IF_ERROR(VisitNameString(visitor, &name)); |
420 | | |
421 | 369k | loop_filter.nonserialized_is_modular = is_modular; |
422 | 369k | JXL_RETURN_IF_ERROR(visitor->VisitNested(&loop_filter)); |
423 | | |
424 | 359k | JXL_QUIET_RETURN_IF_ERROR(visitor->BeginExtensions(&extensions)); |
425 | | // Extensions: in chronological order of being added to the format. |
426 | 356k | return visitor->EndExtensions(); |
427 | 359k | } |
428 | | |
429 | 0 | std::string FrameHeader::DebugString() const { |
430 | 0 | std::ostringstream os; |
431 | 0 | os << (encoding == FrameEncoding::kVarDCT ? "VarDCT" : "Modular"); |
432 | 0 | os << ","; |
433 | 0 | os << (frame_type == FrameType::kRegularFrame ? "Regular" |
434 | 0 | : frame_type == FrameType::kDCFrame ? "DC" |
435 | 0 | : frame_type == FrameType::kReferenceOnly ? "Reference" |
436 | 0 | : "SkipProgressive"); |
437 | 0 | if (frame_type == FrameType::kDCFrame) { |
438 | 0 | os << "(lv" << dc_level << ")"; |
439 | 0 | } |
440 | |
|
441 | 0 | if (flags) { |
442 | 0 | os << ","; |
443 | 0 | uint32_t remaining = flags; |
444 | |
|
445 | 0 | #define TEST_FLAG(name) \ |
446 | 0 | if (flags & Flags::k##name) { \ |
447 | 0 | remaining &= ~Flags::k##name; \ |
448 | 0 | os << #name; \ |
449 | 0 | if (remaining) os << "|"; \ |
450 | 0 | } |
451 | 0 | TEST_FLAG(Noise); |
452 | 0 | TEST_FLAG(Patches); |
453 | 0 | TEST_FLAG(Splines); |
454 | 0 | TEST_FLAG(UseDcFrame); |
455 | 0 | TEST_FLAG(SkipAdaptiveDCSmoothing); |
456 | 0 | #undef TEST_FLAG |
457 | 0 | } |
458 | |
|
459 | 0 | os << ","; |
460 | 0 | os << (color_transform == ColorTransform::kXYB ? "XYB" |
461 | 0 | : color_transform == ColorTransform::kYCbCr ? "YCbCr" |
462 | 0 | : "None"); |
463 | |
|
464 | 0 | if (encoding == FrameEncoding::kModular) { |
465 | 0 | os << ",shift=" << group_size_shift; |
466 | 0 | } else if (color_transform == ColorTransform::kXYB) { |
467 | 0 | os << ",qm=" << x_qm_scale << ";" << b_qm_scale; |
468 | 0 | } |
469 | 0 | if (frame_type != FrameType::kReferenceOnly) { |
470 | 0 | os << "," << passes.DebugString(); |
471 | 0 | } |
472 | 0 | if (custom_size_or_origin) { |
473 | 0 | os << ",xs=" << frame_size.xsize; |
474 | 0 | os << ",ys=" << frame_size.ysize; |
475 | 0 | if (frame_type == FrameType::kRegularFrame || |
476 | 0 | frame_type == FrameType::kSkipProgressive) { |
477 | 0 | os << ",x0=" << frame_origin.x0; |
478 | 0 | os << ",y0=" << frame_origin.y0; |
479 | 0 | } |
480 | 0 | } |
481 | 0 | if (upsampling > 1) os << ",up=" << upsampling; |
482 | 0 | if (loop_filter.gab) os << ",Gaborish"; |
483 | 0 | if (loop_filter.epf_iters > 0) os << ",epf=" << loop_filter.epf_iters; |
484 | 0 | if (animation_frame.duration > 0) os << ",dur=" << animation_frame.duration; |
485 | 0 | if (frame_type == FrameType::kRegularFrame || |
486 | 0 | frame_type == FrameType::kSkipProgressive) { |
487 | 0 | os << blending_info.DebugString(); |
488 | 0 | } |
489 | 0 | if (save_as_reference > 0) os << ",ref=" << save_as_reference; |
490 | 0 | os << "," << (save_before_color_transform ? "before" : "after") << "_ct"; |
491 | 0 | if (is_last) os << ",last"; |
492 | 0 | return os.str(); |
493 | 0 | } |
494 | | |
495 | | } // namespace jxl |