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