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);
44 FileGIF::FileGIF(Asset *asset, File *file)
45 : FileBase(asset, file)
67 int FileGIF::check_sig(Asset *asset)
69 FILE *stream = fopen(asset->path, "rb");
72 int ret = fread(test, 1, 6, stream);
75 test[0] == 'G' && test[1] == 'I' && test[2] == 'F' &&
76 test[3] == '8' && (test[4] == '7' || test[4] == '9') &&
77 test[5] == 'a' ) return 1;
82 int FileGIF::colormodel_supported(int colormodel)
87 int FileGIF::get_best_colormodel(Asset *asset, int driver)
93 int FileGIF::read_frame_header(char *path)
95 FILE *stream = fopen(path, "rb");
97 unsigned char test[16];
98 int ret = fread(test, 16, 1, stream);
100 if( ret < 1 ) return 1;
101 asset->format = FILE_GIF;
102 asset->width = test[6] | (test[7] << 8);
103 asset->height = test[8] | (test[9] << 8);
111 static int input_file(GifFileType *gif_file, GifByteType *buffer, int bytes)
113 FileGIF *file = (FileGIF*)gif_file->UserData;
114 fseek(file->fp, file->offset, SEEK_SET);
115 bytes = fread(buffer, 1, bytes, file->fp);
116 file->offset += bytes;
120 int FileGIF::open_file(int rd, int wr)
122 return rd ? ropen_path(asset->path) :
123 wr ? wopen_path(asset->path) :
127 int FileGIF::ropen_path(const char *path)
129 fp = fopen(path, "r");
130 int result = !fp ? 1 : 0;
133 gif_file = DGifOpen(this, input_file, &err);
135 eprintf("FileGIF::ropen_path %d: %s\n", __LINE__, GifErrorString(err));
144 int FileGIF::wopen_path(const char *path)
146 fd = open(path, O_CREAT+O_TRUNC+O_WRONLY, 0777);
147 int result = fd < 0 ? 1 : 0;
149 gif_file = EGifOpenFileHandle(fd, &err);
151 eprintf("FileGIF::wopen_path %d: %s\n", __LINE__, GifErrorString(err));
161 int FileGIF::write_frames(VFrame ***frames, int len)
163 int result = !gif_file ? 1 : 0;
164 for( int i=0; i<len && !result; ++i )
165 result = write_frame(frames[0][i]);
169 int FileGIF::open_gif()
171 file_pos.remove_all();
172 int width = asset->width;
173 int height = asset->height;
174 int result = read_frame_header(asset->path);
176 asset->actual_width = asset->width;
177 if( width ) asset->width = width;
178 asset->actual_height = asset->height;
179 if( height ) asset->height = height;
181 if( !asset->frame_rate )
182 asset->frame_rate = 10;
183 asset->video_data = 1;
184 row_size = gif_file->SWidth * sizeof(GifPixelType);
185 bg = (GifRowType)malloc(row_size);
186 for( int i=0; i<gif_file->SWidth; ++i )
187 bg[i] = gif_file->SBackGroundColor;
188 rows = gif_file->SHeight;
189 buffer = (GifRowType*)malloc(sizeof(GifRowType) * rows);
190 for( int i=0; i<gif_file->SHeight; ++i ) {
191 buffer[i] = (GifRowType)malloc(row_size);
192 memcpy(buffer[i], bg, row_size);
195 asset->video_length = file_pos.size();
200 int FileGIF::close_file()
203 EGifCloseFile(gif_file, &err);
212 if( bg ) { free(bg); bg = 0; }
214 for( int k=0; k<rows; ++k )
216 free(buffer); buffer = 0;
225 FileBase::close_file();
229 int FileGIF::scan_gif()
232 int64_t file_offset = offset;
233 file_pos.remove_all();
234 int image_pos = offset, ret;
235 // read all imgs, build file_pos index
236 while( (ret=read_next_image(0)) > 0 ) {
237 file_pos.append(image_pos);
241 offset = file_offset;
245 int FileGIF::set_video_position(int64_t pos)
247 if( !gif_file || !asset->video_length ) return 1;
248 int64_t sz = file_pos.size();
249 eof = pos < 0 || pos >= sz ? 1 : 0;
250 offset = !eof ? file_pos[pos] : 0;
254 int FileGIF::read_frame(VFrame *output)
256 if( !gif_file ) return 1;
257 for( int i=0; i<gif_file->SHeight; ++i )
258 memcpy(buffer[i], bg, row_size);
259 int ret = read_next_image(output) > 0 ? 0 : 1;
263 // ret = -1:err, 0:eof, 1:frame
264 int FileGIF::read_next_image(VFrame *output)
267 GifRecordType record_type;
269 while( !ret && !eof ) {
270 if( DGifGetRecordType(gif_file, &record_type) == GIF_ERROR ) {
271 err = gif_file->Error;
272 eprintf("FileGIF::read_frame %d: %s\n", __LINE__, GifErrorString(err));
277 switch( record_type ) {
278 case IMAGE_DESC_RECORD_TYPE: {
279 if( DGifGetImageDesc(gif_file) == GIF_ERROR ) {
280 err = gif_file->Error;
281 eprintf("FileGIF::read_frame %d: %s\n", __LINE__, GifErrorString(err));
284 int row = gif_file->Image.Top;
285 int col = gif_file->Image.Left;
286 int width = gif_file->Image.Width;
287 int height = gif_file->Image.Height;
288 if( gif_file->Image.Left + gif_file->Image.Width > gif_file->SWidth ||
289 gif_file->Image.Top + gif_file->Image.Height > gif_file->SHeight )
291 if( !ret && gif_file->Image.Interlace ) {
292 static int InterlacedOffset[] = { 0, 4, 2, 1 };
293 static int InterlacedJumps[] = { 8, 8, 4, 2 };
294 /* Need to perform 4 passes on the images: */
295 for( int i=0; i<4; ++i ) {
296 int j = row + InterlacedOffset[i];
297 for( ; !ret && j<row + height; j+=InterlacedJumps[i] ) {
298 if( DGifGetLine(gif_file, &buffer[j][col], width) == GIF_ERROR )
304 for( int i=0; !ret && i<height; ++i ) {
305 if (DGifGetLine(gif_file, &buffer[row++][col], width) == GIF_ERROR)
311 case EXTENSION_RECORD_TYPE: {
313 GifByteType *ExtData = 0;
314 if( DGifGetExtension(gif_file, &ExtFunction, &ExtData) == GIF_ERROR )
316 while( !ret && ExtData ) {
317 if( DGifGetExtensionNext(gif_file, &ExtData) == GIF_ERROR )
321 case TERMINATE_RECORD_TYPE:
330 ColorMapObject *color_map = 0;
332 color_map = gif_file->Image.ColorMap;
333 if( !color_map ) color_map = gif_file->SColorMap;
334 if( !color_map ) ret = -1;
336 if( ret > 0 && output ) {
337 int screen_width = gif_file->SWidth;
338 int screen_height = gif_file->SHeight;
339 for( int i=0; i<screen_height; ++i ) {
340 GifRowType row = buffer[i];
341 unsigned char *out_ptr = output->get_rows()[i];
342 for( int j=0; j<screen_width; ++j ) {
343 GifColorType *color_map_entry = &color_map->Colors[row[j]];
344 *out_ptr++ = color_map_entry->Red;
345 *out_ptr++ = color_map_entry->Green;
346 *out_ptr++ = color_map_entry->Blue;
353 int FileGIF::write_frame(VFrame *frame)
355 int w = frame->get_w(), h = frame->get_h();
356 ColorMapObject *cmap = 0;
357 int cmap_sz = depth >= 0 ? 1 << depth : 0;
358 int64_t len = w * h * sizeof(GifByteType);
359 GifByteType *bfr = (GifByteType *) malloc(len);
360 int result = !bfr ? 1 : 0;
362 VFrame gbrp(w, h, BC_GBRP);
363 gbrp.transfer_from(frame);
364 if( !(cmap = GifMakeMapObject(cmap_sz, 0)) )
367 GifByteType *gp = (GifByteType *)gbrp.get_r();
368 GifByteType *bp = (GifByteType *)gbrp.get_g();
369 GifByteType *rp = (GifByteType *)gbrp.get_b();
370 if( GifQuantizeBuffer(w, h, &cmap_sz, rp, gp, bp,
371 bfr, cmap->Colors) == GIF_ERROR )
375 if( !result && !writes &&
376 EGifPutScreenDesc(gif_file, w, h, depth, 0, 0) == GIF_ERROR )
379 EGifPutImageDesc(gif_file, 0, 0, w, h, 0, cmap) == GIF_ERROR )
382 GifByteType *bp = bfr;
383 for( int y=0; !result && y<h; ++y ) {
384 if( EGifPutLine(gif_file, bp, w) == GIF_ERROR )
388 GifFreeMapObject(cmap);
394 static int write_data(GifFileType *gif_file, const GifByteType *bfr, int bytes)
396 FileGIF *file = (FileGIF*)gif_file->UserData;
397 VFrame *output = file->output;
398 long size = output->get_compressed_size();
399 long alloc = output->get_compressed_allocated();
400 long len = size + bytes;
402 output->allocate_compressed_data(2*size + bytes);
403 unsigned char *data = output->get_data() + size;
404 memcpy(data, bfr, bytes);
405 output->set_compressed_size(len);
409 int FileGIF::wopen_data(VFrame *output)
412 gif_file = EGifOpen(this, write_data, &err);
414 eprintf("FileGIF::wopen_data %d: %s\n", __LINE__, GifErrorString(err));
418 output->set_compressed_size(0);
419 this->output = output;
426 FileGIFList::FileGIFList(Asset *asset, File *file)
427 : FileList(asset, file, "GIFLIST", ".gif", FILE_UNKNOWN, FILE_GIF_LIST)
431 FileGIFList::~FileGIFList()
435 int FileGIFList::check_sig(Asset *asset)
437 FILE *stream = fopen(asset->path, "rb");
439 unsigned char test[16];
440 int ret = fread(test, 16, 1, stream);
442 if( ret < 1 ) return 1;
443 if( test[0] == 'G' && test[1] == 'I' && test[2] == 'F' &&
444 test[3] == 'L' && test[4] == 'I' && test[5] == 'S' && test[6] == 'T')
450 int FileGIFList::colormodel_supported(int colormodel) { return BC_RGB888; }
451 int FileGIFList::get_best_colormodel(Asset *asset, int driver) { return BC_RGB888; }
453 int FileGIFList::read_frame_header(char *path)
455 FILE *stream = fopen(path, "rb");
457 unsigned char test[16];
458 int ret = fread(test, 16, 1, stream);
460 if( ret < 1 ) return 1;
461 asset->format = FILE_GIF_LIST;
462 asset->width = test[6] | (test[7] << 8);
463 asset->height = test[8] | (test[9] << 8);
470 int FileGIFList::read_frame(VFrame *output, char *path)
472 Asset *asset = new Asset(path);
473 FileGIF gif(asset, file);
474 int ret = gif.ropen_path(path);
476 ret = gif.read_frame(output);
477 asset->remove_user();
481 int FileGIFList::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
483 int native_cmodel = BC_RGB888;
484 if( frame->get_color_model() != native_cmodel ) {
485 GIFUnit *gif_unit = (GIFUnit *)unit;
486 if( !gif_unit->temp_frame ) gif_unit->temp_frame =
487 new VFrame(frame->get_w(), frame->get_h(), native_cmodel);
488 gif_unit->temp_frame->transfer_from(frame);
489 frame = gif_unit->temp_frame;
492 FileGIF gif(asset, file);
493 int ret = gif.wopen_data(data);
495 ret = gif.write_frame(frame);
499 FrameWriterUnit* FileGIFList::new_writer_unit(FrameWriter *writer)
501 return new GIFUnit(this, writer);
504 int FileGIFList::verify_file_list()
506 // go through all .gif files in the list and
507 // verify their sizes match or not.
508 //printf("\nAsset Path: %s\n", asset->path);
509 FILE *stream = fopen(asset->path, "rb");
511 char string[BCTEXTLEN];
512 int width, height, prev_width=-1, prev_height=-1;
513 // build the path prefix
514 char prefix[BCTEXTLEN], *bp = prefix, *cp = strrchr(asset->path, '/');
515 for( int i=0, n=!cp ? 0 : cp-asset->path; i<n; ++i ) *bp++ = asset->path[i];
517 // read entire input file
518 while( !feof(stream) && fgets(string, BCTEXTLEN, stream) ) {
519 int len = strlen(string);
520 if(!len || string[0] == '#' || string[0] == ' ' || isalnum(string[0])) continue;
521 if( string[len-1] == '\n' ) string[len-1] = 0;
522 // a possible .gif file path? fetch it
523 char path[BCTEXTLEN], *pp = path, *ep = pp + sizeof(path)-1;
524 if( string[0] == '.' && string[1] == '/' && prefix[0] )
525 pp += snprintf(pp, ep-pp, "%s/", prefix);
526 snprintf(pp, ep-pp, "%s", string);
527 // check if a valid file exists
528 if(!access(path, R_OK)) {
529 // check file header for size
530 FILE *gif_file_temp = fopen(path, "rb");
532 unsigned char test[16];
533 int ret = fread(test, 16, 1, gif_file_temp);
534 fclose(gif_file_temp);
535 if( ret < 1 ) continue;
536 // get height and width of gif file
537 width = test[6] | (test[7] << 8);
538 height = test[8] | (test[9] << 8);
539 // test with previous
540 if ( (prev_width == -1) && (prev_height == -1) ) {
542 prev_height = height;
545 else if ( (prev_width != width) || (prev_height != height) ) {
546 // this is the error case we are trying to avoid
557 // not sure if our function should be the one to raise not found error
563 GIFUnit::GIFUnit(FileGIFList *file, FrameWriter *writer)
564 : FrameWriterUnit(writer)