frame_header.cc (19829B)
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 BlendMode default_value, BlendMode* blend_mode) { 26 uint32_t encoded = static_cast<uint32_t>(*blend_mode); 27 28 JXL_QUIET_RETURN_IF_ERROR(visitor->U32( 29 Val(static_cast<uint32_t>(BlendMode::kReplace)), 30 Val(static_cast<uint32_t>(BlendMode::kAdd)), 31 Val(static_cast<uint32_t>(BlendMode::kBlend)), BitsOffset(2, 3), 32 static_cast<uint32_t>(default_value), &encoded)); 33 if (encoded > static_cast<uint32_t>(BlendMode::kMul)) { 34 return JXL_FAILURE("Invalid blend_mode"); 35 } 36 *blend_mode = static_cast<BlendMode>(encoded); 37 return true; 38 } 39 40 static Status VisitFrameType(Visitor* JXL_RESTRICT visitor, 41 FrameType default_value, FrameType* frame_type) { 42 uint32_t encoded = static_cast<uint32_t>(*frame_type); 43 44 JXL_QUIET_RETURN_IF_ERROR( 45 visitor->U32(Val(static_cast<uint32_t>(FrameType::kRegularFrame)), 46 Val(static_cast<uint32_t>(FrameType::kDCFrame)), 47 Val(static_cast<uint32_t>(FrameType::kReferenceOnly)), 48 Val(static_cast<uint32_t>(FrameType::kSkipProgressive)), 49 static_cast<uint32_t>(default_value), &encoded)); 50 *frame_type = static_cast<FrameType>(encoded); 51 return true; 52 } 53 54 BlendingInfo::BlendingInfo() { Bundle::Init(this); } 55 56 Status BlendingInfo::VisitFields(Visitor* JXL_RESTRICT visitor) { 57 JXL_QUIET_RETURN_IF_ERROR( 58 VisitBlendMode(visitor, BlendMode::kReplace, &mode)); 59 if (visitor->Conditional(nonserialized_num_extra_channels > 0 && 60 (mode == BlendMode::kBlend || 61 mode == BlendMode::kAlphaWeightedAdd))) { 62 // Up to 11 alpha channels for blending. 63 JXL_QUIET_RETURN_IF_ERROR(visitor->U32( 64 Val(0), Val(1), Val(2), BitsOffset(3, 3), 0, &alpha_channel)); 65 if (visitor->IsReading() && 66 alpha_channel >= nonserialized_num_extra_channels) { 67 return JXL_FAILURE("Invalid alpha channel for blending"); 68 } 69 } 70 if (visitor->Conditional((nonserialized_num_extra_channels > 0 && 71 (mode == BlendMode::kBlend || 72 mode == BlendMode::kAlphaWeightedAdd)) || 73 mode == BlendMode::kMul)) { 74 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &clamp)); 75 } 76 // 'old' frame for blending. Only necessary if this is not a full frame, or 77 // blending is not kReplace. 78 if (visitor->Conditional(mode != BlendMode::kReplace || 79 nonserialized_is_partial_frame)) { 80 JXL_QUIET_RETURN_IF_ERROR( 81 visitor->U32(Val(0), Val(1), Val(2), Val(3), 0, &source)); 82 } 83 return true; 84 } 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 : nonserialized_metadata(metadata) { 109 Bundle::Init(this); 110 } 111 Status AnimationFrame::VisitFields(Visitor* JXL_RESTRICT visitor) { 112 if (visitor->Conditional(nonserialized_metadata != nullptr && 113 nonserialized_metadata->m.have_animation)) { 114 JXL_QUIET_RETURN_IF_ERROR( 115 visitor->U32(Val(0), Val(1), Bits(8), Bits(32), 0, &duration)); 116 } 117 118 if (visitor->Conditional( 119 nonserialized_metadata != nullptr && 120 nonserialized_metadata->m.animation.have_timecodes)) { 121 JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(32, 0, &timecode)); 122 } 123 return true; 124 } 125 126 YCbCrChromaSubsampling::YCbCrChromaSubsampling() { Bundle::Init(this); } 127 Passes::Passes() { Bundle::Init(this); } 128 Status Passes::VisitFields(Visitor* JXL_RESTRICT visitor) { 129 JXL_QUIET_RETURN_IF_ERROR( 130 visitor->U32(Val(1), Val(2), Val(3), BitsOffset(3, 4), 1, &num_passes)); 131 JXL_ENSURE(num_passes <= kMaxNumPasses); // Cannot happen when reading 132 133 if (visitor->Conditional(num_passes != 1)) { 134 JXL_QUIET_RETURN_IF_ERROR(visitor->U32( 135 Val(0), Val(1), Val(2), BitsOffset(1, 3), 0, &num_downsample)); 136 JXL_ENSURE(num_downsample <= 4); // 1,2,4,8 137 if (num_downsample > num_passes) { 138 return JXL_FAILURE("num_downsample %u > num_passes %u", num_downsample, 139 num_passes); 140 } 141 142 for (uint32_t i = 0; i < num_passes - 1; i++) { 143 JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(2, 0, &shift[i])); 144 } 145 shift[num_passes - 1] = 0; 146 147 for (uint32_t i = 0; i < num_downsample; ++i) { 148 JXL_QUIET_RETURN_IF_ERROR( 149 visitor->U32(Val(1), Val(2), Val(4), Val(8), 1, &downsample[i])); 150 if (i > 0 && downsample[i] >= downsample[i - 1]) { 151 return JXL_FAILURE("downsample sequence should be decreasing"); 152 } 153 } 154 for (uint32_t i = 0; i < num_downsample; ++i) { 155 JXL_QUIET_RETURN_IF_ERROR( 156 visitor->U32(Val(0), Val(1), Val(2), Bits(3), 0, &last_pass[i])); 157 if (i > 0 && last_pass[i] <= last_pass[i - 1]) { 158 return JXL_FAILURE("last_pass sequence should be increasing"); 159 } 160 if (last_pass[i] >= num_passes) { 161 return JXL_FAILURE("last_pass %u >= num_passes %u", last_pass[i], 162 num_passes); 163 } 164 } 165 } 166 167 return true; 168 } 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 : animation_frame(metadata), nonserialized_metadata(metadata) { 198 Bundle::Init(this); 199 } 200 201 Status ReadFrameHeader(BitReader* JXL_RESTRICT reader, 202 FrameHeader* JXL_RESTRICT frame) { 203 return Bundle::Read(reader, frame); 204 } 205 206 Status FrameHeader::VisitFields(Visitor* JXL_RESTRICT visitor) { 207 if (visitor->AllDefault(*this, &all_default)) { 208 // Overwrite all serialized fields, but not any nonserialized_*. 209 visitor->SetDefault(this); 210 return true; 211 } 212 213 JXL_QUIET_RETURN_IF_ERROR( 214 VisitFrameType(visitor, FrameType::kRegularFrame, &frame_type)); 215 if (visitor->IsReading() && nonserialized_is_preview && 216 frame_type != kRegularFrame) { 217 return JXL_FAILURE("Only regular frame could be a preview"); 218 } 219 220 // FrameEncoding. 221 bool is_modular = (encoding == FrameEncoding::kModular); 222 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &is_modular)); 223 encoding = (is_modular ? FrameEncoding::kModular : FrameEncoding::kVarDCT); 224 225 // Flags 226 JXL_QUIET_RETURN_IF_ERROR(visitor->U64(0, &flags)); 227 228 // Color transform 229 bool xyb_encoded = nonserialized_metadata == nullptr || 230 nonserialized_metadata->m.xyb_encoded; 231 232 if (xyb_encoded) { 233 color_transform = ColorTransform::kXYB; 234 } else { 235 // Alternate if kYCbCr. 236 bool alternate = color_transform == ColorTransform::kYCbCr; 237 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &alternate)); 238 color_transform = 239 (alternate ? ColorTransform::kYCbCr : ColorTransform::kNone); 240 } 241 242 // Chroma subsampling for YCbCr, if no DC frame is used. 243 if (visitor->Conditional(color_transform == ColorTransform::kYCbCr && 244 ((flags & kUseDcFrame) == 0))) { 245 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&chroma_subsampling)); 246 } 247 248 size_t num_extra_channels = 249 nonserialized_metadata != nullptr 250 ? nonserialized_metadata->m.extra_channel_info.size() 251 : 0; 252 253 // Upsampling 254 if (visitor->Conditional((flags & kUseDcFrame) == 0)) { 255 JXL_QUIET_RETURN_IF_ERROR( 256 visitor->U32(Val(1), Val(2), Val(4), Val(8), 1, &upsampling)); 257 if (nonserialized_metadata != nullptr && 258 visitor->Conditional(num_extra_channels != 0)) { 259 const std::vector<ExtraChannelInfo>& extra_channels = 260 nonserialized_metadata->m.extra_channel_info; 261 extra_channel_upsampling.resize(extra_channels.size(), 1); 262 for (size_t i = 0; i < extra_channels.size(); ++i) { 263 uint32_t dim_shift = 264 nonserialized_metadata->m.extra_channel_info[i].dim_shift; 265 uint32_t& ec_upsampling = extra_channel_upsampling[i]; 266 ec_upsampling >>= dim_shift; 267 JXL_QUIET_RETURN_IF_ERROR( 268 visitor->U32(Val(1), Val(2), Val(4), Val(8), 1, &ec_upsampling)); 269 ec_upsampling <<= dim_shift; 270 if (ec_upsampling < upsampling) { 271 return JXL_FAILURE( 272 "EC upsampling (%u) < color upsampling (%u), which is invalid.", 273 ec_upsampling, upsampling); 274 } 275 if (ec_upsampling > 8) { 276 return JXL_FAILURE("EC upsampling too large (%u)", ec_upsampling); 277 } 278 } 279 } else { 280 extra_channel_upsampling.clear(); 281 } 282 } 283 284 // Modular- or VarDCT-specific data. 285 if (visitor->Conditional(encoding == FrameEncoding::kModular)) { 286 JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(2, 1, &group_size_shift)); 287 } 288 if (visitor->Conditional(encoding == FrameEncoding::kVarDCT && 289 color_transform == ColorTransform::kXYB)) { 290 JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(3, 3, &x_qm_scale)); 291 JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(3, 2, &b_qm_scale)); 292 } else { 293 x_qm_scale = b_qm_scale = 2; // noop 294 } 295 296 // Not useful for kPatchSource 297 if (visitor->Conditional(frame_type != FrameType::kReferenceOnly)) { 298 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&passes)); 299 } 300 301 if (visitor->Conditional(frame_type == FrameType::kDCFrame)) { 302 // Up to 4 pyramid levels - for up to 16384x downsampling. 303 JXL_QUIET_RETURN_IF_ERROR( 304 visitor->U32(Val(1), Val(2), Val(3), Val(4), 1, &dc_level)); 305 } 306 if (frame_type != FrameType::kDCFrame) { 307 dc_level = 0; 308 } 309 310 bool is_partial_frame = false; 311 if (visitor->Conditional(frame_type != FrameType::kDCFrame)) { 312 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &custom_size_or_origin)); 313 if (visitor->Conditional(custom_size_or_origin)) { 314 const U32Enc enc(Bits(8), BitsOffset(11, 256), BitsOffset(14, 2304), 315 BitsOffset(30, 18688)); 316 // Frame offset, only if kRegularFrame or kSkipProgressive. 317 if (visitor->Conditional(frame_type == FrameType::kRegularFrame || 318 frame_type == FrameType::kSkipProgressive)) { 319 uint32_t ux0 = PackSigned(frame_origin.x0); 320 uint32_t uy0 = PackSigned(frame_origin.y0); 321 JXL_QUIET_RETURN_IF_ERROR(visitor->U32(enc, 0, &ux0)); 322 JXL_QUIET_RETURN_IF_ERROR(visitor->U32(enc, 0, &uy0)); 323 frame_origin.x0 = UnpackSigned(ux0); 324 frame_origin.y0 = UnpackSigned(uy0); 325 } 326 // Frame size 327 JXL_QUIET_RETURN_IF_ERROR(visitor->U32(enc, 0, &frame_size.xsize)); 328 JXL_QUIET_RETURN_IF_ERROR(visitor->U32(enc, 0, &frame_size.ysize)); 329 if (custom_size_or_origin && 330 (frame_size.xsize == 0 || frame_size.ysize == 0)) { 331 return JXL_FAILURE( 332 "Invalid crop dimensions for frame: zero width or height"); 333 } 334 int32_t image_xsize = default_xsize(); 335 int32_t image_ysize = default_ysize(); 336 if (frame_type == FrameType::kRegularFrame || 337 frame_type == FrameType::kSkipProgressive) { 338 is_partial_frame |= frame_origin.x0 > 0; 339 is_partial_frame |= frame_origin.y0 > 0; 340 is_partial_frame |= (static_cast<int32_t>(frame_size.xsize) + 341 frame_origin.x0) < image_xsize; 342 is_partial_frame |= (static_cast<int32_t>(frame_size.ysize) + 343 frame_origin.y0) < image_ysize; 344 } 345 } 346 } 347 348 // Blending info, animation info and whether this is the last frame or not. 349 if (visitor->Conditional(frame_type == FrameType::kRegularFrame || 350 frame_type == FrameType::kSkipProgressive)) { 351 blending_info.nonserialized_num_extra_channels = num_extra_channels; 352 blending_info.nonserialized_is_partial_frame = is_partial_frame; 353 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&blending_info)); 354 bool replace_all = (blending_info.mode == BlendMode::kReplace); 355 extra_channel_blending_info.resize(num_extra_channels); 356 for (size_t i = 0; i < num_extra_channels; i++) { 357 auto& ec_blending_info = extra_channel_blending_info[i]; 358 ec_blending_info.nonserialized_is_partial_frame = is_partial_frame; 359 ec_blending_info.nonserialized_num_extra_channels = num_extra_channels; 360 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&ec_blending_info)); 361 replace_all &= (ec_blending_info.mode == BlendMode::kReplace); 362 } 363 if (visitor->IsReading() && nonserialized_is_preview) { 364 if (!replace_all || custom_size_or_origin) { 365 return JXL_FAILURE("Preview is not compatible with blending"); 366 } 367 } 368 if (visitor->Conditional(nonserialized_metadata != nullptr && 369 nonserialized_metadata->m.have_animation)) { 370 animation_frame.nonserialized_metadata = nonserialized_metadata; 371 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&animation_frame)); 372 } 373 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(true, &is_last)); 374 } else { 375 is_last = false; 376 } 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 if (visitor->Conditional(frame_type != kDCFrame && !is_last)) { 382 JXL_QUIET_RETURN_IF_ERROR( 383 visitor->U32(Val(0), Val(1), Val(2), Val(3), 0, &save_as_reference)); 384 } 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 if (frame_type != FrameType::kDCFrame) { 393 if (visitor->Conditional(CanBeReferenced() && 394 blending_info.mode == BlendMode::kReplace && 395 !is_partial_frame && 396 (frame_type == FrameType::kRegularFrame || 397 frame_type == FrameType::kSkipProgressive))) { 398 JXL_QUIET_RETURN_IF_ERROR( 399 visitor->Bool(false, &save_before_color_transform)); 400 } else if (visitor->Conditional(frame_type == FrameType::kReferenceOnly)) { 401 JXL_QUIET_RETURN_IF_ERROR( 402 visitor->Bool(true, &save_before_color_transform)); 403 size_t xsize = custom_size_or_origin ? frame_size.xsize 404 : nonserialized_metadata->xsize(); 405 size_t ysize = custom_size_or_origin ? frame_size.ysize 406 : nonserialized_metadata->ysize(); 407 if (!save_before_color_transform && 408 (xsize < nonserialized_metadata->xsize() || 409 ysize < nonserialized_metadata->ysize() || frame_origin.x0 != 0 || 410 frame_origin.y0 != 0)) { 411 return JXL_FAILURE( 412 "non-patch reference frame with invalid crop: %" PRIuS "x%" PRIuS 413 "%+d%+d", 414 xsize, ysize, static_cast<int>(frame_origin.x0), 415 static_cast<int>(frame_origin.y0)); 416 } 417 } 418 } else { 419 save_before_color_transform = true; 420 } 421 422 JXL_QUIET_RETURN_IF_ERROR(VisitNameString(visitor, &name)); 423 424 loop_filter.nonserialized_is_modular = is_modular; 425 JXL_RETURN_IF_ERROR(visitor->VisitNested(&loop_filter)); 426 427 JXL_QUIET_RETURN_IF_ERROR(visitor->BeginExtensions(&extensions)); 428 // Extensions: in chronological order of being added to the format. 429 return visitor->EndExtensions(); 430 } 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