diff options
Diffstat (limited to 'src/tile/bitstream/mode_info.cc')
-rw-r--r-- | src/tile/bitstream/mode_info.cc | 1303 |
1 files changed, 1303 insertions, 0 deletions
diff --git a/src/tile/bitstream/mode_info.cc b/src/tile/bitstream/mode_info.cc new file mode 100644 index 0000000..0b22eb0 --- /dev/null +++ b/src/tile/bitstream/mode_info.cc @@ -0,0 +1,1303 @@ +// Copyright 2019 The libgav1 Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#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 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 +}; + +// For each possible value of the combined signs (which is read from the +// bitstream), this array stores the following: sign_u, sign_v, alpha_u_context, +// alpha_v_context. Only positive entries are used. Entry at index i is computed +// as follows: +// sign_u = i / 3 +// sign_v = i % 3 +// alpha_u_context = i - 2 +// alpha_v_context = (sign_v - 1) * 3 + sign_u +constexpr int8_t kCflAlphaLookup[kCflAlphaSignsSymbolCount][4] = { + {0, 1, -2, 0}, {0, 2, -1, 3}, {1, 0, 0, -2}, {1, 1, 1, 1}, + {1, 2, 2, 4}, {2, 0, 3, -1}, {2, 1, 4, 2}, {2, 2, 5, 5}, +}; + +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. +// In the spec, the weights of all the nearest mvs are incremented by a bonus +// weight which is larger than any natural weight, and the weights of the mvs +// are compared with this bonus weight to determine their contexts. We replace +// this procedure by introducing |nearest_mv_count| in PredictionParameters, +// which records the count of the nearest mvs. Since all the nearest mvs are in +// the beginning of the mv stack, the |index| of a mv in the mv stack can be +// compared with |nearest_mv_count| to get that mv's context. +int GetRefMvIndexContext(int nearest_mv_count, int index) { + if (index + 1 < nearest_mv_count) { + return 0; + } + if (index + 1 == nearest_mv_count) { + return 1; + } + return 2; +} + +// Returns true if both the width and height of the block is less than 64. +bool IsBlockDimensionLessThan64(BlockSize size) { + return size <= kBlock32x32 && size != kBlock16x64; +} + +int GetUseCompoundReferenceContext(const Tile::Block& block) { + if (block.top_available[kPlaneY] && block.left_available[kPlaneY]) { + 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[kPlaneY]) { + return block.IsTopSingle() + ? static_cast<int>(IsBackwardReference(block.TopReference(0))) + : 3; + } + if (block.left_available[kPlaneY]) { + return block.IsLeftSingle() + ? static_cast<int>(IsBackwardReference(block.LeftReference(0))) + : 3; + } + return 1; +} + +// Calculates count0 by calling block.CountReferences() on the frame types from +// type0_start to type0_end, inclusive, and summing the results. +// Calculates count1 by calling block.CountReferences() on the frame types from +// type1_start to type1_end, inclusive, and summing the results. +// Compares count0 with count1 and returns 0, 1 or 2. +// +// See count_refs and ref_count_ctx in 8.3.2. +int GetReferenceContext(const Tile::Block& block, + ReferenceFrameType type0_start, + ReferenceFrameType type0_end, + ReferenceFrameType type1_start, + ReferenceFrameType type1_end) { + 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); +} + +} // namespace + +bool Tile::ReadSegmentId(const Block& block) { + int top_left = -1; + if (block.top_available[kPlaneY] && block.left_available[kPlaneY]) { + top_left = + block_parameters_holder_.Find(block.row4x4 - 1, block.column4x4 - 1) + ->segment_id; + } + int top = -1; + if (block.top_available[kPlaneY]) { + top = block.bp_top->segment_id; + } + int left = -1; + if (block.left_available[kPlaneY]) { + 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[kPlaneY] && block.bp_top->skip) { + ++context; + } + if (block.left_available[kPlaneY] && 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[kPlaneY] + ? static_cast<int>(block.bp_left->skip_mode) + : 0) + + (block.top_available[kPlaneY] ? 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] = + frame_header_.cdef.bits > 0 + ? static_cast<int16_t>(reader_.ReadLiteral(frame_header_.cdef.bits)) + : 0; + for (int i = row4x4; i < row4x4 + block.height4x4; i += cdef_size4x4) { + for (int j = column4x4; j < column4x4 + block.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[kPlaneY] ? block.bp_top->y_mode : kPredictionModeDc; + const PredictionMode left_mode = block.left_available[kPlaneY] + ? 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 int8_t* const cfl_lookup = kCflAlphaLookup[signs]; + const auto sign_u = static_cast<CflSign>(cfl_lookup[0]); + const auto sign_v = static_cast<CflSign>(cfl_lookup[1]); + PredictionParameters& prediction_parameters = + *block.bp->prediction_parameters; + prediction_parameters.cfl_alpha_u = 0; + if (sign_u != kCflSignZero) { + assert(cfl_lookup[2] >= 0); + prediction_parameters.cfl_alpha_u = + reader_.ReadSymbol<kCflAlphaSymbolCount>( + symbol_decoder_context_.cfl_alpha_cdf[cfl_lookup[2]]) + + 1; + if (sign_u == kCflSignNegative) prediction_parameters.cfl_alpha_u *= -1; + } + prediction_parameters.cfl_alpha_v = 0; + if (sign_v != kCflSignZero) { + assert(cfl_lookup[3] >= 0); + prediction_parameters.cfl_alpha_v = + reader_.ReadSymbol<kCflAlphaSymbolCount>( + symbol_decoder_context_.cfl_alpha_cdf[cfl_lookup[3]]) + + 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 = block.residual_size[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]; + if (chroma_from_luma_allowed) { + bp.uv_mode = static_cast<PredictionMode>( + reader_.ReadSymbol<kIntraPredictionModesUV>(cdf)); + } else { + bp.uv_mode = static_cast<PredictionMode>( + reader_.ReadSymbol<kIntraPredictionModesUV - 1>(cdf)); + } +} + +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<kNumMvJointTypes>( + symbol_decoder_context_.mv_joint_cdf[context])); + if (mv_joint == kMvJointTypeHorizontalZeroVerticalNonZero || + mv_joint == kMvJointTypeNonZero) { + bp.mv.mv[index].mv[0] = ReadMotionVectorComponent(block, 0); + } + if (mv_joint == kMvJointTypeHorizontalNonZeroVerticalZero || + mv_joint == kMvJointTypeNonZero) { + bp.mv.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; + MvContexts dummy_mode_contexts; + FindMvStack(block, /*is_compound=*/false, &dummy_mode_contexts); + return AssignIntraMv(block); + } + 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>(block.width4x4)); + const int y_limit = std::min(frame_header_.rows4x4 - block.row4x4, + static_cast<int>(block.height4x4)); + 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[kPlaneY] + ? static_cast<int>(block.bp_left->use_predicted_segment_id) + : 0) + + (block.top_available[kPlaneY] + ? 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[kPlaneY] && block.left_available[kPlaneY]) { + context = (block.IsTopIntra() && block.IsLeftIntra()) + ? 3 + : static_cast<int>(block.IsTopIntra() || block.IsLeftIntra()); + } else if (block.top_available[kPlaneY] || block.left_available[kPlaneY]) { + context = 2 * static_cast<int>(block.top_available[kPlaneY] + ? 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; +} + +CompoundReferenceType Tile::ReadCompoundReferenceType(const Block& block) { + // compound and inter. + const bool top_comp_inter = block.top_available[kPlaneY] && + !block.IsTopIntra() && !block.IsTopSingle(); + const bool left_comp_inter = block.left_available[kPlaneY] && + !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[kPlaneY] && !block.IsTopIntra() && + block.left_available[kPlaneY] && !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[kPlaneY] && block.left_available[kPlaneY]) { + 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])); +} + +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 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) break; + // drl_mode in the spec. + const bool ref_mv_index_bit = reader_.ReadSymbol( + symbol_decoder_context_.ref_mv_index_cdf[GetRefMvIndexContext( + prediction_parameters.nearest_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; +} + +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[kPlaneY]) { + 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[kPlaneY]) { + 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 ReferenceInfo& reference_info = *current_frame_.reference_info(); + const int forward = + std::abs(reference_info.relative_distance_from[bp.reference_frame[0]]); + const int backward = + std::abs(reference_info.relative_distance_from[bp.reference_frame[1]]); + int context = (forward == backward) ? 3 : 0; + if (block.top_available[kPlaneY]) { + 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[kPlaneY]) { + 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_.ReadBit()); + } else if (prediction_parameters.compound_prediction_type == + kCompoundPredictionTypeDiffWeighted) { + prediction_parameters.mask_is_inverse = + static_cast<bool>(reader_.ReadBit()); + } + 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[kPlaneY]) { + 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[kPlaneY]) { + 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; + MvContexts mode_contexts; + FindMvStack(block, is_compound, &mode_contexts); + ReadInterPredictionModeY(block, mode_contexts); + ReadRefMvIndex(block); + if (!AssignInterMv(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 |