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