1 /*****************************************************************************
2 * This file is part of gfxprim library. *
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. *
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. *
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 *
19 * Copyright (C) 2009-2014 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
25 General functions for loading and saving bitmaps.
32 #include <sys/types.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
] = {
60 static unsigned int get_last_loader(void)
64 for (i
= 0; i
< MAX_LOADERS
; i
++) {
72 int GP_LoaderRegister(const GP_Loader
*self
)
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",
92 GP_DEBUG(1, "Loaders table is full");
102 void GP_LoaderUnregister(const GP_Loader
*self
)
104 unsigned int i
, last
= get_last_loader();
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
;
119 GP_WARN("Loader '%s' (%p) wasn't registered", self
->fmt_name
, self
);
122 void GP_ListLoaders(void)
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
));
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
]);
144 if (loaders
[i
+1] != NULL
)
149 static const GP_Loader
*loader_by_extension(const char *ext
)
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
);
166 static const char *get_ext(const char *path
)
168 size_t len
= strlen(path
);
171 for (i
= len
- 1; i
>= 0; i
--)
181 const GP_Loader
*GP_LoaderByFilename(const char *path
)
183 const char *ext
= get_ext(path
);
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
)
199 const GP_Loader
*ret
;
201 GP_DEBUG(1, "Trying to load by file signature");
203 f
= fopen(path
, "rb");
207 GP_DEBUG(1, "Failed to open file '%s'", path
);
211 if (fread(buf
, sizeof(buf
), 1, f
) < 1) {
212 GP_DEBUG(1, "Failed to read start of the file '%s'", path
);
219 ret
= GP_LoaderBySignature(buf
);
232 GP_Pixmap
*GP_ReadImage(GP_IO
*io
, GP_ProgressCallback
*callback
)
234 GP_Pixmap
*ret
= NULL
;
236 GP_ReadImageEx(io
, &ret
, NULL
, callback
);
241 int GP_ReadImageEx(GP_IO
*io
, GP_Pixmap
**img
, GP_DataStorage
*meta_data
,
242 GP_ProgressCallback
*callback
)
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",
255 if (GP_IOFill(io
, buf
, sizeof(buf
))) {
256 GP_DEBUG(1, "Failed to read first 32 bytes: %s",
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",
267 loader
= GP_LoaderBySignature(buf
);
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] : ' ');
279 GP_DEBUG(1, "Loader for '%s' does not support reading",
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
)
295 GP_DEBUG(1, "Loading Image '%s'", src_path
);
302 io
= GP_IOFile(src_path
, GP_IO_RDONLY
);
306 ret
= self
->Read(io
, img
, storage
, callback
);
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
);
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
);
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
);
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
);
359 int GP_LoadImageEx(const char *src_path
,
360 GP_Pixmap
**img
, GP_DataStorage
*meta_data
,
361 GP_ProgressCallback
*callback
)
366 if (access(src_path
, R_OK
)) {
368 GP_DEBUG(1, "Failed to access file '%s' : %s",
369 src_path
, strerror(errno
));
374 if (stat(src_path
, &st
)) {
375 GP_WARN("Failed to stat '%s': %s", src_path
, strerror(errno
));
377 if (st
.st_mode
& S_IFDIR
) {
383 const GP_Loader
*ext_load
, *sig_load
;
385 ext_load
= GP_LoaderByFilename(src_path
);
388 if (!GP_LoaderLoadImageEx(ext_load
, src_path
,
389 img
, meta_data
, callback
))
394 * Operation was aborted, just here exit.
396 if (errno
== ECANCELED
)
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?",
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
);
418 if (!GP_LoaderLoadImageEx(sig_load
, src_path
,
419 img
, meta_data
, callback
))
427 int GP_LoadMetaData(const char *src_path
, GP_DataStorage
*storage
)
429 const GP_Loader
*loader
;
433 // TODO unify this with GP_LoadImage()
434 if (access(src_path
, R_OK
)) {
436 GP_DEBUG(1, "Failed to access file '%s' : %s",
437 src_path
, strerror(errno
));
442 if (stat(src_path
, &st
)) {
443 GP_WARN("Failed to stat '%s': %s", src_path
, strerror(errno
));
445 if (st
.st_mode
& S_IFDIR
) {
451 loader
= GP_LoaderByFilename(src_path
);
456 return GP_LoaderLoadImageEx(loader
, src_path
, NULL
, storage
, NULL
);
463 int GP_LoaderSaveImage(const GP_Loader
*self
, const GP_Pixmap
*src
,
464 const char *dst_path
, GP_ProgressCallback
*callback
)
468 GP_DEBUG(1, "Saving image '%s' format %s", dst_path
, self
->fmt_name
);
475 io
= GP_IOFile(dst_path
, GP_IO_WRONLY
);
480 if (self
->Write(src
, io
, callback
)) {
486 if (GP_IOClose(io
)) {
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
);
504 return GP_LoaderSaveImage(l
, src
, dst_path
, callback
);
507 const GP_Loader
*GP_LoaderBySignature(const void *buf
)
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
);
518 GP_DEBUG(1, "Loader not found");