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