1 /*
2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10 */
11
12 #ifndef AOM_AV1_COMMON_FILTER_H_
13 #define AOM_AV1_COMMON_FILTER_H_
14
15 #include <assert.h>
16
17 #include "config/aom_config.h"
18
19 #include "aom/aom_integer.h"
20 #include "aom_dsp/aom_filter.h"
21 #include "aom_ports/mem.h"
22 #include "av1/common/enums.h"
23
24 #ifdef __cplusplus
25 extern "C" {
26 #endif
27
28 #define MAX_FILTER_TAP 8
29
30 typedef enum ATTRIBUTE_PACKED {
31 EIGHTTAP_REGULAR,
32 EIGHTTAP_SMOOTH,
33 MULTITAP_SHARP,
34 BILINEAR,
35 INTERP_FILTERS_ALL,
36 SWITCHABLE_FILTERS = BILINEAR,
37 SWITCHABLE = SWITCHABLE_FILTERS + 1, /* the last switchable one */
38 EXTRA_FILTERS = INTERP_FILTERS_ALL - SWITCHABLE_FILTERS,
39 INTERP_INVALID = 0xff,
40 } InterpFilter;
41
42 enum {
43 USE_2_TAPS_ORIG = 0, // This is used in temporal filtering.
44 USE_2_TAPS,
45 USE_4_TAPS,
46 USE_8_TAPS,
47 } UENUM1BYTE(SUBPEL_SEARCH_TYPE);
48
49 enum {
50 INTERP_EVAL_LUMA_EVAL_CHROMA = 0,
51 INTERP_SKIP_LUMA_EVAL_CHROMA,
52 INTERP_EVAL_INVALID,
53 INTERP_SKIP_LUMA_SKIP_CHROMA,
54 } UENUM1BYTE(INTERP_EVAL_PLANE);
55
56 enum {
57 INTERP_HORZ_NEQ_VERT_NEQ = 0,
58 INTERP_HORZ_EQ_VERT_NEQ,
59 INTERP_HORZ_NEQ_VERT_EQ,
60 INTERP_HORZ_EQ_VERT_EQ,
61 INTERP_PRED_TYPE_ALL,
62 } UENUM1BYTE(INTERP_PRED_TYPE);
63 // Pack two InterpFilter's into a uint32_t: since there are at most 10 filters,
64 // we can use 16 bits for each and have more than enough space. This reduces
65 // argument passing and unifies the operation of setting a (pair of) filters.
66 typedef struct InterpFilters {
67 uint16_t y_filter;
68 uint16_t x_filter;
69 } InterpFilters;
70
71 typedef union int_interpfilters {
72 uint32_t as_int;
73 InterpFilters as_filters;
74 } int_interpfilters;
75
av1_extract_interp_filter(int_interpfilters filters,int dir)76 static INLINE InterpFilter av1_extract_interp_filter(int_interpfilters filters,
77 int dir) {
78 return (InterpFilter)((dir) ? filters.as_filters.x_filter
79 : filters.as_filters.y_filter);
80 }
81
82 static INLINE int_interpfilters
av1_broadcast_interp_filter(InterpFilter filter)83 av1_broadcast_interp_filter(InterpFilter filter) {
84 int_interpfilters filters;
85 filters.as_filters.x_filter = filter;
86 filters.as_filters.y_filter = filter;
87 return filters;
88 }
89
av1_unswitchable_filter(InterpFilter filter)90 static INLINE InterpFilter av1_unswitchable_filter(InterpFilter filter) {
91 return filter == SWITCHABLE ? EIGHTTAP_REGULAR : filter;
92 }
93
94 /* (1 << LOG_SWITCHABLE_FILTERS) > SWITCHABLE_FILTERS */
95 #define LOG_SWITCHABLE_FILTERS 2
96
97 #define SWITCHABLE_FILTER_CONTEXTS ((SWITCHABLE_FILTERS + 1) * 4)
98 #define INTER_FILTER_COMP_OFFSET (SWITCHABLE_FILTERS + 1)
99 #define INTER_FILTER_DIR_OFFSET ((SWITCHABLE_FILTERS + 1) * 2)
100 #define ALLOW_ALL_INTERP_FILT_MASK (0x01ff)
101
102 typedef struct InterpFilterParams {
103 const int16_t *filter_ptr;
104 uint16_t taps;
105 uint16_t subpel_shifts;
106 InterpFilter interp_filter;
107 } InterpFilterParams;
108
109 DECLARE_ALIGNED(256, static const InterpKernel,
110 av1_bilinear_filters[SUBPEL_SHIFTS]) = {
111 { 0, 0, 0, 128, 0, 0, 0, 0 }, { 0, 0, 0, 120, 8, 0, 0, 0 },
112 { 0, 0, 0, 112, 16, 0, 0, 0 }, { 0, 0, 0, 104, 24, 0, 0, 0 },
113 { 0, 0, 0, 96, 32, 0, 0, 0 }, { 0, 0, 0, 88, 40, 0, 0, 0 },
114 { 0, 0, 0, 80, 48, 0, 0, 0 }, { 0, 0, 0, 72, 56, 0, 0, 0 },
115 { 0, 0, 0, 64, 64, 0, 0, 0 }, { 0, 0, 0, 56, 72, 0, 0, 0 },
116 { 0, 0, 0, 48, 80, 0, 0, 0 }, { 0, 0, 0, 40, 88, 0, 0, 0 },
117 { 0, 0, 0, 32, 96, 0, 0, 0 }, { 0, 0, 0, 24, 104, 0, 0, 0 },
118 { 0, 0, 0, 16, 112, 0, 0, 0 }, { 0, 0, 0, 8, 120, 0, 0, 0 }
119 };
120
121 DECLARE_ALIGNED(256, static const InterpKernel,
122 av1_sub_pel_filters_8[SUBPEL_SHIFTS]) = {
123 { 0, 0, 0, 128, 0, 0, 0, 0 }, { 0, 2, -6, 126, 8, -2, 0, 0 },
124 { 0, 2, -10, 122, 18, -4, 0, 0 }, { 0, 2, -12, 116, 28, -8, 2, 0 },
125 { 0, 2, -14, 110, 38, -10, 2, 0 }, { 0, 2, -14, 102, 48, -12, 2, 0 },
126 { 0, 2, -16, 94, 58, -12, 2, 0 }, { 0, 2, -14, 84, 66, -12, 2, 0 },
127 { 0, 2, -14, 76, 76, -14, 2, 0 }, { 0, 2, -12, 66, 84, -14, 2, 0 },
128 { 0, 2, -12, 58, 94, -16, 2, 0 }, { 0, 2, -12, 48, 102, -14, 2, 0 },
129 { 0, 2, -10, 38, 110, -14, 2, 0 }, { 0, 2, -8, 28, 116, -12, 2, 0 },
130 { 0, 0, -4, 18, 122, -10, 2, 0 }, { 0, 0, -2, 8, 126, -6, 2, 0 }
131 };
132
133 DECLARE_ALIGNED(256, static const InterpKernel,
134 av1_sub_pel_filters_8sharp[SUBPEL_SHIFTS]) = {
135 { 0, 0, 0, 128, 0, 0, 0, 0 }, { -2, 2, -6, 126, 8, -2, 2, 0 },
136 { -2, 6, -12, 124, 16, -6, 4, -2 }, { -2, 8, -18, 120, 26, -10, 6, -2 },
137 { -4, 10, -22, 116, 38, -14, 6, -2 }, { -4, 10, -22, 108, 48, -18, 8, -2 },
138 { -4, 10, -24, 100, 60, -20, 8, -2 }, { -4, 10, -24, 90, 70, -22, 10, -2 },
139 { -4, 12, -24, 80, 80, -24, 12, -4 }, { -2, 10, -22, 70, 90, -24, 10, -4 },
140 { -2, 8, -20, 60, 100, -24, 10, -4 }, { -2, 8, -18, 48, 108, -22, 10, -4 },
141 { -2, 6, -14, 38, 116, -22, 10, -4 }, { -2, 6, -10, 26, 120, -18, 8, -2 },
142 { -2, 4, -6, 16, 124, -12, 6, -2 }, { 0, 2, -2, 8, 126, -6, 2, -2 }
143 };
144
145 DECLARE_ALIGNED(256, static const InterpKernel,
146 av1_sub_pel_filters_8smooth[SUBPEL_SHIFTS]) = {
147 { 0, 0, 0, 128, 0, 0, 0, 0 }, { 0, 2, 28, 62, 34, 2, 0, 0 },
148 { 0, 0, 26, 62, 36, 4, 0, 0 }, { 0, 0, 22, 62, 40, 4, 0, 0 },
149 { 0, 0, 20, 60, 42, 6, 0, 0 }, { 0, 0, 18, 58, 44, 8, 0, 0 },
150 { 0, 0, 16, 56, 46, 10, 0, 0 }, { 0, -2, 16, 54, 48, 12, 0, 0 },
151 { 0, -2, 14, 52, 52, 14, -2, 0 }, { 0, 0, 12, 48, 54, 16, -2, 0 },
152 { 0, 0, 10, 46, 56, 16, 0, 0 }, { 0, 0, 8, 44, 58, 18, 0, 0 },
153 { 0, 0, 6, 42, 60, 20, 0, 0 }, { 0, 0, 4, 40, 62, 22, 0, 0 },
154 { 0, 0, 4, 36, 62, 26, 0, 0 }, { 0, 0, 2, 34, 62, 28, 2, 0 }
155 };
156
157 static const InterpFilterParams
158 av1_interp_filter_params_list[SWITCHABLE_FILTERS + 1] = {
159 { (const int16_t *)av1_sub_pel_filters_8, SUBPEL_TAPS, SUBPEL_SHIFTS,
160 EIGHTTAP_REGULAR },
161 { (const int16_t *)av1_sub_pel_filters_8smooth, SUBPEL_TAPS,
162 SUBPEL_SHIFTS, EIGHTTAP_SMOOTH },
163 { (const int16_t *)av1_sub_pel_filters_8sharp, SUBPEL_TAPS, SUBPEL_SHIFTS,
164 MULTITAP_SHARP },
165 { (const int16_t *)av1_bilinear_filters, SUBPEL_TAPS, SUBPEL_SHIFTS,
166 BILINEAR }
167 };
168
169 // A special 2-tap bilinear filter for IntraBC chroma. IntraBC uses full pixel
170 // MV for luma. If sub-sampling exists, chroma may possibly use half-pel MV.
171 DECLARE_ALIGNED(256, static const int16_t,
172 av1_intrabc_bilinear_filter[2 * SUBPEL_SHIFTS]) = {
173 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
174 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
175 };
176
177 static const InterpFilterParams av1_intrabc_filter_params = {
178 av1_intrabc_bilinear_filter, 2, 0, BILINEAR
179 };
180
181 DECLARE_ALIGNED(256, static const InterpKernel,
182 av1_sub_pel_filters_4[SUBPEL_SHIFTS]) = {
183 { 0, 0, 0, 128, 0, 0, 0, 0 }, { 0, 0, -4, 126, 8, -2, 0, 0 },
184 { 0, 0, -8, 122, 18, -4, 0, 0 }, { 0, 0, -10, 116, 28, -6, 0, 0 },
185 { 0, 0, -12, 110, 38, -8, 0, 0 }, { 0, 0, -12, 102, 48, -10, 0, 0 },
186 { 0, 0, -14, 94, 58, -10, 0, 0 }, { 0, 0, -12, 84, 66, -10, 0, 0 },
187 { 0, 0, -12, 76, 76, -12, 0, 0 }, { 0, 0, -10, 66, 84, -12, 0, 0 },
188 { 0, 0, -10, 58, 94, -14, 0, 0 }, { 0, 0, -10, 48, 102, -12, 0, 0 },
189 { 0, 0, -8, 38, 110, -12, 0, 0 }, { 0, 0, -6, 28, 116, -10, 0, 0 },
190 { 0, 0, -4, 18, 122, -8, 0, 0 }, { 0, 0, -2, 8, 126, -4, 0, 0 }
191 };
192 DECLARE_ALIGNED(256, static const InterpKernel,
193 av1_sub_pel_filters_4smooth[SUBPEL_SHIFTS]) = {
194 { 0, 0, 0, 128, 0, 0, 0, 0 }, { 0, 0, 30, 62, 34, 2, 0, 0 },
195 { 0, 0, 26, 62, 36, 4, 0, 0 }, { 0, 0, 22, 62, 40, 4, 0, 0 },
196 { 0, 0, 20, 60, 42, 6, 0, 0 }, { 0, 0, 18, 58, 44, 8, 0, 0 },
197 { 0, 0, 16, 56, 46, 10, 0, 0 }, { 0, 0, 14, 54, 48, 12, 0, 0 },
198 { 0, 0, 12, 52, 52, 12, 0, 0 }, { 0, 0, 12, 48, 54, 14, 0, 0 },
199 { 0, 0, 10, 46, 56, 16, 0, 0 }, { 0, 0, 8, 44, 58, 18, 0, 0 },
200 { 0, 0, 6, 42, 60, 20, 0, 0 }, { 0, 0, 4, 40, 62, 22, 0, 0 },
201 { 0, 0, 4, 36, 62, 26, 0, 0 }, { 0, 0, 2, 34, 62, 30, 0, 0 }
202 };
203
204 static const uint16_t
205 av1_interp_dual_filt_mask[INTERP_PRED_TYPE_ALL - 2][SWITCHABLE_FILTERS] = {
206 { (1 << REG_REG) | (1 << SMOOTH_REG) | (1 << SHARP_REG),
207 (1 << REG_SMOOTH) | (1 << SMOOTH_SMOOTH) | (1 << SHARP_SMOOTH),
208 (1 << REG_SHARP) | (1 << SMOOTH_SHARP) | (1 << SHARP_SHARP) },
209 { (1 << REG_REG) | (1 << REG_SMOOTH) | (1 << REG_SHARP),
210 (1 << SMOOTH_REG) | (1 << SMOOTH_SMOOTH) | (1 << SMOOTH_SHARP),
211 (1 << SHARP_REG) | (1 << SHARP_SMOOTH) | (1 << SHARP_SHARP) }
212 };
213
214 // For w<=4, MULTITAP_SHARP is the same as EIGHTTAP_REGULAR
215 static const InterpFilterParams av1_interp_4tap[SWITCHABLE_FILTERS + 1] = {
216 { (const int16_t *)av1_sub_pel_filters_4, SUBPEL_TAPS, SUBPEL_SHIFTS,
217 EIGHTTAP_REGULAR },
218 { (const int16_t *)av1_sub_pel_filters_4smooth, SUBPEL_TAPS, SUBPEL_SHIFTS,
219 EIGHTTAP_SMOOTH },
220 { (const int16_t *)av1_sub_pel_filters_4, SUBPEL_TAPS, SUBPEL_SHIFTS,
221 EIGHTTAP_REGULAR },
222 { (const int16_t *)av1_bilinear_filters, SUBPEL_TAPS, SUBPEL_SHIFTS,
223 BILINEAR },
224 };
225
226 static INLINE const InterpFilterParams *
av1_get_interp_filter_params_with_block_size(const InterpFilter interp_filter,const int w)227 av1_get_interp_filter_params_with_block_size(const InterpFilter interp_filter,
228 const int w) {
229 if (w <= 4) return &av1_interp_4tap[interp_filter];
230 return &av1_interp_filter_params_list[interp_filter];
231 }
232
av1_get_interp_filter_kernel(const InterpFilter interp_filter,int subpel_search)233 static INLINE const int16_t *av1_get_interp_filter_kernel(
234 const InterpFilter interp_filter, int subpel_search) {
235 assert(subpel_search >= USE_2_TAPS);
236 return (subpel_search == USE_2_TAPS)
237 ? av1_interp_4tap[BILINEAR].filter_ptr
238 : ((subpel_search == USE_4_TAPS)
239 ? av1_interp_4tap[interp_filter].filter_ptr
240 : av1_interp_filter_params_list[interp_filter].filter_ptr);
241 }
242
av1_get_interp_filter_subpel_kernel(const InterpFilterParams * const filter_params,const int subpel)243 static INLINE const int16_t *av1_get_interp_filter_subpel_kernel(
244 const InterpFilterParams *const filter_params, const int subpel) {
245 return filter_params->filter_ptr + filter_params->taps * subpel;
246 }
247
av1_get_filter(int subpel_search)248 static INLINE const InterpFilterParams *av1_get_filter(int subpel_search) {
249 assert(subpel_search >= USE_2_TAPS);
250
251 switch (subpel_search) {
252 case USE_2_TAPS: return &av1_interp_4tap[BILINEAR];
253 case USE_4_TAPS: return &av1_interp_4tap[EIGHTTAP_REGULAR];
254 case USE_8_TAPS: return &av1_interp_filter_params_list[EIGHTTAP_REGULAR];
255 default: assert(0); return NULL;
256 }
257 }
258
reset_interp_filter_allowed_mask(uint16_t * allow_interp_mask,DUAL_FILTER_TYPE filt_type)259 static INLINE void reset_interp_filter_allowed_mask(
260 uint16_t *allow_interp_mask, DUAL_FILTER_TYPE filt_type) {
261 uint16_t tmp = (~(1 << filt_type)) & 0xffff;
262 *allow_interp_mask &= (tmp & ALLOW_ALL_INTERP_FILT_MASK);
263 }
264
set_interp_filter_allowed_mask(uint16_t * allow_interp_mask,DUAL_FILTER_TYPE filt_type)265 static INLINE void set_interp_filter_allowed_mask(uint16_t *allow_interp_mask,
266 DUAL_FILTER_TYPE filt_type) {
267 *allow_interp_mask |= (1 << filt_type);
268 }
269
get_interp_filter_allowed_mask(uint16_t allow_interp_mask,DUAL_FILTER_TYPE filt_type)270 static INLINE uint8_t get_interp_filter_allowed_mask(
271 uint16_t allow_interp_mask, DUAL_FILTER_TYPE filt_type) {
272 return (allow_interp_mask >> filt_type) & 1;
273 }
274
275 #ifdef __cplusplus
276 } // extern "C"
277 #endif
278
279 #endif // AOM_AV1_COMMON_FILTER_H_
280