| // 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 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 |
| }; |
| |
| // 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. |
| 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 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[kPlaneTypeUV] == 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_.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) { |
| 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 |