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