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