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