Implement on the fly padding in decoder

Calculate the amount of padding required for motion
compensation in the decoder and pad the reference
frame(s) accordingly.

Change-Id: I0819f13e0c9aa8d3b790d49f0fc08684a535b830
diff --git a/av1/common/reconinter.c b/av1/common/reconinter.c
index 63481eb..108eb1f 100644
--- a/av1/common/reconinter.c
+++ b/av1/common/reconinter.c
@@ -32,23 +32,26 @@
 
 // This function will determine whether or not to create a warped
 // prediction.
-static INLINE int allow_warp(const MB_MODE_INFO *const mbmi,
-                             const WarpTypesAllowed *const warp_types,
-                             const WarpedMotionParams *const gm_params,
-                             int build_for_obmc, int x_scale, int y_scale,
-                             WarpedMotionParams *final_warp_params) {
+int av1_allow_warp(const MB_MODE_INFO *const mbmi,
+                   const WarpTypesAllowed *const warp_types,
+                   const WarpedMotionParams *const gm_params,
+                   int build_for_obmc, int x_scale, int y_scale,
+                   WarpedMotionParams *final_warp_params) {
   if (x_scale != SCALE_SUBPEL_SHIFTS || y_scale != SCALE_SUBPEL_SHIFTS)
     return 0;
 
-  *final_warp_params = default_warp_params;
+  if (final_warp_params != NULL) *final_warp_params = default_warp_params;
 
   if (build_for_obmc) return 0;
 
   if (warp_types->local_warp_allowed && !mbmi->wm_params[0].invalid) {
-    memcpy(final_warp_params, &mbmi->wm_params[0], sizeof(*final_warp_params));
+    if (final_warp_params != NULL)
+      memcpy(final_warp_params, &mbmi->wm_params[0],
+             sizeof(*final_warp_params));
     return 1;
   } else if (warp_types->global_warp_allowed && !gm_params->invalid) {
-    memcpy(final_warp_params, gm_params, sizeof(*final_warp_params));
+    if (final_warp_params != NULL)
+      memcpy(final_warp_params, gm_params, sizeof(*final_warp_params));
     return 1;
   }
 
@@ -73,8 +76,8 @@
   WarpedMotionParams final_warp_params;
   const int do_warp =
       (w >= 8 && h >= 8 &&
-       allow_warp(mi, warp_types, &xd->global_motion[mi->ref_frame[ref]],
-                  build_for_obmc, xs, ys, &final_warp_params));
+       av1_allow_warp(mi, warp_types, &xd->global_motion[mi->ref_frame[ref]],
+                      build_for_obmc, xs, ys, &final_warp_params));
   if (do_warp && xd->cur_frame_force_integer_mv == 0) {
     const struct macroblockd_plane *const pd = &xd->plane[plane];
     const struct buf_2d *const pre_buf = &pd->pre[ref];
diff --git a/av1/common/reconinter.h b/av1/common/reconinter.h
index 2bae318..73d8dea 100644
--- a/av1/common/reconinter.h
+++ b/av1/common/reconinter.h
@@ -453,6 +453,12 @@
                                 int order_idx, int *fwd_offset, int *bck_offset,
                                 int *use_jnt_comp_avg, int is_compound);
 
+int av1_allow_warp(const MB_MODE_INFO *const mbmi,
+                   const WarpTypesAllowed *const warp_types,
+                   const WarpedMotionParams *const gm_params,
+                   int build_for_obmc, int x_scale, int y_scale,
+                   WarpedMotionParams *final_warp_params);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index 93b62c53..172ac7e 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -335,11 +335,135 @@
   aom_merge_corrupted_flag(&xd->corrupted, reader_corrupted_flag);
 }
 
+typedef struct PadBlock {
+  int x0;
+  int x1;
+  int y0;
+  int y1;
+} PadBlock;
+
+static void highbd_build_mc_border(const uint8_t *src8, uint8_t *dst8,
+                                   int stride, int x, int y, int b_w, int b_h,
+                                   int w, int h) {
+  // Get a pointer to the start of the real data for this row.
+  const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
+  uint16_t *dst = CONVERT_TO_SHORTPTR(dst8);
+  const uint16_t *ref_row = src - x - y * stride;
+
+  if (y >= h)
+    ref_row += (h - 1) * stride;
+  else if (y > 0)
+    ref_row += y * stride;
+
+  do {
+    int right = 0, copy;
+    int left = x < 0 ? -x : 0;
+
+    if (left > b_w) left = b_w;
+
+    if (x + b_w > w) right = x + b_w - w;
+
+    if (right > b_w) right = b_w;
+
+    copy = b_w - left - right;
+
+    if (left) aom_memset16(dst, ref_row[0], left);
+
+    if (copy) memcpy(dst + left, ref_row + x + left, copy * sizeof(uint16_t));
+
+    if (right) aom_memset16(dst + left + copy, ref_row[w - 1], right);
+
+    dst += stride;
+    ++y;
+
+    if (y > 0 && y < h) ref_row += stride;
+  } while (--b_h);
+}
+
+static void build_mc_border(const uint8_t *src, uint8_t *dst, int stride, int x,
+                            int y, int b_w, int b_h, int w, int h) {
+  // Get a pointer to the start of the real data for this row.
+  const uint8_t *ref_row = src - x - y * stride;
+
+  if (y >= h)
+    ref_row += (h - 1) * stride;
+  else if (y > 0)
+    ref_row += y * stride;
+
+  do {
+    int right = 0, copy;
+    int left = x < 0 ? -x : 0;
+
+    if (left > b_w) left = b_w;
+
+    if (x + b_w > w) right = x + b_w - w;
+
+    if (right > b_w) right = b_w;
+
+    copy = b_w - left - right;
+
+    if (left) memset(dst, ref_row[0], left);
+    if (copy) memcpy(dst + left, ref_row + x + left, copy);
+    if (right) memset(dst + left + copy, ref_row[w - 1], right);
+
+    dst += stride;
+    ++y;
+
+    if (y > 0 && y < h) ref_row += stride;
+  } while (--b_h);
+}
+
+static INLINE void extend_mc_borders(MACROBLOCKD *xd,
+                                     const struct scale_factors *const sf,
+                                     struct buf_2d *const pre_buf,
+                                     MV32 scaled_mv, PadBlock block,
+                                     int subpel_x_mv, int subpel_y_mv,
+                                     int do_warp) {
+  const int is_scaled = av1_is_scaled(sf);
+  // Get reference width and height.
+  int frame_width = pre_buf->width;
+  int frame_height = pre_buf->height;
+
+  // Do border extension if there is motion or
+  // width/height is not a multiple of 8 pixels.
+  if ((!do_warp) && (is_scaled || scaled_mv.col || scaled_mv.row ||
+                     (frame_width & 0x7) || (frame_height & 0x7))) {
+    if (subpel_x_mv || (sf->x_step_q4 != SUBPEL_SHIFTS)) {
+      block.x0 -= AOM_INTERP_EXTEND - 1;
+      block.x1 += AOM_INTERP_EXTEND;
+    }
+
+    if (subpel_y_mv || (sf->y_step_q4 != SUBPEL_SHIFTS)) {
+      block.y0 -= AOM_INTERP_EXTEND - 1;
+      block.y1 += AOM_INTERP_EXTEND;
+    }
+
+    // Skip border extension if block is inside the frame.
+    if (block.x0 < 0 || block.x1 > frame_width - 1 || block.y0 < 0 ||
+        block.y1 > frame_height - 1) {
+      // Get reference block pointer.
+      uint8_t *buf_ptr = pre_buf->buf0 + block.y0 * pre_buf->stride + block.x0;
+      int buf_stride = pre_buf->stride;
+
+      // Extend the border.
+      if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH)
+        highbd_build_mc_border(buf_ptr, buf_ptr, buf_stride, block.x0, block.y0,
+                               block.x1 - block.x0, block.y1 - block.y0,
+                               frame_width, frame_height);
+      else
+        build_mc_border(buf_ptr, buf_ptr, buf_stride, block.x0, block.y0,
+                        block.x1 - block.x0, block.y1 - block.y0, frame_width,
+                        frame_height);
+    }
+  }
+}
+
 static INLINE void dec_calc_subpel_params(
     MACROBLOCKD *xd, const struct scale_factors *const sf, const MV mv,
     int plane, const int pre_x, const int pre_y, int x, int y,
     struct buf_2d *const pre_buf, uint8_t **pre, SubpelParams *subpel_params,
-    int bw, int bh) {
+    PadBlock *block, int bw, int bh, int mi_x, int mi_y, MV32 *scaled_mv,
+    int *subpel_x_mv, int *subpel_y_mv) {
   struct macroblockd_plane *const pd = &xd->plane[plane];
   const int is_scaled = av1_is_scaled(sf);
   if (is_scaled) {
@@ -368,14 +492,54 @@
     subpel_params->subpel_y = pos_y & SCALE_SUBPEL_MASK;
     subpel_params->xs = sf->x_step_q4;
     subpel_params->ys = sf->y_step_q4;
+
+    // Get block position in the scaled reference frame.
+    MV temp_mv;
+    temp_mv = clamp_mv_to_umv_border_sb(xd, &mv, bw, bh, pd->subsampling_x,
+                                        pd->subsampling_y);
+    *scaled_mv = av1_scale_mv(&temp_mv, (mi_x + x), (mi_y + y), sf);
+    scaled_mv->row += SCALE_EXTRA_OFF;
+    scaled_mv->col += SCALE_EXTRA_OFF;
+
+    // Get reference block top left coordinate.
+    block->x0 = pos_x >> SCALE_SUBPEL_BITS;
+    block->y0 = pos_y >> SCALE_SUBPEL_BITS;
+
+    // Get reference block bottom right coordinate.
+    block->x1 =
+        ((pos_x + (bw - 1) * subpel_params->xs) >> SCALE_SUBPEL_BITS) + 1;
+    block->y1 =
+        ((pos_y + (bh - 1) * subpel_params->ys) >> SCALE_SUBPEL_BITS) + 1;
+
+    *subpel_x_mv = scaled_mv->col & SCALE_SUBPEL_MASK;
+    *subpel_y_mv = scaled_mv->row & SCALE_SUBPEL_MASK;
   } else {
+    // Get block position in current frame.
+    int pos_x = (pre_x + x) << SUBPEL_BITS;
+    int pos_y = (pre_y + y) << SUBPEL_BITS;
+
     const MV mv_q4 = clamp_mv_to_umv_border_sb(
         xd, &mv, bw, bh, pd->subsampling_x, pd->subsampling_y);
     subpel_params->xs = subpel_params->ys = SCALE_SUBPEL_SHIFTS;
+    scaled_mv->row = mv_q4.row;
+    scaled_mv->col = mv_q4.col;
     subpel_params->subpel_x = (mv_q4.col & SUBPEL_MASK) << SCALE_EXTRA_BITS;
     subpel_params->subpel_y = (mv_q4.row & SUBPEL_MASK) << SCALE_EXTRA_BITS;
     *pre = pre_buf->buf + (y + (mv_q4.row >> SUBPEL_BITS)) * pre_buf->stride +
            (x + (mv_q4.col >> SUBPEL_BITS));
+
+    // Get reference block top left coordinate.
+    pos_x += scaled_mv->col;
+    pos_y += scaled_mv->row;
+    block->x0 = pos_x >> SUBPEL_BITS;
+    block->y0 = pos_y >> SUBPEL_BITS;
+
+    // Get reference block bottom right coordinate.
+    block->x1 = (pos_x >> SUBPEL_BITS) + (bw - 1) + 1;
+    block->y1 = (pos_y >> SUBPEL_BITS) + (bh - 1) + 1;
+
+    *subpel_x_mv = scaled_mv->col & SUBPEL_MASK;
+    *subpel_y_mv = scaled_mv->row & SUBPEL_MASK;
   }
 }
 
@@ -473,12 +637,19 @@
 
         uint8_t *pre;
         SubpelParams subpel_params;
+        MV32 scaled_mv;
+        PadBlock block;
+        int subpel_x_mv, subpel_y_mv;
         WarpTypesAllowed warp_types;
         warp_types.global_warp_allowed = is_global[ref];
         warp_types.local_warp_allowed = this_mbmi->motion_mode == WARPED_CAUSAL;
 
         dec_calc_subpel_params(xd, sf, mv, plane, pre_x, pre_y, x, y, pre_buf,
-                               &pre, &subpel_params, bw, bh);
+                               &pre, &subpel_params, &block, bw, bh, mi_x, mi_y,
+                               &scaled_mv, &subpel_x_mv, &subpel_y_mv);
+
+        extend_mc_borders(xd, sf, pre_buf, scaled_mv, block, subpel_x_mv,
+                          subpel_y_mv, 0);
 
         conv_params.ref = ref;
         conv_params.do_average = ref;
@@ -516,8 +687,27 @@
       struct buf_2d *const pre_buf = is_intrabc ? dst_buf : &pd->pre[ref];
       const MV mv = mi->mv[ref].as_mv;
 
+      MV32 scaled_mv;
+      PadBlock block;
+      int subpel_x_mv, subpel_y_mv;
+
       dec_calc_subpel_params(xd, sf, mv, plane, pre_x, pre_y, 0, 0, pre_buf,
-                             &pre[ref], &subpel_params[ref], bw, bh);
+                             &pre[ref], &subpel_params[ref], &block, bw, bh,
+                             mi_x, mi_y, &scaled_mv, &subpel_x_mv,
+                             &subpel_y_mv);
+
+      WarpTypesAllowed warp_types;
+      warp_types.global_warp_allowed = is_global[ref];
+      warp_types.local_warp_allowed = mi->motion_mode == WARPED_CAUSAL;
+      int do_warp = (bw >= 8 && bh >= 8 &&
+                     av1_allow_warp(mi, &warp_types,
+                                    &xd->global_motion[mi->ref_frame[ref]],
+                                    build_for_obmc, subpel_params[ref].xs,
+                                    subpel_params[ref].ys, NULL));
+      do_warp = (do_warp && xd->cur_frame_force_integer_mv == 0);
+
+      extend_mc_borders(xd, sf, pre_buf, scaled_mv, block, subpel_x_mv,
+                        subpel_y_mv, do_warp);
     }
 
     ConvolveParams conv_params = get_conv_params_no_round(
diff --git a/av1/decoder/decoder.c b/av1/decoder/decoder.c
index 33bfb8c..d676bce 100644
--- a/av1/decoder/decoder.c
+++ b/av1/decoder/decoder.c
@@ -290,15 +290,6 @@
 
     unlock_buffer_pool(pool);
     cm->frame_to_show = get_frame_new_buffer(cm);
-
-    // For now, we only extend the frame borders when the whole frame is
-    // decoded. Later, if needed, extend the border for the decoded tile on the
-    // frame border.
-    if (pbi->dec_tile_row == -1 && pbi->dec_tile_col == -1)
-      // TODO(debargha): Fix encoder side mv range, so that we can use the
-      // inner border extension. As of now use the larger extension.
-      // aom_extend_frame_inner_borders(cm->frame_to_show, av1_num_planes(cm));
-      aom_extend_frame_borders(cm->frame_to_show, av1_num_planes(cm));
   }
 
   pbi->hold_ref_buf = 0;