git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / gfx / scaler / scaler.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.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 <stdlib.h>
25#include <string.h>
26#include <math.h>
27
28#include <gfx/scaler/scaler.h>
29#include <gfx/scaler/scaler_int.h>
30#include <gfx/scaler/filter.h>
31#include <gfx/scaler/pixconv.h>
32
33static bool allocate_frames(struct scaler_ctx *ctx)
34{
35 uint64_t *scaled_frame = NULL;
36 ctx->scaled.stride = ((ctx->out_width + 7) & ~7) * sizeof(uint64_t);
37 ctx->scaled.width = ctx->out_width;
38 ctx->scaled.height = ctx->in_height;
39 scaled_frame = (uint64_t*)calloc(sizeof(uint64_t),
40 (ctx->scaled.stride * ctx->scaled.height) >> 3);
41
42 if (!scaled_frame)
43 return false;
44
45 ctx->scaled.frame = scaled_frame;
46
47 if (ctx->in_fmt != SCALER_FMT_ARGB8888)
48 {
49 uint32_t *input_frame = NULL;
50 ctx->input.stride = ((ctx->in_width + 7) & ~7) * sizeof(uint32_t);
51 input_frame = (uint32_t*)calloc(sizeof(uint32_t),
52 (ctx->input.stride * ctx->in_height) >> 2);
53
54 if (!input_frame)
55 return false;
56
57 ctx->input.frame = input_frame;
58 }
59
60 if (ctx->out_fmt != SCALER_FMT_ARGB8888)
61 {
62 uint32_t *output_frame = NULL;
63 ctx->output.stride = ((ctx->out_width + 7) & ~7) * sizeof(uint32_t);
64
65 output_frame = (uint32_t*)calloc(sizeof(uint32_t),
66 (ctx->output.stride * ctx->out_height) >> 2);
67
68 if (!output_frame)
69 return false;
70
71 ctx->output.frame = output_frame;
72 }
73
74 return true;
75}
76
77bool scaler_ctx_gen_filter(struct scaler_ctx *ctx)
78{
79 scaler_ctx_gen_reset(ctx);
80
81 ctx->scaler_special = NULL;
82 ctx->unscaled = false;
83
84 if (!allocate_frames(ctx))
85 return false;
86
87 if ( ctx->in_width == ctx->out_width
88 && ctx->in_height == ctx->out_height)
89 {
90 ctx->unscaled = true; /* Only pixel format conversion ... */
91
92 if (ctx->in_fmt == ctx->out_fmt)
93 ctx->direct_pixconv = conv_copy;
94 else
95 {
96 /* Bind a pixel converter callback function to the
97 * 'direct_pixconv' function pointer of the scaler context object. */
98 switch (ctx->in_fmt)
99 {
100 case SCALER_FMT_0RGB1555:
101 switch (ctx->out_fmt)
102 {
103 case SCALER_FMT_ARGB8888:
104 ctx->direct_pixconv = conv_0rgb1555_argb8888;
105 break;
106 case SCALER_FMT_RGB565:
107 ctx->direct_pixconv = conv_0rgb1555_rgb565;
108 break;
109 case SCALER_FMT_BGR24:
110 ctx->direct_pixconv = conv_0rgb1555_bgr24;
111 break;
112 default:
113 break;
114 }
115 break;
116 case SCALER_FMT_RGB565:
117 switch (ctx->out_fmt)
118 {
119 case SCALER_FMT_ARGB8888:
120 ctx->direct_pixconv = conv_rgb565_argb8888;
121 break;
122 case SCALER_FMT_ABGR8888:
123 ctx->direct_pixconv = conv_rgb565_abgr8888;
124 break;
125 case SCALER_FMT_BGR24:
126 ctx->direct_pixconv = conv_rgb565_bgr24;
127 break;
128 case SCALER_FMT_0RGB1555:
129 ctx->direct_pixconv = conv_rgb565_0rgb1555;
130 break;
131 default:
132 break;
133 }
134 break;
135 case SCALER_FMT_BGR24:
136 switch (ctx->out_fmt)
137 {
138 case SCALER_FMT_ARGB8888:
139 ctx->direct_pixconv = conv_bgr24_argb8888;
140 break;
141 case SCALER_FMT_RGB565:
142 ctx->direct_pixconv = conv_bgr24_rgb565;
143 default:
144 break;
145 }
146 break;
147 case SCALER_FMT_ARGB8888:
148 switch (ctx->out_fmt)
149 {
150 case SCALER_FMT_0RGB1555:
151 ctx->direct_pixconv = conv_argb8888_0rgb1555;
152 break;
153 case SCALER_FMT_BGR24:
154 ctx->direct_pixconv = conv_argb8888_bgr24;
155 break;
156 case SCALER_FMT_ABGR8888:
157 ctx->direct_pixconv = conv_argb8888_abgr8888;
158 break;
159 case SCALER_FMT_RGBA4444:
160 ctx->direct_pixconv = conv_argb8888_rgba4444;
161 break;
162 default:
163 break;
164 }
165 break;
166 case SCALER_FMT_YUYV:
167 switch (ctx->out_fmt)
168 {
169 case SCALER_FMT_ARGB8888:
170 ctx->direct_pixconv = conv_yuyv_argb8888;
171 break;
172 default:
173 break;
174 }
175 break;
176 case SCALER_FMT_RGBA4444:
177 switch (ctx->out_fmt)
178 {
179 case SCALER_FMT_ARGB8888:
180 ctx->direct_pixconv = conv_rgba4444_argb8888;
181 break;
182 case SCALER_FMT_RGB565:
183 ctx->direct_pixconv = conv_rgba4444_rgb565;
184 break;
185 default:
186 break;
187 }
188 break;
189 case SCALER_FMT_ABGR8888:
190 switch (ctx->out_fmt)
191 {
192 case SCALER_FMT_BGR24:
193 ctx->direct_pixconv = conv_abgr8888_bgr24;
194 break;
195 default:
196 break;
197 }
198 break;
199 }
200
201 if (!ctx->direct_pixconv)
202 return false;
203 }
204 }
205 else
206 {
207 ctx->scaler_horiz = scaler_argb8888_horiz;
208 ctx->scaler_vert = scaler_argb8888_vert;
209
210 switch (ctx->in_fmt)
211 {
212 case SCALER_FMT_ARGB8888:
213 /* No need to convert :D */
214 break;
215
216 case SCALER_FMT_0RGB1555:
217 ctx->in_pixconv = conv_0rgb1555_argb8888;
218 break;
219
220 case SCALER_FMT_RGB565:
221 ctx->in_pixconv = conv_rgb565_argb8888;
222 break;
223
224 case SCALER_FMT_BGR24:
225 ctx->in_pixconv = conv_bgr24_argb8888;
226 break;
227
228 case SCALER_FMT_RGBA4444:
229 ctx->in_pixconv = conv_rgba4444_argb8888;
230 break;
231
232 default:
233 return false;
234 }
235
236 switch (ctx->out_fmt)
237 {
238 case SCALER_FMT_ARGB8888:
239 /* No need to convert :D */
240 break;
241
242 case SCALER_FMT_RGBA4444:
243 ctx->out_pixconv = conv_argb8888_rgba4444;
244 break;
245
246 case SCALER_FMT_0RGB1555:
247 ctx->out_pixconv = conv_argb8888_0rgb1555;
248 break;
249
250 case SCALER_FMT_BGR24:
251 ctx->out_pixconv = conv_argb8888_bgr24;
252 break;
253
254 case SCALER_FMT_ABGR8888:
255 ctx->out_pixconv = conv_argb8888_abgr8888;
256 break;
257
258 default:
259 return false;
260 }
261
262 if (!scaler_gen_filter(ctx))
263 return false;
264 }
265
266 return true;
267}
268
269void scaler_ctx_gen_reset(struct scaler_ctx *ctx)
270{
271 if (ctx->horiz.filter)
272 free(ctx->horiz.filter);
273 if (ctx->horiz.filter_pos)
274 free(ctx->horiz.filter_pos);
275 if (ctx->vert.filter)
276 free(ctx->vert.filter);
277 if (ctx->vert.filter_pos)
278 free(ctx->vert.filter_pos);
279 if (ctx->scaled.frame)
280 free(ctx->scaled.frame);
281 if (ctx->input.frame)
282 free(ctx->input.frame);
283 if (ctx->output.frame)
284 free(ctx->output.frame);
285
286 ctx->horiz.filter = NULL;
287 ctx->horiz.filter_len = 0;
288 ctx->horiz.filter_stride = 0;
289 ctx->horiz.filter_pos = NULL;
290
291 ctx->vert.filter = NULL;
292 ctx->vert.filter_len = 0;
293 ctx->vert.filter_stride = 0;
294 ctx->vert.filter_pos = NULL;
295
296 ctx->scaled.frame = NULL;
297 ctx->scaled.width = 0;
298 ctx->scaled.height = 0;
299 ctx->scaled.stride = 0;
300
301 ctx->input.frame = NULL;
302 ctx->input.stride = 0;
303
304 ctx->output.frame = NULL;
305 ctx->output.stride = 0;
306}
307
308/**
309 * scaler_ctx_scale:
310 * @ctx : pointer to scaler context object.
311 * @output : pointer to output image.
312 * @input : pointer to input image.
313 *
314 * Scales an input image to an output image.
315 **/
316void scaler_ctx_scale(struct scaler_ctx *ctx,
317 void *output, const void *input)
318{
319 const void *input_frame = input;
320 void *output_frame = output;
321 int input_stride = ctx->in_stride;
322 int output_stride = ctx->out_stride;
323
324 if (ctx->in_fmt != SCALER_FMT_ARGB8888)
325 {
326 ctx->in_pixconv(ctx->input.frame, input,
327 ctx->in_width, ctx->in_height,
328 ctx->input.stride, ctx->in_stride);
329
330 input_frame = ctx->input.frame;
331 input_stride = ctx->input.stride;
332 }
333
334 if (ctx->out_fmt != SCALER_FMT_ARGB8888)
335 {
336 output_frame = ctx->output.frame;
337 output_stride = ctx->output.stride;
338 }
339
340 /* Take some special, and (hopefully) more optimized path. */
341 if (ctx->scaler_special)
342 ctx->scaler_special(ctx, output_frame, input_frame,
343 ctx->out_width, ctx->out_height,
344 ctx->in_width, ctx->in_height,
345 output_stride, input_stride);
346 else
347 {
348 /* Take generic filter path. */
349 if (ctx->scaler_horiz)
350 ctx->scaler_horiz(ctx, input_frame, input_stride);
351 if (ctx->scaler_vert)
352 ctx->scaler_vert (ctx, output, output_stride);
353 }
354
355 if (ctx->out_fmt != SCALER_FMT_ARGB8888)
356 ctx->out_pixconv(output, ctx->output.frame,
357 ctx->out_width, ctx->out_height,
358 ctx->out_stride, ctx->output.stride);
359}