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