]> mj.ucw.cz Git - libucw.git/blob - images/io-libungif.c
3faf6c51bd5f8637c6a211214284b73799d5a332
[libucw.git] / images / io-libungif.c
1 /*
2  *      Image Library -- libungif
3  *
4  *      (c) 2006 Pavel Charvat <pchar@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #undef LOCAL_DEBUG
11
12 #include "lib/lib.h"
13 #include "lib/mempool.h"
14 #include "lib/fastbuf.h"
15 #include "images/images.h"
16 #include "images/color.h"
17 #include "images/io-main.h"
18 #include <gif_lib.h>
19
20 struct libungif_read_data {
21   GifFileType *gif;
22   int transparent_index;
23 };
24
25 static int
26 libungif_read_func(GifFileType *gif, GifByteType *ptr, int len)
27 {
28   DBG("libungif_read_func(len=%d)", len);
29   uns readed, total = 0;
30   while (len && (readed = bread((struct fastbuf *)gif->UserData, (byte *)ptr, len)))
31     {
32       len -= readed;
33       ptr += readed;
34       total += readed;
35     }
36   return total;
37 }
38
39 static void
40 libungif_read_cancel(struct image_io *io)
41 {
42   DBG("libungif_read_cancel()");
43
44   DGifCloseFile(io->read_data);
45 }
46
47 int
48 libungif_read_header(struct image_io *io)
49 {
50   DBG("libungif_read_header()");
51
52   /* Create libungif structure */
53   GifFileType *gif;
54   if (unlikely(!(gif = DGifOpen(io->fastbuf, libungif_read_func))))
55     {
56       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot create libungif structure.");
57       return 0;
58     }
59
60   struct libungif_read_data *rd = io->read_data = mp_alloc(io->internal_pool, sizeof(*rd));
61   rd->gif = gif;
62
63   DBG("executing DGifSlurp()");
64   if (unlikely(DGifSlurp(gif) != GIF_OK))
65     {
66       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Gif read failed.");
67       DGifCloseFile(gif);
68       return 0;
69     }
70
71   DBG("ImageCount=%d ColorResolution=%d SBackGroundColor=%d SColorMap=%p", gif->ImageCount, gif->SColorResolution, gif->SBackGroundColor, gif->SColorMap);
72   if (unlikely(!gif->ImageCount))
73     {
74       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "There are no images in gif file.");
75       DGifCloseFile(gif);
76       return 0;
77     }
78
79   /* Read image parameters */
80   SavedImage *image = gif->SavedImages;
81   if (unlikely(image->ImageDesc.Width <= 0 || image->ImageDesc.Height <= 0 ||
82       image->ImageDesc.Width > (int)IMAGE_MAX_SIZE || image->ImageDesc.Height > (int)IMAGE_MAX_SIZE))
83     {
84       image_thread_err(io->thread, IMAGE_ERR_INVALID_DIMENSIONS, "Invalid gif dimensions.");
85       DGifCloseFile(gif);
86       return 0;
87     }
88   ColorMapObject *color_map = image->ImageDesc.ColorMap ? : gif->SColorMap;
89   if (unlikely(!color_map))
90     {
91       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Missing palette.");
92       DGifCloseFile(gif);
93       return 0;
94     }
95   io->cols = image->ImageDesc.Width;
96   io->rows = image->ImageDesc.Height;
97   if (unlikely((io->number_of_colors = color_map->ColorCount) > 256))
98     {
99       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Too many gif colors.");
100       DGifCloseFile(gif);
101       return 0;
102     }
103   io->flags = COLOR_SPACE_RGB | IMAGE_IO_HAS_PALETTE;
104
105   /* Search extension blocks */
106   rd->transparent_index = -1;
107   for (int i = 0; i < image->ExtensionBlockCount; i++)
108     {
109       ExtensionBlock *e = image->ExtensionBlocks + i;
110       if (e->Function == 0xF9)
111         {
112           DBG("Found graphics control extension");
113           if (unlikely(e->ByteCount != 4))
114             {
115               image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Invalid graphics control extension.");
116               DGifCloseFile(gif);
117               return 0;
118             }
119           byte *b = e->Bytes;
120           /* transparent color present */
121           if (b[0] & 1)
122             {
123               rd->transparent_index = b[3];
124               io->flags |= IMAGE_ALPHA;
125               if (gif->SColorMap)
126                 {
127                   GifColorType *background = color_map->Colors + gif->SBackGroundColor;
128                   color_make_rgb(&io->background_color, background->Red, background->Green, background->Blue);
129                 }
130             }
131           /* We've got everything we need :-) */
132           break;
133         }
134       else
135         DBG("Found unknown extension: type=%d size=%d", e->Function, e->ByteCount);
136     }
137
138   /* Success */
139   io->read_cancel = libungif_read_cancel;
140   return 1;
141 }
142
143 int
144 libungif_read_data(struct image_io *io)
145 {
146   DBG("libungif_read_data()");
147
148   struct libungif_read_data *rd = io->read_data;
149   GifFileType *gif = rd->gif;
150   SavedImage *image = gif->SavedImages;
151
152   /* Prepare image */
153   struct image_io_read_data_internals rdi;
154   if (unlikely(!image_io_read_data_prepare(&rdi, io, image->ImageDesc.Width, image->ImageDesc.Height, io->flags)))
155     {
156       DGifCloseFile(gif);
157       return 0;
158     }
159
160   /* Get pixels and palette */
161   byte *pixels = (byte *)image->RasterBits;
162   ColorMapObject *color_map = image->ImageDesc.ColorMap ? : gif->SColorMap;
163   GifColorType *palette = color_map->Colors;
164   byte *img_end = rdi.image->pixels + rdi.image->image_size;
165
166   /* Handle deinterlacing */
167   uns dein_step, dein_next;
168   if (image->ImageDesc.Interlace)
169     {
170       DBG("Deinterlaced image");
171       dein_step = dein_next = rdi.image->row_size << 3;
172     }
173   else
174     dein_step = dein_next = rdi.image->row_size;
175
176   /* Convert pixels */
177   switch (rdi.image->pixel_size)
178     {
179       case 1:
180         {
181           byte pal[256], *pal_pos = pal, *pal_end = pal + 256;
182           for (uns i = 0; i < (uns)color_map->ColorCount; i++, pal_pos++, palette++)
183             *pal_pos = rgb_to_gray_func(palette->Red, palette->Green, palette->Blue);
184           if (pal_pos != pal_end)
185             bzero(pal_pos, pal_end - pal_pos);
186           if (rd->transparent_index >= 0 && (io->flags & IMAGE_IO_USE_BACKGROUND))
187             color_put_grayscale(pal + rd->transparent_index, &io->background_color);
188 #         define DO_ROW_END do{ \
189               walk_row_start += dein_step; \
190               if (walk_row_start >= img_end) \
191                 { uns n = dein_next >> 1; walk_row_start = rdi.image->pixels + n, dein_step = dein_next; dein_next = n; } \
192             }while(0)
193 #         define IMAGE_WALK_PREFIX(x) walk_##x
194 #         define IMAGE_WALK_INLINE
195 #         define IMAGE_WALK_IMAGE (rdi.image)
196 #         define IMAGE_WALK_UNROLL 4
197 #         define IMAGE_WALK_COL_STEP 1
198 #         define IMAGE_WALK_ROW_STEP 0
199 #         define IMAGE_WALK_DO_STEP do{ *walk_pos = pal[*pixels++]; }while(0)
200 #         define IMAGE_WALK_DO_ROW_END DO_ROW_END
201 #         include "images/image-walk.h"
202           break;
203         }
204       case 2:
205         {
206           byte pal[256 * 2], *pal_pos = pal, *pal_end = pal + 256 * 2;
207           for (uns i = 0; i < (uns)color_map->ColorCount; i++, pal_pos += 2, palette++)
208             {
209               pal_pos[0] = rgb_to_gray_func(palette->Red, palette->Green, palette->Blue);
210               pal_pos[1] = 255;
211             }
212           if (pal_pos != pal_end)
213             bzero(pal_pos, pal_end - pal_pos);
214           if (rd->transparent_index >= 0)
215             pal[rd->transparent_index * 2 + 1] = 0;
216 #         define IMAGE_WALK_PREFIX(x) walk_##x
217 #         define IMAGE_WALK_INLINE
218 #         define IMAGE_WALK_IMAGE (rdi.image)
219 #         define IMAGE_WALK_UNROLL 4
220 #         define IMAGE_WALK_COL_STEP 2
221 #         define IMAGE_WALK_ROW_STEP 0
222 #         define IMAGE_WALK_DO_STEP do{ *(u16 *)walk_pos = ((u16 *)pal)[*pixels++]; }while(0)
223 #         define IMAGE_WALK_DO_ROW_END DO_ROW_END
224 #         include "images/image-walk.h"
225           break;
226         }
227       case 3:
228         {
229           byte pal[256 * 4], *pal_pos = pal, *pal_end = pal + 256 * 4;
230           for (uns i = 0; i < (uns)color_map->ColorCount; i++, pal_pos += 4, palette++)
231             {
232               pal_pos[0] = palette->Red;
233               pal_pos[1] = palette->Green;
234               pal_pos[2] = palette->Blue;
235             }
236           if (pal_pos != pal_end)
237             bzero(pal_pos, pal_end - pal_pos);
238           if (rd->transparent_index >= 0 && (io->flags & IMAGE_IO_USE_BACKGROUND))
239             color_put_rgb(pal + 4 * rd->transparent_index, &io->background_color);
240 #         define IMAGE_WALK_PREFIX(x) walk_##x
241 #         define IMAGE_WALK_INLINE
242 #         define IMAGE_WALK_IMAGE (rdi.image)
243 #         define IMAGE_WALK_UNROLL 4
244 #         define IMAGE_WALK_COL_STEP 3
245 #         define IMAGE_WALK_ROW_STEP 0
246 #         define IMAGE_WALK_DO_STEP do{ byte *p = pal + 4 * (*pixels++); walk_pos[0] = p[0]; walk_pos[1] = p[1]; walk_pos[2] = p[2]; }while(0)
247 #         define IMAGE_WALK_DO_ROW_END DO_ROW_END
248 #         include "images/image-walk.h"
249           break;
250         }
251       case 4:
252         {
253           byte pal[256 * 4], *pal_pos = pal, *pal_end = pal + 256 * 4;
254           for (uns i = 0; i < (uns)color_map->ColorCount; i++, pal_pos += 4, palette++)
255             {
256               pal_pos[0] = palette->Red;
257               pal_pos[1] = palette->Green;
258               pal_pos[2] = palette->Blue;
259               pal_pos[3] = 255;
260             }
261           if (pal_pos != pal_end)
262             bzero(pal_pos, pal_end - pal_pos);
263           if (rd->transparent_index >= 0)
264             pal[rd->transparent_index * 4 + 3] = 0;
265 #         define IMAGE_WALK_PREFIX(x) walk_##x
266 #         define IMAGE_WALK_INLINE
267 #         define IMAGE_WALK_IMAGE (rdi.image)
268 #         define IMAGE_WALK_UNROLL 4
269 #         define IMAGE_WALK_COL_STEP 4
270 #         define IMAGE_WALK_ROW_STEP 0
271 #         define IMAGE_WALK_DO_STEP do{ *(u32 *)walk_pos = ((u32 *)pal)[*pixels++]; }while(0)
272 #         define IMAGE_WALK_DO_ROW_END DO_ROW_END
273 #         include "images/image-walk.h"
274           break;
275         }
276       default:
277         ASSERT(0);
278     }
279
280   /* Destroy libungif structure */
281   DGifCloseFile(gif);
282
283   /* Finish image */
284   return image_io_read_data_finish(&rdi, io);
285 }