b0ba14e9dbb044bb46865b3fb2891fc9f6e696e3
[privoxy.git] / deanimate.c
1 const char deanimate_rcs[] = "$Id: deanimate.c,v 1.2 2001/07/13 13:46:20 oes Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/deanimate.c,v $
5  *
6  * Purpose     :  Declares functions to deanimate GIF images on the fly.
7  *                
8  *                Functions declared include: gif_deanimate, buf_free,
9  *                buf_copy,  buf_getbyte, gif_skip_data_block, and
10  *                gif_extract_image
11  *
12  * Copyright   :  Written by and Copyright (C) 2001 Andreas S. Oesterhelt
13  *                for the SourceForge IJBSWA team. http://ijbswa.sourceforge.net
14  *
15  *                Based on the GIF file format specification (see
16  *                http://tronche.com/computer-graphics/gif/gif89a.html)
17  *                and ideas from the Image::DeAnim Perl module by
18  *                Ken MacFarlane, <ksm+cpan@universal.dca.net>
19  *
20  *                This program is free software; you can redistribute it 
21  *                and/or modify it under the terms of the GNU General
22  *                Public License as published by the Free Software
23  *                Foundation; either version 2 of the License, or (at
24  *                your option) any later version.
25  *
26  *                This program is distributed in the hope that it will
27  *                be useful, but WITHOUT ANY WARRANTY; without even the
28  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
29  *                PARTICULAR PURPOSE.  See the GNU General Public
30  *                License for more details.
31  *
32  *                The GNU General Public License should be included with
33  *                this file.  If not, you can view it at
34  *                http://www.gnu.org/copyleft/gpl.html
35  *                or write to the Free Software Foundation, Inc., 59
36  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
37  *
38  * Revisions   :
39  *    $Log: deanimate.c,v $
40  *    Revision 1.2  2001/07/13 13:46:20  oes
41  *    Introduced GIF deanimation feature
42  *
43  *
44  **********************************************************************/
45 \f
46
47 #include "config.h"
48
49 #include <string.h>
50 #include <fcntl.h>
51
52 #include "project.h"
53 #include "deanimate.h"
54 #include "miscutil.h"
55
56 const char deanimate_h_rcs[] = DEANIMATE_H_VERSION;
57
58 /*********************************************************************
59  * 
60  * Function    :  buf_free
61  *
62  * Description :  Safely frees a struct binbuffer
63  *
64  * Parameters  :
65  *          1  :  buf = Pointer to the binbuffer to be freed
66  *
67  * Returns     :  N/A
68  *
69  *********************************************************************/
70 void buf_free(struct binbuffer *buf)
71 {
72    if (buf == NULL) return;
73
74    if (buf->buffer != NULL)
75    {
76       free(buf->buffer);
77    }
78
79    free(buf);
80
81 }
82
83
84 /*********************************************************************
85  * 
86  * Function    :  buf_copy
87  *
88  * Description :  Safely copies a given amount of bytes from one
89  *                struct binbuffer to another, advancing the
90  *                offsets appropriately.
91  *
92  * Parameters  :
93  *          1  :  src = Pointer to the source binbuffer
94  *          2  :  dst = Pointer to the destination binbuffer
95  *          3  :  length = Number of bytes to be copied
96  *
97  * Returns     :  0 on success, 1 on failiure.
98  *
99  *********************************************************************/
100 int buf_copy(struct binbuffer *src, struct binbuffer *dst, int length)
101 {
102    char *p;
103
104    /*
105     * Sanity check: Can't copy more data than we have
106     */
107    if (src->offset + length > src->size) 
108    {
109       return 1;
110    }
111
112    /*
113     * If dst can't hold the new data, get mem first. (In chunks
114     * of 1000 bytes, so we don't have to realloc() too often)
115     */
116    if (dst->offset + length > dst->size)
117    {
118       dst->size = dst->size + length + 1000 - (dst->size + length) % 1000;
119       p = dst->buffer;
120       dst->buffer = (char *)realloc(dst->buffer, dst->size);
121
122       if (dst->buffer == NULL)
123       {
124          free(p);
125          return 1;
126       }
127    }
128
129    /*
130     * Now that it's safe, memcpy() the desired amount of
131     * data from src to dst and adjust the offsets
132     */
133    memcpy(dst->buffer + dst->offset, src->buffer + src->offset, length);
134    src->offset += length;
135    dst->offset += length;
136
137    return 0;
138
139 }
140
141
142 /*********************************************************************
143  * 
144  * Function    :  buf_getbyte
145  *
146  * Description :  Safely gets a byte from a given binbuffer at a
147  *                given offset
148  *
149  * Parameters  :
150  *          1  :  buf = Pointer to the source binbuffer
151  *          2  :  offset = Offset to the desired byte
152  *
153  * Returns     :  The byte on success, or 0 on failiure
154  *
155  *********************************************************************/
156 unsigned char buf_getbyte(struct binbuffer *src, int offset)
157 {
158    if (src->offset + offset < src->size)
159    {
160       return (unsigned char)*(src->buffer + src->offset + offset);
161    }
162    else
163    {
164       return '\0';
165    }
166
167 }
168
169
170 /*********************************************************************
171  * 
172  * Function    :  gif_skip_data_block
173  *
174  * Description :  Safely advances the offset of a given struct binbuffer
175  *                that contains a GIF image and whose offset is
176  *                positioned at the start of a data block behind
177  *                that block.
178  *
179  * Parameters  :
180  *          1  :  buf = Pointer to the binbuffer
181  *
182  * Returns     :  0 on success, or 1 on failiure
183  *
184  *********************************************************************/
185 int gif_skip_data_block(struct binbuffer *buf)
186 {
187    unsigned char c;
188
189    /* 
190     * Data blocks are sequences of chunks, which are headed
191     * by a one-byte length field, with the last chunk having
192     * zero length.
193     */
194    while(c = buf_getbyte(buf, 0))
195    {
196       if ((buf->offset += c + 1) >= buf->size - 1)
197       {
198          return 1;
199       }
200    }
201    buf->offset++;
202
203    return 0;
204
205 }
206
207
208 /*********************************************************************
209  * 
210  * Function    :  gif_extract_image
211  *
212  * Description :  Safely extracts an image data block from a given
213  *                struct binbuffer that contains a GIF image and whose
214  *                offset is positioned at the start of a data block 
215  *                into a given destination binbuffer.
216  *
217  * Parameters  :
218  *          1  :  src = Pointer to the source binbuffer
219  *          2  :  dst = Pointer to the destination binbuffer
220  *
221  * Returns     :  0 on success, or 1 on failiure
222  *
223  *********************************************************************/
224 int gif_extract_image(struct binbuffer *src, struct binbuffer *dst)
225 {
226    unsigned char c;
227    
228    /*
229     * Remember the colormap flag and copy the image head
230     */
231    c = buf_getbyte(src, 9);
232    if (buf_copy(src, dst, 10))
233    {
234       return 1;
235    }
236
237    /*
238     * If the image has a local colormap, copy it.
239     */
240    if (c & 0x80)
241    {
242       if (buf_copy(src, dst, 3 * (1 << ((c & 0x07) + 1))))
243       {
244          return 1;
245       }           
246    }
247    if (buf_copy(src, dst, 1)) return 1;
248
249    /*
250     * Copy the image chunk by chunk.
251     */
252    while(c = buf_getbyte(src, 0))
253    {
254       if (buf_copy(src, dst, c + 1)) return 1;
255    }
256    if (buf_copy(src, dst, 1)) return 1;
257
258    /*
259     * Trim and rewind the dst buffer
260     */
261    dst->buffer = (char *)realloc(dst->buffer, dst->offset);
262    dst->size = dst->offset;
263    dst->offset = 0;
264
265    return(0);
266
267 }
268
269 /*********************************************************************
270  * 
271  * Function    :  gif_deanimate
272  *
273  * Description :  Deanimate a given GIF image, i.e. given a GIF with
274  *                an (optional) image block and an arbitrary number
275  *                of image extension blocks, produce an output GIF with
276  *                only one image block that contains the last image
277  *                (extenstion) block of the original.
278  *                Also strip Comments, Application extenstions, etc.
279  *
280  * Parameters  :
281  *          1  :  src = Pointer to the source binbuffer
282  *          2  :  dst = Pointer to the destination binbuffer
283  *
284  * Returns     :  0 on success, or 1 on failiure
285  *
286  *********************************************************************/
287 int gif_deanimate(struct binbuffer *src, struct binbuffer *dst)
288 {
289    unsigned char c;
290    struct binbuffer *image;
291
292    if (NULL == src || NULL == dst)
293    {
294       return 1;
295    }
296
297    c = buf_getbyte(src, 10);
298
299    /*
300     * Check & copy GIF header 
301     */
302    if (strncmp(src->buffer, "GIF89a", 6)) 
303    {
304       fprintf(stderr, "This is not a GIF98a!\n");
305       return 1;
306    }
307    else
308    {
309       if (buf_copy(src, dst, 13))
310       {
311          return 1;
312       }
313    }
314
315    /*
316     * Look for global colormap and  copy if found.
317     */
318    if(c & 0x80)
319    {
320       if (buf_copy(src, dst, 3 * (1 << ((c & 0x07) + 1))))
321       {
322          return 1;
323       }
324    }
325
326    /*
327     * Reserve a buffer for the current image block
328     */
329    if (NULL == (image = (struct binbuffer *)zalloc(sizeof(*image))))
330    {
331       return 1;
332    }
333
334    /*
335     * Parse the GIF block by block and copy the relevant
336     * parts to dst
337     */
338    while(src->offset < src->size)
339    {
340       switch(buf_getbyte(src, 0))
341       {
342          /*
343           *  End-of-GIF Marker: Append current image and return
344           */
345       case 0x3b:
346          if (buf_copy(image, dst, image->size) || buf_copy(src, dst, 1))
347          {
348             goto failed;
349          }
350          buf_free(image);
351          return(0);
352
353          /* 
354           * Image block: Extract to current image buffer
355           */
356       case 0x2c:
357          image->offset = 0;
358          if (gif_extract_image(src, image))
359          {
360             goto failed;
361          }
362          continue;
363
364          /*
365           * Extension block: Look at next byte and decide
366           */
367       case 0x21:
368          switch (buf_getbyte(src, 1))
369          {
370             /*
371              * Image extension: Copy extension  header and image
372              *                  to the current image buffer
373              */
374          case 0xf9:
375             image->offset = 0;
376             if (buf_copy(src, image, 8) || buf_getbyte(src, 0) != 0x2c) goto failed;
377             if (gif_extract_image(src, image)) goto failed;
378             continue;
379
380             /*
381              * Application extension: Skip
382              */
383          case 0xff:
384             if ((src->offset += 14) >= src->size || gif_skip_data_block(src)) goto failed;
385             continue;
386
387             /*
388              * Comment extension: Skip
389              */
390          case 0xfe:
391             if ((src->offset += 2) >= src->size || gif_skip_data_block(src)) goto failed;
392             continue;
393
394             /*
395              * Plain text extension: Skip
396              */
397          case 0x01:
398             if ((src->offset += 15) >= src->size || gif_skip_data_block(src)) goto failed;
399             continue;
400
401             /*
402              * Ooops, what type of extension is that?
403              */
404          default:
405             goto failed;
406
407          }
408
409          /*
410           * Ooops, what type of block is that?
411           */
412       default:
413          goto failed;
414          
415       }
416    } /* -END- while src */
417
418    /*
419     * Either we got here by goto, or because the GIF is
420     * bogus and EOF was reached before an end-of-gif marker 
421     * was found.
422     */
423
424 failed:
425    buf_free(image);
426    return 1;
427
428 }
429
430
431 /*
432   Local Variables:
433   tab-width: 3
434   end:
435 */