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