update libchdr
[pcsx_rearmed.git] / deps / libretro-common / formats / image_texture.c
1 /* Copyright  (C) 2010-2020 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (image_texture.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 <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stddef.h>
27
28 #include <boolean.h>
29 #include <formats/image.h>
30 #include <file/nbio.h>
31 #include <string/stdstring.h>
32
33 enum image_type_enum image_texture_get_type(const char *path)
34 {
35    /* We are comparing against a fixed list of file
36     * extensions, the longest (jpeg) being 4 characters
37     * in length. We therefore only need to extract the first
38     * 5 characters from the extension of the input path
39     * to correctly validate a match */
40    const char *ext = NULL;
41    char ext_lower[6];
42
43    ext_lower[0] = '\0';
44
45    if (string_is_empty(path))
46       return IMAGE_TYPE_NONE;
47
48    /* Get file extension */
49    ext = strrchr(path, '.');
50
51    if (!ext || (*(++ext) == '\0'))
52       return IMAGE_TYPE_NONE;
53
54    /* Copy and convert to lower case */
55    strlcpy(ext_lower, ext, sizeof(ext_lower));
56    string_to_lower(ext_lower);
57
58 #ifdef HAVE_RPNG
59    if (string_is_equal(ext_lower, "png"))
60       return IMAGE_TYPE_PNG;
61 #endif
62 #ifdef HAVE_RJPEG
63    if (string_is_equal(ext_lower, "jpg") ||
64        string_is_equal(ext_lower, "jpeg"))
65       return IMAGE_TYPE_JPEG;
66 #endif
67 #ifdef HAVE_RBMP
68    if (string_is_equal(ext_lower, "bmp"))
69       return IMAGE_TYPE_BMP;
70 #endif
71 #ifdef HAVE_RTGA
72    if (string_is_equal(ext_lower, "tga"))
73       return IMAGE_TYPE_TGA;
74 #endif
75
76    return IMAGE_TYPE_NONE;
77 }
78
79 bool image_texture_set_color_shifts(
80       unsigned *r_shift, unsigned *g_shift, unsigned *b_shift,
81       unsigned *a_shift,
82       struct texture_image *out_img
83       )
84 {
85    *a_shift             = 24;
86    *r_shift             = 16;
87    *g_shift             = 8;
88    *b_shift             = 0;
89
90    if (out_img->supports_rgba)
91    {
92       *r_shift = 0;
93       *b_shift = 16;
94       return true;
95    }
96
97    return false;
98 }
99
100 bool image_texture_color_convert(unsigned r_shift,
101       unsigned g_shift, unsigned b_shift, unsigned a_shift,
102       struct texture_image *out_img)
103 {
104    /* This is quite uncommon. */
105    if (a_shift != 24 || r_shift != 16 || g_shift != 8 || b_shift != 0)
106    {
107       uint32_t i;
108       uint32_t num_pixels = out_img->width * out_img->height;
109       uint32_t *pixels    = (uint32_t*)out_img->pixels;
110
111       for (i = 0; i < num_pixels; i++)
112       {
113          uint32_t col = pixels[i];
114          uint8_t a    = (uint8_t)(col >> 24);
115          uint8_t r    = (uint8_t)(col >> 16);
116          uint8_t g    = (uint8_t)(col >>  8);
117          uint8_t b    = (uint8_t)(col >>  0);
118          /* Explicitly cast these to uint32_t to prevent
119           * ASAN runtime error: left shift of 255 by 24 places
120           * cannot be represented in type 'int' */
121          pixels[i]    = ((uint32_t)a << a_shift) |
122                         ((uint32_t)r << r_shift) |
123                         ((uint32_t)g << g_shift) |
124                         ((uint32_t)b << b_shift);
125       }
126
127       return true;
128    }
129
130    return false;
131 }
132
133 #ifdef GEKKO
134
135 #define GX_BLIT_LINE_32(off) \
136 { \
137    unsigned x; \
138    const uint16_t *tmp_src = src; \
139    uint16_t       *tmp_dst = dst; \
140    for (x = 0; x < width2 >> 3; x++, tmp_src += 8, tmp_dst += 32) \
141    { \
142       tmp_dst[  0 + off] = tmp_src[0]; \
143       tmp_dst[ 16 + off] = tmp_src[1]; \
144       tmp_dst[  1 + off] = tmp_src[2]; \
145       tmp_dst[ 17 + off] = tmp_src[3]; \
146       tmp_dst[  2 + off] = tmp_src[4]; \
147       tmp_dst[ 18 + off] = tmp_src[5]; \
148       tmp_dst[  3 + off] = tmp_src[6]; \
149       tmp_dst[ 19 + off] = tmp_src[7]; \
150    } \
151    src += tmp_pitch; \
152 }
153
154 static bool image_texture_internal_gx_convert_texture32(
155       struct texture_image *image)
156 {
157    unsigned tmp_pitch, width2, i;
158    const uint16_t *src = NULL;
159    uint16_t *dst       = NULL;
160    /* Memory allocation in libogc is extremely primitive so try
161     * to avoid gaps in memory when converting by copying over to
162     * a temporary buffer first, then converting over into
163     * main buffer again. */
164    void *tmp           = malloc(image->width
165          * image->height * sizeof(uint32_t));
166
167    if (!tmp)
168       return false;
169
170    memcpy(tmp, image->pixels, image->width
171          * image->height * sizeof(uint32_t));
172    tmp_pitch = (image->width * sizeof(uint32_t)) >> 1;
173
174    image->width       &= ~3;
175    image->height      &= ~3;
176    width2              = image->width << 1;
177    src                 = (uint16_t*)tmp;
178    dst                 = (uint16_t*)image->pixels;
179
180    for (i = 0; i < image->height; i += 4, dst += 4 * width2)
181    {
182       GX_BLIT_LINE_32(0)
183       GX_BLIT_LINE_32(4)
184       GX_BLIT_LINE_32(8)
185       GX_BLIT_LINE_32(12)
186    }
187
188    free(tmp);
189    return true;
190 }
191 #endif
192
193 static bool image_texture_load_internal(
194       enum image_type_enum type,
195       void *ptr,
196       size_t len,
197       struct texture_image *out_img,
198       unsigned a_shift, unsigned r_shift,
199       unsigned g_shift, unsigned b_shift)
200 {
201    int ret;
202    bool success = false;
203    void *img    = image_transfer_new(type);
204
205    if (!img)
206       goto end;
207
208    image_transfer_set_buffer_ptr(img, type, (uint8_t*)ptr, len);
209
210    if (!image_transfer_start(img, type))
211       goto end;
212
213    while (image_transfer_iterate(img, type));
214
215    if (!image_transfer_is_valid(img, type))
216       goto end;
217
218    do
219    {
220       ret = image_transfer_process(img, type,
221             (uint32_t**)&out_img->pixels, len, &out_img->width,
222             &out_img->height);
223    } while (ret == IMAGE_PROCESS_NEXT);
224
225    if (ret == IMAGE_PROCESS_ERROR || ret == IMAGE_PROCESS_ERROR_END)
226       goto end;
227
228    image_texture_color_convert(r_shift, g_shift, b_shift,
229          a_shift, out_img);
230
231 #ifdef GEKKO
232    if (!image_texture_internal_gx_convert_texture32(out_img))
233    {
234       image_texture_free(out_img);
235       goto end;
236    }
237 #endif
238
239    success = true;
240
241 end:
242    if (img)
243       image_transfer_free(img, type);
244
245    return success;
246 }
247
248 void image_texture_free(struct texture_image *img)
249 {
250    if (!img)
251       return;
252
253    if (img->pixels)
254       free(img->pixels);
255    img->width  = 0;
256    img->height = 0;
257    img->pixels = NULL;
258 }
259
260 bool image_texture_load_buffer(struct texture_image *out_img,
261    enum image_type_enum type, void *buffer, size_t buffer_len)
262 {
263    unsigned r_shift, g_shift, b_shift, a_shift;
264    image_texture_set_color_shifts(&r_shift, &g_shift, &b_shift,
265       &a_shift, out_img);
266
267    if (type != IMAGE_TYPE_NONE)
268    {
269       if (image_texture_load_internal(
270          type, buffer, buffer_len, out_img,
271          a_shift, r_shift, g_shift, b_shift))
272       {
273          return true;
274       }
275    }
276
277    out_img->supports_rgba = false;
278    out_img->pixels = NULL;
279    out_img->width = 0;
280    out_img->height = 0;
281
282    return false;
283 }
284
285 bool image_texture_load(struct texture_image *out_img,
286       const char *path)
287 {
288    unsigned r_shift, g_shift, b_shift, a_shift;
289    size_t file_len             = 0;
290    struct nbio_t      *handle  = NULL;
291    void                   *ptr = NULL;
292    enum image_type_enum type  = image_texture_get_type(path);
293
294    image_texture_set_color_shifts(&r_shift, &g_shift, &b_shift,
295          &a_shift, out_img);
296
297    if (type != IMAGE_TYPE_NONE)
298    {
299       handle = (struct nbio_t*)nbio_open(path, NBIO_READ);
300       if (!handle)
301          goto error;
302       nbio_begin_read(handle);
303
304       while (!nbio_iterate(handle));
305
306       ptr = nbio_get_ptr(handle, &file_len);
307
308       if (!ptr)
309          goto error;
310
311       if (image_texture_load_internal(
312                type,
313                ptr, file_len, out_img,
314                a_shift, r_shift, g_shift, b_shift))
315          goto success;
316    }
317
318 error:
319    out_img->supports_rgba = false;
320    out_img->pixels        = NULL;
321    out_img->width         = 0;
322    out_img->height        = 0;
323    if (handle)
324       nbio_free(handle);
325
326    return false;
327
328 success:
329    if (handle)
330       nbio_free(handle);
331
332    return true;
333 }