convolve.c 35.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/*
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
 *
 * This source code is subject to the terms of the BSD 2 Clause License and
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
 * was not distributed with this source code in the LICENSE file, you can
 * obtain it at www.aomedia.org/license/software. 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 www.aomedia.org/license/patent.
 */

12
#include <assert.h>
Angie Chiang's avatar
Angie Chiang committed
13
#include <string.h>
14

15
#include "./aom_dsp_rtcd.h"
Yaowu Xu's avatar
Yaowu Xu committed
16
#include "./av1_rtcd.h"
17
#include "av1/common/convolve.h"
18
#include "av1/common/filter.h"
Angie Chiang's avatar
Angie Chiang committed
19
#include "av1/common/onyxc_int.h"
Yaowu Xu's avatar
Yaowu Xu committed
20
#include "aom_dsp/aom_dsp_common.h"
21
#include "aom_ports/mem.h"
22

23 24
#define MAX_BLOCK_WIDTH (MAX_SB_SIZE)
#define MAX_BLOCK_HEIGHT (MAX_SB_SIZE)
25 26
#define MAX_STEP (32)

Yaowu Xu's avatar
Yaowu Xu committed
27 28 29
void av1_convolve_horiz_c(const uint8_t *src, int src_stride, uint8_t *dst,
                          int dst_stride, int w, int h,
                          const InterpFilterParams filter_params,
30 31
                          const int subpel_x_q4, int x_step_q4,
                          ConvolveParams *conv_params) {
32
  int x, y;
33
  int filter_size = filter_params.taps;
Angie Chiang's avatar
Angie Chiang committed
34
  assert(conv_params->round == CONVOLVE_OPT_ROUND);
35 36 37 38 39
  src -= filter_size / 2 - 1;
  for (y = 0; y < h; ++y) {
    int x_q4 = subpel_x_q4;
    for (x = 0; x < w; ++x) {
      const uint8_t *const src_x = &src[x_q4 >> SUBPEL_BITS];
Yaowu Xu's avatar
Yaowu Xu committed
40
      const int16_t *x_filter = av1_get_interp_filter_subpel_kernel(
clang-format's avatar
clang-format committed
41
          filter_params, x_q4 & SUBPEL_MASK);
42 43
      int k, sum = 0;
      for (k = 0; k < filter_size; ++k) sum += src_x[k] * x_filter[k];
44

Angie Chiang's avatar
Angie Chiang committed
45 46 47 48 49
      sum = clip_pixel(ROUND_POWER_OF_TWO(sum, FILTER_BITS));
      if (conv_params->ref)
        dst[x] = ROUND_POWER_OF_TWO(dst[x] + sum, 1);
      else
        dst[x] = sum;
50

51 52 53 54 55 56 57
      x_q4 += x_step_q4;
    }
    src += src_stride;
    dst += dst_stride;
  }
}

Yaowu Xu's avatar
Yaowu Xu committed
58 59 60
void av1_convolve_vert_c(const uint8_t *src, int src_stride, uint8_t *dst,
                         int dst_stride, int w, int h,
                         const InterpFilterParams filter_params,
61 62
                         const int subpel_y_q4, int y_step_q4,
                         ConvolveParams *conv_params) {
63
  int x, y;
64
  int filter_size = filter_params.taps;
Angie Chiang's avatar
Angie Chiang committed
65
  assert(conv_params->round == CONVOLVE_OPT_ROUND);
66 67 68 69 70
  src -= src_stride * (filter_size / 2 - 1);
  for (x = 0; x < w; ++x) {
    int y_q4 = subpel_y_q4;
    for (y = 0; y < h; ++y) {
      const uint8_t *const src_y = &src[(y_q4 >> SUBPEL_BITS) * src_stride];
Yaowu Xu's avatar
Yaowu Xu committed
71
      const int16_t *y_filter = av1_get_interp_filter_subpel_kernel(
clang-format's avatar
clang-format committed
72
          filter_params, y_q4 & SUBPEL_MASK);
73 74 75
      int k, sum = 0;
      for (k = 0; k < filter_size; ++k)
        sum += src_y[k * src_stride] * y_filter[k];
76

Angie Chiang's avatar
Angie Chiang committed
77 78 79 80 81
      sum = clip_pixel(ROUND_POWER_OF_TWO(sum, FILTER_BITS));
      if (conv_params->ref)
        dst[y * dst_stride] = ROUND_POWER_OF_TWO(dst[y * dst_stride] + sum, 1);
      else
        dst[y * dst_stride] = sum;
82

83 84 85 86 87 88 89
      y_q4 += y_step_q4;
    }
    ++src;
    ++dst;
  }
}

Angie Chiang's avatar
Angie Chiang committed
90
static void convolve_copy(const uint8_t *src, int src_stride, uint8_t *dst,
91 92
                          int dst_stride, int w, int h,
                          ConvolveParams *conv_params) {
Angie Chiang's avatar
Angie Chiang committed
93
  assert(conv_params->round == CONVOLVE_OPT_ROUND);
94
  if (conv_params->ref == 0) {
Angie Chiang's avatar
Angie Chiang committed
95
    int r;
Angie Chiang's avatar
Angie Chiang committed
96
    for (r = 0; r < h; ++r) {
Angie Chiang's avatar
Angie Chiang committed
97
      memcpy(dst, src, w);
Angie Chiang's avatar
Angie Chiang committed
98 99 100 101 102 103
      src += src_stride;
      dst += dst_stride;
    }
  } else {
    int r, c;
    for (r = 0; r < h; ++r) {
104
      for (c = 0; c < w; ++c) {
Angie Chiang's avatar
Angie Chiang committed
105
        dst[c] = clip_pixel(ROUND_POWER_OF_TWO(dst[c] + src[c], 1));
106
      }
Angie Chiang's avatar
Angie Chiang committed
107 108 109 110 111 112
      src += src_stride;
      dst += dst_stride;
    }
  }
}

113 114 115
void av1_convolve_horiz_facade(const uint8_t *src, int src_stride, uint8_t *dst,
                               int dst_stride, int w, int h,
                               const InterpFilterParams filter_params,
116 117
                               const int subpel_x_q4, int x_step_q4,
                               ConvolveParams *conv_params) {
Angie Chiang's avatar
Angie Chiang committed
118 119
  assert(conv_params->round == CONVOLVE_OPT_ROUND);
  if (filter_params.taps == SUBPEL_TAPS) {
120 121
    const int16_t *filter_x =
        av1_get_interp_filter_subpel_kernel(filter_params, subpel_x_q4);
122
    if (conv_params->ref == 0)
123 124 125 126 127 128
      aom_convolve8_horiz(src, src_stride, dst, dst_stride, filter_x, x_step_q4,
                          NULL, -1, w, h);
    else
      aom_convolve8_avg_horiz(src, src_stride, dst, dst_stride, filter_x,
                              x_step_q4, NULL, -1, w, h);
  } else {
Angie Chiang's avatar
Angie Chiang committed
129 130
    av1_convolve_horiz(src, src_stride, dst, dst_stride, w, h, filter_params,
                       subpel_x_q4, x_step_q4, conv_params);
131 132 133
  }
}

134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
void av1_convolve_horiz_facade_c(const uint8_t *src, int src_stride,
                                 uint8_t *dst, int dst_stride, int w, int h,
                                 const InterpFilterParams filter_params,
                                 const int subpel_x_q4, int x_step_q4,
                                 ConvolveParams *conv_params) {
  assert(conv_params->round == CONVOLVE_OPT_ROUND);
  if (filter_params.taps == SUBPEL_TAPS) {
    const int16_t *filter_x =
        av1_get_interp_filter_subpel_kernel(filter_params, subpel_x_q4);
    if (conv_params->ref == 0)
      aom_convolve8_horiz_c(src, src_stride, dst, dst_stride, filter_x,
                            x_step_q4, NULL, -1, w, h);
    else
      aom_convolve8_avg_horiz_c(src, src_stride, dst, dst_stride, filter_x,
                                x_step_q4, NULL, -1, w, h);
  } else {
    av1_convolve_horiz_c(src, src_stride, dst, dst_stride, w, h, filter_params,
                         subpel_x_q4, x_step_q4, conv_params);
  }
}

155 156 157
void av1_convolve_vert_facade(const uint8_t *src, int src_stride, uint8_t *dst,
                              int dst_stride, int w, int h,
                              const InterpFilterParams filter_params,
158 159
                              const int subpel_y_q4, int y_step_q4,
                              ConvolveParams *conv_params) {
Angie Chiang's avatar
Angie Chiang committed
160 161
  assert(conv_params->round == CONVOLVE_OPT_ROUND);
  if (filter_params.taps == SUBPEL_TAPS) {
162 163
    const int16_t *filter_y =
        av1_get_interp_filter_subpel_kernel(filter_params, subpel_y_q4);
164
    if (conv_params->ref == 0) {
165 166 167 168 169 170 171
      aom_convolve8_vert(src, src_stride, dst, dst_stride, NULL, -1, filter_y,
                         y_step_q4, w, h);
    } else {
      aom_convolve8_avg_vert(src, src_stride, dst, dst_stride, NULL, -1,
                             filter_y, y_step_q4, w, h);
    }
  } else {
Angie Chiang's avatar
Angie Chiang committed
172 173
    av1_convolve_vert(src, src_stride, dst, dst_stride, w, h, filter_params,
                      subpel_y_q4, y_step_q4, conv_params);
174 175 176
  }
}

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
void av1_convolve_vert_facade_c(const uint8_t *src, int src_stride,
                                uint8_t *dst, int dst_stride, int w, int h,
                                const InterpFilterParams filter_params,
                                const int subpel_y_q4, int y_step_q4,
                                ConvolveParams *conv_params) {
  assert(conv_params->round == CONVOLVE_OPT_ROUND);
  if (filter_params.taps == SUBPEL_TAPS) {
    const int16_t *filter_y =
        av1_get_interp_filter_subpel_kernel(filter_params, subpel_y_q4);
    if (conv_params->ref == 0) {
      aom_convolve8_vert_c(src, src_stride, dst, dst_stride, NULL, -1, filter_y,
                           y_step_q4, w, h);
    } else {
      aom_convolve8_avg_vert_c(src, src_stride, dst, dst_stride, NULL, -1,
                               filter_y, y_step_q4, w, h);
    }
  } else {
    av1_convolve_vert_c(src, src_stride, dst, dst_stride, w, h, filter_params,
                        subpel_y_q4, y_step_q4, conv_params);
  }
}

Angie Chiang's avatar
Angie Chiang committed
199
#if CONFIG_CONVOLVE_ROUND
Angie Chiang's avatar
Angie Chiang committed
200
void av1_convolve_rounding(const int32_t *src, int src_stride, uint8_t *dst,
Angie Chiang's avatar
Angie Chiang committed
201
                           int dst_stride, int w, int h, int bits) {
Angie Chiang's avatar
Angie Chiang committed
202 203 204 205
  int r, c;
  for (r = 0; r < h; ++r) {
    for (c = 0; c < w; ++c) {
      dst[r * dst_stride + c] =
Angie Chiang's avatar
Angie Chiang committed
206
          clip_pixel(ROUND_POWER_OF_TWO_SIGNED(src[r * src_stride + c], bits));
Angie Chiang's avatar
Angie Chiang committed
207 208 209
    }
  }
}
Angie Chiang's avatar
Angie Chiang committed
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232

void av1_convolve_2d(const uint8_t *src, int src_stride, CONV_BUF_TYPE *dst,
                     int dst_stride, int w, int h,
                     InterpFilterParams *filter_params_x,
                     InterpFilterParams *filter_params_y, const int subpel_x_q4,
                     const int subpel_y_q4, ConvolveParams *conv_params) {
  int x, y, k;
  CONV_BUF_TYPE im_block[(MAX_SB_SIZE + MAX_FILTER_TAP - 1) * MAX_SB_SIZE];
  int im_h = h + filter_params_y->taps - 1;
  int im_stride = w;
  const int fo_vert = filter_params_y->taps / 2 - 1;
  const int fo_horiz = filter_params_x->taps / 2 - 1;
  (void)conv_params;
  // horizontal filter
  const uint8_t *src_horiz = src - fo_vert * src_stride;
  const int16_t *x_filter = av1_get_interp_filter_subpel_kernel(
      *filter_params_x, subpel_x_q4 & SUBPEL_MASK);
  for (y = 0; y < im_h; ++y) {
    for (x = 0; x < w; ++x) {
      CONV_BUF_TYPE sum = 0;
      for (k = 0; k < filter_params_x->taps; ++k) {
        sum += x_filter[k] * src_horiz[y * src_stride + x - fo_horiz + k];
      }
Angie Chiang's avatar
Angie Chiang committed
233 234 235 236
#if CONFIG_COMPOUND_ROUND
      im_block[y * im_stride + x] =
          clip_pixel(ROUND_POWER_OF_TWO_SIGNED(sum, conv_params->round_0));
#else
Angie Chiang's avatar
Angie Chiang committed
237 238
      im_block[y * im_stride + x] =
          ROUND_POWER_OF_TWO_SIGNED(sum, conv_params->round_0);
Angie Chiang's avatar
Angie Chiang committed
239
#endif
Angie Chiang's avatar
Angie Chiang committed
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
    }
  }

  // vertical filter
  CONV_BUF_TYPE *src_vert = im_block + fo_vert * im_stride;
  const int16_t *y_filter = av1_get_interp_filter_subpel_kernel(
      *filter_params_y, subpel_y_q4 & SUBPEL_MASK);
  for (y = 0; y < h; ++y) {
    for (x = 0; x < w; ++x) {
      CONV_BUF_TYPE sum = 0;
      for (k = 0; k < filter_params_y->taps; ++k) {
        sum += y_filter[k] * src_vert[(y - fo_vert + k) * im_stride + x];
      }
      dst[y * dst_stride + x] +=
          ROUND_POWER_OF_TWO_SIGNED(sum, conv_params->round_1);
    }
  }
}

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
static INLINE void transpose_uint8(uint8_t *dst, int dst_stride,
                                   const uint8_t *src, int src_stride, int w,
                                   int h) {
  int r, c;
  for (r = 0; r < h; ++r)
    for (c = 0; c < w; ++c)
      dst[c * (dst_stride) + r] = src[r * (src_stride) + c];
}

static INLINE void transpose_int32(int32_t *dst, int dst_stride,
                                   const int32_t *src, int src_stride, int w,
                                   int h) {
  int r, c;
  for (r = 0; r < h; ++r)
    for (c = 0; c < w; ++c)
      dst[c * (dst_stride) + r] = src[r * (src_stride) + c];
}

Angie Chiang's avatar
Angie Chiang committed
277 278 279 280 281 282 283 284 285 286
void av1_convolve_2d_facade(const uint8_t *src, int src_stride, uint8_t *dst,
                            int dst_stride, int w, int h,
                            const InterpFilter *interp_filter,
                            const int subpel_x_q4, int x_step_q4,
                            const int subpel_y_q4, int y_step_q4,
                            ConvolveParams *conv_params) {
  (void)x_step_q4;
  (void)y_step_q4;
  (void)dst;
  (void)dst_stride;
287
#if CONFIG_DUAL_FILTER
Angie Chiang's avatar
Angie Chiang committed
288 289 290 291
  InterpFilterParams filter_params_x =
      av1_get_interp_filter_params(interp_filter[1 + 2 * conv_params->ref]);
  InterpFilterParams filter_params_y =
      av1_get_interp_filter_params(interp_filter[0 + 2 * conv_params->ref]);
292

293
#if USE_EXTRA_FILTER
294 295 296 297 298 299
  if (filter_params_x.interp_filter == MULTITAP_SHARP &&
      filter_params_y.interp_filter == MULTITAP_SHARP) {
    // Avoid two directions both using 12-tap filter.
    // This will reduce hardware implementation cost.
    filter_params_y = av1_get_interp_filter_params(EIGHTTAP_SHARP);
  }
300
#endif  // USE_EXTRA_FILTER
301 302 303 304 305
#else
  InterpFilterParams filter_params_x =
      av1_get_interp_filter_params(*interp_filter);
  InterpFilterParams filter_params_y =
      av1_get_interp_filter_params(*interp_filter);
306
#endif
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333

  if (filter_params_y.taps < filter_params_x.taps) {
    uint8_t tr_src[(MAX_SB_SIZE + MAX_FILTER_TAP - 1) *
                   (MAX_SB_SIZE + MAX_FILTER_TAP - 1)];
    int tr_src_stride = MAX_SB_SIZE + MAX_FILTER_TAP - 1;
    CONV_BUF_TYPE tr_dst[MAX_SB_SIZE * MAX_SB_SIZE];
    int tr_dst_stride = MAX_SB_SIZE;
    int fo_vert = filter_params_y.taps / 2 - 1;
    int fo_horiz = filter_params_x.taps / 2 - 1;

    transpose_uint8(tr_src, tr_src_stride,
                    src - fo_vert * src_stride - fo_horiz, src_stride,
                    w + filter_params_x.taps - 1, h + filter_params_y.taps - 1);
    transpose_int32(tr_dst, tr_dst_stride, conv_params->dst,
                    conv_params->dst_stride, w, h);

    // horizontal and vertical parameters are swapped because of the transpose
    av1_convolve_2d(tr_src + fo_horiz * tr_src_stride + fo_vert, tr_src_stride,
                    tr_dst, tr_dst_stride, h, w, &filter_params_y,
                    &filter_params_x, subpel_y_q4, subpel_x_q4, conv_params);
    transpose_int32(conv_params->dst, conv_params->dst_stride, tr_dst,
                    tr_dst_stride, h, w);
  } else {
    av1_convolve_2d(src, src_stride, conv_params->dst, conv_params->dst_stride,
                    w, h, &filter_params_x, &filter_params_y, subpel_x_q4,
                    subpel_y_q4, conv_params);
  }
Angie Chiang's avatar
Angie Chiang committed
334 335
}

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
#if CONFIG_HIGHBITDEPTH
static INLINE void transpose_uint16(uint16_t *dst, int dst_stride,
                                    const uint16_t *src, int src_stride, int w,
                                    int h) {
  int r, c;
  for (r = 0; r < h; ++r)
    for (c = 0; c < w; ++c) dst[c * dst_stride + r] = src[r * src_stride + c];
}

void av1_highbd_convolve_rounding(const int32_t *src, int src_stride,
                                  uint8_t *dst8, int dst_stride, int w, int h,
                                  int bits, int bd) {
  uint16_t *dst = CONVERT_TO_SHORTPTR(dst8);
  int r, c;
  for (r = 0; r < h; ++r) {
    for (c = 0; c < w; ++c) {
      dst[r * dst_stride + c] = clip_pixel_highbd(
          ROUND_POWER_OF_TWO_SIGNED(src[r * src_stride + c], bits), bd);
    }
  }
}

void av1_highbd_convolve_2d(const uint16_t *src, int src_stride,
                            CONV_BUF_TYPE *dst, int dst_stride, int w, int h,
                            InterpFilterParams *filter_params_x,
                            InterpFilterParams *filter_params_y,
                            const int subpel_x_q4, const int subpel_y_q4,
                            ConvolveParams *conv_params, int bd) {
  int x, y, k;
  CONV_BUF_TYPE im_block[(MAX_SB_SIZE + MAX_FILTER_TAP - 1) * MAX_SB_SIZE];
  int im_h = h + filter_params_y->taps - 1;
  int im_stride = w;
  const int fo_vert = filter_params_y->taps / 2 - 1;
  const int fo_horiz = filter_params_x->taps / 2 - 1;
  (void)conv_params;
  // horizontal filter
  const uint16_t *src_horiz = src - fo_vert * src_stride;
  const int16_t *x_filter = av1_get_interp_filter_subpel_kernel(
      *filter_params_x, subpel_x_q4 & SUBPEL_MASK);
  for (y = 0; y < im_h; ++y) {
    for (x = 0; x < w; ++x) {
      CONV_BUF_TYPE sum = 0;
      for (k = 0; k < filter_params_x->taps; ++k) {
        sum += x_filter[k] * src_horiz[y * src_stride + x - fo_horiz + k];
      }
#if CONFIG_COMPOUND_ROUND
      im_block[y * im_stride + x] = clip_pixel_highbd(
          ROUND_POWER_OF_TWO_SIGNED(sum, conv_params->round_0), bd);
#else
      (void)bd;
      im_block[y * im_stride + x] =
          ROUND_POWER_OF_TWO_SIGNED(sum, conv_params->round_0);
#endif
    }
  }

  // vertical filter
  CONV_BUF_TYPE *src_vert = im_block + fo_vert * im_stride;
  const int16_t *y_filter = av1_get_interp_filter_subpel_kernel(
      *filter_params_y, subpel_y_q4 & SUBPEL_MASK);
  for (y = 0; y < h; ++y) {
    for (x = 0; x < w; ++x) {
      CONV_BUF_TYPE sum = 0;
      for (k = 0; k < filter_params_y->taps; ++k) {
        sum += y_filter[k] * src_vert[(y - fo_vert + k) * im_stride + x];
      }
      dst[y * dst_stride + x] +=
          ROUND_POWER_OF_TWO_SIGNED(sum, conv_params->round_1);
    }
  }
}

void av1_highbd_convolve_2d_facade(const uint8_t *src8, int src_stride,
                                   uint8_t *dst, int dst_stride, int w, int h,
                                   const InterpFilter *interp_filter,
                                   const int subpel_x_q4, int x_step_q4,
                                   const int subpel_y_q4, int y_step_q4,
                                   ConvolveParams *conv_params, int bd) {
  (void)x_step_q4;
  (void)y_step_q4;
  (void)dst;
  (void)dst_stride;
#if CONFIG_DUAL_FILTER
  InterpFilterParams filter_params_x =
      av1_get_interp_filter_params(interp_filter[1 + 2 * conv_params->ref]);
  InterpFilterParams filter_params_y =
      av1_get_interp_filter_params(interp_filter[0 + 2 * conv_params->ref]);

424
#if USE_EXTRA_FILTER
425 426 427 428 429 430
  if (filter_params_x.interp_filter == MULTITAP_SHARP &&
      filter_params_y.interp_filter == MULTITAP_SHARP) {
    // Avoid two directions both using 12-tap filter.
    // This will reduce hardware implementation cost.
    filter_params_y = av1_get_interp_filter_params(EIGHTTAP_SHARP);
  }
431
#endif
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
#else
  InterpFilterParams filter_params_x =
      av1_get_interp_filter_params(*interp_filter);
  InterpFilterParams filter_params_y =
      av1_get_interp_filter_params(*interp_filter);
#endif
  const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
  if (filter_params_y.taps < filter_params_x.taps) {
    uint16_t tr_src[(MAX_SB_SIZE + MAX_FILTER_TAP - 1) *
                    (MAX_SB_SIZE + MAX_FILTER_TAP - 1)];
    int tr_src_stride = MAX_SB_SIZE + MAX_FILTER_TAP - 1;
    CONV_BUF_TYPE tr_dst[MAX_SB_SIZE * MAX_SB_SIZE];
    int tr_dst_stride = MAX_SB_SIZE;
    int fo_vert = filter_params_y.taps / 2 - 1;
    int fo_horiz = filter_params_x.taps / 2 - 1;

    transpose_uint16(
        tr_src, tr_src_stride, src - fo_vert * src_stride - fo_horiz,
        src_stride, w + filter_params_x.taps - 1, h + filter_params_y.taps - 1);
    transpose_int32(tr_dst, tr_dst_stride, conv_params->dst,
                    conv_params->dst_stride, w, h);

    // horizontal and vertical parameters are swapped because of the transpose
    av1_highbd_convolve_2d(tr_src + fo_horiz * tr_src_stride + fo_vert,
                           tr_src_stride, tr_dst, tr_dst_stride, h, w,
                           &filter_params_y, &filter_params_x, subpel_y_q4,
                           subpel_x_q4, conv_params, bd);
    transpose_int32(conv_params->dst, conv_params->dst_stride, tr_dst,
                    tr_dst_stride, h, w);
  } else {
    av1_highbd_convolve_2d(src, src_stride, conv_params->dst,
                           conv_params->dst_stride, w, h, &filter_params_x,
                           &filter_params_y, subpel_x_q4, subpel_y_q4,
                           conv_params, bd);
  }
}
#endif  // CONFIG_HIGHBITDEPTH

Angie Chiang's avatar
Angie Chiang committed
470
#endif  // CONFIG_CONVOLVE_ROUND
Angie Chiang's avatar
Angie Chiang committed
471

472 473 474 475 476 477 478 479
typedef void (*ConvolveFunc)(const uint8_t *src, int src_stride, uint8_t *dst,
                             int dst_stride, int w, int h,
                             const InterpFilterParams filter_params,
                             const int subpel_q4, int step_q4,
                             ConvolveParams *conv_params);

static void convolve_helper(const uint8_t *src, int src_stride, uint8_t *dst,
                            int dst_stride, int w, int h,
480
#if CONFIG_DUAL_FILTER
481
                            const InterpFilter *interp_filter,
482
#else
483
                            const InterpFilter interp_filter,
484
#endif
485 486 487 488 489
                            const int subpel_x_q4, int x_step_q4,
                            const int subpel_y_q4, int y_step_q4,
                            ConvolveParams *conv_params,
                            ConvolveFunc convolve_horiz,
                            ConvolveFunc convolve_vert) {
Angie Chiang's avatar
Angie Chiang committed
490 491
  int ignore_horiz = x_step_q4 == 16 && subpel_x_q4 == 0;
  int ignore_vert = y_step_q4 == 16 && subpel_y_q4 == 0;
Angie Chiang's avatar
Angie Chiang committed
492 493 494 495 496 497 498 499 500 501
#if CONFIG_DUAL_FILTER
  InterpFilterParams filter_params_x =
      av1_get_interp_filter_params(interp_filter[1 + 2 * conv_params->ref]);
  InterpFilterParams filter_params_y =
      av1_get_interp_filter_params(interp_filter[0 + 2 * conv_params->ref]);
  InterpFilterParams filter_params;
#else
  InterpFilterParams filter_params =
      av1_get_interp_filter_params(interp_filter);
#endif
Angie Chiang's avatar
Angie Chiang committed
502
  assert(conv_params->round == CONVOLVE_OPT_ROUND);
503 504 505 506 507 508

  assert(w <= MAX_BLOCK_WIDTH);
  assert(h <= MAX_BLOCK_HEIGHT);
  assert(y_step_q4 <= MAX_STEP);
  assert(x_step_q4 <= MAX_STEP);

Angie Chiang's avatar
Angie Chiang committed
509
  if (ignore_horiz && ignore_vert) {
510
    convolve_copy(src, src_stride, dst, dst_stride, w, h, conv_params);
Angie Chiang's avatar
Angie Chiang committed
511
  } else if (ignore_vert) {
512
#if CONFIG_DUAL_FILTER
Angie Chiang's avatar
Angie Chiang committed
513
    filter_params = filter_params_x;
514 515
#endif
    assert(filter_params.taps <= MAX_FILTER_TAP);
516 517
    convolve_horiz(src, src_stride, dst, dst_stride, w, h, filter_params,
                   subpel_x_q4, x_step_q4, conv_params);
Angie Chiang's avatar
Angie Chiang committed
518
  } else if (ignore_horiz) {
519
#if CONFIG_DUAL_FILTER
Angie Chiang's avatar
Angie Chiang committed
520
    filter_params = filter_params_y;
521 522
#endif
    assert(filter_params.taps <= MAX_FILTER_TAP);
523 524
    convolve_vert(src, src_stride, dst, dst_stride, w, h, filter_params,
                  subpel_y_q4, y_step_q4, conv_params);
Angie Chiang's avatar
Angie Chiang committed
525
  } else {
526 527 528 529 530 531
    // temp's size is set to a 256 aligned value to facilitate SIMD
    // implementation. The value is greater than (maximum possible intermediate
    // height or width) * MAX_SB_SIZE
    DECLARE_ALIGNED(16, uint8_t,
                    temp[((MAX_SB_SIZE * 2 + 16) + 16) * MAX_SB_SIZE]);
    int max_intermediate_size = ((MAX_SB_SIZE * 2 + 16) + 16);
532
    int filter_size;
533
#if CONFIG_DUAL_FILTER && USE_EXTRA_FILTER
534 535
    if (interp_filter[0 + 2 * conv_params->ref] == MULTITAP_SHARP &&
        interp_filter[1 + 2 * conv_params->ref] == MULTITAP_SHARP) {
536 537 538 539
      // Avoid two directions both using 12-tap filter.
      // This will reduce hardware implementation cost.
      filter_params_y = av1_get_interp_filter_params(EIGHTTAP_SHARP);
    }
540

541 542 543 544
    // we do filter with fewer taps first to reduce hardware implementation
    // complexity
    if (filter_params_y.taps < filter_params_x.taps) {
      int intermediate_width;
545
      int temp_stride = max_intermediate_size;
546 547
      ConvolveParams temp_conv_params;
      temp_conv_params.ref = 0;
548
      temp_conv_params.round = CONVOLVE_OPT_ROUND;
549 550 551 552
      filter_params = filter_params_y;
      filter_size = filter_params_x.taps;
      intermediate_width =
          (((w - 1) * x_step_q4 + subpel_x_q4) >> SUBPEL_BITS) + filter_size;
553
      assert(intermediate_width <= max_intermediate_size);
Angie Chiang's avatar
Angie Chiang committed
554

555
      assert(filter_params.taps <= MAX_FILTER_TAP);
556

557 558 559
      convolve_vert(src - (filter_size / 2 - 1), src_stride, temp, temp_stride,
                    intermediate_width, h, filter_params, subpel_y_q4,
                    y_step_q4, &temp_conv_params);
560

561 562
      filter_params = filter_params_x;
      assert(filter_params.taps <= MAX_FILTER_TAP);
563 564
      convolve_horiz(temp + (filter_size / 2 - 1), temp_stride, dst, dst_stride,
                     w, h, filter_params, subpel_x_q4, x_step_q4, conv_params);
565
    } else
566
#endif  // CONFIG_DUAL_FILTER && USE_EXTRA_FILTER
567 568
    {
      int intermediate_height;
569
      int temp_stride = MAX_SB_SIZE;
570 571
      ConvolveParams temp_conv_params;
      temp_conv_params.ref = 0;
572
      temp_conv_params.round = CONVOLVE_OPT_ROUND;
573 574 575 576 577 578 579 580
#if CONFIG_DUAL_FILTER
      filter_params = filter_params_x;
      filter_size = filter_params_y.taps;
#else
      filter_size = filter_params.taps;
#endif
      intermediate_height =
          (((h - 1) * y_step_q4 + subpel_y_q4) >> SUBPEL_BITS) + filter_size;
581 582
      assert(intermediate_height <= max_intermediate_size);
      (void)max_intermediate_size;
583 584 585

      assert(filter_params.taps <= MAX_FILTER_TAP);

586 587 588
      convolve_horiz(src - src_stride * (filter_size / 2 - 1), src_stride, temp,
                     temp_stride, w, intermediate_height, filter_params,
                     subpel_x_q4, x_step_q4, &temp_conv_params);
589 590 591 592 593 594

#if CONFIG_DUAL_FILTER
      filter_params = filter_params_y;
#endif
      assert(filter_params.taps <= MAX_FILTER_TAP);

595 596 597
      convolve_vert(temp + temp_stride * (filter_size / 2 - 1), temp_stride,
                    dst, dst_stride, w, h, filter_params, subpel_y_q4,
                    y_step_q4, conv_params);
598
    }
Angie Chiang's avatar
Angie Chiang committed
599
  }
600 601
}

602 603 604 605 606 607 608 609 610 611 612 613 614 615
void av1_convolve(const uint8_t *src, int src_stride, uint8_t *dst,
                  int dst_stride, int w, int h,
#if CONFIG_DUAL_FILTER
                  const InterpFilter *interp_filter,
#else
                  const InterpFilter interp_filter,
#endif
                  const int subpel_x_q4, int x_step_q4, const int subpel_y_q4,
                  int y_step_q4, ConvolveParams *conv_params) {
  convolve_helper(src, src_stride, dst, dst_stride, w, h, interp_filter,
                  subpel_x_q4, x_step_q4, subpel_y_q4, y_step_q4, conv_params,
                  av1_convolve_horiz_facade, av1_convolve_vert_facade);
}

616 617 618 619 620 621 622 623 624
void av1_convolve_c(const uint8_t *src, int src_stride, uint8_t *dst,
                    int dst_stride, int w, int h,
#if CONFIG_DUAL_FILTER
                    const InterpFilter *interp_filter,
#else
                    const InterpFilter interp_filter,
#endif
                    const int subpel_x_q4, int x_step_q4, const int subpel_y_q4,
                    int y_step_q4, ConvolveParams *conv_params) {
625 626 627
  convolve_helper(src, src_stride, dst, dst_stride, w, h, interp_filter,
                  subpel_x_q4, x_step_q4, subpel_y_q4, y_step_q4, conv_params,
                  av1_convolve_horiz_facade_c, av1_convolve_vert_facade_c);
628 629
}

Angie Chiang's avatar
Angie Chiang committed
630
void av1_lowbd_convolve_init_c(void) {
Angie Chiang's avatar
Angie Chiang committed
631 632 633 634
  // A placeholder for SIMD initialization
  return;
}

Angie Chiang's avatar
Angie Chiang committed
635 636 637 638 639 640
void av1_highbd_convolve_init_c(void) {
  // A placeholder for SIMD initialization
  return;
}

void av1_convolve_init(AV1_COMMON *cm) {
641
#if CONFIG_HIGHBITDEPTH
Angie Chiang's avatar
Angie Chiang committed
642 643 644 645 646 647 648 649 650 651 652
  if (cm->use_highbitdepth)
    av1_highbd_convolve_init();
  else
    av1_lowbd_convolve_init();
#else
  (void)cm;
  av1_lowbd_convolve_init();
#endif
  return;
}

653
#if CONFIG_HIGHBITDEPTH
Yaowu Xu's avatar
Yaowu Xu committed
654 655 656 657 658
void av1_highbd_convolve_horiz_c(const uint16_t *src, int src_stride,
                                 uint16_t *dst, int dst_stride, int w, int h,
                                 const InterpFilterParams filter_params,
                                 const int subpel_x_q4, int x_step_q4, int avg,
                                 int bd) {
659
  int x, y;
660
  int filter_size = filter_params.taps;
661 662 663 664 665
  src -= filter_size / 2 - 1;
  for (y = 0; y < h; ++y) {
    int x_q4 = subpel_x_q4;
    for (x = 0; x < w; ++x) {
      const uint16_t *const src_x = &src[x_q4 >> SUBPEL_BITS];
Yaowu Xu's avatar
Yaowu Xu committed
666
      const int16_t *x_filter = av1_get_interp_filter_subpel_kernel(
clang-format's avatar
clang-format committed
667
          filter_params, x_q4 & SUBPEL_MASK);
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
      int k, sum = 0;
      for (k = 0; k < filter_size; ++k) sum += src_x[k] * x_filter[k];
      if (avg)
        dst[x] = ROUND_POWER_OF_TWO(
            dst[x] +
                clip_pixel_highbd(ROUND_POWER_OF_TWO(sum, FILTER_BITS), bd),
            1);
      else
        dst[x] = clip_pixel_highbd(ROUND_POWER_OF_TWO(sum, FILTER_BITS), bd);
      x_q4 += x_step_q4;
    }
    src += src_stride;
    dst += dst_stride;
  }
}

Yaowu Xu's avatar
Yaowu Xu committed
684 685 686 687 688
void av1_highbd_convolve_vert_c(const uint16_t *src, int src_stride,
                                uint16_t *dst, int dst_stride, int w, int h,
                                const InterpFilterParams filter_params,
                                const int subpel_y_q4, int y_step_q4, int avg,
                                int bd) {
689
  int x, y;
690
  int filter_size = filter_params.taps;
691 692 693 694 695 696
  src -= src_stride * (filter_size / 2 - 1);

  for (x = 0; x < w; ++x) {
    int y_q4 = subpel_y_q4;
    for (y = 0; y < h; ++y) {
      const uint16_t *const src_y = &src[(y_q4 >> SUBPEL_BITS) * src_stride];
Yaowu Xu's avatar
Yaowu Xu committed
697
      const int16_t *y_filter = av1_get_interp_filter_subpel_kernel(
clang-format's avatar
clang-format committed
698
          filter_params, y_q4 & SUBPEL_MASK);
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
      int k, sum = 0;
      for (k = 0; k < filter_size; ++k)
        sum += src_y[k * src_stride] * y_filter[k];
      if (avg) {
        dst[y * dst_stride] = ROUND_POWER_OF_TWO(
            dst[y * dst_stride] +
                clip_pixel_highbd(ROUND_POWER_OF_TWO(sum, FILTER_BITS), bd),
            1);
      } else {
        dst[y * dst_stride] =
            clip_pixel_highbd(ROUND_POWER_OF_TWO(sum, FILTER_BITS), bd);
      }
      y_q4 += y_step_q4;
    }
    ++src;
    ++dst;
  }
}

Angie Chiang's avatar
Angie Chiang committed
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
static void highbd_convolve_copy(const uint16_t *src, int src_stride,
                                 uint16_t *dst, int dst_stride, int w, int h,
                                 int avg, int bd) {
  if (avg == 0) {
    int r;
    for (r = 0; r < h; ++r) {
      memcpy(dst, src, w * sizeof(*src));
      src += src_stride;
      dst += dst_stride;
    }
  } else {
    int r, c;
    for (r = 0; r < h; ++r) {
      for (c = 0; c < w; ++c) {
        dst[c] = clip_pixel_highbd(ROUND_POWER_OF_TWO(dst[c] + src[c], 1), bd);
      }
      src += src_stride;
      dst += dst_stride;
    }
  }
}

740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
void av1_highbd_convolve_horiz_facade(const uint8_t *src8, int src_stride,
                                      uint8_t *dst8, int dst_stride, int w,
                                      int h,
                                      const InterpFilterParams filter_params,
                                      const int subpel_x_q4, int x_step_q4,
                                      int avg, int bd) {
  uint16_t *src = CONVERT_TO_SHORTPTR(src8);
  uint16_t *dst = CONVERT_TO_SHORTPTR(dst8);
  if (filter_params.taps == SUBPEL_TAPS) {
    const int16_t *filter_x =
        av1_get_interp_filter_subpel_kernel(filter_params, subpel_x_q4);
    if (avg == 0)
      aom_highbd_convolve8_horiz(src8, src_stride, dst8, dst_stride, filter_x,
                                 x_step_q4, NULL, -1, w, h, bd);
    else
      aom_highbd_convolve8_avg_horiz(src8, src_stride, dst8, dst_stride,
                                     filter_x, x_step_q4, NULL, -1, w, h, bd);
  } else {
    av1_highbd_convolve_horiz(src, src_stride, dst, dst_stride, w, h,
                              filter_params, subpel_x_q4, x_step_q4, avg, bd);
  }
}

void av1_highbd_convolve_vert_facade(const uint8_t *src8, int src_stride,
                                     uint8_t *dst8, int dst_stride, int w,
                                     int h,
                                     const InterpFilterParams filter_params,
                                     const int subpel_y_q4, int y_step_q4,
                                     int avg, int bd) {
  uint16_t *src = CONVERT_TO_SHORTPTR(src8);
  uint16_t *dst = CONVERT_TO_SHORTPTR(dst8);
771

772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
  if (filter_params.taps == SUBPEL_TAPS) {
    const int16_t *filter_y =
        av1_get_interp_filter_subpel_kernel(filter_params, subpel_y_q4);
    if (avg == 0) {
      aom_highbd_convolve8_vert(src8, src_stride, dst8, dst_stride, NULL, -1,
                                filter_y, y_step_q4, w, h, bd);
    } else {
      aom_highbd_convolve8_avg_vert(src8, src_stride, dst8, dst_stride, NULL,
                                    -1, filter_y, y_step_q4, w, h, bd);
    }
  } else {
    av1_highbd_convolve_vert(src, src_stride, dst, dst_stride, w, h,
                             filter_params, subpel_y_q4, y_step_q4, avg, bd);
  }
}

Yaowu Xu's avatar
Yaowu Xu committed
788 789
void av1_highbd_convolve(const uint8_t *src8, int src_stride, uint8_t *dst8,
                         int dst_stride, int w, int h,
790
#if CONFIG_DUAL_FILTER
James Zern's avatar
James Zern committed
791
                         const InterpFilter *interp_filter,
792
#else
James Zern's avatar
James Zern committed
793
                         const InterpFilter interp_filter,
794
#endif
Yaowu Xu's avatar
Yaowu Xu committed
795 796 797
                         const int subpel_x_q4, int x_step_q4,
                         const int subpel_y_q4, int y_step_q4, int ref_idx,
                         int bd) {
Angie Chiang's avatar
Angie Chiang committed
798 799 800 801
  uint16_t *src = CONVERT_TO_SHORTPTR(src8);
  uint16_t *dst = CONVERT_TO_SHORTPTR(dst8);
  int ignore_horiz = x_step_q4 == 16 && subpel_x_q4 == 0;
  int ignore_vert = y_step_q4 == 16 && subpel_y_q4 == 0;
802 803 804 805 806 807

  assert(w <= MAX_BLOCK_WIDTH);
  assert(h <= MAX_BLOCK_HEIGHT);
  assert(y_step_q4 <= MAX_STEP);
  assert(x_step_q4 <= MAX_STEP);

Angie Chiang's avatar
Angie Chiang committed
808
  if (ignore_horiz && ignore_vert) {
809
    highbd_convolve_copy(src, src_stride, dst, dst_stride, w, h, ref_idx, bd);
Angie Chiang's avatar
Angie Chiang committed
810
  } else if (ignore_vert) {
811 812
#if CONFIG_DUAL_FILTER
    InterpFilterParams filter_params =
Yaowu Xu's avatar
Yaowu Xu committed
813
        av1_get_interp_filter_params(interp_filter[1 + 2 * ref_idx]);
814 815
#else
    InterpFilterParams filter_params =
Yaowu Xu's avatar
Yaowu Xu committed
816
        av1_get_interp_filter_params(interp_filter);
817
#endif
818 819 820
    av1_highbd_convolve_horiz_facade(src8, src_stride, dst8, dst_stride, w, h,
                                     filter_params, subpel_x_q4, x_step_q4,
                                     ref_idx, bd);
Angie Chiang's avatar
Angie Chiang committed
821
  } else if (ignore_horiz) {
822 823
#if CONFIG_DUAL_FILTER
    InterpFilterParams filter_params =
Yaowu Xu's avatar
Yaowu Xu committed
824
        av1_get_interp_filter_params(interp_filter[0 + 2 * ref_idx]);
825 826
#else
    InterpFilterParams filter_params =
Yaowu Xu's avatar
Yaowu Xu committed
827
        av1_get_interp_filter_params(interp_filter);
828
#endif
829 830 831
    av1_highbd_convolve_vert_facade(src8, src_stride, dst8, dst_stride, w, h,
                                    filter_params, subpel_y_q4, y_step_q4,
                                    ref_idx, bd);
Angie Chiang's avatar
Angie Chiang committed
832
  } else {
833 834 835 836 837
    // temp's size is set to a 256 aligned value to facilitate SIMD
    // implementation. The value is greater than (maximum possible intermediate
    // height or width) * MAX_SB_SIZE
    DECLARE_ALIGNED(16, uint16_t,
                    temp[((MAX_SB_SIZE * 2 + 16) + 16) * MAX_SB_SIZE]);
Yaowu Xu's avatar
Yaowu Xu committed
838
    uint8_t *temp8 = CONVERT_TO_BYTEPTR(temp);
839 840 841
    int max_intermediate_size = ((MAX_SB_SIZE * 2 + 16) + 16);
    int filter_size;
    InterpFilterParams filter_params;
842
#if CONFIG_DUAL_FILTER
Yaowu Xu's avatar
Yaowu Xu committed
843 844 845 846
    InterpFilterParams filter_params_x =
        av1_get_interp_filter_params(interp_filter[1 + 2 * ref_idx]);
    InterpFilterParams filter_params_y =
        av1_get_interp_filter_params(interp_filter[0 + 2 * ref_idx]);
847 848 849
#endif

#if CONFIG_DUAL_FILTER && USE_EXTRA_FILTER
850 851 852 853 854 855
    if (interp_filter[0 + 2 * ref_idx] == MULTITAP_SHARP &&
        interp_filter[1 + 2 * ref_idx] == MULTITAP_SHARP) {
      // Avoid two directions both using 12-tap filter.
      // This will reduce hardware implementation cost.
      filter_params_y = av1_get_interp_filter_params(EIGHTTAP_SHARP);
    }
856 857 858 859 860 861 862 863 864 865
    if (filter_params_y.taps < filter_params_x.taps) {
      int intermediate_width;
      int temp_stride = max_intermediate_size;
      filter_params = filter_params_y;
      filter_size = filter_params_x.taps;
      intermediate_width =
          (((w - 1) * x_step_q4 + subpel_x_q4) >> SUBPEL_BITS) + filter_size;
      assert(intermediate_width <= max_intermediate_size);

      assert(filter_params.taps <= MAX_FILTER_TAP);
Angie Chiang's avatar
Angie Chiang committed
866

867 868 869
      av1_highbd_convolve_vert_facade(
          src8 - (filter_size / 2 - 1), src_stride, temp8, temp_stride,
          intermediate_width, h, filter_params, subpel_y_q4, y_step_q4, 0, bd);
870

871 872 873 874 875 876 877
      filter_params = filter_params_x;
      assert(filter_params.taps <= MAX_FILTER_TAP);

      av1_highbd_convolve_horiz_facade(
          temp8 + (filter_size / 2 - 1), temp_stride, dst8, dst_stride, w, h,
          filter_params, subpel_x_q4, x_step_q4, ref_idx, bd);
    } else
878
#endif  // CONFIG_DUAL_FILTER && USE_EXTRA_FILTER
879 880 881
    {
      int intermediate_height;
      int temp_stride = MAX_SB_SIZE;
882
#if CONFIG_DUAL_FILTER
883 884 885 886 887
      filter_params = filter_params_x;
      filter_size = filter_params_y.taps;
#else
      filter_params = av1_get_interp_filter_params(interp_filter);
      filter_size = filter_params.taps;
888
#endif
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
      intermediate_height =
          (((h - 1) * y_step_q4 + subpel_y_q4) >> SUBPEL_BITS) + filter_size;
      assert(intermediate_height <= max_intermediate_size);
      (void)max_intermediate_size;

      av1_highbd_convolve_horiz_facade(
          src8 - src_stride * (filter_size / 2 - 1), src_stride, temp8,
          temp_stride, w, intermediate_height, filter_params, subpel_x_q4,
          x_step_q4, 0, bd);

#if CONFIG_DUAL_FILTER
      filter_params = filter_params_y;
#endif
      filter_size = filter_params.taps;
      assert(filter_params.taps <= MAX_FILTER_TAP);
904

905 906 907 908
      av1_highbd_convolve_vert_facade(
          temp8 + temp_stride * (filter_size / 2 - 1), temp_stride, dst8,
          dst_stride, w, h, filter_params, subpel_y_q4, y_step_q4, ref_idx, bd);
    }
Angie Chiang's avatar
Angie Chiang committed
909
  }
910
}
911
#endif  // CONFIG_HIGHBITDEPTH