summaryrefslogtreecommitdiffstats
path: root/third_party/aom/av1/common/restoration.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/aom/av1/common/restoration.c')
-rw-r--r--third_party/aom/av1/common/restoration.c1556
1 files changed, 0 insertions, 1556 deletions
diff --git a/third_party/aom/av1/common/restoration.c b/third_party/aom/av1/common/restoration.c
deleted file mode 100644
index d276a915b..000000000
--- a/third_party/aom/av1/common/restoration.c
+++ /dev/null
@@ -1,1556 +0,0 @@
-/*
- * 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 "config/aom_config.h"
-#include "config/aom_dsp_rtcd.h"
-#include "config/aom_scale_rtcd.h"
-
-#include "aom_mem/aom_mem.h"
-#include "av1/common/onyxc_int.h"
-#include "av1/common/resize.h"
-#include "av1/common/restoration.h"
-#include "aom_dsp/aom_dsp_common.h"
-#include "aom_mem/aom_mem.h"
-
-#include "aom_ports/mem.h"
-
-// The 's' values are calculated based on original 'r' and 'e' values in the
-// spec using GenSgrprojVtable().
-// Note: Setting r = 0 skips the filter; with corresponding s = -1 (invalid).
-const sgr_params_type sgr_params[SGRPROJ_PARAMS] = {
- { { 2, 1 }, { 140, 3236 } }, { { 2, 1 }, { 112, 2158 } },
- { { 2, 1 }, { 93, 1618 } }, { { 2, 1 }, { 80, 1438 } },
- { { 2, 1 }, { 70, 1295 } }, { { 2, 1 }, { 58, 1177 } },
- { { 2, 1 }, { 47, 1079 } }, { { 2, 1 }, { 37, 996 } },
- { { 2, 1 }, { 30, 925 } }, { { 2, 1 }, { 25, 863 } },
- { { 0, 1 }, { -1, 2589 } }, { { 0, 1 }, { -1, 1618 } },
- { { 0, 1 }, { -1, 1177 } }, { { 0, 1 }, { -1, 925 } },
- { { 2, 0 }, { 56, -1 } }, { { 2, 0 }, { 22, -1 } },
-};
-
-AV1PixelRect av1_whole_frame_rect(const AV1_COMMON *cm, int is_uv) {
- AV1PixelRect rect;
-
- int ss_x = is_uv && cm->seq_params.subsampling_x;
- int ss_y = is_uv && cm->seq_params.subsampling_y;
-
- rect.top = 0;
- rect.bottom = ROUND_POWER_OF_TWO(cm->height, ss_y);
- rect.left = 0;
- rect.right = ROUND_POWER_OF_TWO(cm->superres_upscaled_width, ss_x);
- return rect;
-}
-
-// 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.
-int av1_lr_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) {
- // 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.
- const AV1PixelRect tile_rect = av1_whole_frame_rect(cm, is_uv);
- const int max_tile_w = tile_rect.right - tile_rect.left;
- const int max_tile_h = tile_rect.bottom - tile_rect.top;
-
- // 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 = av1_lr_count_units_in_tile(unit_size, max_tile_w);
- const int vpertile = av1_lr_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 = 1;
- const int nunits = ntiles * rsi->units_per_tile;
-
- aom_free(rsi->unit_info);
- CHECK_MEM_ERROR(cm, rsi->unit_info,
- (RestorationUnitInfo *)aom_memalign(
- 16, sizeof(*rsi->unit_info) * nunits));
-}
-
-void av1_free_restoration_struct(RestorationInfo *rst_info) {
- aom_free(rst_info->unit_info);
- rst_info->unit_info = NULL;
-}
-
-#if 0
-// Pair of values for each sgrproj parameter:
-// Index 0 corresponds to r[0], e[0]
-// Index 1 corresponds to r[1], e[1]
-int sgrproj_mtable[SGRPROJ_PARAMS][2];
-
-static void GenSgrprojVtable() {
- for (int i = 0; i < SGRPROJ_PARAMS; ++i) {
- const sgr_params_type *const params = &sgr_params[i];
- for (int j = 0; j < 2; ++j) {
- const int e = params->e[j];
- const int r = params->r[j];
- if (r == 0) { // filter is disabled
- sgrproj_mtable[i][j] = -1; // mark invalid
- } else { // filter is enabled
- const int n = (2 * r + 1) * (2 * r + 1);
- const int n2e = n * n * e;
- assert(n2e != 0);
- sgrproj_mtable[i][j] = (((1 << SGRPROJ_MTABLE_BITS) + n2e / 2) / n2e);
- }
- }
- }
-}
-#endif
-
-void av1_loop_restoration_precal() {
-#if 0
- GenSgrprojVtable();
-#endif
-}
-
-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);
- }
-}
-
-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));
- }
-}
-
-void extend_frame(uint8_t *data, int width, int height, int stride,
- int border_horz, int border_vert, int highbd) {
- if (highbd)
- extend_frame_highbd(CONVERT_TO_SHORTPTR(data), width, height, stride,
- border_horz, border_vert);
- else
- 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);
-}
-
-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));
-}
-
-static void copy_tile(int width, int height, const uint8_t *src, int src_stride,
- uint8_t *dst, int dst_stride, int highbd) {
- if (highbd)
- copy_tile_highbd(width, height, CONVERT_TO_SHORTPTR(src), src_stride,
- CONVERT_TO_SHORTPTR(dst), dst_stride);
- else
- copy_tile_lowbd(width, height, src, src_stride, dst, dst_stride);
-}
-
-#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 we need to
-// fill out a border of 3 pixels above/below the stripe according to the
-// following
-// rules:
-//
-// * At a frame boundary, we copy the outermost row of CDEF pixels three times.
-// This extension is done by a call to extend_frame() at the start of the loop
-// restoration process, so the value of copy_above/copy_below doesn't strictly
-// matter.
-// However, by setting *copy_above = *copy_below = 1 whenever loop filtering
-// across tiles is disabled, we can allow
-// {setup,restore}_processing_stripe_boundary to assume that the top/bottom
-// data has always been copied, simplifying the behaviour at the left and
-// right edges of tiles.
-//
-// * If we're at a tile boundary and loop filtering across tiles is enabled,
-// then there is a logical stripe which is 64 pixels high, but which is split
-// into an 8px high and a 56px high stripe so that the processing (and
-// coefficient set usage) can be aligned to tiles.
-// In this case, we use the 3 rows of CDEF output across the boundary for
-// context; this corresponds to leaving the frame buffer as-is.
-//
-// * If we're at a tile boundary and loop filtering across tiles is disabled,
-// then we take the outermost row of CDEF pixels *within the current tile*
-// and copy it three times. Thus we behave exactly as if the tile were a full
-// frame.
-//
-// * Otherwise, we're at a stripe boundary within a tile. In that case, we
-// take 2 rows of deblocked pixels and extend them to 3 rows of context.
-//
-// The distinction between the latter two cases is handled by the
-// av1_loop_restoration_save_boundary_lines() function, so here we just need
-// to decide if we're overwriting the above/below boundary pixels or not.
-static void get_stripe_boundary_info(const RestorationTileLimits *limits,
- const AV1PixelRect *tile_rect, int ss_y,
- int *copy_above, int *copy_below) {
- *copy_above = 1;
- *copy_below = 1;
-
- const int full_stripe_height = RESTORATION_PROC_UNIT_SIZE >> ss_y;
- const int runit_offset = RESTORATION_UNIT_OFFSET >> ss_y;
-
- const int first_stripe_in_tile = (limits->v_start == tile_rect->top);
- const int this_stripe_height =
- full_stripe_height - (first_stripe_in_tile ? runit_offset : 0);
- const int last_stripe_in_tile =
- (limits->v_start + this_stripe_height >= tile_rect->bottom);
-
- if (first_stripe_in_tile) *copy_above = 0;
- if (last_stripe_in_tile) *copy_below = 0;
-}
-
-// Overwrite the border pixels around a processing stripe so that the conditions
-// listed above get_stripe_boundary_info() are preserved.
-// We save the pixels which get overwritten into a temporary buffer, so that
-// they can be restored by restore_processing_stripe_boundary() after we've
-// processed the stripe.
-//
-// limits gives the rectangular limits of the remaining stripes for the current
-// restoration unit. rsb is the stored stripe boundaries (taken from either
-// deblock or CDEF output as necessary).
-//
-// tile_rect is the limits of the current tile and tile_stripe0 is the index of
-// the first stripe in this tile (needed to convert the tile-relative stripe
-// index we get from limits into something we can look up in rsb).
-static void setup_processing_stripe_boundary(
- const RestorationTileLimits *limits, const RestorationStripeBoundaries *rsb,
- int rsb_row, int use_highbd, int h, uint8_t *data8, int data_stride,
- RestorationLineBuffers *rlbs, int copy_above, int copy_below, int opt) {
- // Offsets within the line buffers. The buffer logically starts at column
- // -RESTORATION_EXTRA_HORZ so the 1st column (at x0 - RESTORATION_EXTRA_HORZ)
- // has column x0 in the buffer.
- 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 = limits->h_start - RESTORATION_EXTRA_HORZ;
-
- // Replace RESTORATION_BORDER pixels above the top of the stripe
- // We expand RESTORATION_CTX_VERT=2 lines from rsb->stripe_boundary_above
- // to fill RESTORATION_BORDER=3 lines of above pixels. This is done by
- // duplicating the topmost of the 2 lines (see the AOMMAX call when
- // calculating src_row, which gets the values 0, 0, 1 for i = -3, -2, -1).
- //
- // Special case: If we're at the top of a tile, which isn't on the topmost
- // tile row, and we're allowed to loop filter across tiles, then we have a
- // logical 64-pixel-high stripe which has been split into an 8-pixel high
- // stripe and a 56-pixel high stripe (the current one). So, in this case,
- // we want to leave the boundary alone!
- if (!opt) {
- if (copy_above) {
- uint8_t *data8_tl = data8 + data_x0 + limits->v_start * data_stride;
-
- for (int i = -RESTORATION_BORDER; i < 0; ++i) {
- const int buf_row = rsb_row + AOMMAX(i + RESTORATION_CTX_VERT, 0);
- const int buf_off = buf_x0_off + buf_row * buf_stride;
- const uint8_t *buf =
- rsb->stripe_boundary_above + (buf_off << use_highbd);
- uint8_t *dst8 = data8_tl + i * data_stride;
- // Save old pixels, then replace with data from stripe_boundary_above
- memcpy(rlbs->tmp_save_above[i + RESTORATION_BORDER],
- REAL_PTR(use_highbd, dst8), line_size);
- memcpy(REAL_PTR(use_highbd, dst8), buf, line_size);
- }
- }
-
- // Replace RESTORATION_BORDER pixels below the bottom of the stripe.
- // The second buffer row is repeated, so src_row gets the values 0, 1, 1
- // for i = 0, 1, 2.
- if (copy_below) {
- const int stripe_end = limits->v_start + h;
- uint8_t *data8_bl = data8 + data_x0 + stripe_end * data_stride;
-
- for (int i = 0; i < RESTORATION_BORDER; ++i) {
- const int buf_row = rsb_row + AOMMIN(i, RESTORATION_CTX_VERT - 1);
- const int buf_off = buf_x0_off + buf_row * buf_stride;
- const uint8_t *src =
- rsb->stripe_boundary_below + (buf_off << use_highbd);
-
- uint8_t *dst8 = data8_bl + i * data_stride;
- // Save old pixels, then replace with data from stripe_boundary_below
- memcpy(rlbs->tmp_save_below[i], REAL_PTR(use_highbd, dst8), line_size);
- memcpy(REAL_PTR(use_highbd, dst8), src, line_size);
- }
- }
- } else {
- if (copy_above) {
- uint8_t *data8_tl = data8 + data_x0 + limits->v_start * data_stride;
-
- // Only save and overwrite i=-RESTORATION_BORDER line.
- uint8_t *dst8 = data8_tl + (-RESTORATION_BORDER) * data_stride;
- // Save old pixels, then replace with data from stripe_boundary_above
- memcpy(rlbs->tmp_save_above[0], REAL_PTR(use_highbd, dst8), line_size);
- memcpy(REAL_PTR(use_highbd, dst8),
- REAL_PTR(use_highbd,
- data8_tl + (-RESTORATION_BORDER + 1) * data_stride),
- line_size);
- }
-
- if (copy_below) {
- const int stripe_end = limits->v_start + h;
- uint8_t *data8_bl = data8 + data_x0 + stripe_end * data_stride;
-
- // Only save and overwrite i=2 line.
- uint8_t *dst8 = data8_bl + 2 * data_stride;
- // Save old pixels, then replace with data from stripe_boundary_below
- memcpy(rlbs->tmp_save_below[2], REAL_PTR(use_highbd, dst8), line_size);
- memcpy(REAL_PTR(use_highbd, dst8),
- REAL_PTR(use_highbd, data8_bl + (2 - 1) * data_stride), line_size);
- }
- }
-}
-
-// This function restores the boundary lines modified by
-// setup_processing_stripe_boundary.
-//
-// Note: We need to be careful when handling the corners of the processing
-// unit, because (eg.) the top-left corner is considered to be part of
-// both the left and top borders. This means that, depending on the
-// loop_filter_across_tiles_enabled flag, the corner pixels might get
-// overwritten twice, once as part of the "top" border and once as part
-// of the "left" border (or similar for other corners).
-//
-// Everything works out fine as long as we make sure to reverse the order
-// when restoring, ie. we need to restore the left/right borders followed
-// by the top/bottom borders.
-static void restore_processing_stripe_boundary(
- const RestorationTileLimits *limits, const RestorationLineBuffers *rlbs,
- int use_highbd, int h, uint8_t *data8, int data_stride, int copy_above,
- int copy_below, int opt) {
- 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 = limits->h_start - RESTORATION_EXTRA_HORZ;
-
- if (!opt) {
- if (copy_above) {
- uint8_t *data8_tl = data8 + data_x0 + limits->v_start * data_stride;
- for (int i = -RESTORATION_BORDER; i < 0; ++i) {
- uint8_t *dst8 = data8_tl + i * data_stride;
- memcpy(REAL_PTR(use_highbd, dst8),
- rlbs->tmp_save_above[i + RESTORATION_BORDER], line_size);
- }
- }
-
- if (copy_below) {
- const int stripe_bottom = limits->v_start + h;
- uint8_t *data8_bl = data8 + data_x0 + stripe_bottom * data_stride;
-
- for (int i = 0; i < RESTORATION_BORDER; ++i) {
- if (stripe_bottom + i >= limits->v_end + RESTORATION_BORDER) break;
-
- uint8_t *dst8 = data8_bl + i * data_stride;
- memcpy(REAL_PTR(use_highbd, dst8), rlbs->tmp_save_below[i], line_size);
- }
- }
- } else {
- if (copy_above) {
- uint8_t *data8_tl = data8 + data_x0 + limits->v_start * data_stride;
-
- // Only restore i=-RESTORATION_BORDER line.
- uint8_t *dst8 = data8_tl + (-RESTORATION_BORDER) * data_stride;
- memcpy(REAL_PTR(use_highbd, dst8), rlbs->tmp_save_above[0], line_size);
- }
-
- if (copy_below) {
- const int stripe_bottom = limits->v_start + h;
- uint8_t *data8_bl = data8 + data_x0 + stripe_bottom * data_stride;
-
- // Only restore i=2 line.
- if (stripe_bottom + 2 < limits->v_end + RESTORATION_BORDER) {
- uint8_t *dst8 = data8_bl + 2 * data_stride;
- memcpy(REAL_PTR(use_highbd, dst8), rlbs->tmp_save_below[2], line_size);
- }
- }
- }
-}
-
-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);
- const ConvolveParams conv_params = get_conv_params_wiener(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;
- av1_wiener_convolve_add_src(
- src_p, src_stride, dst_p, dst_stride, rui->wiener_info.hfilter, 16,
- rui->wiener_info.vfilter, 16, w, stripe_height, &conv_params);
- }
-}
-
-/* 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;
- assert(width > 2 * SGRPROJ_BORDER_HORZ);
- assert(height > 2 * SGRPROJ_BORDER_VERT);
-
- // 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;
- assert(width > 2 * SGRPROJ_BORDER_HORZ);
- assert(height > 2 * SGRPROJ_BORDER_VERT);
-
- // 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 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
- assert(0 && "Invalid value of r in self-guided filter");
-}
-
-void decode_xq(const int *xqd, int *xq, const sgr_params_type *params) {
- if (params->r[0] == 0) {
- xq[0] = 0;
- xq[1] = (1 << SGRPROJ_PRJ_BITS) - xqd[1];
- } else if (params->r[1] == 0) {
- xq[0] = xqd[0];
- xq[1] = 0;
- } else {
- xq[0] = xqd[0];
- xq[1] = (1 << SGRPROJ_PRJ_BITS) - xq[0] - xqd[1];
- }
-}
-
-const int32_t x_by_xplus1[256] = {
- // Special case: Map 0 -> 1 (corresponding to a value of 1/256)
- // instead of 0. See comments in selfguided_restoration_internal() for why
- 1, 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,
-};
-
-static void calculate_intermediate_result(int32_t *dgd, int width, int height,
- int dgd_stride, int bit_depth,
- int sgr_params_idx, int radius_idx,
- int pass, int32_t *A, int32_t *B) {
- const sgr_params_type *const params = &sgr_params[sgr_params_idx];
- const int r = params->r[radius_idx];
- const int width_ext = width + 2 * SGRPROJ_BORDER_HORZ;
- const int height_ext = height + 2 * SGRPROJ_BORDER_VERT;
- // 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;
- const int step = pass == 0 ? 1 : 2;
- int i, j;
-
- assert(r <= MAX_RADIUS && "Need MAX_RADIUS >= r");
- assert(r <= SGRPROJ_BORDER_VERT - 1 && r <= SGRPROJ_BORDER_HORZ - 1 &&
- "Need SGRPROJ_BORDER_* >= r+1");
-
- 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);
- A += SGRPROJ_BORDER_VERT * buf_stride + SGRPROJ_BORDER_HORZ;
- B += SGRPROJ_BORDER_VERT * buf_stride + SGRPROJ_BORDER_HORZ;
- // Calculate the eventual A[] and B[] arrays. Include a 1-pixel border - ie,
- // for a 64x64 processing unit, we calculate 66x66 pixels of A[] and B[].
- for (i = -1; i < height + 1; i += step) {
- for (j = -1; j < width + 1; ++j) {
- const int k = i * buf_stride + j;
- const int n = (2 * r + 1) * (2 * r + 1);
-
- // 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.
- // This bound on p is due to:
- // https://en.wikipedia.org/wiki/Popoviciu's_inequality_on_variances
- //
- // 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;
-
- const uint32_t s = params->s[radius_idx];
-
- // 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);
-
- // Note: We have to be quite careful about the value of A[k].
- // This is used as a blend factor between individual pixel values and the
- // local mean. So it logically has a range of [0, 256], including both
- // endpoints.
- //
- // This is a pain for hardware, as we'd like something which can be stored
- // in exactly 8 bits.
- // Further, in the calculation of B[k] below, if z == 0 and r == 2,
- // then A[k] "should be" 0. But then we can end up setting B[k] to a value
- // slightly above 2^(8 + bit depth), due to rounding in the value of
- // one_by_x[25-1].
- //
- // Thus we saturate so that, when z == 0, A[k] is set to 1 instead of 0.
- // This fixes the above issues (256 - A[k] fits in a uint8, and we can't
- // overflow), without significantly affecting the final result: z == 0
- // implies that the image is essentially "flat", so the local mean and
- // individual pixel values are very similar.
- //
- // Note that saturating on the other side, ie. requring A[k] <= 255,
- // would be a bad idea, as that corresponds to the case where the image
- // is very variable, when we want to preserve the local pixel value as
- // much as possible.
- A[k] = x_by_xplus1[AOMMIN(z, 255)]; // in range [1, 256]
-
- // SGRPROJ_SGR - A[k] < 2^8 (from above), 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)
- // This holds even with the rounding in one_by_x and in the overall
- // result, as long as SGRPROJ_SGR - A[k] is strictly less than 2^8.
- 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);
- }
- }
-}
-
-static void selfguided_restoration_fast_internal(
- int32_t *dgd, int width, int height, int dgd_stride, int32_t *dst,
- int dst_stride, int bit_depth, int sgr_params_idx, int radius_idx) {
- const sgr_params_type *const params = &sgr_params[sgr_params_idx];
- const int r = params->r[radius_idx];
- const int width_ext = width + 2 * SGRPROJ_BORDER_HORZ;
- // 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_;
- int i, j;
- calculate_intermediate_result(dgd, width, height, dgd_stride, bit_depth,
- sgr_params_idx, radius_idx, 1, A, B);
- A += SGRPROJ_BORDER_VERT * buf_stride + SGRPROJ_BORDER_HORZ;
- B += SGRPROJ_BORDER_VERT * buf_stride + SGRPROJ_BORDER_HORZ;
-
- // Use the A[] and B[] arrays to calculate the filtered image
- (void)r;
- assert(r == 2);
- for (i = 0; i < height; ++i) {
- if (!(i & 1)) { // even row
- for (j = 0; j < width; ++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 - buf_stride] + A[k + buf_stride]) * 6 +
- (A[k - 1 - buf_stride] + A[k - 1 + buf_stride] +
- A[k + 1 - buf_stride] + A[k + 1 + buf_stride]) *
- 5;
- const int32_t b = (B[k - buf_stride] + B[k + buf_stride]) * 6 +
- (B[k - 1 - buf_stride] + B[k - 1 + buf_stride] +
- B[k + 1 - buf_stride] + B[k + 1 + buf_stride]) *
- 5;
- const int32_t v = a * dgd[l] + b;
- dst[m] =
- ROUND_POWER_OF_TWO(v, SGRPROJ_SGR_BITS + nb - SGRPROJ_RST_BITS);
- }
- } else { // odd row
- for (j = 0; j < width; ++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 = 4;
- const int32_t a = A[k] * 6 + (A[k - 1] + A[k + 1]) * 5;
- const int32_t b = B[k] * 6 + (B[k - 1] + B[k + 1]) * 5;
- const int32_t v = a * dgd[l] + b;
- dst[m] =
- ROUND_POWER_OF_TWO(v, SGRPROJ_SGR_BITS + nb - SGRPROJ_RST_BITS);
- }
- }
- }
-}
-
-static void selfguided_restoration_internal(int32_t *dgd, int width, int height,
- int dgd_stride, int32_t *dst,
- int dst_stride, int bit_depth,
- int sgr_params_idx,
- int radius_idx) {
- const int width_ext = width + 2 * SGRPROJ_BORDER_HORZ;
- // 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_;
- int i, j;
- calculate_intermediate_result(dgd, width, height, dgd_stride, bit_depth,
- sgr_params_idx, radius_idx, 0, A, B);
- A += SGRPROJ_BORDER_VERT * buf_stride + SGRPROJ_BORDER_HORZ;
- B += SGRPROJ_BORDER_VERT * buf_stride + SGRPROJ_BORDER_HORZ;
-
- // Use the A[] and B[] arrays to calculate the filtered image
- for (i = 0; i < height; ++i) {
- for (j = 0; j < width; ++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);
- }
- }
-}
-
-int av1_selfguided_restoration_c(const uint8_t *dgd8, int width, int height,
- int dgd_stride, int32_t *flt0, int32_t *flt1,
- int flt_stride, int sgr_params_idx,
- int bit_depth, int highbd) {
- 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;
-
- if (highbd) {
- const uint16_t *dgd16 = CONVERT_TO_SHORTPTR(dgd8);
- for (int i = -SGRPROJ_BORDER_VERT; i < height + SGRPROJ_BORDER_VERT; ++i) {
- for (int j = -SGRPROJ_BORDER_HORZ; j < width + SGRPROJ_BORDER_HORZ; ++j) {
- dgd32[i * dgd32_stride + j] = dgd16[i * dgd_stride + j];
- }
- }
- } else {
- for (int i = -SGRPROJ_BORDER_VERT; i < height + SGRPROJ_BORDER_VERT; ++i) {
- for (int j = -SGRPROJ_BORDER_HORZ; j < width + SGRPROJ_BORDER_HORZ; ++j) {
- dgd32[i * dgd32_stride + j] = dgd8[i * dgd_stride + j];
- }
- }
- }
-
- const sgr_params_type *const params = &sgr_params[sgr_params_idx];
- // If params->r == 0 we skip the corresponding filter. We only allow one of
- // the radii to be 0, as having both equal to 0 would be equivalent to
- // skipping SGR entirely.
- assert(!(params->r[0] == 0 && params->r[1] == 0));
-
- if (params->r[0] > 0)
- selfguided_restoration_fast_internal(dgd32, width, height, dgd32_stride,
- flt0, flt_stride, bit_depth,
- sgr_params_idx, 0);
- if (params->r[1] > 0)
- selfguided_restoration_internal(dgd32, width, height, dgd32_stride, flt1,
- flt_stride, bit_depth, sgr_params_idx, 1);
- return 0;
-}
-
-void apply_selfguided_restoration_c(const uint8_t *dat8, int width, int height,
- int stride, int eps, const int *xqd,
- uint8_t *dst8, int dst_stride,
- int32_t *tmpbuf, int bit_depth,
- int highbd) {
- int32_t *flt0 = tmpbuf;
- int32_t *flt1 = flt0 + RESTORATION_UNITPELS_MAX;
- assert(width * height <= RESTORATION_UNITPELS_MAX);
-
- const int ret = av1_selfguided_restoration_c(
- dat8, width, height, stride, flt0, flt1, width, eps, bit_depth, highbd);
- (void)ret;
- assert(!ret);
- const sgr_params_type *const params = &sgr_params[eps];
- int xq[2];
- decode_xq(xqd, xq, params);
- for (int i = 0; i < height; ++i) {
- for (int j = 0; j < width; ++j) {
- const int k = i * width + j;
- uint8_t *dst8ij = dst8 + i * dst_stride + j;
- const uint8_t *dat8ij = dat8 + i * stride + j;
-
- const uint16_t pre_u = highbd ? *CONVERT_TO_SHORTPTR(dat8ij) : *dat8ij;
- const int32_t u = (int32_t)pre_u << SGRPROJ_RST_BITS;
- int32_t v = u << SGRPROJ_PRJ_BITS;
- // If params->r == 0 then we skipped the filtering in
- // av1_selfguided_restoration_c, i.e. flt[k] == u
- if (params->r[0] > 0) v += xq[0] * (flt0[k] - u);
- if (params->r[1] > 0) v += xq[1] * (flt1[k] - u);
- const int16_t w =
- (int16_t)ROUND_POWER_OF_TWO(v, SGRPROJ_PRJ_BITS + SGRPROJ_RST_BITS);
-
- const uint16_t out = clip_pixel_highbd(w, bit_depth);
- if (highbd)
- *CONVERT_TO_SHORTPTR(dst8ij) = out;
- else
- *dst8ij = (uint8_t)out;
- }
- }
-}
-
-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, bit_depth, 0);
- }
-}
-
-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;
- const ConvolveParams conv_params = get_conv_params_wiener(bit_depth);
-
- 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;
- av1_highbd_wiener_convolve_add_src(src8_p, src_stride, dst8_p, dst_stride,
- rui->wiener_info.hfilter, 16,
- rui->wiener_info.vfilter, 16, w,
- stripe_height, &conv_params, 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);
- apply_selfguided_restoration(src8 + j, w, stripe_height, src_stride,
- rui->sgrproj_info.ep, rui->sgrproj_info.xqd,
- dst8 + j, dst_stride, tmpbuf, bit_depth, 1);
- }
-}
-
-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);
-
-#define NUM_STRIPE_FILTERS 4
-
-static const stripe_filter_fun stripe_filters[NUM_STRIPE_FILTERS] = {
- wiener_filter_stripe, sgrproj_filter_stripe, wiener_filter_stripe_highbd,
- sgrproj_filter_stripe_highbd
-};
-
-// Filter one restoration unit
-void av1_loop_restoration_filter_unit(
- const RestorationTileLimits *limits, const RestorationUnitInfo *rui,
- const RestorationStripeBoundaries *rsb, RestorationLineBuffers *rlbs,
- const AV1PixelRect *tile_rect, int tile_stripe0, int ss_x, int ss_y,
- int highbd, int bit_depth, uint8_t *data8, int stride, uint8_t *dst8,
- int dst_stride, int32_t *tmpbuf, int optimized_lr) {
- 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];
-
- const int procunit_width = RESTORATION_PROC_UNIT_SIZE >> ss_x;
-
- // Convolve the whole tile one stripe at a time
- RestorationTileLimits remaining_stripes = *limits;
- int i = 0;
- while (i < unit_h) {
- int copy_above, copy_below;
- remaining_stripes.v_start = limits->v_start + i;
-
- get_stripe_boundary_info(&remaining_stripes, tile_rect, ss_y, &copy_above,
- &copy_below);
-
- const int full_stripe_height = RESTORATION_PROC_UNIT_SIZE >> ss_y;
- const int runit_offset = RESTORATION_UNIT_OFFSET >> ss_y;
-
- // Work out where this stripe's boundaries are within
- // rsb->stripe_boundary_{above,below}
- const int tile_stripe =
- (remaining_stripes.v_start - tile_rect->top + runit_offset) /
- full_stripe_height;
- const int frame_stripe = tile_stripe0 + tile_stripe;
- const int rsb_row = RESTORATION_CTX_VERT * frame_stripe;
-
- // Calculate this stripe's height, based on two rules:
- // * The topmost stripe in each tile is 8 luma pixels shorter than usual.
- // * We can't extend past the end of the current restoration unit
- const int nominal_stripe_height =
- full_stripe_height - ((tile_stripe == 0) ? runit_offset : 0);
- const int h = AOMMIN(nominal_stripe_height,
- remaining_stripes.v_end - remaining_stripes.v_start);
-
- setup_processing_stripe_boundary(&remaining_stripes, rsb, rsb_row, highbd,
- h, data8, stride, rlbs, copy_above,
- copy_below, optimized_lr);
-
- stripe_filter(rui, unit_w, h, procunit_width, data8_tl + i * stride, stride,
- dst8_tl + i * dst_stride, dst_stride, tmpbuf, bit_depth);
-
- restore_processing_stripe_boundary(&remaining_stripes, rlbs, highbd, h,
- data8, stride, copy_above, copy_below,
- optimized_lr);
-
- i += h;
- }
-}
-
-static void filter_frame_on_tile(int tile_row, int tile_col, void *priv,
- AV1_COMMON *cm) {
- (void)tile_col;
- FilterFrameCtxt *ctxt = (FilterFrameCtxt *)priv;
- ctxt->tile_stripe0 = (tile_row == 0) ? 0 : cm->rst_end_stripe[tile_row - 1];
-}
-
-static void filter_frame_on_unit(const RestorationTileLimits *limits,
- const AV1PixelRect *tile_rect,
- int rest_unit_idx, void *priv, int32_t *tmpbuf,
- RestorationLineBuffers *rlbs) {
- FilterFrameCtxt *ctxt = (FilterFrameCtxt *)priv;
- const RestorationInfo *rsi = ctxt->rsi;
-
- av1_loop_restoration_filter_unit(
- limits, &rsi->unit_info[rest_unit_idx], &rsi->boundaries, rlbs, tile_rect,
- ctxt->tile_stripe0, ctxt->ss_x, ctxt->ss_y, ctxt->highbd, ctxt->bit_depth,
- ctxt->data8, ctxt->data_stride, ctxt->dst8, ctxt->dst_stride, tmpbuf,
- rsi->optimized_lr);
-}
-
-void av1_loop_restoration_filter_frame_init(AV1LrStruct *lr_ctxt,
- YV12_BUFFER_CONFIG *frame,
- AV1_COMMON *cm, int optimized_lr,
- int num_planes) {
- const SequenceHeader *const seq_params = &cm->seq_params;
- const int bit_depth = seq_params->bit_depth;
- const int highbd = seq_params->use_highbitdepth;
- lr_ctxt->dst = &cm->rst_frame;
-
- const int frame_width = frame->crop_widths[0];
- const int frame_height = frame->crop_heights[0];
- if (aom_realloc_frame_buffer(
- lr_ctxt->dst, frame_width, frame_height, seq_params->subsampling_x,
- seq_params->subsampling_y, highbd, 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");
-
- lr_ctxt->on_rest_unit = filter_frame_on_unit;
- lr_ctxt->frame = frame;
- for (int plane = 0; plane < num_planes; ++plane) {
- RestorationInfo *rsi = &cm->rst_info[plane];
- RestorationType rtype = rsi->frame_restoration_type;
- rsi->optimized_lr = optimized_lr;
-
- if (rtype == RESTORE_NONE) {
- 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];
- FilterFrameCtxt *lr_plane_ctxt = &lr_ctxt->ctxt[plane];
-
- extend_frame(frame->buffers[plane], plane_width, plane_height,
- frame->strides[is_uv], RESTORATION_BORDER, RESTORATION_BORDER,
- highbd);
-
- lr_plane_ctxt->rsi = rsi;
- lr_plane_ctxt->ss_x = is_uv && seq_params->subsampling_x;
- lr_plane_ctxt->ss_y = is_uv && seq_params->subsampling_y;
- lr_plane_ctxt->highbd = highbd;
- lr_plane_ctxt->bit_depth = bit_depth;
- lr_plane_ctxt->data8 = frame->buffers[plane];
- lr_plane_ctxt->dst8 = lr_ctxt->dst->buffers[plane];
- lr_plane_ctxt->data_stride = frame->strides[is_uv];
- lr_plane_ctxt->dst_stride = lr_ctxt->dst->strides[is_uv];
- lr_plane_ctxt->tile_rect = av1_whole_frame_rect(cm, is_uv);
- filter_frame_on_tile(LR_TILE_ROW, LR_TILE_COL, lr_plane_ctxt, cm);
- }
-}
-
-void av1_loop_restoration_copy_planes(AV1LrStruct *loop_rest_ctxt,
- AV1_COMMON *cm, int num_planes) {
- typedef void (*copy_fun)(const YV12_BUFFER_CONFIG *src_ybc,
- YV12_BUFFER_CONFIG *dst_ybc, int hstart, int hend,
- int vstart, int vend);
- static const copy_fun copy_funs[3] = {
- aom_yv12_partial_copy_y, aom_yv12_partial_copy_u, aom_yv12_partial_copy_v
- };
-
- for (int plane = 0; plane < num_planes; ++plane) {
- if (cm->rst_info[plane].frame_restoration_type == RESTORE_NONE) continue;
- AV1PixelRect tile_rect = loop_rest_ctxt->ctxt[plane].tile_rect;
- copy_funs[plane](loop_rest_ctxt->dst, loop_rest_ctxt->frame, tile_rect.left,
- tile_rect.right, tile_rect.top, tile_rect.bottom);
- }
-}
-
-static void foreach_rest_unit_in_planes(AV1LrStruct *lr_ctxt, AV1_COMMON *cm,
- int num_planes) {
- FilterFrameCtxt *ctxt = lr_ctxt->ctxt;
-
- for (int plane = 0; plane < num_planes; ++plane) {
- if (cm->rst_info[plane].frame_restoration_type == RESTORE_NONE) {
- continue;
- }
-
- av1_foreach_rest_unit_in_plane(cm, plane, lr_ctxt->on_rest_unit,
- &ctxt[plane], &ctxt[plane].tile_rect,
- cm->rst_tmpbuf, cm->rlbs);
- }
-}
-
-void av1_loop_restoration_filter_frame(YV12_BUFFER_CONFIG *frame,
- AV1_COMMON *cm, int optimized_lr,
- void *lr_ctxt) {
- assert(!cm->all_lossless);
- const int num_planes = av1_num_planes(cm);
-
- AV1LrStruct *loop_rest_ctxt = (AV1LrStruct *)lr_ctxt;
-
- av1_loop_restoration_filter_frame_init(loop_rest_ctxt, frame, cm,
- optimized_lr, num_planes);
-
- foreach_rest_unit_in_planes(loop_rest_ctxt, cm, num_planes);
-
- av1_loop_restoration_copy_planes(loop_rest_ctxt, cm, num_planes);
-}
-
-void av1_foreach_rest_unit_in_row(
- RestorationTileLimits *limits, const AV1PixelRect *tile_rect,
- rest_unit_visitor_t on_rest_unit, int row_number, int unit_size,
- int unit_idx0, int hunits_per_tile, int vunits_per_tile, int plane,
- void *priv, int32_t *tmpbuf, RestorationLineBuffers *rlbs,
- sync_read_fn_t on_sync_read, sync_write_fn_t on_sync_write,
- struct AV1LrSyncData *const lr_sync) {
- const int tile_w = tile_rect->right - tile_rect->left;
- const int ext_size = unit_size * 3 / 2;
- 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 + row_number * hunits_per_tile + j;
-
- // No sync for even numbered rows
- // For odd numbered rows, Loop Restoration of current block requires the LR
- // of top-right and bottom-right blocks to be completed
-
- // top-right sync
- on_sync_read(lr_sync, row_number, j, plane);
- if ((row_number + 1) < vunits_per_tile)
- // bottom-right sync
- on_sync_read(lr_sync, row_number + 2, j, plane);
-
- on_rest_unit(limits, tile_rect, unit_idx, priv, tmpbuf, rlbs);
-
- on_sync_write(lr_sync, row_number, j, hunits_per_tile, plane);
-
- x0 += w;
- ++j;
- }
-}
-
-void av1_lr_sync_read_dummy(void *const lr_sync, int r, int c, int plane) {
- (void)lr_sync;
- (void)r;
- (void)c;
- (void)plane;
-}
-
-void av1_lr_sync_write_dummy(void *const lr_sync, int r, int c,
- const int sb_cols, int plane) {
- (void)lr_sync;
- (void)r;
- (void)c;
- (void)sb_cols;
- (void)plane;
-}
-
-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 vunits_per_tile, int units_per_tile, int unit_size,
- int ss_y, int plane, rest_unit_visitor_t on_rest_unit, void *priv,
- int32_t *tmpbuf, RestorationLineBuffers *rlbs) {
- 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);
- // Offset the tile upwards to align with the restoration processing stripe
- const int voffset = RESTORATION_UNIT_OFFSET >> ss_y;
- limits.v_start = AOMMAX(tile_rect->top, limits.v_start - voffset);
- if (limits.v_end < tile_rect->bottom) limits.v_end -= voffset;
-
- av1_foreach_rest_unit_in_row(
- &limits, tile_rect, on_rest_unit, i, unit_size, unit_idx0,
- hunits_per_tile, vunits_per_tile, plane, priv, tmpbuf, rlbs,
- av1_lr_sync_read_dummy, av1_lr_sync_write_dummy, NULL);
-
- y0 += h;
- ++i;
- }
-}
-
-void av1_foreach_rest_unit_in_plane(const struct AV1Common *cm, int plane,
- rest_unit_visitor_t on_rest_unit,
- void *priv, AV1PixelRect *tile_rect,
- int32_t *tmpbuf,
- RestorationLineBuffers *rlbs) {
- const int is_uv = plane > 0;
- const int ss_y = is_uv && cm->seq_params.subsampling_y;
-
- const RestorationInfo *rsi = &cm->rst_info[plane];
-
- foreach_rest_unit_in_tile(tile_rect, LR_TILE_ROW, LR_TILE_COL, LR_TILE_COLS,
- rsi->horz_units_per_tile, rsi->vert_units_per_tile,
- rsi->units_per_tile, rsi->restoration_unit_size,
- ss_y, plane, on_rest_unit, priv, tmpbuf, rlbs);
-}
-
-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) {
- assert(rcol0 && rcol1 && rrow0 && rrow1);
-
- if (bsize != cm->seq_params.sb_size) return 0;
- if (cm->rst_info[plane].frame_restoration_type == RESTORE_NONE) return 0;
-
- assert(!cm->all_lossless);
-
- const int is_uv = plane > 0;
-
- const AV1PixelRect tile_rect = av1_whole_frame_rect(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 = 0;
- const int mi_left = 0;
-
- // 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];
-
- const RestorationInfo *rsi = &cm->rst_info[plane];
- const int size = rsi->restoration_unit_size;
-
- // 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 = av1_lr_count_units_in_tile(size, tile_w);
- const int vert_units = av1_lr_count_units_in_tile(size, tile_h);
-
- // The size of an MI-unit on this plane of the image
- const int ss_x = is_uv && cm->seq_params.subsampling_x;
- const int ss_y = is_uv && cm->seq_params.subsampling_y;
- const int mi_size_x = MI_SIZE >> ss_x;
- const int mi_size_y = MI_SIZE >> ss_y;
-
- // Write m for the relative mi column or row, D for the superres denominator
- // and N for the superres numerator. If u is the upscaled pixel offset then
- // we can write the downscaled pixel offset in two ways as:
- //
- // MI_SIZE * m = N / D u
- //
- // from which we get u = D * MI_SIZE * m / N
- const int mi_to_num_x = av1_superres_scaled(cm)
- ? mi_size_x * cm->superres_scale_denominator
- : mi_size_x;
- const int mi_to_num_y = mi_size_y;
- const int denom_x = av1_superres_scaled(cm) ? size * SCALE_NUMERATOR : size;
- const int denom_y = size;
-
- const int rnd_x = denom_x - 1;
- const int rnd_y = denom_y - 1;
-
- // 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 runit column 10.1, the first matching runit has column
- // index 11)
- *rcol0 = (mi_rel_col0 * mi_to_num_x + rnd_x) / denom_x;
- *rrow0 = (mi_rel_row0 * mi_to_num_y + rnd_y) / denom_y;
-
- // 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_x + rnd_x) / denom_x, horz_units);
- *rrow1 = AOMMIN((mi_rel_row1 * mi_to_num_y + rnd_y) / denom_y, vert_units);
-
- return *rcol0 < *rcol1 && *rrow0 < *rrow1;
-}
-
-// Extend to left and right
-static void extend_lines(uint8_t *buf, int width, int height, int stride,
- int extend, int use_highbitdepth) {
- for (int i = 0; i < height; ++i) {
- if (use_highbitdepth) {
- uint16_t *buf16 = (uint16_t *)buf;
- aom_memset16(buf16 - extend, buf16[0], extend);
- aom_memset16(buf16 + width, buf16[width - 1], extend);
- } else {
- memset(buf - extend, buf[0], extend);
- memset(buf + width, buf[width - 1], extend);
- }
- buf += stride;
- }
-}
-
-static void save_deblock_boundary_lines(
- const YV12_BUFFER_CONFIG *frame, const AV1_COMMON *cm, int plane, int row,
- int stripe, int use_highbd, int is_above,
- RestorationStripeBoundaries *boundaries) {
- const int is_uv = plane > 0;
- const uint8_t *src_buf = REAL_PTR(use_highbd, frame->buffers[plane]);
- const int src_stride = frame->strides[is_uv] << use_highbd;
- const uint8_t *src_rows = src_buf + row * src_stride;
-
- uint8_t *bdry_buf = is_above ? boundaries->stripe_boundary_above
- : boundaries->stripe_boundary_below;
- uint8_t *bdry_start = bdry_buf + (RESTORATION_EXTRA_HORZ << use_highbd);
- const int bdry_stride = boundaries->stripe_boundary_stride << use_highbd;
- uint8_t *bdry_rows = bdry_start + RESTORATION_CTX_VERT * stripe * bdry_stride;
-
- // There is a rare case in which a processing stripe can end 1px above the
- // crop border. In this case, we do want to use deblocked pixels from below
- // the stripe (hence why we ended up in this function), but instead of
- // fetching 2 "below" rows we need to fetch one and duplicate it.
- // This is equivalent to clamping the sample locations against the crop border
- const int lines_to_save =
- AOMMIN(RESTORATION_CTX_VERT, frame->crop_heights[is_uv] - row);
- assert(lines_to_save == 1 || lines_to_save == 2);
-
- int upscaled_width;
- int line_bytes;
- if (av1_superres_scaled(cm)) {
- const int ss_x = is_uv && cm->seq_params.subsampling_x;
- upscaled_width = (cm->superres_upscaled_width + ss_x) >> ss_x;
- line_bytes = upscaled_width << use_highbd;
- if (use_highbd)
- av1_upscale_normative_rows(
- cm, CONVERT_TO_BYTEPTR(src_rows), frame->strides[is_uv],
- CONVERT_TO_BYTEPTR(bdry_rows), boundaries->stripe_boundary_stride,
- plane, lines_to_save);
- else
- av1_upscale_normative_rows(cm, src_rows, frame->strides[is_uv], bdry_rows,
- boundaries->stripe_boundary_stride, plane,
- lines_to_save);
- } else {
- upscaled_width = frame->crop_widths[is_uv];
- line_bytes = upscaled_width << use_highbd;
- for (int i = 0; i < lines_to_save; i++) {
- memcpy(bdry_rows + i * bdry_stride, src_rows + i * src_stride,
- line_bytes);
- }
- }
- // If we only saved one line, then copy it into the second line buffer
- if (lines_to_save == 1)
- memcpy(bdry_rows + bdry_stride, bdry_rows, line_bytes);
-
- extend_lines(bdry_rows, upscaled_width, RESTORATION_CTX_VERT, bdry_stride,
- RESTORATION_EXTRA_HORZ, use_highbd);
-}
-
-static void save_cdef_boundary_lines(const YV12_BUFFER_CONFIG *frame,
- const AV1_COMMON *cm, int plane, int row,
- int stripe, int use_highbd, int is_above,
- RestorationStripeBoundaries *boundaries) {
- const int is_uv = plane > 0;
- const uint8_t *src_buf = REAL_PTR(use_highbd, frame->buffers[plane]);
- const int src_stride = frame->strides[is_uv] << use_highbd;
- const uint8_t *src_rows = src_buf + row * src_stride;
-
- uint8_t *bdry_buf = is_above ? boundaries->stripe_boundary_above
- : boundaries->stripe_boundary_below;
- uint8_t *bdry_start = bdry_buf + (RESTORATION_EXTRA_HORZ << use_highbd);
- const int bdry_stride = boundaries->stripe_boundary_stride << use_highbd;
- uint8_t *bdry_rows = bdry_start + RESTORATION_CTX_VERT * stripe * bdry_stride;
- const int src_width = frame->crop_widths[is_uv];
-
- // At the point where this function is called, we've already applied
- // superres. So we don't need to extend the lines here, we can just
- // pull directly from the topmost row of the upscaled frame.
- const int ss_x = is_uv && cm->seq_params.subsampling_x;
- const int upscaled_width = av1_superres_scaled(cm)
- ? (cm->superres_upscaled_width + ss_x) >> ss_x
- : src_width;
- const int line_bytes = upscaled_width << use_highbd;
- for (int i = 0; i < RESTORATION_CTX_VERT; i++) {
- // Copy the line at 'row' into both context lines. This is because
- // we want to (effectively) extend the outermost row of CDEF data
- // from this tile to produce a border, rather than using deblocked
- // pixels from the tile above/below.
- memcpy(bdry_rows + i * bdry_stride, src_rows, line_bytes);
- }
- extend_lines(bdry_rows, upscaled_width, RESTORATION_CTX_VERT, bdry_stride,
- RESTORATION_EXTRA_HORZ, use_highbd);
-}
-
-static void save_tile_row_boundary_lines(const YV12_BUFFER_CONFIG *frame,
- int use_highbd, int plane,
- AV1_COMMON *cm, int after_cdef) {
- const int is_uv = plane > 0;
- const int ss_y = is_uv && cm->seq_params.subsampling_y;
- const int stripe_height = RESTORATION_PROC_UNIT_SIZE >> ss_y;
- const int stripe_off = RESTORATION_UNIT_OFFSET >> ss_y;
-
- // Get the tile rectangle, with height rounded up to the next multiple of 8
- // luma pixels (only relevant for the bottom tile of the frame)
- const AV1PixelRect tile_rect = av1_whole_frame_rect(cm, is_uv);
- const int stripe0 = 0;
-
- RestorationStripeBoundaries *boundaries = &cm->rst_info[plane].boundaries;
-
- const int plane_height = ROUND_POWER_OF_TWO(cm->height, ss_y);
-
- int tile_stripe;
- for (tile_stripe = 0;; ++tile_stripe) {
- const int rel_y0 = AOMMAX(0, tile_stripe * stripe_height - stripe_off);
- const int y0 = tile_rect.top + rel_y0;
- if (y0 >= tile_rect.bottom) break;
-
- const int rel_y1 = (tile_stripe + 1) * stripe_height - stripe_off;
- const int y1 = AOMMIN(tile_rect.top + rel_y1, tile_rect.bottom);
-
- const int frame_stripe = stripe0 + tile_stripe;
-
- // In this case, we should only use CDEF pixels at the top
- // and bottom of the frame as a whole; internal tile boundaries
- // can use deblocked pixels from adjacent tiles for context.
- const int use_deblock_above = (frame_stripe > 0);
- const int use_deblock_below = (y1 < plane_height);
-
- if (!after_cdef) {
- // Save deblocked context where needed.
- if (use_deblock_above) {
- save_deblock_boundary_lines(frame, cm, plane, y0 - RESTORATION_CTX_VERT,
- frame_stripe, use_highbd, 1, boundaries);
- }
- if (use_deblock_below) {
- save_deblock_boundary_lines(frame, cm, plane, y1, frame_stripe,
- use_highbd, 0, boundaries);
- }
- } else {
- // Save CDEF context where needed. Note that we need to save the CDEF
- // context for a particular boundary iff we *didn't* save deblocked
- // context for that boundary.
- //
- // In addition, we need to save copies of the outermost line within
- // the tile, rather than using data from outside the tile.
- if (!use_deblock_above) {
- save_cdef_boundary_lines(frame, cm, plane, y0, frame_stripe, use_highbd,
- 1, boundaries);
- }
- if (!use_deblock_below) {
- save_cdef_boundary_lines(frame, cm, plane, y1 - 1, frame_stripe,
- use_highbd, 0, boundaries);
- }
- }
- }
-}
-
-// For each RESTORATION_PROC_UNIT_SIZE 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, int after_cdef) {
- const int num_planes = av1_num_planes(cm);
- const int use_highbd = cm->seq_params.use_highbitdepth;
- for (int p = 0; p < num_planes; ++p) {
- save_tile_row_boundary_lines(frame, use_highbd, p, cm, after_cdef);
- }
-}