1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stddef.h>
4 #include <string.h>
5 #include <gif_lib.h>
6 
7 #include "readers.h"
8 
9 #if defined(GIFLIB_MAJOR) && (GIFLIB_MAJOR >= 5)
10 #define GIF5DATA(e)		e
PrintGifError(int err)11 static void PrintGifError(int err)
12 {
13     fprintf(stderr, "GIF library error: %s\n", GifErrorString(err));
14 }
15 #else
16 #define GIF5DATA(x)
17 #define PrintGifError(e)	PrintGifError()
18 #define DGifOpenFileHandle(x,e)	DGifOpenFileHandle(x)
19 #define DGifCloseFile(x,e)	DGifCloseFile(x)
20 #endif
21 
22 struct gif_state {
23     FILE         *infile;
24     GifFileType  *gif;
25     GifPixelType *row;
26     GifPixelType *il;
27     int w,h;
28 };
29 
30 static GifRecordType
gif_fileread(struct gif_state * h)31 gif_fileread(struct gif_state *h)
32 {
33     GifRecordType RecordType;
34     GifByteType *Extension;
35     int ExtCode, rc;
36     char *type;
37 
38     for (;;) {
39 	if (GIF_ERROR == DGifGetRecordType(h->gif,&RecordType)) {
40 	    if (debug)
41 		fprintf(stderr,"gif: DGifGetRecordType failed\n");
42 	    PrintGifError(h->gif->Error);
43 	    return -1;
44 	}
45 	switch (RecordType) {
46 	case IMAGE_DESC_RECORD_TYPE:
47 	    if (debug)
48 		fprintf(stderr,"gif: IMAGE_DESC_RECORD_TYPE found\n");
49 	    return RecordType;
50 	case EXTENSION_RECORD_TYPE:
51 	    if (debug)
52 		fprintf(stderr,"gif: EXTENSION_RECORD_TYPE found\n");
53 	    for (rc = DGifGetExtension(h->gif,&ExtCode,&Extension);
54 		 NULL != Extension;
55 		 rc = DGifGetExtensionNext(h->gif,&Extension)) {
56 		if (rc == GIF_ERROR) {
57 		    if (debug)
58 			fprintf(stderr,"gif: DGifGetExtension failed\n");
59 		    PrintGifError(h->gif->Error);
60 		    return -1;
61 		}
62 		if (debug) {
63 		    switch (ExtCode) {
64 		    case COMMENT_EXT_FUNC_CODE:     type="comment";   break;
65 		    case GRAPHICS_EXT_FUNC_CODE:    type="graphics";  break;
66 		    case PLAINTEXT_EXT_FUNC_CODE:   type="plaintext"; break;
67 		    case APPLICATION_EXT_FUNC_CODE: type="appl";      break;
68 		    default:                        type="???";       break;
69 		    }
70 		    fprintf(stderr,"gif: extcode=0x%x [%s]\n",ExtCode,type);
71 		}
72 	    }
73 	    break;
74 	case TERMINATE_RECORD_TYPE:
75 	    if (debug)
76 		fprintf(stderr,"gif: TERMINATE_RECORD_TYPE found\n");
77 	    return RecordType;
78 	default:
79 	    if (debug)
80 		fprintf(stderr,"gif: unknown record type [%d]\n",RecordType);
81 	    return -1;
82 	}
83     }
84 }
85 
86 #if 0
87 static void
88 gif_skipimage(struct gif_state *h)
89 {
90     unsigned char *line;
91     int i;
92 
93     if (debug)
94 	fprintf(stderr,"gif: skipping image record ...\n");
95     DGifGetImageDesc(h->gif);
96     line = malloc(h->gif->SWidth);
97     for (i = 0; i < h->gif->SHeight; i++)
98 	DGifGetLine(h->gif, line, h->gif->SWidth);
99     free(line);
100 }
101 #endif
102 
103 static void*
gif_init(FILE * fp,char * filename,unsigned int page,struct ida_image_info * info,int thumbnail)104 gif_init(FILE *fp, char *filename, unsigned int page,
105 	 struct ida_image_info *info, int thumbnail)
106 {
107     struct gif_state *h;
108     GifRecordType RecordType;
109     int i, image = 0;
110     GIF5DATA(int giferror = 0;)
111 
112     h = malloc(sizeof(*h));
113     memset(h,0,sizeof(*h));
114 
115     h->infile = fp;
116     h->gif = DGifOpenFileHandle(fileno(fp), &giferror);
117     h->row = malloc(h->gif->SWidth * sizeof(GifPixelType));
118 
119     while (0 == image) {
120 	RecordType = gif_fileread(h);
121 	switch (RecordType) {
122 	case IMAGE_DESC_RECORD_TYPE:
123 	    if (GIF_ERROR == DGifGetImageDesc(h->gif)) {
124 		if (debug)
125 		    fprintf(stderr,"gif: DGifGetImageDesc failed\n");
126 		PrintGifError(giferror);
127 	    }
128 	    if (NULL == h->gif->SColorMap &&
129 		NULL == h->gif->Image.ColorMap) {
130 		if (debug)
131 		    fprintf(stderr,"gif: oops: no colormap found\n");
132 		goto oops;
133 	    }
134 #if 0
135 	    info->width  = h->w = h->gif->SWidth;
136 	    info->height = h->h = h->gif->SHeight;
137 #else
138 	    info->width  = h->w = h->gif->Image.Width;
139 	    info->height = h->h = h->gif->Image.Height;
140 #endif
141             info->npages = 1;
142 	    image = 1;
143 	    if (debug)
144 		fprintf(stderr,"gif: reading image record ...\n");
145 	    if (h->gif->Image.Interlace) {
146 		if (debug)
147 		    fprintf(stderr,"gif: interlaced\n");
148 		h->il = malloc(h->w * h->h * sizeof(GifPixelType));
149 		for (i = 0; i < h->h; i += 8)
150 		    DGifGetLine(h->gif, h->il + h->w*i,h->w);
151 		for (i = 4; i < h->gif->SHeight; i += 8)
152 		    DGifGetLine(h->gif, h->il + h->w*i,h->w);
153 		for (i = 2; i < h->gif->SHeight; i += 4)
154 		    DGifGetLine(h->gif, h->il + h->w*i,h->w);
155 	    }
156 	    break;
157 	case TERMINATE_RECORD_TYPE:
158 	default:
159 	    goto oops;
160 	}
161     }
162     if (0 == info->width || 0 == info->height)
163 	goto oops;
164 
165     if (debug)
166 	fprintf(stderr,"gif: s=%dx%d i=%dx%d\n",
167 		h->gif->SWidth,h->gif->SHeight,
168 		h->gif->Image.Width,h->gif->Image.Height);
169     return h;
170 
171  oops:
172     if (debug)
173 	fprintf(stderr,"gif: fatal error, aborting\n");
174     DGifCloseFile(h->gif, NULL);
175     fclose(h->infile);
176     free(h->row);
177     free(h);
178     return NULL;
179 }
180 
181 static void
gif_read(unsigned char * dst,unsigned int line,void * data)182 gif_read(unsigned char *dst, unsigned int line, void *data)
183 {
184     struct gif_state *h = data;
185     GifColorType *cmap;
186     int x;
187 
188     if (h->gif->Image.Interlace) {
189 	if (line % 2) {
190 	    DGifGetLine(h->gif, h->row, h->w);
191 	} else {
192 	    memcpy(h->row, h->il + h->w * line, h->w);
193 	}
194     } else {
195 	DGifGetLine(h->gif, h->row, h->w);
196     }
197     cmap = h->gif->Image.ColorMap ?
198 	h->gif->Image.ColorMap->Colors : h->gif->SColorMap->Colors;
199     for (x = 0; x < h->w; x++) {
200         dst[0] = cmap[h->row[x]].Red;
201 	dst[1] = cmap[h->row[x]].Green;
202 	dst[2] = cmap[h->row[x]].Blue;
203 	dst += 3;
204     }
205 }
206 
207 static void
gif_done(void * data)208 gif_done(void *data)
209 {
210     struct gif_state *h = data;
211 
212     if (debug)
213 	fprintf(stderr,"gif: done, cleaning up\n");
214     DGifCloseFile(h->gif, NULL);
215     fclose(h->infile);
216     if (h->il)
217 	free(h->il);
218     free(h->row);
219     free(h);
220 }
221 
222 static struct ida_loader gif_loader = {
223     magic: "GIF",
224     moff:  0,
225     mlen:  3,
226     name:  "giflib",
227     init:  gif_init,
228     read:  gif_read,
229     done:  gif_done,
230 };
231 
init_rd(void)232 static void __init init_rd(void)
233 {
234     load_register(&gif_loader);
235 }
236