loaders: Implement Write() instead of Save()
[gfxprim.git] / libs / loaders / GP_Loader.c
blobf1d51f244c88a6b29cee83fb627b46016ae91066
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
8 * *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
18 * *
19 * Copyright (C) 2009-2014 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
25 General functions for loading and saving bitmaps.
29 #include <string.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <ctype.h>
37 #include "core/GP_Debug.h"
39 #include "loaders/GP_Loaders.h"
40 #include "loaders/GP_Loader.h"
42 #define MAX_LOADERS 64
44 static const GP_Loader *loaders[MAX_LOADERS] = {
45 &GP_JPG,
46 &GP_PNG,
47 &GP_TIFF,
48 &GP_GIF,
49 &GP_BMP,
50 &GP_PBM,
51 &GP_PGM,
52 &GP_PPM,
53 &GP_PNM,
54 &GP_JP2,
55 &GP_PCX,
56 &GP_PSP,
57 &GP_PSD,
60 static unsigned int get_last_loader(void)
62 unsigned int i;
64 for (i = 0; i < MAX_LOADERS; i++) {
65 if (!loaders[i])
66 return i ? i - 1 : 0;
69 return i - 1;
72 int GP_LoaderRegister(const GP_Loader *self)
74 unsigned int i;
76 GP_DEBUG(1, "Registering loader for '%s'", self->fmt_name);
78 /* We have to keep the last terminating NULL */
79 for (i = 0; i < MAX_LOADERS - 2; i++) {
80 if (loaders[i] == self) {
81 GP_DEBUG(1, "Loader '%s' allready registered",
82 self->fmt_name);
83 errno = EEXIST;
84 return 1;
87 if (!loaders[i])
88 break;
91 if (loaders[i]) {
92 GP_DEBUG(1, "Loaders table is full");
93 errno = ENOSPC;
94 return 1;
97 loaders[i] = self;
99 return 0;
102 void GP_LoaderUnregister(const GP_Loader *self)
104 unsigned int i, last = get_last_loader();
106 if (self == NULL)
107 return;
109 GP_DEBUG(1, "Unregistering loader for '%s'", self->fmt_name);
111 for (i = 0; loaders[i]; i++) {
112 if (loaders[i] == self) {
113 loaders[i] = loaders[last];
114 loaders[last] = NULL;
115 return;
119 GP_WARN("Loader '%s' (%p) wasn't registered", self->fmt_name, self);
122 void GP_ListLoaders(void)
124 unsigned int i, j;
126 for (i = 0; loaders[i]; i++) {
127 printf("Format: %s\n", loaders[i]->fmt_name);
128 printf("Read:\t%s\n", loaders[i]->Read ? "Yes" : "No");
129 printf("Write:\t%s\n", loaders[i]->Write ? "Yes" : "No");
130 if (loaders[i]->save_ptypes) {
131 printf("Write Pixel Types: ");
132 for (j = 0; loaders[i]->save_ptypes[j]; j++) {
133 GP_PixelType ptype = loaders[i]->save_ptypes[j];
134 printf("%s ", GP_PixelTypeName(ptype));
136 printf("\n");
138 printf("Match:\t%s\n", loaders[i]->Match ? "Yes" : "No");
139 printf("Extensions: ");
140 for (j = 0; loaders[i]->extensions[j]; j++)
141 printf("%s ", loaders[i]->extensions[j]);
142 printf("\n");
144 if (loaders[i+1] != NULL)
145 printf("\n");
149 static const GP_Loader *loader_by_extension(const char *ext)
151 unsigned int i, j;
153 for (i = 0; loaders[i]; i++) {
154 for (j = 0; loaders[i]->extensions[j] != NULL; j++) {
155 if (!strcasecmp(ext, loaders[i]->extensions[j])) {
156 GP_DEBUG(1, "Found loader '%s'",
157 loaders[i]->fmt_name);
158 return loaders[i];
163 return NULL;
166 static const char *get_ext(const char *path)
168 size_t len = strlen(path);
169 int i;
171 for (i = len - 1; i >= 0; i--)
172 if (path[i] == '.')
173 break;
175 if (path[i] != '.')
176 return NULL;
178 return path + i + 1;
181 const GP_Loader *GP_LoaderByFilename(const char *path)
183 const char *ext = get_ext(path);
185 if (ext == NULL)
186 return NULL;
188 GP_DEBUG(1, "Loading file by filename extension '%s'", ext);
190 return loader_by_extension(ext);
193 static const GP_Loader *loader_by_signature(const char *path)
195 uint8_t buf[32];
196 FILE *f;
197 int err;
199 const GP_Loader *ret;
201 GP_DEBUG(1, "Trying to load by file signature");
203 f = fopen(path, "rb");
205 if (f == NULL) {
206 err = errno;
207 GP_DEBUG(1, "Failed to open file '%s'", path);
208 goto err0;
211 if (fread(buf, sizeof(buf), 1, f) < 1) {
212 GP_DEBUG(1, "Failed to read start of the file '%s'", path);
213 err = EIO;
214 goto err1;
217 fclose(f);
219 ret = GP_LoaderBySignature(buf);
221 if (ret == NULL)
222 errno = ENOSYS;
224 return ret;
225 err1:
226 fclose(f);
227 err0:
228 errno = err;
229 return NULL;
232 GP_Context *GP_ReadImage(GP_IO *io, GP_ProgressCallback *callback)
234 char buf[32];
235 off_t start;
236 const GP_Loader *loader;
238 start = GP_IOTell(io);
239 if (start == (off_t)-1) {
240 GP_DEBUG(1, "Failed to get IO stream offset: %s",
241 strerror(errno));
242 return NULL;
245 if (GP_IOFill(io, buf, sizeof(buf))) {
246 GP_DEBUG(1, "Failed to read first 32 bytes: %s",
247 strerror(errno));
248 return NULL;
251 if (GP_IOSeek(io, start, GP_IO_SEEK_SET) != start) {
252 GP_DEBUG(1, "Failed to seek at the start of the stream: %s",
253 strerror(errno));
254 return NULL;
257 loader = GP_LoaderBySignature(buf);
259 if (!loader) {
260 GP_DEBUG(1, "Failed to find a loader by signature for"
261 "(%x (%c) %x (%c)...)",
262 buf[0], isprint(buf[0]) ? buf[0] : ' ',
263 buf[1], isprint(buf[1]) ? buf[1] : ' ');
264 errno = ENOSYS;
265 return NULL;
268 if (!loader->Read) {
269 GP_DEBUG(1, "Loader for '%s' does not support reading",
270 loader->fmt_name);
271 errno = ENOSYS;
272 return NULL;
275 return loader->Read(io, callback);
278 GP_Context *GP_LoaderLoadImage(const GP_Loader *self, const char *src_path,
279 GP_ProgressCallback *callback)
281 GP_IO *io;
282 GP_Context *res;
283 int err;
285 GP_DEBUG(1, "Loading Image '%s'", src_path);
287 if (!self->Read) {
288 errno = ENOSYS;
289 return NULL;
292 io = GP_IOFile(src_path, GP_IO_RDONLY);
293 if (!io)
294 return NULL;
296 res = self->Read(io, callback);
298 err = errno;
299 GP_IOClose(io);
300 errno = err;
302 return res;
305 GP_Context *GP_LoadImage(const char *src_path, GP_ProgressCallback *callback)
307 int err;
308 struct stat st;
310 if (access(src_path, R_OK)) {
311 err = errno;
312 GP_DEBUG(1, "Failed to access file '%s' : %s",
313 src_path, strerror(errno));
314 errno = err;
315 return NULL;
318 if (stat(src_path, &st)) {
319 GP_WARN("Failed to stat '%s': %s", src_path, strerror(errno));
320 } else {
321 if (st.st_mode & S_IFDIR) {
322 errno = EISDIR;
323 return NULL;
327 GP_Context *img;
328 const GP_Loader *ext_load = NULL, *sig_load;
330 ext_load = GP_LoaderByFilename(src_path);
332 if (ext_load) {
333 img = GP_LoaderLoadImage(ext_load, src_path, callback);
335 if (img)
336 return img;
339 sig_load = loader_by_signature(src_path);
342 * Avoid further work if extension matches the signature but image
343 * couldn't be loaded. Probably unimplemented format or damaged file.
345 if (ext_load == sig_load)
346 return NULL;
348 if (ext_load && sig_load) {
349 GP_WARN("File '%s': Extension says %s but signature %s",
350 src_path, ext_load->fmt_name, sig_load->fmt_name);
353 if (sig_load)
354 return GP_LoaderLoadImage(sig_load, src_path, callback);
356 errno = ENOSYS;
357 return NULL;
360 int GP_LoadMetaData(const char *src_path, GP_MetaData *data)
362 const char *ext;
363 int err;
365 if (access(src_path, R_OK)) {
366 err = errno;
367 GP_DEBUG(1, "Failed to access file '%s' : %s",
368 src_path, strerror(errno));
369 errno = err;
370 return 1;
373 ext = get_ext(src_path);
375 if (ext == NULL)
376 goto out;
378 if (!strcasecmp(ext, "jpg") || !strcasecmp(ext, "jpeg"))
379 return GP_LoadJPGMetaData(src_path, data);
381 if (!strcasecmp(ext, "png"))
382 return GP_LoadPNGMetaData(src_path, data);
384 out:
385 errno = ENOSYS;
386 return 1;
389 int GP_LoaderSaveImage(const GP_Loader *self, const GP_Context *src,
390 const char *dst_path, GP_ProgressCallback *callback)
392 GP_IO *io;
394 GP_DEBUG(1, "Saving image '%s' format %s", dst_path, self->fmt_name);
396 if (!self->Write) {
397 errno = ENOSYS;
398 return 1;
401 io = GP_IOFile(dst_path, GP_IO_WRONLY);
403 if (!io)
404 return 1;
406 if (self->Write(src, io, callback)) {
407 GP_IOClose(io);
408 unlink(dst_path);
409 return 1;
412 if (GP_IOClose(io)) {
413 unlink(dst_path);
414 return 1;
417 return 0;
420 int GP_SaveImage(const GP_Context *src, const char *dst_path,
421 GP_ProgressCallback *callback)
423 const GP_Loader *l = GP_LoaderByFilename(dst_path);
425 if (l == NULL) {
426 errno = EINVAL;
427 return 1;
430 return GP_LoaderSaveImage(l, src, dst_path, callback);
433 const GP_Loader *GP_LoaderBySignature(const void *buf)
435 unsigned int i;
437 for (i = 0; loaders[i]; i++) {
438 if (loaders[i]->Match && loaders[i]->Match(buf) == 1) {
439 GP_DEBUG(1, "Found loader '%s'", loaders[i]->fmt_name);
440 return loaders[i];
444 GP_DEBUG(1, "Loader not found");
446 return NULL;