no longer need ffmpeg patch0 which was for Termux
[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
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
44 FileGIF::FileGIF(Asset *asset, File *file)
45  : FileBase(asset, file)
46 {
47         offset = 0;
48         err = 0;
49         gif_file = 0;
50         eof = 1;
51         row_size = 0;
52         rows = 0;
53         depth = 8;
54         fp = 0;
55         fd = -1;
56         writes = -1;
57         buffer = 0;
58         bg = 0;
59         output = 0;
60 }
61
62 FileGIF::~FileGIF()
63 {
64         close_file();
65 }
66
67 int FileGIF::check_sig(Asset *asset)
68 {
69         FILE *stream = fopen(asset->path, "rb");
70         if( stream ) {
71                 char test[8];
72                 int ret = fread(test, 1, 6, stream);
73                 fclose(stream);
74                 if( ret >= 6 &&
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;
78         }
79         return 0;
80 }
81
82 int FileGIF::colormodel_supported(int colormodel)
83 {
84         return BC_RGB888;
85 }
86
87 int FileGIF::get_best_colormodel(Asset *asset, int driver)
88 {
89         return BC_RGB888;
90 }
91
92
93 int FileGIF::read_frame_header(char *path)
94 {
95         FILE *stream = fopen(path, "rb");
96         if( stream ) {
97                 unsigned char test[16];
98                 int ret = fread(test, 16, 1, stream);
99                 fclose(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);
104                 return 0;
105         }
106
107         perror(path);
108         return 1;
109 }
110
111 static int input_file(GifFileType *gif_file, GifByteType *buffer, int bytes)
112 {
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;
117         return bytes;
118 }
119
120 int FileGIF::open_file(int rd, int wr)
121 {
122         return  rd ? ropen_path(asset->path) :
123                 wr ? wopen_path(asset->path) :
124                 0 ;
125 }
126
127 int FileGIF::ropen_path(const char *path)
128 {
129         fp = fopen(path, "r");
130         int result = !fp ? 1 : 0;
131         if( !result ) {
132                 offset = 0;  eof = 0;
133                 gif_file = DGifOpen(this, input_file, &err);
134                 if( !gif_file ) {
135                         eprintf("FileGIF::ropen_path %d: %s\n", __LINE__, GifErrorString(err));
136                         result = 1;
137                 }
138         }
139         if( !result )
140                 result = open_gif();
141         return result;
142 }
143
144 int FileGIF::wopen_path(const char *path)
145 {
146         fd = open(path, O_CREAT+O_TRUNC+O_WRONLY, 0777);
147         int result = fd < 0 ? 1 : 0;
148         if( !result ) {
149                 gif_file = EGifOpenFileHandle(fd, &err);
150                 if( !gif_file ) {
151                         eprintf("FileGIF::wopen_path %d: %s\n", __LINE__, GifErrorString(err));
152                         result = 1;
153                 }
154         }
155         if( !result ) {
156                 writes = 0;
157         }
158         return result;
159 }
160
161 int FileGIF::write_frames(VFrame ***frames, int len)
162 {
163         int result = !gif_file ? 1 : 0;
164         for( int i=0; i<len && !result; ++i )
165                 result = write_frame(frames[0][i]);
166         return result;
167 }
168
169 int FileGIF::open_gif()
170 {
171         file_pos.remove_all();
172         int width = asset->width;
173         int height = asset->height;
174         int result = read_frame_header(asset->path);
175         if( !result ) {
176                 asset->actual_width = asset->width;
177                 if( width ) asset->width = width;
178                 asset->actual_height = asset->height;
179                 if( height ) asset->height = height;
180                 asset->layers = 1;
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);
193                 }
194                 result = scan_gif();
195                 asset->video_length = file_pos.size();
196         }
197         return result;
198 }
199
200 int FileGIF::close_file()
201 {
202         if( gif_file ) {
203                 EGifCloseFile(gif_file, &err);
204                 gif_file = 0;
205         }
206         if( fp ) {
207                 fclose(fp);  fp = 0;
208         }
209         if( fd >= 0 ) {
210                 close(fd);  fd = -1;
211         }
212         if( bg ) { free(bg);  bg = 0; }
213         if( buffer ) {
214                 for( int k=0; k<rows; ++k )
215                         free(buffer[k]);
216                 free(buffer);  buffer = 0;
217                 rows = 0;
218         }
219         offset = 0;
220         row_size = 0;
221         err = 0;
222         writes = -1;
223         eof = 1;
224         output = 0;
225         FileBase::close_file();
226         return 0;
227 }
228
229 int FileGIF::scan_gif()
230 {
231         int file_eof = eof;
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);
238                 image_pos = offset;
239         }
240         eof = file_eof;
241         offset = file_offset;
242         return ret;
243 }
244
245 int FileGIF::set_video_position(int64_t pos)
246 {
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;
251         return 0;
252 }
253
254 int FileGIF::read_frame(VFrame *output)
255 {
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;
260         return ret;
261 }
262
263 // ret = -1:err, 0:eof, 1:frame
264 int FileGIF::read_next_image(VFrame *output)
265 {
266         int ret = 0;
267         GifRecordType record_type;
268
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));
273                         ret = -1;
274                         break;
275                 }
276
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));
282                                 break;
283                         }
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 )
290                                 ret = -1;
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 )
299                                                         ret = -1;
300                                         }
301                                 }
302                         }
303                         else {
304                                 for( int i=0; !ret && i<height; ++i ) {
305                                         if (DGifGetLine(gif_file, &buffer[row++][col], width) == GIF_ERROR)
306                                                 ret = -1;
307                                 }
308                         }
309                         ret = 1;
310                         break; }
311                 case EXTENSION_RECORD_TYPE: {
312                         int ExtFunction = 0;
313                         GifByteType *ExtData = 0;
314                         if( DGifGetExtension(gif_file, &ExtFunction, &ExtData) == GIF_ERROR )
315                                 ret = -1;
316                         while( !ret && ExtData ) {
317                                 if( DGifGetExtensionNext(gif_file, &ExtData) == GIF_ERROR )
318                                         ret = -1;
319                         }
320                         break; }
321                 case TERMINATE_RECORD_TYPE:
322                         eof = 1;
323                         break;
324                 default:
325                         ret = -1;
326                         break;
327                 }
328         }
329
330         ColorMapObject *color_map = 0;
331         if( ret > 0 ) {
332                 color_map = gif_file->Image.ColorMap;
333                 if( !color_map ) color_map = gif_file->SColorMap;
334                 if( !color_map ) ret = -1;
335         }
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;
347                         }
348                 }
349         }
350         return ret;
351 }
352
353 int FileGIF::write_frame(VFrame *frame)
354 {
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;
361         if( !result ) {
362                 VFrame gbrp(w, h, BC_GBRP);
363                 gbrp.transfer_from(frame);
364                 if( !(cmap = GifMakeMapObject(cmap_sz, 0)) )
365                         result = 1;
366                 if( !result ) {
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 )
372                                 result = 1;
373                 }
374         }
375         if( !result && !writes &&
376             EGifPutScreenDesc(gif_file, w, h, depth, 0, 0) == GIF_ERROR )
377                 result = 1;
378         if( !result &&
379             EGifPutImageDesc(gif_file, 0, 0, w, h, 0, cmap) == GIF_ERROR )
380                 result = 1;
381
382         GifByteType *bp = bfr;
383         for( int y=0; !result && y<h; ++y ) {
384                 if( EGifPutLine(gif_file, bp, w) == GIF_ERROR )
385                         result = 1;
386                 bp += w;
387         }
388         GifFreeMapObject(cmap);
389         if( bfr ) free(bfr);
390         ++writes;
391         return result;
392 }
393
394 static int write_data(GifFileType *gif_file, const GifByteType *bfr, int bytes)
395 {
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;
401         if( len > alloc )
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);
406         return bytes;
407 }
408
409 int FileGIF::wopen_data(VFrame *output)
410 {
411         int result = 0;
412         gif_file = EGifOpen(this, write_data, &err);
413         if( !gif_file ) {
414                 eprintf("FileGIF::wopen_data %d: %s\n", __LINE__, GifErrorString(err));
415                 result = 1;
416         }
417         if( !result ) {
418                 output->set_compressed_size(0);
419                 this->output = output;
420                 writes = 0;
421         }
422         return result;
423 }
424
425
426 FileGIFList::FileGIFList(Asset *asset, File *file)
427  : FileList(asset, file, "GIFLIST", ".gif", FILE_UNKNOWN, FILE_GIF_LIST)
428 {
429 }
430
431 FileGIFList::~FileGIFList()
432 {
433 }
434
435 int FileGIFList::check_sig(Asset *asset)
436 {
437         FILE *stream = fopen(asset->path, "rb");
438         if( stream ) {
439                 unsigned char test[16];
440                 int ret = fread(test, 16, 1, stream);
441                 fclose(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')
445                         return 1;
446         }
447         return 0;
448 }
449
450 int FileGIFList::colormodel_supported(int colormodel) { return BC_RGB888; }
451 int FileGIFList::get_best_colormodel(Asset *asset, int driver) { return BC_RGB888; }
452
453 int FileGIFList::read_frame_header(char *path)
454 {
455         FILE *stream = fopen(path, "rb");
456         if( stream ) {
457                 unsigned char test[16];
458                 int ret = fread(test, 16, 1, stream);
459                 fclose(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);
464                 return 0;
465         }
466         perror(path);
467         return 1;
468 }
469
470 int FileGIFList::read_frame(VFrame *output, char *path)
471 {
472         Asset *asset = new Asset(path);
473         FileGIF gif(asset, file);
474         int ret = gif.ropen_path(path);
475         if( !ret )
476                 ret = gif.read_frame(output);
477         asset->remove_user();
478         return ret;
479 }
480
481 int FileGIFList::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
482 {
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;
490         }
491
492         FileGIF gif(asset, file);
493         int ret = gif.wopen_data(data);
494         if( !ret )
495                 ret = gif.write_frame(frame);
496         return ret;
497 }
498
499 FrameWriterUnit* FileGIFList::new_writer_unit(FrameWriter *writer)
500 {
501         return new GIFUnit(this, writer);
502 }
503
504 int FileGIFList::verify_file_list()
505 {
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");
510  if (stream) {
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];
516   *bp = 0;
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");
531     if (gif_file_temp) {
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) ) {
541       prev_width = width;
542       prev_height = height;
543       continue;
544      }
545      else if ( (prev_width != width) || (prev_height != height) ) {
546       // this is the error case we are trying to avoid
547       fclose(stream);
548       return 0;
549      }
550     }
551
552    }
553   }
554   fclose(stream);
555   return 1;
556  }
557  // not sure if our function should be the one to raise not found error
558  perror(asset->path);
559  return 0;
560 }
561
562
563 GIFUnit::GIFUnit(FileGIFList *file, FrameWriter *writer)
564  : FrameWriterUnit(writer)
565 {
566         this->file = file;
567         temp_frame = 0;
568 }
569
570 GIFUnit::~GIFUnit()
571 {
572         delete temp_frame;
573 }
574