gif rework, C41 booby fix, add ext+s for list seq, features5
authorGood Guy <good1.2guy@gmail.com>
Thu, 28 Feb 2019 01:51:29 +0000 (18:51 -0700)
committerGood Guy <good1.2guy@gmail.com>
Thu, 28 Feb 2019 01:51:29 +0000 (18:51 -0700)
12 files changed:
cinelerra-5.1/cinelerra/file.C
cinelerra-5.1/cinelerra/file.inc
cinelerra-5.1/cinelerra/filegif.C
cinelerra-5.1/cinelerra/filegif.h
cinelerra-5.1/cinelerra/filelist.C
cinelerra-5.1/cinelerra/formatpopup.C
cinelerra-5.1/cinelerra/formattools.C
cinelerra-5.1/doc/Features5.pdf
cinelerra-5.1/msg/txt
cinelerra-5.1/plugins/C41/c41.C
cinelerra-5.1/plugins/C41/c41.h
cinelerra-5.1/thirdparty/src/giflib-5.1.6.patch2 [new file with mode: 0644]

index c538428..f611b81 100644 (file)
@@ -415,8 +415,11 @@ int File::probe()
                        return FILE_OK;
                }
                if( !strcmp(pref->name,"GIF") ) { // GIF file
-                       if( !FileGIF::check_sig(this->asset)) continue;
-                       file = new FileGIF(this->asset, this);
+                       if( FileGIFList::check_sig(this->asset) )
+                               file = new FileGIFList(this->asset, this);
+                       else if( FileGIF::check_sig(this->asset) )
+                               file = new FileGIF(this->asset, this);
+                       else continue;
                        return FILE_OK;
                }
 #ifdef HAVE_EXR
@@ -526,9 +529,12 @@ int File::open_file(Preferences *preferences,
                break;
 
        case FILE_GIF:
-       case FILE_GIF_LIST:
                file = new FileGIF(this->asset, this);
                break;
+       case FILE_GIF_LIST:
+               file = new FileGIFList(this->asset, this);
+               break;
+
 #ifdef HAVE_OPENEXR
        case FILE_EXR:
        case FILE_EXR_LIST:
@@ -1149,6 +1155,7 @@ int File::read_frame(VFrame *frame, int is_thread)
 
                if( !temp_frame ) {
                        temp_frame = new VFrame(asset->width, asset->height, supported_colormodel, 0);
+                       temp_frame->clear_frame();
                }
 
 //                     printf("File::read_frame %d\n", __LINE__);
@@ -1215,6 +1222,8 @@ int File::strtoformat(const char *format)
        if( !strcasecmp(format, _(EXR_NAME)) ) return FILE_EXR;
        if( !strcasecmp(format, _(EXR_LIST_NAME)) ) return FILE_EXR_LIST;
        if( !strcasecmp(format, _(FLAC_NAME)) ) return FILE_FLAC;
+       if( !strcasecmp(format, _(GIF_NAME)) ) return FILE_GIF;
+       if( !strcasecmp(format, _(GIF_LIST_NAME)) ) return FILE_GIF_LIST;
        if( !strcasecmp(format, _(CR2_NAME)) ) return FILE_CR2;
        if( !strcasecmp(format, _(CR2_LIST_NAME)) ) return FILE_CR2_LIST;
        if( !strcasecmp(format, _(MPEG_NAME)) ) return FILE_MPEG;
@@ -1249,6 +1258,8 @@ const char* File::formattostr(int format)
        case FILE_CR2:          return _(CR2_NAME);
        case FILE_CR2_LIST:     return _(CR2_LIST_NAME);
        case FILE_FLAC:         return _(FLAC_NAME);
+       case FILE_GIF:          return _(GIF_NAME);
+       case FILE_GIF_LIST:     return _(GIF_LIST_NAME);
        case FILE_EXR:          return _(EXR_NAME);
        case FILE_EXR_LIST:     return _(EXR_LIST_NAME);
 #ifdef HAVE_LIBZMPEG
@@ -1397,6 +1408,8 @@ int File::renders_video(int format)
        case FILE_CR2_LIST:
        case FILE_EXR:
        case FILE_EXR_LIST:
+       case FILE_GIF:
+       case FILE_GIF_LIST:
        case FILE_PNG:
        case FILE_PNG_LIST:
        case FILE_PPM:
@@ -1468,19 +1481,21 @@ const char* File::get_tag(int format)
        case FILE_RAWDV:        return "dv";
        case FILE_DB:           return "db";
        case FILE_EXR:          return "exr";
-       case FILE_EXR_LIST:     return "exr";
+       case FILE_EXR_LIST:     return "exrs";
        case FILE_FLAC:         return "flac";
        case FILE_JPEG:         return "jpg";
-       case FILE_JPEG_LIST:    return "jpg";
+       case FILE_JPEG_LIST:    return "jpgs";
+       case FILE_GIF:          return "gif";
+       case FILE_GIF_LIST:     return "gifs";
        case FILE_PCM:          return "pcm";
        case FILE_PNG:          return "png";
-       case FILE_PNG_LIST:     return "png";
+       case FILE_PNG_LIST:     return "pngs";
        case FILE_PPM:          return "ppm";
-       case FILE_PPM_LIST:     return "ppm";
+       case FILE_PPM_LIST:     return "ppms";
        case FILE_TGA:          return "tga";
-       case FILE_TGA_LIST:     return "tga";
+       case FILE_TGA_LIST:     return "tgas";
        case FILE_TIFF:         return "tif";
-       case FILE_TIFF_LIST:    return "tif";
+       case FILE_TIFF_LIST:    return "tifs";
        case FILE_VMPEG:        return "m2v";
        case FILE_WAV:          return "wav";
        case FILE_FFMPEG:       return "ffmpg";
index 856c4d5..3d5f07a 100644 (file)
@@ -104,6 +104,8 @@ N_("EXR")
 N_("EXR Sequence")
 N_("FFMPEG")
 N_("FLAC")
+N_("GIF")
+N_("GIF Sequence")
 N_("JPEG")
 N_("JPEG Sequence")
 N_("Microsoft WAV")
@@ -139,6 +141,8 @@ N_("Unknown sound")
 #define EXR_NAME               "EXR"
 #define FFMPEG_NAME            "FFMPEG"
 #define FLAC_NAME              "FLAC"
+#define GIF_NAME               "GIF"
+#define GIF_LIST_NAME          "GIF Sequence"
 #define JPEG_LIST_NAME         "JPEG Sequence"
 #define JPEG_NAME              "JPEG"
 #define MPEG_NAME              "MPEG Stream"   // For capture only
index e2f14c3..73c3d8e 100644 (file)
 #include "mainerror.h"
 #include "interlacemodes.h"
 #include "vframe.h"
-#include <string.h>
-
 
-static int gif_err = 0;
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
 
 FileGIF::FileGIF(Asset *asset, File *file)
- : FileList(asset, file, "GIFLIST", ".gif", FILE_GIF, FILE_GIF_LIST)
+ : FileBase(asset, file)
 {
+       offset = 0;
+       err = 0;
+       gif_file = 0;
+       eof = 1;
+       row_size = 0;
+       rows = 0;
+       depth = 8;
+       fp = 0;
+       fd = -1;
+       writes = -1;
+       buffer = 0;
+       bg = 0;
+       output = 0;
 }
 
 FileGIF::~FileGIF()
 {
+       close_file();
 }
 
 int FileGIF::check_sig(Asset *asset)
 {
        FILE *stream = fopen(asset->path, "rb");
-
-       if(stream)
-       {
+       if( stream ) {
                char test[8];
                int ret = fread(test, 1, 6, stream);
                fclose(stream);
-
                if( ret >= 6 &&
-                       test[0] == 'G' && test[1] == 'I' && test[2] == 'F' &&
-                       test[3] == '8' && test[4] == '7' && test[5] == 'A')
-               {
-                       eprintf("FileGIFF: version error (87A): \"%s\".\n", asset->path);
-                       return 1;
-               }
-       }
-
-       if(strlen(asset->path) > 4)
-       {
-               int len = strlen(asset->path);
-               if(!strncasecmp(asset->path + len - 4, ".gif", 4)) return 1;
+                   test[0] == 'G' && test[1] == 'I' && test[2] == 'F' &&
+                   test[3] == '8' && (test[4] == '7' || test[4] == '9') &&
+                   test[5] == 'a' ) return 1;
        }
        return 0;
 }
@@ -82,16 +85,14 @@ int FileGIF::get_best_colormodel(Asset *asset, int driver)
 int FileGIF::read_frame_header(char *path)
 {
        FILE *stream = fopen(path, "rb");
-
-       if(stream)
-       {
+       if( stream ) {
                unsigned char test[16];
                int ret = fread(test, 16, 1, stream);
                fclose(stream);
                if( ret < 1 ) return 1;
+               asset->format = FILE_GIF;
                asset->width = test[6] | (test[7] << 8);
                asset->height = test[8] | (test[9] << 8);
-//printf("FileGIF::read_frame_header %d %d %d\n", __LINE__, asset->width, asset->height);
                return 0;
        }
 
@@ -99,65 +100,191 @@ int FileGIF::read_frame_header(char *path)
        return 1;
 }
 
-
-static int input_func(GifFileType *gif_file, GifByteType *buffer, int bytes)
+static int input_file(GifFileType *gif_file, GifByteType *buffer, int bytes)
 {
        FileGIF *file = (FileGIF*)gif_file->UserData;
-       if( file->offset + bytes > file->size )
-               bytes = file->size - file->offset;
-       if( bytes > 0 ) {
-               memcpy(buffer, file->data + file->offset, bytes);
-               file->offset += bytes;
-       }
+       fseek(file->fp, file->offset, SEEK_SET);
+       bytes = fread(buffer, 1, bytes, file->fp);
+       file->offset += bytes;
        return bytes;
 }
 
-int FileGIF::read_frame(VFrame *output, VFrame *input)
+int FileGIF::open_file(int rd, int wr)
 {
-       data = input->get_data();
-       offset = 0;
-       size = input->get_compressed_size();
+       return  rd ? ropen_path(asset->path) :
+               wr ? wopen_path(asset->path) :
+               0 ;
+}
 
-       GifFileType *gif_file = DGifOpen(this, input_func, &gif_err);
-       if( !gif_file ) {
-               eprintf("FileGIF::read_frame %d: %s\n", __LINE__, GifErrorString(gif_err));
-               return 1;
+int FileGIF::ropen_path(const char *path)
+{
+       fp = fopen(path, "r");
+       int result = !fp ? 1 : 0;
+       if( !result ) {
+               offset = 0;  eof = 0;
+               gif_file = DGifOpen(this, input_file, &err);
+               if( !gif_file ) {
+                       eprintf("FileGIF::ropen_path %d: %s\n", __LINE__, GifErrorString(err));
+                       result = 1;
+               }
+       }
+       if( !result )
+               result = open_gif();
+       return result;
+}
+
+int FileGIF::wopen_path(const char *path)
+{
+       fd = open(path, O_CREAT+O_TRUNC+O_WRONLY, 0777);
+       int result = fd < 0 ? 1 : 0;
+       if( !result ) {
+               gif_file = EGifOpenFileHandle(fd, &err);
+               if( !gif_file ) {
+                       eprintf("FileGIF::wopen_path %d: %s\n", __LINE__, GifErrorString(err));
+                       result = 1;
+               }
+       }
+       if( !result ) {
+               writes = 0;
+       }
+       return result;
+}
+
+int FileGIF::write_frames(VFrame ***frames, int len)
+{
+       int result = !gif_file ? 1 : 0;
+       for( int i=0; i<len && !result; ++i )
+               result = write_frame(frames[0][i]);
+       return result;
+}
+
+int FileGIF::open_gif()
+{
+       file_pos.remove_all();
+       int width = asset->width;
+       int height = asset->height;
+       int result = read_frame_header(asset->path);
+       if( !result ) {
+               asset->actual_width = asset->width;
+               if( width ) asset->width = width;
+               asset->actual_height = asset->height;
+               if( height ) asset->height = height;
+               asset->layers = 1;
+               if( !asset->frame_rate )
+                       asset->frame_rate = 1;
+               asset->video_data = 1;
+               row_size = gif_file->SWidth * sizeof(GifPixelType);
+               bg = (GifRowType)malloc(row_size);
+               for( int i=0; i<gif_file->SWidth; ++i )
+                       bg[i] = gif_file->SBackGroundColor;
+               rows = gif_file->SHeight;
+               buffer = (GifRowType*)malloc(sizeof(GifRowType) * rows);
+               for( int i=0; i<gif_file->SHeight; ++i ) {
+                       buffer[i] = (GifRowType)malloc(row_size);
+                       memcpy(buffer[i], bg, row_size);
+               }
+               result = scan_gif();
+               asset->video_length = file_pos.size();
        }
+       if( !result ) {
+               asset->video_data = 1;
+               if( !asset->frame_rate )
+                       asset->frame_rate = 10;
+       }
+       return result;
+}
 
-       GifRowType *gif_buffer = (GifRowType*)malloc(sizeof(GifRowType) * gif_file->SHeight);
-       int row_size = gif_file->SWidth * sizeof(GifPixelType);
-       gif_buffer[0] = (GifRowType)malloc(row_size);
+int FileGIF::close_file()
+{
+       if( gif_file ) {
+               EGifCloseFile(gif_file, &err);
+               gif_file = 0;
+       }
+       if( fp ) {
+               fclose(fp);  fp = 0;
+       }
+       if( fd >= 0 ) {
+               close(fd);  fd = -1;
+       }
+       if( bg ) { free(bg);  bg = 0; }
+       if( buffer ) {
+               for( int k=0; k<rows; ++k )
+                       free(buffer[k]);
+               free(buffer);  buffer = 0;
+               rows = 0;
+       }
+       offset = 0;
+       row_size = 0;
+       err = 0;
+       writes = -1;
+       eof = 1;
+       output = 0;
+       FileBase::close_file();
+       return 0;
+}
 
-       for( int i=0; i<gif_file->SWidth; ++i )
-               gif_buffer[0][i] = gif_file->SBackGroundColor;
-       for( int i=0; i<gif_file->SHeight; ++i ) {
-               gif_buffer[i] = (GifRowType)malloc(row_size);
-               memcpy(gif_buffer[i], gif_buffer[0], row_size);
+int FileGIF::scan_gif()
+{
+       int file_eof = eof;
+       int64_t file_offset = offset;
+       file_pos.remove_all();
+       int image_pos = offset, ret;
+// read all imgs, build file_pos index
+       while( (ret=read_next_image(0)) > 0 ) {
+               file_pos.append(image_pos);
+               image_pos = offset;
        }
+       eof = file_eof;
+       offset = file_offset;
+       return ret;
+}
+
+int FileGIF::set_video_position(int64_t pos)
+{
+       if( !gif_file || !asset->video_length ) return 1;
+       int64_t sz = file_pos.size();
+       eof = pos < 0 || pos >= sz ? 1 : 0;
+       offset = !eof ? file_pos[pos] : 0;
+       return 0;
+}
+
+int FileGIF::read_frame(VFrame *output)
+{
+       if( !gif_file ) return 1;
+       for( int i=0; i<gif_file->SHeight; ++i )
+               memcpy(buffer[i], bg, row_size);
+       int ret = read_next_image(output) > 0 ? 0 : 1;
+       return ret;
+}
 
-       int ret = 0, done = 0;
+// ret = -1:err, 0:eof, 1:frame
+int FileGIF::read_next_image(VFrame *output)
+{
+       int ret = 0;
        GifRecordType record_type;
-       while( !ret && !done ) {
+
+       while( !ret && !eof ) {
                if( DGifGetRecordType(gif_file, &record_type) == GIF_ERROR ) {
-                       eprintf("FileGIF::read_frame %d: %s\n", __LINE__, GifErrorString(gif_err));
-                       ret = 1;
+                       err = gif_file->Error;
+                       eprintf("FileGIF::read_frame %d: %s\n", __LINE__, GifErrorString(err));
+                       ret = -1;
                        break;
                }
 
                switch( record_type ) {
                case IMAGE_DESC_RECORD_TYPE: {
                        if( DGifGetImageDesc(gif_file) == GIF_ERROR ) {
-                               eprintf("FileGIF::read_frame %d: %s\n", __LINE__, GifErrorString(gif_err));
+                               err = gif_file->Error;
+                               eprintf("FileGIF::read_frame %d: %s\n", __LINE__, GifErrorString(err));
                                break;
                        }
                        int row = gif_file->Image.Top;
                        int col = gif_file->Image.Left;
                        int width = gif_file->Image.Width;
                        int height = gif_file->Image.Height;
-                       int ret = 0;
                        if( gif_file->Image.Left + gif_file->Image.Width > gif_file->SWidth ||
                            gif_file->Image.Top + gif_file->Image.Height > gif_file->SHeight )
-                               ret = 1;
+                               ret = -1;
                        if( !ret && gif_file->Image.Interlace ) {
                                static int InterlacedOffset[] = { 0, 4, 2, 1 };
                                static int InterlacedJumps[] = { 8, 8, 4, 2 };
@@ -165,61 +292,221 @@ int FileGIF::read_frame(VFrame *output, VFrame *input)
                                for( int i=0; i<4; ++i ) {
                                        int j = row + InterlacedOffset[i];
                                        for( ; !ret && j<row + height; j+=InterlacedJumps[i] ) {
-                                               if( DGifGetLine(gif_file, &gif_buffer[j][col], width) == GIF_ERROR )
-                                                       ret = 1;
+                                               if( DGifGetLine(gif_file, &buffer[j][col], width) == GIF_ERROR )
+                                                       ret = -1;
                                        }
                                }
                        }
                        else {
-                               for( int i=0; !ret && i<height; ++i ) {
-                                       if (DGifGetLine(gif_file, &gif_buffer[row++][col], width) == GIF_ERROR)
-                                               ret = 1;
+                               for( int i=0; !ret && i<height; ++i ) {
+                                       if (DGifGetLine(gif_file, &buffer[row++][col], width) == GIF_ERROR)
+                                               ret = -1;
                                }
                        }
+                       ret = 1;
                        break; }
                case EXTENSION_RECORD_TYPE: {
                        int ExtFunction = 0;
                        GifByteType *ExtData = 0;
                        if( DGifGetExtension(gif_file, &ExtFunction, &ExtData) == GIF_ERROR )
-                               ret = 1;
+                               ret = -1;
                        while( !ret && ExtData ) {
                                if( DGifGetExtensionNext(gif_file, &ExtData) == GIF_ERROR )
-                                       ret = 1;
+                                       ret = -1;
                        }
                        break; }
                case TERMINATE_RECORD_TYPE:
-                       done = 1;
+                       eof = 1;
                        break;
                default:
+                       ret = -1;
                        break;
                }
        }
 
        ColorMapObject *color_map = 0;
-       if( !ret ) {
-               //int background = gif_file->SBackGroundColor;
+       if( ret > 0 ) {
                color_map = gif_file->Image.ColorMap;
                if( !color_map ) color_map = gif_file->SColorMap;
-               if( !color_map ) ret = 1;
+               if( !color_map ) ret = -1;
        }
-       if( !ret ) {
+       if( ret > 0 && output ) {
                int screen_width = gif_file->SWidth;
                int screen_height = gif_file->SHeight;
                for( int i=0; i<screen_height; ++i ) {
-                       GifRowType gif_row = gif_buffer[i];
+                       GifRowType row = buffer[i];
                        unsigned char *out_ptr = output->get_rows()[i];
                        for( int j=0; j<screen_width; ++j ) {
-                               GifColorType *color_map_entry = &color_map->Colors[gif_row[j]];
+                               GifColorType *color_map_entry = &color_map->Colors[row[j]];
                                *out_ptr++ = color_map_entry->Red;
                                *out_ptr++ = color_map_entry->Green;
                                *out_ptr++ = color_map_entry->Blue;
                        }
                }
        }
-       for( int k=0; k<gif_file->SHeight; ++k )
-               free(gif_buffer[k]);
-       free(gif_buffer);
-       DGifCloseFile(gif_file, &gif_err);
        return ret;
 }
 
+int FileGIF::write_frame(VFrame *frame)
+{
+       int w = frame->get_w(), h = frame->get_h();
+       ColorMapObject *cmap = 0;
+       int cmap_sz = depth >= 0 ? 1 << depth : 0;
+       int64_t len = w * h * sizeof(GifByteType);
+       GifByteType *bfr = (GifByteType *) malloc(len);
+       int result = !bfr ? 1 : 0;
+       if( !result ) {
+               VFrame gbrp(w, h, BC_GBRP);
+               gbrp.transfer_from(frame);
+               if( !(cmap = GifMakeMapObject(cmap_sz, 0)) )
+                       result = 1;
+               if( !result ) {
+                       GifByteType *gp = (GifByteType *)gbrp.get_r();
+                       GifByteType *bp = (GifByteType *)gbrp.get_g();
+                       GifByteType *rp = (GifByteType *)gbrp.get_b();
+                       if( GifQuantizeBuffer(w, h, &cmap_sz, rp, gp, bp,
+                                       bfr, cmap->Colors) == GIF_ERROR )
+                               result = 1;
+               }
+       }
+       if( !result && !writes &&
+           EGifPutScreenDesc(gif_file, w, h, depth, 0, 0) == GIF_ERROR )
+               result = 1;
+       if( !result &&
+           EGifPutImageDesc(gif_file, 0, 0, w, h, 0, cmap) == GIF_ERROR )
+               result = 1;
+
+       GifByteType *bp = bfr;
+       for( int y=0; !result && y<h; ++y ) {
+               if( EGifPutLine(gif_file, bp, w) == GIF_ERROR )
+                       result = 1;
+               bp += w;
+       }
+       GifFreeMapObject(cmap);
+       if( bfr ) free(bfr);
+       ++writes;
+       return result;
+}
+
+static int write_data(GifFileType *gif_file, const GifByteType *bfr, int bytes)
+{
+       FileGIF *file = (FileGIF*)gif_file->UserData;
+       VFrame *output = file->output;
+       long size = output->get_compressed_size();
+       long alloc = output->get_compressed_allocated();
+       long len = size + bytes;
+       if( len > alloc )
+               output->allocate_compressed_data(2*size + bytes);
+       unsigned char *data = output->get_data() + size;
+       memcpy(data, bfr, bytes);
+       output->set_compressed_size(len);
+       return bytes;
+}
+
+int FileGIF::wopen_data(VFrame *output)
+{
+       int result = 0;
+       gif_file = EGifOpen(this, write_data, &err);
+       if( !gif_file ) {
+               eprintf("FileGIF::wopen_data %d: %s\n", __LINE__, GifErrorString(err));
+               result = 1;
+       }
+       if( !result ) {
+               output->set_compressed_size(0);
+               this->output = output;
+               writes = 0;
+       }
+       return result;
+}
+
+
+FileGIFList::FileGIFList(Asset *asset, File *file)
+ : FileList(asset, file, "GIFLIST", ".gif", FILE_UNKNOWN, FILE_GIF_LIST)
+{
+}
+
+FileGIFList::~FileGIFList()
+{
+}
+
+int FileGIFList::check_sig(Asset *asset)
+{
+       FILE *stream = fopen(asset->path, "rb");
+       if( stream ) {
+               unsigned char test[16];
+               int ret = fread(test, 16, 1, stream);
+               fclose(stream);
+               if( ret < 1 ) return 1;
+               if( test[0] == 'G' && test[1] == 'I' && test[2] == 'F' &&
+                   test[3] == 'L' && test[4] == 'I' && test[5] == 'S' && test[6] == 'T')
+                       return 1;
+       }
+       return 0;
+}
+
+int FileGIFList::colormodel_supported(int colormodel) { return BC_RGB888; }
+int FileGIFList::get_best_colormodel(Asset *asset, int driver) { return BC_RGB888; }
+
+int FileGIFList::read_frame_header(char *path)
+{
+       FILE *stream = fopen(path, "rb");
+       if( stream ) {
+               unsigned char test[16];
+               int ret = fread(test, 16, 1, stream);
+               fclose(stream);
+               if( ret < 1 ) return 1;
+               asset->format = FILE_GIF_LIST;
+               asset->width = test[6] | (test[7] << 8);
+               asset->height = test[8] | (test[9] << 8);
+               return 0;
+       }
+       perror(path);
+       return 1;
+}
+
+int FileGIFList::read_frame(VFrame *output, char *path)
+{
+       Asset *asset = new Asset(path);
+       FileGIF gif(asset, file);
+       int ret = gif.ropen_path(path);
+       if( !ret )
+               ret = gif.read_frame(output);
+       asset->remove_user();
+       return ret;
+}
+
+int FileGIFList::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
+{
+       int native_cmodel = BC_RGB888;
+       if( frame->get_color_model() != native_cmodel ) {
+               GIFUnit *gif_unit = (GIFUnit *)unit;
+               if( !gif_unit->temp_frame ) gif_unit->temp_frame =
+                       new VFrame(frame->get_w(), frame->get_h(), native_cmodel);
+               gif_unit->temp_frame->transfer_from(frame);
+               frame = gif_unit->temp_frame;
+       }
+
+       FileGIF gif(asset, file);
+       int ret = gif.wopen_data(data);
+       if( !ret )
+               ret = gif.write_frame(frame);
+       return ret;
+}
+
+FrameWriterUnit* FileGIFList::new_writer_unit(FrameWriter *writer)
+{
+       return new GIFUnit(this, writer);
+}
+
+GIFUnit::GIFUnit(FileGIFList *file, FrameWriter *writer)
+ : FrameWriterUnit(writer)
+{
+       this->file = file;
+       temp_frame = 0;
+}
+
+GIFUnit::~GIFUnit()
+{
+       delete temp_frame;
+}
+
index da1db59..74cae2a 100644 (file)
 #ifndef FILEGIF_H
 #define FILEGIF_H
 
+#include "arraylist.h"
 #include "file.inc"
 #include "filelist.h"
 #include "vframe.inc"
 
+#include "gif_lib.h"
+
 // This header file is representative of any single frame file format.
 
-class FileGIF : public FileList
+class FileGIF : public FileBase
 {
 public:
        FileGIF(Asset *asset, File *file);
@@ -37,14 +40,57 @@ public:
        static int get_best_colormodel(Asset *asset, int driver);
        static int check_sig(Asset *asset);
        int colormodel_supported(int colormodel);
+       int open_file(int rd, int wr);
+       int ropen_path(const char *path);
+       int wopen_path(const char *path);
+       int wopen_data(VFrame *frame);
+       int open_gif();
+       int close_file();
+       int set_video_position(int64_t pos);
 
        int read_frame_header(char *path);
-       int read_frame(VFrame *output, VFrame *input);
+       int read_frame(VFrame *output);
+       int read_next_image(VFrame *output);
+       int scan_gif();
+       int write_frames(VFrame ***frames, int len);
+       int write_frame(VFrame *frame);
 
-       unsigned char *data;
-       int offset;
-       int size;
+       int64_t offset;
+       int err, eof;
+       int fd, depth, writes;
+       int rows, row_size;
+       FILE *fp;
+       GifFileType *gif_file;
+       GifPixelType *bg;
+       GifRowType *buffer;
+       ArrayList<int64_t> file_pos;
+       VFrame *output;
 };
 
+class FileGIFList : public FileList
+{
+public:
+       FileGIFList(Asset *asset, File *file);
+       ~FileGIFList();
+
+       static int check_sig(Asset *asset);
+       int colormodel_supported(int colormodel);
+       int get_best_colormodel(Asset *asset, int driver);
+       int read_frame_header(char *path);
+       int use_path() { return 1; }
+       int read_frame(VFrame *output, char *path);
+       int write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit);
+       FrameWriterUnit* new_writer_unit(FrameWriter *writer);
+};
+
+class GIFUnit : public FrameWriterUnit
+{
+public:
+       GIFUnit(FileGIFList *file, FrameWriter *writer);
+       ~GIFUnit();
+
+       FileGIFList *file;
+       VFrame *temp_frame;
+};
 
 #endif
index d04ee5a..ee287bc 100644 (file)
@@ -113,28 +113,26 @@ int FileList::open_file(int rd, int wr)
                        if(stream)
                        {
                                char string[BCTEXTLEN];
-                               (void)fread(string, strlen(list_prefix), 1, stream);
+                               int len = strlen(list_prefix);
+                               int ret = fread(string, 1, strlen(list_prefix), stream);
                                fclose(stream);
-
-                               if(!strncasecmp(string, list_prefix, strlen(list_prefix)))
-                               {
-
-//printf("FileList::open_file %d\n", __LINE__);
+                               result = len == ret ? 0 : 1;
+                               if( !result && !strncasecmp(string, list_prefix, len)) {
                                        asset->format = list_type;
-
-// Open index here or get frame size from file.
                                        result = read_list_header();
-//printf("FileList::open_file %d %s\n", __LINE__, path_list.values[0]);
-                                       if(!result) result = read_frame_header(path_list.values[0]);
+                                       if( !result )
+                                               result = read_frame_header(path_list.values[0]);
                                }
-                               else
-                               {
-//printf("FileList::open_file 2\n", asset->use_header);
-//printf("FileList::open_file %d\n", __LINE__);
+                               else if( !result && frame_type != FILE_UNKNOWN ) {
                                        asset->format = frame_type;
+                                       result = read_frame_header(asset->path);
+                                       asset->video_length = -1;
+                               }
+                               else
+                                       result = 1;
+                               if( !result ) {
                                        int width = asset->width;
                                        int height = asset->height;
-                                       result = read_frame_header(asset->path);
                                        asset->actual_width = asset->width;
                                        if( width ) asset->width = width;
                                        asset->actual_height = asset->height;
@@ -142,9 +140,7 @@ int FileList::open_file(int rd, int wr)
                                        asset->layers = 1;
                                        if( !asset->frame_rate )
                                                asset->frame_rate = 1;
-                                       asset->video_length = -1;
                                }
-                               result = 0;
                        }
                        else
                                eprintf(_("Error while opening \"%s\" for reading. \n%m\n"), asset->path);
index 66f1e72..31a8bd0 100644 (file)
@@ -59,7 +59,10 @@ void FormatPopup::create_objects()
                post_item(FILE_JPEG);
        post_item(FILE_JPEG_LIST);
 
+
        if(!use_brender) {
+               post_item(FILE_GIF);
+               post_item(FILE_GIF_LIST);
 #ifdef HAVE_OPENEXR
                post_item(FILE_EXR);
                post_item(FILE_EXR_LIST);
index 312f39c..dc189a0 100644 (file)
@@ -375,9 +375,8 @@ void FormatTools::update_extension()
                                extension_ptr++;
                        }
 
-                       if(*ptr1 == 0 &&
-                               *extension_ptr != 0 &&
-                               *extension_ptr != '/')
+                       if( (!*ptr1 && (*extension_ptr && *extension_ptr != '/')) ||
+                           (*ptr1 && (!*extension_ptr || *extension_ptr == '/')) )
                                need_extension = 1;
                }
 
index addeae3..4228741 100644 (file)
Binary files a/cinelerra-5.1/doc/Features5.pdf and b/cinelerra-5.1/doc/Features5.pdf differ
index 8def3c2..7ec204e 100644 (file)
@@ -12,6 +12,11 @@ Cinfinity icons selected in Preferences Sam (CC BY 3.0,
 Neophyte theme selected in Preferences by Olaf Wolff (CC BY 4.0,
   https://creativecommons.org/licenses/by/4.0/)
 .
+February 2019 New Features of note:
+  AV1 and WebP format usage is now included.
+  ShuttlePro/Xpress stabilization/setup/definition is complete.
+  Transitions now allow for length in frames, time and seconds.
+  Gif formatted files have had major improvements added.
 January 2019 New Features of note:
   Capability for Shuttle usage has been added.
   Drag Handle management includes Slide and Edge too.
index b8a6cb5..e56c3c0 100644 (file)
@@ -227,8 +227,12 @@ C41Window::C41Window(C41Effect *plugin)
        plugin->config.window_w, plugin->config.window_h,
        500, 510, 1)
 {
-       int x = 10, y = 10;
+}
 
+void C41Window::create_objects()
+{
+       int x = 10, y = 10;
+       C41Effect *plugin = (C41Effect *)client;
        add_subwindow(active = new C41Enable(plugin,
                &plugin->config.active, x, y, _("Activate processing")));
        y += 40;
index d231533..391f2f1 100644 (file)
@@ -122,6 +122,7 @@ class C41Window : public PluginClientWindow
 {
 public:
        C41Window(C41Effect *client);
+       void create_objects();
 
        void update();
        void update_magic();
diff --git a/cinelerra-5.1/thirdparty/src/giflib-5.1.6.patch2 b/cinelerra-5.1/thirdparty/src/giflib-5.1.6.patch2
new file mode 100644 (file)
index 0000000..8a7ad8b
--- /dev/null
@@ -0,0 +1,125 @@
+diff -ur a/quantize.c b/quantize.c
+--- a/quantize.c       2019-02-11 07:43:57.000000000 -0700
++++ b/quantize.c       2019-02-27 17:20:06.369498072 -0700
+# SortRGBAxis is static and not locked, qsort recoded
+# GAErrorToken is also static and not locked, not fixed
+@@ -11,8 +11,9 @@
+ ******************************************************************************/
+-#include <stdlib.h>
+ #include <stdio.h>
++#include <stdlib.h>
++
+ #include "gif_lib.h"
+ #include "gif_lib_private.h"
+@@ -22,8 +23,6 @@
+ #define BITS_PER_PRIM_COLOR 5
+ #define MAX_PRIM_COLOR      0x1f
+-static int SortRGBAxis;
+-
+ typedef struct QuantizedColorType {
+     GifByteType RGB[3];
+     GifByteType NewColorIndex;
+@@ -31,6 +30,40 @@
+     struct QuantizedColorType *Pnext;
+ } QuantizedColorType;
++static int QCmpr(QuantizedColorType *a, QuantizedColorType *b, int i)
++{
++      int i0 = i, i1 = i+1, i2 = i+2;
++      if( i1 >= 3 ) i1 -= 3;
++      if( i2 >= 3 ) i2 -= 3;
++      /* sort on all axes of the color space! */
++      int hash_a = (a->RGB[i0] << 16) | (a->RGB[i1] << 8) | (a->RGB[i2] << 0);
++      int hash_b = (b->RGB[i0] << 16) | (b->RGB[i1] << 8) | (b->RGB[i2] << 0);
++      return hash_a - hash_b;
++}
++
++static int QSplit(QuantizedColorType **q, int l, int r, int i)
++{
++      int m;
++      QuantizedColorType *t;
++      for(;;) {
++              while( QCmpr(q[r],q[l], i) >= 0 ) if( ++l == r ) return r;
++              t = q[l];  q[l] = q[r];  q[r] = t;  m = l;  l = r;  r = m;
++              while( QCmpr(q[l],q[r], i) >= 0 ) if( r == --l ) return r;
++              t = q[l];  q[l] = q[r];  q[r] = t;  m = l;  l = r;  r = m;
++      }
++}
++
++static void QSort(QuantizedColorType **q, int ll, int rr, int i)
++{
++      for(;;) {
++              int l = ll+1;  if( l == rr ) return;
++              int r = rr-1;  if( l == r ) return;
++              int m = QSplit(q, l, r, i);
++              QSort(q, ll, m, i);
++              ll = m;
++      }
++}
++
+ typedef struct NewColorMapType {
+     GifByteType RGBMin[3], RGBWidth[3];
+     unsigned int NumEntries; /* # of QuantizedColorType in linked list below */
+@@ -41,7 +74,6 @@
+ static int SubdivColorMap(NewColorMapType * NewColorSubdiv,
+                           unsigned int ColorMapSize,
+                           unsigned int *NewColorMapSize);
+-static int SortCmpRtn(const void *Entry1, const void *Entry2);
+ /******************************************************************************
+  Quantize high resolution image into lower one. Input image consists of a
+@@ -198,6 +230,7 @@
+                unsigned int ColorMapSize,
+                unsigned int *NewColorMapSize) {
++    int SortRGBAxis = 0;
+     unsigned int i, j, Index = 0;
+     QuantizedColorType *QuantizedColor, **SortArray;
+@@ -234,19 +267,7 @@
+              j++, QuantizedColor = QuantizedColor->Pnext)
+             SortArray[j] = QuantizedColor;
+-      /*
+-       * Because qsort isn't stable, this can produce differing 
+-       * results for the order of tuples depending on platform
+-       * details of how qsort() is implemented.
+-       *
+-       * We mitigate this problem by sorting on all three axes rather
+-       * than only the one specied by SortRGBAxis; that way the instability
+-       * can only become an issue if there are multiple color indices
+-       * referring to identical RGB tuples.  Older versions of this 
+-       * sorted on only the one axis.
+-       */
+-        qsort(SortArray, NewColorSubdiv[Index].NumEntries,
+-              sizeof(QuantizedColorType *), SortCmpRtn);
++        QSort(SortArray, -1, NewColorSubdiv[Index].NumEntries, SortRGBAxis);
+         /* Relink the sorted list into one: */
+         for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++)
+@@ -310,21 +331,4 @@
+  Routine called by qsort to compare two entries.
+ *****************************************************************************/
+-static int
+-SortCmpRtn(const void *Entry1,
+-           const void *Entry2) {
+-         QuantizedColorType *entry1 = (*((QuantizedColorType **) Entry1));
+-         QuantizedColorType *entry2 = (*((QuantizedColorType **) Entry2));
+-
+-         /* sort on all axes of the color space! */
+-         int hash1 = entry1->RGB[SortRGBAxis] * 256 * 256
+-                              + entry1->RGB[(SortRGBAxis+1) % 3] * 256
+-                              + entry1->RGB[(SortRGBAxis+2) % 3];
+-         int hash2 = entry2->RGB[SortRGBAxis] * 256 * 256
+-                              + entry2->RGB[(SortRGBAxis+1) % 3] * 256
+-                              + entry2->RGB[(SortRGBAxis+2) % 3];
+-
+-    return hash1 - hash2;
+-}
+-
+ /* end */