blob: e93ca491003ad11e35ed023bde35db04b1747e23 [file] [log] [blame]
/*
* 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.
*
*/
#include <math.h>
#include "./aom_config.h"
#include "./aom_dsp_rtcd.h"
#include "./aom_scale_rtcd.h"
#include "av1/common/onyxc_int.h"
#if CONFIG_FRAME_SUPERRES
#include "av1/common/resize.h"
#endif
#include "av1/common/restoration.h"
#include "aom_dsp/aom_dsp_common.h"
#include "aom_mem/aom_mem.h"
#include "aom_ports/mem.h"
const sgr_params_type sgr_params[SGRPROJ_PARAMS] = {
#if USE_HIGHPASS_IN_SGRPROJ
// corner, edge, r2, eps2
{ -1, 2, 1, 1 }, { -1, 2, 1, 2 }, { -1, 2, 1, 3 }, { -1, 2, 1, 4 },
{ -1, 2, 1, 5 }, { -2, 3, 1, 2 }, { -2, 3, 1, 3 }, { -2, 3, 1, 4 },
{ -2, 3, 1, 5 }, { -2, 3, 1, 6 }, { -3, 4, 1, 3 }, { -3, 4, 1, 4 },
{ -3, 4, 1, 5 }, { -3, 4, 1, 6 }, { -3, 4, 1, 7 }, { -3, 4, 1, 8 }
#else
// r1, eps1, r2, eps2
#if MAX_RADIUS == 2
{ 2, 12, 1, 4 }, { 2, 15, 1, 6 }, { 2, 18, 1, 8 }, { 2, 20, 1, 9 },
{ 2, 22, 1, 10 }, { 2, 25, 1, 11 }, { 2, 35, 1, 12 }, { 2, 45, 1, 13 },
{ 2, 55, 1, 14 }, { 2, 65, 1, 15 }, { 2, 75, 1, 16 }, { 2, 30, 1, 6 },
{ 2, 50, 1, 12 }, { 2, 60, 1, 13 }, { 2, 70, 1, 14 }, { 2, 80, 1, 15 },
#else
{ 2, 12, 1, 4 }, { 2, 15, 1, 6 }, { 2, 18, 1, 8 }, { 2, 20, 1, 9 },
{ 2, 22, 1, 10 }, { 2, 25, 1, 11 }, { 2, 35, 1, 12 }, { 2, 45, 1, 13 },
{ 2, 55, 1, 14 }, { 2, 65, 1, 15 }, { 2, 75, 1, 16 }, { 3, 30, 1, 10 },
{ 3, 50, 1, 12 }, { 3, 50, 2, 25 }, { 3, 60, 2, 35 }, { 3, 70, 2, 45 },
#endif // MAX_RADIUS == 2
#endif
};
#if CONFIG_MAX_TILE
static void tile_width_and_height(const AV1_COMMON *cm, int is_uv, int sb_w,
int sb_h, int *px_w, int *px_h) {
const int scaled_sb_w = sb_w << MAX_MIB_SIZE_LOG2;
const int scaled_sb_h = sb_h << MAX_MIB_SIZE_LOG2;
const int ss_x = is_uv && cm->subsampling_x;
const int ss_y = is_uv && cm->subsampling_y;
*px_w = (scaled_sb_w + ss_x) >> ss_x;
*px_h = (scaled_sb_h + ss_y) >> ss_y;
#if CONFIG_FRAME_SUPERRES
if (!av1_superres_unscaled(cm)) {
av1_calculate_unscaled_superres_size(px_w, px_h,
cm->superres_scale_denominator);
}
#endif // CONFIG_FRAME_SUPERRES
}
#endif // CONFIG_MAX_TILE
// Count horizontal or vertical units per tile (use a width or height for
// tile_size, respectively). We basically want to divide the tile size by the
// size of a restoration unit. Rather than rounding up unconditionally as you
// might expect, we round to nearest, which models the way a right or bottom
// restoration unit can extend to up to 150% its normal width or height. The
// max with 1 is to deal with tiles that are smaller than half of a restoration
// unit.
static int count_units_in_tile(int unit_size, int tile_size) {
return AOMMAX((tile_size + (unit_size >> 1)) / unit_size, 1);
}
void av1_alloc_restoration_struct(AV1_COMMON *cm, RestorationInfo *rsi,
int is_uv) {
#if CONFIG_MAX_TILE
// We need to allocate enough space for restoration units to cover the
// largest tile. Without CONFIG_MAX_TILE, this is always the tile at the
// top-left and we can use av1_get_tile_rect. With CONFIG_MAX_TILE, we have
// to do the computation ourselves, iterating over the tiles and keeping
// track of the largest width and height, then upscaling.
int max_sb_w = 0;
int max_sb_h = 0;
for (int i = 0; i < cm->tile_cols; ++i) {
const int sb_w = cm->tile_col_start_sb[i + 1] - cm->tile_col_start_sb[i];
max_sb_w = AOMMAX(max_sb_w, sb_w);
}
for (int i = 0; i < cm->tile_rows; ++i) {
const int sb_h = cm->tile_row_start_sb[i + 1] - cm->tile_row_start_sb[i];
max_sb_h = AOMMAX(max_sb_h, sb_h);
}
int max_tile_w, max_tile_h;
tile_width_and_height(cm, is_uv, max_sb_w, max_sb_h, &max_tile_w,
&max_tile_h);
#else
TileInfo tile_info;
av1_tile_init(&tile_info, cm, 0, 0);
const AV1PixelRect tile_rect = av1_get_tile_rect(&tile_info, cm, is_uv);
assert(tile_rect.left == 0 && tile_rect.top == 0);
const int max_tile_w = tile_rect.right;
const int max_tile_h = tile_rect.bottom;
#endif // CONFIG_MAX_TILE
// To calculate hpertile and vpertile (horizontal and vertical units per
// tile), we basically want to divide the largest tile width or height by the
// size of a restoration unit. Rather than rounding up unconditionally as you
// might expect, we round to nearest, which models the way a right or bottom
// restoration unit can extend to up to 150% its normal width or height. The
// max with 1 is to deal with tiles that are smaller than half of a
// restoration unit.
const int unit_size = rsi->restoration_unit_size;
const int hpertile = count_units_in_tile(unit_size, max_tile_w);
const int vpertile = count_units_in_tile(unit_size, max_tile_h);
rsi->units_per_tile = hpertile * vpertile;
rsi->horz_units_per_tile = hpertile;
rsi->vert_units_per_tile = vpertile;
const int ntiles = cm->tile_rows * cm->tile_cols;
const int nunits = ntiles * rsi->units_per_tile;
aom_free(rsi->unit_info);
CHECK_MEM_ERROR(cm, rsi->unit_info, (RestorationUnitInfo *)aom_malloc(
sizeof(*rsi->unit_info) * nunits));
}
void av1_free_restoration_struct(RestorationInfo *rst_info) {
aom_free(rst_info->unit_info);
rst_info->unit_info = NULL;
}
// TODO(debargha): This table can be substantially reduced since only a few
// values are actually used.
int sgrproj_mtable[MAX_EPS][MAX_NELEM];
static void GenSgrprojVtable() {
int e, n;
for (e = 1; e <= MAX_EPS; ++e)
for (n = 1; n <= MAX_NELEM; ++n) {
const int n2e = n * n * e;
sgrproj_mtable[e - 1][n - 1] =
(((1 << SGRPROJ_MTABLE_BITS) + n2e / 2) / n2e);
}
}
void av1_loop_restoration_precal() { GenSgrprojVtable(); }
static void extend_frame_lowbd(uint8_t *data, int width, int height, int stride,
int border_horz, int border_vert) {
uint8_t *data_p;
int i;
for (i = 0; i < height; ++i) {
data_p = data + i * stride;
memset(data_p - border_horz, data_p[0], border_horz);
memset(data_p + width, data_p[width - 1], border_horz);
}
data_p = data - border_horz;
for (i = -border_vert; i < 0; ++i) {
memcpy(data_p + i * stride, data_p, width + 2 * border_horz);
}
for (i = height; i < height + border_vert; ++i) {
memcpy(data_p + i * stride, data_p + (height - 1) * stride,
width + 2 * border_horz);
}
}
#if CONFIG_HIGHBITDEPTH
static void extend_frame_highbd(uint16_t *data, int width, int height,
int stride, int border_horz, int border_vert) {
uint16_t *data_p;
int i, j;
for (i = 0; i < height; ++i) {
data_p = data + i * stride;
for (j = -border_horz; j < 0; ++j) data_p[j] = data_p[0];
for (j = width; j < width + border_horz; ++j) data_p[j] = data_p[width - 1];
}
data_p = data - border_horz;
for (i = -border_vert; i < 0; ++i) {
memcpy(data_p + i * stride, data_p,
(width + 2 * border_horz) * sizeof(uint16_t));
}
for (i = height; i < height + border_vert; ++i) {
memcpy(data_p + i * stride, data_p + (height - 1) * stride,
(width + 2 * border_horz) * sizeof(uint16_t));
}
}
#endif
void extend_frame(uint8_t *data, int width, int height, int stride,
int border_horz, int border_vert, int highbd) {
#if !CONFIG_HIGHBITDEPTH
assert(highbd == 0);
(void)highbd;
#else
if (highbd)
extend_frame_highbd(CONVERT_TO_SHORTPTR(data), width, height, stride,
border_horz, border_vert);
else
#endif
extend_frame_lowbd(data, width, height, stride, border_horz, border_vert);
}
static void copy_tile_lowbd(int width, int height, const uint8_t *src,
int src_stride, uint8_t *dst, int dst_stride) {
for (int i = 0; i < height; ++i)
memcpy(dst + i * dst_stride, src + i * src_stride, width);
}
#if CONFIG_HIGHBITDEPTH
static void copy_tile_highbd(int width, int height, const uint16_t *src,
int src_stride, uint16_t *dst, int dst_stride) {
for (int i = 0; i < height; ++i)
memcpy(dst + i * dst_stride, src + i * src_stride, width * sizeof(*dst));
}
#endif
static void copy_tile(int width, int height, const uint8_t *src, int src_stride,
uint8_t *dst, int dst_stride, int highbd) {
#if !CONFIG_HIGHBITDEPTH
assert(highbd == 0);
(void)highbd;
#else
if (highbd)
copy_tile_highbd(width, height, CONVERT_TO_SHORTPTR(src), src_stride,
CONVERT_TO_SHORTPTR(dst), dst_stride);
else
#endif
copy_tile_lowbd(width, height, src, src_stride, dst, dst_stride);
}
#if CONFIG_STRIPED_LOOP_RESTORATION
#define REAL_PTR(hbd, d) ((hbd) ? (uint8_t *)CONVERT_TO_SHORTPTR(d) : (d))
// With striped loop restoration, the filtering for each 64-pixel stripe gets
// most of its input from the output of CDEF (stored in data8), but pixels just
// above and below the stripe come straight from the deblocker. These have been
// stored away in separate buffers.
//
// This function modifies data8 (which was the output from CDEF) by copying in
// the boundary pixels. Before doing so, it saves the pixels that get
// overwritten into a temporary buffer. They will be restored again by
// restore_processing_stripe_boundary.
//
// limits gives the rectangular limits of the remaining stripes for the current
// restoration unit. rsb is the stored stripe boundaries (the saved output from
// the deblocker). stripe_height is the height of each stripe. ss_y is true if
// we're on a chroma plane with vertical subsampling. use_highbd is true if the
// data has 2 bytes per pixel. rlbs contain scratch buffers to hold the CDEF
// data (written back to the frame by restore_processing_stripe_boundary)
static int setup_processing_stripe_boundary(
const RestorationTileLimits *limits, const RestorationStripeBoundaries *rsb,
int stripe_height, int ss_y, int use_highbd, uint8_t *data8, int stride,
RestorationLineBuffers *rlbs) {
// Which stripe is this? limits->v_start is the top of the stripe in pixel
// units, but we add tile_offset to get the number of pixels from the top of
// the first stripe, which lies off the image.
const int tile_offset = RESTORATION_TILE_OFFSET >> ss_y;
const int stripe_index = (limits->v_start + tile_offset) / stripe_height;
// Horizontal offsets within the line buffers. The buffer logically starts at
// column -RESTORATION_EXTRA_HORZ. We'll start our copy from the column
// limits->h_start - RESTORATION_EXTRA_HORZ and copy up to the column
// limits->h_end + RESTORATION_EXTRA_HORZ.
const int buf_stride = rsb->stripe_boundary_stride;
const int buf_x0_off = limits->h_start;
const int line_width =
(limits->h_end - limits->h_start) + 2 * RESTORATION_EXTRA_HORZ;
const int line_size = line_width << use_highbd;
const int data_x0_off = limits->h_start - RESTORATION_EXTRA_HORZ;
assert(CONFIG_HIGHBITDEPTH || !use_highbd);
// Replace the pixels above the top of the stripe, unless this is the top of
// the image.
// We expand 2 lines from rsb->stripe_boundary_above to fill 3 lines of above
// pixels. This is done by duplicating the topmost of the 2 lines.
if (stripe_index > 0) {
const int above_buf_y = 2 * (stripe_index - 1);
uint8_t *data8_tl = data8 + (limits->v_start - 3) * stride + data_x0_off;
for (int i = 0; i < 3; ++i) {
const int src_row = AOMMAX(0, i - 1);
const int buf_off = buf_x0_off + (above_buf_y + src_row) * buf_stride;
const uint8_t *src = rsb->stripe_boundary_above + (buf_off << use_highbd);
uint8_t *dst8 = data8_tl + i * stride;
// Save old pixels, then replace with data from boundary_above_buf
memcpy(rlbs->tmp_save_above[i], REAL_PTR(use_highbd, dst8), line_size);
memcpy(REAL_PTR(use_highbd, dst8), src, line_size);
}
}
// Replace the pixels below the bottom of the stripe if necessary. This might
// not be needed if the stripe is less than stripe_height high (which might
// happen on the bottom of a loop restoration unit), in which case
// rows_needed_below might be negative.
// Similarly to above, we expand 2 lines from rb->stripe_boundary_below into
// 3 lines of below pixels. This time we duplicate the bottommost row.
const int stripe_bottom = stripe_height * (1 + stripe_index) - tile_offset;
const int rows_needed_below = AOMMIN(limits->v_end + 3 - stripe_bottom, 3);
const int below_buf_y = 2 * stripe_index;
uint8_t *data8_bl = data8 + stripe_bottom * stride + data_x0_off;
for (int i = 0; i < rows_needed_below; ++i) {
const int src_row = AOMMIN(1, i);
const int buf_off = buf_x0_off + (below_buf_y + src_row) * buf_stride;
const uint8_t *src = rsb->stripe_boundary_below + (buf_off << use_highbd);
uint8_t *dst8 = data8_bl + i * stride;
// Save old pixels, then replace with data from boundary_below_buf
memcpy(rlbs->tmp_save_below[i], REAL_PTR(use_highbd, dst8), line_size);
memcpy(REAL_PTR(use_highbd, dst8), src, line_size);
}
// Finally, return the actual height of this stripe.
return AOMMIN(limits->v_end, stripe_bottom) - limits->v_start;
}
// This function restores the boundary lines modified by
// setup_processing_stripe_boundary.
static void restore_processing_stripe_boundary(
const RestorationTileLimits *limits, const RestorationLineBuffers *rlbs,
int stripe_height, int ss_y, int use_highbd, uint8_t *data8, int stride) {
const int tile_offset = RESTORATION_TILE_OFFSET >> ss_y;
const int stripe_index = (limits->v_start + tile_offset) / stripe_height;
const int line_width =
(limits->h_end - limits->h_start) + 2 * RESTORATION_EXTRA_HORZ;
const int line_size = line_width << use_highbd;
const int data_x0_off = limits->h_start - RESTORATION_EXTRA_HORZ;
assert(CONFIG_HIGHBITDEPTH || !use_highbd);
if (stripe_index > 0) {
uint8_t *data8_tl = data8 + (limits->v_start - 3) * stride + data_x0_off;
for (int i = 0; i < 3; ++i) {
uint8_t *dst8 = data8_tl + i * stride;
// Save old pixels, then replace with data from boundary_above_buf
memcpy(REAL_PTR(use_highbd, dst8), rlbs->tmp_save_above[i], line_size);
}
}
const int stripe_bottom = stripe_height * (1 + stripe_index) - tile_offset;
const int rows_needed_below = AOMMIN(limits->v_end + 3 - stripe_bottom, 3);
uint8_t *data8_bl = data8 + stripe_bottom * stride + data_x0_off;
for (int i = 0; i < rows_needed_below; ++i) {
uint8_t *dst8 = data8_bl + i * stride;
// Save old pixels, then replace with data from boundary_below_buf
memcpy(REAL_PTR(use_highbd, dst8), rlbs->tmp_save_below[i], line_size);
}
}
#undef REAL_PTR
#endif
#if USE_WIENER_HIGH_INTERMEDIATE_PRECISION
#define wiener_convolve8_add_src aom_convolve8_add_src_hip
#else
#define wiener_convolve8_add_src aom_convolve8_add_src
#endif
static void wiener_filter_stripe(const RestorationUnitInfo *rui,
int stripe_width, int stripe_height,
int procunit_width, const uint8_t *src,
int src_stride, uint8_t *dst, int dst_stride,
int32_t *tmpbuf, int bit_depth) {
(void)tmpbuf;
(void)bit_depth;
assert(bit_depth == 8);
for (int j = 0; j < stripe_width; j += procunit_width) {
int w = AOMMIN(procunit_width, (stripe_width - j + 15) & ~15);
const uint8_t *src_p = src + j;
uint8_t *dst_p = dst + j;
wiener_convolve8_add_src(src_p, src_stride, dst_p, dst_stride,
rui->wiener_info.hfilter, 16,
rui->wiener_info.vfilter, 16, w, stripe_height);
}
}
/* Calculate windowed sums (if sqr=0) or sums of squares (if sqr=1)
over the input. The window is of size (2r + 1)x(2r + 1), and we
specialize to r = 1, 2, 3. A default function is used for r > 3.
Each loop follows the same format: We keep a window's worth of input
in individual variables and select data out of that as appropriate.
*/
static void boxsum1(int32_t *src, int width, int height, int src_stride,
int sqr, int32_t *dst, int dst_stride) {
int i, j, a, b, c;
// Vertical sum over 3-pixel regions, from src into dst.
if (!sqr) {
for (j = 0; j < width; ++j) {
a = src[j];
b = src[src_stride + j];
c = src[2 * src_stride + j];
dst[j] = a + b;
for (i = 1; i < height - 2; ++i) {
// Loop invariant: At the start of each iteration,
// a = src[(i - 1) * src_stride + j]
// b = src[(i ) * src_stride + j]
// c = src[(i + 1) * src_stride + j]
dst[i * dst_stride + j] = a + b + c;
a = b;
b = c;
c = src[(i + 2) * src_stride + j];
}
dst[i * dst_stride + j] = a + b + c;
dst[(i + 1) * dst_stride + j] = b + c;
}
} else {
for (j = 0; j < width; ++j) {
a = src[j] * src[j];
b = src[src_stride + j] * src[src_stride + j];
c = src[2 * src_stride + j] * src[2 * src_stride + j];
dst[j] = a + b;
for (i = 1; i < height - 2; ++i) {
dst[i * dst_stride + j] = a + b + c;
a = b;
b = c;
c = src[(i + 2) * src_stride + j] * src[(i + 2) * src_stride + j];
}
dst[i * dst_stride + j] = a + b + c;
dst[(i + 1) * dst_stride + j] = b + c;
}
}
// Horizontal sum over 3-pixel regions of dst
for (i = 0; i < height; ++i) {
a = dst[i * dst_stride];
b = dst[i * dst_stride + 1];
c = dst[i * dst_stride + 2];
dst[i * dst_stride] = a + b;
for (j = 1; j < width - 2; ++j) {
// Loop invariant: At the start of each iteration,
// a = src[i * src_stride + (j - 1)]
// b = src[i * src_stride + (j )]
// c = src[i * src_stride + (j + 1)]
dst[i * dst_stride + j] = a + b + c;
a = b;
b = c;
c = dst[i * dst_stride + (j + 2)];
}
dst[i * dst_stride + j] = a + b + c;
dst[i * dst_stride + (j + 1)] = b + c;
}
}
static void boxsum2(int32_t *src, int width, int height, int src_stride,
int sqr, int32_t *dst, int dst_stride) {
int i, j, a, b, c, d, e;
// Vertical sum over 5-pixel regions, from src into dst.
if (!sqr) {
for (j = 0; j < width; ++j) {
a = src[j];
b = src[src_stride + j];
c = src[2 * src_stride + j];
d = src[3 * src_stride + j];
e = src[4 * src_stride + j];
dst[j] = a + b + c;
dst[dst_stride + j] = a + b + c + d;
for (i = 2; i < height - 3; ++i) {
// Loop invariant: At the start of each iteration,
// a = src[(i - 2) * src_stride + j]
// b = src[(i - 1) * src_stride + j]
// c = src[(i ) * src_stride + j]
// d = src[(i + 1) * src_stride + j]
// e = src[(i + 2) * src_stride + j]
dst[i * dst_stride + j] = a + b + c + d + e;
a = b;
b = c;
c = d;
d = e;
e = src[(i + 3) * src_stride + j];
}
dst[i * dst_stride + j] = a + b + c + d + e;
dst[(i + 1) * dst_stride + j] = b + c + d + e;
dst[(i + 2) * dst_stride + j] = c + d + e;
}
} else {
for (j = 0; j < width; ++j) {
a = src[j] * src[j];
b = src[src_stride + j] * src[src_stride + j];
c = src[2 * src_stride + j] * src[2 * src_stride + j];
d = src[3 * src_stride + j] * src[3 * src_stride + j];
e = src[4 * src_stride + j] * src[4 * src_stride + j];
dst[j] = a + b + c;
dst[dst_stride + j] = a + b + c + d;
for (i = 2; i < height - 3; ++i) {
dst[i * dst_stride + j] = a + b + c + d + e;
a = b;
b = c;
c = d;
d = e;
e = src[(i + 3) * src_stride + j] * src[(i + 3) * src_stride + j];
}
dst[i * dst_stride + j] = a + b + c + d + e;
dst[(i + 1) * dst_stride + j] = b + c + d + e;
dst[(i + 2) * dst_stride + j] = c + d + e;
}
}
// Horizontal sum over 5-pixel regions of dst
for (i = 0; i < height; ++i) {
a = dst[i * dst_stride];
b = dst[i * dst_stride + 1];
c = dst[i * dst_stride + 2];
d = dst[i * dst_stride + 3];
e = dst[i * dst_stride + 4];
dst[i * dst_stride] = a + b + c;
dst[i * dst_stride + 1] = a + b + c + d;
for (j = 2; j < width - 3; ++j) {
// Loop invariant: At the start of each iteration,
// a = src[i * src_stride + (j - 2)]
// b = src[i * src_stride + (j - 1)]
// c = src[i * src_stride + (j )]
// d = src[i * src_stride + (j + 1)]
// e = src[i * src_stride + (j + 2)]
dst[i * dst_stride + j] = a + b + c + d + e;
a = b;
b = c;
c = d;
d = e;
e = dst[i * dst_stride + (j + 3)];
}
dst[i * dst_stride + j] = a + b + c + d + e;
dst[i * dst_stride + (j + 1)] = b + c + d + e;
dst[i * dst_stride + (j + 2)] = c + d + e;
}
}
static void boxsum3(int32_t *src, int width, int height, int src_stride,
int sqr, int32_t *dst, int dst_stride) {
int i, j, a, b, c, d, e, f, g;
// Vertical sum over 7-pixel regions, from src into dst.
if (!sqr) {
for (j = 0; j < width; ++j) {
a = src[j];
b = src[1 * src_stride + j];
c = src[2 * src_stride + j];
d = src[3 * src_stride + j];
e = src[4 * src_stride + j];
f = src[5 * src_stride + j];
g = src[6 * src_stride + j];
dst[j] = a + b + c + d;
dst[dst_stride + j] = a + b + c + d + e;
dst[2 * dst_stride + j] = a + b + c + d + e + f;
for (i = 3; i < height - 4; ++i) {
dst[i * dst_stride + j] = a + b + c + d + e + f + g;
a = b;
b = c;
c = d;
d = e;
e = f;
f = g;
g = src[(i + 4) * src_stride + j];
}
dst[i * dst_stride + j] = a + b + c + d + e + f + g;
dst[(i + 1) * dst_stride + j] = b + c + d + e + f + g;
dst[(i + 2) * dst_stride + j] = c + d + e + f + g;
dst[(i + 3) * dst_stride + j] = d + e + f + g;
}
} else {
for (j = 0; j < width; ++j) {
a = src[j] * src[j];
b = src[1 * src_stride + j] * src[1 * src_stride + j];
c = src[2 * src_stride + j] * src[2 * src_stride + j];
d = src[3 * src_stride + j] * src[3 * src_stride + j];
e = src[4 * src_stride + j] * src[4 * src_stride + j];
f = src[5 * src_stride + j] * src[5 * src_stride + j];
g = src[6 * src_stride + j] * src[6 * src_stride + j];
dst[j] = a + b + c + d;
dst[dst_stride + j] = a + b + c + d + e;
dst[2 * dst_stride + j] = a + b + c + d + e + f;
for (i = 3; i < height - 4; ++i) {
dst[i * dst_stride + j] = a + b + c + d + e + f + g;
a = b;
b = c;
c = d;
d = e;
e = f;
f = g;
g = src[(i + 4) * src_stride + j] * src[(i + 4) * src_stride + j];
}
dst[i * dst_stride + j] = a + b + c + d + e + f + g;
dst[(i + 1) * dst_stride + j] = b + c + d + e + f + g;
dst[(i + 2) * dst_stride + j] = c + d + e + f + g;
dst[(i + 3) * dst_stride + j] = d + e + f + g;
}
}
// Horizontal sum over 7-pixel regions of dst
for (i = 0; i < height; ++i) {
a = dst[i * dst_stride];
b = dst[i * dst_stride + 1];
c = dst[i * dst_stride + 2];
d = dst[i * dst_stride + 3];
e = dst[i * dst_stride + 4];
f = dst[i * dst_stride + 5];
g = dst[i * dst_stride + 6];
dst[i * dst_stride] = a + b + c + d;
dst[i * dst_stride + 1] = a + b + c + d + e;
dst[i * dst_stride + 2] = a + b + c + d + e + f;
for (j = 3; j < width - 4; ++j) {
dst[i * dst_stride + j] = a + b + c + d + e + f + g;
a = b;
b = c;
c = d;
d = e;
e = f;
f = g;
g = dst[i * dst_stride + (j + 4)];
}
dst[i * dst_stride + j] = a + b + c + d + e + f + g;
dst[i * dst_stride + (j + 1)] = b + c + d + e + f + g;
dst[i * dst_stride + (j + 2)] = c + d + e + f + g;
dst[i * dst_stride + (j + 3)] = d + e + f + g;
}
}
// Generic version for any r. To be removed after experiments are done.
static void boxsumr(int32_t *src, int width, int height, int src_stride, int r,
int sqr, int32_t *dst, int dst_stride) {
int32_t *tmp = aom_malloc(width * height * sizeof(*tmp));
int tmp_stride = width;
int i, j;
if (sqr) {
for (j = 0; j < width; ++j) tmp[j] = src[j] * src[j];
for (j = 0; j < width; ++j)
for (i = 1; i < height; ++i)
tmp[i * tmp_stride + j] =
tmp[(i - 1) * tmp_stride + j] +
src[i * src_stride + j] * src[i * src_stride + j];
} else {
memcpy(tmp, src, sizeof(*tmp) * width);
for (j = 0; j < width; ++j)
for (i = 1; i < height; ++i)
tmp[i * tmp_stride + j] =
tmp[(i - 1) * tmp_stride + j] + src[i * src_stride + j];
}
for (i = 0; i <= r; ++i)
memcpy(&dst[i * dst_stride], &tmp[(i + r) * tmp_stride],
sizeof(*tmp) * width);
for (i = r + 1; i < height - r; ++i)
for (j = 0; j < width; ++j)
dst[i * dst_stride + j] =
tmp[(i + r) * tmp_stride + j] - tmp[(i - r - 1) * tmp_stride + j];
for (i = height - r; i < height; ++i)
for (j = 0; j < width; ++j)
dst[i * dst_stride + j] = tmp[(height - 1) * tmp_stride + j] -
tmp[(i - r - 1) * tmp_stride + j];
for (i = 0; i < height; ++i) tmp[i * tmp_stride] = dst[i * dst_stride];
for (i = 0; i < height; ++i)
for (j = 1; j < width; ++j)
tmp[i * tmp_stride + j] =
tmp[i * tmp_stride + j - 1] + dst[i * src_stride + j];
for (j = 0; j <= r; ++j)
for (i = 0; i < height; ++i)
dst[i * dst_stride + j] = tmp[i * tmp_stride + j + r];
for (j = r + 1; j < width - r; ++j)
for (i = 0; i < height; ++i)
dst[i * dst_stride + j] =
tmp[i * tmp_stride + j + r] - tmp[i * tmp_stride + j - r - 1];
for (j = width - r; j < width; ++j)
for (i = 0; i < height; ++i)
dst[i * dst_stride + j] =
tmp[i * tmp_stride + width - 1] - tmp[i * tmp_stride + j - r - 1];
aom_free(tmp);
}
static void boxsum(int32_t *src, int width, int height, int src_stride, int r,
int sqr, int32_t *dst, int dst_stride) {
if (r == 1)
boxsum1(src, width, height, src_stride, sqr, dst, dst_stride);
else if (r == 2)
boxsum2(src, width, height, src_stride, sqr, dst, dst_stride);
else if (r == 3)
boxsum3(src, width, height, src_stride, sqr, dst, dst_stride);
else
boxsumr(src, width, height, src_stride, r, sqr, dst, dst_stride);
}
static void boxnum(int width, int height, int r, int8_t *num, int num_stride) {
int i, j;
for (i = 0; i <= r; ++i) {
for (j = 0; j <= r; ++j) {
num[i * num_stride + j] = (r + 1 + i) * (r + 1 + j);
num[i * num_stride + (width - 1 - j)] = num[i * num_stride + j];
num[(height - 1 - i) * num_stride + j] = num[i * num_stride + j];
num[(height - 1 - i) * num_stride + (width - 1 - j)] =
num[i * num_stride + j];
}
}
for (j = 0; j <= r; ++j) {
const int val = (2 * r + 1) * (r + 1 + j);
for (i = r + 1; i < height - r; ++i) {
num[i * num_stride + j] = val;
num[i * num_stride + (width - 1 - j)] = val;
}
}
for (i = 0; i <= r; ++i) {
const int val = (2 * r + 1) * (r + 1 + i);
for (j = r + 1; j < width - r; ++j) {
num[i * num_stride + j] = val;
num[(height - 1 - i) * num_stride + j] = val;
}
}
for (i = r + 1; i < height - r; ++i) {
for (j = r + 1; j < width - r; ++j) {
num[i * num_stride + j] = (2 * r + 1) * (2 * r + 1);
}
}
}
void decode_xq(const int *xqd, int *xq) {
xq[0] = xqd[0];
xq[1] = (1 << SGRPROJ_PRJ_BITS) - xq[0] - xqd[1];
}
const int32_t x_by_xplus1[256] = {
0, 128, 171, 192, 205, 213, 219, 224, 228, 230, 233, 235, 236, 238, 239,
240, 241, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, 247, 247,
248, 248, 248, 248, 249, 249, 249, 249, 249, 250, 250, 250, 250, 250, 250,
250, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 252, 252, 252, 252,
252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 253, 253,
253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253,
253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
256,
};
const int32_t one_by_x[MAX_NELEM] = {
4096, 2048, 1365, 1024, 819, 683, 585, 512, 455, 410, 372, 341, 315,
293, 273, 256, 241, 228, 216, 205, 195, 186, 178, 171, 164,
#if MAX_RADIUS > 2
158, 152, 146, 141, 137, 132, 128, 124, 120, 117, 114, 111, 108,
105, 102, 100, 98, 95, 93, 91, 89, 87, 85, 84
#endif // MAX_RADIUS > 2
};
static void av1_selfguided_restoration_internal(int32_t *dgd, int width,
int height, int dgd_stride,
int32_t *dst, int dst_stride,
int bit_depth, int r, int eps) {
const int width_ext = width + 2 * SGRPROJ_BORDER_HORZ;
const int height_ext = height + 2 * SGRPROJ_BORDER_VERT;
const int num_stride = width_ext;
// Adjusting the stride of A and B here appears to avoid bad cache effects,
// leading to a significant speed improvement.
// We also align the stride to a multiple of 16 bytes, for consistency
// with the SIMD version of this function.
int buf_stride = ((width_ext + 3) & ~3) + 16;
int32_t A_[RESTORATION_PROC_UNIT_PELS];
int32_t B_[RESTORATION_PROC_UNIT_PELS];
int32_t *A = A_;
int32_t *B = B_;
int8_t num_[RESTORATION_PROC_UNIT_PELS];
int8_t *num = num_ + SGRPROJ_BORDER_VERT * num_stride + SGRPROJ_BORDER_HORZ;
int i, j;
// Don't filter tiles with dimensions < 5 on any axis
if ((width < 5) || (height < 5)) return;
boxsum(dgd - dgd_stride * SGRPROJ_BORDER_VERT - SGRPROJ_BORDER_HORZ,
width_ext, height_ext, dgd_stride, r, 0, B, buf_stride);
boxsum(dgd - dgd_stride * SGRPROJ_BORDER_VERT - SGRPROJ_BORDER_HORZ,
width_ext, height_ext, dgd_stride, r, 1, A, buf_stride);
boxnum(width_ext, height_ext, r, num_, num_stride);
assert(r <= 3);
A += SGRPROJ_BORDER_VERT * buf_stride + SGRPROJ_BORDER_HORZ;
B += SGRPROJ_BORDER_VERT * buf_stride + SGRPROJ_BORDER_HORZ;
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
const int k = i * buf_stride + j;
const int n = num[i * num_stride + j];
// a < 2^16 * n < 2^22 regardless of bit depth
uint32_t a = ROUND_POWER_OF_TWO(A[k], 2 * (bit_depth - 8));
// b < 2^8 * n < 2^14 regardless of bit depth
uint32_t b = ROUND_POWER_OF_TWO(B[k], bit_depth - 8);
// Each term in calculating p = a * n - b * b is < 2^16 * n^2 < 2^28,
// and p itself satisfies p < 2^14 * n^2 < 2^26.
// Note: Sometimes, in high bit depth, we can end up with a*n < b*b.
// This is an artefact of rounding, and can only happen if all pixels
// are (almost) identical, so in this case we saturate to p=0.
uint32_t p = (a * n < b * b) ? 0 : a * n - b * b;
uint32_t s = sgrproj_mtable[eps - 1][n - 1];
// p * s < (2^14 * n^2) * round(2^20 / n^2 eps) < 2^34 / eps < 2^32
// as long as eps >= 4. So p * s fits into a uint32_t, and z < 2^12
// (this holds even after accounting for the rounding in s)
const uint32_t z = ROUND_POWER_OF_TWO(p * s, SGRPROJ_MTABLE_BITS);
A[k] = x_by_xplus1[AOMMIN(z, 255)]; // < 2^8
// SGRPROJ_SGR - A[k] < 2^8, B[k] < 2^(bit_depth) * n,
// one_by_x[n - 1] = round(2^12 / n)
// => the product here is < 2^(20 + bit_depth) <= 2^32,
// and B[k] is set to a value < 2^(8 + bit depth)
B[k] = (int32_t)ROUND_POWER_OF_TWO((uint32_t)(SGRPROJ_SGR - A[k]) *
(uint32_t)B[k] *
(uint32_t)one_by_x[n - 1],
SGRPROJ_RECIP_BITS);
}
}
i = 0;
j = 0;
{
const int k = i * buf_stride + j;
const int l = i * dgd_stride + j;
const int m = i * dst_stride + j;
const int nb = 3;
const int32_t a =
3 * A[k] + 2 * A[k + 1] + 2 * A[k + buf_stride] + A[k + buf_stride + 1];
const int32_t b =
3 * B[k] + 2 * B[k + 1] + 2 * B[k + buf_stride] + B[k + buf_stride + 1];
const int32_t v = a * dgd[l] + b;
dst[m] = ROUND_POWER_OF_TWO(v, SGRPROJ_SGR_BITS + nb - SGRPROJ_RST_BITS);
}
i = 0;
j = width - 1;
{
const int k = i * buf_stride + j;
const int l = i * dgd_stride + j;
const int m = i * dst_stride + j;
const int nb = 3;
const int32_t a =
3 * A[k] + 2 * A[k - 1] + 2 * A[k + buf_stride] + A[k + buf_stride - 1];
const int32_t b =
3 * B[k] + 2 * B[k - 1] + 2 * B[k + buf_stride] + B[k + buf_stride - 1];
const int32_t v = a * dgd[l] + b;
dst[m] = ROUND_POWER_OF_TWO(v, SGRPROJ_SGR_BITS + nb - SGRPROJ_RST_BITS);
}
i = height - 1;
j = 0;
{
const int k = i * buf_stride + j;
const int l = i * dgd_stride + j;
const int m = i * dst_stride + j;
const int nb = 3;
const int32_t a =
3 * A[k] + 2 * A[k + 1] + 2 * A[k - buf_stride] + A[k - buf_stride + 1];
const int32_t b =
3 * B[k] + 2 * B[k + 1] + 2 * B[k - buf_stride] + B[k - buf_stride + 1];
const int32_t v = a * dgd[l] + b;
dst[m] = ROUND_POWER_OF_TWO(v, SGRPROJ_SGR_BITS + nb - SGRPROJ_RST_BITS);
}
i = height - 1;
j = width - 1;
{
const int k = i * buf_stride + j;
const int l = i * dgd_stride + j;
const int m = i * dst_stride + j;
const int nb = 3;
const int32_t a =
3 * A[k] + 2 * A[k - 1] + 2 * A[k - buf_stride] + A[k - buf_stride - 1];
const int32_t b =
3 * B[k] + 2 * B[k - 1] + 2 * B[k - buf_stride] + B[k - buf_stride - 1];
const int32_t v = a * dgd[l] + b;
dst[m] = ROUND_POWER_OF_TWO(v, SGRPROJ_SGR_BITS + nb - SGRPROJ_RST_BITS);
}
i = 0;
for (j = 1; j < width - 1; ++j) {
const int k = i * buf_stride + j;
const int l = i * dgd_stride + j;
const int m = i * dst_stride + j;
const int nb = 3;
const int32_t a = A[k] + 2 * (A[k - 1] + A[k + 1]) + A[k + buf_stride] +
A[k + buf_stride - 1] + A[k + buf_stride + 1];
const int32_t b = B[k] + 2 * (B[k - 1] + B[k + 1]) + B[k + buf_stride] +
B[k + buf_stride - 1] + B[k + buf_stride + 1];
const int32_t v = a * dgd[l] + b;
dst[m] = ROUND_POWER_OF_TWO(v, SGRPROJ_SGR_BITS + nb - SGRPROJ_RST_BITS);
}
i = height - 1;
for (j = 1; j < width - 1; ++j) {
const int k = i * buf_stride + j;
const int l = i * dgd_stride + j;
const int m = i * dst_stride + j;
const int nb = 3;
const int32_t a = A[k] + 2 * (A[k - 1] + A[k + 1]) + A[k - buf_stride] +
A[k - buf_stride - 1] + A[k - buf_stride + 1];
const int32_t b = B[k] + 2 * (B[k - 1] + B[k + 1]) + B[k - buf_stride] +
B[k - buf_stride - 1] + B[k - buf_stride + 1];
const int32_t v = a * dgd[l] + b;
dst[m] = ROUND_POWER_OF_TWO(v, SGRPROJ_SGR_BITS + nb - SGRPROJ_RST_BITS);
}
j = 0;
for (i = 1; i < height - 1; ++i) {
const int k = i * buf_stride + j;
const int l = i * dgd_stride + j;
const int m = i * dst_stride + j;
const int nb = 3;
const int32_t a = A[k] + 2 * (A[k - buf_stride] + A[k + buf_stride]) +
A[k + 1] + A[k - buf_stride + 1] + A[k + buf_stride + 1];
const int32_t b = B[k] + 2 * (B[k - buf_stride] + B[k + buf_stride]) +
B[k + 1] + B[k - buf_stride + 1] + B[k + buf_stride + 1];
const int32_t v = a * dgd[l] + b;
dst[m] = ROUND_POWER_OF_TWO(v, SGRPROJ_SGR_BITS + nb - SGRPROJ_RST_BITS);
}
j = width - 1;
for (i = 1; i < height - 1; ++i) {
const int k = i * buf_stride + j;
const int l = i * dgd_stride + j;
const int m = i * dst_stride + j;
const int nb = 3;
const int32_t a = A[k] + 2 * (A[k - buf_stride] + A[k + buf_stride]) +
A[k - 1] + A[k - buf_stride - 1] + A[k + buf_stride - 1];
const int32_t b = B[k] + 2 * (B[k - buf_stride] + B[k + buf_stride]) +
B[k - 1] + B[k - buf_stride - 1] + B[k + buf_stride - 1];
const int32_t v = a * dgd[l] + b;
dst[m] = ROUND_POWER_OF_TWO(v, SGRPROJ_SGR_BITS + nb - SGRPROJ_RST_BITS);
}
for (i = 1; i < height - 1; ++i) {
for (j = 1; j < width - 1; ++j) {
const int k = i * buf_stride + j;
const int l = i * dgd_stride + j;
const int m = i * dst_stride + j;
const int nb = 5;
const int32_t a =
(A[k] + A[k - 1] + A[k + 1] + A[k - buf_stride] + A[k + buf_stride]) *
4 +
(A[k - 1 - buf_stride] + A[k - 1 + buf_stride] +
A[k + 1 - buf_stride] + A[k + 1 + buf_stride]) *
3;
const int32_t b =
(B[k] + B[k - 1] + B[k + 1] + B[k - buf_stride] + B[k + buf_stride]) *
4 +
(B[k - 1 - buf_stride] + B[k - 1 + buf_stride] +
B[k + 1 - buf_stride] + B[k + 1 + buf_stride]) *
3;
const int32_t v = a * dgd[l] + b;
dst[m] = ROUND_POWER_OF_TWO(v, SGRPROJ_SGR_BITS + nb - SGRPROJ_RST_BITS);
}
}
}
void av1_selfguided_restoration_c(const uint8_t *dgd, int width, int height,
int stride, int32_t *dst, int dst_stride,
int r, int eps) {
int32_t dgd32_[RESTORATION_PROC_UNIT_PELS];
const int dgd32_stride = width + 2 * SGRPROJ_BORDER_HORZ;
int32_t *dgd32 =
dgd32_ + dgd32_stride * SGRPROJ_BORDER_VERT + SGRPROJ_BORDER_HORZ;
int i, j;
for (i = -SGRPROJ_BORDER_VERT; i < height + SGRPROJ_BORDER_VERT; ++i) {
for (j = -SGRPROJ_BORDER_HORZ; j < width + SGRPROJ_BORDER_HORZ; ++j) {
dgd32[i * dgd32_stride + j] = dgd[i * stride + j];
}
}
av1_selfguided_restoration_internal(dgd32, width, height, dgd32_stride, dst,
dst_stride, 8, r, eps);
}
void av1_highpass_filter_c(const uint8_t *dgd, int width, int height,
int stride, int32_t *dst, int dst_stride, int corner,
int edge) {
int i, j;
const int center = (1 << SGRPROJ_RST_BITS) - 4 * (corner + edge);
i = 0;
j = 0;
{
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] =
center * dgd[k] + edge * (dgd[k + 1] + dgd[k + stride] + dgd[k] * 2) +
corner * (dgd[k + stride + 1] + dgd[k + 1] + dgd[k + stride] + dgd[k]);
}
i = 0;
j = width - 1;
{
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] =
center * dgd[k] + edge * (dgd[k - 1] + dgd[k + stride] + dgd[k] * 2) +
corner * (dgd[k + stride - 1] + dgd[k - 1] + dgd[k + stride] + dgd[k]);
}
i = height - 1;
j = 0;
{
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] =
center * dgd[k] + edge * (dgd[k + 1] + dgd[k - stride] + dgd[k] * 2) +
corner * (dgd[k - stride + 1] + dgd[k + 1] + dgd[k - stride] + dgd[k]);
}
i = height - 1;
j = width - 1;
{
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] =
center * dgd[k] + edge * (dgd[k - 1] + dgd[k - stride] + dgd[k] * 2) +
corner * (dgd[k - stride - 1] + dgd[k - 1] + dgd[k - stride] + dgd[k]);
}
i = 0;
for (j = 1; j < width - 1; ++j) {
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] = center * dgd[k] +
edge * (dgd[k - 1] + dgd[k + stride] + dgd[k + 1] + dgd[k]) +
corner * (dgd[k + stride - 1] + dgd[k + stride + 1] + dgd[k - 1] +
dgd[k + 1]);
}
i = height - 1;
for (j = 1; j < width - 1; ++j) {
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] = center * dgd[k] +
edge * (dgd[k - 1] + dgd[k - stride] + dgd[k + 1] + dgd[k]) +
corner * (dgd[k - stride - 1] + dgd[k - stride + 1] + dgd[k - 1] +
dgd[k + 1]);
}
j = 0;
for (i = 1; i < height - 1; ++i) {
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] = center * dgd[k] +
edge * (dgd[k - stride] + dgd[k + 1] + dgd[k + stride] + dgd[k]) +
corner * (dgd[k + stride + 1] + dgd[k - stride + 1] +
dgd[k - stride] + dgd[k + stride]);
}
j = width - 1;
for (i = 1; i < height - 1; ++i) {
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] = center * dgd[k] +
edge * (dgd[k - stride] + dgd[k - 1] + dgd[k + stride] + dgd[k]) +
corner * (dgd[k + stride - 1] + dgd[k - stride - 1] +
dgd[k - stride] + dgd[k + stride]);
}
for (i = 1; i < height - 1; ++i) {
for (j = 1; j < width - 1; ++j) {
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] =
center * dgd[k] +
edge * (dgd[k - stride] + dgd[k - 1] + dgd[k + stride] + dgd[k + 1]) +
corner * (dgd[k + stride - 1] + dgd[k - stride - 1] +
dgd[k - stride + 1] + dgd[k + stride + 1]);
}
}
}
void apply_selfguided_restoration_c(const uint8_t *dat, int width, int height,
int stride, int eps, const int *xqd,
uint8_t *dst, int dst_stride,
int32_t *tmpbuf) {
int xq[2];
int32_t *flt1 = tmpbuf;
int32_t *flt2 = flt1 + RESTORATION_TILEPELS_MAX;
int i, j;
assert(width * height <= RESTORATION_TILEPELS_MAX);
#if USE_HIGHPASS_IN_SGRPROJ
av1_highpass_filter_c(dat, width, height, stride, flt1, width,
sgr_params[eps].corner, sgr_params[eps].edge);
#else
av1_selfguided_restoration_c(dat, width, height, stride, flt1, width,
sgr_params[eps].r1, sgr_params[eps].e1);
#endif // USE_HIGHPASS_IN_SGRPROJ
av1_selfguided_restoration_c(dat, width, height, stride, flt2, width,
sgr_params[eps].r2, sgr_params[eps].e2);
decode_xq(xqd, xq);
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
const int k = i * width + j;
const int l = i * stride + j;
const int m = i * dst_stride + j;
const int32_t u = ((int32_t)dat[l] << SGRPROJ_RST_BITS);
const int32_t f1 = (int32_t)flt1[k] - u;
const int32_t f2 = (int32_t)flt2[k] - u;
const int32_t v = xq[0] * f1 + xq[1] * f2 + (u << SGRPROJ_PRJ_BITS);
const int16_t w =
(int16_t)ROUND_POWER_OF_TWO(v, SGRPROJ_PRJ_BITS + SGRPROJ_RST_BITS);
dst[m] = clip_pixel(w);
}
}
}
static void sgrproj_filter_stripe(const RestorationUnitInfo *rui,
int stripe_width, int stripe_height,
int procunit_width, const uint8_t *src,
int src_stride, uint8_t *dst, int dst_stride,
int32_t *tmpbuf, int bit_depth) {
(void)bit_depth;
assert(bit_depth == 8);
for (int j = 0; j < stripe_width; j += procunit_width) {
int w = AOMMIN(procunit_width, stripe_width - j);
apply_selfguided_restoration(src + j, w, stripe_height, src_stride,
rui->sgrproj_info.ep, rui->sgrproj_info.xqd,
dst + j, dst_stride, tmpbuf);
}
}
#if CONFIG_HIGHBITDEPTH
#if USE_WIENER_HIGH_INTERMEDIATE_PRECISION
#define wiener_highbd_convolve8_add_src aom_highbd_convolve8_add_src_hip
#else
#define wiener_highbd_convolve8_add_src aom_highbd_convolve8_add_src
#endif
static void wiener_filter_stripe_highbd(const RestorationUnitInfo *rui,
int stripe_width, int stripe_height,
int procunit_width, const uint8_t *src8,
int src_stride, uint8_t *dst8,
int dst_stride, int32_t *tmpbuf,
int bit_depth) {
(void)tmpbuf;
for (int j = 0; j < stripe_width; j += procunit_width) {
int w = AOMMIN(procunit_width, (stripe_width - j + 15) & ~15);
const uint8_t *src8_p = src8 + j;
uint8_t *dst8_p = dst8 + j;
wiener_highbd_convolve8_add_src(
src8_p, src_stride, dst8_p, dst_stride, rui->wiener_info.hfilter, 16,
rui->wiener_info.vfilter, 16, w, stripe_height, bit_depth);
}
}
void av1_selfguided_restoration_highbd_c(const uint16_t *dgd, int width,
int height, int stride, int32_t *dst,
int dst_stride, int bit_depth, int r,
int eps) {
int32_t dgd32_[RESTORATION_PROC_UNIT_PELS];
const int dgd32_stride = width + 2 * SGRPROJ_BORDER_HORZ;
int32_t *dgd32 =
dgd32_ + dgd32_stride * SGRPROJ_BORDER_VERT + SGRPROJ_BORDER_HORZ;
int i, j;
for (i = -SGRPROJ_BORDER_VERT; i < height + SGRPROJ_BORDER_VERT; ++i) {
for (j = -SGRPROJ_BORDER_HORZ; j < width + SGRPROJ_BORDER_HORZ; ++j) {
dgd32[i * dgd32_stride + j] = dgd[i * stride + j];
}
}
av1_selfguided_restoration_internal(dgd32, width, height, dgd32_stride, dst,
dst_stride, bit_depth, r, eps);
}
void av1_highpass_filter_highbd_c(const uint16_t *dgd, int width, int height,
int stride, int32_t *dst, int dst_stride,
int corner, int edge) {
int i, j;
const int center = (1 << SGRPROJ_RST_BITS) - 4 * (corner + edge);
i = 0;
j = 0;
{
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] =
center * dgd[k] + edge * (dgd[k + 1] + dgd[k + stride] + dgd[k] * 2) +
corner * (dgd[k + stride + 1] + dgd[k + 1] + dgd[k + stride] + dgd[k]);
}
i = 0;
j = width - 1;
{
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] =
center * dgd[k] + edge * (dgd[k - 1] + dgd[k + stride] + dgd[k] * 2) +
corner * (dgd[k + stride - 1] + dgd[k - 1] + dgd[k + stride] + dgd[k]);
}
i = height - 1;
j = 0;
{
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] =
center * dgd[k] + edge * (dgd[k + 1] + dgd[k - stride] + dgd[k] * 2) +
corner * (dgd[k - stride + 1] + dgd[k + 1] + dgd[k - stride] + dgd[k]);
}
i = height - 1;
j = width - 1;
{
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] =
center * dgd[k] + edge * (dgd[k - 1] + dgd[k - stride] + dgd[k] * 2) +
corner * (dgd[k - stride - 1] + dgd[k - 1] + dgd[k - stride] + dgd[k]);
}
i = 0;
for (j = 1; j < width - 1; ++j) {
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] = center * dgd[k] +
edge * (dgd[k - 1] + dgd[k + stride] + dgd[k + 1] + dgd[k]) +
corner * (dgd[k + stride - 1] + dgd[k + stride + 1] + dgd[k - 1] +
dgd[k + 1]);
}
i = height - 1;
for (j = 1; j < width - 1; ++j) {
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] = center * dgd[k] +
edge * (dgd[k - 1] + dgd[k - stride] + dgd[k + 1] + dgd[k]) +
corner * (dgd[k - stride - 1] + dgd[k - stride + 1] + dgd[k - 1] +
dgd[k + 1]);
}
j = 0;
for (i = 1; i < height - 1; ++i) {
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] = center * dgd[k] +
edge * (dgd[k - stride] + dgd[k + 1] + dgd[k + stride] + dgd[k]) +
corner * (dgd[k + stride + 1] + dgd[k - stride + 1] +
dgd[k - stride] + dgd[k + stride]);
}
j = width - 1;
for (i = 1; i < height - 1; ++i) {
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] = center * dgd[k] +
edge * (dgd[k - stride] + dgd[k - 1] + dgd[k + stride] + dgd[k]) +
corner * (dgd[k + stride - 1] + dgd[k - stride - 1] +
dgd[k - stride] + dgd[k + stride]);
}
for (i = 1; i < height - 1; ++i) {
for (j = 1; j < width - 1; ++j) {
const int k = i * stride + j;
const int l = i * dst_stride + j;
dst[l] =
center * dgd[k] +
edge * (dgd[k - stride] + dgd[k - 1] + dgd[k + stride] + dgd[k + 1]) +
corner * (dgd[k + stride - 1] + dgd[k - stride - 1] +
dgd[k - stride + 1] + dgd[k + stride + 1]);
}
}
}
void apply_selfguided_restoration_highbd_c(const uint16_t *dat, int width,
int height, int stride,
int bit_depth, int eps,
const int *xqd, uint16_t *dst,
int dst_stride, int32_t *tmpbuf) {
int xq[2];
int32_t *flt1 = tmpbuf;
int32_t *flt2 = flt1 + RESTORATION_TILEPELS_MAX;
int i, j;
assert(width * height <= RESTORATION_TILEPELS_MAX);
#if USE_HIGHPASS_IN_SGRPROJ
av1_highpass_filter_highbd_c(dat, width, height, stride, flt1, width,
sgr_params[eps].corner, sgr_params[eps].edge);
#else
av1_selfguided_restoration_highbd_c(dat, width, height, stride, flt1, width,
bit_depth, sgr_params[eps].r1,
sgr_params[eps].e1);
#endif // USE_HIGHPASS_IN_SGRPROJ
av1_selfguided_restoration_highbd_c(dat, width, height, stride, flt2, width,
bit_depth, sgr_params[eps].r2,
sgr_params[eps].e2);
decode_xq(xqd, xq);
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
const int k = i * width + j;
const int l = i * stride + j;
const int m = i * dst_stride + j;
const int32_t u = ((int32_t)dat[l] << SGRPROJ_RST_BITS);
const int32_t f1 = (int32_t)flt1[k] - u;
const int32_t f2 = (int32_t)flt2[k] - u;
const int32_t v = xq[0] * f1 + xq[1] * f2 + (u << SGRPROJ_PRJ_BITS);
const int16_t w =
(int16_t)ROUND_POWER_OF_TWO(v, SGRPROJ_PRJ_BITS + SGRPROJ_RST_BITS);
dst[m] = (uint16_t)clip_pixel_highbd(w, bit_depth);
}
}
}
static void sgrproj_filter_stripe_highbd(const RestorationUnitInfo *rui,
int stripe_width, int stripe_height,
int procunit_width,
const uint8_t *src8, int src_stride,
uint8_t *dst8, int dst_stride,
int32_t *tmpbuf, int bit_depth) {
for (int j = 0; j < stripe_width; j += procunit_width) {
int w = AOMMIN(procunit_width, stripe_width - j);
const uint16_t *data_p = CONVERT_TO_SHORTPTR(src8) + j;
uint16_t *dst_p = CONVERT_TO_SHORTPTR(dst8) + j;
apply_selfguided_restoration_highbd(
data_p, w, stripe_height, src_stride, bit_depth, rui->sgrproj_info.ep,
rui->sgrproj_info.xqd, dst_p, dst_stride, tmpbuf);
}
}
#endif // CONFIG_HIGHBITDEPTH
typedef void (*stripe_filter_fun)(const RestorationUnitInfo *rui,
int stripe_width, int stripe_height,
int procunit_width, const uint8_t *src,
int src_stride, uint8_t *dst, int dst_stride,
int32_t *tmpbuf, int bit_depth);
#if CONFIG_HIGHBITDEPTH
#define NUM_STRIPE_FILTERS 4
#else
#define NUM_STRIPE_FILTERS 2
#endif
static const stripe_filter_fun stripe_filters[NUM_STRIPE_FILTERS] = {
wiener_filter_stripe, sgrproj_filter_stripe,
#if CONFIG_HIGHBITDEPTH
wiener_filter_stripe_highbd, sgrproj_filter_stripe_highbd
#endif // CONFIG_HIGHBITDEPTH
};
void av1_loop_restoration_filter_unit(const RestorationTileLimits *limits,
const RestorationUnitInfo *rui,
#if CONFIG_STRIPED_LOOP_RESTORATION
const RestorationStripeBoundaries *rsb,
RestorationLineBuffers *rlbs, int ss_y,
#endif
int procunit_width, int procunit_height,
int highbd, int bit_depth, uint8_t *data8,
int stride, uint8_t *dst8, int dst_stride,
int32_t *tmpbuf) {
RestorationType unit_rtype = rui->restoration_type;
int unit_h = limits->v_end - limits->v_start;
int unit_w = limits->h_end - limits->h_start;
uint8_t *data8_tl = data8 + limits->v_start * stride + limits->h_start;
uint8_t *dst8_tl = dst8 + limits->v_start * dst_stride + limits->h_start;
if (unit_rtype == RESTORE_NONE) {
copy_tile(unit_w, unit_h, data8_tl, stride, dst8_tl, dst_stride, highbd);
return;
}
const int filter_idx = 2 * highbd + (unit_rtype == RESTORE_SGRPROJ);
assert(filter_idx < NUM_STRIPE_FILTERS);
const stripe_filter_fun stripe_filter = stripe_filters[filter_idx];
// Convolve the whole tile one stripe at a time
#if CONFIG_STRIPED_LOOP_RESTORATION
RestorationTileLimits remaining_stripes = *limits;
#endif
int i = 0;
while (i < unit_h) {
#if CONFIG_STRIPED_LOOP_RESTORATION
remaining_stripes.v_start = limits->v_start + i;
int h = setup_processing_stripe_boundary(&remaining_stripes, rsb,
procunit_height, ss_y, highbd,
data8, stride, rlbs);
#else
const int h = AOMMIN(procunit_height, (unit_h - i + 15) & ~15);
#endif
stripe_filter(rui, unit_w, h, procunit_width, data8_tl + i * stride, stride,
dst8_tl + i * dst_stride, dst_stride, tmpbuf, bit_depth);
#if CONFIG_STRIPED_LOOP_RESTORATION
restore_processing_stripe_boundary(
&remaining_stripes, rlbs, procunit_height, ss_y, highbd, data8, stride);
#endif
i += h;
}
}
struct restore_borders {
int hborder, vborder;
};
static const struct restore_borders restore_borders[RESTORE_TYPES] = {
{ 0, 0 },
{ WIENER_BORDER_HORZ, WIENER_BORDER_VERT },
{ SGRPROJ_BORDER_HORZ, SGRPROJ_BORDER_VERT },
{ RESTORATION_BORDER_HORZ, RESTORATION_BORDER_VERT }
};
typedef struct {
const RestorationInfo *rsi;
#if CONFIG_STRIPED_LOOP_RESTORATION
RestorationLineBuffers *rlbs;
int ss_y;
#endif
int highbd, bit_depth;
uint8_t *data8, *dst8;
int data_stride, dst_stride;
int32_t *tmpbuf;
} FilterFrameCtxt;
static void filter_frame_on_unit(const RestorationTileLimits *limits,
int rest_unit_idx, void *priv) {
FilterFrameCtxt *ctxt = (FilterFrameCtxt *)priv;
const RestorationInfo *rsi = ctxt->rsi;
av1_loop_restoration_filter_unit(limits, &rsi->unit_info[rest_unit_idx],
#if CONFIG_STRIPED_LOOP_RESTORATION
&rsi->boundaries, ctxt->rlbs, ctxt->ss_y,
#endif
rsi->procunit_width, rsi->procunit_height,
ctxt->highbd, ctxt->bit_depth, ctxt->data8,
ctxt->data_stride, ctxt->dst8,
ctxt->dst_stride, ctxt->tmpbuf);
}
void av1_loop_restoration_filter_frame(YV12_BUFFER_CONFIG *frame,
AV1_COMMON *cm, RestorationInfo *rsi,
int components_pattern,
YV12_BUFFER_CONFIG *dst) {
YV12_BUFFER_CONFIG dst_;
typedef void (*copy_fun)(const YV12_BUFFER_CONFIG *src,
YV12_BUFFER_CONFIG *dst);
static const copy_fun copy_funs[3] = { aom_yv12_copy_y, aom_yv12_copy_u,
aom_yv12_copy_v };
for (int plane = 0; plane < 3; ++plane) {
if ((components_pattern == 1 << plane) &&
(rsi[plane].frame_restoration_type == RESTORE_NONE)) {
if (dst) copy_funs[plane](frame, dst);
return;
}
}
if (components_pattern ==
((1 << AOM_PLANE_Y) | (1 << AOM_PLANE_U) | (1 << AOM_PLANE_V))) {
// All components
if (rsi[0].frame_restoration_type == RESTORE_NONE &&
rsi[1].frame_restoration_type == RESTORE_NONE &&
rsi[2].frame_restoration_type == RESTORE_NONE) {
if (dst) aom_yv12_copy_frame(frame, dst);
return;
}
}
if (!dst) {
dst = &dst_;
memset(dst, 0, sizeof(YV12_BUFFER_CONFIG));
if (aom_realloc_frame_buffer(dst, frame->y_crop_width, frame->y_crop_height,
cm->subsampling_x, cm->subsampling_y,
#if CONFIG_HIGHBITDEPTH
cm->use_highbitdepth,
#endif
AOM_BORDER_IN_PIXELS, cm->byte_alignment, NULL,
NULL, NULL) < 0)
aom_internal_error(&cm->error, AOM_CODEC_MEM_ERROR,
"Failed to allocate restoration dst buffer");
}
#if CONFIG_STRIPED_LOOP_RESTORATION
RestorationLineBuffers rlbs;
#endif
#if CONFIG_HIGHBITDEPTH
const int bit_depth = cm->bit_depth;
const int highbd = cm->use_highbitdepth;
#else
const int bit_depth = 8;
const int highbd = 0;
#endif
for (int plane = 0; plane < 3; ++plane) {
if (!((components_pattern >> plane) & 1)) continue;
const RestorationInfo *prsi = &rsi[plane];
RestorationType rtype = prsi->frame_restoration_type;
if (rtype == RESTORE_NONE) {
copy_funs[plane](frame, dst);
continue;
}
const int is_uv = plane > 0;
const int plane_width = frame->crop_widths[is_uv];
const int plane_height = frame->crop_heights[is_uv];
const struct restore_borders *borders =
&restore_borders[prsi->frame_restoration_type];
extend_frame(frame->buffers[plane], plane_width, plane_height,
frame->strides[is_uv], borders->hborder, borders->vborder,
highbd);
FilterFrameCtxt ctxt;
ctxt.rsi = prsi;
#if CONFIG_STRIPED_LOOP_RESTORATION
const int ss_y = is_uv && cm->subsampling_y;
ctxt.rlbs = &rlbs;
ctxt.ss_y = ss_y;
#endif
ctxt.highbd = highbd;
ctxt.bit_depth = bit_depth;
ctxt.data8 = frame->buffers[plane];
ctxt.dst8 = dst->buffers[plane];
ctxt.data_stride = frame->strides[is_uv];
ctxt.dst_stride = dst->strides[is_uv];
ctxt.tmpbuf = cm->rst_tmpbuf;
av1_foreach_rest_unit_in_frame(cm, plane, NULL, filter_frame_on_unit,
&ctxt);
}
if (dst == &dst_) {
for (int plane = 0; plane < 3; ++plane) {
if ((components_pattern >> plane) & 1) {
copy_funs[plane](dst, frame);
}
}
aom_free_frame_buffer(dst);
}
}
static void foreach_rest_unit_in_tile(const AV1PixelRect *tile_rect,
int tile_row, int tile_col, int tile_cols,
int hunits_per_tile, int units_per_tile,
int unit_size, int ss_y,
rest_unit_visitor_t on_rest_unit,
void *priv) {
const int tile_w = tile_rect->right - tile_rect->left;
const int tile_h = tile_rect->bottom - tile_rect->top;
const int ext_size = unit_size * 3 / 2;
const int tile_idx = tile_col + tile_row * tile_cols;
const int unit_idx0 = tile_idx * units_per_tile;
int y0 = 0, i = 0;
while (y0 < tile_h) {
int remaining_h = tile_h - y0;
int h = (remaining_h < ext_size) ? remaining_h : unit_size;
RestorationTileLimits limits;
limits.v_start = tile_rect->top + y0;
limits.v_end = tile_rect->top + y0 + h;
assert(limits.v_end <= tile_rect->bottom);
#if CONFIG_STRIPED_LOOP_RESTORATION
// Offset the tile upwards to align with the restoration processing stripe
const int voffset = RESTORATION_TILE_OFFSET >> ss_y;
limits.v_start = AOMMAX(0, limits.v_start - voffset);
if (limits.v_end < tile_rect->bottom) limits.v_end -= voffset;
#else
(void)ss_y;
#endif
int x0 = 0, j = 0;
while (x0 < tile_w) {
int remaining_w = tile_w - x0;
int w = (remaining_w < ext_size) ? remaining_w : unit_size;
limits.h_start = tile_rect->left + x0;
limits.h_end = tile_rect->left + x0 + w;
assert(limits.h_end <= tile_rect->right);
const int unit_idx = unit_idx0 + i * hunits_per_tile + j;
on_rest_unit(&limits, unit_idx, priv);
x0 += w;
++j;
}
y0 += h;
++i;
}
}
void av1_foreach_rest_unit_in_frame(const struct AV1Common *cm, int plane,
rest_tile_start_visitor_t on_tile,
rest_unit_visitor_t on_rest_unit,
void *priv) {
const int is_uv = plane > 0;
const int ss_y = is_uv && cm->subsampling_y;
const RestorationInfo *rsi = &cm->rst_info[plane];
TileInfo tile_info;
for (int tile_row = 0; tile_row < cm->tile_rows; ++tile_row) {
av1_tile_set_row(&tile_info, cm, tile_row);
for (int tile_col = 0; tile_col < cm->tile_cols; ++tile_col) {
av1_tile_set_col(&tile_info, cm, tile_col);
if (on_tile) on_tile(tile_row, tile_col, priv);
AV1PixelRect tile_rect = av1_get_tile_rect(&tile_info, cm, is_uv);
foreach_rest_unit_in_tile(&tile_rect, tile_row, tile_col, cm->tile_cols,
rsi->horz_units_per_tile, rsi->units_per_tile,
rsi->restoration_unit_size, ss_y, on_rest_unit,
priv);
}
}
}
#if CONFIG_MAX_TILE
// Get the horizontal or vertical index of the tile containing mi_x. For a
// horizontal index, mi_x should be the left-most column for some block in mi
// units and tile_x_start_sb should be cm->tile_col_start_sb. The return value
// will be "tile_col" for the tile containing that block.
//
// For a vertical index, mi_x should be the block's top row and tile_x_start_sb
// should be cm->tile_row_start_sb. The return value will be "tile_row" for the
// tile containing the block.
static int get_tile_idx(const int *tile_x_start_sb, int mi_x) {
int sb_x = mi_x << MAX_MIB_SIZE_LOG2;
for (int i = 0; i < MAX_TILE_COLS; ++i) {
if (tile_x_start_sb[i + 1] > sb_x) return i;
}
// This shouldn't happen if tile_x_start_sb has been filled in
// correctly.
assert(0);
return 0;
}
#endif
int av1_loop_restoration_corners_in_sb(const struct AV1Common *cm, int plane,
int mi_row, int mi_col, BLOCK_SIZE bsize,
int *rcol0, int *rcol1, int *rrow0,
int *rrow1, int *tile_tl_idx) {
assert(rcol0 && rcol1 && rrow0 && rrow1);
if (bsize != cm->sb_size) return 0;
const int is_uv = plane > 0;
// Which tile contains the superblock? Find that tile's top-left in mi-units,
// together with the tile's size in pixels.
#if CONFIG_MAX_TILE
const int tile_row = get_tile_idx(cm->tile_row_start_sb, mi_row);
const int tile_col = get_tile_idx(cm->tile_col_start_sb, mi_col);
const int sb_t = cm->tile_row_start_sb[tile_row];
const int sb_l = cm->tile_col_start_sb[tile_col];
const int sb_b = cm->tile_row_start_sb[tile_row + 1];
const int sb_r = cm->tile_col_start_sb[tile_col + 1];
int tile_w, tile_h;
tile_width_and_height(cm, is_uv, sb_r - sb_l, sb_t - sb_b, &tile_w, &tile_h);
const int mi_top = sb_t << MAX_MIB_SIZE_LOG2;
const int mi_left = sb_l << MAX_MIB_SIZE_LOG2;
#else
const int tile_row = mi_row / cm->tile_height;
const int tile_col = mi_col / cm->tile_width;
TileInfo tile_info;
av1_tile_init(&tile_info, cm, tile_row, tile_col);
const AV1PixelRect tile_rect = av1_get_tile_rect(&tile_info, cm, is_uv);
const int tile_w = tile_rect.right - tile_rect.left;
const int tile_h = tile_rect.bottom - tile_rect.top;
const int mi_top = tile_info.mi_row_start;
const int mi_left = tile_info.mi_col_start;
#endif // CONFIG_MAX_TILE
// Compute the mi-unit corners of the superblock relative to the top-left of
// the tile
const int mi_rel_row0 = mi_row - mi_top;
const int mi_rel_col0 = mi_col - mi_left;
const int mi_rel_row1 = mi_rel_row0 + mi_size_high[bsize];
const int mi_rel_col1 = mi_rel_col0 + mi_size_wide[bsize];
#if CONFIG_FRAME_SUPERRES
const int mi_to_num = MI_SIZE * SCALE_NUMERATOR;
const int denom = cm->superres_scale_denominator;
#else
const int mi_to_num = MI_SIZE;
const int denom = 1;
#endif // CONFIG_FRAME_SUPERRES
const RestorationInfo *rsi = &cm->rst_info[plane];
const int size = rsi->restoration_unit_size;
const int rnd = size * denom - 1;
// Calculate the number of restoration units in this tile (which might be
// strictly less than rsi->horz_units_per_tile and rsi->vert_units_per_tile)
const int horz_units = count_units_in_tile(size, tile_w);
const int vert_units = count_units_in_tile(size, tile_h);
// rcol0/rrow0 should be the first column/row of restoration units (relative
// to the top-left of the tile) that doesn't start left/below of
// mi_col/mi_row. For this calculation, we need to round up the division (if
// the sb starts at rtile column 10.1, the first matching rtile has column
// index 11)
*rcol0 = (mi_rel_col0 * mi_to_num + rnd) / (size * denom);
*rrow0 = (mi_rel_row0 * mi_to_num + rnd) / (size * denom);
// rel_col1/rel_row1 is the equivalent calculation, but for the superblock
// below-right. If we're at the bottom or right of the tile, this restoration
// unit might not exist, in which case we'll clamp accordingly.
*rcol1 = AOMMIN((mi_rel_col1 * mi_to_num + rnd) / (size * denom), horz_units);
*rrow1 = AOMMIN((mi_rel_row1 * mi_to_num + rnd) / (size * denom), vert_units);
const int tile_idx = tile_col + tile_row * cm->tile_cols;
*tile_tl_idx = tile_idx * rsi->units_per_tile;
return *rcol0 < *rcol1 && *rrow0 < *rrow1;
}
#if CONFIG_STRIPED_LOOP_RESTORATION
// Extend to left and right
static void extend_line(uint8_t *buf, int width, int extend,
int use_highbitdepth) {
int i;
if (use_highbitdepth) {
uint16_t val, *buf16 = (uint16_t *)buf;
val = buf16[0];
for (i = 0; i < extend; i++) buf16[-1 - i] = val;
val = buf16[width - 1];
for (i = 0; i < extend; i++) buf16[width + i] = val;
} else {
uint8_t val;
val = buf[0];
for (i = 0; i < extend; i++) buf[-1 - i] = val;
val = buf[width - 1];
for (i = 0; i < extend; i++) buf[width + i] = val;
}
}
// For each 64 pixel high stripe, save 4 scan lines to be used as boundary in
// the loop restoration process. The lines are saved in
// rst_internal.stripe_boundary_lines
void av1_loop_restoration_save_boundary_lines(const YV12_BUFFER_CONFIG *frame,
AV1_COMMON *cm) {
for (int p = 0; p < MAX_MB_PLANE; ++p) {
const int is_uv = p > 0;
const uint8_t *src_buf = frame->buffers[p];
const int src_width = frame->crop_widths[is_uv];
const int src_height = frame->crop_heights[is_uv];
const int src_stride = frame->strides[is_uv];
const int stripe_height = 64 >> (is_uv && cm->subsampling_y);
const int stripe_offset = (56 >> (is_uv && cm->subsampling_y)) - 2;
RestorationStripeBoundaries *boundaries = &cm->rst_info[p].boundaries;
uint8_t *boundary_above_buf = boundaries->stripe_boundary_above;
uint8_t *boundary_below_buf = boundaries->stripe_boundary_below;
const int boundary_stride = boundaries->stripe_boundary_stride;
#if CONFIG_HIGHBITDEPTH
const int use_highbitdepth = cm->use_highbitdepth;
if (use_highbitdepth) {
src_buf = (uint8_t *)CONVERT_TO_SHORTPTR(src_buf);
}
#else
const int use_highbitdepth = 0;
#endif
src_buf += (stripe_offset * src_stride) << use_highbitdepth;
boundary_above_buf += RESTORATION_EXTRA_HORZ << use_highbitdepth;
boundary_below_buf += RESTORATION_EXTRA_HORZ << use_highbitdepth;
// Loop over stripes
for (int stripe_y = stripe_offset; stripe_y < src_height;
stripe_y += stripe_height) {
// Save 2 lines above the LR stripe (offset -9, -10)
for (int yy = 0; yy < 2; yy++) {
if (stripe_y + yy < src_height) {
memcpy(boundary_above_buf, src_buf, src_width << use_highbitdepth);
extend_line(boundary_above_buf, src_width, RESTORATION_EXTRA_HORZ,
use_highbitdepth);
src_buf += src_stride << use_highbitdepth;
boundary_above_buf += boundary_stride << use_highbitdepth;
}
}
// Save 2 lines below the LR stripe (offset 56,57)
for (int yy = 2; yy < 4; yy++) {
if (stripe_y + yy < src_height) {
memcpy(boundary_below_buf, src_buf, src_width << use_highbitdepth);
extend_line(boundary_below_buf, src_width, RESTORATION_EXTRA_HORZ,
use_highbitdepth);
src_buf += src_stride << use_highbitdepth;
boundary_below_buf += boundary_stride << use_highbitdepth;
}
}
// jump to next stripe
src_buf += ((stripe_height - 4) * src_stride) << use_highbitdepth;
}
}
}
#endif // CONFIG_STRIPED_LOOP_RESTORATION