4 * Copyright (C) 2014 Adam Williams <broadcast at earthling dot net>
5 * Copyright (C) 2003-2016 Cinelerra CV contributors
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.
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.
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
24 #include "bcsignals.h"
28 #include "mainerror.h"
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
46 #define ABS(x) ((x) > 0 ? (x) : (-(x)))
48 #define COLOR_ARRAY_SIZE 32768
49 #define BITS_PER_PRIM_COLOR 5
50 #define MAX_PRIM_COLOR 0x1f
52 typedef struct QuantizedColorType {
54 GifByteType NewColorIndex;
56 struct QuantizedColorType *Pnext;
59 static int QCmpr(QuantizedColorType *a, QuantizedColorType *b, int i)
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;
70 static int QSplit(QuantizedColorType **q, int l, int r, int i)
73 QuantizedColorType *t;
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;
82 static void QSort(QuantizedColorType **q, int ll, int rr, int i)
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);
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;
100 static int SubdivColorMap(NewColorMapType * NewColorSubdiv,
101 unsigned int ColorMapSize,
102 unsigned int *NewColorMapSize);
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 ******************************************************************************/
118 GifQuantizeBuffer(unsigned int Width,
121 GifByteType * RedInput,
122 GifByteType * GreenInput,
123 GifByteType * BlueInput,
124 GifByteType * OutputBuffer,
125 GifColorType * OutputColorMap) {
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;
134 ColorArrayEntries = (QuantizedColorType *)malloc(
135 sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE);
136 if (ColorArrayEntries == NULL) {
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) &
144 ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR;
145 ColorArrayEntries[i].Count = 0;
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++;
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;
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)
173 QuantizedColor = NewColorSubdiv[0].QuantizedColors = &ColorArrayEntries[i];
175 while (++i < COLOR_ARRAY_SIZE)
176 if (ColorArrayEntries[i].Count > 0) {
177 QuantizedColor->Pnext = &ColorArrayEntries[i];
178 QuantizedColor = &ColorArrayEntries[i];
181 QuantizedColor->Pnext = NULL;
183 NewColorSubdiv[0].NumEntries = NumOfEntries; /* Different sampled colors */
184 NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */
186 if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) !=
188 free((char *)ColorArrayEntries);
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;
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;
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;
217 /* Finally scan the input buffer again and put the mapped index in the
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]);
238 "Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n",
239 MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]);
242 free((char *)ColorArrayEntries);
244 *ColorMapSize = NewColorMapSize;
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 *******************************************************************************/
256 SubdivColorMap(NewColorMapType * NewColorSubdiv,
257 unsigned int ColorMapSize,
258 unsigned int *NewColorMapSize) {
261 unsigned int i, j, Index = 0;
262 QuantizedColorType *QuantizedColor, **SortArray;
264 while (ColorMapSize > *NewColorMapSize) {
265 /* Find candidate for subdivision: */
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];
283 /* Split the entry Index into two along the axis SortRGBAxis: */
285 /* Sort all elements in that entry along the given axis and split at
287 SortArray = (QuantizedColorType **)malloc(
288 sizeof(QuantizedColorType *) *
289 NewColorSubdiv[Index].NumEntries);
290 if (SortArray == NULL)
292 for (j = 0, QuantizedColor = NewColorSubdiv[Index].QuantizedColors;
293 j < NewColorSubdiv[Index].NumEntries && QuantizedColor != NULL;
294 j++, QuantizedColor = QuantizedColor->Pnext)
295 SortArray[j] = QuantizedColor;
297 QSort(SortArray, -1, NewColorSubdiv[Index].NumEntries, SortRGBAxis);
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);
306 /* Now simply add the Counts until we have half of the Count: */
307 Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count;
309 Count = QuantizedColor->Count;
310 while (QuantizedColor->Pnext != NULL &&
311 (Sum -= QuantizedColor->Pnext->Count) >= 0 &&
312 QuantizedColor->Pnext->Pnext != NULL) {
313 QuantizedColor = QuantizedColor->Pnext;
315 Count += QuantizedColor->Count;
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.
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);
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];
343 NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] =
344 NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] +
345 NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] - MinColor;
346 NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor;
348 NewColorSubdiv[Index].RGBWidth[SortRGBAxis] =
349 MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis];
351 (*NewColorMapSize)++;
360 FileGIF::FileGIF(Asset *asset, File *file)
361 : FileBase(asset, file)
383 int FileGIF::check_sig(Asset *asset)
385 FILE *stream = fopen(asset->path, "rb");
388 int ret = fread(test, 1, 6, stream);
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;
398 int FileGIF::colormodel_supported(int colormodel)
403 int FileGIF::get_best_colormodel(Asset *asset, int driver)
409 int FileGIF::read_frame_header(char *path)
411 FILE *stream = fopen(path, "rb");
413 unsigned char test[16];
414 int ret = fread(test, 16, 1, 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);
427 static int input_file(GifFileType *gif_file, GifByteType *buffer, int bytes)
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;
436 int FileGIF::open_file(int rd, int wr)
438 return rd ? ropen_path(asset->path) :
439 wr ? wopen_path(asset->path) :
443 int FileGIF::ropen_path(const char *path)
445 fp = fopen(path, "r");
446 int result = !fp ? 1 : 0;
449 gif_file = DGifOpen(this, input_file, &err);
451 eprintf("FileGIF::ropen_path %d: %s\n", __LINE__, GifErrorString(err));
460 int FileGIF::wopen_path(const char *path)
462 fd = open(path, O_CREAT+O_TRUNC+O_WRONLY, 0777);
463 int result = fd < 0 ? 1 : 0;
465 gif_file = EGifOpenFileHandle(fd, &err);
467 eprintf("FileGIF::wopen_path %d: %s\n", __LINE__, GifErrorString(err));
477 int FileGIF::write_frames(VFrame ***frames, int len)
479 int result = !gif_file ? 1 : 0;
480 for( int i=0; i<len && !result; ++i )
481 result = write_frame(frames[0][i]);
485 int FileGIF::open_gif()
487 file_pos.remove_all();
488 int width = asset->width;
489 int height = asset->height;
490 int result = read_frame_header(asset->path);
492 asset->actual_width = asset->width;
493 if( width ) asset->width = width;
494 asset->actual_height = asset->height;
495 if( height ) asset->height = height;
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);
511 asset->video_length = file_pos.size();
516 int FileGIF::close_file()
519 EGifCloseFile(gif_file, &err);
528 if( bg ) { free(bg); bg = 0; }
530 for( int k=0; k<rows; ++k )
532 free(buffer); buffer = 0;
541 FileBase::close_file();
545 int FileGIF::scan_gif()
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);
557 offset = file_offset;
561 int FileGIF::set_video_position(int64_t pos)
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;
570 int FileGIF::read_frame(VFrame *output)
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;
579 // ret = -1:err, 0:eof, 1:frame
580 int FileGIF::read_next_image(VFrame *output)
583 GifRecordType record_type;
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));
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));
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 )
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 )
620 for( int i=0; !ret && i<height; ++i ) {
621 if (DGifGetLine(gif_file, &buffer[row++][col], width) == GIF_ERROR)
627 case EXTENSION_RECORD_TYPE: {
629 GifByteType *ExtData = 0;
630 if( DGifGetExtension(gif_file, &ExtFunction, &ExtData) == GIF_ERROR )
632 while( !ret && ExtData ) {
633 if( DGifGetExtensionNext(gif_file, &ExtData) == GIF_ERROR )
637 case TERMINATE_RECORD_TYPE:
646 ColorMapObject *color_map = 0;
648 color_map = gif_file->Image.ColorMap;
649 if( !color_map ) color_map = gif_file->SColorMap;
650 if( !color_map ) ret = -1;
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;
669 int FileGIF::write_frame(VFrame *frame)
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;
678 VFrame gbrp(w, h, BC_GBRP);
679 gbrp.transfer_from(frame);
680 if( !(cmap = GifMakeMapObject(cmap_sz, 0)) )
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 )
691 if( !result && !writes &&
692 EGifPutScreenDesc(gif_file, w, h, depth, 0, 0) == GIF_ERROR )
695 EGifPutImageDesc(gif_file, 0, 0, w, h, 0, cmap) == GIF_ERROR )
698 GifByteType *bp = bfr;
699 for( int y=0; !result && y<h; ++y ) {
700 if( EGifPutLine(gif_file, bp, w) == GIF_ERROR )
704 GifFreeMapObject(cmap);
710 static int write_data(GifFileType *gif_file, const GifByteType *bfr, int bytes)
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;
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);
725 int FileGIF::wopen_data(VFrame *output)
728 gif_file = EGifOpen(this, write_data, &err);
730 eprintf("FileGIF::wopen_data %d: %s\n", __LINE__, GifErrorString(err));
734 output->set_compressed_size(0);
735 this->output = output;
742 FileGIFList::FileGIFList(Asset *asset, File *file)
743 : FileList(asset, file, "GIFLIST", ".gif", FILE_UNKNOWN, FILE_GIF_LIST)
747 FileGIFList::~FileGIFList()
751 int FileGIFList::check_sig(Asset *asset)
753 FILE *stream = fopen(asset->path, "rb");
755 unsigned char test[16];
756 int ret = fread(test, 16, 1, 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')
766 int FileGIFList::colormodel_supported(int colormodel) { return BC_RGB888; }
767 int FileGIFList::get_best_colormodel(Asset *asset, int driver) { return BC_RGB888; }
769 int FileGIFList::read_frame_header(char *path)
771 FILE *stream = fopen(path, "rb");
773 unsigned char test[16];
774 int ret = fread(test, 16, 1, 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);
786 int FileGIFList::read_frame(VFrame *output, char *path)
788 Asset *asset = new Asset(path);
789 FileGIF gif(asset, file);
790 int ret = gif.ropen_path(path);
792 ret = gif.read_frame(output);
793 asset->remove_user();
797 int FileGIFList::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
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;
808 FileGIF gif(asset, file);
809 int ret = gif.wopen_data(data);
811 ret = gif.write_frame(frame);
815 FrameWriterUnit* FileGIFList::new_writer_unit(FrameWriter *writer)
817 return new GIFUnit(this, writer);
820 int FileGIFList::verify_file_list()
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");
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];
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");
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) ) {
858 prev_height = height;
861 else if ( (prev_width != width) || (prev_height != height) ) {
862 // this is the error case we are trying to avoid
873 // not sure if our function should be the one to raise not found error
879 GIFUnit::GIFUnit(FileGIFList *file, FrameWriter *writer)
880 : FrameWriterUnit(writer)