blob: b2e6039b783e91b6e0e54c2f09062f7fedf69dc2 [file] [log] [blame]
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <vector>
#include "src/buffer_pool.h"
#include "src/dsp/constants.h"
#include "src/motion_vector.h"
#include "src/obu_parser.h"
#include "src/prediction_mask.h"
#include "src/symbol_decoder_context.h"
#include "src/tile.h"
#include "src/utils/array_2d.h"
#include "src/utils/bit_mask_set.h"
#include "src/utils/block_parameters_holder.h"
#include "src/utils/common.h"
#include "src/utils/constants.h"
#include "src/utils/entropy_decoder.h"
#include "src/utils/logging.h"
#include "src/utils/segmentation.h"
#include "src/utils/segmentation_map.h"
#include "src/utils/types.h"
namespace libgav1 {
namespace {
constexpr int kDeltaQSmall = 3;
constexpr int kDeltaLfSmall = 3;
constexpr int kNoScale = 1 << kReferenceFrameScalePrecision;
constexpr uint8_t kIntraYModeContext[kIntraPredictionModesY] = {
0, 1, 2, 3, 4, 4, 4, 4, 3, 0, 1, 2, 0};
constexpr uint8_t kSizeGroup[kMaxBlockSizes] = {
0, 0, 0, 0, 1, 1, 1, 0, 1, 2, 2, 2, 1, 2, 3, 3, 2, 3, 3, 3, 3, 3};
constexpr int kCompoundModeNewMvContexts = 5;
constexpr uint8_t kCompoundModeContextMap[3][kCompoundModeNewMvContexts] = {
{0, 1, 1, 1, 1}, {1, 2, 3, 4, 4}, {4, 4, 5, 6, 7}};
enum CflSign : uint8_t {
kCflSignZero = 0,
kCflSignNegative = 1,
kCflSignPositive = 2
};
constexpr BitMaskSet kPredictionModeHasNearMvMask(kPredictionModeNearMv,
kPredictionModeNearNearMv,
kPredictionModeNearNewMv,
kPredictionModeNewNearMv);
constexpr BitMaskSet kIsInterIntraModeAllowedMask(kBlock8x8, kBlock8x16,
kBlock16x8, kBlock16x16,
kBlock16x32, kBlock32x16,
kBlock32x32);
bool IsBackwardReference(ReferenceFrameType type) {
return type >= kReferenceFrameBackward && type <= kReferenceFrameAlternate;
}
bool IsSameDirectionReferencePair(ReferenceFrameType type1,
ReferenceFrameType type2) {
return (type1 >= kReferenceFrameBackward) ==
(type2 >= kReferenceFrameBackward);
}
// This is called neg_deinterleave() in the spec.
int DecodeSegmentId(int diff, int reference, int max) {
if (reference == 0) return diff;
if (reference >= max - 1) return max - diff - 1;
const int value = ((diff & 1) != 0) ? reference + ((diff + 1) >> 1)
: reference - (diff >> 1);
const int reference2 = (reference << 1);
if (reference2 < max) {
return (diff <= reference2) ? value : diff;
}
return (diff <= ((max - reference - 1) << 1)) ? value : max - (diff + 1);
}
// This is called DrlCtxStack in section 7.10.2.14 of the spec.
int GetRefMvIndexContext(
const CandidateMotionVector ref_mv_stack[kMaxRefMvStackSize], int count,
int index) {
if (index + 1 >= count ||
(ref_mv_stack[index].weight >= kExtraWeightForNearestMvs &&
ref_mv_stack[index + 1].weight >= kExtraWeightForNearestMvs)) {
return 0;
}
if (ref_mv_stack[index].weight >= kExtraWeightForNearestMvs &&
ref_mv_stack[index + 1].weight < kExtraWeightForNearestMvs) {
return 1;
}
if (ref_mv_stack[index].weight < kExtraWeightForNearestMvs &&
ref_mv_stack[index + 1].weight < kExtraWeightForNearestMvs) {
return 2;
}
return 0;
}
// Returns true if the either the width or the height of the block is equal to
// four.
bool IsBlockDimension4(BlockSize size) {
return size < kBlock8x8 || size == kBlock16x4;
}
// Returns true if both the width and height of the block is less than 64.
bool IsBlockDimensionLessThan64(BlockSize size) {
return size <= kBlock32x32 && size != kBlock16x64;
}
} // namespace
bool Tile::ReadSegmentId(const Block& block) {
int top_left = -1;
if (block.top_available && block.left_available) {
top_left =
block_parameters_holder_.Find(block.row4x4 - 1, block.column4x4 - 1)
->segment_id;
}
int top = -1;
if (block.top_available) {
top = block.bp_top->segment_id;
}
int left = -1;
if (block.left_available) {
left = block.bp_left->segment_id;
}
int pred;
if (top == -1) {
pred = (left == -1) ? 0 : left;
} else if (left == -1) {
pred = top;
} else {
pred = (top_left == top) ? top : left;
}
BlockParameters& bp = *block.bp;
if (bp.skip) {
bp.segment_id = pred;
return true;
}
int context = 0;
if (top_left < 0) {
context = 0;
} else if (top_left == top && top_left == left) {
context = 2;
} else if (top_left == top || top_left == left || top == left) {
context = 1;
}
uint16_t* const segment_id_cdf =
symbol_decoder_context_.segment_id_cdf[context];
const int encoded_segment_id =
reader_.ReadSymbol<kMaxSegments>(segment_id_cdf);
bp.segment_id =
DecodeSegmentId(encoded_segment_id, pred,
frame_header_.segmentation.last_active_segment_id + 1);
// Check the bitstream conformance requirement in Section 6.10.8 of the spec.
if (bp.segment_id < 0 ||
bp.segment_id > frame_header_.segmentation.last_active_segment_id) {
LIBGAV1_DLOG(
ERROR,
"Corrupted segment_ids: encoded %d, last active %d, postprocessed %d",
encoded_segment_id, frame_header_.segmentation.last_active_segment_id,
bp.segment_id);
return false;
}
return true;
}
bool Tile::ReadIntraSegmentId(const Block& block) {
BlockParameters& bp = *block.bp;
if (!frame_header_.segmentation.enabled) {
bp.segment_id = 0;
return true;
}
return ReadSegmentId(block);
}
void Tile::ReadSkip(const Block& block) {
BlockParameters& bp = *block.bp;
if (frame_header_.segmentation.segment_id_pre_skip &&
frame_header_.segmentation.FeatureActive(bp.segment_id,
kSegmentFeatureSkip)) {
bp.skip = true;
return;
}
int context = 0;
if (block.top_available && block.bp_top->skip) {
++context;
}
if (block.left_available && block.bp_left->skip) {
++context;
}
uint16_t* const skip_cdf = symbol_decoder_context_.skip_cdf[context];
bp.skip = reader_.ReadSymbol(skip_cdf);
}
void Tile::ReadSkipMode(const Block& block) {
BlockParameters& bp = *block.bp;
if (!frame_header_.skip_mode_present ||
frame_header_.segmentation.FeatureActive(bp.segment_id,
kSegmentFeatureSkip) ||
frame_header_.segmentation.FeatureActive(bp.segment_id,
kSegmentFeatureReferenceFrame) ||
frame_header_.segmentation.FeatureActive(bp.segment_id,
kSegmentFeatureGlobalMv) ||
IsBlockDimension4(block.size)) {
bp.skip_mode = false;
return;
}
const int context =
(block.left_available ? static_cast<int>(block.bp_left->skip_mode) : 0) +
(block.top_available ? static_cast<int>(block.bp_top->skip_mode) : 0);
bp.skip_mode =
reader_.ReadSymbol(symbol_decoder_context_.skip_mode_cdf[context]);
}
void Tile::ReadCdef(const Block& block) {
BlockParameters& bp = *block.bp;
if (bp.skip || frame_header_.coded_lossless ||
!sequence_header_.enable_cdef || frame_header_.allow_intrabc) {
return;
}
const int cdef_size4x4 = kNum4x4BlocksWide[kBlock64x64];
const int cdef_mask4x4 = ~(cdef_size4x4 - 1);
const int row4x4 = block.row4x4 & cdef_mask4x4;
const int column4x4 = block.column4x4 & cdef_mask4x4;
const int row = DivideBy16(row4x4);
const int column = DivideBy16(column4x4);
if (cdef_index_[row][column] == -1) {
cdef_index_[row][column] =
static_cast<int16_t>(reader_.ReadLiteral(frame_header_.cdef.bits));
const int width4x4 = kNum4x4BlocksWide[block.size];
const int height4x4 = kNum4x4BlocksHigh[block.size];
for (int i = row4x4; i < row4x4 + height4x4; i += cdef_size4x4) {
for (int j = column4x4; j < column4x4 + width4x4; j += cdef_size4x4) {
cdef_index_[DivideBy16(i)][DivideBy16(j)] = cdef_index_[row][column];
}
}
}
}
int Tile::ReadAndClipDelta(uint16_t* const cdf, int delta_small, int scale,
int min_value, int max_value, int value) {
int abs = reader_.ReadSymbol<kDeltaSymbolCount>(cdf);
if (abs == delta_small) {
const int remaining_bit_count =
static_cast<int>(reader_.ReadLiteral(3)) + 1;
const int abs_remaining_bits =
static_cast<int>(reader_.ReadLiteral(remaining_bit_count));
abs = abs_remaining_bits + (1 << remaining_bit_count) + 1;
}
if (abs != 0) {
const bool sign = static_cast<bool>(reader_.ReadBit());
const int scaled_abs = abs << scale;
const int reduced_delta = sign ? -scaled_abs : scaled_abs;
value += reduced_delta;
value = Clip3(value, min_value, max_value);
}
return value;
}
void Tile::ReadQuantizerIndexDelta(const Block& block) {
assert(read_deltas_);
BlockParameters& bp = *block.bp;
if ((block.size == SuperBlockSize() && bp.skip)) {
return;
}
current_quantizer_index_ =
ReadAndClipDelta(symbol_decoder_context_.delta_q_cdf, kDeltaQSmall,
frame_header_.delta_q.scale, kMinLossyQuantizer,
kMaxQuantizer, current_quantizer_index_);
}
void Tile::ReadLoopFilterDelta(const Block& block) {
assert(read_deltas_);
BlockParameters& bp = *block.bp;
if (!frame_header_.delta_lf.present ||
(block.size == SuperBlockSize() && bp.skip)) {
return;
}
int frame_lf_count = 1;
if (frame_header_.delta_lf.multi) {
frame_lf_count = kFrameLfCount - (PlaneCount() > 1 ? 0 : 2);
}
bool recompute_deblock_filter_levels = false;
for (int i = 0; i < frame_lf_count; ++i) {
uint16_t* const delta_lf_abs_cdf =
frame_header_.delta_lf.multi
? symbol_decoder_context_.delta_lf_multi_cdf[i]
: symbol_decoder_context_.delta_lf_cdf;
const int8_t old_delta_lf = delta_lf_[i];
delta_lf_[i] = ReadAndClipDelta(
delta_lf_abs_cdf, kDeltaLfSmall, frame_header_.delta_lf.scale,
-kMaxLoopFilterValue, kMaxLoopFilterValue, delta_lf_[i]);
recompute_deblock_filter_levels =
recompute_deblock_filter_levels || (old_delta_lf != delta_lf_[i]);
}
delta_lf_all_zero_ =
(delta_lf_[0] | delta_lf_[1] | delta_lf_[2] | delta_lf_[3]) == 0;
if (!delta_lf_all_zero_ && recompute_deblock_filter_levels) {
post_filter_.ComputeDeblockFilterLevels(delta_lf_, deblock_filter_levels_);
}
}
void Tile::ReadPredictionModeY(const Block& block, bool intra_y_mode) {
uint16_t* cdf;
if (intra_y_mode) {
const PredictionMode top_mode =
block.top_available ? block.bp_top->y_mode : kPredictionModeDc;
const PredictionMode left_mode =
block.left_available ? block.bp_left->y_mode : kPredictionModeDc;
const int top_context = kIntraYModeContext[top_mode];
const int left_context = kIntraYModeContext[left_mode];
cdf = symbol_decoder_context_
.intra_frame_y_mode_cdf[top_context][left_context];
} else {
cdf = symbol_decoder_context_.y_mode_cdf[kSizeGroup[block.size]];
}
block.bp->y_mode = static_cast<PredictionMode>(
reader_.ReadSymbol<kIntraPredictionModesY>(cdf));
}
void Tile::ReadIntraAngleInfo(const Block& block, PlaneType plane_type) {
BlockParameters& bp = *block.bp;
PredictionParameters& prediction_parameters =
*block.bp->prediction_parameters;
prediction_parameters.angle_delta[plane_type] = 0;
const PredictionMode mode =
(plane_type == kPlaneTypeY) ? bp.y_mode : bp.uv_mode;
if (IsBlockSmallerThan8x8(block.size) || !IsDirectionalMode(mode)) return;
uint16_t* const cdf =
symbol_decoder_context_.angle_delta_cdf[mode - kPredictionModeVertical];
prediction_parameters.angle_delta[plane_type] =
reader_.ReadSymbol<kAngleDeltaSymbolCount>(cdf);
prediction_parameters.angle_delta[plane_type] -= kMaxAngleDelta;
}
void Tile::ReadCflAlpha(const Block& block) {
const int signs = reader_.ReadSymbol<kCflAlphaSignsSymbolCount>(
symbol_decoder_context_.cfl_alpha_signs_cdf);
const auto sign_u = static_cast<CflSign>((signs + 1) / 3);
const auto sign_v = static_cast<CflSign>((signs + 1) % 3);
PredictionParameters& prediction_parameters =
*block.bp->prediction_parameters;
prediction_parameters.cfl_alpha_u = 0;
if (sign_u != kCflSignZero) {
prediction_parameters.cfl_alpha_u =
reader_.ReadSymbol<kCflAlphaSymbolCount>(
symbol_decoder_context_.cfl_alpha_cdf[signs - 2]) +
1;
if (sign_u == kCflSignNegative) prediction_parameters.cfl_alpha_u *= -1;
}
prediction_parameters.cfl_alpha_v = 0;
if (sign_v != kCflSignZero) {
const int context = (sign_v - 1) * 3 + sign_u;
prediction_parameters.cfl_alpha_v =
reader_.ReadSymbol<kCflAlphaSymbolCount>(
symbol_decoder_context_.cfl_alpha_cdf[context]) +
1;
if (sign_v == kCflSignNegative) prediction_parameters.cfl_alpha_v *= -1;
}
}
void Tile::ReadPredictionModeUV(const Block& block) {
BlockParameters& bp = *block.bp;
bool chroma_from_luma_allowed;
if (frame_header_.segmentation.lossless[bp.segment_id]) {
chroma_from_luma_allowed =
kPlaneResidualSize[block.size][subsampling_x_[kPlaneU]]
[subsampling_y_[kPlaneU]] == kBlock4x4;
} else {
chroma_from_luma_allowed = IsBlockDimensionLessThan64(block.size);
}
uint16_t* const cdf =
symbol_decoder_context_
.uv_mode_cdf[static_cast<int>(chroma_from_luma_allowed)][bp.y_mode];
const int symbol_count =
kIntraPredictionModesUV - static_cast<int>(!chroma_from_luma_allowed);
bp.uv_mode =
static_cast<PredictionMode>(reader_.ReadSymbol(cdf, symbol_count));
}
int Tile::ReadMotionVectorComponent(const Block& block, const int component) {
const int context =
static_cast<int>(block.bp->prediction_parameters->use_intra_block_copy);
const bool sign = reader_.ReadSymbol(
symbol_decoder_context_.mv_sign_cdf[component][context]);
const int mv_class = reader_.ReadSymbol<kMvClassSymbolCount>(
symbol_decoder_context_.mv_class_cdf[component][context]);
int magnitude = 1;
int value;
uint16_t* fraction_cdf;
uint16_t* precision_cdf;
if (mv_class == 0) {
value = static_cast<int>(reader_.ReadSymbol(
symbol_decoder_context_.mv_class0_bit_cdf[component][context]));
fraction_cdf = symbol_decoder_context_
.mv_class0_fraction_cdf[component][context][value];
precision_cdf = symbol_decoder_context_
.mv_class0_high_precision_cdf[component][context];
} else {
assert(mv_class <= kMvBitSymbolCount);
value = 0;
for (int i = 0; i < mv_class; ++i) {
const int bit = static_cast<int>(reader_.ReadSymbol(
symbol_decoder_context_.mv_bit_cdf[component][context][i]));
value |= bit << i;
}
magnitude += 2 << (mv_class + 2);
fraction_cdf = symbol_decoder_context_.mv_fraction_cdf[component][context];
precision_cdf =
symbol_decoder_context_.mv_high_precision_cdf[component][context];
}
const int fraction =
(frame_header_.force_integer_mv == 0)
? reader_.ReadSymbol<kMvFractionSymbolCount>(fraction_cdf)
: 3;
const int precision =
frame_header_.allow_high_precision_mv
? static_cast<int>(reader_.ReadSymbol(precision_cdf))
: 1;
magnitude += (value << 3) | (fraction << 1) | precision;
return sign ? -magnitude : magnitude;
}
void Tile::ReadMotionVector(const Block& block, int index) {
BlockParameters& bp = *block.bp;
const int context =
static_cast<int>(block.bp->prediction_parameters->use_intra_block_copy);
const auto mv_joint = static_cast<MvJointType>(
reader_.ReadSymbol(symbol_decoder_context_.mv_joint_cdf[context],
static_cast<int>(kNumMvJointTypes)));
if (mv_joint == kMvJointTypeHorizontalZeroVerticalNonZero ||
mv_joint == kMvJointTypeNonZero) {
bp.mv[index].mv[0] = ReadMotionVectorComponent(block, 0);
}
if (mv_joint == kMvJointTypeHorizontalNonZeroVerticalZero ||
mv_joint == kMvJointTypeNonZero) {
bp.mv[index].mv[1] = ReadMotionVectorComponent(block, 1);
}
}
void Tile::ReadFilterIntraModeInfo(const Block& block) {
BlockParameters& bp = *block.bp;
PredictionParameters& prediction_parameters =
*block.bp->prediction_parameters;
prediction_parameters.use_filter_intra = false;
if (!sequence_header_.enable_filter_intra || bp.y_mode != kPredictionModeDc ||
bp.palette_mode_info.size[kPlaneTypeY] != 0 ||
!IsBlockDimensionLessThan64(block.size)) {
return;
}
prediction_parameters.use_filter_intra = reader_.ReadSymbol(
symbol_decoder_context_.use_filter_intra_cdf[block.size]);
if (prediction_parameters.use_filter_intra) {
prediction_parameters.filter_intra_mode = static_cast<FilterIntraPredictor>(
reader_.ReadSymbol<kNumFilterIntraPredictors>(
symbol_decoder_context_.filter_intra_mode_cdf));
}
}
bool Tile::DecodeIntraModeInfo(const Block& block) {
BlockParameters& bp = *block.bp;
bp.skip = false;
if (frame_header_.segmentation.segment_id_pre_skip &&
!ReadIntraSegmentId(block)) {
return false;
}
bp.skip_mode = false;
ReadSkip(block);
if (!frame_header_.segmentation.segment_id_pre_skip &&
!ReadIntraSegmentId(block)) {
return false;
}
ReadCdef(block);
if (read_deltas_) {
ReadQuantizerIndexDelta(block);
ReadLoopFilterDelta(block);
read_deltas_ = false;
}
PredictionParameters& prediction_parameters =
*block.bp->prediction_parameters;
prediction_parameters.use_intra_block_copy = false;
if (frame_header_.allow_intrabc) {
prediction_parameters.use_intra_block_copy =
reader_.ReadSymbol(symbol_decoder_context_.intra_block_copy_cdf);
}
if (prediction_parameters.use_intra_block_copy) {
bp.is_inter = true;
bp.reference_frame[0] = kReferenceFrameIntra;
bp.reference_frame[1] = kReferenceFrameNone;
bp.y_mode = kPredictionModeDc;
bp.uv_mode = kPredictionModeDc;
prediction_parameters.motion_mode = kMotionModeSimple;
prediction_parameters.compound_prediction_type =
kCompoundPredictionTypeAverage;
bp.palette_mode_info.size[kPlaneTypeY] = 0;
bp.palette_mode_info.size[kPlaneTypeUV] = 0;
bp.interpolation_filter[0] = kInterpolationFilterBilinear;
bp.interpolation_filter[1] = kInterpolationFilterBilinear;
FindMvStack(block, /*is_compound=*/false, reference_frame_sign_bias_,
*motion_field_mv_, prediction_parameters.ref_mv_stack,
&prediction_parameters.ref_mv_count, /*contexts=*/nullptr,
prediction_parameters.global_mv);
return AssignMv(block, /*is_compound=*/false);
}
bp.is_inter = false;
return ReadIntraBlockModeInfo(block, /*intra_y_mode=*/true);
}
int8_t Tile::ComputePredictedSegmentId(const Block& block) const {
// If prev_segment_ids_ is null, treat it as if it pointed to a segmentation
// map containing all 0s.
if (prev_segment_ids_ == nullptr) return 0;
const int x_limit = std::min(frame_header_.columns4x4 - block.column4x4,
static_cast<int>(kNum4x4BlocksWide[block.size]));
const int y_limit = std::min(frame_header_.rows4x4 - block.row4x4,
static_cast<int>(kNum4x4BlocksHigh[block.size]));
int8_t id = 7;
for (int y = 0; y < y_limit; ++y) {
for (int x = 0; x < x_limit; ++x) {
const int8_t prev_segment_id =
prev_segment_ids_->segment_id(block.row4x4 + y, block.column4x4 + x);
id = std::min(id, prev_segment_id);
}
}
return id;
}
bool Tile::ReadInterSegmentId(const Block& block, bool pre_skip) {
BlockParameters& bp = *block.bp;
if (!frame_header_.segmentation.enabled) {
bp.segment_id = 0;
return true;
}
if (!frame_header_.segmentation.update_map) {
bp.segment_id = ComputePredictedSegmentId(block);
return true;
}
if (pre_skip) {
if (!frame_header_.segmentation.segment_id_pre_skip) {
bp.segment_id = 0;
return true;
}
} else if (bp.skip) {
bp.use_predicted_segment_id = false;
return ReadSegmentId(block);
}
if (frame_header_.segmentation.temporal_update) {
const int context =
(block.left_available
? static_cast<int>(block.bp_left->use_predicted_segment_id)
: 0) +
(block.top_available
? static_cast<int>(block.bp_top->use_predicted_segment_id)
: 0);
bp.use_predicted_segment_id = reader_.ReadSymbol(
symbol_decoder_context_.use_predicted_segment_id_cdf[context]);
if (bp.use_predicted_segment_id) {
bp.segment_id = ComputePredictedSegmentId(block);
return true;
}
}
return ReadSegmentId(block);
}
void Tile::ReadIsInter(const Block& block) {
BlockParameters& bp = *block.bp;
if (bp.skip_mode) {
bp.is_inter = true;
return;
}
if (frame_header_.segmentation.FeatureActive(bp.segment_id,
kSegmentFeatureReferenceFrame)) {
bp.is_inter =
frame_header_.segmentation
.feature_data[bp.segment_id][kSegmentFeatureReferenceFrame] !=
kReferenceFrameIntra;
return;
}
if (frame_header_.segmentation.FeatureActive(bp.segment_id,
kSegmentFeatureGlobalMv)) {
bp.is_inter = true;
return;
}
int context = 0;
if (block.top_available && block.left_available) {
context = (block.IsTopIntra() && block.IsLeftIntra())
? 3
: static_cast<int>(block.IsTopIntra() || block.IsLeftIntra());
} else if (block.top_available || block.left_available) {
context = 2 * static_cast<int>(block.top_available ? block.IsTopIntra()
: block.IsLeftIntra());
}
bp.is_inter =
reader_.ReadSymbol(symbol_decoder_context_.is_inter_cdf[context]);
}
bool Tile::ReadIntraBlockModeInfo(const Block& block, bool intra_y_mode) {
BlockParameters& bp = *block.bp;
bp.reference_frame[0] = kReferenceFrameIntra;
bp.reference_frame[1] = kReferenceFrameNone;
ReadPredictionModeY(block, intra_y_mode);
ReadIntraAngleInfo(block, kPlaneTypeY);
if (block.HasChroma()) {
ReadPredictionModeUV(block);
if (bp.uv_mode == kPredictionModeChromaFromLuma) {
ReadCflAlpha(block);
}
ReadIntraAngleInfo(block, kPlaneTypeUV);
}
ReadPaletteModeInfo(block);
ReadFilterIntraModeInfo(block);
return true;
}
int Tile::GetUseCompoundReferenceContext(const Block& block) {
if (block.top_available && block.left_available) {
if (block.IsTopSingle() && block.IsLeftSingle()) {
return static_cast<int>(IsBackwardReference(block.TopReference(0))) ^
static_cast<int>(IsBackwardReference(block.LeftReference(0)));
}
if (block.IsTopSingle()) {
return 2 + static_cast<int>(IsBackwardReference(block.TopReference(0)) ||
block.IsTopIntra());
}
if (block.IsLeftSingle()) {
return 2 + static_cast<int>(IsBackwardReference(block.LeftReference(0)) ||
block.IsLeftIntra());
}
return 4;
}
if (block.top_available) {
return block.IsTopSingle()
? static_cast<int>(IsBackwardReference(block.TopReference(0)))
: 3;
}
if (block.left_available) {
return block.IsLeftSingle()
? static_cast<int>(IsBackwardReference(block.LeftReference(0)))
: 3;
}
return 1;
}
CompoundReferenceType Tile::ReadCompoundReferenceType(const Block& block) {
// compound and inter.
const bool top_comp_inter =
block.top_available && !block.IsTopIntra() && !block.IsTopSingle();
const bool left_comp_inter =
block.left_available && !block.IsLeftIntra() && !block.IsLeftSingle();
// unidirectional compound.
const bool top_uni_comp =
top_comp_inter && IsSameDirectionReferencePair(block.TopReference(0),
block.TopReference(1));
const bool left_uni_comp =
left_comp_inter && IsSameDirectionReferencePair(block.LeftReference(0),
block.LeftReference(1));
int context;
if (block.top_available && !block.IsTopIntra() && block.left_available &&
!block.IsLeftIntra()) {
const int same_direction = static_cast<int>(IsSameDirectionReferencePair(
block.TopReference(0), block.LeftReference(0)));
if (!top_comp_inter && !left_comp_inter) {
context = 1 + MultiplyBy2(same_direction);
} else if (!top_comp_inter) {
context = left_uni_comp ? 3 + same_direction : 1;
} else if (!left_comp_inter) {
context = top_uni_comp ? 3 + same_direction : 1;
} else {
if (!top_uni_comp && !left_uni_comp) {
context = 0;
} else if (!top_uni_comp || !left_uni_comp) {
context = 2;
} else {
context = 3 + static_cast<int>(
(block.TopReference(0) == kReferenceFrameBackward) ==
(block.LeftReference(0) == kReferenceFrameBackward));
}
}
} else if (block.top_available && block.left_available) {
if (top_comp_inter) {
context = 1 + MultiplyBy2(static_cast<int>(top_uni_comp));
} else if (left_comp_inter) {
context = 1 + MultiplyBy2(static_cast<int>(left_uni_comp));
} else {
context = 2;
}
} else if (top_comp_inter) {
context = MultiplyBy4(static_cast<int>(top_uni_comp));
} else if (left_comp_inter) {
context = MultiplyBy4(static_cast<int>(left_uni_comp));
} else {
context = 2;
}
return static_cast<CompoundReferenceType>(reader_.ReadSymbol(
symbol_decoder_context_.compound_reference_type_cdf[context]));
}
int Tile::GetReferenceContext(const Block& block,
ReferenceFrameType type0_start,
ReferenceFrameType type0_end,
ReferenceFrameType type1_start,
ReferenceFrameType type1_end) const {
int count0 = 0;
int count1 = 0;
for (int type = type0_start; type <= type0_end; ++type) {
count0 += block.CountReferences(static_cast<ReferenceFrameType>(type));
}
for (int type = type1_start; type <= type1_end; ++type) {
count1 += block.CountReferences(static_cast<ReferenceFrameType>(type));
}
return (count0 < count1) ? 0 : (count0 == count1 ? 1 : 2);
}
template <bool is_single, bool is_backward, int index>
uint16_t* Tile::GetReferenceCdf(
const Block& block,
CompoundReferenceType type /*= kNumCompoundReferenceTypes*/) {
int context = 0;
if ((type == kCompoundReferenceUnidirectional && index == 0) ||
(is_single && index == 1)) {
// uni_comp_ref and single_ref_p1.
context =
GetReferenceContext(block, kReferenceFrameLast, kReferenceFrameGolden,
kReferenceFrameBackward, kReferenceFrameAlternate);
} else if (type == kCompoundReferenceUnidirectional && index == 1) {
// uni_comp_ref_p1.
context =
GetReferenceContext(block, kReferenceFrameLast2, kReferenceFrameLast2,
kReferenceFrameLast3, kReferenceFrameGolden);
} else if ((type == kCompoundReferenceUnidirectional && index == 2) ||
(type == kCompoundReferenceBidirectional && index == 2) ||
(is_single && index == 5)) {
// uni_comp_ref_p2, comp_ref_p2 and single_ref_p5.
context =
GetReferenceContext(block, kReferenceFrameLast3, kReferenceFrameLast3,
kReferenceFrameGolden, kReferenceFrameGolden);
} else if ((type == kCompoundReferenceBidirectional && index == 0) ||
(is_single && index == 3)) {
// comp_ref and single_ref_p3.
context =
GetReferenceContext(block, kReferenceFrameLast, kReferenceFrameLast2,
kReferenceFrameLast3, kReferenceFrameGolden);
} else if ((type == kCompoundReferenceBidirectional && index == 1) ||
(is_single && index == 4)) {
// comp_ref_p1 and single_ref_p4.
context =
GetReferenceContext(block, kReferenceFrameLast, kReferenceFrameLast,
kReferenceFrameLast2, kReferenceFrameLast2);
} else if ((is_single && index == 2) || (is_backward && index == 0)) {
// single_ref_p2 and comp_bwdref.
context = GetReferenceContext(
block, kReferenceFrameBackward, kReferenceFrameAlternate2,
kReferenceFrameAlternate, kReferenceFrameAlternate);
} else if ((is_single && index == 6) || (is_backward && index == 1)) {
// single_ref_p6 and comp_bwdref_p1.
context = GetReferenceContext(
block, kReferenceFrameBackward, kReferenceFrameBackward,
kReferenceFrameAlternate2, kReferenceFrameAlternate2);
}
if (is_single) {
// The index parameter for single references is offset by one since the spec
// uses 1-based index for these elements.
return symbol_decoder_context_.single_reference_cdf[context][index - 1];
}
if (is_backward) {
return symbol_decoder_context_
.compound_backward_reference_cdf[context][index];
}
return symbol_decoder_context_.compound_reference_cdf[type][context][index];
}
void Tile::ReadReferenceFrames(const Block& block) {
BlockParameters& bp = *block.bp;
if (bp.skip_mode) {
bp.reference_frame[0] = frame_header_.skip_mode_frame[0];
bp.reference_frame[1] = frame_header_.skip_mode_frame[1];
return;
}
if (frame_header_.segmentation.FeatureActive(bp.segment_id,
kSegmentFeatureReferenceFrame)) {
bp.reference_frame[0] = static_cast<ReferenceFrameType>(
frame_header_.segmentation
.feature_data[bp.segment_id][kSegmentFeatureReferenceFrame]);
bp.reference_frame[1] = kReferenceFrameNone;
return;
}
if (frame_header_.segmentation.FeatureActive(bp.segment_id,
kSegmentFeatureSkip) ||
frame_header_.segmentation.FeatureActive(bp.segment_id,
kSegmentFeatureGlobalMv)) {
bp.reference_frame[0] = kReferenceFrameLast;
bp.reference_frame[1] = kReferenceFrameNone;
return;
}
const int block_width4x4 = kNum4x4BlocksWide[block.size];
const int block_height4x4 = kNum4x4BlocksHigh[block.size];
const bool use_compound_reference =
frame_header_.reference_mode_select &&
std::min(block_width4x4, block_height4x4) >= 2 &&
reader_.ReadSymbol(symbol_decoder_context_.use_compound_reference_cdf
[GetUseCompoundReferenceContext(block)]);
if (use_compound_reference) {
CompoundReferenceType reference_type = ReadCompoundReferenceType(block);
if (reference_type == kCompoundReferenceUnidirectional) {
// uni_comp_ref.
if (reader_.ReadSymbol(
GetReferenceCdf<false, false, 0>(block, reference_type))) {
bp.reference_frame[0] = kReferenceFrameBackward;
bp.reference_frame[1] = kReferenceFrameAlternate;
return;
}
// uni_comp_ref_p1.
if (!reader_.ReadSymbol(
GetReferenceCdf<false, false, 1>(block, reference_type))) {
bp.reference_frame[0] = kReferenceFrameLast;
bp.reference_frame[1] = kReferenceFrameLast2;
return;
}
// uni_comp_ref_p2.
if (reader_.ReadSymbol(
GetReferenceCdf<false, false, 2>(block, reference_type))) {
bp.reference_frame[0] = kReferenceFrameLast;
bp.reference_frame[1] = kReferenceFrameGolden;
return;
}
bp.reference_frame[0] = kReferenceFrameLast;
bp.reference_frame[1] = kReferenceFrameLast3;
return;
}
assert(reference_type == kCompoundReferenceBidirectional);
// comp_ref.
if (reader_.ReadSymbol(
GetReferenceCdf<false, false, 0>(block, reference_type))) {
// comp_ref_p2.
bp.reference_frame[0] =
reader_.ReadSymbol(
GetReferenceCdf<false, false, 2>(block, reference_type))
? kReferenceFrameGolden
: kReferenceFrameLast3;
} else {
// comp_ref_p1.
bp.reference_frame[0] =
reader_.ReadSymbol(
GetReferenceCdf<false, false, 1>(block, reference_type))
? kReferenceFrameLast2
: kReferenceFrameLast;
}
// comp_bwdref.
if (reader_.ReadSymbol(GetReferenceCdf<false, true, 0>(block))) {
bp.reference_frame[1] = kReferenceFrameAlternate;
} else {
// comp_bwdref_p1.
bp.reference_frame[1] =
reader_.ReadSymbol(GetReferenceCdf<false, true, 1>(block))
? kReferenceFrameAlternate2
: kReferenceFrameBackward;
}
return;
}
assert(!use_compound_reference);
bp.reference_frame[1] = kReferenceFrameNone;
// single_ref_p1.
if (reader_.ReadSymbol(GetReferenceCdf<true, false, 1>(block))) {
// single_ref_p2.
if (reader_.ReadSymbol(GetReferenceCdf<true, false, 2>(block))) {
bp.reference_frame[0] = kReferenceFrameAlternate;
return;
}
// single_ref_p6.
bp.reference_frame[0] =
reader_.ReadSymbol(GetReferenceCdf<true, false, 6>(block))
? kReferenceFrameAlternate2
: kReferenceFrameBackward;
return;
}
// single_ref_p3.
if (reader_.ReadSymbol(GetReferenceCdf<true, false, 3>(block))) {
// single_ref_p5.
bp.reference_frame[0] =
reader_.ReadSymbol(GetReferenceCdf<true, false, 5>(block))
? kReferenceFrameGolden
: kReferenceFrameLast3;
return;
}
// single_ref_p4.
bp.reference_frame[0] =
reader_.ReadSymbol(GetReferenceCdf<true, false, 4>(block))
? kReferenceFrameLast2
: kReferenceFrameLast;
}
void Tile::ReadInterPredictionModeY(const Block& block,
const MvContexts& mode_contexts) {
BlockParameters& bp = *block.bp;
if (bp.skip_mode) {
bp.y_mode = kPredictionModeNearestNearestMv;
return;
}
if (frame_header_.segmentation.FeatureActive(bp.segment_id,
kSegmentFeatureSkip) ||
frame_header_.segmentation.FeatureActive(bp.segment_id,
kSegmentFeatureGlobalMv)) {
bp.y_mode = kPredictionModeGlobalMv;
return;
}
if (bp.reference_frame[1] > kReferenceFrameIntra) {
const int idx0 = mode_contexts.reference_mv >> 1;
const int idx1 =
std::min(mode_contexts.new_mv, kCompoundModeNewMvContexts - 1);
const int context = kCompoundModeContextMap[idx0][idx1];
const int offset = reader_.ReadSymbol<kNumCompoundInterPredictionModes>(
symbol_decoder_context_.compound_prediction_mode_cdf[context]);
bp.y_mode =
static_cast<PredictionMode>(kPredictionModeNearestNearestMv + offset);
return;
}
// new_mv.
if (!reader_.ReadSymbol(
symbol_decoder_context_.new_mv_cdf[mode_contexts.new_mv])) {
bp.y_mode = kPredictionModeNewMv;
return;
}
// zero_mv.
if (!reader_.ReadSymbol(
symbol_decoder_context_.zero_mv_cdf[mode_contexts.zero_mv])) {
bp.y_mode = kPredictionModeGlobalMv;
return;
}
// ref_mv.
bp.y_mode =
reader_.ReadSymbol(
symbol_decoder_context_.reference_mv_cdf[mode_contexts.reference_mv])
? kPredictionModeNearMv
: kPredictionModeNearestMv;
}
void Tile::ReadRefMvIndex(const Block& block) {
BlockParameters& bp = *block.bp;
PredictionParameters& prediction_parameters =
*block.bp->prediction_parameters;
prediction_parameters.ref_mv_index = 0;
if (bp.y_mode != kPredictionModeNewMv &&
bp.y_mode != kPredictionModeNewNewMv &&
!kPredictionModeHasNearMvMask.Contains(bp.y_mode)) {
return;
}
const int start =
static_cast<int>(kPredictionModeHasNearMvMask.Contains(bp.y_mode));
prediction_parameters.ref_mv_index = start;
for (int i = start; i < start + 2; ++i) {
if (prediction_parameters.ref_mv_count <= i + 1) continue;
// drl_mode in the spec.
const bool ref_mv_index_bit = reader_.ReadSymbol(
symbol_decoder_context_.ref_mv_index_cdf[GetRefMvIndexContext(
prediction_parameters.ref_mv_stack,
prediction_parameters.ref_mv_count, i)]);
prediction_parameters.ref_mv_index = i + static_cast<int>(ref_mv_index_bit);
if (!ref_mv_index_bit) return;
}
}
void Tile::ReadInterIntraMode(const Block& block, bool is_compound) {
BlockParameters& bp = *block.bp;
PredictionParameters& prediction_parameters =
*block.bp->prediction_parameters;
prediction_parameters.inter_intra_mode = kNumInterIntraModes;
prediction_parameters.is_wedge_inter_intra = false;
if (bp.skip_mode || !sequence_header_.enable_interintra_compound ||
is_compound || !kIsInterIntraModeAllowedMask.Contains(block.size)) {
return;
}
// kSizeGroup[block.size] is guaranteed to be non-zero because of the block
// size constraint enforced in the above condition.
assert(kSizeGroup[block.size] - 1 >= 0);
if (!reader_.ReadSymbol(
symbol_decoder_context_
.is_inter_intra_cdf[kSizeGroup[block.size] - 1])) {
prediction_parameters.inter_intra_mode = kNumInterIntraModes;
return;
}
prediction_parameters.inter_intra_mode =
static_cast<InterIntraMode>(reader_.ReadSymbol<kNumInterIntraModes>(
symbol_decoder_context_
.inter_intra_mode_cdf[kSizeGroup[block.size] - 1]));
bp.reference_frame[1] = kReferenceFrameIntra;
prediction_parameters.angle_delta[kPlaneTypeY] = 0;
prediction_parameters.angle_delta[kPlaneTypeUV] = 0;
prediction_parameters.use_filter_intra = false;
prediction_parameters.is_wedge_inter_intra = reader_.ReadSymbol(
symbol_decoder_context_.is_wedge_inter_intra_cdf[block.size]);
if (!prediction_parameters.is_wedge_inter_intra) return;
prediction_parameters.wedge_index =
reader_.ReadSymbol<kWedgeIndexSymbolCount>(
symbol_decoder_context_.wedge_index_cdf[block.size]);
prediction_parameters.wedge_sign = 0;
}
bool Tile::IsScaled(ReferenceFrameType type) const {
const int index =
frame_header_.reference_frame_index[type - kReferenceFrameLast];
const int x_scale = ((reference_frames_[index]->upscaled_width()
<< kReferenceFrameScalePrecision) +
DivideBy2(frame_header_.width)) /
frame_header_.width;
if (x_scale != kNoScale) return true;
const int y_scale = ((reference_frames_[index]->frame_height()
<< kReferenceFrameScalePrecision) +
DivideBy2(frame_header_.height)) /
frame_header_.height;
return y_scale != kNoScale;
}
void Tile::ReadMotionMode(const Block& block, bool is_compound) {
BlockParameters& bp = *block.bp;
PredictionParameters& prediction_parameters =
*block.bp->prediction_parameters;
const auto global_motion_type =
frame_header_.global_motion[bp.reference_frame[0]].type;
if (bp.skip_mode || !frame_header_.is_motion_mode_switchable ||
IsBlockDimension4(block.size) ||
(frame_header_.force_integer_mv == 0 &&
(bp.y_mode == kPredictionModeGlobalMv ||
bp.y_mode == kPredictionModeGlobalGlobalMv) &&
global_motion_type > kGlobalMotionTransformationTypeTranslation) ||
is_compound || bp.reference_frame[1] == kReferenceFrameIntra ||
!block.HasOverlappableCandidates()) {
prediction_parameters.motion_mode = kMotionModeSimple;
return;
}
prediction_parameters.num_warp_samples = 0;
int num_samples_scanned = 0;
memset(prediction_parameters.warp_estimate_candidates, 0,
sizeof(prediction_parameters.warp_estimate_candidates));
FindWarpSamples(block, &prediction_parameters.num_warp_samples,
&num_samples_scanned,
prediction_parameters.warp_estimate_candidates);
if (frame_header_.force_integer_mv != 0 ||
prediction_parameters.num_warp_samples == 0 ||
!frame_header_.allow_warped_motion || IsScaled(bp.reference_frame[0])) {
prediction_parameters.motion_mode =
reader_.ReadSymbol(symbol_decoder_context_.use_obmc_cdf[block.size])
? kMotionModeObmc
: kMotionModeSimple;
return;
}
prediction_parameters.motion_mode =
static_cast<MotionMode>(reader_.ReadSymbol<kNumMotionModes>(
symbol_decoder_context_.motion_mode_cdf[block.size]));
}
uint16_t* Tile::GetIsExplicitCompoundTypeCdf(const Block& block) {
int context = 0;
if (block.top_available) {
if (!block.IsTopSingle()) {
context += static_cast<int>(block.bp_top->is_explicit_compound_type);
} else if (block.TopReference(0) == kReferenceFrameAlternate) {
context += 3;
}
}
if (block.left_available) {
if (!block.IsLeftSingle()) {
context += static_cast<int>(block.bp_left->is_explicit_compound_type);
} else if (block.LeftReference(0) == kReferenceFrameAlternate) {
context += 3;
}
}
return symbol_decoder_context_.is_explicit_compound_type_cdf[std::min(
context, kIsExplicitCompoundTypeContexts - 1)];
}
uint16_t* Tile::GetIsCompoundTypeAverageCdf(const Block& block) {
const BlockParameters& bp = *block.bp;
const int forward = std::abs(GetRelativeDistance(
current_frame_.order_hint(bp.reference_frame[0]),
frame_header_.order_hint, sequence_header_.enable_order_hint,
sequence_header_.order_hint_bits));
const int backward = std::abs(GetRelativeDistance(
current_frame_.order_hint(bp.reference_frame[1]),
frame_header_.order_hint, sequence_header_.enable_order_hint,
sequence_header_.order_hint_bits));
int context = (forward == backward) ? 3 : 0;
if (block.top_available) {
if (!block.IsTopSingle()) {
context += static_cast<int>(block.bp_top->is_compound_type_average);
} else if (block.TopReference(0) == kReferenceFrameAlternate) {
++context;
}
}
if (block.left_available) {
if (!block.IsLeftSingle()) {
context += static_cast<int>(block.bp_left->is_compound_type_average);
} else if (block.LeftReference(0) == kReferenceFrameAlternate) {
++context;
}
}
return symbol_decoder_context_.is_compound_type_average_cdf[context];
}
void Tile::ReadCompoundType(const Block& block, bool is_compound) {
BlockParameters& bp = *block.bp;
bp.is_explicit_compound_type = false;
bp.is_compound_type_average = true;
PredictionParameters& prediction_parameters =
*block.bp->prediction_parameters;
if (bp.skip_mode) {
prediction_parameters.compound_prediction_type =
kCompoundPredictionTypeAverage;
return;
}
if (is_compound) {
if (sequence_header_.enable_masked_compound) {
bp.is_explicit_compound_type =
reader_.ReadSymbol(GetIsExplicitCompoundTypeCdf(block));
}
if (bp.is_explicit_compound_type) {
if (kIsWedgeCompoundModeAllowed.Contains(block.size)) {
// Only kCompoundPredictionTypeWedge and
// kCompoundPredictionTypeDiffWeighted are signaled explicitly.
prediction_parameters.compound_prediction_type =
static_cast<CompoundPredictionType>(reader_.ReadSymbol(
symbol_decoder_context_.compound_type_cdf[block.size]));
} else {
prediction_parameters.compound_prediction_type =
kCompoundPredictionTypeDiffWeighted;
}
} else {
if (sequence_header_.enable_jnt_comp) {
bp.is_compound_type_average =
reader_.ReadSymbol(GetIsCompoundTypeAverageCdf(block));
prediction_parameters.compound_prediction_type =
bp.is_compound_type_average ? kCompoundPredictionTypeAverage
: kCompoundPredictionTypeDistance;
} else {
prediction_parameters.compound_prediction_type =
kCompoundPredictionTypeAverage;
return;
}
}
if (prediction_parameters.compound_prediction_type ==
kCompoundPredictionTypeWedge) {
prediction_parameters.wedge_index =
reader_.ReadSymbol<kWedgeIndexSymbolCount>(
symbol_decoder_context_.wedge_index_cdf[block.size]);
prediction_parameters.wedge_sign =
static_cast<int>(reader_.ReadLiteral(1));
} else if (prediction_parameters.compound_prediction_type ==
kCompoundPredictionTypeDiffWeighted) {
prediction_parameters.mask_is_inverse =
static_cast<bool>(reader_.ReadLiteral(1));
}
return;
}
if (prediction_parameters.inter_intra_mode != kNumInterIntraModes) {
prediction_parameters.compound_prediction_type =
prediction_parameters.is_wedge_inter_intra
? kCompoundPredictionTypeWedge
: kCompoundPredictionTypeIntra;
return;
}
prediction_parameters.compound_prediction_type =
kCompoundPredictionTypeAverage;
}
uint16_t* Tile::GetInterpolationFilterCdf(const Block& block, int direction) {
const BlockParameters& bp = *block.bp;
int context = MultiplyBy8(direction) +
MultiplyBy4(static_cast<int>(bp.reference_frame[1] >
kReferenceFrameIntra));
int top_type = kNumExplicitInterpolationFilters;
if (block.top_available) {
if (block.bp_top->reference_frame[0] == bp.reference_frame[0] ||
block.bp_top->reference_frame[1] == bp.reference_frame[0]) {
top_type = block.bp_top->interpolation_filter[direction];
}
}
int left_type = kNumExplicitInterpolationFilters;
if (block.left_available) {
if (block.bp_left->reference_frame[0] == bp.reference_frame[0] ||
block.bp_left->reference_frame[1] == bp.reference_frame[0]) {
left_type = block.bp_left->interpolation_filter[direction];
}
}
if (left_type == top_type) {
context += left_type;
} else if (left_type == kNumExplicitInterpolationFilters) {
context += top_type;
} else if (top_type == kNumExplicitInterpolationFilters) {
context += left_type;
} else {
context += kNumExplicitInterpolationFilters;
}
return symbol_decoder_context_.interpolation_filter_cdf[context];
}
void Tile::ReadInterpolationFilter(const Block& block) {
BlockParameters& bp = *block.bp;
if (frame_header_.interpolation_filter != kInterpolationFilterSwitchable) {
static_assert(
sizeof(bp.interpolation_filter) / sizeof(bp.interpolation_filter[0]) ==
2,
"Interpolation filter array size is not 2");
for (auto& interpolation_filter : bp.interpolation_filter) {
interpolation_filter = frame_header_.interpolation_filter;
}
return;
}
bool interpolation_filter_present = true;
if (bp.skip_mode ||
block.bp->prediction_parameters->motion_mode == kMotionModeLocalWarp) {
interpolation_filter_present = false;
} else if (!IsBlockDimension4(block.size) &&
bp.y_mode == kPredictionModeGlobalMv) {
interpolation_filter_present =
frame_header_.global_motion[bp.reference_frame[0]].type ==
kGlobalMotionTransformationTypeTranslation;
} else if (!IsBlockDimension4(block.size) &&
bp.y_mode == kPredictionModeGlobalGlobalMv) {
interpolation_filter_present =
frame_header_.global_motion[bp.reference_frame[0]].type ==
kGlobalMotionTransformationTypeTranslation ||
frame_header_.global_motion[bp.reference_frame[1]].type ==
kGlobalMotionTransformationTypeTranslation;
}
for (int i = 0; i < (sequence_header_.enable_dual_filter ? 2 : 1); ++i) {
bp.interpolation_filter[i] =
interpolation_filter_present
? static_cast<InterpolationFilter>(
reader_.ReadSymbol<kNumExplicitInterpolationFilters>(
GetInterpolationFilterCdf(block, i)))
: kInterpolationFilterEightTap;
}
if (!sequence_header_.enable_dual_filter) {
bp.interpolation_filter[1] = bp.interpolation_filter[0];
}
}
bool Tile::ReadInterBlockModeInfo(const Block& block) {
BlockParameters& bp = *block.bp;
bp.palette_mode_info.size[kPlaneTypeY] = 0;
bp.palette_mode_info.size[kPlaneTypeUV] = 0;
ReadReferenceFrames(block);
const bool is_compound = bp.reference_frame[1] > kReferenceFrameIntra;
PredictionParameters& prediction_parameters =
*block.bp->prediction_parameters;
MvContexts mode_contexts;
FindMvStack(block, is_compound, reference_frame_sign_bias_, *motion_field_mv_,
prediction_parameters.ref_mv_stack,
&prediction_parameters.ref_mv_count, &mode_contexts,
prediction_parameters.global_mv);
ReadInterPredictionModeY(block, mode_contexts);
ReadRefMvIndex(block);
if (!AssignMv(block, is_compound)) return false;
ReadInterIntraMode(block, is_compound);
ReadMotionMode(block, is_compound);
ReadCompoundType(block, is_compound);
ReadInterpolationFilter(block);
return true;
}
bool Tile::DecodeInterModeInfo(const Block& block) {
BlockParameters& bp = *block.bp;
block.bp->prediction_parameters->use_intra_block_copy = false;
bp.skip = false;
if (!ReadInterSegmentId(block, /*pre_skip=*/true)) return false;
ReadSkipMode(block);
if (bp.skip_mode) {
bp.skip = true;
} else {
ReadSkip(block);
}
if (!frame_header_.segmentation.segment_id_pre_skip &&
!ReadInterSegmentId(block, /*pre_skip=*/false)) {
return false;
}
ReadCdef(block);
if (read_deltas_) {
ReadQuantizerIndexDelta(block);
ReadLoopFilterDelta(block);
read_deltas_ = false;
}
ReadIsInter(block);
return bp.is_inter ? ReadInterBlockModeInfo(block)
: ReadIntraBlockModeInfo(block, /*intra_y_mode=*/false);
}
bool Tile::DecodeModeInfo(const Block& block) {
return IsIntraFrame(frame_header_.frame_type) ? DecodeIntraModeInfo(block)
: DecodeInterModeInfo(block);
}
} // namespace libgav1