Commit | Line | Data |
---|---|---|
3719602c PC |
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 | } |