Rename GP_Context -> GP_Pixmap
[gfxprim.git] / libs / loaders / GP_Loader.c
blob0d6c449b2414818b05ceef0f1c784e041d634681
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_Pixmap *GP_ReadImage(GP_IO *io, GP_ProgressCallback *callback)
234 GP_Pixmap *ret = NULL;
236 GP_ReadImageEx(io, &ret, NULL, callback);
238 return ret;
241 int GP_ReadImageEx(GP_IO *io, GP_Pixmap **img, GP_DataStorage *meta_data,
242 GP_ProgressCallback *callback)
244 char buf[32];
245 off_t start;
246 const GP_Loader *loader;
248 start = GP_IOTell(io);
249 if (start == (off_t)-1) {
250 GP_DEBUG(1, "Failed to get IO stream offset: %s",
251 strerror(errno));
252 return 1;
255 if (GP_IOFill(io, buf, sizeof(buf))) {
256 GP_DEBUG(1, "Failed to read first 32 bytes: %s",
257 strerror(errno));
258 return 1;
261 if (GP_IOSeek(io, start, GP_IO_SEEK_SET) != start) {
262 GP_DEBUG(1, "Failed to seek at the start of the stream: %s",
263 strerror(errno));
264 return 1;
267 loader = GP_LoaderBySignature(buf);
269 if (!loader) {
270 GP_DEBUG(1, "Failed to find a loader by signature for"
271 "(%x (%c) %x (%c)...)",
272 buf[0], isprint(buf[0]) ? buf[0] : ' ',
273 buf[1], isprint(buf[1]) ? buf[1] : ' ');
274 errno = ENOSYS;
275 return 1;
278 if (!loader->Read) {
279 GP_DEBUG(1, "Loader for '%s' does not support reading",
280 loader->fmt_name);
281 errno = ENOSYS;
282 return 1;
285 return loader->Read(io, img, meta_data, callback);
288 int GP_LoaderLoadImageEx(const GP_Loader *self, const char *src_path,
289 GP_Pixmap **img, GP_DataStorage *storage,
290 GP_ProgressCallback *callback)
292 GP_IO *io;
293 int err, ret;
295 GP_DEBUG(1, "Loading Image '%s'", src_path);
297 if (!self->Read) {
298 errno = ENOSYS;
299 return ENOSYS;
302 io = GP_IOFile(src_path, GP_IO_RDONLY);
303 if (!io)
304 return 1;
306 ret = self->Read(io, img, storage, callback);
308 err = errno;
309 GP_IOClose(io);
310 errno = err;
312 return ret;
316 GP_Pixmap *GP_LoaderLoadImage(const GP_Loader *self, const char *src_path,
317 GP_ProgressCallback *callback)
319 GP_Pixmap *ret = NULL;
321 GP_LoaderLoadImageEx(self, src_path, &ret, NULL, callback);
323 return ret;
326 GP_Pixmap *GP_LoaderReadImage(const GP_Loader *self, GP_IO *io,
327 GP_ProgressCallback *callback)
329 GP_Pixmap *ret = NULL;
331 GP_LoaderReadImageEx(self, io, &ret, NULL, callback);
333 return ret;
336 int GP_LoaderReadImageEx(const GP_Loader *self, GP_IO *io,
337 GP_Pixmap **img, GP_DataStorage *data,
338 GP_ProgressCallback *callback)
340 GP_DEBUG(1, "Reading image (I/O %p)", io);
342 if (!self->Read) {
343 errno = ENOSYS;
344 return ENOSYS;
347 return self->Read(io, img, data, callback);
350 GP_Pixmap *GP_LoadImage(const char *src_path, GP_ProgressCallback *callback)
352 GP_Pixmap *ret = NULL;
354 GP_LoadImageEx(src_path, &ret, NULL, callback);
356 return ret;
359 int GP_LoadImageEx(const char *src_path,
360 GP_Pixmap **img, GP_DataStorage *meta_data,
361 GP_ProgressCallback *callback)
363 int err;
364 struct stat st;
366 if (access(src_path, R_OK)) {
367 err = errno;
368 GP_DEBUG(1, "Failed to access file '%s' : %s",
369 src_path, strerror(errno));
370 errno = err;
371 return 1;
374 if (stat(src_path, &st)) {
375 GP_WARN("Failed to stat '%s': %s", src_path, strerror(errno));
376 } else {
377 if (st.st_mode & S_IFDIR) {
378 errno = EISDIR;
379 return 1;
383 const GP_Loader *ext_load, *sig_load;
385 ext_load = GP_LoaderByFilename(src_path);
387 if (ext_load) {
388 if (!GP_LoaderLoadImageEx(ext_load, src_path,
389 img, meta_data, callback))
390 return 0;
394 * Operation was aborted, just here exit.
396 if (errno == ECANCELED)
397 return 1;
399 sig_load = loader_by_signature(src_path);
402 * Avoid further work if extension matches the signature but image
403 * couldn't be loaded. Probably unimplemented format or damaged file.
405 if (ext_load == sig_load) {
406 GP_WARN("Signature matches extension but file '%s' "
407 "can't be loaded. Unsupported/damaged file?",
408 src_path);
409 return 1;
412 if (ext_load && sig_load) {
413 GP_WARN("File '%s': Extension says %s but signature %s",
414 src_path, ext_load->fmt_name, sig_load->fmt_name);
417 if (sig_load) {
418 if (!GP_LoaderLoadImageEx(sig_load, src_path,
419 img, meta_data, callback))
420 return 0;
423 errno = ENOSYS;
424 return 1;
427 int GP_LoadMetaData(const char *src_path, GP_DataStorage *storage)
429 const GP_Loader *loader;
430 struct stat st;
431 int err;
433 // TODO unify this with GP_LoadImage()
434 if (access(src_path, R_OK)) {
435 err = errno;
436 GP_DEBUG(1, "Failed to access file '%s' : %s",
437 src_path, strerror(errno));
438 errno = err;
439 return 1;
442 if (stat(src_path, &st)) {
443 GP_WARN("Failed to stat '%s': %s", src_path, strerror(errno));
444 } else {
445 if (st.st_mode & S_IFDIR) {
446 errno = EISDIR;
447 return 1;
451 loader = GP_LoaderByFilename(src_path);
453 if (loader == NULL)
454 goto out;
456 return GP_LoaderLoadImageEx(loader, src_path, NULL, storage, NULL);
458 out:
459 errno = ENOSYS;
460 return 1;
463 int GP_LoaderSaveImage(const GP_Loader *self, const GP_Pixmap *src,
464 const char *dst_path, GP_ProgressCallback *callback)
466 GP_IO *io;
468 GP_DEBUG(1, "Saving image '%s' format %s", dst_path, self->fmt_name);
470 if (!self->Write) {
471 errno = ENOSYS;
472 return 1;
475 io = GP_IOFile(dst_path, GP_IO_WRONLY);
477 if (!io)
478 return 1;
480 if (self->Write(src, io, callback)) {
481 GP_IOClose(io);
482 unlink(dst_path);
483 return 1;
486 if (GP_IOClose(io)) {
487 unlink(dst_path);
488 return 1;
491 return 0;
494 int GP_SaveImage(const GP_Pixmap *src, const char *dst_path,
495 GP_ProgressCallback *callback)
497 const GP_Loader *l = GP_LoaderByFilename(dst_path);
499 if (l == NULL) {
500 errno = EINVAL;
501 return 1;
504 return GP_LoaderSaveImage(l, src, dst_path, callback);
507 const GP_Loader *GP_LoaderBySignature(const void *buf)
509 unsigned int i;
511 for (i = 0; loaders[i]; i++) {
512 if (loaders[i]->Match && loaders[i]->Match(buf) == 1) {
513 GP_DEBUG(1, "Found loader '%s'", loaders[i]->fmt_name);
514 return loaders[i];
518 GP_DEBUG(1, "Loader not found");
520 return NULL;