1 /* Copyright (C) 2010-2018 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
allocate_frames(struct scaler_ctx * ctx)33 static 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
scaler_ctx_gen_filter(struct scaler_ctx * ctx)77 bool 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 default:
142 break;
143 }
144 break;
145 case SCALER_FMT_ARGB8888:
146 switch (ctx->out_fmt)
147 {
148 case SCALER_FMT_0RGB1555:
149 ctx->direct_pixconv = conv_argb8888_0rgb1555;
150 break;
151 case SCALER_FMT_BGR24:
152 ctx->direct_pixconv = conv_argb8888_bgr24;
153 break;
154 case SCALER_FMT_ABGR8888:
155 ctx->direct_pixconv = conv_argb8888_abgr8888;
156 break;
157 case SCALER_FMT_RGBA4444:
158 ctx->direct_pixconv = conv_argb8888_rgba4444;
159 break;
160 default:
161 break;
162 }
163 break;
164 case SCALER_FMT_YUYV:
165 switch (ctx->out_fmt)
166 {
167 case SCALER_FMT_ARGB8888:
168 ctx->direct_pixconv = conv_yuyv_argb8888;
169 break;
170 default:
171 break;
172 }
173 break;
174 case SCALER_FMT_RGBA4444:
175 switch (ctx->out_fmt)
176 {
177 case SCALER_FMT_ARGB8888:
178 ctx->direct_pixconv = conv_rgba4444_argb8888;
179 break;
180 case SCALER_FMT_RGB565:
181 ctx->direct_pixconv = conv_rgba4444_rgb565;
182 break;
183 default:
184 break;
185 }
186 break;
187 case SCALER_FMT_ABGR8888:
188 switch (ctx->out_fmt)
189 {
190 case SCALER_FMT_BGR24:
191 ctx->direct_pixconv = conv_abgr8888_bgr24;
192 break;
193 default:
194 break;
195 }
196 break;
197 }
198
199 if (!ctx->direct_pixconv)
200 return false;
201 }
202 }
203 else
204 {
205 ctx->scaler_horiz = scaler_argb8888_horiz;
206 ctx->scaler_vert = scaler_argb8888_vert;
207
208 switch (ctx->in_fmt)
209 {
210 case SCALER_FMT_ARGB8888:
211 /* No need to convert :D */
212 break;
213
214 case SCALER_FMT_0RGB1555:
215 ctx->in_pixconv = conv_0rgb1555_argb8888;
216 break;
217
218 case SCALER_FMT_RGB565:
219 ctx->in_pixconv = conv_rgb565_argb8888;
220 break;
221
222 case SCALER_FMT_BGR24:
223 ctx->in_pixconv = conv_bgr24_argb8888;
224 break;
225
226 case SCALER_FMT_RGBA4444:
227 ctx->in_pixconv = conv_rgba4444_argb8888;
228 break;
229
230 default:
231 return false;
232 }
233
234 switch (ctx->out_fmt)
235 {
236 case SCALER_FMT_ARGB8888:
237 /* No need to convert :D */
238 break;
239
240 case SCALER_FMT_RGBA4444:
241 ctx->out_pixconv = conv_argb8888_rgba4444;
242 break;
243
244 case SCALER_FMT_0RGB1555:
245 ctx->out_pixconv = conv_argb8888_0rgb1555;
246 break;
247
248 case SCALER_FMT_BGR24:
249 ctx->out_pixconv = conv_argb8888_bgr24;
250 break;
251
252 case SCALER_FMT_ABGR8888:
253 ctx->out_pixconv = conv_argb8888_abgr8888;
254 break;
255
256 default:
257 return false;
258 }
259
260 if (!scaler_gen_filter(ctx))
261 return false;
262 }
263
264 return true;
265 }
266
scaler_ctx_gen_reset(struct scaler_ctx * ctx)267 void scaler_ctx_gen_reset(struct scaler_ctx *ctx)
268 {
269 if (ctx->horiz.filter)
270 free(ctx->horiz.filter);
271 if (ctx->horiz.filter_pos)
272 free(ctx->horiz.filter_pos);
273 if (ctx->vert.filter)
274 free(ctx->vert.filter);
275 if (ctx->vert.filter_pos)
276 free(ctx->vert.filter_pos);
277 if (ctx->scaled.frame)
278 free(ctx->scaled.frame);
279 if (ctx->input.frame)
280 free(ctx->input.frame);
281 if (ctx->output.frame)
282 free(ctx->output.frame);
283
284 ctx->horiz.filter = NULL;
285 ctx->horiz.filter_len = 0;
286 ctx->horiz.filter_stride = 0;
287 ctx->horiz.filter_pos = NULL;
288
289 ctx->vert.filter = NULL;
290 ctx->vert.filter_len = 0;
291 ctx->vert.filter_stride = 0;
292 ctx->vert.filter_pos = NULL;
293
294 ctx->scaled.frame = NULL;
295 ctx->scaled.width = 0;
296 ctx->scaled.height = 0;
297 ctx->scaled.stride = 0;
298
299 ctx->input.frame = NULL;
300 ctx->input.stride = 0;
301
302 ctx->output.frame = NULL;
303 ctx->output.stride = 0;
304 }
305
306 /**
307 * scaler_ctx_scale:
308 * @ctx : pointer to scaler context object.
309 * @output : pointer to output image.
310 * @input : pointer to input image.
311 *
312 * Scales an input image to an output image.
313 **/
scaler_ctx_scale(struct scaler_ctx * ctx,void * output,const void * input)314 void scaler_ctx_scale(struct scaler_ctx *ctx,
315 void *output, const void *input)
316 {
317 const void *input_frame = input;
318 void *output_frame = output;
319 int input_stride = ctx->in_stride;
320 int output_stride = ctx->out_stride;
321
322 if (ctx->in_fmt != SCALER_FMT_ARGB8888)
323 {
324 ctx->in_pixconv(ctx->input.frame, input,
325 ctx->in_width, ctx->in_height,
326 ctx->input.stride, ctx->in_stride);
327
328 input_frame = ctx->input.frame;
329 input_stride = ctx->input.stride;
330 }
331
332 if (ctx->out_fmt != SCALER_FMT_ARGB8888)
333 {
334 output_frame = ctx->output.frame;
335 output_stride = ctx->output.stride;
336 }
337
338 /* Take some special, and (hopefully) more optimized path. */
339 if (ctx->scaler_special)
340 ctx->scaler_special(ctx, output_frame, input_frame,
341 ctx->out_width, ctx->out_height,
342 ctx->in_width, ctx->in_height,
343 output_stride, input_stride);
344 else
345 {
346 /* Take generic filter path. */
347 if (ctx->scaler_horiz)
348 ctx->scaler_horiz(ctx, input_frame, input_stride);
349 if (ctx->scaler_vert)
350 ctx->scaler_vert (ctx, output, output_stride);
351 }
352
353 if (ctx->out_fmt != SCALER_FMT_ARGB8888)
354 ctx->out_pixconv(output, ctx->output.frame,
355 ctx->out_width, ctx->out_height,
356 ctx->out_stride, ctx->output.stride);
357 }
358