Credit Andrew - fix vorbis audio which was scratchy and ensure aging plugin does...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / filegif.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2014 Adam Williams <broadcast at earthling dot net>
5  * Copyright (C) 2003-2016 Cinelerra CV contributors
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22 #ifdef HAVE_GIFLIB
23 #include "asset.h"
24 #include "bcsignals.h"
25 #include "file.h"
26 #include "filegif.h"
27 #include "gif_lib.h"
28 #include "mainerror.h"
29 #include "vframe.h"
30
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <string.h>
35 #include <ctype.h>
36
37 //from "getarg.h"
38 extern "C"
39 int GifQuantizeBuffer(unsigned int Width, unsigned int Height,
40                    int *ColorMapSize, GifByteType * RedInput,
41                    GifByteType * GreenInput, GifByteType * BlueInput,
42                    GifByteType * OutputBuffer,
43                    GifColorType * OutputColorMap);
44 #if GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 2 || GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE >= 9
45
46 #define ABS(x)    ((x) > 0 ? (x) : (-(x)))
47
48 #define COLOR_ARRAY_SIZE 32768
49 #define BITS_PER_PRIM_COLOR 5
50 #define MAX_PRIM_COLOR      0x1f
51
52 typedef struct QuantizedColorType {
53     GifByteType RGB[3];
54     GifByteType NewColorIndex;
55     long Count;
56     struct QuantizedColorType *Pnext;
57 } QuantizedColorType;
58
59 static int QCmpr(QuantizedColorType *a, QuantizedColorType *b, int i)
60 {
61         int i0 = i, i1 = i+1, i2 = i+2;
62         if( i1 >= 3 ) i1 -= 3;
63         if( i2 >= 3 ) i2 -= 3;
64         /* sort on all axes of the color space! */
65         int hash_a = (a->RGB[i0] << 16) | (a->RGB[i1] << 8) | (a->RGB[i2] << 0);
66         int hash_b = (b->RGB[i0] << 16) | (b->RGB[i1] << 8) | (b->RGB[i2] << 0);
67         return hash_a - hash_b;
68 }
69
70 static int QSplit(QuantizedColorType **q, int l, int r, int i)
71 {
72         int m;
73         QuantizedColorType *t;
74         for(;;) {
75                 while( QCmpr(q[r],q[l], i) >= 0 ) if( ++l == r ) return r;
76                 t = q[l];  q[l] = q[r];  q[r] = t;  m = l;  l = r;  r = m;
77                 while( QCmpr(q[l],q[r], i) >= 0 ) if( r == --l ) return r;
78                 t = q[l];  q[l] = q[r];  q[r] = t;  m = l;  l = r;  r = m;
79         }
80 }
81
82 static void QSort(QuantizedColorType **q, int ll, int rr, int i)
83 {
84         for(;;) {
85                 int l = ll+1;  if( l == rr ) return;
86                 int r = rr-1;  if( l == r ) return;
87                 int m = QSplit(q, l, r, i);
88                 QSort(q, ll, m, i);
89                 ll = m;
90         }
91 }
92
93 typedef struct NewColorMapType {
94     GifByteType RGBMin[3], RGBWidth[3];
95     unsigned int NumEntries; /* # of QuantizedColorType in linked list below */
96     unsigned long Count; /* Total number of pixels in all the entries */
97     QuantizedColorType *QuantizedColors;
98 } NewColorMapType;
99
100 static int SubdivColorMap(NewColorMapType * NewColorSubdiv,
101                           unsigned int ColorMapSize,
102                           unsigned int *NewColorMapSize);
103
104
105 /******************************************************************************
106  Quantize high resolution image into lower one. Input image consists of a
107  2D array for each of the RGB colors with size Width by Height. There is no
108  Color map for the input. Output is a quantized image with 2D array of
109  indexes into the output color map.
110    Note input image can be 24 bits at the most (8 for red/green/blue) and
111  the output has 256 colors at the most (256 entries in the color map.).
112  ColorMapSize specifies size of color map up to 256 and will be updated to
113  real size before returning.
114    Also non of the parameter are allocated by this routine.
115    This function returns GIF_OK if successful, GIF_ERROR otherwise.
116 ******************************************************************************/
117 int
118 GifQuantizeBuffer(unsigned int Width,
119                unsigned int Height,
120                int *ColorMapSize,
121                GifByteType * RedInput,
122                GifByteType * GreenInput,
123                GifByteType * BlueInput,
124                GifByteType * OutputBuffer,
125                GifColorType * OutputColorMap) {
126
127     unsigned int Index, NumOfEntries;
128     int i, j, MaxRGBError[3];
129     unsigned int NewColorMapSize;
130     long Red, Green, Blue;
131     NewColorMapType NewColorSubdiv[256];
132     QuantizedColorType *ColorArrayEntries, *QuantizedColor;
133
134     ColorArrayEntries = (QuantizedColorType *)malloc(
135                            sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE);
136     if (ColorArrayEntries == NULL) {
137         return GIF_ERROR;
138     }
139
140     for (i = 0; i < COLOR_ARRAY_SIZE; i++) {
141         ColorArrayEntries[i].RGB[0] = i >> (2 * BITS_PER_PRIM_COLOR);
142         ColorArrayEntries[i].RGB[1] = (i >> BITS_PER_PRIM_COLOR) &
143            MAX_PRIM_COLOR;
144         ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR;
145         ColorArrayEntries[i].Count = 0;
146     }
147
148     /* Sample the colors and their distribution: */
149     for (i = 0; i < (int)(Width * Height); i++) {
150         Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
151                   (2 * BITS_PER_PRIM_COLOR)) +
152                 ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
153                   BITS_PER_PRIM_COLOR) +
154                 (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
155         ColorArrayEntries[Index].Count++;
156     }
157
158     /* Put all the colors in the first entry of the color map, and call the
159      * recursive subdivision process.  */
160     for (i = 0; i < 256; i++) {
161         NewColorSubdiv[i].QuantizedColors = NULL;
162         NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0;
163         for (j = 0; j < 3; j++) {
164             NewColorSubdiv[i].RGBMin[j] = 0;
165             NewColorSubdiv[i].RGBWidth[j] = 255;
166         }
167     }
168
169     /* Find the non empty entries in the color table and chain them: */
170     for (i = 0; i < COLOR_ARRAY_SIZE; i++)
171         if (ColorArrayEntries[i].Count > 0)
172             break;
173     QuantizedColor = NewColorSubdiv[0].QuantizedColors = &ColorArrayEntries[i];
174     NumOfEntries = 1;
175     while (++i < COLOR_ARRAY_SIZE)
176         if (ColorArrayEntries[i].Count > 0) {
177             QuantizedColor->Pnext = &ColorArrayEntries[i];
178             QuantizedColor = &ColorArrayEntries[i];
179             NumOfEntries++;
180         }
181     QuantizedColor->Pnext = NULL;
182
183     NewColorSubdiv[0].NumEntries = NumOfEntries; /* Different sampled colors */
184     NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */
185     NewColorMapSize = 1;
186     if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) !=
187        GIF_OK) {
188         free((char *)ColorArrayEntries);
189         return GIF_ERROR;
190     }
191     if (NewColorMapSize < *ColorMapSize) {
192         /* And clear rest of color map: */
193         for (i = NewColorMapSize; i < *ColorMapSize; i++)
194             OutputColorMap[i].Red = OutputColorMap[i].Green =
195                 OutputColorMap[i].Blue = 0;
196     }
197
198     /* Average the colors in each entry to be the color to be used in the
199      * output color map, and plug it into the output color map itself. */
200     for (i = 0; i < NewColorMapSize; i++) {
201         if ((j = NewColorSubdiv[i].NumEntries) > 0) {
202             QuantizedColor = NewColorSubdiv[i].QuantizedColors;
203             Red = Green = Blue = 0;
204             while (QuantizedColor) {
205                 QuantizedColor->NewColorIndex = i;
206                 Red += QuantizedColor->RGB[0];
207                 Green += QuantizedColor->RGB[1];
208                 Blue += QuantizedColor->RGB[2];
209                 QuantizedColor = QuantizedColor->Pnext;
210             }
211             OutputColorMap[i].Red = (Red << (8 - BITS_PER_PRIM_COLOR)) / j;
212             OutputColorMap[i].Green = (Green << (8 - BITS_PER_PRIM_COLOR)) / j;
213             OutputColorMap[i].Blue = (Blue << (8 - BITS_PER_PRIM_COLOR)) / j;
214         }
215     }
216
217     /* Finally scan the input buffer again and put the mapped index in the
218      * output buffer.  */
219     MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0;
220     for (i = 0; i < (int)(Width * Height); i++) {
221         Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
222                  (2 * BITS_PER_PRIM_COLOR)) +
223                 ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
224                  BITS_PER_PRIM_COLOR) +
225                 (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
226         Index = ColorArrayEntries[Index].NewColorIndex;
227         OutputBuffer[i] = Index;
228         if (MaxRGBError[0] < ABS(OutputColorMap[Index].Red - RedInput[i]))
229             MaxRGBError[0] = ABS(OutputColorMap[Index].Red - RedInput[i]);
230         if (MaxRGBError[1] < ABS(OutputColorMap[Index].Green - GreenInput[i]))
231             MaxRGBError[1] = ABS(OutputColorMap[Index].Green - GreenInput[i]);
232         if (MaxRGBError[2] < ABS(OutputColorMap[Index].Blue - BlueInput[i]))
233             MaxRGBError[2] = ABS(OutputColorMap[Index].Blue - BlueInput[i]);
234     }
235
236 #ifdef DEBUG
237     fprintf(stderr,
238             "Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n",
239             MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]);
240 #endif /* DEBUG */
241
242     free((char *)ColorArrayEntries);
243
244     *ColorMapSize = NewColorMapSize;
245
246     return GIF_OK;
247 }
248
249 /******************************************************************************
250  Routine to subdivide the RGB space recursively using median cut in each
251  axes alternatingly until ColorMapSize different cubes exists.
252  The biggest cube in one dimension is subdivide unless it has only one entry.
253  Returns GIF_ERROR if failed, otherwise GIF_OK.
254 *******************************************************************************/
255 static int
256 SubdivColorMap(NewColorMapType * NewColorSubdiv,
257                unsigned int ColorMapSize,
258                unsigned int *NewColorMapSize) {
259
260     int SortRGBAxis = 0;
261     unsigned int i, j, Index = 0;
262     QuantizedColorType *QuantizedColor, **SortArray;
263
264     while (ColorMapSize > *NewColorMapSize) {
265         /* Find candidate for subdivision: */
266         long Sum, Count;
267         int MaxSize = -1;
268         unsigned int NumEntries, MinColor, MaxColor;
269         for (i = 0; i < *NewColorMapSize; i++) {
270             for (j = 0; j < 3; j++) {
271                 if ((((int)NewColorSubdiv[i].RGBWidth[j]) > MaxSize) &&
272                       (NewColorSubdiv[i].NumEntries > 1)) {
273                     MaxSize = NewColorSubdiv[i].RGBWidth[j];
274                     Index = i;
275                     SortRGBAxis = j;
276                 }
277             }
278         }
279
280         if (MaxSize == -1)
281             return GIF_OK;
282
283         /* Split the entry Index into two along the axis SortRGBAxis: */
284
285         /* Sort all elements in that entry along the given axis and split at
286          * the median.  */
287         SortArray = (QuantizedColorType **)malloc(
288                       sizeof(QuantizedColorType *) *
289                       NewColorSubdiv[Index].NumEntries);
290         if (SortArray == NULL)
291             return GIF_ERROR;
292         for (j = 0, QuantizedColor = NewColorSubdiv[Index].QuantizedColors;
293              j < NewColorSubdiv[Index].NumEntries && QuantizedColor != NULL;
294              j++, QuantizedColor = QuantizedColor->Pnext)
295             SortArray[j] = QuantizedColor;
296
297         QSort(SortArray, -1, NewColorSubdiv[Index].NumEntries, SortRGBAxis);
298
299         /* Relink the sorted list into one: */
300         for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++)
301             SortArray[j]->Pnext = SortArray[j + 1];
302         SortArray[NewColorSubdiv[Index].NumEntries - 1]->Pnext = NULL;
303         NewColorSubdiv[Index].QuantizedColors = QuantizedColor = SortArray[0];
304         free((char *)SortArray);
305
306         /* Now simply add the Counts until we have half of the Count: */
307         Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count;
308         NumEntries = 1;
309         Count = QuantizedColor->Count;
310         while (QuantizedColor->Pnext != NULL &&
311                (Sum -= QuantizedColor->Pnext->Count) >= 0 &&
312                QuantizedColor->Pnext->Pnext != NULL) {
313             QuantizedColor = QuantizedColor->Pnext;
314             NumEntries++;
315             Count += QuantizedColor->Count;
316         }
317         /* Save the values of the last color of the first half, and first
318          * of the second half so we can update the Bounding Boxes later.
319          * Also as the colors are quantized and the BBoxes are full 0..255,
320          * they need to be rescaled.
321          */
322         MaxColor = QuantizedColor->RGB[SortRGBAxis]; /* Max. of first half */
323         /* coverity[var_deref_op] */
324         MinColor = QuantizedColor->Pnext->RGB[SortRGBAxis]; /* of second */
325         MaxColor <<= (8 - BITS_PER_PRIM_COLOR);
326         MinColor <<= (8 - BITS_PER_PRIM_COLOR);
327
328         /* Partition right here: */
329         NewColorSubdiv[*NewColorMapSize].QuantizedColors =
330            QuantizedColor->Pnext;
331         QuantizedColor->Pnext = NULL;
332         NewColorSubdiv[*NewColorMapSize].Count = Count;
333         NewColorSubdiv[Index].Count -= Count;
334         NewColorSubdiv[*NewColorMapSize].NumEntries =
335            NewColorSubdiv[Index].NumEntries - NumEntries;
336         NewColorSubdiv[Index].NumEntries = NumEntries;
337         for (j = 0; j < 3; j++) {
338             NewColorSubdiv[*NewColorMapSize].RGBMin[j] =
339                NewColorSubdiv[Index].RGBMin[j];
340             NewColorSubdiv[*NewColorMapSize].RGBWidth[j] =
341                NewColorSubdiv[Index].RGBWidth[j];
342         }
343         NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] =
344            NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] +
345            NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] - MinColor;
346         NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor;
347
348         NewColorSubdiv[Index].RGBWidth[SortRGBAxis] =
349            MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis];
350
351         (*NewColorMapSize)++;
352     }
353
354     return GIF_OK;
355 }
356
357 #endif
358
359
360 FileGIF::FileGIF(Asset *asset, File *file)
361  : FileBase(asset, file)
362 {
363         offset = 0;
364         err = 0;
365         gif_file = 0;
366         eof = 1;
367         row_size = 0;
368         rows = 0;
369         depth = 8;
370         fp = 0;
371         fd = -1;
372         writes = -1;
373         buffer = 0;
374         bg = 0;
375         output = 0;
376 }
377
378 FileGIF::~FileGIF()
379 {
380         close_file();
381 }
382
383 int FileGIF::check_sig(Asset *asset)
384 {
385         FILE *stream = fopen(asset->path, "rb");
386         if( stream ) {
387                 char test[8];
388                 int ret = fread(test, 1, 6, stream);
389                 fclose(stream);
390                 if( ret >= 6 &&
391                     test[0] == 'G' && test[1] == 'I' && test[2] == 'F' &&
392                     test[3] == '8' && (test[4] == '7' || test[4] == '9') &&
393                     test[5] == 'a' ) return 1;
394         }
395         return 0;
396 }
397
398 int FileGIF::colormodel_supported(int colormodel)
399 {
400         return BC_RGB888;
401 }
402
403 int FileGIF::get_best_colormodel(Asset *asset, int driver)
404 {
405         return BC_RGB888;
406 }
407
408
409 int FileGIF::read_frame_header(char *path)
410 {
411         FILE *stream = fopen(path, "rb");
412         if( stream ) {
413                 unsigned char test[16];
414                 int ret = fread(test, 16, 1, stream);
415                 fclose(stream);
416                 if( ret < 1 ) return 1;
417                 asset->format = FILE_GIF;
418                 asset->width = test[6] | (test[7] << 8);
419                 asset->height = test[8] | (test[9] << 8);
420                 return 0;
421         }
422
423         perror(path);
424         return 1;
425 }
426
427 static int input_file(GifFileType *gif_file, GifByteType *buffer, int bytes)
428 {
429         FileGIF *file = (FileGIF*)gif_file->UserData;
430         fseek(file->fp, file->offset, SEEK_SET);
431         bytes = fread(buffer, 1, bytes, file->fp);
432         file->offset += bytes;
433         return bytes;
434 }
435
436 int FileGIF::open_file(int rd, int wr)
437 {
438         return  rd ? ropen_path(asset->path) :
439                 wr ? wopen_path(asset->path) :
440                 0 ;
441 }
442
443 int FileGIF::ropen_path(const char *path)
444 {
445         fp = fopen(path, "r");
446         int result = !fp ? 1 : 0;
447         if( !result ) {
448                 offset = 0;  eof = 0;
449                 gif_file = DGifOpen(this, input_file, &err);
450                 if( !gif_file ) {
451                         eprintf("FileGIF::ropen_path %d: %s\n", __LINE__, GifErrorString(err));
452                         result = 1;
453                 }
454         }
455         if( !result )
456                 result = open_gif();
457         return result;
458 }
459
460 int FileGIF::wopen_path(const char *path)
461 {
462         fd = open(path, O_CREAT+O_TRUNC+O_WRONLY, 0777);
463         int result = fd < 0 ? 1 : 0;
464         if( !result ) {
465                 gif_file = EGifOpenFileHandle(fd, &err);
466                 if( !gif_file ) {
467                         eprintf("FileGIF::wopen_path %d: %s\n", __LINE__, GifErrorString(err));
468                         result = 1;
469                 }
470         }
471         if( !result ) {
472                 writes = 0;
473         }
474         return result;
475 }
476
477 int FileGIF::write_frames(VFrame ***frames, int len)
478 {
479         int result = !gif_file ? 1 : 0;
480         for( int i=0; i<len && !result; ++i )
481                 result = write_frame(frames[0][i]);
482         return result;
483 }
484
485 int FileGIF::open_gif()
486 {
487         file_pos.remove_all();
488         int width = asset->width;
489         int height = asset->height;
490         int result = read_frame_header(asset->path);
491         if( !result ) {
492                 asset->actual_width = asset->width;
493                 if( width ) asset->width = width;
494                 asset->actual_height = asset->height;
495                 if( height ) asset->height = height;
496                 asset->layers = 1;
497                 if( !asset->frame_rate )
498                         asset->frame_rate = 10;
499                 asset->video_data = 1;
500                 row_size = gif_file->SWidth * sizeof(GifPixelType);
501                 bg = (GifRowType)malloc(row_size);
502                 for( int i=0; i<gif_file->SWidth; ++i )
503                         bg[i] = gif_file->SBackGroundColor;
504                 rows = gif_file->SHeight;
505                 buffer = (GifRowType*)malloc(sizeof(GifRowType) * rows);
506                 for( int i=0; i<gif_file->SHeight; ++i ) {
507                         buffer[i] = (GifRowType)malloc(row_size);
508                         memcpy(buffer[i], bg, row_size);
509                 }
510                 result = scan_gif();
511                 asset->video_length = file_pos.size();
512         }
513         return result;
514 }
515
516 int FileGIF::close_file()
517 {
518         if( gif_file ) {
519                 EGifCloseFile(gif_file, &err);
520                 gif_file = 0;
521         }
522         if( fp ) {
523                 fclose(fp);  fp = 0;
524         }
525         if( fd >= 0 ) {
526                 close(fd);  fd = -1;
527         }
528         if( bg ) { free(bg);  bg = 0; }
529         if( buffer ) {
530                 for( int k=0; k<rows; ++k )
531                         free(buffer[k]);
532                 free(buffer);  buffer = 0;
533                 rows = 0;
534         }
535         offset = 0;
536         row_size = 0;
537         err = 0;
538         writes = -1;
539         eof = 1;
540         output = 0;
541         FileBase::close_file();
542         return 0;
543 }
544
545 int FileGIF::scan_gif()
546 {
547         int file_eof = eof;
548         int64_t file_offset = offset;
549         file_pos.remove_all();
550         int image_pos = offset, ret;
551 // read all imgs, build file_pos index
552         while( (ret=read_next_image(0)) > 0 ) {
553                 file_pos.append(image_pos);
554                 image_pos = offset;
555         }
556         eof = file_eof;
557         offset = file_offset;
558         return ret;
559 }
560
561 int FileGIF::set_video_position(int64_t pos)
562 {
563         if( !gif_file || !asset->video_length ) return 1;
564         int64_t sz = file_pos.size();
565         eof = pos < 0 || pos >= sz ? 1 : 0;
566         offset = !eof ? file_pos[pos] : 0;
567         return 0;
568 }
569
570 int FileGIF::read_frame(VFrame *output)
571 {
572         if( !gif_file ) return 1;
573         for( int i=0; i<gif_file->SHeight; ++i )
574                 memcpy(buffer[i], bg, row_size);
575         int ret = read_next_image(output) > 0 ? 0 : 1;
576         return ret;
577 }
578
579 // ret = -1:err, 0:eof, 1:frame
580 int FileGIF::read_next_image(VFrame *output)
581 {
582         int ret = 0;
583         GifRecordType record_type;
584
585         while( !ret && !eof ) {
586                 if( DGifGetRecordType(gif_file, &record_type) == GIF_ERROR ) {
587                         err = gif_file->Error;
588                         eprintf("FileGIF::read_frame %d: %s\n", __LINE__, GifErrorString(err));
589                         ret = -1;
590                         break;
591                 }
592
593                 switch( record_type ) {
594                 case IMAGE_DESC_RECORD_TYPE: {
595                         if( DGifGetImageDesc(gif_file) == GIF_ERROR ) {
596                                 err = gif_file->Error;
597                                 eprintf("FileGIF::read_frame %d: %s\n", __LINE__, GifErrorString(err));
598                                 break;
599                         }
600                         int row = gif_file->Image.Top;
601                         int col = gif_file->Image.Left;
602                         int width = gif_file->Image.Width;
603                         int height = gif_file->Image.Height;
604                         if( gif_file->Image.Left + gif_file->Image.Width > gif_file->SWidth ||
605                             gif_file->Image.Top + gif_file->Image.Height > gif_file->SHeight )
606                                 ret = -1;
607                         if( !ret && gif_file->Image.Interlace ) {
608                                 static int InterlacedOffset[] = { 0, 4, 2, 1 };
609                                 static int InterlacedJumps[] = { 8, 8, 4, 2 };
610 /* Need to perform 4 passes on the images: */
611                                 for( int i=0; i<4; ++i ) {
612                                         int j = row + InterlacedOffset[i];
613                                         for( ; !ret && j<row + height; j+=InterlacedJumps[i] ) {
614                                                 if( DGifGetLine(gif_file, &buffer[j][col], width) == GIF_ERROR )
615                                                         ret = -1;
616                                         }
617                                 }
618                         }
619                         else {
620                                 for( int i=0; !ret && i<height; ++i ) {
621                                         if (DGifGetLine(gif_file, &buffer[row++][col], width) == GIF_ERROR)
622                                                 ret = -1;
623                                 }
624                         }
625                         ret = 1;
626                         break; }
627                 case EXTENSION_RECORD_TYPE: {
628                         int ExtFunction = 0;
629                         GifByteType *ExtData = 0;
630                         if( DGifGetExtension(gif_file, &ExtFunction, &ExtData) == GIF_ERROR )
631                                 ret = -1;
632                         while( !ret && ExtData ) {
633                                 if( DGifGetExtensionNext(gif_file, &ExtData) == GIF_ERROR )
634                                         ret = -1;
635                         }
636                         break; }
637                 case TERMINATE_RECORD_TYPE:
638                         eof = 1;
639                         break;
640                 default:
641                         ret = -1;
642                         break;
643                 }
644         }
645
646         ColorMapObject *color_map = 0;
647         if( ret > 0 ) {
648                 color_map = gif_file->Image.ColorMap;
649                 if( !color_map ) color_map = gif_file->SColorMap;
650                 if( !color_map ) ret = -1;
651         }
652         if( ret > 0 && output ) {
653                 int screen_width = gif_file->SWidth;
654                 int screen_height = gif_file->SHeight;
655                 for( int i=0; i<screen_height; ++i ) {
656                         GifRowType row = buffer[i];
657                         unsigned char *out_ptr = output->get_rows()[i];
658                         for( int j=0; j<screen_width; ++j ) {
659                                 GifColorType *color_map_entry = &color_map->Colors[row[j]];
660                                 *out_ptr++ = color_map_entry->Red;
661                                 *out_ptr++ = color_map_entry->Green;
662                                 *out_ptr++ = color_map_entry->Blue;
663                         }
664                 }
665         }
666         return ret;
667 }
668
669 int FileGIF::write_frame(VFrame *frame)
670 {
671         int w = frame->get_w(), h = frame->get_h();
672         ColorMapObject *cmap = 0;
673         int cmap_sz = depth >= 0 ? 1 << depth : 0;
674         int64_t len = w * h * sizeof(GifByteType);
675         GifByteType *bfr = (GifByteType *) malloc(len);
676         int result = !bfr ? 1 : 0;
677         if( !result ) {
678                 VFrame gbrp(w, h, BC_GBRP);
679                 gbrp.transfer_from(frame);
680                 if( !(cmap = GifMakeMapObject(cmap_sz, 0)) )
681                         result = 1;
682                 if( !result ) {
683                         GifByteType *gp = (GifByteType *)gbrp.get_r();
684                         GifByteType *bp = (GifByteType *)gbrp.get_g();
685                         GifByteType *rp = (GifByteType *)gbrp.get_b();
686                         if( GifQuantizeBuffer(w, h, &cmap_sz, rp, gp, bp,
687                                         bfr, cmap->Colors) == GIF_ERROR )
688                                 result = 1;
689                 }
690         }
691         if( !result && !writes &&
692             EGifPutScreenDesc(gif_file, w, h, depth, 0, 0) == GIF_ERROR )
693                 result = 1;
694         if( !result &&
695             EGifPutImageDesc(gif_file, 0, 0, w, h, 0, cmap) == GIF_ERROR )
696                 result = 1;
697
698         GifByteType *bp = bfr;
699         for( int y=0; !result && y<h; ++y ) {
700                 if( EGifPutLine(gif_file, bp, w) == GIF_ERROR )
701                         result = 1;
702                 bp += w;
703         }
704         GifFreeMapObject(cmap);
705         if( bfr ) free(bfr);
706         ++writes;
707         return result;
708 }
709
710 static int write_data(GifFileType *gif_file, const GifByteType *bfr, int bytes)
711 {
712         FileGIF *file = (FileGIF*)gif_file->UserData;
713         VFrame *output = file->output;
714         long size = output->get_compressed_size();
715         long alloc = output->get_compressed_allocated();
716         long len = size + bytes;
717         if( len > alloc )
718                 output->allocate_compressed_data(2*size + bytes);
719         unsigned char *data = output->get_data() + size;
720         memcpy(data, bfr, bytes);
721         output->set_compressed_size(len);
722         return bytes;
723 }
724
725 int FileGIF::wopen_data(VFrame *output)
726 {
727         int result = 0;
728         gif_file = EGifOpen(this, write_data, &err);
729         if( !gif_file ) {
730                 eprintf("FileGIF::wopen_data %d: %s\n", __LINE__, GifErrorString(err));
731                 result = 1;
732         }
733         if( !result ) {
734                 output->set_compressed_size(0);
735                 this->output = output;
736                 writes = 0;
737         }
738         return result;
739 }
740
741
742 FileGIFList::FileGIFList(Asset *asset, File *file)
743  : FileList(asset, file, "GIFLIST", ".gif", FILE_UNKNOWN, FILE_GIF_LIST)
744 {
745 }
746
747 FileGIFList::~FileGIFList()
748 {
749 }
750
751 int FileGIFList::check_sig(Asset *asset)
752 {
753         FILE *stream = fopen(asset->path, "rb");
754         if( stream ) {
755                 unsigned char test[16];
756                 int ret = fread(test, 16, 1, stream);
757                 fclose(stream);
758                 if( ret < 1 ) return 1;
759                 if( test[0] == 'G' && test[1] == 'I' && test[2] == 'F' &&
760                     test[3] == 'L' && test[4] == 'I' && test[5] == 'S' && test[6] == 'T')
761                         return 1;
762         }
763         return 0;
764 }
765
766 int FileGIFList::colormodel_supported(int colormodel) { return BC_RGB888; }
767 int FileGIFList::get_best_colormodel(Asset *asset, int driver) { return BC_RGB888; }
768
769 int FileGIFList::read_frame_header(char *path)
770 {
771         FILE *stream = fopen(path, "rb");
772         if( stream ) {
773                 unsigned char test[16];
774                 int ret = fread(test, 16, 1, stream);
775                 fclose(stream);
776                 if( ret < 1 ) return 1;
777                 asset->format = FILE_GIF_LIST;
778                 asset->width = test[6] | (test[7] << 8);
779                 asset->height = test[8] | (test[9] << 8);
780                 return 0;
781         }
782         perror(path);
783         return 1;
784 }
785
786 int FileGIFList::read_frame(VFrame *output, char *path)
787 {
788         Asset *asset = new Asset(path);
789         FileGIF gif(asset, file);
790         int ret = gif.ropen_path(path);
791         if( !ret )
792                 ret = gif.read_frame(output);
793         asset->remove_user();
794         return ret;
795 }
796
797 int FileGIFList::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
798 {
799         int native_cmodel = BC_RGB888;
800         if( frame->get_color_model() != native_cmodel ) {
801                 GIFUnit *gif_unit = (GIFUnit *)unit;
802                 if( !gif_unit->temp_frame ) gif_unit->temp_frame =
803                         new VFrame(frame->get_w(), frame->get_h(), native_cmodel);
804                 gif_unit->temp_frame->transfer_from(frame);
805                 frame = gif_unit->temp_frame;
806         }
807
808         FileGIF gif(asset, file);
809         int ret = gif.wopen_data(data);
810         if( !ret )
811                 ret = gif.write_frame(frame);
812         return ret;
813 }
814
815 FrameWriterUnit* FileGIFList::new_writer_unit(FrameWriter *writer)
816 {
817         return new GIFUnit(this, writer);
818 }
819
820 int FileGIFList::verify_file_list()
821 {
822  // go through all .gif files in the list and
823  // verify their sizes match or not.
824  //printf("\nAsset Path: %s\n", asset->path);
825  FILE *stream = fopen(asset->path, "rb");
826  if (stream) {
827   char string[BCTEXTLEN];
828   int width, height, prev_width=-1, prev_height=-1;
829   // build the path prefix
830   char prefix[BCTEXTLEN], *bp = prefix, *cp = strrchr(asset->path, '/');
831   for( int i=0, n=!cp ? 0 : cp-asset->path; i<n; ++i ) *bp++ = asset->path[i];
832   *bp = 0;
833   // read entire input file
834   while( !feof(stream) && fgets(string, BCTEXTLEN, stream) ) {
835    int len = strlen(string);
836    if(!len || string[0] == '#' || string[0] == ' ' || isalnum(string[0])) continue;
837    if( string[len-1] == '\n' ) string[len-1] = 0;
838    // a possible .gif file path? fetch it
839    char path[BCTEXTLEN], *pp = path, *ep = pp + sizeof(path)-1;
840    if( string[0] == '.' && string[1] == '/' && prefix[0] )
841     pp += snprintf(pp, ep-pp, "%s/", prefix);
842    snprintf(pp, ep-pp, "%s", string);
843    // check if a valid file exists
844    if(!access(path, R_OK)) {
845     // check file header for size
846     FILE *gif_file_temp = fopen(path, "rb");
847     if (gif_file_temp) {
848      unsigned char test[16];
849      int ret = fread(test, 16, 1, gif_file_temp);
850      fclose(gif_file_temp);
851      if( ret < 1 ) continue;
852      // get height and width of gif file
853      width = test[6] | (test[7] << 8);
854      height = test[8] | (test[9] << 8);
855      // test with previous
856      if ( (prev_width == -1) && (prev_height == -1) ) {
857       prev_width = width;
858       prev_height = height;
859       continue;
860      }
861      else if ( (prev_width != width) || (prev_height != height) ) {
862       // this is the error case we are trying to avoid
863       fclose(stream);
864       return 0;
865      }
866     }
867
868    }
869   }
870   fclose(stream);
871   return 1;
872  }
873  // not sure if our function should be the one to raise not found error
874  perror(asset->path);
875  return 0;
876 }
877
878
879 GIFUnit::GIFUnit(FileGIFList *file, FrameWriter *writer)
880  : FrameWriterUnit(writer)
881 {
882         this->file = file;
883         temp_frame = 0;
884 }
885
886 GIFUnit::~GIFUnit()
887 {
888         delete temp_frame;
889 }
890
891 #endif