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