]> mj.ucw.cz Git - libucw.git/blob - images/io-libungif.c
a3bfc397eea43daf1c859e6dcbb676e765b300cc
[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 static int
21 libungif_read_func(GifFileType *gif, GifByteType *ptr, int len)
22 {
23   DBG("libungif_read_func(len=%d)", len);
24   return bread((struct fastbuf *)gif->UserData, (byte *)ptr, len);
25 }
26
27 static void
28 libungif_read_cancel(struct image_io *io)
29 {
30   DBG("libungif_read_cancel()");
31
32   DGifCloseFile(io->read_data);
33 }
34
35 int
36 libungif_read_header(struct image_io *io)
37 {
38   DBG("libungif_read_header()");
39
40   /* Create libungif structure */
41   GifFileType *gif;
42   if (unlikely(!(gif = io->read_data = DGifOpen(io->fastbuf, libungif_read_func))))
43     {
44       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot create libungif structure.");
45       return 0;
46     }
47
48   DBG("executing DGifSlurp()");
49   if (unlikely(DGifSlurp(gif) != GIF_OK))
50     {
51       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Gif read failed.");
52       DGifCloseFile(gif);
53       return 0;
54     }
55
56   DBG("ImageCount=%d ColorResolution=%d SBackGroundColor=%d SColorMap=%p", gif->ImageCount, gif->SColorResolution, gif->SBackGroundColor, gif->SColorMap);
57   if (unlikely(!gif->ImageCount))
58     {
59       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "There are no images in gif file.");
60       DGifCloseFile(gif);
61       return 0;
62     }
63
64   /* Read image parameters */
65   SavedImage *image = gif->SavedImages;
66   if (unlikely(image->ImageDesc.Width <= 0 || image->ImageDesc.Height <= 0 ||
67       image->ImageDesc.Width > (int)IMAGE_MAX_SIZE || image->ImageDesc.Height > (int)IMAGE_MAX_SIZE))
68     {
69       image_thread_err(io->thread, IMAGE_ERR_INVALID_DIMENSIONS, "Invalid gif dimensions.");
70       DGifCloseFile(gif);
71       return 0;
72     }
73   ColorMapObject *color_map = image->ImageDesc.ColorMap ? : gif->SColorMap;
74   if (unlikely(!color_map))
75     {
76       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Missing palette.");
77       DGifCloseFile(gif);
78       return 0;
79     }
80   io->cols = image->ImageDesc.Width;
81   io->rows = image->ImageDesc.Height;
82   if (unlikely((io->number_of_colors = color_map->ColorCount) > 256))
83     {
84       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Too many gif colors.");
85       DGifCloseFile(gif);
86       return 0;
87     }
88   io->flags = COLOR_SPACE_RGB | IMAGE_IO_HAS_PALETTE;
89   /* FIXME transparent GIFs disabled */
90 #if 0
91   if (gif->SColorMap && !image->ImageDesc.ColorMap && (uns)gif->SBackGroundColor < (uns)color_map->ColorCount)
92     {
93       io->flags |= IMAGE_ALPHA | IMAGE_IO_HAS_BACKGROUND;
94       GifColorType *background = color_map->Colors + gif->SBackGroundColor;
95       color_make_rgb(&io->background_color, background->Red, background->Green, background->Blue);
96     }
97 #endif
98
99   /* Success */
100   io->read_cancel = libungif_read_cancel;
101   return 1;
102 }
103
104 int
105 libungif_read_data(struct image_io *io)
106 {
107   DBG("libungif_read_data()");
108
109   GifFileType *gif = io->read_data;
110   SavedImage *image = gif->SavedImages;
111
112   /* Prepare image */
113   struct image_io_read_data_internals rdi;
114   if (unlikely(!image_io_read_data_prepare(&rdi, io, image->ImageDesc.Width, image->ImageDesc.Height, io->flags)))
115     {
116       DGifCloseFile(gif);
117       return 0;
118     }
119
120   /* Get pixels and palette */
121   byte *pixels = (byte *)image->RasterBits;
122   ColorMapObject *color_map = image->ImageDesc.ColorMap ? : gif->SColorMap;
123   GifColorType *palette = color_map->Colors;
124   uns background = (
125   /* FIXME: transparent GIFs disabled */
126 #if 1
127     0 &&
128 #endif
129     gif->SColorMap && !image->ImageDesc.ColorMap) ? gif->SBackGroundColor : 256;
130   byte *img_end = rdi.image->pixels + rdi.image->image_size;
131
132   /* Handle deinterlacing */
133   uns dein_step, dein_next;
134   if (image->ImageDesc.Interlace)
135     dein_step = dein_next = rdi.image->row_size << 3;
136   else
137     dein_step = dein_next = rdi.image->row_size;
138
139   /* Convert pixels */
140   switch (rdi.image->pixel_size)
141     {
142       case 1:
143         {
144           byte pal[256], *pal_pos = pal, *pal_end = pal + 256;
145           for (uns i = 0; i < (uns)color_map->ColorCount; i++, pal_pos++, palette++)
146             *pal_pos = rgb_to_gray_func(palette->Red, palette->Green, palette->Blue);
147           if (pal_pos != pal_end)
148             bzero(pal_pos, pal_end - pal_pos);
149           if (io->flags & IMAGE_IO_USE_BACKGROUND)
150             color_put_grayscale(pal + background, &io->background_color);
151 #         define DO_ROW_END do{ \
152               walk_row_start += dein_step; \
153               if (walk_row_start > img_end) \
154                 { uns n = dein_next >> 1; walk_row_start = rdi.image->pixels + n, dein_step = dein_next; dein_next = n; } \
155             }while(0)
156 #         define IMAGE_WALK_PREFIX(x) walk_##x
157 #         define IMAGE_WALK_INLINE
158 #         define IMAGE_WALK_IMAGE (rdi.image)
159 #         define IMAGE_WALK_UNROLL 4
160 #         define IMAGE_WALK_COL_STEP 1
161 #         define IMAGE_WALK_ROW_STEP 0
162 #         define IMAGE_WALK_DO_STEP do{ *walk_pos = pal[*pixels++]; }while(0)
163 #         define IMAGE_WALK_DO_ROW_END DO_ROW_END
164 #         include "images/image-walk.h"
165           break;
166         }
167       case 2:
168         {
169           byte pal[256 * 2], *pal_pos = pal, *pal_end = pal + 256 * 2;
170           for (uns i = 0; i < (uns)color_map->ColorCount; i++, pal_pos += 2, palette++)
171             {
172               pal_pos[0] = rgb_to_gray_func(palette->Red, palette->Green, palette->Blue);
173               pal_pos[1] = 255;
174             }
175           if (pal_pos != pal_end)
176             bzero(pal_pos, pal_end - pal_pos);
177           if (background < 256)
178             pal[background * 2 + 1] = 0;
179 #         define IMAGE_WALK_PREFIX(x) walk_##x
180 #         define IMAGE_WALK_INLINE
181 #         define IMAGE_WALK_IMAGE (rdi.image)
182 #         define IMAGE_WALK_UNROLL 4
183 #         define IMAGE_WALK_COL_STEP 2
184 #         define IMAGE_WALK_ROW_STEP 0
185 #         define IMAGE_WALK_DO_STEP do{ *(u16 *)walk_pos = ((u16 *)pal)[*pixels++]; }while(0)
186 #         define IMAGE_WALK_DO_ROW_END DO_ROW_END
187 #         include "images/image-walk.h"
188           break;
189         }
190       case 3:
191         {
192           byte pal[256 * 4], *pal_pos = pal, *pal_end = pal + 256 * 4;
193           for (uns i = 0; i < (uns)color_map->ColorCount; i++, pal_pos += 4, palette++)
194             {
195               pal_pos[0] = palette->Red;
196               pal_pos[1] = palette->Green;
197               pal_pos[2] = palette->Blue;
198             }
199           if (pal_pos != pal_end)
200             bzero(pal_pos, pal_end - pal_pos);
201           if (io->flags & IMAGE_IO_USE_BACKGROUND)
202             color_put_rgb(pal + background, &io->background_color);
203 #         define IMAGE_WALK_PREFIX(x) walk_##x
204 #         define IMAGE_WALK_INLINE
205 #         define IMAGE_WALK_IMAGE (rdi.image)
206 #         define IMAGE_WALK_UNROLL 4
207 #         define IMAGE_WALK_COL_STEP 3
208 #         define IMAGE_WALK_ROW_STEP 0
209 #         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)
210 #         define IMAGE_WALK_DO_ROW_END DO_ROW_END
211 #         include "images/image-walk.h"
212           break;
213         }
214       case 4:
215         {
216           byte pal[256 * 4], *pal_pos = pal, *pal_end = pal + 256 * 4;
217           for (uns i = 0; i < (uns)color_map->ColorCount; i++, pal_pos += 4, palette++)
218             {
219               pal_pos[0] = palette->Red;
220               pal_pos[1] = palette->Green;
221               pal_pos[2] = palette->Blue;
222               pal_pos[3] = 255;
223             }
224           if (pal_pos != pal_end)
225             bzero(pal_pos, pal_end - pal_pos);
226           if (background < 256)
227             pal[background * 4 + 3] = 0;
228 #         define IMAGE_WALK_PREFIX(x) walk_##x
229 #         define IMAGE_WALK_INLINE
230 #         define IMAGE_WALK_IMAGE (rdi.image)
231 #         define IMAGE_WALK_UNROLL 4
232 #         define IMAGE_WALK_COL_STEP 4
233 #         define IMAGE_WALK_ROW_STEP 0
234 #         define IMAGE_WALK_DO_STEP do{ *(u32 *)walk_pos = ((u32 *)pal)[*pixels++]; }while(0)
235 #         define IMAGE_WALK_DO_ROW_END DO_ROW_END
236 #         include "images/image-walk.h"
237           break;
238         }
239       default:
240         ASSERT(0);
241     }
242
243   /* Destroy libungif structure */
244   DGifCloseFile(gif);
245
246   /* Finish image */
247   return image_io_read_data_finish(&rdi, io);
248 }