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