resize.c 37.6 KB
Newer Older
Jingning Han's avatar
Jingning Han committed
1
/*
Yaowu Xu's avatar
Yaowu Xu committed
2
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
Jingning Han's avatar
Jingning Han committed
3
 *
Yaowu Xu's avatar
Yaowu Xu committed
4 5 6 7 8 9
 * 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.
Jingning Han's avatar
Jingning Han committed
10 11 12 13 14 15 16 17 18
 */

#include <assert.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

19
#include "./aom_config.h"
20
#if CONFIG_HIGHBITDEPTH
Yaowu Xu's avatar
Yaowu Xu committed
21
#include "aom_dsp/aom_dsp_common.h"
22
#endif  // CONFIG_HIGHBITDEPTH
23
#include "aom_ports/mem.h"
24
#include "aom_scale/aom_scale.h"
25
#include "av1/common/common.h"
26
#include "av1/common/resize.h"
Jingning Han's avatar
Jingning Han committed
27

28 29
#include "./aom_scale_rtcd.h"

30
#define FILTER_BITS 7
Jingning Han's avatar
Jingning Han committed
31

32
#define INTERP_TAPS 8
33 34
#define SUBPEL_BITS_RS 5
#define SUBPEL_MASK_RS ((1 << SUBPEL_BITS_RS) - 1)
35
#define INTERP_PRECISION_BITS 32
Jingning Han's avatar
Jingning Han committed
36 37 38 39

typedef int16_t interp_kernel[INTERP_TAPS];

// Filters for interpolation (0.5-band) - note this also filters integer pels.
40
static const interp_kernel filteredinterp_filters500[(1 << SUBPEL_BITS_RS)] = {
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
  { -3, 0, 35, 64, 35, 0, -3, 0 },    { -3, -1, 34, 64, 36, 1, -3, 0 },
  { -3, -1, 32, 64, 38, 1, -3, 0 },   { -2, -2, 31, 63, 39, 2, -3, 0 },
  { -2, -2, 29, 63, 41, 2, -3, 0 },   { -2, -2, 28, 63, 42, 3, -4, 0 },
  { -2, -3, 27, 63, 43, 4, -4, 0 },   { -2, -3, 25, 62, 45, 5, -4, 0 },
  { -2, -3, 24, 62, 46, 5, -4, 0 },   { -2, -3, 23, 61, 47, 6, -4, 0 },
  { -2, -3, 21, 60, 49, 7, -4, 0 },   { -1, -4, 20, 60, 50, 8, -4, -1 },
  { -1, -4, 19, 59, 51, 9, -4, -1 },  { -1, -4, 17, 58, 52, 10, -4, 0 },
  { -1, -4, 16, 57, 53, 12, -4, -1 }, { -1, -4, 15, 56, 54, 13, -4, -1 },
  { -1, -4, 14, 55, 55, 14, -4, -1 }, { -1, -4, 13, 54, 56, 15, -4, -1 },
  { -1, -4, 12, 53, 57, 16, -4, -1 }, { 0, -4, 10, 52, 58, 17, -4, -1 },
  { -1, -4, 9, 51, 59, 19, -4, -1 },  { -1, -4, 8, 50, 60, 20, -4, -1 },
  { 0, -4, 7, 49, 60, 21, -3, -2 },   { 0, -4, 6, 47, 61, 23, -3, -2 },
  { 0, -4, 5, 46, 62, 24, -3, -2 },   { 0, -4, 5, 45, 62, 25, -3, -2 },
  { 0, -4, 4, 43, 63, 27, -3, -2 },   { 0, -4, 3, 42, 63, 28, -2, -2 },
  { 0, -3, 2, 41, 63, 29, -2, -2 },   { 0, -3, 2, 39, 63, 31, -2, -2 },
  { 0, -3, 1, 38, 64, 32, -1, -3 },   { 0, -3, 1, 36, 64, 34, -1, -3 }
Jingning Han's avatar
Jingning Han committed
57 58 59
};

// Filters for interpolation (0.625-band) - note this also filters integer pels.
60
static const interp_kernel filteredinterp_filters625[(1 << SUBPEL_BITS_RS)] = {
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
  { -1, -8, 33, 80, 33, -8, -1, 0 }, { -1, -8, 30, 80, 35, -8, -1, 1 },
  { -1, -8, 28, 80, 37, -7, -2, 1 }, { 0, -8, 26, 79, 39, -7, -2, 1 },
  { 0, -8, 24, 79, 41, -7, -2, 1 },  { 0, -8, 22, 78, 43, -6, -2, 1 },
  { 0, -8, 20, 78, 45, -5, -3, 1 },  { 0, -8, 18, 77, 48, -5, -3, 1 },
  { 0, -8, 16, 76, 50, -4, -3, 1 },  { 0, -8, 15, 75, 52, -3, -4, 1 },
  { 0, -7, 13, 74, 54, -3, -4, 1 },  { 0, -7, 11, 73, 56, -2, -4, 1 },
  { 0, -7, 10, 71, 58, -1, -4, 1 },  { 1, -7, 8, 70, 60, 0, -5, 1 },
  { 1, -6, 6, 68, 62, 1, -5, 1 },    { 1, -6, 5, 67, 63, 2, -5, 1 },
  { 1, -6, 4, 65, 65, 4, -6, 1 },    { 1, -5, 2, 63, 67, 5, -6, 1 },
  { 1, -5, 1, 62, 68, 6, -6, 1 },    { 1, -5, 0, 60, 70, 8, -7, 1 },
  { 1, -4, -1, 58, 71, 10, -7, 0 },  { 1, -4, -2, 56, 73, 11, -7, 0 },
  { 1, -4, -3, 54, 74, 13, -7, 0 },  { 1, -4, -3, 52, 75, 15, -8, 0 },
  { 1, -3, -4, 50, 76, 16, -8, 0 },  { 1, -3, -5, 48, 77, 18, -8, 0 },
  { 1, -3, -5, 45, 78, 20, -8, 0 },  { 1, -2, -6, 43, 78, 22, -8, 0 },
  { 1, -2, -7, 41, 79, 24, -8, 0 },  { 1, -2, -7, 39, 79, 26, -8, 0 },
  { 1, -2, -7, 37, 80, 28, -8, -1 }, { 1, -1, -8, 35, 80, 30, -8, -1 },
Jingning Han's avatar
Jingning Han committed
77 78 79
};

// Filters for interpolation (0.75-band) - note this also filters integer pels.
80
static const interp_kernel filteredinterp_filters750[(1 << SUBPEL_BITS_RS)] = {
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
  { 2, -11, 25, 96, 25, -11, 2, 0 }, { 2, -11, 22, 96, 28, -11, 2, 0 },
  { 2, -10, 19, 95, 31, -11, 2, 0 }, { 2, -10, 17, 95, 34, -12, 2, 0 },
  { 2, -9, 14, 94, 37, -12, 2, 0 },  { 2, -8, 12, 93, 40, -12, 1, 0 },
  { 2, -8, 9, 92, 43, -12, 1, 1 },   { 2, -7, 7, 91, 46, -12, 1, 0 },
  { 2, -7, 5, 90, 49, -12, 1, 0 },   { 2, -6, 3, 88, 52, -12, 0, 1 },
  { 2, -5, 1, 86, 55, -12, 0, 1 },   { 2, -5, -1, 84, 58, -11, 0, 1 },
  { 2, -4, -2, 82, 61, -11, -1, 1 }, { 2, -4, -4, 80, 64, -10, -1, 1 },
  { 1, -3, -5, 77, 67, -9, -1, 1 },  { 1, -3, -6, 75, 70, -8, -2, 1 },
  { 1, -2, -7, 72, 72, -7, -2, 1 },  { 1, -2, -8, 70, 75, -6, -3, 1 },
  { 1, -1, -9, 67, 77, -5, -3, 1 },  { 1, -1, -10, 64, 80, -4, -4, 2 },
  { 1, -1, -11, 61, 82, -2, -4, 2 }, { 1, 0, -11, 58, 84, -1, -5, 2 },
  { 1, 0, -12, 55, 86, 1, -5, 2 },   { 1, 0, -12, 52, 88, 3, -6, 2 },
  { 0, 1, -12, 49, 90, 5, -7, 2 },   { 0, 1, -12, 46, 91, 7, -7, 2 },
  { 1, 1, -12, 43, 92, 9, -8, 2 },   { 0, 1, -12, 40, 93, 12, -8, 2 },
  { 0, 2, -12, 37, 94, 14, -9, 2 },  { 0, 2, -12, 34, 95, 17, -10, 2 },
  { 0, 2, -11, 31, 95, 19, -10, 2 }, { 0, 2, -11, 28, 96, 22, -11, 2 }
Jingning Han's avatar
Jingning Han committed
97 98 99
};

// Filters for interpolation (0.875-band) - note this also filters integer pels.
100
static const interp_kernel filteredinterp_filters875[(1 << SUBPEL_BITS_RS)] = {
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
  { 3, -8, 13, 112, 13, -8, 3, 0 },   { 3, -7, 10, 112, 17, -9, 3, -1 },
  { 2, -6, 7, 111, 21, -9, 3, -1 },   { 2, -5, 4, 111, 24, -10, 3, -1 },
  { 2, -4, 1, 110, 28, -11, 3, -1 },  { 1, -3, -1, 108, 32, -12, 4, -1 },
  { 1, -2, -3, 106, 36, -13, 4, -1 }, { 1, -1, -6, 105, 40, -14, 4, -1 },
  { 1, -1, -7, 102, 44, -14, 4, -1 }, { 1, 0, -9, 100, 48, -15, 4, -1 },
  { 1, 1, -11, 97, 53, -16, 4, -1 },  { 0, 1, -12, 95, 57, -16, 4, -1 },
  { 0, 2, -13, 91, 61, -16, 4, -1 },  { 0, 2, -14, 88, 65, -16, 4, -1 },
  { 0, 3, -15, 84, 69, -17, 4, 0 },   { 0, 3, -16, 81, 73, -16, 3, 0 },
  { 0, 3, -16, 77, 77, -16, 3, 0 },   { 0, 3, -16, 73, 81, -16, 3, 0 },
  { 0, 4, -17, 69, 84, -15, 3, 0 },   { -1, 4, -16, 65, 88, -14, 2, 0 },
  { -1, 4, -16, 61, 91, -13, 2, 0 },  { -1, 4, -16, 57, 95, -12, 1, 0 },
  { -1, 4, -16, 53, 97, -11, 1, 1 },  { -1, 4, -15, 48, 100, -9, 0, 1 },
  { -1, 4, -14, 44, 102, -7, -1, 1 }, { -1, 4, -14, 40, 105, -6, -1, 1 },
  { -1, 4, -13, 36, 106, -3, -2, 1 }, { -1, 4, -12, 32, 108, -1, -3, 1 },
  { -1, 3, -11, 28, 110, 1, -4, 2 },  { -1, 3, -10, 24, 111, 4, -5, 2 },
  { -1, 3, -9, 21, 111, 7, -6, 2 },   { -1, 3, -9, 17, 112, 10, -7, 3 }
Jingning Han's avatar
Jingning Han committed
117 118 119
};

// Filters for interpolation (full-band) - no filtering for integer pixels
120
static const interp_kernel filteredinterp_filters1000[(1 << SUBPEL_BITS_RS)] = {
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
  { 0, 0, 0, 128, 0, 0, 0, 0 },        { 0, 1, -3, 128, 3, -1, 0, 0 },
  { -1, 2, -6, 127, 7, -2, 1, 0 },     { -1, 3, -9, 126, 12, -4, 1, 0 },
  { -1, 4, -12, 125, 16, -5, 1, 0 },   { -1, 4, -14, 123, 20, -6, 2, 0 },
  { -1, 5, -15, 120, 25, -8, 2, 0 },   { -1, 5, -17, 118, 30, -9, 3, -1 },
  { -1, 6, -18, 114, 35, -10, 3, -1 }, { -1, 6, -19, 111, 41, -12, 3, -1 },
  { -1, 6, -20, 107, 46, -13, 4, -1 }, { -1, 6, -21, 103, 52, -14, 4, -1 },
  { -1, 6, -21, 99, 57, -16, 5, -1 },  { -1, 6, -21, 94, 63, -17, 5, -1 },
  { -1, 6, -20, 89, 68, -18, 5, -1 },  { -1, 6, -20, 84, 73, -19, 6, -1 },
  { -1, 6, -20, 79, 79, -20, 6, -1 },  { -1, 6, -19, 73, 84, -20, 6, -1 },
  { -1, 5, -18, 68, 89, -20, 6, -1 },  { -1, 5, -17, 63, 94, -21, 6, -1 },
  { -1, 5, -16, 57, 99, -21, 6, -1 },  { -1, 4, -14, 52, 103, -21, 6, -1 },
  { -1, 4, -13, 46, 107, -20, 6, -1 }, { -1, 3, -12, 41, 111, -19, 6, -1 },
  { -1, 3, -10, 35, 114, -18, 6, -1 }, { -1, 3, -9, 30, 118, -17, 5, -1 },
  { 0, 2, -8, 25, 120, -15, 5, -1 },   { 0, 2, -6, 20, 123, -14, 4, -1 },
  { 0, 1, -5, 16, 125, -12, 4, -1 },   { 0, 1, -4, 12, 126, -9, 3, -1 },
  { 0, 1, -2, 7, 127, -6, 2, -1 },     { 0, 0, -1, 3, 128, -3, 1, 0 }
Jingning Han's avatar
Jingning Han committed
137 138 139
};

// Filters for factor of 2 downsampling.
Yaowu Xu's avatar
Yaowu Xu committed
140 141
static const int16_t av1_down2_symeven_half_filter[] = { 56, 12, -3, -1 };
static const int16_t av1_down2_symodd_half_filter[] = { 64, 35, 0, -3 };
Jingning Han's avatar
Jingning Han committed
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158

static const interp_kernel *choose_interp_filter(int inlength, int outlength) {
  int outlength16 = outlength * 16;
  if (outlength16 >= inlength * 16)
    return filteredinterp_filters1000;
  else if (outlength16 >= inlength * 13)
    return filteredinterp_filters875;
  else if (outlength16 >= inlength * 11)
    return filteredinterp_filters750;
  else if (outlength16 >= inlength * 9)
    return filteredinterp_filters625;
  else
    return filteredinterp_filters500;
}

static void interpolate(const uint8_t *const input, int inlength,
                        uint8_t *output, int outlength) {
159 160 161 162 163 164 165 166
  const int64_t delta =
      (((uint64_t)inlength << 32) + outlength / 2) / outlength;
  const int64_t offset =
      inlength > outlength
          ? (((int64_t)(inlength - outlength) << 31) + outlength / 2) /
                outlength
          : -(((int64_t)(outlength - inlength) << 31) + outlength / 2) /
                outlength;
Jingning Han's avatar
Jingning Han committed
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
  uint8_t *optr = output;
  int x, x1, x2, sum, k, int_pel, sub_pel;
  int64_t y;

  const interp_kernel *interp_filters =
      choose_interp_filter(inlength, outlength);

  x = 0;
  y = offset;
  while ((y >> INTERP_PRECISION_BITS) < (INTERP_TAPS / 2 - 1)) {
    x++;
    y += delta;
  }
  x1 = x;
  x = outlength - 1;
  y = delta * x + offset;
183 184
  while ((y >> INTERP_PRECISION_BITS) + (int64_t)(INTERP_TAPS / 2) >=
         inlength) {
Jingning Han's avatar
Jingning Han committed
185 186 187 188 189 190 191 192
    x--;
    y -= delta;
  }
  x2 = x;
  if (x1 > x2) {
    for (x = 0, y = offset; x < outlength; ++x, y += delta) {
      const int16_t *filter;
      int_pel = y >> INTERP_PRECISION_BITS;
193 194
      sub_pel =
          (y >> (INTERP_PRECISION_BITS - SUBPEL_BITS_RS)) & SUBPEL_MASK_RS;
Jingning Han's avatar
Jingning Han committed
195 196 197 198
      filter = interp_filters[sub_pel];
      sum = 0;
      for (k = 0; k < INTERP_TAPS; ++k) {
        const int pk = int_pel - INTERP_TAPS / 2 + 1 + k;
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
199
        sum += filter[k] * input[AOMMAX(AOMMIN(pk, inlength - 1), 0)];
Jingning Han's avatar
Jingning Han committed
200 201 202 203 204 205 206 207
      }
      *optr++ = clip_pixel(ROUND_POWER_OF_TWO(sum, FILTER_BITS));
    }
  } else {
    // Initial part.
    for (x = 0, y = offset; x < x1; ++x, y += delta) {
      const int16_t *filter;
      int_pel = y >> INTERP_PRECISION_BITS;
208 209
      sub_pel =
          (y >> (INTERP_PRECISION_BITS - SUBPEL_BITS_RS)) & SUBPEL_MASK_RS;
Jingning Han's avatar
Jingning Han committed
210 211 212
      filter = interp_filters[sub_pel];
      sum = 0;
      for (k = 0; k < INTERP_TAPS; ++k)
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
213
        sum += filter[k] * input[AOMMAX(int_pel - INTERP_TAPS / 2 + 1 + k, 0)];
Jingning Han's avatar
Jingning Han committed
214 215 216 217 218 219
      *optr++ = clip_pixel(ROUND_POWER_OF_TWO(sum, FILTER_BITS));
    }
    // Middle part.
    for (; x <= x2; ++x, y += delta) {
      const int16_t *filter;
      int_pel = y >> INTERP_PRECISION_BITS;
220 221
      sub_pel =
          (y >> (INTERP_PRECISION_BITS - SUBPEL_BITS_RS)) & SUBPEL_MASK_RS;
Jingning Han's avatar
Jingning Han committed
222 223 224 225 226 227 228 229 230 231
      filter = interp_filters[sub_pel];
      sum = 0;
      for (k = 0; k < INTERP_TAPS; ++k)
        sum += filter[k] * input[int_pel - INTERP_TAPS / 2 + 1 + k];
      *optr++ = clip_pixel(ROUND_POWER_OF_TWO(sum, FILTER_BITS));
    }
    // End part.
    for (; x < outlength; ++x, y += delta) {
      const int16_t *filter;
      int_pel = y >> INTERP_PRECISION_BITS;
232 233
      sub_pel =
          (y >> (INTERP_PRECISION_BITS - SUBPEL_BITS_RS)) & SUBPEL_MASK_RS;
Jingning Han's avatar
Jingning Han committed
234 235 236
      filter = interp_filters[sub_pel];
      sum = 0;
      for (k = 0; k < INTERP_TAPS; ++k)
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
237 238
        sum += filter[k] *
               input[AOMMIN(int_pel - INTERP_TAPS / 2 + 1 + k, inlength - 1)];
Jingning Han's avatar
Jingning Han committed
239 240 241 242 243
      *optr++ = clip_pixel(ROUND_POWER_OF_TWO(sum, FILTER_BITS));
    }
  }
}

244
#ifndef __clang_analyzer__
Jingning Han's avatar
Jingning Han committed
245 246 247
static void down2_symeven(const uint8_t *const input, int length,
                          uint8_t *output) {
  // Actual filter len = 2 * filter_len_half.
Yaowu Xu's avatar
Yaowu Xu committed
248 249
  const int16_t *filter = av1_down2_symeven_half_filter;
  const int filter_len_half = sizeof(av1_down2_symeven_half_filter) / 2;
Jingning Han's avatar
Jingning Han committed
250 251 252 253 254 255 256 257 258 259 260
  int i, j;
  uint8_t *optr = output;
  int l1 = filter_len_half;
  int l2 = (length - filter_len_half);
  l1 += (l1 & 1);
  l2 += (l2 & 1);
  if (l1 > l2) {
    // Short input length.
    for (i = 0; i < length; i += 2) {
      int sum = (1 << (FILTER_BITS - 1));
      for (j = 0; j < filter_len_half; ++j) {
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
261 262 263
        sum +=
            (input[AOMMAX(i - j, 0)] + input[AOMMIN(i + 1 + j, length - 1)]) *
            filter[j];
Jingning Han's avatar
Jingning Han committed
264 265 266 267 268 269 270 271 272
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel(sum);
    }
  } else {
    // Initial part.
    for (i = 0; i < l1; i += 2) {
      int sum = (1 << (FILTER_BITS - 1));
      for (j = 0; j < filter_len_half; ++j) {
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
273
        sum += (input[AOMMAX(i - j, 0)] + input[i + 1 + j]) * filter[j];
Jingning Han's avatar
Jingning Han committed
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel(sum);
    }
    // Middle part.
    for (; i < l2; i += 2) {
      int sum = (1 << (FILTER_BITS - 1));
      for (j = 0; j < filter_len_half; ++j) {
        sum += (input[i - j] + input[i + 1 + j]) * filter[j];
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel(sum);
    }
    // End part.
    for (; i < length; i += 2) {
      int sum = (1 << (FILTER_BITS - 1));
      for (j = 0; j < filter_len_half; ++j) {
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
291 292
        sum +=
            (input[i - j] + input[AOMMIN(i + 1 + j, length - 1)]) * filter[j];
Jingning Han's avatar
Jingning Han committed
293 294 295 296 297 298
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel(sum);
    }
  }
}
299
#endif
Jingning Han's avatar
Jingning Han committed
300 301 302 303

static void down2_symodd(const uint8_t *const input, int length,
                         uint8_t *output) {
  // Actual filter len = 2 * filter_len_half - 1.
Yaowu Xu's avatar
Yaowu Xu committed
304 305
  const int16_t *filter = av1_down2_symodd_half_filter;
  const int filter_len_half = sizeof(av1_down2_symodd_half_filter) / 2;
Jingning Han's avatar
Jingning Han committed
306 307 308 309 310 311 312 313 314 315 316 317 318
  int i, j;
  uint8_t *optr = output;
  int l1 = filter_len_half - 1;
  int l2 = (length - filter_len_half + 1);
  l1 += (l1 & 1);
  l2 += (l2 & 1);
  if (l1 > l2) {
    // Short input length.
    for (i = 0; i < length; i += 2) {
      int sum = (1 << (FILTER_BITS - 1)) + input[i] * filter[0];
      for (j = 1; j < filter_len_half; ++j) {
        sum += (input[(i - j < 0 ? 0 : i - j)] +
                input[(i + j >= length ? length - 1 : i + j)]) *
319
               filter[j];
Jingning Han's avatar
Jingning Han committed
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel(sum);
    }
  } else {
    // Initial part.
    for (i = 0; i < l1; i += 2) {
      int sum = (1 << (FILTER_BITS - 1)) + input[i] * filter[0];
      for (j = 1; j < filter_len_half; ++j) {
        sum += (input[(i - j < 0 ? 0 : i - j)] + input[i + j]) * filter[j];
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel(sum);
    }
    // Middle part.
    for (; i < l2; i += 2) {
      int sum = (1 << (FILTER_BITS - 1)) + input[i] * filter[0];
      for (j = 1; j < filter_len_half; ++j) {
        sum += (input[i - j] + input[i + j]) * filter[j];
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel(sum);
    }
    // End part.
    for (; i < length; i += 2) {
      int sum = (1 << (FILTER_BITS - 1)) + input[i] * filter[0];
      for (j = 1; j < filter_len_half; ++j) {
        sum += (input[i - j] + input[(i + j >= length ? length - 1 : i + j)]) *
348
               filter[j];
Jingning Han's avatar
Jingning Han committed
349 350 351 352 353 354 355 356 357
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel(sum);
    }
  }
}

static int get_down2_length(int length, int steps) {
  int s;
358
  for (s = 0; s < steps; ++s) length = (length + 1) >> 1;
Jingning Han's avatar
Jingning Han committed
359 360 361 362 363 364 365 366 367 368 369 370 371
  return length;
}

static int get_down2_steps(int in_length, int out_length) {
  int steps = 0;
  int proj_in_length;
  while ((proj_in_length = get_down2_length(in_length, 1)) >= out_length) {
    ++steps;
    in_length = proj_in_length;
  }
  return steps;
}

372 373
static void resize_multistep(const uint8_t *const input, int length,
                             uint8_t *output, int olength, uint8_t *otmp) {
Jingning Han's avatar
Jingning Han committed
374
  if (length == olength) {
375
    memcpy(output, input, sizeof(output[0]) * length);
Jingning Han's avatar
Jingning Han committed
376 377
    return;
  }
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
378
  const int steps = get_down2_steps(length, olength);
Jingning Han's avatar
Jingning Han committed
379 380 381 382

  if (steps > 0) {
    uint8_t *out = NULL;
    int filteredlength = length;
383 384

    assert(otmp != NULL);
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
385 386
    uint8_t *otmp2 = otmp + get_down2_length(length, 1);
    for (int s = 0; s < steps; ++s) {
Jingning Han's avatar
Jingning Han committed
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 424
      const int proj_filteredlength = get_down2_length(filteredlength, 1);
      const uint8_t *const in = (s == 0 ? input : out);
      if (s == steps - 1 && proj_filteredlength == olength)
        out = output;
      else
        out = (s & 1 ? otmp2 : otmp);
      if (filteredlength & 1)
        down2_symodd(in, filteredlength, out);
      else
        down2_symeven(in, filteredlength, out);
      filteredlength = proj_filteredlength;
    }
    if (filteredlength != olength) {
      interpolate(out, filteredlength, output, olength);
    }
  } else {
    interpolate(input, length, output, olength);
  }
}

static void fill_col_to_arr(uint8_t *img, int stride, int len, uint8_t *arr) {
  int i;
  uint8_t *iptr = img;
  uint8_t *aptr = arr;
  for (i = 0; i < len; ++i, iptr += stride) {
    *aptr++ = *iptr;
  }
}

static void fill_arr_to_col(uint8_t *img, int stride, int len, uint8_t *arr) {
  int i;
  uint8_t *iptr = img;
  uint8_t *aptr = arr;
  for (i = 0; i < len; ++i, iptr += stride) {
    *iptr = *aptr++;
  }
}

Yaowu Xu's avatar
Yaowu Xu committed
425 426 427
void av1_resize_plane(const uint8_t *const input, int height, int width,
                      int in_stride, uint8_t *output, int height2, int width2,
                      int out_stride) {
Jingning Han's avatar
Jingning Han committed
428 429
  int i;
  uint8_t *intbuf = (uint8_t *)malloc(sizeof(uint8_t) * width2 * height);
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
430
  uint8_t *tmpbuf = (uint8_t *)malloc(sizeof(uint8_t) * AOMMAX(width, height));
431 432
  uint8_t *arrbuf = (uint8_t *)malloc(sizeof(uint8_t) * height);
  uint8_t *arrbuf2 = (uint8_t *)malloc(sizeof(uint8_t) * height2);
433
  if (intbuf == NULL || tmpbuf == NULL || arrbuf == NULL || arrbuf2 == NULL)
434
    goto Error;
Jingning Han's avatar
Jingning Han committed
435 436 437 438 439
  assert(width > 0);
  assert(height > 0);
  assert(width2 > 0);
  assert(height2 > 0);
  for (i = 0; i < height; ++i)
440 441
    resize_multistep(input + in_stride * i, width, intbuf + width2 * i, width2,
                     tmpbuf);
Jingning Han's avatar
Jingning Han committed
442 443
  for (i = 0; i < width2; ++i) {
    fill_col_to_arr(intbuf + i, width2, height, arrbuf);
444 445
    resize_multistep(arrbuf, height, arrbuf2, height2, tmpbuf);
    fill_arr_to_col(output + i, out_stride, height2, arrbuf2);
Jingning Han's avatar
Jingning Han committed
446
  }
447

448
Error:
Jingning Han's avatar
Jingning Han committed
449 450 451
  free(intbuf);
  free(tmpbuf);
  free(arrbuf);
452
  free(arrbuf2);
Jingning Han's avatar
Jingning Han committed
453 454
}

455
#if CONFIG_HIGHBITDEPTH
Jingning Han's avatar
Jingning Han committed
456 457 458 459
static void highbd_interpolate(const uint16_t *const input, int inlength,
                               uint16_t *output, int outlength, int bd) {
  const int64_t delta =
      (((uint64_t)inlength << 32) + outlength / 2) / outlength;
460 461 462 463 464 465
  const int64_t offset =
      inlength > outlength
          ? (((int64_t)(inlength - outlength) << 31) + outlength / 2) /
                outlength
          : -(((int64_t)(outlength - inlength) << 31) + outlength / 2) /
                outlength;
Jingning Han's avatar
Jingning Han committed
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
  uint16_t *optr = output;
  int x, x1, x2, sum, k, int_pel, sub_pel;
  int64_t y;

  const interp_kernel *interp_filters =
      choose_interp_filter(inlength, outlength);

  x = 0;
  y = offset;
  while ((y >> INTERP_PRECISION_BITS) < (INTERP_TAPS / 2 - 1)) {
    x++;
    y += delta;
  }
  x1 = x;
  x = outlength - 1;
  y = delta * x + offset;
482 483
  while ((y >> INTERP_PRECISION_BITS) + (int64_t)(INTERP_TAPS / 2) >=
         inlength) {
Jingning Han's avatar
Jingning Han committed
484 485 486 487 488 489 490 491
    x--;
    y -= delta;
  }
  x2 = x;
  if (x1 > x2) {
    for (x = 0, y = offset; x < outlength; ++x, y += delta) {
      const int16_t *filter;
      int_pel = y >> INTERP_PRECISION_BITS;
492 493
      sub_pel =
          (y >> (INTERP_PRECISION_BITS - SUBPEL_BITS_RS)) & SUBPEL_MASK_RS;
Jingning Han's avatar
Jingning Han committed
494 495 496 497
      filter = interp_filters[sub_pel];
      sum = 0;
      for (k = 0; k < INTERP_TAPS; ++k) {
        const int pk = int_pel - INTERP_TAPS / 2 + 1 + k;
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
498
        sum += filter[k] * input[AOMMAX(AOMMIN(pk, inlength - 1), 0)];
Jingning Han's avatar
Jingning Han committed
499 500 501 502 503 504 505 506
      }
      *optr++ = clip_pixel_highbd(ROUND_POWER_OF_TWO(sum, FILTER_BITS), bd);
    }
  } else {
    // Initial part.
    for (x = 0, y = offset; x < x1; ++x, y += delta) {
      const int16_t *filter;
      int_pel = y >> INTERP_PRECISION_BITS;
507 508
      sub_pel =
          (y >> (INTERP_PRECISION_BITS - SUBPEL_BITS_RS)) & SUBPEL_MASK_RS;
Jingning Han's avatar
Jingning Han committed
509 510 511
      filter = interp_filters[sub_pel];
      sum = 0;
      for (k = 0; k < INTERP_TAPS; ++k)
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
512
        sum += filter[k] * input[AOMMAX(int_pel - INTERP_TAPS / 2 + 1 + k, 0)];
Jingning Han's avatar
Jingning Han committed
513 514 515 516 517 518
      *optr++ = clip_pixel_highbd(ROUND_POWER_OF_TWO(sum, FILTER_BITS), bd);
    }
    // Middle part.
    for (; x <= x2; ++x, y += delta) {
      const int16_t *filter;
      int_pel = y >> INTERP_PRECISION_BITS;
519 520
      sub_pel =
          (y >> (INTERP_PRECISION_BITS - SUBPEL_BITS_RS)) & SUBPEL_MASK_RS;
Jingning Han's avatar
Jingning Han committed
521 522 523 524 525 526 527 528 529 530
      filter = interp_filters[sub_pel];
      sum = 0;
      for (k = 0; k < INTERP_TAPS; ++k)
        sum += filter[k] * input[int_pel - INTERP_TAPS / 2 + 1 + k];
      *optr++ = clip_pixel_highbd(ROUND_POWER_OF_TWO(sum, FILTER_BITS), bd);
    }
    // End part.
    for (; x < outlength; ++x, y += delta) {
      const int16_t *filter;
      int_pel = y >> INTERP_PRECISION_BITS;
531 532
      sub_pel =
          (y >> (INTERP_PRECISION_BITS - SUBPEL_BITS_RS)) & SUBPEL_MASK_RS;
Jingning Han's avatar
Jingning Han committed
533 534 535
      filter = interp_filters[sub_pel];
      sum = 0;
      for (k = 0; k < INTERP_TAPS; ++k)
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
536 537
        sum += filter[k] *
               input[AOMMIN(int_pel - INTERP_TAPS / 2 + 1 + k, inlength - 1)];
Jingning Han's avatar
Jingning Han committed
538 539 540 541
      *optr++ = clip_pixel_highbd(ROUND_POWER_OF_TWO(sum, FILTER_BITS), bd);
    }
  }
}
542
#ifndef __clang_analyzer__
Jingning Han's avatar
Jingning Han committed
543 544 545
static void highbd_down2_symeven(const uint16_t *const input, int length,
                                 uint16_t *output, int bd) {
  // Actual filter len = 2 * filter_len_half.
Yaowu Xu's avatar
Yaowu Xu committed
546 547
  static const int16_t *filter = av1_down2_symeven_half_filter;
  const int filter_len_half = sizeof(av1_down2_symeven_half_filter) / 2;
Jingning Han's avatar
Jingning Han committed
548 549 550 551 552 553 554 555 556 557 558
  int i, j;
  uint16_t *optr = output;
  int l1 = filter_len_half;
  int l2 = (length - filter_len_half);
  l1 += (l1 & 1);
  l2 += (l2 & 1);
  if (l1 > l2) {
    // Short input length.
    for (i = 0; i < length; i += 2) {
      int sum = (1 << (FILTER_BITS - 1));
      for (j = 0; j < filter_len_half; ++j) {
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
559 560 561
        sum +=
            (input[AOMMAX(0, i - j)] + input[AOMMIN(i + 1 + j, length - 1)]) *
            filter[j];
Jingning Han's avatar
Jingning Han committed
562 563 564 565 566 567 568 569 570
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel_highbd(sum, bd);
    }
  } else {
    // Initial part.
    for (i = 0; i < l1; i += 2) {
      int sum = (1 << (FILTER_BITS - 1));
      for (j = 0; j < filter_len_half; ++j) {
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
571
        sum += (input[AOMMAX(0, i - j)] + input[i + 1 + j]) * filter[j];
Jingning Han's avatar
Jingning Han committed
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel_highbd(sum, bd);
    }
    // Middle part.
    for (; i < l2; i += 2) {
      int sum = (1 << (FILTER_BITS - 1));
      for (j = 0; j < filter_len_half; ++j) {
        sum += (input[i - j] + input[i + 1 + j]) * filter[j];
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel_highbd(sum, bd);
    }
    // End part.
    for (; i < length; i += 2) {
      int sum = (1 << (FILTER_BITS - 1));
      for (j = 0; j < filter_len_half; ++j) {
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
589 590
        sum +=
            (input[i - j] + input[AOMMIN(i + 1 + j, length - 1)]) * filter[j];
Jingning Han's avatar
Jingning Han committed
591 592 593 594 595 596 597 598
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel_highbd(sum, bd);
    }
  }
}

static void highbd_down2_symodd(const uint16_t *const input, int length,
599
                                uint16_t *output, int bd) {
Jingning Han's avatar
Jingning Han committed
600
  // Actual filter len = 2 * filter_len_half - 1.
Yaowu Xu's avatar
Yaowu Xu committed
601 602
  static const int16_t *filter = av1_down2_symodd_half_filter;
  const int filter_len_half = sizeof(av1_down2_symodd_half_filter) / 2;
Jingning Han's avatar
Jingning Han committed
603 604 605 606 607 608 609 610 611 612 613
  int i, j;
  uint16_t *optr = output;
  int l1 = filter_len_half - 1;
  int l2 = (length - filter_len_half + 1);
  l1 += (l1 & 1);
  l2 += (l2 & 1);
  if (l1 > l2) {
    // Short input length.
    for (i = 0; i < length; i += 2) {
      int sum = (1 << (FILTER_BITS - 1)) + input[i] * filter[0];
      for (j = 1; j < filter_len_half; ++j) {
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
614
        sum += (input[AOMMAX(i - j, 0)] + input[AOMMIN(i + j, length - 1)]) *
615
               filter[j];
Jingning Han's avatar
Jingning Han committed
616 617 618 619 620 621 622 623 624
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel_highbd(sum, bd);
    }
  } else {
    // Initial part.
    for (i = 0; i < l1; i += 2) {
      int sum = (1 << (FILTER_BITS - 1)) + input[i] * filter[0];
      for (j = 1; j < filter_len_half; ++j) {
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
625
        sum += (input[AOMMAX(i - j, 0)] + input[i + j]) * filter[j];
Jingning Han's avatar
Jingning Han committed
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel_highbd(sum, bd);
    }
    // Middle part.
    for (; i < l2; i += 2) {
      int sum = (1 << (FILTER_BITS - 1)) + input[i] * filter[0];
      for (j = 1; j < filter_len_half; ++j) {
        sum += (input[i - j] + input[i + j]) * filter[j];
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel_highbd(sum, bd);
    }
    // End part.
    for (; i < length; i += 2) {
      int sum = (1 << (FILTER_BITS - 1)) + input[i] * filter[0];
      for (j = 1; j < filter_len_half; ++j) {
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
643
        sum += (input[i - j] + input[AOMMIN(i + j, length - 1)]) * filter[j];
Jingning Han's avatar
Jingning Han committed
644 645 646 647 648 649
      }
      sum >>= FILTER_BITS;
      *optr++ = clip_pixel_highbd(sum, bd);
    }
  }
}
650
#endif
Jingning Han's avatar
Jingning Han committed
651

652 653 654
static void highbd_resize_multistep(const uint16_t *const input, int length,
                                    uint16_t *output, int olength,
                                    uint16_t *otmp, int bd) {
Jingning Han's avatar
Jingning Han committed
655
  if (length == olength) {
656
    memcpy(output, input, sizeof(output[0]) * length);
Jingning Han's avatar
Jingning Han committed
657 658
    return;
  }
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
659
  const int steps = get_down2_steps(length, olength);
Jingning Han's avatar
Jingning Han committed
660 661 662 663

  if (steps > 0) {
    uint16_t *out = NULL;
    int filteredlength = length;
664 665

    assert(otmp != NULL);
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
666 667
    uint16_t *otmp2 = otmp + get_down2_length(length, 1);
    for (int s = 0; s < steps; ++s) {
Jingning Han's avatar
Jingning Han committed
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
      const int proj_filteredlength = get_down2_length(filteredlength, 1);
      const uint16_t *const in = (s == 0 ? input : out);
      if (s == steps - 1 && proj_filteredlength == olength)
        out = output;
      else
        out = (s & 1 ? otmp2 : otmp);
      if (filteredlength & 1)
        highbd_down2_symodd(in, filteredlength, out, bd);
      else
        highbd_down2_symeven(in, filteredlength, out, bd);
      filteredlength = proj_filteredlength;
    }
    if (filteredlength != olength) {
      highbd_interpolate(out, filteredlength, output, olength, bd);
    }
  } else {
    highbd_interpolate(input, length, output, olength, bd);
  }
}

static void highbd_fill_col_to_arr(uint16_t *img, int stride, int len,
                                   uint16_t *arr) {
  int i;
  uint16_t *iptr = img;
  uint16_t *aptr = arr;
  for (i = 0; i < len; ++i, iptr += stride) {
    *aptr++ = *iptr;
  }
}

static void highbd_fill_arr_to_col(uint16_t *img, int stride, int len,
                                   uint16_t *arr) {
  int i;
  uint16_t *iptr = img;
  uint16_t *aptr = arr;
  for (i = 0; i < len; ++i, iptr += stride) {
    *iptr = *aptr++;
  }
}

Yaowu Xu's avatar
Yaowu Xu committed
708 709 710
void av1_highbd_resize_plane(const uint8_t *const input, int height, int width,
                             int in_stride, uint8_t *output, int height2,
                             int width2, int out_stride, int bd) {
Jingning Han's avatar
Jingning Han committed
711 712
  int i;
  uint16_t *intbuf = (uint16_t *)malloc(sizeof(uint16_t) * width2 * height);
713
  uint16_t *tmpbuf =
Sebastien Alaiwan's avatar
Sebastien Alaiwan committed
714
      (uint16_t *)malloc(sizeof(uint16_t) * AOMMAX(width, height));
715 716
  uint16_t *arrbuf = (uint16_t *)malloc(sizeof(uint16_t) * height);
  uint16_t *arrbuf2 = (uint16_t *)malloc(sizeof(uint16_t) * height2);
717 718
  if (intbuf == NULL || tmpbuf == NULL || arrbuf == NULL || arrbuf2 == NULL)
    goto Error;
Jingning Han's avatar
Jingning Han committed
719 720 721 722 723 724
  for (i = 0; i < height; ++i) {
    highbd_resize_multistep(CONVERT_TO_SHORTPTR(input + in_stride * i), width,
                            intbuf + width2 * i, width2, tmpbuf, bd);
  }
  for (i = 0; i < width2; ++i) {
    highbd_fill_col_to_arr(intbuf + i, width2, height, arrbuf);
725
    highbd_resize_multistep(arrbuf, height, arrbuf2, height2, tmpbuf, bd);
Jingning Han's avatar
Jingning Han committed
726
    highbd_fill_arr_to_col(CONVERT_TO_SHORTPTR(output + i), out_stride, height2,
727
                           arrbuf2);
Jingning Han's avatar
Jingning Han committed
728
  }
729

730
Error:
Jingning Han's avatar
Jingning Han committed
731 732 733
  free(intbuf);
  free(tmpbuf);
  free(arrbuf);
734
  free(arrbuf2);
Jingning Han's avatar
Jingning Han committed
735
}
736
#endif  // CONFIG_HIGHBITDEPTH
Yaowu Xu's avatar
Yaowu Xu committed
737 738 739 740 741 742 743 744 745 746 747

void av1_resize_frame420(const uint8_t *const y, int y_stride,
                         const uint8_t *const u, const uint8_t *const v,
                         int uv_stride, int height, int width, uint8_t *oy,
                         int oy_stride, uint8_t *ou, uint8_t *ov,
                         int ouv_stride, int oheight, int owidth) {
  av1_resize_plane(y, height, width, y_stride, oy, oheight, owidth, oy_stride);
  av1_resize_plane(u, height / 2, width / 2, uv_stride, ou, oheight / 2,
                   owidth / 2, ouv_stride);
  av1_resize_plane(v, height / 2, width / 2, uv_stride, ov, oheight / 2,
                   owidth / 2, ouv_stride);
Jingning Han's avatar
Jingning Han committed
748 749
}

Yaowu Xu's avatar
Yaowu Xu committed
750 751 752 753 754 755 756 757 758 759
void av1_resize_frame422(const uint8_t *const y, int y_stride,
                         const uint8_t *const u, const uint8_t *const v,
                         int uv_stride, int height, int width, uint8_t *oy,
                         int oy_stride, uint8_t *ou, uint8_t *ov,
                         int ouv_stride, int oheight, int owidth) {
  av1_resize_plane(y, height, width, y_stride, oy, oheight, owidth, oy_stride);
  av1_resize_plane(u, height, width / 2, uv_stride, ou, oheight, owidth / 2,
                   ouv_stride);
  av1_resize_plane(v, height, width / 2, uv_stride, ov, oheight, owidth / 2,
                   ouv_stride);
Jingning Han's avatar
Jingning Han committed
760 761
}

Yaowu Xu's avatar
Yaowu Xu committed
762 763 764 765 766 767 768 769 770 771
void av1_resize_frame444(const uint8_t *const y, int y_stride,
                         const uint8_t *const u, const uint8_t *const v,
                         int uv_stride, int height, int width, uint8_t *oy,
                         int oy_stride, uint8_t *ou, uint8_t *ov,
                         int ouv_stride, int oheight, int owidth) {
  av1_resize_plane(y, height, width, y_stride, oy, oheight, owidth, oy_stride);
  av1_resize_plane(u, height, width, uv_stride, ou, oheight, owidth,
                   ouv_stride);
  av1_resize_plane(v, height, width, uv_stride, ov, oheight, owidth,
                   ouv_stride);
Jingning Han's avatar
Jingning Han committed
772 773
}

774
#if CONFIG_HIGHBITDEPTH
Yaowu Xu's avatar
Yaowu Xu committed
775 776 777 778 779 780 781 782 783 784 785 786
void av1_highbd_resize_frame420(const uint8_t *const y, int y_stride,
                                const uint8_t *const u, const uint8_t *const v,
                                int uv_stride, int height, int width,
                                uint8_t *oy, int oy_stride, uint8_t *ou,
                                uint8_t *ov, int ouv_stride, int oheight,
                                int owidth, int bd) {
  av1_highbd_resize_plane(y, height, width, y_stride, oy, oheight, owidth,
                          oy_stride, bd);
  av1_highbd_resize_plane(u, height / 2, width / 2, uv_stride, ou, oheight / 2,
                          owidth / 2, ouv_stride, bd);
  av1_highbd_resize_plane(v, height / 2, width / 2, uv_stride, ov, oheight / 2,
                          owidth / 2, ouv_stride, bd);
Jingning Han's avatar
Jingning Han committed
787 788
}

Yaowu Xu's avatar
Yaowu Xu committed
789 790 791 792 793 794 795 796 797 798 799 800
void av1_highbd_resize_frame422(const uint8_t *const y, int y_stride,
                                const uint8_t *const u, const uint8_t *const v,
                                int uv_stride, int height, int width,
                                uint8_t *oy, int oy_stride, uint8_t *ou,
                                uint8_t *ov, int ouv_stride, int oheight,
                                int owidth, int bd) {
  av1_highbd_resize_plane(y, height, width, y_stride, oy, oheight, owidth,
                          oy_stride, bd);
  av1_highbd_resize_plane(u, height, width / 2, uv_stride, ou, oheight,
                          owidth / 2, ouv_stride, bd);
  av1_highbd_resize_plane(v, height, width / 2, uv_stride, ov, oheight,
                          owidth / 2, ouv_stride, bd);
Jingning Han's avatar
Jingning Han committed
801 802
}

Yaowu Xu's avatar
Yaowu Xu committed
803 804 805 806 807 808 809 810 811 812 813 814
void av1_highbd_resize_frame444(const uint8_t *const y, int y_stride,
                                const uint8_t *const u, const uint8_t *const v,
                                int uv_stride, int height, int width,
                                uint8_t *oy, int oy_stride, uint8_t *ou,
                                uint8_t *ov, int ouv_stride, int oheight,
                                int owidth, int bd) {
  av1_highbd_resize_plane(y, height, width, y_stride, oy, oheight, owidth,
                          oy_stride, bd);
  av1_highbd_resize_plane(u, height, width, uv_stride, ou, oheight, owidth,
                          ouv_stride, bd);
  av1_highbd_resize_plane(v, height, width, uv_stride, ov, oheight, owidth,
                          ouv_stride, bd);
Jingning Han's avatar
Jingning Han committed
815
}
816
#endif  // CONFIG_HIGHBITDEPTH
817 818

#if CONFIG_HIGHBITDEPTH
819 820
void av1_resize_and_extend_frame(const YV12_BUFFER_CONFIG *src,
                                 YV12_BUFFER_CONFIG *dst, int bd) {
821
#else
822 823
void av1_resize_and_extend_frame(const YV12_BUFFER_CONFIG *src,
                                 YV12_BUFFER_CONFIG *dst) {
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
#endif  // CONFIG_HIGHBITDEPTH
  // TODO(dkovalev): replace YV12_BUFFER_CONFIG with aom_image_t
  int i;
  const uint8_t *const srcs[3] = { src->y_buffer, src->u_buffer,
                                   src->v_buffer };
  const int src_strides[3] = { src->y_stride, src->uv_stride, src->uv_stride };
  const int src_widths[3] = { src->y_crop_width, src->uv_crop_width,
                              src->uv_crop_width };
  const int src_heights[3] = { src->y_crop_height, src->uv_crop_height,
                               src->uv_crop_height };
  uint8_t *const dsts[3] = { dst->y_buffer, dst->u_buffer, dst->v_buffer };
  const int dst_strides[3] = { dst->y_stride, dst->uv_stride, dst->uv_stride };
  const int dst_widths[3] = { dst->y_crop_width, dst->uv_crop_width,
                              dst->uv_crop_width };
  const int dst_heights[3] = { dst->y_crop_height, dst->uv_crop_height,
                               dst->uv_crop_height };

  for (i = 0; i < MAX_MB_PLANE; ++i) {
#if CONFIG_HIGHBITDEPTH
    if (src->flags & YV12_FLAG_HIGHBITDEPTH)
      av1_highbd_resize_plane(srcs[i], src_heights[i], src_widths[i],
                              src_strides[i], dsts[i], dst_heights[i],
                              dst_widths[i], dst_strides[i], bd);
    else
#endif  // CONFIG_HIGHBITDEPTH
      av1_resize_plane(srcs[i], src_heights[i], src_widths[i], src_strides[i],
                       dsts[i], dst_heights[i], dst_widths[i], dst_strides[i]);
  }
  aom_extend_frame_borders(dst);
}

YV12_BUFFER_CONFIG *av1_scale_if_required_fast(AV1_COMMON *cm,
                                               YV12_BUFFER_CONFIG *unscaled,
                                               YV12_BUFFER_CONFIG *scaled) {
858 859
  if (cm->width != unscaled->y_crop_width ||
      cm->height != unscaled->y_crop_height) {
860 861 862 863 864 865 866 867 868 869 870 871
    // For 2x2 scaling down.
    aom_scale_frame(unscaled, scaled, unscaled->y_buffer, 9, 2, 1, 2, 1, 0);
    aom_extend_frame_borders(scaled);
    return scaled;
  } else {
    return unscaled;
  }
}

YV12_BUFFER_CONFIG *av1_scale_if_required(AV1_COMMON *cm,
                                          YV12_BUFFER_CONFIG *unscaled,
                                          YV12_BUFFER_CONFIG *scaled) {
872 873
  if (cm->width != unscaled->y_crop_width ||
      cm->height != unscaled->y_crop_height) {
874
#if CONFIG_HIGHBITDEPTH
875
    av1_resize_and_extend_frame(unscaled, scaled, (int)cm->bit_depth);
876
#else
877
    av1_resize_and_extend_frame(unscaled, scaled);
878 879 880 881 882 883
#endif  // CONFIG_HIGHBITDEPTH
    return scaled;
  } else {
    return unscaled;
  }
}
884

885 886 887
void av1_calculate_scaled_size(int *width, int *height, int num, int den) {
  *width = *width * num / den;
  *height = *height * num / den;
888 889
}

890
#if CONFIG_FRAME_SUPERRES
891 892 893 894 895 896 897 898 899 900 901 902 903
// TODO(afergs): Look for in-place upscaling
// TODO(afergs): aom_ vs av1_ functions? Which can I use?
// Upscale decoded image.
void av1_superres_upscale(AV1_COMMON *cm, BufferPool *const pool) {
  if (av1_superres_unscaled(cm)) return;

  YV12_BUFFER_CONFIG copy_buffer;
  memset(&copy_buffer, 0, sizeof(copy_buffer));

  YV12_BUFFER_CONFIG *const frame_to_show = get_frame_new_buffer(cm);

  if (aom_alloc_frame_buffer(&copy_buffer, cm->width, cm->height,
                             cm->subsampling_x, cm->subsampling_y,
904
#if CONFIG_HIGHBITDEPTH
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
                             cm->use_highbitdepth,
#endif  // CONFIG_HIGHBITDEPTH
                             AOM_BORDER_IN_PIXELS, cm->byte_alignment))
    aom_internal_error(&cm->error, AOM_CODEC_MEM_ERROR,
                       "Failed to allocate copy buffer for superres upscaling");

  // Copy function assumes the frames are the same size, doesn't copy bit_depth.
  aom_yv12_copy_frame(frame_to_show, &copy_buffer);
  copy_buffer.bit_depth = frame_to_show->bit_depth;
  assert(copy_buffer.y_crop_width == cm->width);
  assert(copy_buffer.y_crop_height == cm->height);

  // Realloc the current frame buffer at a higher resolution in place.
  if (pool != NULL) {
    // Use callbacks if on the decoder.
    aom_codec_frame_buffer_t *fb =
        &pool->frame_bufs[cm->new_fb_idx].raw_frame_buffer;
    aom_release_frame_buffer_cb_fn_t release_fb_cb = pool->release_fb_cb;
    aom_get_frame_buffer_cb_fn_t cb = pool->get_fb_cb;
    void *cb_priv = pool->cb_priv;

    // Realloc with callback does not release the frame buffer - release first.
    if (release_fb_cb(cb_priv, fb))
      aom_internal_error(
          &cm->error, AOM_CODEC_MEM_ERROR,
          "Failed to free current frame buffer before superres upscaling");

    if (aom_realloc_frame_buffer(
            frame_to_show, cm->superres_upscaled_width,
            cm->superres_upscaled_height, cm->subsampling_x, cm->subsampling_y,
935
#if CONFIG_HIGHBITDEPTH
936 937 938 939 940 941 942 943 944 945 946
            cm->use_highbitdepth,
#endif  // CONFIG_HIGHBITDEPTH
            AOM_BORDER_IN_PIXELS, cm->byte_alignment, fb, cb, cb_priv))
      aom_internal_error(
          &cm->error, AOM_CODEC_MEM_ERROR,
          "Failed to allocate current frame buffer for superres upscaling");
  } else {
    // Don't use callbacks on the encoder.
    if (aom_alloc_frame_buffer(frame_to_show, cm->superres_upscaled_width,
                               cm->superres_upscaled_height, cm->subsampling_x,
                               cm->subsampling_y,
947
#if CONFIG_HIGHBITDEPTH
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
                               cm->use_highbitdepth,
#endif  // CONFIG_HIGHBITDEPTH
                               AOM_BORDER_IN_PIXELS, cm->byte_alignment))
      aom_internal_error(
          &cm->error, AOM_CODEC_MEM_ERROR,
          "Failed to reallocate current frame buffer for superres upscaling");
  }
  // TODO(afergs): verify frame_to_show is correct after realloc
  //               encoder:
  //               decoder:
  frame_to_show->bit_depth = copy_buffer.bit_depth;
  assert(frame_to_show->y_crop_width == cm->superres_upscaled_width);
  assert(frame_to_show->y_crop_height == cm->superres_upscaled_height);

  // Scale up and back into frame_to_show.
  assert(frame_to_show->y_crop_width != cm->width);
  assert(frame_to_show->y_crop_height != cm->height);
#if CONFIG_HIGHBITDEPTH
  av1_resize_and_extend_frame(&copy_buffer, frame_to_show, (int)cm->bit_depth);
#else
  av1_resize_and_extend_frame(&copy_buffer, frame_to_show);
#endif  // CONFIG_HIGHBITDEPTH

  // Free the copy buffer
  aom_free_frame_buffer(&copy_buffer);
}
#endif  // CONFIG_FRAME_SUPERRES