4 * Copyright (C) 2014 Adam Williams <broadcast at earthling dot net>
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.
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.
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
23 #include "bcsignals.h"
27 #include "mainerror.h"
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
45 #define ABS(x) ((x) > 0 ? (x) : (-(x)))
47 #define COLOR_ARRAY_SIZE 32768
48 #define BITS_PER_PRIM_COLOR 5
49 #define MAX_PRIM_COLOR 0x1f
51 typedef struct QuantizedColorType {
53 GifByteType NewColorIndex;
55 struct QuantizedColorType *Pnext;
58 static int QCmpr(QuantizedColorType *a, QuantizedColorType *b, int i)
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;
69 static int QSplit(QuantizedColorType **q, int l, int r, int i)
72 QuantizedColorType *t;
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;
81 static void QSort(QuantizedColorType **q, int ll, int rr, int i)
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);
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;
99 static int SubdivColorMap(NewColorMapType * NewColorSubdiv,
100 unsigned int ColorMapSize,
101 unsigned int *NewColorMapSize);
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 ******************************************************************************/
117 GifQuantizeBuffer(unsigned int Width,
120 GifByteType * RedInput,
121 GifByteType * GreenInput,
122 GifByteType * BlueInput,
123 GifByteType * OutputBuffer,
124 GifColorType * OutputColorMap) {
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;
133 ColorArrayEntries = (QuantizedColorType *)malloc(
134 sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE);
135 if (ColorArrayEntries == NULL) {
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) &
143 ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR;
144 ColorArrayEntries[i].Count = 0;
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++;
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;
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)
172 QuantizedColor = NewColorSubdiv[0].QuantizedColors = &ColorArrayEntries[i];
174 while (++i < COLOR_ARRAY_SIZE)
175 if (ColorArrayEntries[i].Count > 0) {
176 QuantizedColor->Pnext = &ColorArrayEntries[i];
177 QuantizedColor = &ColorArrayEntries[i];
180 QuantizedColor->Pnext = NULL;
182 NewColorSubdiv[0].NumEntries = NumOfEntries; /* Different sampled colors */
183 NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */
185 if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) !=
187 free((char *)ColorArrayEntries);
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;
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;
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;
216 /* Finally scan the input buffer again and put the mapped index in the
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]);
237 "Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n",
238 MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]);
241 free((char *)ColorArrayEntries);
243 *ColorMapSize = NewColorMapSize;
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 *******************************************************************************/
255 SubdivColorMap(NewColorMapType * NewColorSubdiv,
256 unsigned int ColorMapSize,
257 unsigned int *NewColorMapSize) {
260 unsigned int i, j, Index = 0;
261 QuantizedColorType *QuantizedColor, **SortArray;
263 while (ColorMapSize > *NewColorMapSize) {
264 /* Find candidate for subdivision: */
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];
282 /* Split the entry Index into two along the axis SortRGBAxis: */
284 /* Sort all elements in that entry along the given axis and split at
286 SortArray = (QuantizedColorType **)malloc(
287 sizeof(QuantizedColorType *) *
288 NewColorSubdiv[Index].NumEntries);
289 if (SortArray == NULL)
291 for (j = 0, QuantizedColor = NewColorSubdiv[Index].QuantizedColors;
292 j < NewColorSubdiv[Index].NumEntries && QuantizedColor != NULL;
293 j++, QuantizedColor = QuantizedColor->Pnext)
294 SortArray[j] = QuantizedColor;
296 QSort(SortArray, -1, NewColorSubdiv[Index].NumEntries, SortRGBAxis);
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);
305 /* Now simply add the Counts until we have half of the Count: */
306 Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count;
308 Count = QuantizedColor->Count;
309 while (QuantizedColor->Pnext != NULL &&
310 (Sum -= QuantizedColor->Pnext->Count) >= 0 &&
311 QuantizedColor->Pnext->Pnext != NULL) {
312 QuantizedColor = QuantizedColor->Pnext;
314 Count += QuantizedColor->Count;
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.
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);
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];
342 NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] =
343 NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] +
344 NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] - MinColor;
345 NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor;
347 NewColorSubdiv[Index].RGBWidth[SortRGBAxis] =
348 MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis];
350 (*NewColorMapSize)++;
359 FileGIF::FileGIF(Asset *asset, File *file)
360 : FileBase(asset, file)
382 int FileGIF::check_sig(Asset *asset)
384 FILE *stream = fopen(asset->path, "rb");
387 int ret = fread(test, 1, 6, stream);
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;
397 int FileGIF::colormodel_supported(int colormodel)
402 int FileGIF::get_best_colormodel(Asset *asset, int driver)
408 int FileGIF::read_frame_header(char *path)
410 FILE *stream = fopen(path, "rb");
412 unsigned char test[16];
413 int ret = fread(test, 16, 1, 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);
426 static int input_file(GifFileType *gif_file, GifByteType *buffer, int bytes)
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;
435 int FileGIF::open_file(int rd, int wr)
437 return rd ? ropen_path(asset->path) :
438 wr ? wopen_path(asset->path) :
442 int FileGIF::ropen_path(const char *path)
444 fp = fopen(path, "r");
445 int result = !fp ? 1 : 0;
448 gif_file = DGifOpen(this, input_file, &err);
450 eprintf("FileGIF::ropen_path %d: %s\n", __LINE__, GifErrorString(err));
459 int FileGIF::wopen_path(const char *path)
461 fd = open(path, O_CREAT+O_TRUNC+O_WRONLY, 0777);
462 int result = fd < 0 ? 1 : 0;
464 gif_file = EGifOpenFileHandle(fd, &err);
466 eprintf("FileGIF::wopen_path %d: %s\n", __LINE__, GifErrorString(err));
476 int FileGIF::write_frames(VFrame ***frames, int len)
478 int result = !gif_file ? 1 : 0;
479 for( int i=0; i<len && !result; ++i )
480 result = write_frame(frames[0][i]);
484 int FileGIF::open_gif()
486 file_pos.remove_all();
487 int width = asset->width;
488 int height = asset->height;
489 int result = read_frame_header(asset->path);
491 asset->actual_width = asset->width;
492 if( width ) asset->width = width;
493 asset->actual_height = asset->height;
494 if( height ) asset->height = height;
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);
510 asset->video_length = file_pos.size();
515 int FileGIF::close_file()
518 EGifCloseFile(gif_file, &err);
527 if( bg ) { free(bg); bg = 0; }
529 for( int k=0; k<rows; ++k )
531 free(buffer); buffer = 0;
540 FileBase::close_file();
544 int FileGIF::scan_gif()
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);
556 offset = file_offset;
560 int FileGIF::set_video_position(int64_t pos)
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;
569 int FileGIF::read_frame(VFrame *output)
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;
578 // ret = -1:err, 0:eof, 1:frame
579 int FileGIF::read_next_image(VFrame *output)
582 GifRecordType record_type;
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));
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));
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 )
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 )
619 for( int i=0; !ret && i<height; ++i ) {
620 if (DGifGetLine(gif_file, &buffer[row++][col], width) == GIF_ERROR)
626 case EXTENSION_RECORD_TYPE: {
628 GifByteType *ExtData = 0;
629 if( DGifGetExtension(gif_file, &ExtFunction, &ExtData) == GIF_ERROR )
631 while( !ret && ExtData ) {
632 if( DGifGetExtensionNext(gif_file, &ExtData) == GIF_ERROR )
636 case TERMINATE_RECORD_TYPE:
645 ColorMapObject *color_map = 0;
647 color_map = gif_file->Image.ColorMap;
648 if( !color_map ) color_map = gif_file->SColorMap;
649 if( !color_map ) ret = -1;
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;
668 int FileGIF::write_frame(VFrame *frame)
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;
677 VFrame gbrp(w, h, BC_GBRP);
678 gbrp.transfer_from(frame);
679 if( !(cmap = GifMakeMapObject(cmap_sz, 0)) )
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 )
690 if( !result && !writes &&
691 EGifPutScreenDesc(gif_file, w, h, depth, 0, 0) == GIF_ERROR )
694 EGifPutImageDesc(gif_file, 0, 0, w, h, 0, cmap) == GIF_ERROR )
697 GifByteType *bp = bfr;
698 for( int y=0; !result && y<h; ++y ) {
699 if( EGifPutLine(gif_file, bp, w) == GIF_ERROR )
703 GifFreeMapObject(cmap);
709 static int write_data(GifFileType *gif_file, const GifByteType *bfr, int bytes)
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;
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);
724 int FileGIF::wopen_data(VFrame *output)
727 gif_file = EGifOpen(this, write_data, &err);
729 eprintf("FileGIF::wopen_data %d: %s\n", __LINE__, GifErrorString(err));
733 output->set_compressed_size(0);
734 this->output = output;
741 FileGIFList::FileGIFList(Asset *asset, File *file)
742 : FileList(asset, file, "GIFLIST", ".gif", FILE_UNKNOWN, FILE_GIF_LIST)
746 FileGIFList::~FileGIFList()
750 int FileGIFList::check_sig(Asset *asset)
752 FILE *stream = fopen(asset->path, "rb");
754 unsigned char test[16];
755 int ret = fread(test, 16, 1, 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')
765 int FileGIFList::colormodel_supported(int colormodel) { return BC_RGB888; }
766 int FileGIFList::get_best_colormodel(Asset *asset, int driver) { return BC_RGB888; }
768 int FileGIFList::read_frame_header(char *path)
770 FILE *stream = fopen(path, "rb");
772 unsigned char test[16];
773 int ret = fread(test, 16, 1, 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);
785 int FileGIFList::read_frame(VFrame *output, char *path)
787 Asset *asset = new Asset(path);
788 FileGIF gif(asset, file);
789 int ret = gif.ropen_path(path);
791 ret = gif.read_frame(output);
792 asset->remove_user();
796 int FileGIFList::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
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;
807 FileGIF gif(asset, file);
808 int ret = gif.wopen_data(data);
810 ret = gif.write_frame(frame);
814 FrameWriterUnit* FileGIFList::new_writer_unit(FrameWriter *writer)
816 return new GIFUnit(this, writer);
819 int FileGIFList::verify_file_list()
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");
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];
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");
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) ) {
857 prev_height = height;
860 else if ( (prev_width != width) || (prev_height != height) ) {
861 // this is the error case we are trying to avoid
872 // not sure if our function should be the one to raise not found error
878 GIFUnit::GIFUnit(FileGIFList *file, FrameWriter *writer)
879 : FrameWriterUnit(writer)