drc: handle upto 64k page size
[pcsx_rearmed.git] / deps / libretro-common / gfx / scaler / scaler_filter.c
CommitLineData
3719602c
PC
1/* Copyright (C) 2010-2020 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (scaler_filter.c).
5 * ---------------------------------------------------------------------------------------
6 *
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:
12 *
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14 *
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.
21 */
22
23#include <stdio.h>
24#include <string.h>
25
26#include <gfx/scaler/filter.h>
27#include <gfx/scaler/scaler_int.h>
28#include <retro_inline.h>
29#include <filters.h>
30#include <retro_math.h>
31
32#define FILTER_UNITY (1 << 14)
33
34static INLINE void gen_filter_point_sub(struct scaler_filter *filter,
35 int len, int pos, int step)
36{
37 int i;
38 for (i = 0; i < len; i++, pos += step)
39 {
40 filter->filter_pos[i] = pos >> 16;
41 filter->filter[i] = FILTER_UNITY;
42 }
43}
44
45static INLINE void gen_filter_bilinear_sub(struct scaler_filter *filter,
46 int len, int pos, int step)
47{
48 int i;
49 for (i = 0; i < len; i++, pos += step)
50 {
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];
54 }
55}
56
57static INLINE void gen_filter_sinc_sub(struct scaler_filter *filter,
58 int len, int pos, int step, double phase_mul)
59{
60 int i, j;
61 const int sinc_size = filter->filter_len;
62
63 for (i = 0; i < len; i++, pos += step)
64 {
65 filter->filter_pos[i] = pos >> 16;
66
67 for (j = 0; j < sinc_size; j++)
68 {
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;
72
73 filter->filter[i * sinc_size + j] = sinc_val;
74 }
75 }
76}
77
78static bool validate_filter(struct scaler_ctx *ctx)
79{
80 int i;
81 int max_h_pos;
82 int max_w_pos = ctx->in_width - ctx->horiz.filter_len;
83
84 for (i = 0; i < ctx->out_width; i++)
85 {
86 if (ctx->horiz.filter_pos[i] > max_w_pos || ctx->horiz.filter_pos[i] < 0)
87 {
88#ifndef NDEBUG
89 fprintf(stderr, "Out X = %d => In X = %d\n", i, ctx->horiz.filter_pos[i]);
90#endif
91 return false;
92 }
93 }
94
95 max_h_pos = ctx->in_height - ctx->vert.filter_len;
96
97 for (i = 0; i < ctx->out_height; i++)
98 {
99 if (ctx->vert.filter_pos[i] > max_h_pos || ctx->vert.filter_pos[i] < 0)
100 {
101#ifndef NDEBUG
102 fprintf(stderr, "Out Y = %d => In Y = %d\n", i, ctx->vert.filter_pos[i]);
103#endif
104 return false;
105 }
106 }
107
108 return true;
109}
110
111static void fixup_filter_sub(struct scaler_filter *filter,
112 int out_len, int in_len)
113{
114 int i;
115 int max_pos = in_len - filter->filter_len;
116
117 for (i = 0; i < out_len; i++)
118 {
119 int postsample = filter->filter_pos[i] - max_pos;
120 int presample = -filter->filter_pos[i];
121
122 if (postsample > 0)
123 {
124 int16_t *base_filter = NULL;
125
126 filter->filter_pos[i] -= postsample;
127
128 base_filter = filter->filter + i * filter->filter_stride;
129
130 if (postsample > (int)filter->filter_len)
131 memset(base_filter, 0, filter->filter_len * sizeof(int16_t));
132 else
133 {
134 memmove(base_filter + postsample, base_filter,
135 (filter->filter_len - postsample) * sizeof(int16_t));
136 memset(base_filter, 0, postsample * sizeof(int16_t));
137 }
138 }
139
140 if (presample > 0)
141 {
142 int16_t *base_filter = NULL;
143
144 filter->filter_pos[i] += presample;
145 base_filter = filter->filter + i * filter->filter_stride;
146
147 if (presample > (int)filter->filter_len)
148 memset(base_filter, 0, filter->filter_len * sizeof(int16_t));
149 else
150 {
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));
155 }
156 }
157 }
158}
159
160bool scaler_gen_filter(struct scaler_ctx *ctx)
161{
162 int x_pos, x_step, y_pos, y_step;
163 int sinc_size = 0;
164
165 switch (ctx->scaler_type)
166 {
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;
172 break;
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;
178 break;
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;
186 break;
187 case SCALER_TYPE_UNKNOWN:
188 default:
189 return false;
190 }
191
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);
194
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);
197
198 if (!ctx->horiz.filter || !ctx->vert.filter)
199 return false;
200
201 x_step = (1 << 16) * ctx->in_width / ctx->out_width;
202 y_step = (1 << 16) * ctx->in_height / ctx->out_height;
203
204 switch (ctx->scaler_type)
205 {
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);
209
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);
212
213 ctx->scaler_special = scaler_argb8888_point_special;
214 break;
215
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);
219
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);
222 break;
223
224 case SCALER_TYPE_SINC:
225 /* Need to expand the filter when downsampling
226 * to get a proper low-pass effect. */
227
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);
230
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
235 );
236 break;
237 case SCALER_TYPE_UNKNOWN:
238 break;
239 }
240
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);
244
245 return validate_filter(ctx);
246}