1 /* Copyright (C) 2010-2020 The RetroArch team
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (scaler_filter.c).
5 * ---------------------------------------------------------------------------------------
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 #include <gfx/scaler/filter.h>
27 #include <gfx/scaler/scaler_int.h>
28 #include <retro_inline.h>
30 #include <retro_math.h>
32 #define FILTER_UNITY (1 << 14)
34 static INLINE void gen_filter_point_sub(struct scaler_filter *filter,
35 int len, int pos, int step)
38 for (i = 0; i < len; i++, pos += step)
40 filter->filter_pos[i] = pos >> 16;
41 filter->filter[i] = FILTER_UNITY;
45 static INLINE void gen_filter_bilinear_sub(struct scaler_filter *filter,
46 int len, int pos, int step)
49 for (i = 0; i < len; i++, pos += step)
51 filter->filter_pos[i] = pos >> 16;
52 filter->filter[i * 2 + 1] = (pos & 0xffff) >> 2;
53 filter->filter[i * 2 + 0] = FILTER_UNITY - filter->filter[i * 2 + 1];
57 static INLINE void gen_filter_sinc_sub(struct scaler_filter *filter,
58 int len, int pos, int step, double phase_mul)
61 const int sinc_size = filter->filter_len;
63 for (i = 0; i < len; i++, pos += step)
65 filter->filter_pos[i] = pos >> 16;
67 for (j = 0; j < sinc_size; j++)
69 double sinc_phase = M_PI * ((double)((sinc_size << 15) + (pos & 0xffff)) / 0x10000 - j);
70 double lanczos_phase = sinc_phase / ((sinc_size >> 1));
71 int16_t sinc_val = FILTER_UNITY * sinc(sinc_phase * phase_mul) * sinc(lanczos_phase) * phase_mul;
73 filter->filter[i * sinc_size + j] = sinc_val;
78 static bool validate_filter(struct scaler_ctx *ctx)
82 int max_w_pos = ctx->in_width - ctx->horiz.filter_len;
84 for (i = 0; i < ctx->out_width; i++)
86 if (ctx->horiz.filter_pos[i] > max_w_pos || ctx->horiz.filter_pos[i] < 0)
89 fprintf(stderr, "Out X = %d => In X = %d\n", i, ctx->horiz.filter_pos[i]);
95 max_h_pos = ctx->in_height - ctx->vert.filter_len;
97 for (i = 0; i < ctx->out_height; i++)
99 if (ctx->vert.filter_pos[i] > max_h_pos || ctx->vert.filter_pos[i] < 0)
102 fprintf(stderr, "Out Y = %d => In Y = %d\n", i, ctx->vert.filter_pos[i]);
111 static void fixup_filter_sub(struct scaler_filter *filter,
112 int out_len, int in_len)
115 int max_pos = in_len - filter->filter_len;
117 for (i = 0; i < out_len; i++)
119 int postsample = filter->filter_pos[i] - max_pos;
120 int presample = -filter->filter_pos[i];
124 int16_t *base_filter = NULL;
126 filter->filter_pos[i] -= postsample;
128 base_filter = filter->filter + i * filter->filter_stride;
130 if (postsample > (int)filter->filter_len)
131 memset(base_filter, 0, filter->filter_len * sizeof(int16_t));
134 memmove(base_filter + postsample, base_filter,
135 (filter->filter_len - postsample) * sizeof(int16_t));
136 memset(base_filter, 0, postsample * sizeof(int16_t));
142 int16_t *base_filter = NULL;
144 filter->filter_pos[i] += presample;
145 base_filter = filter->filter + i * filter->filter_stride;
147 if (presample > (int)filter->filter_len)
148 memset(base_filter, 0, filter->filter_len * sizeof(int16_t));
151 memmove(base_filter, base_filter + presample,
152 (filter->filter_len - presample) * sizeof(int16_t));
153 memset(base_filter + (filter->filter_len - presample),
154 0, presample * sizeof(int16_t));
160 bool scaler_gen_filter(struct scaler_ctx *ctx)
162 int x_pos, x_step, y_pos, y_step;
165 switch (ctx->scaler_type)
167 case SCALER_TYPE_POINT:
168 ctx->horiz.filter_len = 1;
169 ctx->horiz.filter_stride = 1;
170 ctx->vert.filter_len = 1;
171 ctx->vert.filter_stride = 1;
173 case SCALER_TYPE_BILINEAR:
174 ctx->horiz.filter_len = 2;
175 ctx->horiz.filter_stride = 2;
176 ctx->vert.filter_len = 2;
177 ctx->vert.filter_stride = 2;
179 case SCALER_TYPE_SINC:
180 sinc_size = 8 * ((ctx->in_width > ctx->out_width)
181 ? next_pow2(ctx->in_width / ctx->out_width) : 1);
182 ctx->horiz.filter_len = sinc_size;
183 ctx->horiz.filter_stride = sinc_size;
184 ctx->vert.filter_len = sinc_size;
185 ctx->vert.filter_stride = sinc_size;
187 case SCALER_TYPE_UNKNOWN:
192 ctx->horiz.filter = (int16_t*)calloc(sizeof(int16_t), ctx->horiz.filter_stride * ctx->out_width);
193 ctx->horiz.filter_pos = (int*)calloc(sizeof(int), ctx->out_width);
195 ctx->vert.filter = (int16_t*)calloc(sizeof(int16_t), ctx->vert.filter_stride * ctx->out_height);
196 ctx->vert.filter_pos = (int*)calloc(sizeof(int), ctx->out_height);
198 if (!ctx->horiz.filter || !ctx->vert.filter)
201 x_step = (1 << 16) * ctx->in_width / ctx->out_width;
202 y_step = (1 << 16) * ctx->in_height / ctx->out_height;
204 switch (ctx->scaler_type)
206 case SCALER_TYPE_POINT:
207 x_pos = (1 << 15) * ctx->in_width / ctx->out_width - (1 << 15);
208 y_pos = (1 << 15) * ctx->in_height / ctx->out_height - (1 << 15);
210 gen_filter_point_sub(&ctx->horiz, ctx->out_width, x_pos, x_step);
211 gen_filter_point_sub(&ctx->vert, ctx->out_height, y_pos, y_step);
213 ctx->scaler_special = scaler_argb8888_point_special;
216 case SCALER_TYPE_BILINEAR:
217 x_pos = (1 << 15) * ctx->in_width / ctx->out_width - (1 << 15);
218 y_pos = (1 << 15) * ctx->in_height / ctx->out_height - (1 << 15);
220 gen_filter_bilinear_sub(&ctx->horiz, ctx->out_width, x_pos, x_step);
221 gen_filter_bilinear_sub(&ctx->vert, ctx->out_height, y_pos, y_step);
224 case SCALER_TYPE_SINC:
225 /* Need to expand the filter when downsampling
226 * to get a proper low-pass effect. */
228 x_pos = (1 << 15) * ctx->in_width / ctx->out_width - (1 << 15) - (sinc_size << 15);
229 y_pos = (1 << 15) * ctx->in_height / ctx->out_height - (1 << 15) - (sinc_size << 15);
231 gen_filter_sinc_sub(&ctx->horiz, ctx->out_width, x_pos, x_step,
232 ctx->in_width > ctx->out_width ? (double)ctx->out_width / ctx->in_width : 1.0);
233 gen_filter_sinc_sub(&ctx->vert, ctx->out_height, y_pos, y_step,
234 ctx->in_height > ctx->out_height ? (double)ctx->out_height / ctx->in_height : 1.0
237 case SCALER_TYPE_UNKNOWN:
241 /* Makes sure that we never sample outside our rectangle. */
242 fixup_filter_sub(&ctx->horiz, ctx->out_width, ctx->in_width);
243 fixup_filter_sub(&ctx->vert, ctx->out_height, ctx->in_height);
245 return validate_filter(ctx);