add new boxblur plugin, mods to videoscope, fix segv for menu btns kfrm-tweak/kfrm...
[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
35 //from "getarg.h"
36 extern "C"
37 int GifQuantizeBuffer(unsigned int Width, unsigned int Height,
38                    int *ColorMapSize, GifByteType * RedInput,
39                    GifByteType * GreenInput, GifByteType * BlueInput,
40                    GifByteType * OutputBuffer,
41                    GifColorType * OutputColorMap);
42
43 FileGIF::FileGIF(Asset *asset, File *file)
44  : FileBase(asset, file)
45 {
46         offset = 0;
47         err = 0;
48         gif_file = 0;
49         eof = 1;
50         row_size = 0;
51         rows = 0;
52         depth = 8;
53         fp = 0;
54         fd = -1;
55         writes = -1;
56         buffer = 0;
57         bg = 0;
58         output = 0;
59 }
60
61 FileGIF::~FileGIF()
62 {
63         close_file();
64 }
65
66 int FileGIF::check_sig(Asset *asset)
67 {
68         FILE *stream = fopen(asset->path, "rb");
69         if( stream ) {
70                 char test[8];
71                 int ret = fread(test, 1, 6, stream);
72                 fclose(stream);
73                 if( ret >= 6 &&
74                     test[0] == 'G' && test[1] == 'I' && test[2] == 'F' &&
75                     test[3] == '8' && (test[4] == '7' || test[4] == '9') &&
76                     test[5] == 'a' ) return 1;
77         }
78         return 0;
79 }
80
81 int FileGIF::colormodel_supported(int colormodel)
82 {
83         return BC_RGB888;
84 }
85
86 int FileGIF::get_best_colormodel(Asset *asset, int driver)
87 {
88         return BC_RGB888;
89 }
90
91
92 int FileGIF::read_frame_header(char *path)
93 {
94         FILE *stream = fopen(path, "rb");
95         if( stream ) {
96                 unsigned char test[16];
97                 int ret = fread(test, 16, 1, stream);
98                 fclose(stream);
99                 if( ret < 1 ) return 1;
100                 asset->format = FILE_GIF;
101                 asset->width = test[6] | (test[7] << 8);
102                 asset->height = test[8] | (test[9] << 8);
103                 return 0;
104         }
105
106         perror(path);
107         return 1;
108 }
109
110 static int input_file(GifFileType *gif_file, GifByteType *buffer, int bytes)
111 {
112         FileGIF *file = (FileGIF*)gif_file->UserData;
113         fseek(file->fp, file->offset, SEEK_SET);
114         bytes = fread(buffer, 1, bytes, file->fp);
115         file->offset += bytes;
116         return bytes;
117 }
118
119 int FileGIF::open_file(int rd, int wr)
120 {
121         return  rd ? ropen_path(asset->path) :
122                 wr ? wopen_path(asset->path) :
123                 0 ;
124 }
125
126 int FileGIF::ropen_path(const char *path)
127 {
128         fp = fopen(path, "r");
129         int result = !fp ? 1 : 0;
130         if( !result ) {
131                 offset = 0;  eof = 0;
132                 gif_file = DGifOpen(this, input_file, &err);
133                 if( !gif_file ) {
134                         eprintf("FileGIF::ropen_path %d: %s\n", __LINE__, GifErrorString(err));
135                         result = 1;
136                 }
137         }
138         if( !result )
139                 result = open_gif();
140         return result;
141 }
142
143 int FileGIF::wopen_path(const char *path)
144 {
145         fd = open(path, O_CREAT+O_TRUNC+O_WRONLY, 0777);
146         int result = fd < 0 ? 1 : 0;
147         if( !result ) {
148                 gif_file = EGifOpenFileHandle(fd, &err);
149                 if( !gif_file ) {
150                         eprintf("FileGIF::wopen_path %d: %s\n", __LINE__, GifErrorString(err));
151                         result = 1;
152                 }
153         }
154         if( !result ) {
155                 writes = 0;
156         }
157         return result;
158 }
159
160 int FileGIF::write_frames(VFrame ***frames, int len)
161 {
162         int result = !gif_file ? 1 : 0;
163         for( int i=0; i<len && !result; ++i )
164                 result = write_frame(frames[0][i]);
165         return result;
166 }
167
168 int FileGIF::open_gif()
169 {
170         file_pos.remove_all();
171         int width = asset->width;
172         int height = asset->height;
173         int result = read_frame_header(asset->path);
174         if( !result ) {
175                 asset->actual_width = asset->width;
176                 if( width ) asset->width = width;
177                 asset->actual_height = asset->height;
178                 if( height ) asset->height = height;
179                 asset->layers = 1;
180                 if( !asset->frame_rate )
181                         asset->frame_rate = 10;
182                 asset->video_data = 1;
183                 row_size = gif_file->SWidth * sizeof(GifPixelType);
184                 bg = (GifRowType)malloc(row_size);
185                 for( int i=0; i<gif_file->SWidth; ++i )
186                         bg[i] = gif_file->SBackGroundColor;
187                 rows = gif_file->SHeight;
188                 buffer = (GifRowType*)malloc(sizeof(GifRowType) * rows);
189                 for( int i=0; i<gif_file->SHeight; ++i ) {
190                         buffer[i] = (GifRowType)malloc(row_size);
191                         memcpy(buffer[i], bg, row_size);
192                 }
193                 result = scan_gif();
194                 asset->video_length = file_pos.size();
195         }
196         return result;
197 }
198
199 int FileGIF::close_file()
200 {
201         if( gif_file ) {
202                 EGifCloseFile(gif_file, &err);
203                 gif_file = 0;
204         }
205         if( fp ) {
206                 fclose(fp);  fp = 0;
207         }
208         if( fd >= 0 ) {
209                 close(fd);  fd = -1;
210         }
211         if( bg ) { free(bg);  bg = 0; }
212         if( buffer ) {
213                 for( int k=0; k<rows; ++k )
214                         free(buffer[k]);
215                 free(buffer);  buffer = 0;
216                 rows = 0;
217         }
218         offset = 0;
219         row_size = 0;
220         err = 0;
221         writes = -1;
222         eof = 1;
223         output = 0;
224         FileBase::close_file();
225         return 0;
226 }
227
228 int FileGIF::scan_gif()
229 {
230         int file_eof = eof;
231         int64_t file_offset = offset;
232         file_pos.remove_all();
233         int image_pos = offset, ret;
234 // read all imgs, build file_pos index
235         while( (ret=read_next_image(0)) > 0 ) {
236                 file_pos.append(image_pos);
237                 image_pos = offset;
238         }
239         eof = file_eof;
240         offset = file_offset;
241         return ret;
242 }
243
244 int FileGIF::set_video_position(int64_t pos)
245 {
246         if( !gif_file || !asset->video_length ) return 1;
247         int64_t sz = file_pos.size();
248         eof = pos < 0 || pos >= sz ? 1 : 0;
249         offset = !eof ? file_pos[pos] : 0;
250         return 0;
251 }
252
253 int FileGIF::read_frame(VFrame *output)
254 {
255         if( !gif_file ) return 1;
256         for( int i=0; i<gif_file->SHeight; ++i )
257                 memcpy(buffer[i], bg, row_size);
258         int ret = read_next_image(output) > 0 ? 0 : 1;
259         return ret;
260 }
261
262 // ret = -1:err, 0:eof, 1:frame
263 int FileGIF::read_next_image(VFrame *output)
264 {
265         int ret = 0;
266         GifRecordType record_type;
267
268         while( !ret && !eof ) {
269                 if( DGifGetRecordType(gif_file, &record_type) == GIF_ERROR ) {
270                         err = gif_file->Error;
271                         eprintf("FileGIF::read_frame %d: %s\n", __LINE__, GifErrorString(err));
272                         ret = -1;
273                         break;
274                 }
275
276                 switch( record_type ) {
277                 case IMAGE_DESC_RECORD_TYPE: {
278                         if( DGifGetImageDesc(gif_file) == GIF_ERROR ) {
279                                 err = gif_file->Error;
280                                 eprintf("FileGIF::read_frame %d: %s\n", __LINE__, GifErrorString(err));
281                                 break;
282                         }
283                         int row = gif_file->Image.Top;
284                         int col = gif_file->Image.Left;
285                         int width = gif_file->Image.Width;
286                         int height = gif_file->Image.Height;
287                         if( gif_file->Image.Left + gif_file->Image.Width > gif_file->SWidth ||
288                             gif_file->Image.Top + gif_file->Image.Height > gif_file->SHeight )
289                                 ret = -1;
290                         if( !ret && gif_file->Image.Interlace ) {
291                                 static int InterlacedOffset[] = { 0, 4, 2, 1 };
292                                 static int InterlacedJumps[] = { 8, 8, 4, 2 };
293 /* Need to perform 4 passes on the images: */
294                                 for( int i=0; i<4; ++i ) {
295                                         int j = row + InterlacedOffset[i];
296                                         for( ; !ret && j<row + height; j+=InterlacedJumps[i] ) {
297                                                 if( DGifGetLine(gif_file, &buffer[j][col], width) == GIF_ERROR )
298                                                         ret = -1;
299                                         }
300                                 }
301                         }
302                         else {
303                                 for( int i=0; !ret && i<height; ++i ) {
304                                         if (DGifGetLine(gif_file, &buffer[row++][col], width) == GIF_ERROR)
305                                                 ret = -1;
306                                 }
307                         }
308                         ret = 1;
309                         break; }
310                 case EXTENSION_RECORD_TYPE: {
311                         int ExtFunction = 0;
312                         GifByteType *ExtData = 0;
313                         if( DGifGetExtension(gif_file, &ExtFunction, &ExtData) == GIF_ERROR )
314                                 ret = -1;
315                         while( !ret && ExtData ) {
316                                 if( DGifGetExtensionNext(gif_file, &ExtData) == GIF_ERROR )
317                                         ret = -1;
318                         }
319                         break; }
320                 case TERMINATE_RECORD_TYPE:
321                         eof = 1;
322                         break;
323                 default:
324                         ret = -1;
325                         break;
326                 }
327         }
328
329         ColorMapObject *color_map = 0;
330         if( ret > 0 ) {
331                 color_map = gif_file->Image.ColorMap;
332                 if( !color_map ) color_map = gif_file->SColorMap;
333                 if( !color_map ) ret = -1;
334         }
335         if( ret > 0 && output ) {
336                 int screen_width = gif_file->SWidth;
337                 int screen_height = gif_file->SHeight;
338                 for( int i=0; i<screen_height; ++i ) {
339                         GifRowType row = buffer[i];
340                         unsigned char *out_ptr = output->get_rows()[i];
341                         for( int j=0; j<screen_width; ++j ) {
342                                 GifColorType *color_map_entry = &color_map->Colors[row[j]];
343                                 *out_ptr++ = color_map_entry->Red;
344                                 *out_ptr++ = color_map_entry->Green;
345                                 *out_ptr++ = color_map_entry->Blue;
346                         }
347                 }
348         }
349         return ret;
350 }
351
352 int FileGIF::write_frame(VFrame *frame)
353 {
354         int w = frame->get_w(), h = frame->get_h();
355         ColorMapObject *cmap = 0;
356         int cmap_sz = depth >= 0 ? 1 << depth : 0;
357         int64_t len = w * h * sizeof(GifByteType);
358         GifByteType *bfr = (GifByteType *) malloc(len);
359         int result = !bfr ? 1 : 0;
360         if( !result ) {
361                 VFrame gbrp(w, h, BC_GBRP);
362                 gbrp.transfer_from(frame);
363                 if( !(cmap = GifMakeMapObject(cmap_sz, 0)) )
364                         result = 1;
365                 if( !result ) {
366                         GifByteType *gp = (GifByteType *)gbrp.get_r();
367                         GifByteType *bp = (GifByteType *)gbrp.get_g();
368                         GifByteType *rp = (GifByteType *)gbrp.get_b();
369                         if( GifQuantizeBuffer(w, h, &cmap_sz, rp, gp, bp,
370                                         bfr, cmap->Colors) == GIF_ERROR )
371                                 result = 1;
372                 }
373         }
374         if( !result && !writes &&
375             EGifPutScreenDesc(gif_file, w, h, depth, 0, 0) == GIF_ERROR )
376                 result = 1;
377         if( !result &&
378             EGifPutImageDesc(gif_file, 0, 0, w, h, 0, cmap) == GIF_ERROR )
379                 result = 1;
380
381         GifByteType *bp = bfr;
382         for( int y=0; !result && y<h; ++y ) {
383                 if( EGifPutLine(gif_file, bp, w) == GIF_ERROR )
384                         result = 1;
385                 bp += w;
386         }
387         GifFreeMapObject(cmap);
388         if( bfr ) free(bfr);
389         ++writes;
390         return result;
391 }
392
393 static int write_data(GifFileType *gif_file, const GifByteType *bfr, int bytes)
394 {
395         FileGIF *file = (FileGIF*)gif_file->UserData;
396         VFrame *output = file->output;
397         long size = output->get_compressed_size();
398         long alloc = output->get_compressed_allocated();
399         long len = size + bytes;
400         if( len > alloc )
401                 output->allocate_compressed_data(2*size + bytes);
402         unsigned char *data = output->get_data() + size;
403         memcpy(data, bfr, bytes);
404         output->set_compressed_size(len);
405         return bytes;
406 }
407
408 int FileGIF::wopen_data(VFrame *output)
409 {
410         int result = 0;
411         gif_file = EGifOpen(this, write_data, &err);
412         if( !gif_file ) {
413                 eprintf("FileGIF::wopen_data %d: %s\n", __LINE__, GifErrorString(err));
414                 result = 1;
415         }
416         if( !result ) {
417                 output->set_compressed_size(0);
418                 this->output = output;
419                 writes = 0;
420         }
421         return result;
422 }
423
424
425 FileGIFList::FileGIFList(Asset *asset, File *file)
426  : FileList(asset, file, "GIFLIST", ".gif", FILE_UNKNOWN, FILE_GIF_LIST)
427 {
428 }
429
430 FileGIFList::~FileGIFList()
431 {
432 }
433
434 int FileGIFList::check_sig(Asset *asset)
435 {
436         FILE *stream = fopen(asset->path, "rb");
437         if( stream ) {
438                 unsigned char test[16];
439                 int ret = fread(test, 16, 1, stream);
440                 fclose(stream);
441                 if( ret < 1 ) return 1;
442                 if( test[0] == 'G' && test[1] == 'I' && test[2] == 'F' &&
443                     test[3] == 'L' && test[4] == 'I' && test[5] == 'S' && test[6] == 'T')
444                         return 1;
445         }
446         return 0;
447 }
448
449 int FileGIFList::colormodel_supported(int colormodel) { return BC_RGB888; }
450 int FileGIFList::get_best_colormodel(Asset *asset, int driver) { return BC_RGB888; }
451
452 int FileGIFList::read_frame_header(char *path)
453 {
454         FILE *stream = fopen(path, "rb");
455         if( stream ) {
456                 unsigned char test[16];
457                 int ret = fread(test, 16, 1, stream);
458                 fclose(stream);
459                 if( ret < 1 ) return 1;
460                 asset->format = FILE_GIF_LIST;
461                 asset->width = test[6] | (test[7] << 8);
462                 asset->height = test[8] | (test[9] << 8);
463                 return 0;
464         }
465         perror(path);
466         return 1;
467 }
468
469 int FileGIFList::read_frame(VFrame *output, char *path)
470 {
471         Asset *asset = new Asset(path);
472         FileGIF gif(asset, file);
473         int ret = gif.ropen_path(path);
474         if( !ret )
475                 ret = gif.read_frame(output);
476         asset->remove_user();
477         return ret;
478 }
479
480 int FileGIFList::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
481 {
482         int native_cmodel = BC_RGB888;
483         if( frame->get_color_model() != native_cmodel ) {
484                 GIFUnit *gif_unit = (GIFUnit *)unit;
485                 if( !gif_unit->temp_frame ) gif_unit->temp_frame =
486                         new VFrame(frame->get_w(), frame->get_h(), native_cmodel);
487                 gif_unit->temp_frame->transfer_from(frame);
488                 frame = gif_unit->temp_frame;
489         }
490
491         FileGIF gif(asset, file);
492         int ret = gif.wopen_data(data);
493         if( !ret )
494                 ret = gif.write_frame(frame);
495         return ret;
496 }
497
498 FrameWriterUnit* FileGIFList::new_writer_unit(FrameWriter *writer)
499 {
500         return new GIFUnit(this, writer);
501 }
502
503 GIFUnit::GIFUnit(FileGIFList *file, FrameWriter *writer)
504  : FrameWriterUnit(writer)
505 {
506         this->file = file;
507         temp_frame = 0;
508 }
509
510 GIFUnit::~GIFUnit()
511 {
512         delete temp_frame;
513 }
514