added scr_lvled, a bunch of libraries, and improved framework code
[raydungeon] / libs / imago / src / imago2.c
1 /*
2 libimago - a multi-format image file input/output library.
3 Copyright (C) 2010-2021 John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published
7 by the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include "imago2.h"
23 #include "ftmodule.h"
24 #include "byteord.h"
25
26 /* calculate int-aligned offset to colormap, right after the end of the pixel data */
27 #define CMAPPTR(fb, fbsz)       \
28         (struct img_colormap*)((((uintptr_t)fb) + (fbsz) + sizeof(int) - 1) & ~(sizeof(int) - 1))
29
30 static int pixel_size(enum img_fmt fmt);
31 static size_t def_read(void *buf, size_t bytes, void *uptr);
32 static size_t def_write(void *buf, size_t bytes, void *uptr);
33 static long def_seek(long offset, int whence, void *uptr);
34
35
36 void img_init(struct img_pixmap *img)
37 {
38         img->pixels = 0;
39         img->width = img->height = 0;
40         img->fmt = IMG_FMT_RGBA32;
41         img->pixelsz = pixel_size(img->fmt);
42         img->name = 0;
43 }
44
45
46 void img_destroy(struct img_pixmap *img)
47 {
48         free(img->pixels);
49         img->pixels = 0;        /* just in case... */
50         img->width = img->height = 0xbadbeef;
51         free(img->name);
52 }
53
54 struct img_pixmap *img_create(void)
55 {
56         struct img_pixmap *p;
57
58         if(!(p = malloc(sizeof *p))) {
59                 return 0;
60         }
61         img_init(p);
62         return p;
63 }
64
65 void img_free(struct img_pixmap *img)
66 {
67         img_destroy(img);
68         free(img);
69 }
70
71 int img_set_name(struct img_pixmap *img, const char *name)
72 {
73         char *tmp;
74
75         if(!(tmp = malloc(strlen(name) + 1))) {
76                 return -1;
77         }
78         strcpy(tmp, name);
79         img->name = tmp;
80         return 0;
81 }
82
83 int img_set_format(struct img_pixmap *img, enum img_fmt fmt)
84 {
85         if(img->pixels) {
86                 return img_convert(img, fmt);
87         }
88         img->fmt = fmt;
89         return 0;
90 }
91
92 int img_copy(struct img_pixmap *dest, struct img_pixmap *src)
93 {
94         if(img_set_pixels(dest, src->width, src->height, src->fmt, src->pixels) == -1) {
95                 return -1;
96         }
97
98         if(src->fmt == IMG_FMT_IDX8) {
99                 *img_colormap(dest) = *img_colormap(src);
100         }
101         return 0;
102 }
103
104 int img_set_pixels(struct img_pixmap *img, int w, int h, enum img_fmt fmt, void *pix)
105 {
106         void *newpix;
107         int pixsz = pixel_size(fmt);
108         int bsz = w * h * pixsz;
109
110         if(fmt == IMG_FMT_IDX8) {
111                 /* add space for the colormap, and space to align it to sizeof(int) */
112                 bsz += sizeof(struct img_colormap) + sizeof(int) - 1;
113         }
114
115         if(!(newpix = malloc(bsz))) {
116                 return -1;
117         }
118
119         if(pix) {
120                 memcpy(newpix, pix, w * h * pixsz);
121         } else {
122                 memset(newpix, 0, bsz);
123         }
124
125         free(img->pixels);
126         img->pixels = newpix;
127         img->width = w;
128         img->height = h;
129         img->pixelsz = pixsz;
130         img->fmt = fmt;
131         return 0;
132 }
133
134 void *img_load_pixels(const char *fname, int *xsz, int *ysz, enum img_fmt fmt)
135 {
136         struct img_pixmap img;
137
138         img_init(&img);
139
140         if(img_load(&img, fname) == -1) {
141                 return 0;
142         }
143         if(img.fmt != fmt) {
144                 if(img_convert(&img, fmt) == -1) {
145                         img_destroy(&img);
146                         return 0;
147                 }
148         }
149
150         *xsz = img.width;
151         *ysz = img.height;
152         return img.pixels;
153 }
154
155 int img_save_pixels(const char *fname, void *pix, int xsz, int ysz, enum img_fmt fmt)
156 {
157         int res;
158         struct img_pixmap img;
159
160         img_init(&img);
161         img.fmt = fmt;
162         img.pixelsz = pixel_size(fmt);
163         img.width = xsz;
164         img.height = ysz;
165         img.pixels = pix;
166
167         res = img_save(&img, fname);
168         img.pixels = 0;
169         img_destroy(&img);
170         return res;
171 }
172
173 void img_free_pixels(void *pix)
174 {
175         free(pix);
176 }
177
178 int img_load(struct img_pixmap *img, const char *fname)
179 {
180         int res;
181         FILE *fp;
182
183         if(!(fp = fopen(fname, "rb"))) {
184                 return -1;
185         }
186         img_set_name(img, fname);
187         res = img_read_file(img, fp);
188         fclose(fp);
189         return res;
190 }
191
192 /* TODO implement filetype selection */
193 int img_save(struct img_pixmap *img, const char *fname)
194 {
195         int res;
196         FILE *fp;
197
198         img_set_name(img, fname);
199
200         if(!(fp = fopen(fname, "wb"))) {
201                 return -1;
202         }
203         res = img_write_file(img, fp);
204         fclose(fp);
205         return res;
206 }
207
208 int img_read_file(struct img_pixmap *img, FILE *fp)
209 {
210         struct img_io io = {0, def_read, def_write, def_seek};
211
212         io.uptr = fp;
213         return img_read(img, &io);
214 }
215
216 int img_write_file(struct img_pixmap *img, FILE *fp)
217 {
218         struct img_io io = {0, def_read, def_write, def_seek};
219
220         io.uptr = fp;
221         return img_write(img, &io);
222 }
223
224 int img_read(struct img_pixmap *img, struct img_io *io)
225 {
226         struct ftype_module *mod;
227
228         if((mod = img_find_format_module(io, img->name))) {
229                 return mod->read(img, io);
230         }
231         return -1;
232 }
233
234 int img_write(struct img_pixmap *img, struct img_io *io)
235 {
236         struct ftype_module *mod;
237
238         if(!img->name || !(mod = img_guess_format(img->name))) {
239                 /* TODO throw some sort of warning? */
240                 /* TODO implement some sort of module priority or let the user specify? */
241                 if(!(mod = img_get_module(0))) {
242                         return -1;
243                 }
244         }
245
246         return mod->write(img, io);
247 }
248
249 int img_to_float(struct img_pixmap *img)
250 {
251         enum img_fmt targ_fmt;
252
253         switch(img->fmt) {
254         case IMG_FMT_GREY8:
255                 targ_fmt = IMG_FMT_GREYF;
256                 break;
257
258         case IMG_FMT_RGB24:
259                 targ_fmt = IMG_FMT_RGBF;
260                 break;
261
262         case IMG_FMT_RGBA32:
263                 targ_fmt = IMG_FMT_RGBAF;
264                 break;
265
266         default:
267                 return 0;       /* already float */
268         }
269
270         return img_convert(img, targ_fmt);
271 }
272
273 int img_to_integer(struct img_pixmap *img)
274 {
275         enum img_fmt targ_fmt;
276
277         switch(img->fmt) {
278         case IMG_FMT_GREYF:
279                 targ_fmt = IMG_FMT_GREY8;
280                 break;
281
282         case IMG_FMT_RGBF:
283                 targ_fmt = IMG_FMT_RGB24;
284                 break;
285
286         case IMG_FMT_RGBAF:
287                 targ_fmt = IMG_FMT_RGBA32;
288                 break;
289
290         default:
291                 return 0;       /* already integer */
292         }
293
294         return img_convert(img, targ_fmt);
295 }
296
297 int img_is_float(struct img_pixmap *img)
298 {
299         return img->fmt >= IMG_FMT_GREYF && img->fmt <= IMG_FMT_RGBAF;
300 }
301
302 int img_has_alpha(struct img_pixmap *img)
303 {
304         if(img->fmt == IMG_FMT_RGBA32 || img->fmt == IMG_FMT_RGBAF) {
305                 return 1;
306         }
307         return 0;
308 }
309
310 int img_is_greyscale(struct img_pixmap *img)
311 {
312         return img->fmt == IMG_FMT_GREY8 || img->fmt == IMG_FMT_GREYF;
313 }
314
315
316 void img_setpixel(struct img_pixmap *img, int x, int y, void *pixel)
317 {
318         char *dest = (char*)img->pixels + (y * img->width + x) * img->pixelsz;
319         memcpy(dest, pixel, img->pixelsz);
320 }
321
322 void img_getpixel(struct img_pixmap *img, int x, int y, void *pixel)
323 {
324         char *dest = (char*)img->pixels + (y * img->width + x) * img->pixelsz;
325         memcpy(pixel, dest, img->pixelsz);
326 }
327
328 void img_setpixel1i(struct img_pixmap *img, int x, int y, int pix)
329 {
330         img_setpixel4i(img, x, y, pix, pix, pix, pix);
331 }
332
333 void img_setpixel1f(struct img_pixmap *img, int x, int y, float pix)
334 {
335         img_setpixel4f(img, x, y, pix, pix, pix, pix);
336 }
337
338 void img_setpixel4i(struct img_pixmap *img, int x, int y, int r, int g, int b, int a)
339 {
340         if(img_is_float(img)) {
341                 img_setpixel4f(img, x, y, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
342         } else {
343                 unsigned char pixel[4];
344                 pixel[0] = r;
345                 pixel[1] = g;
346                 pixel[2] = b;
347                 pixel[3] = a;
348
349                 img_setpixel(img, x, y, pixel);
350         }
351 }
352
353 void img_setpixel4f(struct img_pixmap *img, int x, int y, float r, float g, float b, float a)
354 {
355         if(img_is_float(img)) {
356                 float pixel[4];
357                 pixel[0] = r;
358                 pixel[1] = g;
359                 pixel[2] = b;
360                 pixel[3] = a;
361
362                 img_setpixel(img, x, y, pixel);
363         } else {
364                 img_setpixel4i(img, x, y, (int)(r * 255.0), (int)(g * 255.0), (int)(b * 255.0), (int)(a * 255.0));
365         }
366 }
367
368 void img_getpixel1i(struct img_pixmap *img, int x, int y, int *pix)
369 {
370         int junk[3];
371         img_getpixel4i(img, x, y, pix, junk, junk + 1, junk + 2);
372 }
373
374 void img_getpixel1f(struct img_pixmap *img, int x, int y, float *pix)
375 {
376         float junk[3];
377         img_getpixel4f(img, x, y, pix, junk, junk + 1, junk + 2);
378 }
379
380 void img_getpixel4i(struct img_pixmap *img, int x, int y, int *r, int *g, int *b, int *a)
381 {
382         if(img_is_float(img)) {
383                 float pixel[4] = {0, 0, 0, 0};
384                 img_getpixel(img, x, y, pixel);
385                 *r = pixel[0] * 255.0;
386                 *g = pixel[1] * 255.0;
387                 *b = pixel[2] * 255.0;
388                 *a = pixel[3] * 255.0;
389         } else {
390                 unsigned char pixel[4];
391                 img_getpixel(img, x, y, pixel);
392                 *r = pixel[0];
393                 *g = pixel[1];
394                 *b = pixel[2];
395                 *a = pixel[3];
396         }
397 }
398
399 void img_getpixel4f(struct img_pixmap *img, int x, int y, float *r, float *g, float *b, float *a)
400 {
401         if(img_is_float(img)) {
402                 float pixel[4] = {0, 0, 0, 0};
403                 img_getpixel(img, x, y, pixel);
404                 *r = pixel[0];
405                 *g = pixel[1];
406                 *b = pixel[2];
407                 *a = pixel[3];
408         } else {
409                 unsigned char pixel[4];
410                 img_getpixel(img, x, y, pixel);
411                 *r = pixel[0] / 255.0;
412                 *g = pixel[1] / 255.0;
413                 *b = pixel[2] / 255.0;
414                 *a = pixel[3] / 255.0;
415         }
416 }
417
418 struct img_colormap *img_colormap(struct img_pixmap *img)
419 {
420         if(img->fmt != IMG_FMT_IDX8 || !img->pixels) {
421                 return 0;
422         }
423
424         return CMAPPTR(img->pixels, img->width * img->height * img->pixelsz);
425 }
426
427 void img_io_set_user_data(struct img_io *io, void *uptr)
428 {
429         io->uptr = uptr;
430 }
431
432 void img_io_set_read_func(struct img_io *io, size_t (*read)(void*, size_t, void*))
433 {
434         io->read = read;
435 }
436
437 void img_io_set_write_func(struct img_io *io, size_t (*write)(void*, size_t, void*))
438 {
439         io->write = write;
440 }
441
442 void img_io_set_seek_func(struct img_io *io, long (*seek)(long, int, void*))
443 {
444         io->seek = seek;
445 }
446
447
448 static int pixel_size(enum img_fmt fmt)
449 {
450         switch(fmt) {
451         case IMG_FMT_GREY8:
452         case IMG_FMT_IDX8:
453                 return 1;
454         case IMG_FMT_RGB24:
455                 return 3;
456         case IMG_FMT_RGBA32:
457         case IMG_FMT_BGRA32:
458                 return 4;
459         case IMG_FMT_GREYF:
460                 return sizeof(float);
461         case IMG_FMT_RGBF:
462                 return 3 * sizeof(float);
463         case IMG_FMT_RGBAF:
464                 return 4 * sizeof(float);
465         case IMG_FMT_RGB565:
466                 return 2;
467         default:
468                 break;
469         }
470         return 0;
471 }
472
473 static size_t def_read(void *buf, size_t bytes, void *uptr)
474 {
475         return uptr ? fread(buf, 1, bytes, uptr) : 0;
476 }
477
478 static size_t def_write(void *buf, size_t bytes, void *uptr)
479 {
480         return uptr ? fwrite(buf, 1, bytes, uptr) : 0;
481 }
482
483 static long def_seek(long offset, int whence, void *uptr)
484 {
485         if(!uptr || fseek(uptr, offset, whence) == -1) {
486                 return -1;
487         }
488         return ftell(uptr);
489 }
490