blob: c199d06ae9088f122fc2ed3c4369f070bd86c6ec [file] [log] [blame]
/*
* Copyright (c) 2021, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 3-Clause Clear License
* and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause
* Clear License was not distributed with this source code in the LICENSE file,
* you can obtain it at aomedia.org/license/software-license/bsd-3-c-c/.
* If the Alliance for Open Media Patent License 1.0 was not distributed with
* this source code in the PATENTS file, you can obtain it at
* aomedia.org/license/patent-license/.
*/
#ifndef AOM_AV1_COMMON_TIP_H_
#define AOM_AV1_COMMON_TIP_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "av1/common/av1_common_int.h"
#include "av1/common/mvref_common.h"
#include "av1/common/reconinter.h"
#if CONFIG_OPTFLOW_ON_TIP
#define TIP_RD_CORRECTION 100000
#endif // CONFIG_OPTFLOW_ON_TIP
// Derive temporal motion field from one closest forward and one closet backward
// reference frames, then fill the hole
void av1_setup_tip_motion_field(AV1_COMMON *cm, int check_tip_threshold);
// Generate the whole TIP frame with the temporal motion field
void av1_setup_tip_frame(AV1_COMMON *cm, MACROBLOCKD *xd, uint16_t **mc_buf,
CONV_BUF_TYPE *tmp_conv_dst,
CalcSubpelParamsFunc calc_subpel_params_func);
// Generate TIP reference block if current block is coded as TIP mode
void av1_setup_tip_on_the_fly(AV1_COMMON *cm, MACROBLOCKD *xd,
int blk_row_start, int blk_col_start,
int blk_row_end, int blk_col_end, int mvs_stride,
uint16_t **mc_buf, CONV_BUF_TYPE *tmp_conv_dst,
CalcSubpelParamsFunc calc_subpel_params_func);
// Derive TMVP from closest forward and closet backward reference frames
void av1_derive_tip_nearest_ref_frames_motion_projection(AV1_COMMON *cm);
// Save TMVP motion fields for future frame's TMVP if current frame is coded
// as TIP_FRAME_AS_OUTPUT
void av1_copy_tip_frame_tmvp_mvs(const AV1_COMMON *const cm);
// Compute scale factor for temporal scaling
static AOM_INLINE int tip_derive_scale_factor(int num, int den) {
den = AOMMIN(den, MAX_FRAME_DISTANCE);
num = num > 0 ? AOMMIN(num, MAX_FRAME_DISTANCE)
: AOMMAX(num, -MAX_FRAME_DISTANCE);
return num * div_mult[den];
}
// Temporal scaling the motion vector
static AOM_INLINE void tip_get_mv_projection(MV *output, MV ref,
int scale_factor) {
const int mv_row = ROUND_POWER_OF_TWO_SIGNED(ref.row * scale_factor, 14);
const int mv_col = ROUND_POWER_OF_TWO_SIGNED(ref.col * scale_factor, 14);
const int clamp_max = MV_UPP - 1;
const int clamp_min = MV_LOW + 1;
output->row = (int16_t)clamp(mv_row, clamp_min, clamp_max);
output->col = (int16_t)clamp(mv_col, clamp_min, clamp_max);
}
// Convert motion vector to fullpel and clamp the fullpel mv if it is outside
// of frame boundary
static AOM_INLINE FULLPEL_MV clamp_tip_fullmv(const AV1_COMMON *const cm,
const MV *mv, const int blk_row,
const int blk_col) {
const int y_width = cm->tip_ref.ref_frame_buffer[0]->buf.y_width;
const int y_height = cm->tip_ref.ref_frame_buffer[0]->buf.y_height;
FULLPEL_MV fullmv;
fullmv = get_fullmv_from_mv(mv);
if (fullmv.row + ((blk_row + 1) << TMVP_MI_SZ_LOG2) > y_height) {
fullmv.row = y_height - ((blk_row + 1) << TMVP_MI_SZ_LOG2);
} else if (fullmv.row + (blk_row << TMVP_MI_SZ_LOG2) < 0) {
fullmv.row = -(blk_row << TMVP_MI_SZ_LOG2);
}
if (fullmv.col + ((blk_col + 1) << TMVP_MI_SZ_LOG2) > y_width) {
fullmv.col = y_width - ((blk_col + 1) << TMVP_MI_SZ_LOG2);
} else if (fullmv.col + (blk_col << TMVP_MI_SZ_LOG2) < 0) {
fullmv.col = -(blk_col << TMVP_MI_SZ_LOG2);
}
return fullmv;
}
// SMVP candidate which is derived from TIP mode
static AOM_INLINE void clamp_tip_smvp_refmv(const AV1_COMMON *const cm, MV *mv,
const int blk_row,
const int blk_col) {
const int y_width = cm->tip_ref.ref_frame_buffer[0]->buf.y_width;
const int y_height = cm->tip_ref.ref_frame_buffer[0]->buf.y_height;
FULLPEL_MV fullmv;
fullmv = get_fullmv_from_mv(mv);
const int row_offset = fullmv.row + (blk_row << MI_SIZE_LOG2);
if (row_offset > y_height) {
fullmv.row = y_height - (blk_row << MI_SIZE_LOG2);
} else if (row_offset < 0) {
fullmv.row = -(blk_row << MI_SIZE_LOG2);
}
const int col_offset = fullmv.col + (blk_col << MI_SIZE_LOG2);
if (col_offset > y_width) {
fullmv.col = y_width - (blk_col << MI_SIZE_LOG2);
} else if (col_offset < 0) {
fullmv.col = -(blk_col << MI_SIZE_LOG2);
}
*mv = get_mv_from_fullmv(&fullmv);
}
// Clamp MV to UMV border based on its distance to left/right/top/bottom edge
static AOM_INLINE MV tip_clamp_mv_to_umv_border_sb(
InterPredParams *const inter_pred_params, const MV *src_mv, int bw, int bh,
#if CONFIG_OPTFLOW_REFINEMENT
int use_optflow_refinement,
#endif // CONFIG_OPTFLOW_REFINEMENT
int ss_x, int ss_y) {
// If the MV points so far into the UMV border that no visible pixels
// are used for reconstruction, the subpel part of the MV can be
// discarded and the MV limited to 16 pixels with equivalent results.
const int spel_left = (AOM_INTERP_EXTEND + bw) << SUBPEL_BITS;
const int spel_right = spel_left - SUBPEL_SHIFTS;
const int spel_top = (AOM_INTERP_EXTEND + bh) << SUBPEL_BITS;
const int spel_bottom = spel_top - SUBPEL_SHIFTS;
#if CONFIG_OPTFLOW_REFINEMENT
MV clamped_mv;
if (use_optflow_refinement) {
// optflow refinement always returns MVs with 1/16 precision so it is not
// necessary to shift the MV before clamping
// Here it should be:
// clamped_mv.row = (int16_t)ROUND_POWER_OF_TWO_SIGNED(
// src_mv->row * (1 << SUBPEL_BITS), MV_REFINE_PREC_BITS + ss_y);
// But currently SUBPEL_BITS == MV_REFINE_PREC_BITS
assert(SUBPEL_BITS == MV_REFINE_PREC_BITS);
if (ss_y || ss_x) {
clamped_mv.row = (int16_t)ROUND_POWER_OF_TWO_SIGNED(
src_mv->row * (1 << SUBPEL_BITS), MV_REFINE_PREC_BITS + ss_y);
clamped_mv.col = (int16_t)ROUND_POWER_OF_TWO_SIGNED(
src_mv->col * (1 << SUBPEL_BITS), MV_REFINE_PREC_BITS + ss_x);
} else {
clamped_mv = *src_mv;
}
} else {
clamped_mv.row = (int16_t)(src_mv->row * (1 << (1 - ss_y)));
clamped_mv.col = (int16_t)(src_mv->col * (1 << (1 - ss_x)));
}
#else
MV clamped_mv = { (int16_t)(src_mv->row * (1 << (1 - ss_y))),
(int16_t)(src_mv->col * (1 << (1 - ss_x))) };
#endif // CONFIG_OPTFLOW_REFINEMENT
assert(ss_x <= 1);
assert(ss_y <= 1);
const SubpelMvLimits mv_limits = {
inter_pred_params->dist_to_left_edge * (1 << (1 - ss_x)) - spel_left,
inter_pred_params->dist_to_right_edge * (1 << (1 - ss_x)) + spel_right,
inter_pred_params->dist_to_top_edge * (1 << (1 - ss_y)) - spel_top,
inter_pred_params->dist_to_bottom_edge * (1 << (1 - ss_y)) + spel_bottom
};
clamp_mv(&clamped_mv, &mv_limits);
return clamped_mv;
}
#ifdef __cplusplus
} // extern "C"
#endif
#endif // AOM_AV1_COMMON_TIP_H_