git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / gfx / scaler / scaler.c
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
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
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                   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
269 void 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  **/
316 void 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 }