Coverage Report

Created: 2026-07-30 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/svt-av1/Source/Lib/Codec/super_res.c
Line
Count
Source
1
/*
2
 * Copyright(c) 2019 Netflix, Inc.
3
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
4
 *
5
 * This source code is subject to the terms of the BSD 2 Clause License and
6
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
7
 * was not distributed with this source code in the LICENSE file, you can
8
 * obtain it at https://www.aomedia.org/license/software-license. If the Alliance for Open
9
 * Media Patent License 1.0 was not distributed with this source code in the
10
 * PATENTS file, you can obtain it at https://www.aomedia.org/license/patent-license.
11
 */
12
13
#include "resize.h"
14
#include "utility.h"
15
#include "super_res.h"
16
#include "intra_prediction.h"
17
18
#define FILTER_BITS 7
19
20
const int16_t svt_av1_resize_filter_normative[(1 << RS_SUBPEL_BITS)][UPSCALE_NORMATIVE_TAPS] = {
21
#if UPSCALE_NORMATIVE_TAPS == 8
22
    {0, 0, 0, 128, 0, 0, 0, 0},        {0, 0, -1, 128, 2, -1, 0, 0},      {0, 1, -3, 127, 4, -2, 1, 0},
23
    {0, 1, -4, 127, 6, -3, 1, 0},      {0, 2, -6, 126, 8, -3, 1, 0},      {0, 2, -7, 125, 11, -4, 1, 0},
24
    {-1, 2, -8, 125, 13, -5, 2, 0},    {-1, 3, -9, 124, 15, -6, 2, 0},    {-1, 3, -10, 123, 18, -6, 2, -1},
25
    {-1, 3, -11, 122, 20, -7, 3, -1},  {-1, 4, -12, 121, 22, -8, 3, -1},  {-1, 4, -13, 120, 25, -9, 3, -1},
26
    {-1, 4, -14, 118, 28, -9, 3, -1},  {-1, 4, -15, 117, 30, -10, 4, -1}, {-1, 5, -16, 116, 32, -11, 4, -1},
27
    {-1, 5, -16, 114, 35, -12, 4, -1}, {-1, 5, -17, 112, 38, -12, 4, -1}, {-1, 5, -18, 111, 40, -13, 5, -1},
28
    {-1, 5, -18, 109, 43, -14, 5, -1}, {-1, 6, -19, 107, 45, -14, 5, -1}, {-1, 6, -19, 105, 48, -15, 5, -1},
29
    {-1, 6, -19, 103, 51, -16, 5, -1}, {-1, 6, -20, 101, 53, -16, 6, -1}, {-1, 6, -20, 99, 56, -17, 6, -1},
30
    {-1, 6, -20, 97, 58, -17, 6, -1},  {-1, 6, -20, 95, 61, -18, 6, -1},  {-2, 7, -20, 93, 64, -18, 6, -2},
31
    {-2, 7, -20, 91, 66, -19, 6, -1},  {-2, 7, -20, 88, 69, -19, 6, -1},  {-2, 7, -20, 86, 71, -19, 6, -1},
32
    {-2, 7, -20, 84, 74, -20, 7, -2},  {-2, 7, -20, 81, 76, -20, 7, -1},  {-2, 7, -20, 79, 79, -20, 7, -2},
33
    {-1, 7, -20, 76, 81, -20, 7, -2},  {-2, 7, -20, 74, 84, -20, 7, -2},  {-1, 6, -19, 71, 86, -20, 7, -2},
34
    {-1, 6, -19, 69, 88, -20, 7, -2},  {-1, 6, -19, 66, 91, -20, 7, -2},  {-2, 6, -18, 64, 93, -20, 7, -2},
35
    {-1, 6, -18, 61, 95, -20, 6, -1},  {-1, 6, -17, 58, 97, -20, 6, -1},  {-1, 6, -17, 56, 99, -20, 6, -1},
36
    {-1, 6, -16, 53, 101, -20, 6, -1}, {-1, 5, -16, 51, 103, -19, 6, -1}, {-1, 5, -15, 48, 105, -19, 6, -1},
37
    {-1, 5, -14, 45, 107, -19, 6, -1}, {-1, 5, -14, 43, 109, -18, 5, -1}, {-1, 5, -13, 40, 111, -18, 5, -1},
38
    {-1, 4, -12, 38, 112, -17, 5, -1}, {-1, 4, -12, 35, 114, -16, 5, -1}, {-1, 4, -11, 32, 116, -16, 5, -1},
39
    {-1, 4, -10, 30, 117, -15, 4, -1}, {-1, 3, -9, 28, 118, -14, 4, -1},  {-1, 3, -9, 25, 120, -13, 4, -1},
40
    {-1, 3, -8, 22, 121, -12, 4, -1},  {-1, 3, -7, 20, 122, -11, 3, -1},  {-1, 2, -6, 18, 123, -10, 3, -1},
41
    {0, 2, -6, 15, 124, -9, 3, -1},    {0, 2, -5, 13, 125, -8, 2, -1},    {0, 1, -4, 11, 125, -7, 2, 0},
42
    {0, 1, -3, 8, 126, -6, 2, 0},      {0, 1, -3, 6, 127, -4, 1, 0},      {0, 1, -2, 4, 127, -3, 1, 0},
43
    {0, 0, -1, 2, 128, -1, 0, 0},
44
#else
45
#error "Invalid value of UPSCALE_NORMATIVE_TAPS"
46
#endif // UPSCALE_NORMATIVE_TAPS == 8
47
};
48
49
// Calculates the scaled dimension given the original dimension and the scale
50
// denominator.
51
0
void calculate_scaled_size_helper(uint16_t* dim, uint8_t denom) {
52
0
    if (denom != SCALE_NUMERATOR && denom <= SCALE_DENOMINATOR_MAX) {
53
        // We need to ensure the constraint in "Appendix A" of the spec:
54
        // * FrameWidth is greater than or equal to 16
55
        // * FrameHeight is greater than or equal to 16
56
        // For this, we clamp the downscaled dimension to at least 16. One
57
        // exception: if original dimension itself was < 16, then we keep the
58
        // downscaled dimension to be same as the original, to ensure that resizing
59
        // is valid.
60
0
        const int min_dim = AOMMIN(16, *dim);
61
        // Use this version if we need *dim to be even
62
        // *width = (*width * SCALE_NUMERATOR + denom) / (2 * denom);
63
        // *width <<= 1;
64
0
        *dim = (uint16_t)((*dim * SCALE_NUMERATOR + denom / 2) / (denom));
65
0
        *dim = (uint16_t)AOMMAX(*dim, min_dim);
66
0
    } else if (denom == SCALE_THREE_QUATER) {
67
        // reference scaling resize defines denom 17 as 3/4
68
0
        *dim = (uint16_t)((3 + (*dim * 3)) >> 2);
69
0
    }
70
0
}
71
72
#if CONFIG_ENABLE_RESIZE
73
0
static int32_t av1_get_upscale_convolve_step(int in_length, int out_length) {
74
0
    return ((in_length << RS_SCALE_SUBPEL_BITS) + out_length / 2) / out_length;
75
0
}
76
77
0
static int32_t get_upscale_convolve_x0(int in_length, int out_length, int32_t x_step_qn) {
78
0
    const int     err = out_length * x_step_qn - (in_length << RS_SCALE_SUBPEL_BITS);
79
0
    const int32_t x0  = (-((out_length - in_length) << (RS_SCALE_SUBPEL_BITS - 1)) + out_length / 2) / out_length +
80
0
        RS_SCALE_EXTRA_OFF - err / 2;
81
0
    return (int32_t)((uint32_t)x0 & RS_SCALE_SUBPEL_MASK);
82
0
}
83
#endif // CONFIG_ENABLE_RESIZE
84
85
static void av1_convolve_horiz_rs_c(const uint8_t* src, int src_stride, uint8_t* dst, int dst_stride, int w, int h,
86
0
                                    const int16_t* x_filters, int x0_qn, int x_step_qn) {
87
0
    src -= UPSCALE_NORMATIVE_TAPS / 2 - 1;
88
0
    for (int y = 0; y < h; ++y) {
89
0
        int x_qn = x0_qn;
90
0
        for (int x = 0; x < w; ++x) {
91
0
            const uint8_t* const src_x        = &src[x_qn >> RS_SCALE_SUBPEL_BITS];
92
0
            const int            x_filter_idx = (x_qn & RS_SCALE_SUBPEL_MASK) >> RS_SCALE_EXTRA_BITS;
93
0
            assert(x_filter_idx <= RS_SUBPEL_MASK);
94
0
            const int16_t* const x_filter = &x_filters[x_filter_idx * UPSCALE_NORMATIVE_TAPS];
95
0
            int                  sum      = 0;
96
0
            for (int k = 0; k < UPSCALE_NORMATIVE_TAPS; ++k) {
97
0
                sum += src_x[k] * x_filter[k];
98
0
            }
99
0
            dst[x] = clip_pixel(ROUND_POWER_OF_TWO(sum, FILTER_BITS));
100
0
            x_qn += x_step_qn;
101
0
        }
102
0
        src += src_stride;
103
0
        dst += dst_stride;
104
0
    }
105
0
}
106
107
#if CONFIG_ENABLE_RESIZE
108
static void av1_highbd_convolve_horiz_rs_c(const uint16_t* src, int src_stride, uint16_t* dst, int dst_stride, int w,
109
0
                                           int h, const int16_t* x_filters, int x0_qn, int x_step_qn, int bd) {
110
0
    src -= UPSCALE_NORMATIVE_TAPS / 2 - 1;
111
0
    for (int y = 0; y < h; ++y) {
112
0
        int x_qn = x0_qn;
113
0
        for (int x = 0; x < w; ++x) {
114
0
            const uint16_t* const src_x        = &src[x_qn >> RS_SCALE_SUBPEL_BITS];
115
0
            const int             x_filter_idx = (x_qn & RS_SCALE_SUBPEL_MASK) >> RS_SCALE_EXTRA_BITS;
116
0
            assert(x_filter_idx <= RS_SUBPEL_MASK);
117
0
            const int16_t* const x_filter = &x_filters[x_filter_idx * UPSCALE_NORMATIVE_TAPS];
118
0
            int                  sum      = 0;
119
0
            for (int k = 0; k < UPSCALE_NORMATIVE_TAPS; ++k) {
120
0
                sum += src_x[k] * x_filter[k];
121
0
            }
122
0
            dst[x] = clip_pixel_highbd(ROUND_POWER_OF_TWO(sum, FILTER_BITS), bd);
123
0
            x_qn += x_step_qn;
124
0
        }
125
0
        src += src_stride;
126
0
        dst += dst_stride;
127
0
    }
128
0
}
129
#endif // CONFIG_ENABLE_RESIZE
130
131
void upscale_normative_rect(const uint8_t* const input, int height, int width, int in_stride, uint8_t* output,
132
                            int height2, int width2, int out_stride, int x_step_qn, int x0_qn, int pad_left,
133
0
                            int pad_right) {
134
0
    assert(width > 0);
135
0
    assert(height > 0);
136
0
    assert(width2 > 0);
137
0
    assert(height2 > 0);
138
0
    assert(height2 == height);
139
140
    /* Extend the left/right pixels of the tile column if needed
141
    (either because we can't sample from other tiles, or because we're at
142
    a frame edge).
143
    Save the overwritten pixels into tmp_left and tmp_right.
144
    Note: Because we pass input-1 to av1_convolve_horiz_rs, we need one extra
145
    column of border pixels compared to what we'd naively think.*/
146
0
    const int      border_cols = UPSCALE_NORMATIVE_TAPS / 2 + 1;
147
0
    uint8_t*       tmp_left    = NULL;
148
0
    uint8_t*       tmp_right   = NULL;
149
0
    uint8_t* const in_tl       = (uint8_t*)(input - border_cols);
150
0
    uint8_t* const in_tr       = (uint8_t*)(input + width);
151
152
0
    if (pad_left) {
153
0
        tmp_left = (uint8_t*)svt_aom_malloc(sizeof(*tmp_left) * border_cols * height);
154
0
        for (int i = 0; i < height; i++) {
155
0
            svt_memcpy(tmp_left + i * border_cols, in_tl + i * in_stride, border_cols);
156
0
            memset(in_tl + i * in_stride, input[i * in_stride], border_cols);
157
0
        }
158
0
    }
159
0
    if (pad_right) {
160
0
        tmp_right = (uint8_t*)svt_aom_malloc(sizeof(*tmp_right) * border_cols * height);
161
0
        for (int i = 0; i < height; i++) {
162
0
            svt_memcpy(tmp_right + i * border_cols, in_tr + i * in_stride, border_cols);
163
0
            memset(in_tr + i * in_stride, input[i * in_stride + width - 1], border_cols);
164
0
        }
165
0
    }
166
167
0
    av1_convolve_horiz_rs_c(input - 1,
168
0
                            in_stride,
169
0
                            output,
170
0
                            out_stride,
171
0
                            width2,
172
0
                            height2,
173
0
                            &svt_av1_resize_filter_normative[0][0],
174
0
                            x0_qn,
175
0
                            x_step_qn);
176
177
    /* Restore the left/right border pixels */
178
0
    if (pad_left) {
179
0
        for (int i = 0; i < height; i++) {
180
0
            svt_memcpy(in_tl + i * in_stride, tmp_left + i * border_cols, border_cols);
181
0
        }
182
0
        svt_aom_free(tmp_left);
183
0
    }
184
0
    if (pad_right) {
185
0
        for (int i = 0; i < height; i++) {
186
0
            svt_memcpy(in_tr + i * in_stride, tmp_right + i * border_cols, border_cols);
187
0
        }
188
0
        svt_aom_free(tmp_right);
189
0
    }
190
0
}
191
192
#if CONFIG_ENABLE_RESIZE
193
static void highbd_upscale_normative_rect(const uint8_t* const input, int height, int width, int in_stride,
194
                                          uint8_t* output, int height2, int width2, int out_stride, int x_step_qn,
195
0
                                          int x0_qn, int pad_left, int pad_right, int bd) {
196
0
    assert(width > 0);
197
0
    assert(height > 0);
198
0
    assert(width2 > 0);
199
0
    assert(height2 > 0);
200
0
    assert(height2 == height);
201
202
    /* Extend the left/right pixels of the tile column if needed
203
    (either because we can't sample from other tiles, or because we're at
204
    a frame edge).
205
    Save the overwritten pixels into tmp_left and tmp_right.
206
    Note: Because we pass input-1 to av1_convolve_horiz_rs, we need one extra
207
    column of border pixels compared to what we'd naively think.*/
208
0
    const int       border_cols = UPSCALE_NORMATIVE_TAPS / 2 + 1;
209
0
    const int       border_size = border_cols * sizeof(uint16_t);
210
0
    uint16_t*       tmp_left    = NULL;
211
0
    uint16_t*       tmp_right   = NULL;
212
0
    uint16_t* const input16     = (uint16_t*)input; //CONVERT_TO_SHORTPTR(input);
213
0
    uint16_t* const in_tl       = input16 - border_cols;
214
0
    uint16_t* const in_tr       = input16 + width;
215
0
    if (pad_left) {
216
0
        tmp_left = (uint16_t*)svt_aom_malloc(sizeof(*tmp_left) * border_cols * height);
217
0
        for (int i = 0; i < height; i++) {
218
0
            svt_memcpy(tmp_left + i * border_cols, in_tl + i * in_stride, border_size);
219
0
            svt_aom_memset16(in_tl + i * in_stride, input16[i * in_stride], border_cols);
220
0
        }
221
0
    }
222
0
    if (pad_right) {
223
0
        tmp_right = (uint16_t*)svt_aom_malloc(sizeof(*tmp_right) * border_cols * height);
224
0
        for (int i = 0; i < height; i++) {
225
0
            svt_memcpy(tmp_right + i * border_cols, in_tr + i * in_stride, border_size);
226
0
            svt_aom_memset16(in_tr + i * in_stride, input16[i * in_stride + width - 1], border_cols);
227
0
        }
228
0
    }
229
230
0
    av1_highbd_convolve_horiz_rs_c(((uint16_t*)(input)-1),
231
0
                                   in_stride,
232
0
                                   (uint16_t*)(output),
233
0
                                   out_stride,
234
0
                                   width2,
235
0
                                   height2,
236
0
                                   &svt_av1_resize_filter_normative[0][0],
237
0
                                   x0_qn,
238
0
                                   x_step_qn,
239
0
                                   bd);
240
241
    /*Restore the left/right border pixels*/
242
0
    if (pad_left) {
243
0
        for (int i = 0; i < height; i++) {
244
0
            svt_memcpy(in_tl + i * in_stride, tmp_left + i * border_cols, border_size);
245
0
        }
246
0
        svt_aom_free(tmp_left);
247
0
    }
248
0
    if (pad_right) {
249
0
        for (int i = 0; i < height; i++) {
250
0
            svt_memcpy(in_tr + i * in_stride, tmp_right + i * border_cols, border_size);
251
0
        }
252
0
        svt_aom_free(tmp_right);
253
0
    }
254
0
}
255
#endif // CONFIG_ENABLE_RESIZE
256
257
void svt_av1_upscale_normative_rows(const Av1Common* cm, const uint8_t* src, int src_stride, uint8_t* dst,
258
0
                                    int dst_stride, int rows, int sub_x, int bd, bool is_16bit_pipeline) {
259
#if !CONFIG_ENABLE_RESIZE
260
    // resize/superres disabled in this build; callers are gated off, never reached at runtime.
261
    (void)cm, (void)src, (void)src_stride, (void)dst, (void)dst_stride, (void)rows, (void)sub_x, (void)bd,
262
        (void)is_16bit_pipeline;
263
    assert(0);
264
    return;
265
#else
266
0
    int       high_bd                = bd > EB_EIGHT_BIT || is_16bit_pipeline;
267
0
    const int downscaled_plane_width = ROUND_POWER_OF_TWO(cm->frm_size.frame_width, sub_x);
268
0
    const int upscaled_plane_width   = ROUND_POWER_OF_TWO(cm->frm_size.superres_upscaled_width, sub_x);
269
0
    const int superres_denom         = cm->frm_size.superres_denominator;
270
271
0
    TileInfo      tile_col;
272
0
    const int32_t x_step_qn = av1_get_upscale_convolve_step(downscaled_plane_width, upscaled_plane_width);
273
0
    int32_t       x0_qn     = get_upscale_convolve_x0(downscaled_plane_width, upscaled_plane_width, x_step_qn);
274
0
    for (int j = 0; j < cm->tiles_info.tile_cols; j++) {
275
0
        svt_av1_tile_set_col(&tile_col, &cm->tiles_info, cm->mi_cols, j);
276
277
        /*Determine the limits of this tile column in both the source
278
        and destination images.
279
        Note: The actual location which we start sampling from is
280
        (downscaled_x0 - 1 + (x0_qn/2^14)), and this quantity increases
281
        by exactly dst_width * (x_step_qn/2^14) pixels each iteration.*/
282
0
        const int downscaled_x0 = tile_col.mi_col_start << (MI_SIZE_LOG2 - sub_x);
283
0
        const int downscaled_x1 = tile_col.mi_col_end << (MI_SIZE_LOG2 - sub_x);
284
0
        const int src_width     = downscaled_x1 - downscaled_x0;
285
286
0
        const int upscaled_x0 = (downscaled_x0 * superres_denom) / SCALE_NUMERATOR;
287
0
        int       upscaled_x1;
288
0
        if (j == cm->tiles_info.tile_cols - 1) {
289
            /*Note that we can't just use AOMMIN here - due to rounding,
290
            (downscaled_x1 * superres_denom) / SCALE_NUMERATOR may be less than
291
            upscaled_plane_width.*/
292
0
            upscaled_x1 = upscaled_plane_width;
293
0
        } else {
294
0
            upscaled_x1 = (downscaled_x1 * superres_denom) / SCALE_NUMERATOR;
295
0
        }
296
297
0
        const uint8_t* const src_ptr   = src + (downscaled_x0 << high_bd);
298
0
        uint8_t* const       dst_ptr   = dst + (upscaled_x0 << high_bd);
299
0
        const int            dst_width = upscaled_x1 - upscaled_x0;
300
301
0
        const int pad_left  = (j == 0);
302
0
        const int pad_right = (j == cm->tiles_info.tile_cols - 1);
303
304
0
        if (high_bd) {
305
0
            highbd_upscale_normative_rect(src_ptr,
306
0
                                          rows,
307
0
                                          src_width,
308
0
                                          src_stride,
309
0
                                          dst_ptr,
310
0
                                          rows,
311
0
                                          dst_width,
312
0
                                          dst_stride,
313
0
                                          x_step_qn,
314
0
                                          x0_qn,
315
0
                                          pad_left,
316
0
                                          pad_right,
317
0
                                          bd);
318
0
        } else {
319
0
            upscale_normative_rect(src_ptr,
320
0
                                   rows,
321
0
                                   src_width,
322
0
                                   src_stride,
323
0
                                   dst_ptr,
324
0
                                   rows,
325
0
                                   dst_width,
326
0
                                   dst_stride,
327
0
                                   x_step_qn,
328
0
                                   x0_qn,
329
0
                                   pad_left,
330
0
                                   pad_right);
331
0
        }
332
333
        /*Update the fractional pixel offset to prepare for the next tile col*/
334
0
        x0_qn += (dst_width * x_step_qn) - (src_width << RS_SCALE_SUBPEL_BITS);
335
0
    }
336
0
#endif
337
0
}