rev |
line source |
nuclear@26
|
1 /*
|
nuclear@26
|
2 libimago - a multi-format image file input/output library.
|
nuclear@26
|
3 Copyright (C) 2010 John Tsiombikas <nuclear@member.fsf.org>
|
nuclear@26
|
4
|
nuclear@26
|
5 This program is free software: you can redistribute it and/or modify
|
nuclear@26
|
6 it under the terms of the GNU Lesser General Public License as published
|
nuclear@26
|
7 by the Free Software Foundation, either version 3 of the License, or
|
nuclear@26
|
8 (at your option) any later version.
|
nuclear@26
|
9
|
nuclear@26
|
10 This program is distributed in the hope that it will be useful,
|
nuclear@26
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nuclear@26
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nuclear@26
|
13 GNU Lesser General Public License for more details.
|
nuclear@26
|
14
|
nuclear@26
|
15 You should have received a copy of the GNU Lesser General Public License
|
nuclear@26
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
nuclear@26
|
17 */
|
nuclear@26
|
18
|
nuclear@26
|
19 #include <stdio.h>
|
nuclear@26
|
20 #include <stdlib.h>
|
nuclear@26
|
21 #include <string.h>
|
nuclear@26
|
22 #include "imago2.h"
|
nuclear@26
|
23 #include "ftype_module.h"
|
nuclear@26
|
24
|
nuclear@26
|
25 static int pixel_size(enum img_fmt fmt);
|
nuclear@26
|
26 static size_t def_read(void *buf, size_t bytes, void *uptr);
|
nuclear@26
|
27 static size_t def_write(void *buf, size_t bytes, void *uptr);
|
nuclear@26
|
28 static long def_seek(long offset, int whence, void *uptr);
|
nuclear@26
|
29
|
nuclear@26
|
30
|
nuclear@26
|
31 void img_init(struct img_pixmap *img)
|
nuclear@26
|
32 {
|
nuclear@26
|
33 img->pixels = 0;
|
nuclear@26
|
34 img->width = img->height = 0;
|
nuclear@26
|
35 img->fmt = IMG_FMT_RGBA32;
|
nuclear@26
|
36 img->pixelsz = pixel_size(img->fmt);
|
nuclear@26
|
37 img->name = 0;
|
nuclear@26
|
38 }
|
nuclear@26
|
39
|
nuclear@26
|
40
|
nuclear@26
|
41 void img_destroy(struct img_pixmap *img)
|
nuclear@26
|
42 {
|
nuclear@26
|
43 free(img->pixels);
|
nuclear@26
|
44 img->pixels = 0; /* just in case... */
|
nuclear@26
|
45 img->width = img->height = 0xbadbeef;
|
nuclear@26
|
46 free(img->name);
|
nuclear@26
|
47 }
|
nuclear@26
|
48
|
nuclear@26
|
49 struct img_pixmap *img_create(void)
|
nuclear@26
|
50 {
|
nuclear@26
|
51 struct img_pixmap *p;
|
nuclear@26
|
52
|
nuclear@26
|
53 if(!(p = malloc(sizeof *p))) {
|
nuclear@26
|
54 return 0;
|
nuclear@26
|
55 }
|
nuclear@26
|
56 img_init(p);
|
nuclear@26
|
57 return p;
|
nuclear@26
|
58 }
|
nuclear@26
|
59
|
nuclear@26
|
60 void img_free(struct img_pixmap *img)
|
nuclear@26
|
61 {
|
nuclear@26
|
62 img_destroy(img);
|
nuclear@26
|
63 free(img);
|
nuclear@26
|
64 }
|
nuclear@26
|
65
|
nuclear@26
|
66 int img_set_name(struct img_pixmap *img, const char *name)
|
nuclear@26
|
67 {
|
nuclear@26
|
68 char *tmp;
|
nuclear@26
|
69
|
nuclear@26
|
70 if(!(tmp = malloc(strlen(name) + 1))) {
|
nuclear@26
|
71 return -1;
|
nuclear@26
|
72 }
|
nuclear@26
|
73 strcpy(tmp, name);
|
nuclear@26
|
74 img->name = tmp;
|
nuclear@26
|
75 return 0;
|
nuclear@26
|
76 }
|
nuclear@26
|
77
|
nuclear@26
|
78 int img_set_format(struct img_pixmap *img, enum img_fmt fmt)
|
nuclear@26
|
79 {
|
nuclear@26
|
80 if(img->pixels) {
|
nuclear@26
|
81 return img_convert(img, fmt);
|
nuclear@26
|
82 }
|
nuclear@26
|
83 img->fmt = fmt;
|
nuclear@26
|
84 return 0;
|
nuclear@26
|
85 }
|
nuclear@26
|
86
|
nuclear@26
|
87 int img_copy(struct img_pixmap *dest, struct img_pixmap *src)
|
nuclear@26
|
88 {
|
nuclear@26
|
89 return img_set_pixels(dest, src->width, src->height, src->fmt, src->pixels);
|
nuclear@26
|
90 }
|
nuclear@26
|
91
|
nuclear@26
|
92 int img_set_pixels(struct img_pixmap *img, int w, int h, enum img_fmt fmt, void *pix)
|
nuclear@26
|
93 {
|
nuclear@26
|
94 void *newpix;
|
nuclear@26
|
95 int pixsz = pixel_size(fmt);
|
nuclear@26
|
96
|
nuclear@26
|
97 if(!(newpix = malloc(w * h * pixsz))) {
|
nuclear@26
|
98 return -1;
|
nuclear@26
|
99 }
|
nuclear@26
|
100
|
nuclear@26
|
101 if(pix) {
|
nuclear@26
|
102 memcpy(newpix, pix, w * h * pixsz);
|
nuclear@26
|
103 } else {
|
nuclear@26
|
104 memset(newpix, 0, w * h * pixsz);
|
nuclear@26
|
105 }
|
nuclear@26
|
106
|
nuclear@26
|
107 free(img->pixels);
|
nuclear@26
|
108 img->pixels = newpix;
|
nuclear@26
|
109 img->width = w;
|
nuclear@26
|
110 img->height = h;
|
nuclear@26
|
111 img->pixelsz = pixsz;
|
nuclear@26
|
112 img->fmt = fmt;
|
nuclear@26
|
113 return 0;
|
nuclear@26
|
114 }
|
nuclear@26
|
115
|
nuclear@26
|
116 void *img_load_pixels(const char *fname, int *xsz, int *ysz, enum img_fmt fmt)
|
nuclear@26
|
117 {
|
nuclear@26
|
118 struct img_pixmap img;
|
nuclear@26
|
119
|
nuclear@26
|
120 img_init(&img);
|
nuclear@26
|
121
|
nuclear@26
|
122 if(img_load(&img, fname) == -1) {
|
nuclear@26
|
123 return 0;
|
nuclear@26
|
124 }
|
nuclear@26
|
125 if(img.fmt != fmt) {
|
nuclear@26
|
126 if(img_convert(&img, fmt) == -1) {
|
nuclear@26
|
127 img_destroy(&img);
|
nuclear@26
|
128 return 0;
|
nuclear@26
|
129 }
|
nuclear@26
|
130 }
|
nuclear@26
|
131
|
nuclear@26
|
132 *xsz = img.width;
|
nuclear@26
|
133 *ysz = img.height;
|
nuclear@26
|
134 return img.pixels;
|
nuclear@26
|
135 }
|
nuclear@26
|
136
|
nuclear@26
|
137 int img_save_pixels(const char *fname, void *pix, int xsz, int ysz, enum img_fmt fmt)
|
nuclear@26
|
138 {
|
nuclear@26
|
139 struct img_pixmap img;
|
nuclear@26
|
140
|
nuclear@26
|
141 img_init(&img);
|
nuclear@26
|
142 img.fmt = fmt;
|
nuclear@26
|
143 img.name = (char*)fname;
|
nuclear@26
|
144 img.width = xsz;
|
nuclear@26
|
145 img.height = ysz;
|
nuclear@26
|
146 img.pixels = pix;
|
nuclear@26
|
147
|
nuclear@26
|
148 return img_save(&img, fname);
|
nuclear@26
|
149 }
|
nuclear@26
|
150
|
nuclear@26
|
151 void img_free_pixels(void *pix)
|
nuclear@26
|
152 {
|
nuclear@26
|
153 free(pix);
|
nuclear@26
|
154 }
|
nuclear@26
|
155
|
nuclear@26
|
156 int img_load(struct img_pixmap *img, const char *fname)
|
nuclear@26
|
157 {
|
nuclear@26
|
158 int res;
|
nuclear@26
|
159 FILE *fp;
|
nuclear@26
|
160
|
nuclear@26
|
161 if(!(fp = fopen(fname, "rb"))) {
|
nuclear@26
|
162 return -1;
|
nuclear@26
|
163 }
|
nuclear@26
|
164 res = img_read_file(img, fp);
|
nuclear@26
|
165 fclose(fp);
|
nuclear@26
|
166 return res;
|
nuclear@26
|
167 }
|
nuclear@26
|
168
|
nuclear@26
|
169 /* TODO implement filetype selection */
|
nuclear@26
|
170 int img_save(struct img_pixmap *img, const char *fname)
|
nuclear@26
|
171 {
|
nuclear@26
|
172 int res;
|
nuclear@26
|
173 FILE *fp;
|
nuclear@26
|
174
|
nuclear@26
|
175 img_set_name(img, fname);
|
nuclear@26
|
176
|
nuclear@26
|
177 if(!(fp = fopen(fname, "wb"))) {
|
nuclear@26
|
178 return -1;
|
nuclear@26
|
179 }
|
nuclear@26
|
180 res = img_write_file(img, fp);
|
nuclear@26
|
181 fclose(fp);
|
nuclear@26
|
182 return res;
|
nuclear@26
|
183 }
|
nuclear@26
|
184
|
nuclear@26
|
185 int img_read_file(struct img_pixmap *img, FILE *fp)
|
nuclear@26
|
186 {
|
nuclear@26
|
187 struct img_io io = {0, def_read, def_write, def_seek};
|
nuclear@26
|
188
|
nuclear@26
|
189 io.uptr = fp;
|
nuclear@26
|
190 return img_read(img, &io);
|
nuclear@26
|
191 }
|
nuclear@26
|
192
|
nuclear@26
|
193 int img_write_file(struct img_pixmap *img, FILE *fp)
|
nuclear@26
|
194 {
|
nuclear@26
|
195 struct img_io io = {0, def_read, def_write, def_seek};
|
nuclear@26
|
196
|
nuclear@26
|
197 io.uptr = fp;
|
nuclear@26
|
198 return img_write(img, &io);
|
nuclear@26
|
199 }
|
nuclear@26
|
200
|
nuclear@26
|
201 int img_read(struct img_pixmap *img, struct img_io *io)
|
nuclear@26
|
202 {
|
nuclear@26
|
203 struct ftype_module *mod;
|
nuclear@26
|
204
|
nuclear@26
|
205 if((mod = img_find_format_module(io))) {
|
nuclear@26
|
206 return mod->read(img, io);
|
nuclear@26
|
207 }
|
nuclear@26
|
208 return -1;
|
nuclear@26
|
209 }
|
nuclear@26
|
210
|
nuclear@26
|
211 int img_write(struct img_pixmap *img, struct img_io *io)
|
nuclear@26
|
212 {
|
nuclear@26
|
213 struct ftype_module *mod;
|
nuclear@26
|
214
|
nuclear@26
|
215 if(!img->name || !(mod = img_guess_format(img->name))) {
|
nuclear@26
|
216 /* TODO throw some sort of warning? */
|
nuclear@26
|
217 /* TODO implement some sort of module priority or let the user specify? */
|
nuclear@26
|
218 if(!(mod = img_get_module(0))) {
|
nuclear@26
|
219 return -1;
|
nuclear@26
|
220 }
|
nuclear@26
|
221 }
|
nuclear@26
|
222
|
nuclear@26
|
223 return mod->write(img, io);
|
nuclear@26
|
224 }
|
nuclear@26
|
225
|
nuclear@26
|
226 int img_to_float(struct img_pixmap *img)
|
nuclear@26
|
227 {
|
nuclear@26
|
228 enum img_fmt targ_fmt;
|
nuclear@26
|
229
|
nuclear@26
|
230 switch(img->fmt) {
|
nuclear@26
|
231 case IMG_FMT_GREY8:
|
nuclear@26
|
232 targ_fmt = IMG_FMT_GREYF;
|
nuclear@26
|
233 break;
|
nuclear@26
|
234
|
nuclear@26
|
235 case IMG_FMT_RGB24:
|
nuclear@26
|
236 targ_fmt = IMG_FMT_RGBF;
|
nuclear@26
|
237 break;
|
nuclear@26
|
238
|
nuclear@26
|
239 case IMG_FMT_RGBA32:
|
nuclear@26
|
240 targ_fmt = IMG_FMT_RGBAF;
|
nuclear@26
|
241 break;
|
nuclear@26
|
242
|
nuclear@26
|
243 default:
|
nuclear@26
|
244 return 0; /* already float */
|
nuclear@26
|
245 }
|
nuclear@26
|
246
|
nuclear@26
|
247 return img_convert(img, targ_fmt);
|
nuclear@26
|
248 }
|
nuclear@26
|
249
|
nuclear@26
|
250 int img_to_integer(struct img_pixmap *img)
|
nuclear@26
|
251 {
|
nuclear@26
|
252 enum img_fmt targ_fmt;
|
nuclear@26
|
253
|
nuclear@26
|
254 switch(img->fmt) {
|
nuclear@26
|
255 case IMG_FMT_GREYF:
|
nuclear@26
|
256 targ_fmt = IMG_FMT_GREY8;
|
nuclear@26
|
257 break;
|
nuclear@26
|
258
|
nuclear@26
|
259 case IMG_FMT_RGBF:
|
nuclear@26
|
260 targ_fmt = IMG_FMT_RGB24;
|
nuclear@26
|
261 break;
|
nuclear@26
|
262
|
nuclear@26
|
263 case IMG_FMT_RGBAF:
|
nuclear@26
|
264 targ_fmt = IMG_FMT_RGBA32;
|
nuclear@26
|
265 break;
|
nuclear@26
|
266
|
nuclear@26
|
267 default:
|
nuclear@26
|
268 return 0; /* already integer */
|
nuclear@26
|
269 }
|
nuclear@26
|
270
|
nuclear@26
|
271 return img_convert(img, targ_fmt);
|
nuclear@26
|
272 }
|
nuclear@26
|
273
|
nuclear@26
|
274 int img_is_float(struct img_pixmap *img)
|
nuclear@26
|
275 {
|
nuclear@26
|
276 return img->fmt >= IMG_FMT_GREYF && img->fmt <= IMG_FMT_RGBAF;
|
nuclear@26
|
277 }
|
nuclear@26
|
278
|
nuclear@26
|
279 int img_has_alpha(struct img_pixmap *img)
|
nuclear@26
|
280 {
|
nuclear@26
|
281 return img->fmt >= IMG_FMT_GREY8 && img->fmt <= IMG_FMT_RGBA32;
|
nuclear@26
|
282 }
|
nuclear@26
|
283
|
nuclear@26
|
284 void img_io_set_user_data(struct img_io *io, void *uptr)
|
nuclear@26
|
285 {
|
nuclear@26
|
286 io->uptr = uptr;
|
nuclear@26
|
287 }
|
nuclear@26
|
288
|
nuclear@26
|
289 void img_io_set_read_func(struct img_io *io, size_t (*read)(void*, size_t, void*))
|
nuclear@26
|
290 {
|
nuclear@26
|
291 io->read = read;
|
nuclear@26
|
292 }
|
nuclear@26
|
293
|
nuclear@26
|
294 void img_io_set_write_func(struct img_io *io, size_t (*write)(void*, size_t, void*))
|
nuclear@26
|
295 {
|
nuclear@26
|
296 io->write = write;
|
nuclear@26
|
297 }
|
nuclear@26
|
298
|
nuclear@26
|
299 void img_io_set_seek_func(struct img_io *io, long (*seek)(long, int, void*))
|
nuclear@26
|
300 {
|
nuclear@26
|
301 io->seek = seek;
|
nuclear@26
|
302 }
|
nuclear@26
|
303
|
nuclear@26
|
304
|
nuclear@26
|
305 static int pixel_size(enum img_fmt fmt)
|
nuclear@26
|
306 {
|
nuclear@26
|
307 switch(fmt) {
|
nuclear@26
|
308 case IMG_FMT_GREY8:
|
nuclear@26
|
309 return 1;
|
nuclear@26
|
310 case IMG_FMT_RGB24:
|
nuclear@26
|
311 return 3;
|
nuclear@26
|
312 case IMG_FMT_RGBA32:
|
nuclear@26
|
313 return 4;
|
nuclear@26
|
314 case IMG_FMT_GREYF:
|
nuclear@26
|
315 return sizeof(float);
|
nuclear@26
|
316 case IMG_FMT_RGBF:
|
nuclear@26
|
317 return 3 * sizeof(float);
|
nuclear@26
|
318 case IMG_FMT_RGBAF:
|
nuclear@26
|
319 return 4 * sizeof(float);
|
nuclear@26
|
320 default:
|
nuclear@26
|
321 break;
|
nuclear@26
|
322 }
|
nuclear@26
|
323 return 0;
|
nuclear@26
|
324 }
|
nuclear@26
|
325
|
nuclear@26
|
326 static size_t def_read(void *buf, size_t bytes, void *uptr)
|
nuclear@26
|
327 {
|
nuclear@26
|
328 return uptr ? fread(buf, 1, bytes, uptr) : 0;
|
nuclear@26
|
329 }
|
nuclear@26
|
330
|
nuclear@26
|
331 static size_t def_write(void *buf, size_t bytes, void *uptr)
|
nuclear@26
|
332 {
|
nuclear@26
|
333 return uptr ? fwrite(buf, 1, bytes, uptr) : 0;
|
nuclear@26
|
334 }
|
nuclear@26
|
335
|
nuclear@26
|
336 static long def_seek(long offset, int whence, void *uptr)
|
nuclear@26
|
337 {
|
nuclear@26
|
338 if(!uptr || fseek(uptr, offset, whence) == -1) {
|
nuclear@26
|
339 return -1;
|
nuclear@26
|
340 }
|
nuclear@26
|
341 return ftell(uptr);
|
nuclear@26
|
342 }
|
nuclear@26
|
343
|