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