clpf.c 13.8 KB
Newer Older
1
/*
Yaowu Xu's avatar
Yaowu Xu committed
2 3 4 5 6 7 8 9 10
 * 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.
 */
Steinar Midtskogen's avatar
Steinar Midtskogen committed
11

12
#include "av1/common/clpf.h"
13
#include "./aom_dsp_rtcd.h"
Steinar Midtskogen's avatar
Steinar Midtskogen committed
14
#include "aom/aom_image.h"
15
#include "aom_dsp/aom_dsp_common.h"
16

Steinar Midtskogen's avatar
Steinar Midtskogen committed
17 18
int sign(int i) { return i < 0 ? -1 : 1; }

19
int constrain(int x, int s, unsigned int damping) {
Steinar Midtskogen's avatar
Steinar Midtskogen committed
20
  return sign(x) *
21 22
         AOMMAX(0, abs(x) - AOMMAX(0, abs(x) - s +
                                          (abs(x) >> (damping - get_msb(s)))));
Steinar Midtskogen's avatar
Steinar Midtskogen committed
23 24 25
}

int av1_clpf_sample(int X, int A, int B, int C, int D, int E, int F, int G,
26 27 28 29 30
                    int H, int s, unsigned int dmp) {
  int delta = 1 * constrain(A - X, s, dmp) + 3 * constrain(B - X, s, dmp) +
              1 * constrain(C - X, s, dmp) + 3 * constrain(D - X, s, dmp) +
              3 * constrain(E - X, s, dmp) + 1 * constrain(F - X, s, dmp) +
              3 * constrain(G - X, s, dmp) + 1 * constrain(H - X, s, dmp);
31 32
  return (8 + delta - (delta < 0)) >> 4;
}
33

34 35
void aom_clpf_block_c(const uint8_t *src, uint8_t *dst, int sstride,
                      int dstride, int x0, int y0, int sizex, int sizey,
Steinar Midtskogen's avatar
Steinar Midtskogen committed
36
                      unsigned int strength, BOUNDARY_TYPE bt,
37
                      unsigned int damping) {
38
  int x, y;
Steinar Midtskogen's avatar
Steinar Midtskogen committed
39 40 41 42 43
  const int xmin = x0 - !(bt & TILE_LEFT_BOUNDARY) * 2;
  const int ymin = y0 - !(bt & TILE_ABOVE_BOUNDARY) * 2;
  const int xmax = x0 + sizex + !(bt & TILE_RIGHT_BOUNDARY) * 2 - 1;
  const int ymax = y0 + sizey + !(bt & TILE_BOTTOM_BOUNDARY) * 2 - 1;

44 45
  for (y = y0; y < y0 + sizey; y++) {
    for (x = x0; x < x0 + sizex; x++) {
Steinar Midtskogen's avatar
Steinar Midtskogen committed
46 47 48 49 50 51 52 53 54 55
      const int X = src[y * sstride + x];
      const int A = src[AOMMAX(ymin, y - 2) * sstride + x];
      const int B = src[AOMMAX(ymin, y - 1) * sstride + x];
      const int C = src[y * sstride + AOMMAX(xmin, x - 2)];
      const int D = src[y * sstride + AOMMAX(xmin, x - 1)];
      const int E = src[y * sstride + AOMMIN(xmax, x + 1)];
      const int F = src[y * sstride + AOMMIN(xmax, x + 2)];
      const int G = src[AOMMIN(ymax, y + 1) * sstride + x];
      const int H = src[AOMMIN(ymax, y + 2) * sstride + x];
      const int delta =
56
          av1_clpf_sample(X, A, B, C, D, E, F, G, H, strength, damping);
57
      dst[y * dstride + x] = X + delta;
58 59 60 61
    }
  }
}

62 63 64 65
#if CONFIG_AOM_HIGHBITDEPTH
// Identical to aom_clpf_block_c() apart from "src" and "dst".
void aom_clpf_block_hbd_c(const uint16_t *src, uint16_t *dst, int sstride,
                          int dstride, int x0, int y0, int sizex, int sizey,
Steinar Midtskogen's avatar
Steinar Midtskogen committed
66
                          unsigned int strength, BOUNDARY_TYPE bt,
67
                          unsigned int damping) {
68
  int x, y;
Steinar Midtskogen's avatar
Steinar Midtskogen committed
69 70 71 72
  const int xmin = x0 - !(bt & TILE_LEFT_BOUNDARY) * 2;
  const int ymin = y0 - !(bt & TILE_ABOVE_BOUNDARY) * 2;
  const int xmax = x0 + sizex + !(bt & TILE_RIGHT_BOUNDARY) * 2 - 1;
  const int ymax = y0 + sizey + !(bt & TILE_BOTTOM_BOUNDARY) * 2 - 1;
73

74 75
  for (y = y0; y < y0 + sizey; y++) {
    for (x = x0; x < x0 + sizex; x++) {
Steinar Midtskogen's avatar
Steinar Midtskogen committed
76 77 78 79 80 81 82 83 84 85
      const int X = src[y * sstride + x];
      const int A = src[AOMMAX(ymin, y - 2) * sstride + x];
      const int B = src[AOMMAX(ymin, y - 1) * sstride + x];
      const int C = src[y * sstride + AOMMAX(xmin, x - 2)];
      const int D = src[y * sstride + AOMMAX(xmin, x - 1)];
      const int E = src[y * sstride + AOMMIN(xmax, x + 1)];
      const int F = src[y * sstride + AOMMIN(xmax, x + 2)];
      const int G = src[AOMMIN(ymax, y + 1) * sstride + x];
      const int H = src[AOMMIN(ymax, y + 2) * sstride + x];
      const int delta =
86
          av1_clpf_sample(X, A, B, C, D, E, F, G, H, strength, damping);
87 88 89 90 91 92
      dst[y * dstride + x] = X + delta;
    }
  }
}
#endif

93
// Return number of filtered blocks
94 95 96 97 98 99 100
void av1_clpf_frame(
    const YV12_BUFFER_CONFIG *frame, const YV12_BUFFER_CONFIG *org,
    AV1_COMMON *cm, int enable_fb_flag, unsigned int strength,
    unsigned int fb_size_log2, int plane,
    int (*decision)(int, int, const YV12_BUFFER_CONFIG *,
                    const YV12_BUFFER_CONFIG *, const AV1_COMMON *cm, int, int,
                    int, unsigned int, unsigned int, int8_t *, int)) {
101 102
  /* Constrained low-pass filter (CLPF) */
  int c, k, l, m, n;
Steinar Midtskogen's avatar
Steinar Midtskogen committed
103 104 105 106 107 108 109
  const int subx = plane != AOM_PLANE_Y && frame->subsampling_x;
  const int suby = plane != AOM_PLANE_Y && frame->subsampling_y;
  const int bs = (subx || suby) ? 4 : 8;
  const int bslog = get_msb(bs);
  int width = plane != AOM_PLANE_Y ? frame->uv_crop_width : frame->y_crop_width;
  int height =
      plane != AOM_PLANE_Y ? frame->uv_crop_height : frame->y_crop_height;
110
  int xpos, ypos;
Steinar Midtskogen's avatar
Steinar Midtskogen committed
111
  const int sstride = plane != AOM_PLANE_Y ? frame->uv_stride : frame->y_stride;
112
  int dstride = bs;
113 114 115 116 117 118 119 120
  const int num_fb_hor = (width + (1 << fb_size_log2) - 1) >> fb_size_log2;
  const int num_fb_ver = (height + (1 << fb_size_log2) - 1) >> fb_size_log2;
  uint8_t *cache = NULL;
  uint8_t **cache_ptr = NULL;
  uint8_t **cache_dst = NULL;
  int cache_idx = 0;
  const int cache_size = num_fb_hor << (2 * fb_size_log2);
  const int cache_blocks = cache_size / (bs * bs);
Steinar Midtskogen's avatar
Steinar Midtskogen committed
121 122 123 124 125
  uint8_t *src_buffer =
      plane != AOM_PLANE_Y
          ? (plane == AOM_PLANE_U ? frame->u_buffer : frame->v_buffer)
          : frame->y_buffer;
  uint8_t *dst_buffer;
126 127 128 129 130
  // Damping is the filter cut-off log2 point for the constrain function.
  // For instance, if the damping is 5, neighbour differences above 32 will
  // be ignored and half of the strength will be applied for a difference of 16.
  int damping =
      cm->bit_depth - 5 - (plane != AOM_PLANE_Y) + (cm->base_qindex >> 6);
131

Steinar Midtskogen's avatar
Steinar Midtskogen committed
132
// Make buffer space for in-place filtering
133 134
#if CONFIG_AOM_HIGHBITDEPTH
  strength <<= (cm->bit_depth - 8);
135
  CHECK_MEM_ERROR(cm, cache, aom_malloc(cache_size << !!cm->use_highbitdepth));
Steinar Midtskogen's avatar
Steinar Midtskogen committed
136
  dst_buffer = cm->use_highbitdepth ? CONVERT_TO_BYTEPTR(cache) : cache;
137
#else
138
  CHECK_MEM_ERROR(cm, cache, aom_malloc(cache_size));
Steinar Midtskogen's avatar
Steinar Midtskogen committed
139
  dst_buffer = cache;
140
#endif
141 142 143
  CHECK_MEM_ERROR(cm, cache_ptr, aom_malloc(cache_blocks * sizeof(*cache_ptr)));
  CHECK_MEM_ERROR(cm, cache_dst, aom_malloc(cache_blocks * sizeof(*cache_dst)));
  memset(cache_ptr, 0, cache_blocks * sizeof(*cache_dst));
144

145 146 147 148
  // Iterate over all filter blocks
  for (k = 0; k < num_fb_ver; k++) {
    for (l = 0; l < num_fb_hor; l++) {
      int h, w;
149
      int allskip = !(enable_fb_flag && fb_size_log2 == MAX_FB_SIZE_LOG2);
150 151
      const int xoff = l << fb_size_log2;
      const int yoff = k << fb_size_log2;
152 153
      for (m = 0; allskip && m < (1 << fb_size_log2) / bs; m++) {
        for (n = 0; allskip && n < (1 << fb_size_log2) / bs; n++) {
154 155
          xpos = xoff + n * bs;
          ypos = yoff + m * bs;
156 157
          if (xpos < width && ypos < height) {
            allskip &=
Steinar Midtskogen's avatar
Steinar Midtskogen committed
158 159
                cm->mi_grid_visible[(ypos << suby) / MI_SIZE * cm->mi_stride +
                                    (xpos << subx) / MI_SIZE]
160 161
                    ->mbmi.skip;
          }
162 163
        }
      }
164 165 166 167 168 169 170 171

      // Calculate the actual filter block size near frame edges
      h = AOMMIN(height, (k + 1) << fb_size_log2) & ((1 << fb_size_log2) - 1);
      w = AOMMIN(width, (l + 1) << fb_size_log2) & ((1 << fb_size_log2) - 1);
      h += !h << fb_size_log2;
      w += !w << fb_size_log2;
      if (!allskip &&  // Do not filter the block if all is skip encoded
          (!enable_fb_flag ||
172
           // Only called if fb_flag enabled (luma only)
173
           decision(k, l, frame, org, cm, bs, w / bs, h / bs, strength,
174 175
                    fb_size_log2,
                    cm->clpf_blocks + yoff / MIN_FB_SIZE * cm->clpf_stride +
176 177
                        xoff / MIN_FB_SIZE,
                    plane))) {
178
        // Iterate over all smaller blocks inside the filter block
Steinar Midtskogen's avatar
Steinar Midtskogen committed
179 180
        for (m = 0; m < ((h + bs - 1) >> bslog); m++) {
          for (n = 0; n < ((w + bs - 1) >> bslog); n++) {
181
            int sizex, sizey;
182 183
            xpos = xoff + n * bs;
            ypos = yoff + m * bs;
184 185
            sizex = AOMMIN(width - xpos, bs);
            sizey = AOMMIN(height - ypos, bs);
Steinar Midtskogen's avatar
Steinar Midtskogen committed
186 187
            if (!cm->mi_grid_visible[(ypos << suby) / MI_SIZE * cm->mi_stride +
                                     (xpos << subx) / MI_SIZE]
188 189
                     ->mbmi.skip ||
                (enable_fb_flag && fb_size_log2 == MAX_FB_SIZE_LOG2)) {
190 191 192 193 194
              BOUNDARY_TYPE boundary_type =
                  cm->mi[(ypos << suby) / MI_SIZE * cm->mi_stride +
                         (xpos << subx) / MI_SIZE]
                      .mbmi.boundary_info;

195
              // Temporary buffering needed for in-place filtering
196
              if (cache_ptr[cache_idx]) {
197 198
// Copy filtered block back into the frame
#if CONFIG_AOM_HIGHBITDEPTH
199 200
                if (cm->use_highbitdepth) {
                  uint16_t *const d = CONVERT_TO_SHORTPTR(cache_dst[cache_idx]);
201 202 203 204
                  if (sizex == 8) {
                    for (c = 0; c < sizey; c++) {
                      *(uint64_t *)(d + c * sstride) =
                          *(uint64_t *)(cache_ptr[cache_idx] + c * bs * 2);
Steinar Midtskogen's avatar
Steinar Midtskogen committed
205 206
                      *(uint64_t *)(d + c * sstride + 4) =
                          *(uint64_t *)(cache_ptr[cache_idx] + c * bs * 2 + 8);
207 208 209 210 211 212 213 214 215
                    }
                  } else if (sizex == 4) {
                    for (c = 0; c < sizey; c++)
                      *(uint64_t *)(d + c * sstride) =
                          *(uint64_t *)(cache_ptr[cache_idx] + c * bs * 2);
                  } else {
                    for (c = 0; c < sizey; c++)
                      memcpy(d + c * sstride, cache_ptr[cache_idx] + c * bs * 2,
                             sizex);
216
                  }
217
                } else {
218 219
                  if (sizex == 8)
                    for (c = 0; c < sizey; c++)
Steinar Midtskogen's avatar
Steinar Midtskogen committed
220 221
                      *(uint64_t *)(cache_dst[cache_idx] + c * sstride) =
                          *(uint64_t *)(cache_ptr[cache_idx] + c * bs);
222 223
                  else if (sizex == 4)
                    for (c = 0; c < sizey; c++)
Steinar Midtskogen's avatar
Steinar Midtskogen committed
224 225
                      *(uint32_t *)(cache_dst[cache_idx] + c * sstride) =
                          *(uint32_t *)(cache_ptr[cache_idx] + c * bs);
226 227 228 229
                  else
                    for (c = 0; c < sizey; c++)
                      memcpy(cache_dst[cache_idx] + c * sstride,
                             cache_ptr[cache_idx] + c * bs, sizex);
230
                }
231
#else
232 233
                if (sizex == 8)
                  for (c = 0; c < sizey; c++)
Steinar Midtskogen's avatar
Steinar Midtskogen committed
234 235
                    *(uint64_t *)(cache_dst[cache_idx] + c * sstride) =
                        *(uint64_t *)(cache_ptr[cache_idx] + c * bs);
236 237
                else if (sizex == 4)
                  for (c = 0; c < sizey; c++)
Steinar Midtskogen's avatar
Steinar Midtskogen committed
238 239
                    *(uint32_t *)(cache_dst[cache_idx] + c * sstride) =
                        *(uint32_t *)(cache_ptr[cache_idx] + c * bs);
240 241 242 243
                else
                  for (c = 0; c < sizey; c++)
                    memcpy(cache_dst[cache_idx] + c * sstride,
                           cache_ptr[cache_idx] + c * bs, sizex);
244 245 246 247 248
#endif
              }
#if CONFIG_AOM_HIGHBITDEPTH
              if (cm->use_highbitdepth) {
                cache_ptr[cache_idx] = cache + cache_idx * bs * bs * 2;
Steinar Midtskogen's avatar
Steinar Midtskogen committed
249
                dst_buffer =
250 251
                    CONVERT_TO_BYTEPTR(cache_ptr[cache_idx]) - ypos * bs - xpos;
              } else {
252
                cache_ptr[cache_idx] = cache + cache_idx * bs * bs;
Steinar Midtskogen's avatar
Steinar Midtskogen committed
253
                dst_buffer = cache_ptr[cache_idx] - ypos * bs - xpos;
254
              }
255 256
#else
              cache_ptr[cache_idx] = cache + cache_idx * bs * bs;
Steinar Midtskogen's avatar
Steinar Midtskogen committed
257
              dst_buffer = cache_ptr[cache_idx] - ypos * bs - xpos;
258
#endif
Steinar Midtskogen's avatar
Steinar Midtskogen committed
259
              cache_dst[cache_idx] = src_buffer + ypos * sstride + xpos;
260
              if (++cache_idx >= cache_blocks) cache_idx = 0;
261

262 263 264
// Apply the filter
#if CONFIG_AOM_HIGHBITDEPTH
              if (cm->use_highbitdepth) {
Steinar Midtskogen's avatar
Steinar Midtskogen committed
265 266
                aom_clpf_block_hbd(CONVERT_TO_SHORTPTR(src_buffer),
                                   CONVERT_TO_SHORTPTR(dst_buffer), sstride,
267
                                   dstride, xpos, ypos, sizex, sizey, strength,
268
                                   boundary_type, damping);
269
              } else {
Steinar Midtskogen's avatar
Steinar Midtskogen committed
270
                aom_clpf_block(src_buffer, dst_buffer, sstride, dstride, xpos,
Steinar Midtskogen's avatar
Steinar Midtskogen committed
271
                               ypos, sizex, sizey, strength, boundary_type,
272
                               damping);
273 274
              }
#else
Steinar Midtskogen's avatar
Steinar Midtskogen committed
275
              aom_clpf_block(src_buffer, dst_buffer, sstride, dstride, xpos,
Steinar Midtskogen's avatar
Steinar Midtskogen committed
276
                             ypos, sizex, sizey, strength, boundary_type,
277
                             damping);
278
#endif
279 280
            }
          }
281 282 283 284 285
        }
      }
    }
  }

286 287 288
  // Copy remaining blocks into the frame
  for (cache_idx = 0; cache_idx < cache_blocks && cache_ptr[cache_idx];
       cache_idx++) {
289
#if CONFIG_AOM_HIGHBITDEPTH
290 291 292 293 294
    if (cm->use_highbitdepth) {
      uint16_t *const d = CONVERT_TO_SHORTPTR(cache_dst[cache_idx]);
      for (c = 0; c < bs; c++) {
        *(uint64_t *)(d + c * sstride) =
            *(uint64_t *)(cache_ptr[cache_idx] + c * bs * 2);
Steinar Midtskogen's avatar
Steinar Midtskogen committed
295 296 297
        if (bs == 8)
          *(uint64_t *)(d + c * sstride + 4) =
              *(uint64_t *)(cache_ptr[cache_idx] + c * bs * 2 + 8);
298
      }
299
    } else {
300
      for (c = 0; c < bs; c++)
Steinar Midtskogen's avatar
Steinar Midtskogen committed
301 302 303 304 305 306
        if (bs == 4)
          *(uint32_t *)(cache_dst[cache_idx] + c * sstride) =
              *(uint32_t *)(cache_ptr[cache_idx] + c * bs);
        else
          *(uint64_t *)(cache_dst[cache_idx] + c * sstride) =
              *(uint64_t *)(cache_ptr[cache_idx] + c * bs);
307
    }
308 309
#else
    for (c = 0; c < bs; c++)
Steinar Midtskogen's avatar
Steinar Midtskogen committed
310 311 312 313 314 315
      if (bs == 4)
        *(uint32_t *)(cache_dst[cache_idx] + c * sstride) =
            *(uint32_t *)(cache_ptr[cache_idx] + c * bs);
      else
        *(uint64_t *)(cache_dst[cache_idx] + c * sstride) =
            *(uint64_t *)(cache_ptr[cache_idx] + c * bs);
316
#endif
317 318
  }

319 320 321
  aom_free(cache);
  aom_free(cache_ptr);
  aom_free(cache_dst);
322
}