filters: Add generated Nearest Neighbour filter.
[gfxprim.git] / libs / loaders / GP_GIF.c
blobaa999db86c57137ff4973e01f86bbdc1e168714f
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-2012 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
25 GIF image support using giflib.
29 #include <stdint.h>
30 #include <inttypes.h>
32 #include <errno.h>
33 #include <string.h>
35 #include "../../config.h"
36 #include "core/GP_Pixel.h"
37 #include "core/GP_GetPutPixel.gen.h"
38 #include "gfx/GP_Fill.h"
39 #include "core/GP_Debug.h"
41 #include "GP_GIF.h"
43 #ifdef HAVE_GIFLIB
45 #include <gif_lib.h>
47 int GP_OpenGIF(const char *src_path, void **f)
49 GifFileType *gf;
51 gf = DGifOpenFileName(src_path);
53 if (gf == NULL) {
54 //TODO: error handling
55 errno = EIO;
56 return 1;
59 GP_DEBUG(1, "Have GIF image %ix%i, %i colors, %i bpp",
60 gf->SWidth, gf->SHeight, gf->SColorResolution,
61 gf->SColorMap ? gf->SColorMap->BitsPerPixel : -1);
63 *f = gf;
65 return 0;
68 static const char *rec_type_name(GifRecordType rec_type)
70 switch (rec_type) {
71 case UNDEFINED_RECORD_TYPE:
72 return "Undefined";
73 case SCREEN_DESC_RECORD_TYPE:
74 return "ScreenDesc";
75 case IMAGE_DESC_RECORD_TYPE:
76 return "ImageDesc";
77 case EXTENSION_RECORD_TYPE:
78 return "Extension";
79 case TERMINATE_RECORD_TYPE:
80 return "Terminate";
81 default:
82 return "Invalid";
86 static const char *gif_err_name(int err)
88 switch (err) {
89 case E_GIF_ERR_OPEN_FAILED:
90 return "E_GIF_ERR_OPEN_FAILED";
91 case E_GIF_ERR_WRITE_FAILED:
92 return "E_GIF_ERR_WRITE_FAILED";
93 case E_GIF_ERR_HAS_SCRN_DSCR:
94 return "E_GIF_ERR_HAS_SCRN_DSCR";
95 case E_GIF_ERR_HAS_IMAG_DSCR:
96 return "E_GIF_ERR_HAS_IMAG_DSCR";
97 case E_GIF_ERR_NO_COLOR_MAP:
98 return "E_GIF_ERR_NO_COLOR_MAP";
99 case E_GIF_ERR_DATA_TOO_BIG:
100 return "E_GIF_ERR_DATA_TOO_BIG";
101 case E_GIF_ERR_NOT_ENOUGH_MEM:
102 return "E_GIF_ERR_NOT_ENOUGH_MEM";
103 case E_GIF_ERR_DISK_IS_FULL:
104 return "E_GIF_ERR_DISK_IS_FULL";
105 case E_GIF_ERR_CLOSE_FAILED:
106 return "E_GIF_ERR_CLOSE_FAILED";
107 case E_GIF_ERR_NOT_WRITEABLE:
108 return "E_GIF_ERR_NOT_WRITEABLE";
109 default:
110 return "UNKNOWN";
114 static int read_extensions(GifFileType *gf)
116 uint8_t *gif_ext_ptr;
117 int gif_ext_type;
119 //TODO: Should we free them?
121 if (DGifGetExtension(gf, &gif_ext_type, &gif_ext_ptr) != GIF_OK) {
122 GP_DEBUG(1, "DGifGetExtension() error %s (%i)",
123 gif_err_name(GifLastError()), GifLastError());
124 return EIO;
127 GP_DEBUG(2, "Have GIF extension type %i (ignoring)", gif_ext_type);
129 do {
130 if (DGifGetExtensionNext(gf, &gif_ext_ptr) != GIF_OK) {
131 GP_DEBUG(1, "DGifGetExtension() error %s (%i)",
132 gif_err_name(GifLastError()), GifLastError());
133 return EIO;
136 } while (gif_ext_ptr != NULL);
138 return 0;
141 static inline GifColorType *get_color_from_map(ColorMapObject *map, int idx)
143 if (map->ColorCount <= idx) {
144 GP_DEBUG(1, "Invalid colormap index %i (%i max)",
145 map->ColorCount, idx);
146 return map->Colors;
149 return &map->Colors[idx];
152 static inline GP_Pixel get_color(GifFileType *gf, uint32_t idx)
154 GifColorType *color;
156 //TODO: no color map?
157 if (gf->SColorMap == NULL)
158 return 0;
160 color = get_color_from_map(gf->SColorMap, idx);
162 return GP_Pixel_CREATE_RGB888(color->Red, color->Green, color->Blue);
165 static int get_bg_color(GifFileType *gf, GP_Pixel *pixel)
167 GifColorType *color;
169 if (gf->SColorMap == NULL)
170 return 0;
172 color = get_color_from_map(gf->SColorMap, gf->SBackGroundColor);
174 *pixel = GP_Pixel_CREATE_RGB888(color->Red, color->Green, color->Blue);
176 return 1;
179 GP_Context *GP_ReadGIF(void *f, GP_ProgressCallback *callback)
181 GifFileType *gf = f;
182 GifRecordType rec_type;
183 GP_Context *res = NULL;
184 GP_Pixel bg;
185 int32_t x, y;
186 int err;
188 do {
189 if (DGifGetRecordType(gf, &rec_type) != GIF_OK) {
190 //TODO: error handling
191 GP_DEBUG(1, "DGifGetRecordType() error %s (%i)",
192 gif_err_name(GifLastError()), GifLastError());
193 err = EIO;
194 goto err1;
197 GP_DEBUG(2, "Have GIF record type %s",
198 rec_type_name(rec_type));
200 switch (rec_type) {
201 case EXTENSION_RECORD_TYPE:
202 if ((err = read_extensions(gf)))
203 goto err1;
204 continue;
205 case IMAGE_DESC_RECORD_TYPE:
206 break;
207 default:
208 continue;
211 if (DGifGetImageDesc(gf) != GIF_OK) {
212 //TODO: error handling
213 GP_DEBUG(1, "DGifGetImageDesc() error %s (%i)",
214 gif_err_name(GifLastError()), GifLastError());
215 err = EIO;
216 goto err1;
219 GP_DEBUG(1, "Have GIF Image left-top %ix%i, width-height %ix%i,"
220 " interlace %i, bpp %i", gf->Image.Left, gf->Image.Top,
221 gf->Image.Width, gf->Image.Height, gf->Image.Interlace,
222 gf->Image.ColorMap ? gf->Image.ColorMap->BitsPerPixel : -1);
224 res = GP_ContextAlloc(gf->SWidth, gf->SHeight, GP_PIXEL_RGB888);
226 if (res == NULL) {
227 err = ENOMEM;
228 goto err1;
231 /* If background color is defined, use it */
232 if (get_bg_color(gf, &bg)) {
233 GP_DEBUG(1, "Filling bg color %x", bg);
234 GP_Fill(res, bg);
237 /* Now finally read gif image data */
238 for (y = gf->Image.Top; y < gf->Image.Height; y++) {
239 uint8_t line[gf->Image.Width];
241 DGifGetLine(gf, line, gf->Image.Width);
243 //TODO: just now we have only 8BPP
244 for (x = 0; x < gf->Image.Width; x++)
245 GP_PutPixel_Raw_24BPP(res, x + gf->Image.Left, y, get_color(gf, line[x]));
247 if (GP_ProgressCallbackReport(callback, y - gf->Image.Top,
248 gf->Image.Height,
249 gf->Image.Width)) {
250 GP_DEBUG(1, "Operation aborted");
251 err = ECANCELED;
252 goto err2;
256 //TODO: now we exit after reading first image
257 break;
259 } while (rec_type != TERMINATE_RECORD_TYPE);
262 DGifCloseFile(gf);
264 /* No Image record found :( */
265 if (res == NULL)
266 err = EIO;
268 return res;
269 err2:
270 GP_ContextFree(res);
271 err1:
272 DGifCloseFile(gf);
273 errno = err;
274 return NULL;
277 GP_Context *GP_LoadGIF(const char *src_path, GP_ProgressCallback *callback)
279 void *f;
281 if (GP_OpenGIF(src_path, &f))
282 return NULL;
284 return GP_ReadGIF(f, callback);
287 #else
289 int GP_OpenGIF(const char GP_UNUSED(*src_path),
290 void GP_UNUSED(**f))
292 errno = ENOSYS;
293 return 1;
296 GP_Context *GP_ReadGIF(void GP_UNUSED(*f),
297 GP_ProgressCallback GP_UNUSED(*callback))
299 errno = ENOSYS;
300 return NULL;
303 GP_Context *GP_LoadGIF(const char GP_UNUSED(*src_path),
304 GP_ProgressCallback GP_UNUSED(*callback))
306 errno = ENOSYS;
307 return NULL;
310 #endif /* HAVE_GIFLIB */