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