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