Credit SGE conversion of Adam-s plugins ChromakeyAvid/Color Swatch + updated ContextM... master
authorGood Guy <good1.2guy@gmail.com>
Tue, 11 Jun 2024 00:46:59 +0000 (18:46 -0600)
committerGood Guy <good1.2guy@gmail.com>
Tue, 11 Jun 2024 00:46:59 +0000 (18:46 -0600)
42 files changed:
cinelerra-5.1/cinelerra/bdwrite.C
cinelerra-5.1/cinelerra/ffmpeg.C
cinelerra-5.1/cinelerra/fileac3.C
cinelerra-5.1/cinelerra/fileffmpeg.C
cinelerra-5.1/cinelerra/pluginfclient.C
cinelerra-5.1/configure.ac
cinelerra-5.1/doc/ContextManual.pl
cinelerra-5.1/expanders.es
cinelerra-5.1/expanders.fr
cinelerra-5.1/expanders.txt
cinelerra-5.1/ffmpeg/plugin.opts
cinelerra-5.1/info/plugins.txt
cinelerra-5.1/msg/txt
cinelerra-5.1/plugin_defs
cinelerra-5.1/plugins/Makefile
cinelerra-5.1/plugins/chromakey/chromakey.C
cinelerra-5.1/plugins/chromakey/chromakey.h
cinelerra-5.1/plugins/chromakeyavid/Makefile [new file with mode: 0644]
cinelerra-5.1/plugins/chromakeyavid/chromakey.C [new file with mode: 0644]
cinelerra-5.1/plugins/chromakeyavid/chromakey.h [new file with mode: 0644]
cinelerra-5.1/plugins/chromakeyavid/chromakeyavid.sl [new file with mode: 0644]
cinelerra-5.1/plugins/chromakeyhsv/chromakey.C
cinelerra-5.1/plugins/chromakeyhsv/chromakey.h
cinelerra-5.1/plugins/swatch/Makefile [new file with mode: 0644]
cinelerra-5.1/plugins/swatch/swatch.C [new file with mode: 0644]
cinelerra-5.1/plugins/swatch/swatch.h [new file with mode: 0644]
cinelerra-5.1/thirdparty/downloads.txt
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch1 [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch10 [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch2 [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch3 [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch4 [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch5 [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch7 [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch8 [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch9 [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchB [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchC [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchD [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchZ1 [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchZ2 [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/ffmpeg-7.0.tar.xz [new file with mode: 0644]

index 1b864bb9720ba91a82c06b20771ba08355ad7408..1f7f027ef808e5bd54022381865fd52784905f24 100644 (file)
@@ -2675,7 +2675,7 @@ int media_info::scan()
       break; }
     case AVMEDIA_TYPE_AUDIO: {
       s->coding_type = bd_coding_type(codec_id);
-      s->format = bd_audio_format(st->codecpar->channels);
+      s->format = bd_audio_format(st->codecpar->ch_layout.nb_channels);
       s->rate = bd_audio_rate(st->codecpar->sample_rate);
       strcpy((char*)s->lang, "eng");
       break; }
index 6685f468b205255061c670412dc2108135bf5b83..9b8832dd718c94dd8b815edc7eb5c9d732953dd6 100644 (file)
@@ -880,13 +880,17 @@ void FFAudioStream::init_swr(int ichs, int ifmt, int irate)
        swr_ichs = ichs;  swr_ifmt = ifmt;  swr_irate = irate;
        if( ichs == channels && ifmt == AV_SAMPLE_FMT_FLT && irate == sample_rate )
                return;
-       uint64_t ilayout = av_get_default_channel_layout(ichs);
-       if( !ilayout ) ilayout = ((uint64_t)1<<ichs) - 1;
-       uint64_t olayout = av_get_default_channel_layout(channels);
-       if( !olayout ) olayout = ((uint64_t)1<<channels) - 1;
-       resample_context = swr_alloc_set_opts(NULL,
-               olayout, AV_SAMPLE_FMT_FLT, sample_rate,
-               ilayout, (AVSampleFormat)ifmt, irate,
+       //uint64_t ilayout = av_get_default_channel_layout(ichs);
+       AVChannelLayout ilayout, olayout;
+       av_channel_layout_default(&ilayout, ichs);
+       //if( !ilayout ) ilayout = ((uint64_t)1<<ichs) - 1;
+       //uint64_t olayout = av_get_default_channel_layout(channels);
+       av_channel_layout_default(&olayout, channels);
+       //if( !olayout ) olayout = ((uint64_t)1<<channels) - 1;
+       
+       swr_alloc_set_opts2(&resample_context,
+               &olayout, AV_SAMPLE_FMT_FLT, sample_rate,
+               &ilayout, (AVSampleFormat)ifmt, irate,
                0, NULL);
        if( resample_context )
                swr_init(resample_context);
@@ -963,7 +967,11 @@ int FFAudioStream::encode_activate()
 
 int64_t FFAudioStream::load_buffer(double ** const sp, int len)
 {
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61,3,100)
+       reserve(len+1, st->codecpar->ch_layout.nb_channels);
+#else
        reserve(len+1, st->codecpar->channels);
+#endif
        for( int ch=0; ch<nch; ++ch )
                write(sp[ch], len, ch);
        return put_inp(len);
@@ -983,7 +991,12 @@ int FFAudioStream::init_frame(AVFrame *frame)
 {
        frame->nb_samples = frame_sz;
        frame->format = avctx->sample_fmt;
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61,3,100)
+       frame->ch_layout.u.mask = avctx->ch_layout.u.mask;
+       av_channel_layout_copy(&frame->ch_layout, &avctx->ch_layout);
+#else
        frame->channel_layout = avctx->channel_layout;
+#endif
        frame->sample_rate = avctx->sample_rate;
        int ret = av_frame_get_buffer(frame, 0);
        if (ret < 0)
@@ -1004,7 +1017,7 @@ int FFAudioStream::load(int64_t pos, int len)
        while( ret>=0 && !flushed && curr_pos<end_pos && --i>=0 ) {
                ret = read_frame(frame);
                if( ret > 0 && frame->nb_samples > 0 ) {
-                       init_swr(frame->channels, frame->format, frame->sample_rate);
+                       init_swr(frame->ch_layout.nb_channels, frame->format, frame->sample_rate);
                        load_history(&frame->extended_data[0], frame->nb_samples);
                        curr_pos += frame->nb_samples;
                }
@@ -2780,14 +2793,14 @@ int FFMPEG::open_decoder()
                        ret = vid->create_filter(opt_video_filter);
                        break; }
                case AVMEDIA_TYPE_AUDIO: {
-                       if( avpar->channels < 1 ) continue;
+                       if( avpar->ch_layout.nb_channels < 1 ) continue;
                        if( avpar->sample_rate < 1 ) continue;
                        has_audio = 1;
                        int aidx = ffaudio.size();
                        FFAudioStream *aud = new FFAudioStream(this, st, aidx, i);
                        ffaudio.append(aud);
                        aud->channel0 = astrm_index.size();
-                       aud->channels = avpar->channels;
+                       aud->channels = avpar->ch_layout.nb_channels;
                        for( int ch=0; ch<aud->channels; ++ch )
                                astrm_index.append(ffidx(aidx, ch));
                        aud->sample_rate = avpar->sample_rate;
@@ -2943,10 +2956,13 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                        FFAudioStream *aud = new FFAudioStream(this, st, aidx, fidx);
                        aud->avctx = ctx;  ffaudio.append(aud);  fst = aud;
                        aud->sample_rate = asset->sample_rate;
-                       ctx->channels = aud->channels = asset->channels;
+                       ctx->ch_layout.nb_channels = aud->channels = asset->channels;
                        for( int ch=0; ch<aud->channels; ++ch )
                                astrm_index.append(ffidx(aidx, ch));
-                       ctx->channel_layout =  av_get_default_channel_layout(ctx->channels);
+                       AVChannelLayout ch_layout;
+                       av_channel_layout_default(&ch_layout, ctx->ch_layout.nb_channels);
+                       ctx->ch_layout.u.mask =  ch_layout.u.mask;
+                       av_channel_layout_copy(&ctx->ch_layout, &ch_layout);
                        ctx->sample_rate = check_sample_rate(codec, asset->sample_rate);
                        if( !ctx->sample_rate ) {
                                eprintf(_("check_sample_rate failed %s\n"), filename);
@@ -2958,10 +2974,12 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                        if( sample_fmt == AV_SAMPLE_FMT_NONE )
                                sample_fmt = codec->sample_fmts ? codec->sample_fmts[0] : AV_SAMPLE_FMT_S16;
                        ctx->sample_fmt = sample_fmt;
-                       uint64_t layout = av_get_default_channel_layout(ctx->channels);
-                       aud->resample_context = swr_alloc_set_opts(NULL,
-                               layout, ctx->sample_fmt, aud->sample_rate,
-                               layout, AV_SAMPLE_FMT_FLT, ctx->sample_rate,
+                       //uint64_t layout = av_get_default_channel_layout(ctx->ch_layout.nb_channels);
+                       AVChannelLayout layout;
+                       av_channel_layout_default(&layout, ctx->ch_layout.nb_channels);
+                       swr_alloc_set_opts2(&aud->resample_context,
+                               &layout, ctx->sample_fmt, aud->sample_rate,
+                               &layout, AV_SAMPLE_FMT_FLT, ctx->sample_rate,
                                0, NULL);
                        swr_init(aud->resample_context);
                        aud->writing = -1;
@@ -3929,7 +3947,7 @@ int FFAudioStream::create_filter(const char *filter_spec)
        snprintf(args, sizeof(args),
                "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%jx",
                st->time_base.num, st->time_base.den, avpar->sample_rate,
-               av_get_sample_fmt_name(sample_fmt), avpar->channel_layout);
+               av_get_sample_fmt_name(sample_fmt), avpar->ch_layout.u.mask);
        if( ret >= 0 ) {
                filt_ctx = 0;
                ret = insert_filter("abuffer", args, "in");
@@ -3947,8 +3965,8 @@ int FFAudioStream::create_filter(const char *filter_spec)
                        AV_OPT_SEARCH_CHILDREN);
        if( ret >= 0 )
                ret = av_opt_set_bin(buffersink_ctx, "channel_layouts",
-                       (uint8_t*)&avpar->channel_layout,
-                       sizeof(avpar->channel_layout), AV_OPT_SEARCH_CHILDREN);
+                       (uint8_t*)&avpar->ch_layout.u.mask,
+                       sizeof(avpar->ch_layout.u.mask), AV_OPT_SEARCH_CHILDREN);
        if( ret >= 0 )
                ret = av_opt_set_bin(buffersink_ctx, "sample_rates",
                        (uint8_t*)&sample_rate, sizeof(sample_rate),
@@ -4200,7 +4218,7 @@ printf("audio%d pad %jd %jd (%jd)\n", aud->idx, pos, aud->curr_pos, pos-aud->cur
                        }
                        while( (ret=aud->decode_frame(frame)) > 0 ) {
                                //if( frame->channels != nch ) break;
-                               aud->init_swr(frame->channels, frame->format, frame->sample_rate);
+                               aud->init_swr(frame->ch_layout.nb_channels, frame->format, frame->sample_rate);
                                float *samples;
                                int len = aud->get_samples(samples,
                                         &frame->extended_data[0], frame->nb_samples);
index dd3b4536d0d0997ba90001474ead3f520e828123..63654f32ff430818dc459d8bd24ce97bfe43cbe4 100644 (file)
@@ -142,24 +142,39 @@ int FileAC3::open_file(int rd, int wr)
                        int channels = asset->channels;
                        int sample_rate = asset->sample_rate;
                        int64_t layout = get_channel_layout(channels);
+                       AVChannelLayout ch_layout;
+                       av_channel_layout_default(&ch_layout, channels);
+                       if(!ch_layout.nb_channels) {
+                       printf ("av_ch_layut_default failed! \n"); }
                        int bitrate = asset->ac3_bitrate * 1000;
                        av_init_packet(&avpkt);
                        codec_context = avcodec_alloc_context3(codec);
                        codec_context->bit_rate = bitrate;
                        codec_context->sample_rate = sample_rate;
-                       codec_context->channels = channels;
-                       codec_context->channel_layout = layout;
+                       codec_context->ch_layout.nb_channels = channels;
+                       codec_context->ch_layout.u.mask = layout;
+                       av_channel_layout_copy(&codec_context->ch_layout, &ch_layout);
                        codec_context->sample_fmt = codec->sample_fmts[0];
-                       resample_context = swr_alloc_set_opts(NULL,
-                                       layout, codec_context->sample_fmt, sample_rate,
-                                       layout, AV_SAMPLE_FMT_S16, sample_rate,
+                       SwrContext *tmp_resample_context = NULL;
+                       int ret = swr_alloc_set_opts2(&tmp_resample_context,
+                                       &ch_layout, codec_context->sample_fmt, sample_rate,
+                                       &ch_layout, AV_SAMPLE_FMT_S16, sample_rate,
                                        0, NULL);
-                       swr_init(resample_context);
+                       if(ret <0) printf("swr_alloc eror: %i \n", ret );
+                       if(tmp_resample_context){
+                       resample_context = tmp_resample_context;
+                       if(swr_init(resample_context))
+                       {
+                               eprintf(_("FileAC3::open_file failed to init swr.\n"));
+                               result = 1;
+                       }
+                       }
                        if(avcodec_open2(codec_context, codec, 0))
                        {
                                eprintf(_("FileAC3::open_file failed to open codec.\n"));
                                result = 1;
                        }
+                       av_channel_layout_uninit(&ch_layout);
                }
        }
 
@@ -314,8 +329,9 @@ int FileAC3::write_samples(double **buffer, int64_t len)
                AVFrame *frame = av_frame_alloc();
                frame->nb_samples = frame_size;
                frame->format = avctx->sample_fmt;
-               frame->channel_layout = avctx->channel_layout;
+               av_channel_layout_copy(&frame->ch_layout, &avctx->ch_layout);
                frame->sample_rate = avctx->sample_rate;
+
                ret = av_frame_get_buffer(frame, 0);
                if( ret >= 0 ) {
                        const uint8_t *samples = (uint8_t *)temp_raw +
@@ -323,7 +339,7 @@ int FileAC3::write_samples(double **buffer, int64_t len)
                        ret = swr_convert(resample_context,
                                (uint8_t **)frame->extended_data, frame_size,
                                &samples, frame_size);
-               }
+               } else { printf("fileAC3: av_frame_get_buffer failed!\n"); }
                if( ret >= 0 ) {
                        frame->pts = avctx->sample_rate && avctx->time_base.num ?
                                file->get_audio_position() : AV_NOPTS_VALUE ;
index 2fbf6c86f80327055b703295b5d66017895aaf0e..abed22da197e8929541e072a9df923a56a994112 100644 (file)
@@ -1493,7 +1493,7 @@ int FFOptions_Opt::types(char *rp)
        case AV_OPT_TYPE_SAMPLE_FMT: cp = N_("<sample_fmt>"); break;
        case AV_OPT_TYPE_DURATION: cp = N_("<duration>"); break;
        case AV_OPT_TYPE_COLOR: cp = N_("<color>"); break;
-       case AV_OPT_TYPE_CHANNEL_LAYOUT: cp = N_("<channel_layout>");  break;
+       case AV_OPT_TYPE_CHLAYOUT: cp = N_("<channel_layout>");  break;
        case AV_OPT_TYPE_BOOL: cp = N_("<bool>");  break;
        default: cp = N_("<undef>");  break;
        }
index 2d57743ca3447ef22473bc47fa698505879098cf..b8694b120e29ca26a36adde2e701094d99b86070 100644 (file)
@@ -453,7 +453,7 @@ int PluginFClient_Opt::types(char *rp)
        case AV_OPT_TYPE_SAMPLE_FMT: cp = "<sample_fmt>"; break;
        case AV_OPT_TYPE_DURATION: cp = "<duration>"; break;
        case AV_OPT_TYPE_COLOR: cp = "<color>"; break;
-       case AV_OPT_TYPE_CHANNEL_LAYOUT: cp = "<channel_layout>";  break;
+       case AV_OPT_TYPE_CHLAYOUT: cp = "<channel_layout>";  break;
        default: cp = "<undef>";  break;
        }
        return sprintf(rp, "%s", cp);
@@ -664,7 +664,7 @@ PluginFClient::~PluginFClient()
 bool PluginFClient::is_audio(const AVFilter *fp)
 {
        if( !fp->outputs ) return 0;
-#if LIBAVFILTER_VERSION_MINOR > 2 && LIBAVFILTER_VERSION_MAJOR > 7
+#if LIBAVFILTER_VERSION_MAJOR > 8
        if( avfilter_filter_pad_count(fp, 1) > 1 ) return 0;
 #else
        if( avfilter_pad_count(fp->outputs) > 1 ) return 0;
@@ -672,7 +672,7 @@ bool PluginFClient::is_audio(const AVFilter *fp)
        if( !avfilter_pad_get_name(fp->outputs, 0) ) return 0;
        if( avfilter_pad_get_type(fp->outputs, 0) != AVMEDIA_TYPE_AUDIO ) return 0;
        if( !fp->inputs ) return 1;
-#if LIBAVFILTER_VERSION_MINOR > 2 && LIBAVFILTER_VERSION_MAJOR > 7
+#if  LIBAVFILTER_VERSION_MAJOR > 8
        if( avfilter_filter_pad_count(fp, 0) > 1 ) return 0;
 #else
        if( avfilter_pad_count(fp->inputs) > 1 ) return 0;
@@ -684,7 +684,7 @@ bool PluginFClient::is_audio(const AVFilter *fp)
 bool PluginFClient::is_video(const AVFilter *fp)
 {
        if( !fp->outputs ) return 0;
-#if LIBAVFILTER_VERSION_MINOR > 2 && LIBAVFILTER_VERSION_MAJOR > 7
+#if  LIBAVFILTER_VERSION_MAJOR > 8 
        if( avfilter_filter_pad_count(fp, 1) > 1 ) return 0;
 #else
        if( avfilter_pad_count(fp->outputs) > 1 ) return 0;
@@ -692,7 +692,7 @@ bool PluginFClient::is_video(const AVFilter *fp)
        if( !avfilter_pad_get_name(fp->outputs, 0) ) return 0;
        if( avfilter_pad_get_type(fp->outputs, 0) != AVMEDIA_TYPE_VIDEO ) return 0;
        if( !fp->inputs ) return 1;
-#if LIBAVFILTER_VERSION_MINOR > 2 && LIBAVFILTER_VERSION_MAJOR > 7
+#if  LIBAVFILTER_VERSION_MAJOR > 8
        if( avfilter_filter_pad_count(fp, 0) > 1 ) return 0;
 #else
        if( avfilter_pad_count(fp->inputs) > 1 ) return 0;
@@ -845,16 +845,24 @@ int PluginFAClient::activate()
        }
        AVSampleFormat sample_fmt = AV_SAMPLE_FMT_FLTP;
        int channels = PluginClient::total_in_buffers;
-       uint64_t layout = (((uint64_t)1)<<channels) - 1;
+       //uint64_t layout = (((uint64_t)1)<<channels) - 1;
+       AVChannelLayout layout;
+       
+       av_channel_layout_default(&layout, channels);
+       
+       char chLayoutDescription[128];
+       av_channel_layout_describe(&layout, chLayoutDescription, sizeof(chLayoutDescription));
+    
        AVFilterGraph *graph = !ffilt ? 0 : ffilt->graph;
        int ret = !graph ? -1 : 0;
        if( ret >= 0 && ffilt->filter->inputs ) {
                char args[BCTEXTLEN];
                snprintf(args, sizeof(args),
-                       "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%jx",
-                       1, sample_rate, sample_rate, av_get_sample_fmt_name(sample_fmt), layout);
+                       "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%s:channels=%i",
+                       1, sample_rate, sample_rate, av_get_sample_fmt_name(sample_fmt), chLayoutDescription, channels);
                ret = avfilter_graph_create_filter(&fsrc, avfilter_get_by_name("abuffer"),
                        "in", args, NULL, graph);
+               if(ret <0) printf("abuffer failed!\n");
        }
        if( ret >= 0 )
                ret = avfilter_graph_create_filter(&fsink, avfilter_get_by_name("abuffersink"),
@@ -863,8 +871,8 @@ int PluginFAClient::activate()
                ret = av_opt_set_bin(fsink, "sample_fmts",
                        (uint8_t*)&sample_fmt, sizeof(sample_fmt), AV_OPT_SEARCH_CHILDREN);
        if( ret >= 0 )
-               ret = av_opt_set_bin(fsink, "channel_layouts",
-                       (uint8_t*)&layout, sizeof(layout), AV_OPT_SEARCH_CHILDREN);
+               ret = av_opt_set(fsink, "ch_layouts",
+                       chLayoutDescription, AV_OPT_SEARCH_CHILDREN);
        if( ret >= 0 )
                ret = av_opt_set_bin(fsink, "sample_rates",
                        (uint8_t*)&sample_rate, sizeof(sample_rate), AV_OPT_SEARCH_CHILDREN);
@@ -990,9 +998,19 @@ int PluginFAClient::process_buffer(int64_t size, Samples **buffer, int64_t start
        if( ret >= 0 ) {
                in_channels = get_inchannels();
                out_channels = get_outchannels();
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59,24,100)
+               AVChannelLayout in_ch_layout;
+               AVFilterContext *fctx = ffilt->fctx;
+               AVFilterLink **links = !fctx->nb_outputs ? 0 : fctx->outputs;
+               av_channel_layout_copy(&in_ch_layout, &links[0]->ch_layout);
+#endif
                frame->nb_samples = size;
                frame->format = AV_SAMPLE_FMT_FLTP;
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61,3,100)
+               av_channel_layout_copy(&frame->ch_layout, &in_ch_layout);
+#else
                frame->channel_layout = (1<<in_channels)-1;
+#endif
                frame->sample_rate = sample_rate;
                frame->pts = filter_position;
        }
index 984019e8f97c175617d77c21b537462cb16d11be..8159da8f5626915e11673750fe8cd1ed2894b0ff 100644 (file)
@@ -223,7 +223,7 @@ PKG_3RD([esound],[no],
   [ . ])
 
 PKG_3RD([ffmpeg],[yes],
-  [ffmpeg-6.1],
+  [ffmpeg-7.0],
   [ libavutil/libavutil.a \
     libavcodec/libavcodec.a \
     libpostproc/libpostproc.a \
index f3e25cdb20185a153f4bd9ffcaf86c711e13770e..45d00dc80ab5e12279bc6091424d5a5fe442106c 100755 (executable)
@@ -17,7 +17,7 @@
 # Several important definitions
 
 # ContextManual.pl script API version. Must not be changed !
-$cin_cm_api = 1;
+$cin_cm_api = 2;
 
 # Web browser executable, can be redefined on user's demand
 $cin_browser = $ENV{'CIN_BROWSER'};
@@ -34,8 +34,9 @@ $cin_browser = 'firefox' if $cin_browser eq '';
 # The node with the manual contents
 $contents_node = 'Contents.html';
 
-# The node with the manual index
-$index_node = 'Index.html';
+# The node with the actual index if needed will be found after parsing contents
+$index_node = '';
+$index = '';
 
 # Several special plugin names necessary to rewrite
 %rewrite = (
@@ -80,7 +81,6 @@ $cin_dat = '.' if $cin_dat eq '';
 # Cinelerra HTML manual must reside here
 $cin_man = "$cin_dat/doc/CinelerraGG_Manual";
 $contents = $cin_man.'/'.$contents_node;
-$index = $cin_man.'/'.$index_node;
 #print "ContextManual: using contents $contents\n";
 
 # Cinelerra user's config directory
@@ -134,6 +134,21 @@ if ($help_key eq 'TOC')
 # Show index on this special request
 if ($help_key eq 'IDX')
 {
+  # Index node will be needed now, find it
+  if ($index_node eq '')
+  {
+    $node = '';
+    open CONTENTS, $contents or die "Cannot open $contents: $!";
+    while (<CONTENTS>)
+    {
+      chomp;
+      last if ($node) = /^\s*HREF=\"(.+?\.html)\">\s*Index\s*<\/A>(?:<\/B>)?$/;
+    }
+    close CONTENTS;
+    $index_node = $node if $node ne '';
+    $index_node = 'Index.html' if $index_node eq '';
+    $index = $cin_man.'/'.$index_node;
+  }
   system "$cin_browser \"file://$index\" &";
   exit 0;
 }
@@ -158,6 +173,21 @@ if ($help_key eq 'TOC')
 # Show index on this special request
 if ($help_key eq 'IDX')
 {
+  # Index node will be needed now, find it
+  if ($index_node eq '')
+  {
+    $node = '';
+    open CONTENTS, $contents or die "Cannot open $contents: $!";
+    while (<CONTENTS>)
+    {
+      chomp;
+      last if ($node) = /^\s*HREF=\"(.+?\.html)\">\s*Index\s*<\/A>(?:<\/B>)?$/;
+    }
+    close CONTENTS;
+    $index_node = $node if $node ne '';
+    $index_node = 'Index.html' if $index_node eq '';
+    $index = $cin_man.'/'.$index_node;
+  }
   system "$cin_browser \"file://$index\" &";
   exit 0;
 }
@@ -190,6 +220,21 @@ if ($node eq '')
   }
 }
 
+# Index node will be needed now, find it
+if ($node eq '' && $index_node eq '')
+{
+  seek CONTENTS, 0, 0;
+  while (<CONTENTS>)
+  {
+    chomp;
+    last if ($node) = /^\s*HREF=\"(.+?\.html)\">\s*Index\s*<\/A>(?:<\/B>)?$/;
+  }
+  $index_node = $node if $node ne '';
+  $index_node = 'Index.html' if $index_node eq '';
+  $index = $cin_man.'/'.$index_node;
+  $node = '';
+}
+
 # If not found, search index for the exact key
 if ($node eq '')
 {
index 99c5838914cfa4f7bda1106d9718ea5351dc09ac..5e5ada633ebd8a010af9ea6f40145376d699c63c 100644 (file)
@@ -123,6 +123,8 @@ Video Effects
                Blue Banana
                Chroma key
                Chroma key (HSV)
+               Chroma key (Avid)
+               Color Swatch
                CriKey
                Difference key
                F_chromakey
index 25ed888302aa5b47cdcd6e94e060a5f7d39a8d8b..fa092c21ceebda543989ef9242c0a9a606dfbba8 100644 (file)
@@ -123,6 +123,8 @@ Video Effects
                Blue Banana
                Chroma key
                Chroma key (HSV)
+               Chroma key (Avid)
+               Color Swatch
                CriKey
                Difference key
                F_chromakey
index 0e3ae8418ca1b4203e53916579493d4862438b20..898ade19edc4906b8ddc6d1110f5b0e2ff4782fb 100644 (file)
@@ -124,6 +124,8 @@ Video Effects
                Blue Banana
                Chroma key
                Chroma key (HSV)
+               Chroma key (Avid)
+               Color Swatch
                CriKey
                Difference key
                F_chromakey
index 9fa5e12cd75b75ceee1bee037af20f0bf056ccd5..e9c358258e288f25c79dddae7169d7eca1929782 100644 (file)
@@ -500,3 +500,7 @@ afdelaysrc
 #bilateral_cuda ###Function not implemented
 #bwdif_cuda ###Function not implemented
 #colorspace_cuda ###Function not implemented
+; do not work in 7.0
+#aap ###Input/output error
+#fsync ###No such file or directory
+#tiltandshift ###Resource temporarily available
index daf71caf655c701eddac7350cee1f2c93aeedb94..145924a040fa62864f23d59c47da5ad8e57947ac 100644 (file)
@@ -42,12 +42,17 @@ Chroma key: Erases pixels which match chosen color;
                channel and transparency if there is alpha.
 Chroma key (HSV): Replaces a color with another color or
                transparency using HSV variables.
+Chroma key (Avid): ChromkayHSV version based on the Avid 
+               SpectraMatte algorithm. First attach the Color
+               Swatch plugin to help visualize the effect.
 Color 3 Way:   Modify color of Shadows, Midtones, and Highlights
                as you specify.
 Color Balance: Modify RGB colors or white balance to compensate
                for errors in video such as low lighting.
 ColorSpace:    Tweak colors from one color space/range to another
-               space=BT601/BT709/BT2020  range=MPEG/JPEG
+               space=BT601/BT709/BT2020  range=MPEG/JPEG.
+Color Swatch:  Draws the color space for saturation or brightness.
+               Provides an input to assist in ChromaKey adjustments.
 CriKey:                Regionally based chroma key with interpolation;
                useful when you only want a specific zone defined.
                .
index 48577fb9f974d482762c6c02ab09160c040a8cbf..053c04a4d3c667e236e5ed58219bb393c9ac5f27 100644 (file)
@@ -3,6 +3,10 @@ For usage help, refer to the following:
   https://cinelerra-gg.org/download/CinelerraGG_Manual.pdf
   http://g-raffa.eu/Cinelerra/HOWTO/basics.html
 .
+2024 May changes of note:
+  FFmpeg has been updated from 6.1 to 7.0.
+  ChromaKey and ChromaKeyHSV have improved menus/options.
+  Mjpegtools upgraded from 2.1.0 to 2.2.1.
 2024 January changes of note:
   X265 library has been updated to snapshot of 17-12-2023.
   Libsndfile is now at version 1.2.2.
index cb3c700a90ce5ab280a6229909b2bad5db39182f..ded3fd7094c59a886b142002c83058f3b4fcff2c 100644 (file)
@@ -34,6 +34,7 @@ video := \
        C41 \
        chromakey \
        chromakeyhsv \
+       chromakeyavid \
        color3way \
        colorbalance \
        colorspace \
@@ -112,6 +113,7 @@ video := \
        svg \
        swapchannels \
        swapframes \
+       swatch \
        threshold \
        timeavg \
        timeblur \
index 22aaf43374cbde791029ebd6a5623c46e13becbc..89f164662eafa21ad8ac0ae7a5113f33ae59ed63 100644 (file)
@@ -40,6 +40,7 @@ DIRS = $(OPENCV_OBJS) \
        chorus \
        chromakey \
        chromakeyhsv \
+       chromakeyavid \
        color3way \
        colorbalance \
        colorspace \
@@ -139,6 +140,7 @@ DIRS = $(OPENCV_OBJS) \
        svg \
        swapchannels \
        swapframes \
+       swatch \
        synthesizer \
        threshold \
        timeavg \
index 832a1d45cfe32e4035820002099de57b69173dce..66d72cecc7111efadb016a4f2f62fff87034a310 100644 (file)
 
 ChromaKeyConfig::ChromaKeyConfig()
 {
-       reset();
+       reset(RESET_DEFAULT_SETTINGS);
 }
 
-void ChromaKeyConfig::reset()
+void ChromaKeyConfig::reset(int clear)
 
 {
-       red = 0.0;
-       green = 0.0;
-       blue = 0.0;
-       threshold = 60.0;
-       use_value = 0;
-       slope = 100;
+       switch(clear) {
+               case RESET_ALL :
+                       red = 0.0;
+                       green = 0.0;
+                       blue = 0.0;
+                       threshold = 0.0;
+                       use_value = 0;
+                       slope = 0.0;
+                       break;
+               case RESET_RGB :
+                       red = 0.0;
+                       green = 0.0;
+                       blue = 0.0;
+                       break;
+               case RESET_SLOPE :
+                       slope = 0.0;
+                       break;
+               case RESET_THRESHOLD :
+                       threshold = 0.0;
+                       break;
+               case RESET_DEFAULT_SETTINGS :
+               default:
+                       red = 0.0;
+                       green = 0.0;
+                       blue = 0.0;
+                       threshold = 10.0;
+                       use_value = 0;
+                       slope = 0.0;
+                       break;
+       }
 }
 
 
@@ -110,10 +134,10 @@ int ChromaKeyConfig::get_color()
 
 ChromaKeyWindow::ChromaKeyWindow(ChromaKey *plugin)
  : PluginClientWindow(plugin,
-       xS(320),
-       yS(220),
-       xS(320),
-       yS(220),
+       xS(420),
+       yS(250),
+       xS(420),
+       yS(250),
        0)
 {
        this->plugin = plugin;
@@ -127,35 +151,61 @@ ChromaKeyWindow::~ChromaKeyWindow()
 
 void ChromaKeyWindow::create_objects()
 {
-       int xs10 = xS(10), xs100 = xS(100);
-       int ys10 = yS(10), ys30 = yS(30), ys50 = yS(50);
-       int x = xs10, y = ys10, x1 = xS(100);
+       int xs10 = xS(10), xs20 = xS(20), xs100 = xS(100), xs200 = xS(200);
+       int ys10 = yS(10), ys20 = yS(20), ys30 = yS(30), ys40 = yS(40), ys50 = yS(50);
+       int x = xs10, y = ys10, x2 = xS(80), x3 = xS(180);
+       int clr_x = get_w()-x - xS(22); // note: clrBtn_w = 22
+       int defaultBtn_w = xs100;
 
        BC_Title *title;
-       add_subwindow(title = new BC_Title(x, y, _("Color:")));
-       x += title->get_w() + xs10;
-       add_subwindow(color = new ChromaKeyColor(plugin, this, x, y));
-       x += color->get_w() + xs10;
-       add_subwindow(sample = new BC_SubWindow(x, y, xs100, ys50));
-       y += sample->get_h() + xs10;
-       x = xs10;
-
-       add_subwindow(new BC_Title(x, y, _("Slope:")));
-       add_subwindow(slope = new ChromaKeySlope(plugin, x1, y));
+       BC_TitleBar *title_bar;
+       BC_Bar *bar;
 
+// Color section
+       add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, _("Color")));
+       y += ys20;
+       add_subwindow(color = new ChromaKeyColor(plugin, this, x, y));
+       // Info for Sample rectangle:       x_slider w_slider w_sample
+       //                                        \       |      /    y,   w,     h
+       add_subwindow(sample = new BC_SubWindow(x3 + xs200 - xs100, y, xs100, ys50));
        y += ys30;
-       add_subwindow(new BC_Title(x, y, _("Threshold:")));
-       add_subwindow(threshold = new ChromaKeyThreshold(plugin, x1, y));
-
+       add_subwindow(use_colorpicker = new ChromaKeyUseColorPicker(plugin, this, x, y));
 
+// Key parameters section
+       y += ys30;
+       add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, _("Key parameters")));
+       y += ys20;
+       add_subwindow(title = new BC_Title(x, y, _("Threshold:")));
+       threshold_text = new ChromaKeyFText(plugin, this,
+               0, &(plugin->config.threshold), (x + x2), y, MIN_VALUE, MAX_VALUE);
+       threshold_text->create_objects();
+       threshold_slider = new ChromaKeyFSlider(plugin,
+               threshold_text, &(plugin->config.threshold), x3, y, MIN_VALUE, MAX_VALUE, xs200);
+       add_subwindow(threshold_slider);
+       threshold_text->slider = threshold_slider;
+       add_subwindow(threshold_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_THRESHOLD));
        y += ys30;
-       add_subwindow(use_value = new ChromaKeyUseValue(plugin, x1, y));
 
+       add_subwindow(title = new BC_Title(x, y, _("Slope:")));
+       slope_text = new ChromaKeyFText(plugin, this,
+               0, &(plugin->config.slope), (x + x2), y, MIN_VALUE, MAX_VALUE);
+       slope_text->create_objects();
+       slope_slider = new ChromaKeyFSlider(plugin,
+               slope_text, &(plugin->config.slope), x3, y, MIN_VALUE, MAX_VALUE, xs200);
+       add_subwindow(slope_slider);
+       slope_text->slider = slope_slider;
+       add_subwindow(slope_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_SLOPE));
        y += ys30;
-       add_subwindow(use_colorpicker = new ChromaKeyUseColorPicker(plugin, this, x1, y));
 
-       y += use_colorpicker->get_h() + xs10;
-       add_subwindow(new ChromaKeyReset(plugin, this, x, y));
+       add_subwindow(use_value = new ChromaKeyUseValue(plugin, x, y));
+       y += ys40;
+
+// Reset section
+       add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
+       y += ys10;
+       add_subwindow(reset = new ChromaKeyReset(plugin, this, x, y));
+       add_subwindow(default_settings = new ChromaKeyDefaultSettings(plugin, this,
+               (get_w() - xs10 - defaultBtn_w), y, defaultBtn_w));
 
        color_thread = new ChromaKeyColorThread(plugin, this);
 
@@ -164,6 +214,7 @@ void ChromaKeyWindow::create_objects()
        flush();
 }
 
+
 void ChromaKeyWindow::update_sample()
 {
        sample->set_color(plugin->config.get_color());
@@ -213,49 +264,80 @@ int ChromaKeyColor::handle_event()
 
 
 
-
-ChromaKeyThreshold::ChromaKeyThreshold(ChromaKey *plugin, int x, int y)
- : BC_FSlider(x,
-                       y,
-                       0,
-                       xS(200),
-                       yS(200),
-                       (float)0,
-                       (float)100,
-                       plugin->config.threshold)
+ChromaKeyFText::ChromaKeyFText(ChromaKey *plugin, ChromaKeyWindow *gui,
+       ChromaKeyFSlider *slider, float *output, int x, int y, float min, float max)
+ : BC_TumbleTextBox(gui, *output,
+       min, max, x, y, xS(60), 2)
 {
        this->plugin = plugin;
-       set_precision(0.01);
+       this->gui = gui;
+       this->output = output;
+       this->slider = slider;
+       this->min = min;
+       this->max = max;
+       set_increment(0.01);
 }
-int ChromaKeyThreshold::handle_event()
+
+ChromaKeyFText::~ChromaKeyFText()
 {
-       plugin->config.threshold = get_value();
+}
+
+int ChromaKeyFText::handle_event()
+{
+       *output = atof(get_text());
+       if(*output > max) *output = max;
+       else if(*output < min) *output = min;
+       slider->update(*output);
        plugin->send_configure_change();
        return 1;
 }
 
+ChromaKeyFSlider::ChromaKeyFSlider(ChromaKey *plugin,
+       ChromaKeyFText *text, float *output, int x, int y,
+       float min, float max, int w)
+ : BC_FSlider(x, y, 0, w, w, min, max, *output)
+{
+       this->plugin = plugin;
+       this->output = output;
+       this->text = text;
+       set_precision (0.01);
+       enable_show_value(0); // Hide caption
+}
+
+ChromaKeyFSlider::~ChromaKeyFSlider()
+{
+}
 
-ChromaKeySlope::ChromaKeySlope(ChromaKey *plugin, int x, int y)
- : BC_FSlider(x,
-                       y,
-                       0,
-                       xS(200),
-                       yS(200),
-                       (float)0,
-                       (float)100,
-                       plugin->config.slope)
+int ChromaKeyFSlider::handle_event()
+{
+       *output = get_value();
+       text->update(*output);
+       plugin->send_configure_change();
+       return 1;
+}
+
+ChromaKeyClr::ChromaKeyClr(ChromaKey *plugin, ChromaKeyWindow *gui, int x, int y, int clear)
+ : BC_Button(x, y, plugin->get_theme()->get_image_set("reset_button"))
 {
        this->plugin = plugin;
-       set_precision(0.01);
+       this->gui = gui;
+       this->clear = clear;
+}
+
+ChromaKeyClr::~ChromaKeyClr()
+{
 }
 
-int ChromaKeySlope::handle_event()
+int ChromaKeyClr::handle_event()
 {
-       plugin->config.slope = get_value();
+       plugin->config.reset(clear);
+       gui->update_gui(clear);
        plugin->send_configure_change();
        return 1;
 }
 
+
+
 ChromaKeyUseValue::ChromaKeyUseValue(ChromaKey *plugin, int x, int y)
  : BC_CheckBox(x, y, plugin->config.use_value, _("Use value"))
 {
@@ -277,8 +359,26 @@ ChromaKeyReset::ChromaKeyReset(ChromaKey *plugin, ChromaKeyWindow *gui, int x, i
 
 int ChromaKeyReset::handle_event()
 {
-       plugin->config.reset();
-       gui->update_gui();
+       plugin->config.reset(RESET_ALL);
+       gui->update_gui(RESET_ALL);
+       plugin->send_configure_change();
+       return 1;
+}
+
+ChromaKeyDefaultSettings::ChromaKeyDefaultSettings(ChromaKey *plugin, ChromaKeyWindow *gui,
+       int x, int y, int w)
+ : BC_GenericButton(x, y, w, _("Default"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+ChromaKeyDefaultSettings::~ChromaKeyDefaultSettings()
+{
+}
+int ChromaKeyDefaultSettings::handle_event()
+{
+       plugin->config.reset(RESET_DEFAULT_SETTINGS);
+       gui->update_gui(RESET_DEFAULT_SETTINGS);
        plugin->send_configure_change();
        return 1;
 }
@@ -654,18 +754,37 @@ void ChromaKey::update_gui()
        {
                load_configuration();
                thread->window->lock_window();
-               ((ChromaKeyWindow *)(thread->window))->update_gui();
+               ((ChromaKeyWindow *)(thread->window))->update_gui(RESET_ALL);
                thread->window->unlock_window();
        }
 }
 
-void ChromaKeyWindow::update_gui()
+void ChromaKeyWindow::update_gui(int clear)
 {
        ChromaKeyConfig &config = plugin->config;
-       threshold->update(config.threshold);
-       slope->update(config.slope);
-       use_value->update(config.use_value);
-       update_sample();
+       switch(clear) {
+               case RESET_RGB :
+                       update_sample();
+                       break;
+               case RESET_SLOPE :
+                       slope_text->update(config.slope);
+                       slope_slider->update(config.slope);
+                       break;
+               case RESET_THRESHOLD :
+                       threshold_text->update(config.threshold);
+                       threshold_slider->update(config.threshold);
+                       break;
+               case RESET_ALL :
+               case RESET_DEFAULT_SETTINGS :
+               default:
+                       update_sample();
+                       slope_text->update(config.slope);
+                       slope_slider->update(config.slope);
+                       threshold_text->update(config.threshold);
+                       threshold_slider->update(config.threshold);
+                       use_value->update(config.use_value);
+                       break;
+       }
 }
 
 int ChromaKey::handle_opengl()
index f282554fec9df80c558dedaa5b29802ec0276a69..b8a2cc224da61d65a2a352b2a404912dbbf2bbbf 100644 (file)
 
 #include "colorpicker.h"
 #include "guicast.h"
+#include "theme.h"
 #include "loadbalance.h"
 #include "pluginvclient.h"
 
+#define RESET_DEFAULT_SETTINGS 10
+#define RESET_ALL               0
+#define RESET_RGB               1
+#define RESET_SLOPE             2
+#define RESET_THRESHOLD         3
+
+#define MIN_VALUE   0.00
+#define MAX_VALUE 100.00
 
 class ChromaKey;
 class ChromaKey;
 class ChromaKeyWindow;
+class ChromaKeyFText;
+class ChromaKeyFSlider;
+class ChromaKeyReset;
+class ChromaKeyDefaultSettings;
+class ChromaKeyClr;
 
 class ChromaKeyConfig
 {
 public:
        ChromaKeyConfig();
-       void reset();
+       void reset(int clear);
        void copy_from(ChromaKeyConfig &src);
        int equivalent(ChromaKeyConfig &src);
        void interpolate(ChromaKeyConfig &prev,
@@ -93,6 +107,44 @@ public:
        ChromaKey *plugin;
 };
 
+class ChromaKeyFText : public BC_TumbleTextBox
+{
+public:
+       ChromaKeyFText(ChromaKey *plugin, ChromaKeyWindow *gui,
+               ChromaKeyFSlider *slider, float *output, int x, int y, float min, float max);
+       ~ChromaKeyFText();
+       int handle_event();
+       ChromaKey *plugin;
+       ChromaKeyWindow *gui;
+       ChromaKeyFSlider *slider;
+       float *output;
+       float min, max;
+};
+
+class ChromaKeyFSlider : public BC_FSlider
+{
+public:
+       ChromaKeyFSlider(ChromaKey *plugin,
+               ChromaKeyFText *text, float *output, int x, int y,
+               float min, float max, int w);
+       ~ChromaKeyFSlider();
+       int handle_event();
+       ChromaKey *plugin;
+       ChromaKeyFText *text;
+       float *output;
+};
+
+class ChromaKeyClr : public BC_Button
+{
+public:
+       ChromaKeyClr(ChromaKey *plugin, ChromaKeyWindow *gui, int x, int y, int clear);
+       ~ChromaKeyClr();
+       int handle_event();
+       ChromaKey *plugin;
+       ChromaKeyWindow *gui;
+       int clear;
+};
+
 class ChromaKeyReset : public BC_GenericButton
 {
 public:
@@ -102,6 +154,16 @@ public:
        ChromaKeyWindow *gui;
 };
 
+class ChromaKeyDefaultSettings : public BC_GenericButton
+{
+public:
+       ChromaKeyDefaultSettings(ChromaKey *plugin, ChromaKeyWindow *gui, int x, int y, int w);
+       ~ChromaKeyDefaultSettings();
+       int handle_event();
+       ChromaKey *plugin;
+       ChromaKeyWindow *gui;
+};
+
 class ChromaKeyUseColorPicker : public BC_GenericButton
 {
 public:
@@ -129,16 +191,25 @@ public:
        ~ChromaKeyWindow();
 
        void create_objects();
-       void update_gui();
+       void update_gui(int clear);
        void update_sample();
        void done_event(int result);
 
        ChromaKeyColor *color;
-       ChromaKeyThreshold *threshold;
+
+       ChromaKeyFText *threshold_text;
+       ChromaKeyFSlider *threshold_slider;
+       ChromaKeyClr *threshold_Clr;
+
        ChromaKeyUseValue *use_value;
        ChromaKeyUseColorPicker *use_colorpicker;
-       ChromaKeySlope *slope;
+
+       ChromaKeyFText *slope_text;
+       ChromaKeyFSlider *slope_slider;
+       ChromaKeyClr *slope_Clr;
+
        ChromaKeyReset *reset;
+       ChromaKeyDefaultSettings *default_settings;
        BC_SubWindow *sample;
        ChromaKey *plugin;
        ChromaKeyColorThread *color_thread;
diff --git a/cinelerra-5.1/plugins/chromakeyavid/Makefile b/cinelerra-5.1/plugins/chromakeyavid/Makefile
new file mode 100644 (file)
index 0000000..934f5fd
--- /dev/null
@@ -0,0 +1,13 @@
+include ../../plugin_defs
+
+PLUGIN = chromakeyavid
+OUTPUT_BINS := $(OBJDIR)/chromakeyavid_sl.o
+OBJS := $(OBJDIR)/chromakey.o
+
+include ../../plugin_config
+
+$(OBJDIR)/chromakey.o: chromakey.C
+$(OUTPUT_BINS): chromakeyavid.sl
+       cd $(OBJDIR) && \
+       ../../../guicast/$(OBJDIR)/bootstrap -s chromakeyavid_sl.o ../chromakeyavid.sl
+
diff --git a/cinelerra-5.1/plugins/chromakeyavid/chromakey.C b/cinelerra-5.1/plugins/chromakeyavid/chromakey.C
new file mode 100644 (file)
index 0000000..a1324bd
--- /dev/null
@@ -0,0 +1,1738 @@
+/*
+ * CINELERRA
+ * Copyright (C) 2012-2024 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+// original chromakey HSV author:
+// https://www.mail-archive.com/cinelerra@skolelinux.no/msg00379.html
+// http://jcornet.free.fr/linux/chromakey.html
+
+// He was trying to replicate the Avid SpectraMatte:
+// https://resources.avid.com/SupportFiles/attach/Symphony_Effects_and_CC_Guide_v5.5.pdf
+// page 691
+// but the problem seems to be harder than he thought
+
+// A rewrite got a slightly different spill light processing
+// but still not very useful:
+// https://github.com/vanakala/cinelerra-cve/blob/master/plugins/chromakeyhsv/chromakey.C
+
+// The current implementation is most of the Spectramatte algorithm.
+// The only way to test it is to use the color swatch plugin.
+// Fix brightness to test the saturation wedge.
+// Fix saturation to test the brightness wedge.
+
+// There are boundary artifacts caused by the color swatch showing a slice
+// of a cube.
+// If out slope > 0, constant 0% saturation or 0% brightness is all wedge.
+// If out slope == 0, constant 0% saturation or constant 0% brightness has no wedge
+// The general idea is if it's acting weird, switch between constant brightness
+// & constant saturation.
+
+// TODO: integrated color swatch & alpha blur, but it takes a lot of space in the GUI.
+
+#include "bcdisplayinfo.h"
+#include "bcsignals.h"
+#include "chromakey.h"
+#include "clip.h"
+#include "bchash.h"
+#include "filexml.h"
+#include "guicast.h"
+#include "keyframe.h"
+#include "language.h"
+#include "loadbalance.h"
+#include "playback3d.h"
+#include "bccolors.h"
+#include "pluginvclient.h"
+#include "vframe.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#define WINDOW_W xS(550)
+#define WINDOW_H yS(600)
+#define SLIDER_W xS(250)
+#define MAX_SLOPE 100.0
+#define MAX_VALUE 100.0
+
+ChromaKeyConfig::ChromaKeyConfig ()
+{
+  red   = 0.0;
+  green = 1.0;
+  blue  = 0.0;
+
+  min_brightness   =  50.0;
+  max_brightness   = 100.0;
+  tolerance        =  15.0;
+  saturation_start =   0.0;
+  saturation_line  =  50.0;
+
+  in_slope     = 2;
+  out_slope    = 2;
+  alpha_offset = 0;
+
+  spill_saturation = 0.0;
+  spill_angle      = 0.0;
+  desaturate_only  = 0;
+
+  show_mask = 0;
+
+  undo_red               = red;
+  undo_green             = green;
+  undo_blue              = blue;
+  undo_min_brightness    = min_brightness;
+  undo_max_brightness    = max_brightness;
+  undo_tolerance         = tolerance;
+  undo_saturation_start  = saturation_start;
+  undo_saturation_line   = saturation_line;
+  undo_in_slope          = in_slope;
+  undo_out_slope         = out_slope;
+  undo_alpha_offset      = alpha_offset;
+  undo_spill_saturation  = spill_saturation;
+  undo_spill_angle       = spill_angle;
+  undo_desaturate_only   = desaturate_only;
+  undo_show_mask         = show_mask;
+
+  store_red              = red;
+  store_green            = green;
+  store_blue             = blue;
+  store_min_brightness   = min_brightness;
+  store_max_brightness   = max_brightness;
+  store_tolerance        = tolerance;
+  store_saturation_start = saturation_start;
+  store_saturation_line  = saturation_line;
+  store_in_slope         = in_slope;
+  store_out_slope        = out_slope;
+  store_alpha_offset     = alpha_offset;
+  store_spill_saturation = spill_saturation;
+  store_spill_angle      = spill_angle;
+  store_desaturate_only  = desaturate_only;
+  store_show_mask        = show_mask;
+}
+
+void ChromaKeyConfig::reset(int clear)
+{
+  switch(clear)
+  {
+  case RESET_RGB:
+    red   = 0.0;
+    green = 1.0;
+    blue  = 0.0;
+    break;
+  case RESET_MIN_BRIGHTNESS:
+    min_brightness = 50.0;
+    break;
+  case RESET_MAX_BRIGHTNESS:
+    max_brightness = 100.0;
+    break;
+  case RESET_TOLERANCE:
+    tolerance = 15.0;
+    break;
+  case RESET_SATURATION_START:
+    saturation_start = 0.0;
+    break;
+  case RESET_SATURATION_LINE:
+    saturation_line = 50.0;
+    break;
+  case RESET_IN_SLOPE:
+    in_slope = 2;
+    break;
+  case RESET_OUT_SLOPE:
+    out_slope = 2;
+    break;
+  case RESET_ALPHA_OFFSET:
+    alpha_offset = 0;
+    break;
+  case RESET_SPILL_SATURATION:
+    spill_saturation = 0.0;
+    break;
+  case RESET_SPILL_ANGLE:
+    spill_angle = 0.0;
+    break;
+  case RESET_ALL:
+  case RESET_DEFAULT_SETTINGS:
+  default:
+    undo_red              = red;
+    undo_green            = green;
+    undo_blue             = blue;
+    undo_min_brightness   = min_brightness;
+    undo_max_brightness   = max_brightness;
+    undo_tolerance        = tolerance;
+    undo_saturation_start = saturation_start;
+    undo_saturation_line  = saturation_line;
+    undo_in_slope         = in_slope;
+    undo_out_slope        = out_slope;
+    undo_alpha_offset     = alpha_offset;
+    undo_spill_saturation = spill_saturation;
+    undo_spill_angle      = spill_angle;
+    undo_desaturate_only  = desaturate_only;
+    undo_show_mask        = show_mask;
+
+    red   = 0.0;
+    green = 1.0;
+    blue  = 0.0;
+
+    min_brightness   =  50.0;
+    max_brightness   = 100.0;
+    tolerance        =  15.0;
+    saturation_start =   0.0;
+    saturation_line  =  50.0;
+
+    in_slope     = 2;
+    out_slope    = 2;
+    alpha_offset = 0;
+
+    spill_saturation = 0.0;
+    spill_angle      = 0.0;
+    desaturate_only  = 0;
+
+    show_mask = 0;
+    break;
+  }
+}
+
+
+void
+ChromaKeyConfig::copy_from (ChromaKeyConfig & src)
+{
+  red              = src.red;
+  green            = src.green;
+  blue             = src.blue;
+  spill_saturation = src.spill_saturation;
+  spill_angle      = src.spill_angle;
+  min_brightness   = src.min_brightness;
+  max_brightness   = src.max_brightness;
+  saturation_start = src.saturation_start;
+  saturation_line  = src.saturation_line;
+  tolerance        = src.tolerance;
+  in_slope         = src.in_slope;
+  out_slope        = src.out_slope;
+  alpha_offset     = src.alpha_offset;
+  show_mask        = src.show_mask;
+  desaturate_only  = src.desaturate_only;
+
+  undo_red              = src.red;
+  undo_green            = src.green;
+  undo_blue             = src.blue;
+  undo_min_brightness   = src.min_brightness;
+  undo_max_brightness   = src.max_brightness;
+  undo_tolerance        = src.tolerance;
+  undo_saturation_start = src.saturation_start;
+  undo_saturation_line  = src.saturation_line;
+  undo_in_slope         = src.in_slope;
+  undo_out_slope        = src.out_slope;
+  undo_alpha_offset     = src.alpha_offset;
+  undo_spill_saturation = src.spill_saturation;
+  undo_spill_angle      = src.spill_angle;
+  undo_desaturate_only  = src.desaturate_only;
+  undo_show_mask        = src.show_mask;
+}
+
+int
+ChromaKeyConfig::equivalent (ChromaKeyConfig & src)
+{
+  return (EQUIV (red,              src.red)              &&
+         EQUIV (green,            src.green)            &&
+         EQUIV (blue,             src.blue)             &&
+         EQUIV (spill_saturation, src.spill_saturation) &&
+         EQUIV (spill_angle,      src.spill_angle)      &&
+         EQUIV (min_brightness,   src.min_brightness)   &&
+         EQUIV (max_brightness,   src.max_brightness)   &&
+         EQUIV (saturation_start, src.saturation_start) &&
+         EQUIV (saturation_line,  src.saturation_line)  &&
+         EQUIV (tolerance,        src.tolerance)        &&
+         EQUIV (in_slope,         src.in_slope)         &&
+         EQUIV (out_slope,        src.out_slope)        &&
+         EQUIV (alpha_offset,     src.alpha_offset)     &&
+         show_mask       == src.show_mask &&
+         desaturate_only == src.desaturate_only);
+}
+
+void
+ChromaKeyConfig::interpolate (ChromaKeyConfig & prev,
+                             ChromaKeyConfig & next,
+                             int64_t prev_frame,
+                             int64_t next_frame,
+                             int64_t current_frame)
+{
+  double next_scale =
+    (double) (current_frame - prev_frame) / (next_frame - prev_frame);
+  double prev_scale =
+    (double) (next_frame - current_frame) / (next_frame - prev_frame);
+
+  this->red   = prev.red   * prev_scale + next.red   * next_scale;
+  this->green = prev.green * prev_scale + next.green * next_scale;
+  this->blue  = prev.blue  * prev_scale + next.blue  * next_scale;
+  this->spill_saturation =
+    prev.spill_saturation * prev_scale + next.spill_saturation * next_scale;
+  this->spill_angle =
+    prev.spill_angle * prev_scale + next.tolerance * next_scale;
+  this->min_brightness =
+    prev.min_brightness * prev_scale + next.min_brightness * next_scale;
+  this->max_brightness =
+    prev.max_brightness * prev_scale + next.max_brightness * next_scale;
+  this->saturation_start =
+    prev.saturation_start * prev_scale + next.saturation_start * next_scale;
+  this->saturation_line =
+    prev.saturation_line * prev_scale + next.saturation_line * next_scale;
+  this->tolerance = prev.tolerance * prev_scale + next.tolerance * next_scale;
+  this->in_slope  = prev.in_slope  * prev_scale + next.in_slope  * next_scale;
+  this->out_slope = prev.out_slope * prev_scale + next.out_slope * next_scale;
+  this->alpha_offset =
+    prev.alpha_offset * prev_scale + next.alpha_offset * next_scale;
+  this->show_mask       = prev.show_mask;
+  this->desaturate_only = prev.desaturate_only;
+
+  this->undo_red              = this->red;
+  this->undo_green            = this->green;
+  this->undo_blue             = this->blue;
+  this->undo_min_brightness   = this->min_brightness;
+  this->undo_max_brightness   = this->max_brightness;
+  this->undo_tolerance        = this->tolerance;
+  this->undo_saturation_start = this->saturation_start;
+  this->undo_saturation_line  = this->saturation_line;
+  this->undo_in_slope         = this->in_slope;
+  this->undo_out_slope        = this->out_slope;
+  this->undo_alpha_offset     = this->alpha_offset;
+  this->undo_spill_saturation = this->spill_saturation;
+  this->undo_spill_angle      = this->spill_angle;
+  this->undo_desaturate_only  = this->desaturate_only;
+  this->undo_show_mask        = this->show_mask;
+}
+
+int
+ChromaKeyConfig::get_color ()
+{
+  int red   = (int) (CLIP (this->red,   0, 1) * 0xff);
+  int green = (int) (CLIP (this->green, 0, 1) * 0xff);
+  int blue  = (int) (CLIP (this->blue,  0, 1) * 0xff);
+  return (red << 16) | (green << 8) | blue;
+}
+
+
+
+ChromaKeyWindow::ChromaKeyWindow (ChromaKeyAvid * plugin)
+  : PluginClientWindow(plugin,
+                      WINDOW_W, 
+                      WINDOW_H, 
+                      WINDOW_W, 
+                      WINDOW_H, 
+                      0)
+{
+  this->plugin = plugin;
+  color_thread = 0;
+}
+
+ChromaKeyWindow::~ChromaKeyWindow ()
+{
+  delete color_thread;
+}
+
+void ChromaKeyWindow::create_objects ()
+{
+  int xs10 = xS(10), xs20 = xS(20), xs100 = xS(100), wslid = SLIDER_W;
+  int ys10 = yS(10), ys20 = yS(20), ys30 = yS(30), ys40 = yS(40), ys50 = yS(50);
+  int y = ys10, x2 = xS(160), x3 = xS(260), xs5 = xS(5);
+  int x = xs10;
+  int clr_x = get_w()-x - xS(22); // note: clrBtn_w = 22
+
+  BC_Title *title;
+  BC_Bar *bar;
+  BC_TitleBar *title_bar;
+
+// Color section
+  add_subwindow (title_bar =
+                new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, _("Color")));
+  y += ys20;
+
+  add_subwindow (color = new ChromaKeyColor (plugin, this, x, y));
+  // Info for Sample rectangle:       x_slider w_slider w_sample
+  //                                        \       |      /    y,   w,     h
+  add_subwindow (sample = new BC_SubWindow (x3 + wslid - xs100, y, xs100, ys50));
+  y += ys30;
+  add_subwindow (use_colorpicker = new ChromaKeyUseColorPicker (plugin, this, x, y));
+  y += ys30;
+  add_subwindow (show_mask =
+                new ChromaKeyToggle (plugin, x, y, &plugin->config.show_mask,
+                                     _("Show Mask")));
+
+// Key parameters section
+  y += ys30;
+  add_subwindow (title_bar =
+                new BC_TitleBar (x, y, get_w()-2*x, xs20, xs10,
+                                 _("Key parameters")));
+  y += ys20;
+  add_subwindow (title = new BC_Title (x, y, _("Hue Tolerance:")));
+  tolerance_text = new ChromaKeyText (plugin, this, 0, x+x2, y, 0, MAX_VALUE,
+                                     &plugin->config.tolerance);
+  tolerance_text->create_objects();
+  tolerance = new ChromaKeySlider (plugin, tolerance_text, x3, y, wslid,
+                                  0, MAX_VALUE, &plugin->config.tolerance);
+  add_subwindow (tolerance);
+  tolerance_text->slider = tolerance;
+  clr_x = x3 + tolerance->get_w() + x;
+  add_subwindow (tolerance_clr =
+                new ChromaKeyClr (plugin, this, clr_x, y, RESET_TOLERANCE));
+  y += ys30;
+
+  add_subwindow (title = new BC_Title (x, y, _("Min. Brightness:")));
+  min_brightness_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
+                                          0, MAX_VALUE,
+                                          &plugin->config.min_brightness);
+  min_brightness_text->create_objects();
+  min_brightness = new ChromaKeySlider (plugin, min_brightness_text,
+                                       x3, y, wslid, 0, MAX_VALUE,
+                                       &plugin->config.min_brightness);
+  add_subwindow(min_brightness);
+  min_brightness_text->slider = min_brightness;
+  add_subwindow (min_brightness_clr =
+                new ChromaKeyClr (plugin, this, clr_x, y,
+                                  RESET_MIN_BRIGHTNESS));
+  y += ys30;
+
+  add_subwindow (title = new BC_Title (x, y, _("Max. Brightness:")));
+  max_brightness_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
+                                          0, MAX_VALUE,
+                                          &plugin->config.max_brightness);
+  max_brightness_text->create_objects();
+  max_brightness = new ChromaKeySlider (plugin, max_brightness_text,
+                                       x3, y, wslid, 0, MAX_VALUE,
+                                       &plugin->config.max_brightness);
+  add_subwindow(max_brightness);
+  max_brightness_text->slider = max_brightness;
+  add_subwindow (max_brightness_clr =
+                new ChromaKeyClr (plugin, this, clr_x, y,
+                                  RESET_MAX_BRIGHTNESS));
+  y += ys30;
+
+  add_subwindow (title = new BC_Title (x, y, _("Saturation Start:")));
+  saturation_start_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
+                                            0, MAX_VALUE,
+                                            &plugin->config.saturation_start);
+  saturation_start_text->create_objects();
+  saturation_start = new ChromaKeySlider (plugin, saturation_start_text,
+                                         x3, y, wslid, 0, MAX_VALUE,
+                                         &plugin->config.saturation_start);
+  add_subwindow(saturation_start);
+  saturation_start_text->slider = saturation_start;
+  add_subwindow (saturation_start_clr =
+                new ChromaKeyClr (plugin, this, clr_x, y,
+                                  RESET_SATURATION_START));
+  y += ys30;
+
+  add_subwindow (title = new BC_Title (x, y, _("Saturation Line:")));
+  saturation_line_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
+                                           0, MAX_VALUE,
+                                           &plugin->config.saturation_line);
+  saturation_line_text->create_objects();
+  saturation_line = new ChromaKeySlider (plugin, saturation_line_text,
+                                        x3, y, wslid, 0, MAX_VALUE,
+                                        &plugin->config.saturation_line);
+  add_subwindow(saturation_line);
+  saturation_line_text->slider = saturation_line;
+  add_subwindow (saturation_line_clr =
+                new ChromaKeyClr (plugin, this, clr_x, y,
+                                  RESET_SATURATION_LINE));
+  y += ys40;
+
+// Mask tweaking section
+  add_subwindow (title_bar =
+                new BC_TitleBar (x, y, get_w()-2*x, xs20, xs10,
+                                 _("Mask tweaking")));
+  y += ys20;
+  add_subwindow (title = new BC_Title (x, y, _("In Slope:")));
+  in_slope_text = new ChromaKeyText (plugin, this, 0, x+x2, y, 0, MAX_SLOPE,
+                                    &plugin->config.in_slope);
+  in_slope_text->create_objects();
+  in_slope = new ChromaKeySlider (plugin, in_slope_text, x3, y, wslid,
+                                 0, MAX_SLOPE, &plugin->config.in_slope);
+  add_subwindow(in_slope);
+  in_slope_text->slider = in_slope;
+  add_subwindow (in_slope_clr =
+                new ChromaKeyClr (plugin, this, clr_x, y, RESET_IN_SLOPE));
+  y += ys30;
+
+  add_subwindow (title = new BC_Title (x, y, _("Out Slope:")));
+  out_slope_text = new ChromaKeyText (plugin, this, 0, x+x2, y, 0, MAX_SLOPE,
+                                     &plugin->config.out_slope);
+  out_slope_text->create_objects();
+  out_slope = new ChromaKeySlider (plugin, out_slope_text, x3, y, wslid,
+                                  0, MAX_SLOPE, &plugin->config.out_slope);
+  add_subwindow(out_slope);
+  out_slope_text->slider = out_slope;
+  add_subwindow (out_slope_clr =
+                new ChromaKeyClr (plugin, this, clr_x, y, RESET_OUT_SLOPE));
+  y += ys30;
+
+  add_subwindow (title = new BC_Title (x, y, _("Alpha Offset:")));
+  alpha_offset_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
+                                        -MAX_VALUE, MAX_VALUE,
+                                        &plugin->config.alpha_offset);
+  alpha_offset_text->create_objects();
+  alpha_offset = new ChromaKeySlider (plugin, alpha_offset_text, x3, y, wslid,
+                                     -MAX_VALUE, MAX_VALUE,
+                                     &plugin->config.alpha_offset);
+  add_subwindow(alpha_offset);
+  alpha_offset_text->slider = alpha_offset;
+  add_subwindow (alpha_offset_clr =
+                new ChromaKeyClr (plugin, this, clr_x, y, RESET_ALPHA_OFFSET));
+  y += ys40;
+
+// Spill light control section
+  add_subwindow (title_bar =
+                new BC_TitleBar (x, y, get_w()-2*x, xs20, xs10,
+                                 _("Spill light control")));
+  y += ys20;
+  add_subwindow (title = new BC_Title (x, y, _("Spill Saturation:")));
+  spill_saturation_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
+                                            0, MAX_VALUE,
+                                            &plugin->config.spill_saturation);
+  spill_saturation_text->create_objects();
+  spill_saturation = new ChromaKeySlider (plugin, spill_saturation_text,
+                                         x3, y, wslid, 0, MAX_VALUE,
+                                         &plugin->config.spill_saturation);
+  add_subwindow(spill_saturation);
+  spill_saturation_text->slider = spill_saturation;
+  add_subwindow (spill_saturation_clr =
+                new ChromaKeyClr (plugin, this, clr_x, y,
+                                  RESET_SPILL_SATURATION));
+  y += ys30;
+
+  add_subwindow (title = new BC_Title (x, y, _("Spill Angle:")));
+  spill_angle_text = new ChromaKeyText (plugin, this, 0, x+x2, y, 0, MAX_VALUE,
+                                       &plugin->config.spill_angle);
+  spill_angle_text->create_objects();
+  spill_angle = new ChromaKeySlider (plugin, spill_angle_text, x3, y, wslid,
+                                    0, MAX_VALUE, &plugin->config.spill_angle);
+  add_subwindow(spill_angle);
+  spill_angle_text->slider = spill_angle;
+  add_subwindow (spill_angle_clr =
+                new ChromaKeyClr (plugin, this, clr_x, y, RESET_SPILL_ANGLE));
+  y += ys30;
+
+  add_subwindow(desaturate_only =
+               new ChromaKeyToggle(plugin, x, y,
+                                   &plugin->config.desaturate_only, 
+                                   _("Desaturate Only")));
+  y += ys40;
+
+// Global buttons section
+  add_subwindow (bar = new BC_Bar(x, y, get_w()-2*x));
+  y += ys20;
+  add_subwindow (store = new ChromaKeyStore (plugin, this, x, y));
+  x += store->get_w() + xs5;
+  add_subwindow (recall = new ChromaKeyRecall (plugin, this, x, y));
+  x += recall->get_w() + xs5;
+  add_subwindow (exchange = new ChromaKeyExchange (plugin, this, x, y));
+  x += exchange->get_w() + xs5;
+  add_subwindow (undo = new ChromaKeyUndo (plugin, this, x, y));
+  x += undo->get_w() + xs5;
+  add_subwindow (reset = new ChromaKeyReset (plugin, this, x, y));
+
+  color_thread = new ChromaKeyColorThread (plugin, this);
+
+  update_sample ();
+  show_window ();
+}
+
+void
+ChromaKeyWindow::update_sample ()
+{
+  sample->set_color (plugin->config.get_color ());
+  sample->draw_box (0, 0, sample->get_w (), sample->get_h ());
+  sample->set_color (BLACK);
+  sample->draw_rectangle (0, 0, sample->get_w (), sample->get_h ());
+  sample->flash ();
+}
+
+void ChromaKeyWindow::done_event(int result)
+{
+  color_thread->close_window();
+}
+
+
+ChromaKeyColor::ChromaKeyColor (ChromaKeyAvid * plugin,
+                               ChromaKeyWindow * gui, int x, int y)
+  : BC_GenericButton (x, y, _("Color..."))
+{
+  this->plugin = plugin;
+  this->gui = gui;
+}
+
+int
+ChromaKeyColor::handle_event ()
+{
+  gui->color_thread->start_window (plugin->config.get_color (), 0xff);
+  return 1;
+}
+
+
+
+ChromaKeyText::ChromaKeyText(ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+                            ChromaKeySlider *slider, int x, int y,
+                            float min, float max, float *output)
+  : BC_TumbleTextBox(gui, *output, min, max, x, y, xS(60), 2)
+{
+  this->plugin = plugin;
+  this->gui = gui;
+  this->slider = slider;
+  this->min = min;
+  this->max = max;
+  this->output = output;
+  set_increment(0.01);
+}
+
+ChromaKeyText::~ChromaKeyText()
+{
+}
+
+int ChromaKeyText::handle_event()
+{
+  *output = atof(get_text());
+  if(*output > max) *output = max;
+  if(*output < min) *output = min;
+  slider->update(*output);
+  plugin->send_configure_change();
+  return 1;
+}
+
+ChromaKeySlider::ChromaKeySlider(ChromaKeyAvid *plugin, ChromaKeyText *text,
+                                int x, int y, int w,
+                                float min, float max, float *output)
+  : BC_FSlider(x, y, 0, w, w, min, max, *output)
+{
+  this->plugin = plugin;
+  this->text = text;
+  this->output = output;
+  set_precision(0.01);
+  enable_show_value(0); // Hide caption
+}
+
+ChromaKeySlider::~ChromaKeySlider()
+{
+}
+
+int ChromaKeySlider::handle_event()
+{
+  *output = get_value();
+  text->update(*output);
+  plugin->send_configure_change();
+  return 1;
+}
+
+ChromaKeyClr::ChromaKeyClr(ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+                          int x, int y, int clear)
+  : BC_Button(x, y, plugin->get_theme()->get_image_set("reset_button"))
+{
+  this->plugin = plugin;
+  this->gui = gui;
+  this->clear = clear;
+}
+
+ChromaKeyClr::~ChromaKeyClr()
+{
+}
+
+int ChromaKeyClr::handle_event()
+{
+  plugin->config.reset(clear);
+  gui->update_gui(clear);
+  plugin->send_configure_change();
+  return 1;
+}
+
+ChromaKeyToggle::ChromaKeyToggle (ChromaKeyAvid * plugin,
+                                 int x,
+                                 int y,
+                                 bool *output,
+                                 const char *caption)
+  : BC_CheckBox (x,
+                y,
+                *output ? 1 : 0,
+                caption)
+{
+  this->plugin = plugin;
+  this->output = output;
+}
+
+int
+ChromaKeyToggle::handle_event ()
+{
+  *output = get_value() ? true : false;
+  plugin->send_configure_change ();
+  return 1;
+}
+
+
+ChromaKeyReset::ChromaKeyReset (ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+                               int x, int y)
+  : BC_GenericButton(x, y, _("Reset"))
+{
+  this->plugin = plugin;
+  this->gui = gui;
+}
+
+int ChromaKeyReset::handle_event ()
+{
+  plugin->config.reset(RESET_DEFAULT_SETTINGS);
+  gui->update_gui(RESET_DEFAULT_SETTINGS);
+  plugin->send_configure_change();
+  return 1;
+}
+
+ChromaKeyStore::ChromaKeyStore (ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+                               int x, int y)
+  : BC_GenericButton(x, y, _("Store"))
+{
+  this->plugin = plugin;
+  this->gui = gui;
+}
+
+int ChromaKeyStore::handle_event ()
+{
+  plugin->config.store_red              = plugin->config.red;
+  plugin->config.store_green            = plugin->config.green;
+  plugin->config.store_blue             = plugin->config.blue;
+  plugin->config.store_min_brightness   = plugin->config.min_brightness;
+  plugin->config.store_max_brightness   = plugin->config.max_brightness;
+  plugin->config.store_tolerance        = plugin->config.tolerance;
+  plugin->config.store_saturation_start = plugin->config.saturation_start;
+  plugin->config.store_saturation_line  = plugin->config.saturation_line;
+  plugin->config.store_in_slope         = plugin->config.in_slope;
+  plugin->config.store_out_slope        = plugin->config.out_slope;
+  plugin->config.store_alpha_offset     = plugin->config.alpha_offset;
+  plugin->config.store_spill_saturation = plugin->config.spill_saturation;
+  plugin->config.store_spill_angle      = plugin->config.spill_angle;
+  plugin->config.store_desaturate_only  = plugin->config.desaturate_only;
+  plugin->config.store_show_mask        = plugin->config.show_mask;
+  return 1;
+}
+
+ChromaKeyRecall::ChromaKeyRecall (ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+                                 int x, int y)
+  : BC_GenericButton(x, y, _("Recall"))
+{
+  this->plugin = plugin;
+  this->gui = gui;
+}
+
+int ChromaKeyRecall::handle_event ()
+{
+  plugin->config.undo_red              = plugin->config.red;
+  plugin->config.undo_green            = plugin->config.green;
+  plugin->config.undo_blue             = plugin->config.blue;
+  plugin->config.undo_min_brightness   = plugin->config.min_brightness;
+  plugin->config.undo_max_brightness   = plugin->config.max_brightness;
+  plugin->config.undo_tolerance        = plugin->config.tolerance;
+  plugin->config.undo_saturation_start = plugin->config.saturation_start;
+  plugin->config.undo_saturation_line  = plugin->config.saturation_line;
+  plugin->config.undo_in_slope         = plugin->config.in_slope;
+  plugin->config.undo_out_slope        = plugin->config.out_slope;
+  plugin->config.undo_alpha_offset     = plugin->config.alpha_offset;
+  plugin->config.undo_spill_saturation = plugin->config.spill_saturation;
+  plugin->config.undo_spill_angle      = plugin->config.spill_angle;
+  plugin->config.undo_desaturate_only  = plugin->config.desaturate_only;
+  plugin->config.undo_show_mask        = plugin->config.show_mask;
+
+  plugin->config.red              = plugin->config.store_red;
+  plugin->config.green            = plugin->config.store_green;
+  plugin->config.blue             = plugin->config.store_blue;
+  plugin->config.min_brightness   = plugin->config.store_min_brightness;
+  plugin->config.max_brightness   = plugin->config.store_max_brightness;
+  plugin->config.tolerance        = plugin->config.store_tolerance;
+  plugin->config.saturation_start = plugin->config.store_saturation_start;
+  plugin->config.saturation_line  = plugin->config.store_saturation_line;
+  plugin->config.in_slope         = plugin->config.store_in_slope;
+  plugin->config.out_slope        = plugin->config.store_out_slope;
+  plugin->config.alpha_offset     = plugin->config.store_alpha_offset;
+  plugin->config.spill_saturation = plugin->config.store_spill_saturation;
+  plugin->config.spill_angle      = plugin->config.store_spill_angle;
+  plugin->config.desaturate_only  = plugin->config.store_desaturate_only;
+  plugin->config.show_mask        = plugin->config.store_show_mask;
+
+  gui->update_gui(RESET_DEFAULT_SETTINGS);
+  plugin->send_configure_change();
+  return 1;
+}
+
+ChromaKeyExchange::ChromaKeyExchange (ChromaKeyAvid *plugin,
+                                     ChromaKeyWindow *gui, int x, int y)
+  : BC_GenericButton(x, y, _("Exchange"))
+{
+  this->plugin = plugin;
+  this->gui = gui;
+}
+
+#define CHROMAKEY_SWAP(what,typ,elem) \
+tmp_##typ = plugin->config.what##_##elem; \
+plugin->config.what##_##elem = plugin->config.elem; \
+plugin->config.elem = tmp_##typ;
+
+int ChromaKeyExchange::handle_event ()
+{
+  float tmp_flt;
+  bool tmp_bool;
+
+  plugin->config.undo_red              = plugin->config.red;
+  plugin->config.undo_green            = plugin->config.green;
+  plugin->config.undo_blue             = plugin->config.blue;
+  plugin->config.undo_min_brightness   = plugin->config.min_brightness;
+  plugin->config.undo_max_brightness   = plugin->config.max_brightness;
+  plugin->config.undo_tolerance        = plugin->config.tolerance;
+  plugin->config.undo_saturation_start = plugin->config.saturation_start;
+  plugin->config.undo_saturation_line  = plugin->config.saturation_line;
+  plugin->config.undo_in_slope         = plugin->config.in_slope;
+  plugin->config.undo_out_slope        = plugin->config.out_slope;
+  plugin->config.undo_alpha_offset     = plugin->config.alpha_offset;
+  plugin->config.undo_spill_saturation = plugin->config.spill_saturation;
+  plugin->config.undo_spill_angle      = plugin->config.spill_angle;
+  plugin->config.undo_desaturate_only  = plugin->config.desaturate_only;
+  plugin->config.undo_show_mask        = plugin->config.show_mask;
+
+  CHROMAKEY_SWAP (store, flt,  red)
+  CHROMAKEY_SWAP (store, flt,  green)
+  CHROMAKEY_SWAP (store, flt,  blue)
+  CHROMAKEY_SWAP (store, flt,  min_brightness)
+  CHROMAKEY_SWAP (store, flt,  max_brightness)
+  CHROMAKEY_SWAP (store, flt,  tolerance)
+  CHROMAKEY_SWAP (store, flt,  saturation_start)
+  CHROMAKEY_SWAP (store, flt,  saturation_line)
+  CHROMAKEY_SWAP (store, flt,  in_slope)
+  CHROMAKEY_SWAP (store, flt,  out_slope)
+  CHROMAKEY_SWAP (store, flt,  alpha_offset)
+  CHROMAKEY_SWAP (store, flt,  spill_saturation)
+  CHROMAKEY_SWAP (store, flt,  spill_angle)
+  CHROMAKEY_SWAP (store, bool, desaturate_only)
+  CHROMAKEY_SWAP (store, bool, show_mask)
+
+  gui->update_gui(RESET_DEFAULT_SETTINGS);
+  plugin->send_configure_change();
+  return 1;
+}
+
+ChromaKeyUndo::ChromaKeyUndo (ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+                             int x, int y)
+  : BC_GenericButton(x, y, _("Undo"))
+{
+  this->plugin = plugin;
+  this->gui = gui;
+}
+
+int ChromaKeyUndo::handle_event ()
+{
+  //float tmp_flt;
+  //bool tmp_bool;
+
+  //CHROMAKEY_SWAP (undo, flt,  red)
+  //CHROMAKEY_SWAP (undo, flt,  green)
+  //CHROMAKEY_SWAP (undo, flt,  blue)
+  //CHROMAKEY_SWAP (undo, flt,  min_brightness)
+  //CHROMAKEY_SWAP (undo, flt,  max_brightness)
+  //CHROMAKEY_SWAP (undo, flt,  tolerance)
+  //CHROMAKEY_SWAP (undo, flt,  saturation_start)
+  //CHROMAKEY_SWAP (undo, flt,  saturation_line)
+  //CHROMAKEY_SWAP (undo, flt,  in_slope)
+  //CHROMAKEY_SWAP (undo, flt,  out_slope)
+  //CHROMAKEY_SWAP (undo, flt,  alpha_offset)
+  //CHROMAKEY_SWAP (undo, flt,  spill_saturation)
+  //CHROMAKEY_SWAP (undo, flt,  spill_angle)
+  //CHROMAKEY_SWAP (undo, bool, desaturate_only)
+  //CHROMAKEY_SWAP (undo, bool, show_mask)
+
+  // better not to swap but restore undo values
+
+  plugin->config.red              = plugin->config.undo_red;
+  plugin->config.green            = plugin->config.undo_green;
+  plugin->config.blue             = plugin->config.undo_blue;
+  plugin->config.min_brightness   = plugin->config.undo_min_brightness;
+  plugin->config.max_brightness   = plugin->config.undo_max_brightness;
+  plugin->config.tolerance        = plugin->config.undo_tolerance;
+  plugin->config.saturation_start = plugin->config.undo_saturation_start;
+  plugin->config.saturation_line  = plugin->config.undo_saturation_line;
+  plugin->config.in_slope         = plugin->config.undo_in_slope;
+  plugin->config.out_slope        = plugin->config.undo_out_slope;
+  plugin->config.alpha_offset     = plugin->config.undo_alpha_offset;
+  plugin->config.spill_saturation = plugin->config.undo_spill_saturation;
+  plugin->config.spill_angle      = plugin->config.undo_spill_angle;
+  plugin->config.desaturate_only  = plugin->config.undo_desaturate_only;
+  plugin->config.show_mask        = plugin->config.undo_show_mask;
+
+  gui->update_gui(RESET_DEFAULT_SETTINGS);
+  plugin->send_configure_change();
+  return 1;
+}
+
+ChromaKeyUseColorPicker::ChromaKeyUseColorPicker (ChromaKeyAvid * plugin,
+                                                 ChromaKeyWindow * gui,
+                                                 int x, int y)
+  : BC_GenericButton (x, y, _("Use color picker"))
+{
+  this->plugin = plugin;
+  this->gui = gui;
+}
+
+int
+ChromaKeyUseColorPicker::handle_event ()
+{
+  plugin->config.red = plugin->get_red ();
+  plugin->config.green = plugin->get_green ();
+  plugin->config.blue = plugin->get_blue ();
+
+  gui->update_sample ();
+
+  plugin->send_configure_change ();
+  return 1;
+}
+
+ChromaKeyColorThread::ChromaKeyColorThread (ChromaKeyAvid * plugin,
+                                           ChromaKeyWindow * gui)
+  : ColorPicker (0, _("Color"))
+{
+  this->plugin = plugin;
+  this->gui = gui;
+}
+
+int
+ChromaKeyColorThread::handle_new_color (int output, int alpha)
+{
+  plugin->config.red   = (float) (output & 0xff0000) / 0xff0000;
+  plugin->config.green = (float) (output & 0xff00) / 0xff00;
+  plugin->config.blue  = (float) (output & 0xff) / 0xff;
+
+  get_gui()->unlock_window();
+  gui->lock_window("ChromaKeyColorThread::handle_new_color");
+  gui->update_sample();
+  gui->unlock_window();
+  get_gui()->lock_window("ChromaKeyColorThread::handle_new_color");
+
+  plugin->send_configure_change ();
+  return 1;
+}
+
+
+
+ChromaKeyServer::ChromaKeyServer (ChromaKeyAvid * plugin)
+  : LoadServer (plugin->PluginClient::smp + 1,
+               plugin->PluginClient::smp + 1)
+// DEBUG
+//  : LoadServer (1, 1)
+{
+  this->plugin = plugin;
+}
+
+void
+ChromaKeyServer::init_packages ()
+{
+  for (int i = 0; i < get_total_packages (); i++)
+  {
+    ChromaKeyPackage *pkg = (ChromaKeyPackage *) get_package (i);
+    pkg->y1 = plugin->input->get_h () * i / get_total_packages ();
+    pkg->y2 = plugin->input->get_h () * (i + 1) / get_total_packages ();
+  }
+}
+
+LoadClient *
+ChromaKeyServer::new_client ()
+{
+  return new ChromaKeyUnit (plugin, this);
+}
+
+LoadPackage *
+ChromaKeyServer::new_package ()
+{
+  return new ChromaKeyPackage;
+}
+
+ChromaKeyPackage::ChromaKeyPackage ():LoadPackage ()
+{
+}
+
+ChromaKeyUnit::ChromaKeyUnit (ChromaKeyAvid * plugin, ChromaKeyServer * server)
+  : LoadClient (server)
+{
+  this->plugin = plugin;
+}
+
+
+
+#define ABS(a) (((a) < 0) ? -(a) : (a))
+
+
+
+// Compute the same values in the opengl version
+#define COMMON_VARIABLES \
+    float red = plugin->config.red; \
+    float green = plugin->config.green; \
+    float blue = plugin->config.blue; \
+ \
+    float in_slope = plugin->config.in_slope / MAX_SLOPE; \
+    float out_slope = plugin->config.out_slope / MAX_SLOPE; \
+ \
+/* Convert RGB key to HSV key */ \
+    float hue_key, saturation_key, value_key; \
+    HSV::rgb_to_hsv(red,       \
+               green, \
+               blue, \
+               hue_key, \
+               saturation_key, \
+               value_key); \
+ \
+/* hue range */ \
+    float tolerance = plugin->config.tolerance * 180 / MAX_VALUE; \
+    float tolerance_in = tolerance - in_slope * 180; \
+    tolerance_in = MAX(tolerance_in, 0); \
+    float tolerance_out = tolerance + out_slope * 180; \
+    tolerance_out = MIN(tolerance_out, 180); \
+ \
+/* distance of wedge point from center */ \
+    float sat_distance = plugin->config.saturation_start / MAX_VALUE; \
+/* XY shift of input color to get wedge point */ \
+    float sat_x = -cos(TO_RAD(hue_key)) * sat_distance; \
+    float sat_y = -sin(TO_RAD(hue_key)) * sat_distance; \
+/* minimum saturation after wedge point */ \
+    float min_s = plugin->config.saturation_line / MAX_VALUE; \
+    float min_s_in = min_s + in_slope; \
+    float min_s_out = min_s - out_slope; \
+ \
+    float min_v = plugin->config.min_brightness / MAX_VALUE; \
+    float min_v_in = min_v + in_slope; \
+    float min_v_out = min_v - out_slope; \
+/* ignore min_brightness 0 */ \
+    if(plugin->config.min_brightness == 0) min_v_in = 0; \
+ \
+    float max_v = plugin->config.max_brightness / MAX_VALUE; \
+    float max_v_in = max_v - in_slope; \
+    float max_v_out = max_v + out_slope; \
+/* handle wedge boundaries crossing over */ \
+    if(max_v_in < min_v_in) max_v_in = min_v_in = (max_v_in + min_v_in) / 2; \
+/* ignore max_brightness 100% */ \
+    if(plugin->config.max_brightness == MAX_VALUE) max_v_in = 1.0; \
+ \
+/* distance of spill wedge point from center */ \
+    float spill_distance = sat_distance * (1.0 - plugin->config.spill_saturation / MAX_VALUE); \
+/* XY shift of input color to get spill wedge point */ \
+    float spill_x = -cos(TO_RAD(hue_key)) * spill_distance; \
+    float spill_y = -sin(TO_RAD(hue_key)) * spill_distance; \
+/* tolerance of the spill wedge */ \
+    float spill_tolerance = tolerance + plugin->config.spill_angle * 90.0 / MAX_VALUE; \
+/* borders of the spill wedge */ \
+    float min_h = hue_key - spill_tolerance; \
+    float max_h = hue_key + spill_tolerance; \
+    int desaturate_only = plugin->config.desaturate_only; \
+ \
+    float alpha_offset = plugin->config.alpha_offset / MAX_VALUE;
+
+// shortest distance between 2 hues
+static float hue_distance(float h1, float h2)
+{
+  float result = h1 - h2;
+  if(result < -180) result += 360;
+  else if(result > 180) result -= 360;
+  return result;
+}
+
+// shift H & S based on an X & Y offset
+static void shift_hs(float &h_shifted, 
+                    float &s_shifted, 
+                    float h, 
+                    float s, 
+                    float x_offset,
+                    float y_offset)
+{
+  float h_rad = TO_RAD(h);
+  float x = cos(h_rad) * s;
+  float y = sin(h_rad) * s;
+  x += x_offset;
+  y += y_offset;
+  h_shifted = TO_DEG(atan2(y, x));
+  s_shifted = hypot(x, y);
+}
+
+template <typename component_type> 
+void ChromaKeyUnit::process_chromakey(int components, 
+                                     component_type max, 
+                                     bool use_yuv, 
+                                     ChromaKeyPackage *pkg) 
+{
+  COMMON_VARIABLES
+
+  int w = plugin->input->get_w();
+
+// printf("ChromaKeyUnit::process_chromakey %d hue_key=%f min_h=%f max_h=%f\n", 
+// __LINE__, 
+// hue_key,
+// min_h,
+// max_h);
+// printf("ChromaKeyUnit::process_chromakey %d tolerance_in=%f tolerance=%f tolerance_out=%f\n", 
+// __LINE__, 
+// tolerance_in,
+// tolerance,
+// tolerance_out);
+// printf("ChromaKeyUnit::process_chromakey %d min_s_in=%f min_s=%f min_s_out=%f\n", 
+// __LINE__, 
+// min_s_in,
+// min_s,
+// min_s_out);
+// printf("ChromaKeyUnit::process_chromakey %d min_v_in=%f min_v=%f min_v_out=%f\n", 
+// __LINE__, 
+// min_v_in,
+// min_v,
+// min_v_out);
+// printf("ChromaKeyUnit::process_chromakey %d max_v_in=%f max_v=%f max_v_out=%f\n", 
+// __LINE__, 
+// max_v_in,
+// max_v,
+// max_v_out);
+
+  for (int i = pkg->y1; i < pkg->y2; i++)
+  {
+    component_type *row = (component_type *) plugin->input->get_rows ()[i];
+
+    for (int j = 0; j < w; j++)
+    {
+      float r, g, b, a = 1;
+      if (!use_yuv)
+      {
+       r = (float) row[0] / max;
+       g = (float) row[1] / max;
+       b = (float) row[2] / max;
+      }
+      else             /* Convert pixel to RGB float */
+       YUV::yuv.yuv_to_rgb_f (r, g, b, row[0], row[1], row[2]);
+
+// the input color
+      float h, s, v;
+
+// alpha contribution of each component of the HSV space
+      float ah = 1, as = 1, av = 1;
+
+      HSV::rgb_to_hsv (r, g, b, h, s, v);
+
+// shift the input color in XY to get the wedge point
+      float h_shifted, s_shifted;
+      if(!EQUIV(plugin->config.saturation_start, 0))
+      {
+       shift_hs(h_shifted, 
+                s_shifted, 
+                h, 
+                s, 
+                sat_x,
+                sat_y);
+      }
+      else
+      {
+       h_shifted = h;
+       s_shifted = s;
+      }
+
+/* Get the difference between the shifted input color & the unshifted wedge */
+      float h_diff = hue_distance(h_shifted, hue_key);
+      h_diff = ABS(h_diff);
+
+// alpha contribution from hue difference
+// outside wedge < tolerance_out < tolerance_in < inside wedge < tolerance_in < tolerance_out < outside wedge
+      if (tolerance_out > 0)
+      {
+// completely inside the wedge
+       if (h_diff < tolerance_in)
+         ah = 0;
+       else
+// between the outer & inner slope
+         if(h_diff < tolerance_out)
+           ah = (h_diff - tolerance_in) / (tolerance_out - tolerance_in);
+       if(ah > 1) ah = 1;
+      }
+
+// alpha contribution from saturation
+// outside wedge < min_s_out < min_s_in < inside wedge
+      if(s_shifted > min_s_out)
+      {
+// saturation with offset applied
+// completely inside the wedge
+       if(s_shifted > min_s_in)
+         as = 0;
+// inside the gradient
+       if(s_shifted >= min_s_out)
+         as = (min_s_in - s_shifted) / (min_s_in - min_s_out);
+      }
+
+// alpha contribution from brightness range
+// brightness range is defined by 4 in/out variables
+// outside wedge < min_v_out < min_v_in < inside wedge < max_v_in < max_v_out < outside wedge
+      if(v > min_v_out)
+      {
+       if(v < min_v_in || max_v_in >= 1.0)
+         av = (min_v_in - v) / (min_v_in - min_v_out);
+       else if(v <= max_v_in)
+         av = 0;
+       else if(v <= max_v_out)
+         av = (v - max_v_in) / (max_v_out - max_v_in);
+      }
+
+// combine the alpha contribution of every component into a single alpha
+      a = MAX(as, ah);
+      a = MAX(a, av);
+
+// Spill light processing
+      if(spill_tolerance > 0)
+      {
+// get the difference between the shifted input color to the unshifted spill wedge
+       if(!EQUIV(spill_distance, 0))
+       {
+         shift_hs(h_shifted, 
+                  s_shifted, 
+                  h, 
+                  s, 
+                  spill_x,
+                  spill_y);
+       }
+       else
+       {
+         h_shifted = h;
+         s_shifted = s;
+       }
+
+// Difference between the shifted hue & the unshifted hue key
+       h_diff = hue_distance(h_shifted, hue_key);
+
+// inside the wedge
+       if(ABS(h_diff) < spill_tolerance)
+       {
+         if(!desaturate_only)
+         {
+// the shifted input color in the unshifted wedge
+// gives 2 unshifted border colors & the weighting
+           float blend = 0.5 + h_diff / spill_tolerance / 2;
+// shift the 2 border colors to the output wedge
+           float min_h_shifted;
+           float min_s_shifted;
+           shift_hs(min_h_shifted, 
+                    min_s_shifted, 
+                    min_h, 
+                    s_shifted, 
+                    -spill_x,
+                    -spill_y);
+           float max_h_shifted;
+           float max_s_shifted;
+           shift_hs(max_h_shifted, 
+                    max_s_shifted, 
+                    max_h, 
+                    s_shifted, 
+                    -spill_x,
+                    -spill_y);
+
+// blend the shifted border colors using the unshifted weighting
+// the only thing which doesn't restore the key color & doesn't make an edge is
+// fading the saturation to 0 in the middle
+           if(blend > 0.5)
+           {
+             h = max_h_shifted;
+             s = max_s_shifted;
+    //                        s = max_s_shifted * (blend - 0.5) / 0.5;
+    // DEBUG
+    //h = 0;
+    //s = blend; 
+           }
+           else
+           {
+             h = min_h_shifted;
+             s = min_s_shifted;
+    //                        s = min_s_shifted * (0.5 - blend) / 0.5;
+    // DEBUG
+    //h = 0;
+    //s = blend; 
+           }
+         } 
+         else // !desaturate_only
+         {
+
+// fade the saturation to 0 in the middle
+           s *= ABS(h_diff) / spill_tolerance;
+         }
+
+         if(h < 0) h += 360;
+         HSV::hsv_to_rgb (r, g, b, h, s, v);
+
+// store new color
+         if (!use_yuv)
+         {
+           row[0] = (component_type) ((float) r * max);
+           row[1] = (component_type) ((float) g * max);
+           row[2] = (component_type) ((float) b * max);
+         }
+         else
+           YUV::yuv.rgb_to_yuv_f(r, g, b, row[0], row[1], row[2]);
+       }
+      }
+
+      a += alpha_offset;
+      CLAMP (a, 0.0, 1.0);
+
+      if (plugin->config.show_mask)
+      {
+       if (use_yuv)
+       {
+         row[0] = (component_type) ((float) a * max);
+         row[1] = (component_type) ((float) max / 2);
+         row[2] = (component_type) ((float) max / 2);
+       }
+       else
+       {
+         row[0] = (component_type) ((float) a * max);
+         row[1] = (component_type) ((float) a * max);
+         row[2] = (component_type) ((float) a * max);
+       }
+      }
+
+      /* Multiply alpha and put back in frame */
+      if (components == 4)
+      {
+       row[3] = MIN ((component_type) (a * max), row[3]);
+      }
+      else if (use_yuv)
+      {
+       row[0] = (component_type) ((float) a * row[0]);
+       row[1] = (component_type) ((float) a * (row[1] - (max / 2 + 1)) +
+                                  max / 2 + 1);
+       row[2] = (component_type) ((float) a * (row[2] - (max / 2 + 1)) +
+                                  max / 2 + 1);
+      }
+      else
+      {
+       row[0] = (component_type) ((float) a * row[0]);
+       row[1] = (component_type) ((float) a * row[1]);
+       row[2] = (component_type) ((float) a * row[2]);
+      }
+
+      row += components;
+    }
+  }
+}
+
+
+
+void ChromaKeyUnit::process_package(LoadPackage *package)
+{
+  ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
+
+  switch(plugin->input->get_color_model())
+  {
+  case BC_RGB_FLOAT:
+    process_chromakey<float> (3, 1.0, 0, pkg);
+    break;
+  case BC_RGBA_FLOAT:
+    process_chromakey<float> ( 4, 1.0, 0, pkg);
+    break;
+  case BC_RGB888:
+    process_chromakey<unsigned char> ( 3, 0xff, 0, pkg);
+    break;
+  case BC_RGBA8888:
+    process_chromakey<unsigned char> ( 4, 0xff, 0, pkg);
+    break;
+  case BC_YUV888:
+    process_chromakey<unsigned char> ( 3, 0xff, 1, pkg);
+    break;
+  case BC_YUVA8888:
+    process_chromakey<unsigned char> ( 4, 0xff, 1, pkg);
+    break;
+  case BC_YUV161616:
+    process_chromakey<uint16_t> (3, 0xffff, 1, pkg);
+    break;
+  case BC_YUVA16161616:
+    process_chromakey<uint16_t> (4, 0xffff, 1, pkg);
+    break;
+  }
+}
+
+
+
+REGISTER_PLUGIN(ChromaKeyAvid)
+
+
+
+ChromaKeyAvid::ChromaKeyAvid(PluginServer *server)
+  : PluginVClient(server)
+{
+  engine = 0;
+}
+
+ChromaKeyAvid::~ChromaKeyAvid()
+{
+  if(engine) delete engine;
+}
+
+
+int ChromaKeyAvid::process_buffer(VFrame *frame,
+                                 int64_t start_position,
+                                 double frame_rate)
+{
+  load_configuration();
+  this->input = frame;
+  this->output = frame;
+
+  read_frame(frame,
+            0,
+            start_position,
+            frame_rate,
+            get_use_opengl());
+  if(get_use_opengl()) return run_opengl();
+
+  if(!engine) engine = new ChromaKeyServer(this);
+  engine->process_packages();
+
+  return 0;
+}
+
+const char* ChromaKeyAvid::plugin_title() { return N_("Chroma key (Avid)"); }
+int ChromaKeyAvid::is_realtime() { return 1; }
+
+
+LOAD_CONFIGURATION_MACRO(ChromaKeyAvid, ChromaKeyConfig)
+
+
+void ChromaKeyAvid::save_data(KeyFrame * keyframe)
+{
+  FileXML output;
+  output.set_shared_output(keyframe->xbuf);
+  output.tag.set_title("CHROMAKEY_AVID");
+  output.tag.set_property("RED", config.red);
+  output.tag.set_property("GREEN", config.green);
+  output.tag.set_property("BLUE", config.blue);
+  output.tag.set_property("MIN_BRIGHTNESS", config.min_brightness);
+  output.tag.set_property("MAX_BRIGHTNESS", config.max_brightness);
+  output.tag.set_property("SATURATION_START", config.saturation_start);
+  output.tag.set_property("SATURATION_LINE", config.saturation_line);
+  output.tag.set_property("TOLERANCE", config.tolerance);
+  output.tag.set_property("IN_SLOPE", config.in_slope);
+  output.tag.set_property("OUT_SLOPE", config.out_slope);
+  output.tag.set_property("ALPHA_OFFSET", config.alpha_offset);
+  output.tag.set_property("SPILL_SATURATION", config.spill_saturation);
+  output.tag.set_property("SPILL_ANGLE", config.spill_angle);
+  output.tag.set_property("DESATURATE_ONLY", config.desaturate_only);
+  output.tag.set_property("SHOW_MASK", config.show_mask);
+  output.append_tag();
+  output.tag.set_title("/CHROMAKEY_AVID");
+  output.append_tag();
+  output.append_newline();
+  output.terminate_string();
+}
+
+void ChromaKeyAvid::read_data(KeyFrame * keyframe)
+{
+  FileXML input;
+
+  input.set_shared_input(keyframe->xbuf);
+
+  while( !input.read_tag() )
+  {
+    if( input.tag.title_is("CHROMAKEY_AVID") )
+    {
+      config.red   = input.tag.get_property("RED",   config.red);
+      config.green = input.tag.get_property("GREEN", config.green);
+      config.blue  = input.tag.get_property("BLUE",  config.blue);
+      config.min_brightness =
+       input.tag.get_property("MIN_BRIGHTNESS", config.min_brightness);
+      config.max_brightness =
+       input.tag.get_property("MAX_BRIGHTNESS", config.max_brightness);
+      config.saturation_start =
+       input.tag.get_property("SATURATION_START", config.saturation_start);
+      config.saturation_line =
+       input.tag.get_property("SATURATION_LINE", config.saturation_line);
+      config.tolerance =
+       input.tag.get_property("TOLERANCE", config.tolerance);
+      config.in_slope =
+       input.tag.get_property("IN_SLOPE", config.in_slope);
+      config.out_slope =
+       input.tag.get_property("OUT_SLOPE", config.out_slope);
+      config.alpha_offset =
+       input.tag.get_property("ALPHA_OFFSET", config.alpha_offset);
+      config.spill_saturation =
+       input.tag.get_property("SPILL_SATURATION", config.spill_saturation);
+      config.spill_angle =
+       input.tag.get_property("SPILL_ANGLE", config.spill_angle);
+      config.desaturate_only =
+       input.tag.get_property("DESATURATE_ONLY", config.desaturate_only);
+      config.show_mask =
+       input.tag.get_property("SHOW_MASK", config.show_mask);
+
+      config.undo_red              = config.red;
+      config.undo_green            = config.green;
+      config.undo_blue             = config.blue;
+      config.undo_min_brightness   = config.min_brightness;
+      config.undo_max_brightness   = config.max_brightness;
+      config.undo_tolerance        = config.tolerance;
+      config.undo_saturation_start = config.saturation_start;
+      config.undo_saturation_line  = config.saturation_line;
+      config.undo_in_slope         = config.in_slope;
+      config.undo_out_slope        = config.out_slope;
+      config.undo_alpha_offset     = config.alpha_offset;
+      config.undo_spill_saturation = config.spill_saturation;
+      config.undo_spill_angle      = config.spill_angle;
+      config.undo_desaturate_only  = config.desaturate_only;
+      config.undo_show_mask        = config.show_mask;
+    }
+  }
+}
+
+
+NEW_WINDOW_MACRO(ChromaKeyAvid, ChromaKeyWindow)
+
+
+void ChromaKeyAvid::update_gui()
+{
+  if( thread )
+  {
+    load_configuration();
+    ChromaKeyWindow *window = (ChromaKeyWindow*)thread->window;
+    window->lock_window();
+    window->update_gui(RESET_ALL);
+    window->unlock_window();
+  }
+}
+
+void ChromaKeyWindow::update_gui(int clear)
+{
+  ChromaKeyConfig &config = plugin->config;
+  switch(clear)
+  {
+  case RESET_RGB:
+    update_sample();
+    break;
+  case RESET_MIN_BRIGHTNESS:
+    min_brightness->update(config.min_brightness);
+    min_brightness_text->update(config.min_brightness);
+    break;
+  case RESET_MAX_BRIGHTNESS:
+    max_brightness->update(config.max_brightness);
+    max_brightness_text->update(config.max_brightness);
+    break;
+  case RESET_TOLERANCE:
+    tolerance->update(config.tolerance);
+    tolerance_text->update(config.tolerance);
+    break;
+  case RESET_SATURATION_START:
+    saturation_start->update(config.saturation_start);
+    saturation_start_text->update(config.saturation_start);
+    break;
+  case RESET_SATURATION_LINE:
+    saturation_line->update(config.saturation_line);
+    saturation_line_text->update(config.saturation_line);
+    break;
+  case RESET_IN_SLOPE:
+    in_slope->update(config.in_slope);
+    in_slope_text->update(config.in_slope);
+    break;
+  case RESET_OUT_SLOPE:
+    out_slope->update(config.out_slope);
+    out_slope_text->update(config.out_slope);
+    break;
+  case RESET_ALPHA_OFFSET:
+    alpha_offset->update(config.alpha_offset);
+    alpha_offset_text->update(config.alpha_offset);
+    break;
+  case RESET_SPILL_SATURATION:
+    spill_saturation->update(config.spill_saturation);
+    spill_saturation_text->update(config.spill_saturation);
+    break;
+  case RESET_SPILL_ANGLE:
+    spill_angle->update(config.spill_angle);
+    spill_angle_text->update(config.spill_angle);
+    break;
+  case RESET_ALL:
+  case RESET_DEFAULT_SETTINGS:
+  default:
+    min_brightness->update(config.min_brightness);
+    min_brightness_text->update(config.min_brightness);
+    max_brightness->update(config.max_brightness);
+    max_brightness_text->update(config.max_brightness);
+    saturation_start->update(config.saturation_start);
+    saturation_start_text->update(config.saturation_start);
+    saturation_line->update(config.saturation_line);
+    saturation_line_text->update(config.saturation_line);
+    tolerance->update(config.tolerance);
+    tolerance_text->update(config.tolerance);
+    in_slope->update(config.in_slope);
+    in_slope_text->update(config.in_slope);
+    out_slope->update(config.out_slope);
+    out_slope_text->update(config.out_slope);
+    alpha_offset->update(config.alpha_offset);
+    alpha_offset_text->update(config.alpha_offset);
+    spill_saturation->update(config.spill_saturation);
+    spill_saturation_text->update(config.spill_saturation);
+    spill_angle->update(config.spill_angle);
+    spill_angle_text->update(config.spill_angle);
+    desaturate_only->update(config.desaturate_only);
+    show_mask->update(config.show_mask);
+    update_sample();
+    break;
+  }
+}
+
+
+
+int ChromaKeyAvid::handle_opengl()
+{
+#ifdef HAVE_GL
+// For macro
+       ChromaKeyAvid *plugin = this;
+       COMMON_VARIABLES
+
+       static const char *yuv_shader =
+               "const vec3 black = vec3(0.0, 0.5, 0.5);\n"
+               "uniform mat3 yuv_to_rgb_matrix;\n"
+               "uniform mat3 rgb_to_yuv_matrix;\n"
+               "uniform float yminf;\n"
+               "\n"
+               "vec4 yuv_to_rgb(vec4 color)\n"
+               "{\n"
+               "       color.rgb -= vec3(yminf, 0.5, 0.5);\n"
+               "       color.rgb = yuv_to_rgb_matrix * color.rgb;\n"
+               "       return color;\n"
+               "}\n"
+               "\n"
+               "vec4 rgb_to_yuv(vec4 color)\n"
+               "{\n"
+               "       color.rgb = rgb_to_yuv_matrix * color.rgb;\n"
+               "       color.rgb += vec3(yminf, 0.5, 0.5);\n"
+               "       return color;\n"
+               "}\n";
+
+       static const char *rgb_shader =
+               "const vec3 black = vec3(0.0, 0.0, 0.0);\n"
+               "\n"
+               "vec4 yuv_to_rgb(vec4 color)\n"
+               "{\n"
+               "       return color;\n"
+               "}\n"
+               "vec4 rgb_to_yuv(vec4 color)\n"
+               "{\n"
+               "       return color;\n"
+               "}\n";
+
+       static const char *hsv_shader =
+               "vec4 rgb_to_hsv(vec4 color)\n"
+               "{\n"
+                       RGB_TO_HSV_FRAG("color")
+               "       return color;\n"
+               "}\n"
+               "\n"
+               "vec4 hsv_to_rgb(vec4 color)\n"
+               "{\n"
+                       HSV_TO_RGB_FRAG("color")
+               "       return color;\n"
+               "}\n"
+               "\n";
+
+       static const char *show_rgbmask_shader =
+               "vec4 show_mask(vec4 color, vec4 color2)\n"
+               "{\n"
+               "       return vec4(1.0, 1.0, 1.0, min(color.a, color2.a));"
+               "}\n";
+
+       static const char *show_yuvmask_shader =
+               "vec4 show_mask(vec4 color, vec4 color2)\n"
+               "{\n"
+               "       return vec4(1.0, 0.5, 0.5, min(color.a, color2.a));"
+               "}\n";
+
+       static const char *nomask_shader =
+               "vec4 show_mask(vec4 color, vec4 color2)\n"
+               "{\n"
+               "       return vec4(color.rgb, min(color.a, color2.a));"
+               "}\n";
+
+       get_output()->to_texture();
+       get_output()->enable_opengl();
+       get_output()->init_screen();
+
+        const char *shader_stack[16];
+        memset(shader_stack,0, sizeof(shader_stack));
+        int current_shader = 0;
+
+       shader_stack[current_shader++] = \
+                !BC_CModels::is_yuv(get_output()->get_color_model()) ?
+                       rgb_shader : yuv_shader;
+       shader_stack[current_shader++] = hsv_shader;
+       shader_stack[current_shader++] = !config.show_mask ? nomask_shader :
+                !BC_CModels::is_yuv(get_output()->get_color_model()) ?
+                       show_rgbmask_shader : show_yuvmask_shader ;
+       extern unsigned char _binary_chromakeyavid_sl_start[];
+       static const char *shader_frag = (char*)_binary_chromakeyavid_sl_start;
+       shader_stack[current_shader++] = shader_frag;
+
+       shader_stack[current_shader] = 0;
+       unsigned int shader = VFrame::make_shader(shader_stack);
+       if( shader > 0 ) {
+               glUseProgram(shader);
+               glUniform1i(glGetUniformLocation(shader, "tex"), 0);
+               glUniform1f(glGetUniformLocation(shader, "red"), red);
+               glUniform1f(glGetUniformLocation(shader, "green"), green);
+               glUniform1f(glGetUniformLocation(shader, "blue"), blue);
+               glUniform1f(glGetUniformLocation(shader, "in_slope"), in_slope);
+               glUniform1f(glGetUniformLocation(shader, "out_slope"), out_slope);
+               glUniform1f(glGetUniformLocation(shader, "tolerance"), tolerance);
+               glUniform1f(glGetUniformLocation(shader, "tolerance_in"), tolerance_in);
+               glUniform1f(glGetUniformLocation(shader, "tolerance_out"), tolerance_out);
+               glUniform1f(glGetUniformLocation(shader, "sat_x"), sat_x);
+               glUniform1f(glGetUniformLocation(shader, "sat_y"), sat_y);
+               glUniform1f(glGetUniformLocation(shader, "min_s"), min_s);
+               glUniform1f(glGetUniformLocation(shader, "min_s_in"), min_s_in);
+               glUniform1f(glGetUniformLocation(shader, "min_s_out"), min_s_out);
+               glUniform1f(glGetUniformLocation(shader, "min_v"), min_v);
+               glUniform1f(glGetUniformLocation(shader, "min_v_in"), min_v_in);
+               glUniform1f(glGetUniformLocation(shader, "min_v_out"), min_v_out);
+               glUniform1f(glGetUniformLocation(shader, "max_v"), max_v);
+               glUniform1f(glGetUniformLocation(shader, "max_v_in"), max_v_in);
+               glUniform1f(glGetUniformLocation(shader, "max_v_out"), max_v_out);
+               glUniform1f(glGetUniformLocation(shader, "spill_distance"), spill_distance);
+               glUniform1f(glGetUniformLocation(shader, "spill_x"), spill_x);
+               glUniform1f(glGetUniformLocation(shader, "spill_y"), spill_y);
+               glUniform1f(glGetUniformLocation(shader, "spill_tolerance"), spill_tolerance);
+               glUniform1f(glGetUniformLocation(shader, "min_h"), min_h);
+               glUniform1f(glGetUniformLocation(shader, "max_h"), max_h);
+               glUniform1i(glGetUniformLocation(shader, "desaturate_only"), desaturate_only);
+               glUniform1f(glGetUniformLocation(shader, "alpha_offset"), alpha_offset);
+               glUniform1f(glGetUniformLocation(shader, "hue_key"), hue_key);
+               glUniform1f(glGetUniformLocation(shader, "saturation_key"), saturation_key);
+               glUniform1f(glGetUniformLocation(shader, "value_key"), value_key);
+               if( BC_CModels::is_yuv(get_output()->get_color_model()) )
+                       BC_GL_COLORS(shader);
+       }
+
+       get_output()->bind_texture(0);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+       if(BC_CModels::components(get_output()->get_color_model()) == 3)
+       {
+               glEnable(GL_BLEND);
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+               get_output()->clear_pbuffer();
+       }
+       get_output()->draw_texture();
+
+       glUseProgram(0);
+       get_output()->set_opengl_state(VFrame::SCREEN);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glDisable(GL_BLEND);
+
+
+#endif
+       return 0;
+}
diff --git a/cinelerra-5.1/plugins/chromakeyavid/chromakey.h b/cinelerra-5.1/plugins/chromakeyavid/chromakey.h
new file mode 100644 (file)
index 0000000..e39426e
--- /dev/null
@@ -0,0 +1,385 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CHROMAKEY_H
+#define CHROMAKEY_H
+
+#include "colorpicker.h"
+#include "guicast.h"
+#include "theme.h"
+#include "loadbalance.h"
+#include "pluginvclient.h"
+
+#define RESET_DEFAULT_SETTINGS 20
+#define RESET_ALL               0
+#define RESET_RGB               1
+#define RESET_MIN_BRIGHTNESS    2
+#define RESET_MAX_BRIGHTNESS    3
+#define RESET_TOLERANCE         4
+#define RESET_SATURATION_START  5
+#define RESET_SATURATION_LINE   6
+#define RESET_IN_SLOPE          7
+#define RESET_OUT_SLOPE         8
+#define RESET_ALPHA_OFFSET      9
+#define RESET_SPILL_SATURATION 10
+#define RESET_SPILL_ANGLE      11
+
+class ChromaKeyAvid;
+class ChromaKeyWindow;
+class ChromaKeyText;
+class ChromaKeySlider;
+class ChromaKeyClr;
+class ChromaKeyReset;
+class ChromaKeyStore;
+class ChromaKeyRecall;
+class ChromaKeyExchange;
+class ChromaKeyUndo;
+
+enum {
+  CHROMAKEY_POSTPROCESS_NONE,
+  CHROMAKEY_POSTPROCESS_BLUR,
+  CHROMAKEY_POSTPROCESS_DILATE
+};
+
+
+class ChromaKeyConfig
+{
+public:
+  ChromaKeyConfig();
+  void reset(int clear);
+  void copy_from(ChromaKeyConfig &src);
+  int equivalent(ChromaKeyConfig &src);
+  void interpolate(ChromaKeyConfig &prev,
+                  ChromaKeyConfig &next,
+                  int64_t prev_frame,
+                  int64_t next_frame,
+                  int64_t current_frame);
+  int get_color();
+
+  // Output mode
+  bool  show_mask;
+  // Key color definition
+  float red;
+  float green;
+  float blue;
+  // Key shade definition
+  float min_brightness;
+  float max_brightness;
+  // distance of wedge point from the center
+  float saturation_start;
+  // minimum saturation after wedge point
+  float saturation_line;
+  // hue range
+  float tolerance;
+  // Mask feathering
+  float in_slope;
+  float out_slope;
+  float alpha_offset;
+  // Spill light compensation
+  float spill_saturation;
+  float spill_angle;
+  bool  desaturate_only;
+  // Instances for Store
+  bool  store_show_mask;
+  float store_red;
+  float store_green;
+  float store_blue;
+  float store_min_brightness;
+  float store_max_brightness;
+  float store_saturation_start;
+  float store_saturation_line;
+  float store_tolerance;
+  float store_in_slope;
+  float store_out_slope;
+  float store_alpha_offset;
+  float store_spill_saturation;
+  float store_spill_angle;
+  bool  store_desaturate_only;
+  // Instances for Undo
+  bool  undo_show_mask;
+  float undo_red;
+  float undo_green;
+  float undo_blue;
+  float undo_min_brightness;
+  float undo_max_brightness;
+  float undo_saturation_start;
+  float undo_saturation_line;
+  float undo_tolerance;
+  float undo_in_slope;
+  float undo_out_slope;
+  float undo_alpha_offset;
+  float undo_spill_saturation;
+  float undo_spill_angle;
+  bool  undo_desaturate_only;
+};
+
+
+class ChromaKeyColor : public BC_GenericButton
+{
+public:
+  ChromaKeyColor(ChromaKeyAvid *plugin,
+                ChromaKeyWindow *gui,
+                int x,
+                int y);
+
+  int handle_event();
+
+  ChromaKeyAvid *plugin;
+  ChromaKeyWindow *gui;
+};
+
+class ChromaKeyReset : public BC_GenericButton
+{
+public:
+  ChromaKeyReset(ChromaKeyAvid *plugin, ChromaKeyWindow *gui, int x, int y);
+  int handle_event();
+  ChromaKeyAvid *plugin;
+  ChromaKeyWindow *gui;
+};
+
+class ChromaKeyStore : public BC_GenericButton
+{
+public:
+  ChromaKeyStore(ChromaKeyAvid *plugin, ChromaKeyWindow *gui, int x, int y);
+  int handle_event();
+  ChromaKeyAvid *plugin;
+  ChromaKeyWindow *gui;
+};
+
+class ChromaKeyRecall : public BC_GenericButton
+{
+public:
+  ChromaKeyRecall(ChromaKeyAvid *plugin, ChromaKeyWindow *gui, int x, int y);
+  int handle_event();
+  ChromaKeyAvid *plugin;
+  ChromaKeyWindow *gui;
+};
+
+class ChromaKeyExchange : public BC_GenericButton
+{
+public:
+  ChromaKeyExchange(ChromaKeyAvid *plugin, ChromaKeyWindow *gui, int x, int y);
+  int handle_event();
+  ChromaKeyAvid *plugin;
+  ChromaKeyWindow *gui;
+};
+
+class ChromaKeyUndo : public BC_GenericButton
+{
+public:
+  ChromaKeyUndo(ChromaKeyAvid *plugin, ChromaKeyWindow *gui, int x, int y);
+  int handle_event();
+  ChromaKeyAvid *plugin;
+  ChromaKeyWindow *gui;
+};
+
+class ChromaKeyText : public BC_TumbleTextBox
+{
+public:
+  ChromaKeyText(ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+               ChromaKeySlider *slider, int x, int y,
+               float min, float max, float *output);
+  ~ChromaKeyText();
+  int handle_event();
+  ChromaKeyAvid *plugin;
+  ChromaKeyWindow *gui;
+  ChromaKeySlider *slider;
+  float *output;
+  float min, max;
+};
+
+class ChromaKeySlider : public BC_FSlider
+{
+public:
+  ChromaKeySlider(ChromaKeyAvid *plugin, ChromaKeyText *text,
+                 int x, int y, int w, float min, float max, float *output);
+  ~ChromaKeySlider();
+  int handle_event();
+  ChromaKeyAvid *plugin;
+  ChromaKeyText *text;
+  float *output;
+};
+
+class ChromaKeyClr : public BC_Button
+{
+public:
+  ChromaKeyClr(ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+              int x, int y, int clear);
+  ~ChromaKeyClr();
+  int handle_event();
+  ChromaKeyAvid *plugin;
+  ChromaKeyWindow *gui;
+  int clear;
+};
+
+
+class ChromaKeyUseColorPicker : public BC_GenericButton
+{
+public:
+  ChromaKeyUseColorPicker(ChromaKeyAvid *plugin,
+                         ChromaKeyWindow *gui, int x, int y);
+  int handle_event();
+  ChromaKeyAvid *plugin;
+  ChromaKeyWindow *gui;
+};
+
+class ChromaKeyColorThread : public ColorPicker
+{
+public:
+  ChromaKeyColorThread(ChromaKeyAvid *plugin, ChromaKeyWindow *gui);
+  int handle_new_color(int output, int alpha);
+  ChromaKeyAvid *plugin;
+  ChromaKeyWindow *gui;
+};
+
+class ChromaKeyToggle : public BC_CheckBox
+{
+public:
+  ChromaKeyToggle(ChromaKeyAvid *plugin, 
+                 int x, 
+                 int y,
+                 bool *output,
+                 const char *caption);
+  int handle_event();
+  ChromaKeyAvid *plugin;
+  bool *output;
+};
+
+
+
+class ChromaKeyWindow : public PluginClientWindow
+{
+public:
+  ChromaKeyWindow(ChromaKeyAvid *plugin);
+  ~ChromaKeyWindow();
+
+  void create_objects();
+  void update_sample();
+  void update_gui(int clear);
+  void done_event(int result);
+
+  ChromaKeyColor *color;
+  ChromaKeyUseColorPicker *use_colorpicker;
+
+  ChromaKeyText   *min_brightness_text;
+  ChromaKeySlider *min_brightness;
+  ChromaKeyClr    *min_brightness_clr;
+
+  ChromaKeyText   *max_brightness_text;
+  ChromaKeySlider *max_brightness;
+  ChromaKeyClr    *max_brightness_clr;
+
+  ChromaKeyText   *saturation_start_text;
+  ChromaKeySlider *saturation_start;
+  ChromaKeyClr    *saturation_start_clr;
+
+  ChromaKeyText   *saturation_line_text;
+  ChromaKeySlider *saturation_line;
+  ChromaKeyClr    *saturation_line_clr;
+
+  ChromaKeyText   *tolerance_text;
+  ChromaKeySlider *tolerance;
+  ChromaKeyClr    *tolerance_clr;
+
+  ChromaKeyText   *in_slope_text;
+  ChromaKeySlider *in_slope;
+  ChromaKeyClr    *in_slope_clr;
+
+  ChromaKeyText   *out_slope_text;
+  ChromaKeySlider *out_slope;
+  ChromaKeyClr    *out_slope_clr;
+
+  ChromaKeyText   *alpha_offset_text;
+  ChromaKeySlider *alpha_offset;
+  ChromaKeyClr    *alpha_offset_clr;
+
+  ChromaKeyText   *spill_saturation_text;
+  ChromaKeySlider *spill_saturation;
+  ChromaKeyClr    *spill_saturation_clr;
+
+  ChromaKeyText   *spill_angle_text;
+  ChromaKeySlider *spill_angle;
+  ChromaKeyClr    *spill_angle_clr;
+
+  ChromaKeyToggle *desaturate_only;
+  ChromaKeyToggle *show_mask;
+
+  ChromaKeyReset    *reset;
+  ChromaKeyStore    *store;
+  ChromaKeyRecall   *recall;
+  ChromaKeyExchange *exchange;
+  ChromaKeyUndo     *undo;
+
+  BC_SubWindow *sample;
+  ChromaKeyAvid *plugin;
+  ChromaKeyColorThread *color_thread;
+};
+
+class ChromaKeyServer : public LoadServer
+{
+public:
+  ChromaKeyServer(ChromaKeyAvid *plugin);
+  void init_packages();
+  LoadClient* new_client();
+  LoadPackage* new_package();
+
+  ChromaKeyAvid *plugin;
+};
+
+class ChromaKeyPackage : public LoadPackage
+{
+public:
+  ChromaKeyPackage();
+  int y1, y2;
+};
+
+class ChromaKeyUnit : public LoadClient
+{
+public:
+  ChromaKeyUnit(ChromaKeyAvid *plugin, ChromaKeyServer *server);
+  void process_package(LoadPackage *package);
+  template <typename component_type> void process_chromakey(int components, component_type max, bool use_yuv, ChromaKeyPackage *pkg);
+  bool is_same_color(float r, float g, float b, float rk,float bk,float gk,
+                    float color_threshold, float light_threshold,
+                    int key_main_component);
+
+  ChromaKeyAvid *plugin;
+};
+
+class ChromaKeyAvid : public PluginVClient
+{
+public:
+  ChromaKeyAvid(PluginServer *server);
+  ~ChromaKeyAvid();
+
+  PLUGIN_CLASS_MEMBERS(ChromaKeyConfig);
+  int process_buffer(VFrame *frame, int64_t start_position, double frame_rate);
+  int handle_opengl();
+  int is_realtime();
+  void save_data(KeyFrame *keyframe);
+  void read_data(KeyFrame *keyframe);
+  void update_gui();
+
+  VFrame *input, *output;
+  ChromaKeyServer *engine;
+};
+
+#endif
diff --git a/cinelerra-5.1/plugins/chromakeyavid/chromakeyavid.sl b/cinelerra-5.1/plugins/chromakeyavid/chromakeyavid.sl
new file mode 100644 (file)
index 0000000..2fd86da
--- /dev/null
@@ -0,0 +1,230 @@
+uniform sampler2D tex;
+uniform float red;
+uniform float green;
+uniform float blue;
+uniform float in_slope;
+uniform float out_slope;
+uniform float tolerance;
+uniform float tolerance_in;
+uniform float tolerance_out;
+uniform float sat_x;
+uniform float sat_y;
+uniform float min_s;
+uniform float min_s_in;
+uniform float min_s_out;
+uniform float min_v;
+uniform float min_v_in;
+uniform float min_v_out;
+uniform float max_v;
+uniform float max_v_in;
+uniform float max_v_out;
+uniform float spill_distance;
+uniform float spill_x;
+uniform float spill_y;
+uniform float spill_tolerance;
+uniform float min_h;
+uniform float max_h;
+uniform bool desaturate_only;
+uniform float alpha_offset;
+uniform float hue_key;
+uniform float saturation_key;
+uniform float value_key;
+
+
+
+// shortest distance between 2 hues
+float hue_distance(float h1, float h2)
+{
+       float result = h1 - h2;
+    if(result < -180.0) result += 360.0;
+    else
+    if(result > 180.0) result -= 360.0;
+    return result;
+}
+
+// shift H & S based on an X & Y offset
+void shift_hs(out float h_shifted, 
+    out float s_shifted, 
+    float h, 
+    float s, 
+    float x_offset,
+    float y_offset)
+{
+    float h_rad = radians(h);
+    float x = cos(h_rad) * s;
+    float y = sin(h_rad) * s;
+    x += x_offset;
+    y += y_offset;
+    h_shifted = degrees(atan(y, x));
+    s_shifted = length(vec2(x, y));
+}
+
+
+void main()
+{
+       vec4 color = texture2D(tex, gl_TexCoord[0].st);
+/* Contribution to alpha from each component */
+       float ah = 1.0;
+       float as = 1.0;
+       float av = 1.0;
+    float a = 1.0;
+       vec4 color2;
+       
+/* Convert to HSV */
+       color2 = yuv_to_rgb(color);
+       color2 = rgb_to_hsv(color2);
+    float h = color2.r;
+    float s = color2.g;
+    float v = color2.b;
+
+// shift the color in XY to shift the wedge point
+    float h_shifted, s_shifted;
+    shift_hs(h_shifted, 
+        s_shifted, 
+        h, 
+        s, 
+        sat_x,
+        sat_y);
+
+/* Get the difference between the current hue & the hue key */
+       float h_diff = abs(hue_distance(h_shifted, hue_key));
+
+// alpha contribution from hue difference
+// outside wedge < tolerance_out < tolerance_in < inside wedge < tolerance_in < tolerance_out < outside wedge
+       if (tolerance_out > 0.0)
+    {
+// completely inside the wedge
+               if (h_diff < tolerance_in)
+                       ah = 0.0;
+        else
+// between the outer & inner slope
+               if(h_diff < tolerance_out)
+                       ah = (h_diff - tolerance_in) / (tolerance_out - tolerance_in);
+        if(ah > 1.0) ah = 1.0;
+    }
+
+// alpha contribution from saturation
+// outside wedge < min_s_out < min_s_in < inside wedge
+    if(s_shifted > min_s_out)
+    {
+// saturation with offset applied
+// completely inside the wedge
+        if(s_shifted > min_s_in)
+            as = 0.0;
+// inside the gradient
+        if(s_shifted >= min_s_out)
+                       as = (min_s_in - s_shifted) / (min_s_in - min_s_out);
+    }
+
+
+// alpha contribution from brightness range
+// brightness range is defined by 4 in/out variables
+// outside wedge < min_v_out < min_v_in < inside wedge < max_v_in < max_v_out < outside wedge
+    if(v > min_v_out)
+    {
+        if(v < min_v_in || max_v_in >= 1.0)
+            av = (min_v_in - v) / (min_v_in - min_v_out);
+        else
+        if(v <= max_v_in)
+            av = 0.0;
+        else
+        if(v <= max_v_out)
+            av = (v - max_v_in) / (max_v_out - max_v_in);
+    }
+
+// combine the alpha contribution of every component into a single alpha
+    a = max(as, ah);
+    a = max(a, av);
+
+// Spill light processing
+    if(spill_tolerance > 0.0)
+    {
+// get the difference between the shifted input color to the unshifted spill wedge
+        if(spill_distance != 0.0)
+        {
+            shift_hs(h_shifted, 
+                s_shifted, 
+                h, 
+                s, 
+                spill_x,
+                spill_y);
+        }
+        else
+        {
+            h_shifted = h;
+            s_shifted = s;
+        }
+
+// Difference between the shifted hue & the unshifted hue key
+               h_diff = hue_distance(h_shifted, hue_key);
+
+// inside the wedge
+        if(abs(h_diff) < spill_tolerance)
+        {
+            if(!desaturate_only)
+            {
+// the shifted input color in the unshifted wedge
+// gives 2 unshifted border colors & the weighting
+                float blend = 0.5 + h_diff / spill_tolerance / 2.0;
+// shift the 2 border colors to the output wedge
+                float min_h_shifted;
+                float min_s_shifted;
+                shift_hs(min_h_shifted, 
+                    min_s_shifted, 
+                    min_h, 
+                    s_shifted, 
+                    -spill_x,
+                    -spill_y);
+                float max_h_shifted;
+                float max_s_shifted;
+                shift_hs(max_h_shifted, 
+                    max_s_shifted, 
+                    max_h, 
+                    s_shifted, 
+                    -spill_x,
+                    -spill_y);
+
+
+// blend the shifted border colors using the unshifted weighting
+// the only thing which doesn't restore the key color & doesn't make an edge is
+// fading the saturation to 0 in the middle
+                if(blend > 0.5)
+                {
+                    h = max_h_shifted;
+                    s = max_s_shifted;
+                }
+                else
+                {
+                    h = min_h_shifted;
+                    s = min_s_shifted;
+                }
+            } 
+            else // !desaturate_only
+            {
+
+// fade the saturation to 0 in the middle
+                s *= abs(h_diff) / spill_tolerance;
+            }
+
+
+            if(h < 0.0) h += 360.0;
+
+// store new color
+            color2.r = h;
+            color2.g = s;
+            color2.b = v;
+                   color2 = hsv_to_rgb(color2);
+                   color.rgb = rgb_to_yuv(color2).rgb;
+        }
+    }
+
+
+
+       a += alpha_offset;
+    a = min(a, 1.0);
+    color.a = a;
+/* Convert mask into image */
+       gl_FragColor = show_mask(color, color2);
+}
+
+
index ba7dd025ccf9f8112274667167f98084d95adb3e..0187778c0b68ea827528d459dfdc97f5d0aac958 100644 (file)
 
 ChromaKeyConfig::ChromaKeyConfig ()
 {
-       reset();
+       reset(RESET_DEFAULT_SETTINGS);
 }
 
-void ChromaKeyConfig::reset()
-
+void ChromaKeyConfig::reset(int clear)
 {
-  red = 0.0;
-  green = 1.0;
-  blue = 0.0;
-
-  min_brightness = 50.0;
-  max_brightness = 100.0;
-  tolerance = 15.0;
-  saturation = 0.0;
-  min_saturation = 50.0;
-
-  in_slope = 2;
-  out_slope = 2;
-  alpha_offset = 0;
-
-  spill_threshold = 0.0;
-  spill_amount = 90.0;
-
-  show_mask = 0;
-
+       switch(clear) {
+               case RESET_RGB :
+                       red = 0.0;
+                       green = 1.0;
+                       blue = 0.0;
+                       break;
+               case RESET_MIN_BRIGHTNESS :
+                       min_brightness = 50.0;
+                       break;
+               case RESET_MAX_BRIGHTNESS :
+                       max_brightness = 100.0;
+                       break;
+               case RESET_TOLERANCE :
+                       tolerance = 15.0;
+                       break;
+               case RESET_SATURATION :
+                       saturation = 0.0;
+                       break;
+               case RESET_MIN_SATURATION :
+                       min_saturation = 50.0;
+                       break;
+               case RESET_IN_SLOPE :
+                       in_slope = 2;
+                       break;
+               case RESET_OUT_SLOPE :
+                       out_slope = 2;
+                       break;
+               case RESET_ALPHA_OFFSET :
+                       alpha_offset = 0;
+                       break;
+               case RESET_SPILL_THRESHOLD :
+                       spill_threshold = 0.0;
+                       break;
+               case RESET_SPILL_AMOUNT :
+                       spill_amount = 90.0;
+                       break;
+               case RESET_ALL :
+               case RESET_DEFAULT_SETTINGS :
+               default:
+                       red = 0.0;
+                       green = 1.0;
+                       blue = 0.0;
+                       min_brightness = 50.0;
+                       max_brightness = 100.0;
+                       tolerance = 15.0;
+                       saturation = 0.0;
+                       min_saturation = 50.0;
+
+                       in_slope = 2;
+                       out_slope = 2;
+                       alpha_offset = 0;
+
+                       spill_threshold = 0.0;
+                       spill_amount = 90.0;
+
+                       show_mask = 0;
+                       break;
+       }
 }
 
 
-void
-ChromaKeyConfig::copy_from (ChromaKeyConfig & src)
+void ChromaKeyConfig::copy_from (ChromaKeyConfig & src)
 {
   red = src.red;
   green = src.green;
@@ -89,8 +126,7 @@ ChromaKeyConfig::copy_from (ChromaKeyConfig & src)
   show_mask = src.show_mask;
 }
 
-int
-ChromaKeyConfig::equivalent (ChromaKeyConfig & src)
+int ChromaKeyConfig::equivalent (ChromaKeyConfig & src)
 {
   return (EQUIV (red, src.red) &&
          EQUIV (green, src.green) &&
@@ -108,8 +144,7 @@ ChromaKeyConfig::equivalent (ChromaKeyConfig & src)
          EQUIV (alpha_offset, src.alpha_offset));
 }
 
-void
-ChromaKeyConfig::interpolate (ChromaKeyConfig & prev,
+void ChromaKeyConfig::interpolate (ChromaKeyConfig & prev,
                              ChromaKeyConfig & next,
                              int64_t prev_frame,
                              int64_t next_frame, int64_t current_frame)
@@ -143,8 +178,7 @@ ChromaKeyConfig::interpolate (ChromaKeyConfig & prev,
 
 }
 
-int
-ChromaKeyConfig::get_color ()
+int ChromaKeyConfig::get_color ()
 {
   int red = (int) (CLIP (this->red, 0, 1) * 0xff);
   int green = (int) (CLIP (this->green, 0, 1) * 0xff);
@@ -156,139 +190,180 @@ ChromaKeyConfig::get_color ()
 
 ChromaKeyWindow::ChromaKeyWindow (ChromaKeyHSV * plugin)
  : PluginClientWindow(plugin,
-          xS(400),
-          yS(450),
-          xS(400),
-          yS(450),
+          xS(500),
+          yS(550),
+          xS(500),
+          yS(550),
           0)
 {
-  this->plugin = plugin;
-  color_thread = 0;
+       this->plugin = plugin;
+       color_thread = 0;
 }
 
 ChromaKeyWindow::~ChromaKeyWindow ()
 {
-  delete color_thread;
+       delete color_thread;
 }
 
-void
-ChromaKeyWindow::create_objects ()
+void ChromaKeyWindow::create_objects ()
 {
-       int xs5 = xS(5), xs10 = xS(10), xs30 = xS(30), xs100 = xS(100), xs240 = xS(240);
-       int ys5 = yS(5), ys10 = yS(10), ys25 = yS(25), ys50 = yS(50);
-  int y = ys10, y1, x1 = 0, x2 = xs10;
-  int x = xs30;
-
-  BC_Title *title;
-  BC_Bar *bar;
-  int ymargin = get_text_height(MEDIUMFONT) + xs5;
-  int ymargin2 = get_text_height(MEDIUMFONT) + ys10;
-
-  add_subwindow (title = new BC_Title (x2, y, _("Color:")));
-
-  add_subwindow (color = new ChromaKeyColor (plugin, this, x, y + ys25));
-
-  add_subwindow (sample =
-                new BC_SubWindow (x + color->get_w () + xs10, y, xs100, ys50));
-  y += sample->get_h () + ys10;
-
-  add_subwindow (use_colorpicker =
-                new ChromaKeyUseColorPicker (plugin, this, x, y));
-  y += use_colorpicker->get_h() + ys10;
-
-  add_subwindow (new ChromaKeyReset (plugin, this, x2+xs240, y));
-  add_subwindow (show_mask = new ChromaKeyShowMask (plugin, x2, y));
-       y += show_mask->get_h() + ys5;
-
-       add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
-       y += bar->get_h() + ys5;
-       y1 = y;
-  add_subwindow (new BC_Title (x2, y, _("Key parameters:")));
-  y += ymargin;
-  add_subwindow (title = new BC_Title (x, y, _("Hue Tolerance:")));
-  if(title->get_w() > x1) x1 = title->get_w();
-  y += ymargin;
-  add_subwindow (title = new BC_Title (x, y, _("Min. Brightness:")));
-  if(title->get_w() > x1) x1 = title->get_w();
-  y += ymargin;
-  add_subwindow (title = new BC_Title (x, y, _("Max. Brightness:")));
-  if(title->get_w() > x1) x1 = title->get_w();
-  y += ymargin;
-  add_subwindow (title = new BC_Title (x, y, _("Saturation Offset:")));
-  if(title->get_w() > x1) x1 = title->get_w();
-  y += ymargin;
-  add_subwindow (title = new BC_Title (x, y, _("Min Saturation:")));
-  if(title->get_w() > x1) x1 = title->get_w();
-  y += ymargin2;
-
-       add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
-       y += bar->get_h() + ys5;
-  add_subwindow (title = new BC_Title (x2, y, _("Mask tweaking:")));
-  y += ymargin;
-  add_subwindow (title = new BC_Title (x, y, _("In Slope:")));
-  if(title->get_w() > x1) x1 = title->get_w();
-  y += ymargin;
-  add_subwindow (title = new BC_Title (x, y, _("Out Slope:")));
-  if(title->get_w() > x1) x1 = title->get_w();
-  y += ymargin;
-  add_subwindow (title = new BC_Title (x, y, _("Alpha Offset:")));
-  if(title->get_w() > x1) x1 = title->get_w();
-  y += ymargin2;
-
-
-
-       add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
-       y += bar->get_h() + ys5;
-  add_subwindow (title = new BC_Title (x2, y, _("Spill light control:")));
-  y += ymargin;
-  add_subwindow (title = new BC_Title (x, y, _("Spill Threshold:")));
-  if(title->get_w() > x1) x1 = title->get_w();
-  y += ymargin;
-  add_subwindow (title = new BC_Title (x, y, _("Spill Compensation:")));
-  if(title->get_w() > x1) x1 = title->get_w();
-  y += ymargin;
-
-
-       y = y1;
-  y += ymargin;
-       x1 += x;
-
-
-
-  add_subwindow (tolerance = new ChromaKeyTolerance (plugin, x1, y));
-  y += ymargin;
-  add_subwindow (min_brightness = new ChromaKeyMinBrightness (plugin, x1, y));
-  y += ymargin;
-  add_subwindow (max_brightness = new ChromaKeyMaxBrightness (plugin, x1, y));
-  y += ymargin;
-  add_subwindow (saturation = new ChromaKeySaturation (plugin, x1, y));
-  y += ymargin;
-  add_subwindow (min_saturation = new ChromaKeyMinSaturation (plugin, x1, y));
-  y += ymargin;
-
-       y += bar->get_h() + ys5;
-  y += ymargin2;
-  add_subwindow (in_slope = new ChromaKeyInSlope (plugin, x1, y));
-  y += ymargin;
-  add_subwindow (out_slope = new ChromaKeyOutSlope (plugin, x1, y));
-  y += ymargin;
-  add_subwindow (alpha_offset = new ChromaKeyAlphaOffset (plugin, x1, y));
-   y += ymargin;
-
-       y += bar->get_h() + ys5;
-  y += ymargin2;
-  add_subwindow (spill_threshold = new ChromaKeySpillThreshold (plugin, x1, y));
-  y += ymargin;
-  add_subwindow (spill_amount = new ChromaKeySpillAmount (plugin, x1, y));
-
-  color_thread = new ChromaKeyColorThread (plugin, this);
-
-  update_sample ();
-  show_window ();
+       int xs10 = xS(10), xs20 = xS(20), xs100 = xS(100), xs200 = xS(200);
+       int ys10 = yS(10), ys20 = yS(20), ys30 = yS(30), ys40 = yS(40), ys50 = yS(50);
+       int y = ys10,  x2 = xS(160), x3 = xS(260);
+       int x = xs10;
+       int clr_x = get_w()-x - xS(22); // note: clrBtn_w = 22
+
+       BC_Title *title;
+       BC_TitleBar *title_bar;
+       BC_Bar *bar;
+
+// Color section
+       add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, _("Color")));
+       y += ys20;
+
+       add_subwindow (color = new ChromaKeyColor (plugin, this, x, y));
+       // Info for Sample rectangle:       x_slider w_slider w_sample
+       //                                        \       |      /    y,   w,     h
+       add_subwindow (sample = new BC_SubWindow (x3 + xs200 - xs100, y, xs100, ys50));
+       y += ys30;
+       add_subwindow (use_colorpicker = new ChromaKeyUseColorPicker (plugin, this, x, y));
+       y += ys30;
+       add_subwindow (show_mask = new ChromaKeyShowMask (plugin, x, y));
+
+// Key parameters section
+       y += ys30;
+       add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, _("Key parameters")));
+       y += ys20;
+       add_subwindow (title = new BC_Title (x, y, _("Hue Tolerance:")));
+       tolerance_text = new ChromaKeyFText(plugin, this,
+               0, &(plugin->config.tolerance), (x + x2), y, MIN_VALUE, MAX_VALUE);
+       tolerance_text->create_objects();
+       tolerance_slider = new ChromaKeyFSlider(plugin,
+               tolerance_text, &(plugin->config.tolerance), x3, y, MIN_VALUE, MAX_VALUE, xs200);
+       add_subwindow(tolerance_slider);
+       tolerance_text->slider = tolerance_slider;
+       clr_x = x3 + tolerance_slider->get_w() + x;
+       add_subwindow(tolerance_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_TOLERANCE));
+       y += ys30;
+
+       add_subwindow (title = new BC_Title (x, y, _("Min. Brightness:")));
+       min_brightness_text = new ChromaKeyFText(plugin, this,
+               0, &(plugin->config.min_brightness), (x + x2), y,
+               MIN_VALUE, MAX_VALUE);
+       min_brightness_text->create_objects();
+       min_brightness_slider = new ChromaKeyFSlider(plugin,
+               min_brightness_text, &(plugin->config.min_brightness), x3, y, MIN_VALUE, MAX_VALUE, xs200);
+       add_subwindow(min_brightness_slider);
+       min_brightness_text->slider = min_brightness_slider;
+       add_subwindow(min_brightness_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_MIN_BRIGHTNESS));
+       y += ys30;
+
+       add_subwindow (title = new BC_Title (x, y, _("Max. Brightness:")));
+       max_brightness_text = new ChromaKeyFText(plugin, this,
+               0, &(plugin->config.max_brightness), (x + x2), y, MIN_VALUE, MAX_VALUE);
+       max_brightness_text->create_objects();
+       max_brightness_slider = new ChromaKeyFSlider(plugin,
+               max_brightness_text, &(plugin->config.max_brightness), x3, y, MIN_VALUE, MAX_VALUE, xs200);
+       add_subwindow(max_brightness_slider);
+       max_brightness_text->slider = max_brightness_slider;
+       add_subwindow(max_brightness_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_MAX_BRIGHTNESS));
+       y += ys30;
+
+       add_subwindow (title = new BC_Title (x, y, _("Saturation Offset:")));
+       saturation_text = new ChromaKeyFText(plugin, this,
+               0, &(plugin->config.saturation), (x + x2), y, MIN_VALUE, MAX_VALUE);
+       saturation_text->create_objects();
+       saturation_slider = new ChromaKeyFSlider(plugin,
+               saturation_text, &(plugin->config.saturation), x3, y, MIN_VALUE, MAX_VALUE, xs200);
+       add_subwindow(saturation_slider);
+       saturation_text->slider = saturation_slider;
+       add_subwindow(saturation_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_SATURATION));
+       y += ys30;
+
+       add_subwindow (title = new BC_Title (x, y, _("Min Saturation:")));
+       min_saturation_text = new ChromaKeyFText(plugin, this,
+               0, &(plugin->config.min_saturation), (x + x2), y, MIN_VALUE, MAX_VALUE);
+       min_saturation_text->create_objects();
+       min_saturation_slider = new ChromaKeyFSlider(plugin,
+               min_saturation_text, &(plugin->config.min_saturation), x3, y, MIN_VALUE, MAX_VALUE, xs200);
+       add_subwindow(min_saturation_slider);
+       min_saturation_text->slider = min_saturation_slider;
+       add_subwindow(min_saturation_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_MIN_SATURATION));
+       y += ys40;
+
+// Mask tweaking section
+       add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, _("Mask tweaking")));
+       y += ys20;
+       add_subwindow (title = new BC_Title (x, y, _("In Slope:")));
+       in_slope_text = new ChromaKeyFText(plugin, this,
+               0, &(plugin->config.in_slope), (x + x2), y, MIN_SLOPE, MAX_SLOPE);
+       in_slope_text->create_objects();
+       in_slope_slider = new ChromaKeyFSlider(plugin,
+               in_slope_text, &(plugin->config.in_slope), x3, y, MIN_SLOPE, MAX_SLOPE, xs200);
+       add_subwindow(in_slope_slider);
+       in_slope_text->slider = in_slope_slider;
+       add_subwindow(in_slope_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_IN_SLOPE));
+       y += ys30;
+
+       add_subwindow (title = new BC_Title (x, y, _("Out Slope:")));
+       out_slope_text = new ChromaKeyFText(plugin, this,
+               0, &(plugin->config.out_slope), (x + x2), y, MIN_SLOPE, MAX_SLOPE);
+       out_slope_text->create_objects();
+       out_slope_slider = new ChromaKeyFSlider(plugin,
+               out_slope_text, &(plugin->config.out_slope), x3, y, MIN_SLOPE, MAX_SLOPE, xs200);
+       add_subwindow(out_slope_slider);
+       out_slope_text->slider = out_slope_slider;
+       add_subwindow(out_slope_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_OUT_SLOPE));
+       y += ys30;
+
+       add_subwindow (title = new BC_Title (x, y, _("Alpha Offset:")));
+       alpha_offset_text = new ChromaKeyFText(plugin, this,
+               0, &(plugin->config.alpha_offset), (x + x2), y, -MAX_ALPHA, MAX_ALPHA);
+       alpha_offset_text->create_objects();
+       alpha_offset_slider = new ChromaKeyFSlider(plugin,
+               alpha_offset_text, &(plugin->config.alpha_offset), x3, y, -MAX_ALPHA, MAX_ALPHA, xs200);
+       add_subwindow(alpha_offset_slider);
+       alpha_offset_text->slider = alpha_offset_slider;
+       add_subwindow(alpha_offset_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_ALPHA_OFFSET));
+       y += ys40;
+
+// Spill light control section
+       add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, _("Spill light control")));
+       y += ys20;
+       add_subwindow (title = new BC_Title (x, y, _("Spill Threshold:")));
+       spill_threshold_text = new ChromaKeyFText(plugin, this,
+               0, &(plugin->config.spill_threshold), (x + x2), y, MIN_VALUE, MAX_VALUE);
+       spill_threshold_text->create_objects();
+       spill_threshold_slider = new ChromaKeyFSlider(plugin,
+               spill_threshold_text, &(plugin->config.spill_threshold), x3, y, MIN_VALUE, MAX_VALUE, xs200);
+       add_subwindow(spill_threshold_slider);
+       spill_threshold_text->slider = spill_threshold_slider;
+       add_subwindow(spill_threshold_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_SPILL_THRESHOLD));
+       y += ys30;
+
+       add_subwindow (title = new BC_Title (x, y, _("Spill Compensation:")));
+       spill_amount_text = new ChromaKeyFText(plugin, this,
+               0, &(plugin->config.spill_amount), (x + x2), y, MIN_VALUE, MAX_VALUE);
+       spill_amount_text->create_objects();
+       spill_amount_slider = new ChromaKeyFSlider(plugin,
+               spill_amount_text, &(plugin->config.spill_amount), x3, y, MIN_VALUE, MAX_VALUE, xs200);
+       add_subwindow(spill_amount_slider);
+       spill_amount_text->slider = spill_amount_slider;
+       add_subwindow(spill_amount_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_SPILL_AMOUNT));
+       y += ys40;
+
+// Reset section
+       add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
+       y += ys10;
+       add_subwindow(reset = new ChromaKeyReset(plugin, this, x, y));
+
+       color_thread = new ChromaKeyColorThread (plugin, this);
+
+       update_sample();
+       show_window();
 }
 
-void
-ChromaKeyWindow::update_sample ()
+void ChromaKeyWindow::update_sample()
 {
   sample->set_color (plugin->config.get_color ());
   sample->draw_box (0, 0, sample->get_w (), sample->get_h ());
@@ -311,8 +386,7 @@ BC_GenericButton (x, y, _("Color..."))
   this->gui = gui;
 }
 
-int
-ChromaKeyColor::handle_event ()
+int ChromaKeyColor::handle_event ()
 {
   gui->color_thread->start_window (plugin->config.get_color (), 0xff);
   return 1;
@@ -320,144 +394,80 @@ ChromaKeyColor::handle_event ()
 
 
 
-
-ChromaKeyMinBrightness::ChromaKeyMinBrightness (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
-           y,
-           0,
-           xS(200), yS(200), (float) 0, (float) 100, plugin->config.min_brightness)
+ChromaKeyFText::ChromaKeyFText(ChromaKeyHSV *plugin, ChromaKeyWindow *gui,
+       ChromaKeyFSlider *slider, float *output, int x, int y, float min, float max)
+ : BC_TumbleTextBox(gui, *output,
+       min, max, x, y, xS(60), 2)
 {
-  this->plugin = plugin;
-  set_precision (0.01);
+       this->plugin = plugin;
+       this->gui = gui;
+       this->output = output;
+       this->slider = slider;
+       this->min = min;
+       this->max = max;
+       set_increment(0.01);
 }
 
-int
-ChromaKeyMinBrightness::handle_event ()
+ChromaKeyFText::~ChromaKeyFText()
 {
-  plugin->config.min_brightness = get_value ();
-  plugin->send_configure_change ();
-  return 1;
 }
 
-ChromaKeyMaxBrightness::ChromaKeyMaxBrightness (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
-           y,
-           0,
-           xS(200), yS(200), (float) 0, (float) 100, plugin->config.max_brightness)
+int ChromaKeyFText::handle_event()
 {
-  this->plugin = plugin;
-  set_precision (0.01);
+       *output = atof(get_text());
+       if(*output > max) *output = max;
+       else if(*output < min) *output = min;
+       slider->update(*output);
+       plugin->send_configure_change();
+       return 1;
 }
 
-int
-ChromaKeyMaxBrightness::handle_event ()
+ChromaKeyFSlider::ChromaKeyFSlider(ChromaKeyHSV *plugin,
+       ChromaKeyFText *text, float *output, int x, int y,
+       float min, float max, int w)
+ : BC_FSlider(x, y, 0, w, w, min, max, *output)
 {
-  plugin->config.max_brightness = get_value ();
-  plugin->send_configure_change ();
-  return 1;
-}
-
-
-ChromaKeySaturation::ChromaKeySaturation (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
-           y,
-           0, xS(200), yS(200), (float) 0, (float) 100, plugin->config.saturation)
-{
-  this->plugin = plugin;
-  set_precision (0.01);
-}
-
-int
-ChromaKeySaturation::handle_event ()
-{
-  plugin->config.saturation = get_value ();
-  plugin->send_configure_change ();
-  return 1;
-}
-
-ChromaKeyMinSaturation::ChromaKeyMinSaturation (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
-           y,
-           0,
-           xS(200), yS(200), (float) 0, (float) 100, plugin->config.min_saturation)
-{
-  this->plugin = plugin;
-  set_precision (0.01);
-}
-
-int
-ChromaKeyMinSaturation::handle_event ()
-{
-  plugin->config.min_saturation = get_value ();
-  plugin->send_configure_change ();
-  return 1;
-}
-
-
-ChromaKeyTolerance::ChromaKeyTolerance (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
-           y,
-           0, xS(200), yS(200), (float) 0, (float) 100, plugin->config.tolerance)
-{
-  this->plugin = plugin;
-  set_precision (0.01);
+       this->plugin = plugin;
+       this->output = output;
+       this->text = text;
+       set_precision (0.01);
+       enable_show_value(0); // Hide caption
 }
 
-int
-ChromaKeyTolerance::handle_event ()
+ChromaKeyFSlider::~ChromaKeyFSlider()
 {
-  plugin->config.tolerance = get_value ();
-  plugin->send_configure_change ();
-  return 1;
 }
 
-
-
-ChromaKeyInSlope::ChromaKeyInSlope (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
-           y,
-           0, xS(200), yS(200), (float) 0, (float) 20, plugin->config.in_slope)
+int ChromaKeyFSlider::handle_event()
 {
-  this->plugin = plugin;
-  set_precision (0.01);
+       *output = get_value();
+       text->update(*output);
+       plugin->send_configure_change();
+       return 1;
 }
 
-int
-ChromaKeyInSlope::handle_event ()
+ChromaKeyClr::ChromaKeyClr(ChromaKeyHSV *plugin, ChromaKeyWindow *gui, int x, int y, int clear)
+ : BC_Button(x, y, plugin->get_theme()->get_image_set("reset_button"))
 {
-  plugin->config.in_slope = get_value ();
-  plugin->send_configure_change ();
-  return 1;
+       this->plugin = plugin;
+       this->gui = gui;
+       this->clear = clear;
 }
 
-
-ChromaKeyOutSlope::ChromaKeyOutSlope (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
-           y,
-           0, xS(200), yS(200), (float) 0, (float) 20, plugin->config.out_slope)
+ChromaKeyClr::~ChromaKeyClr()
 {
-  this->plugin = plugin;
-  set_precision (0.01);
 }
 
-int
-ChromaKeyOutSlope::handle_event ()
+int ChromaKeyClr::handle_event()
 {
-  plugin->config.out_slope = get_value ();
-  plugin->send_configure_change ();
-  return 1;
+       plugin->config.reset(clear);
+       gui->update_gui(clear);
+       plugin->send_configure_change();
+       return 1;
 }
 
 
-ChromaKeyAlphaOffset::ChromaKeyAlphaOffset (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
-           y,
-           0,
-           xS(200), yS(200), (float) -100, (float) 100, plugin->config.alpha_offset)
-{
-  this->plugin = plugin;
-  set_precision (0.01);
-}
 
-int
-ChromaKeyAlphaOffset::handle_event ()
-{
-  plugin->config.alpha_offset = get_value ();
-  plugin->send_configure_change ();
-  return 1;
-}
 
 ChromaKeyShowMask::ChromaKeyShowMask (ChromaKeyHSV * plugin, int x, int y):BC_CheckBox (x, y, plugin->config.show_mask,
             _
@@ -467,8 +477,7 @@ ChromaKeyShowMask::ChromaKeyShowMask (ChromaKeyHSV * plugin, int x, int y):BC_Ch
 
 }
 
-int
-ChromaKeyShowMask::handle_event ()
+int ChromaKeyShowMask::handle_event ()
 {
   plugin->config.show_mask = get_value ();
   plugin->send_configure_change ();
@@ -482,12 +491,12 @@ ChromaKeyReset::ChromaKeyReset (ChromaKeyHSV *plugin, ChromaKeyWindow *gui, int
   this->gui = gui;
 }
 
-int ChromaKeyReset::handle_event ()
+int ChromaKeyReset::handle_event()
 {
-  plugin->config.reset();
-  gui->update_gui();
-  plugin->send_configure_change();
-  return 1;
+       plugin->config.reset(RESET_DEFAULT_SETTINGS);
+       gui->update_gui(RESET_DEFAULT_SETTINGS);
+       plugin->send_configure_change();
+       return 1;
 }
 
 ChromaKeyUseColorPicker::ChromaKeyUseColorPicker (ChromaKeyHSV * plugin, ChromaKeyWindow * gui, int x, int y)
@@ -499,8 +508,7 @@ ChromaKeyUseColorPicker::ChromaKeyUseColorPicker (ChromaKeyHSV * plugin, ChromaK
   this->gui = gui;
 }
 
-int
-ChromaKeyUseColorPicker::handle_event ()
+int ChromaKeyUseColorPicker::handle_event ()
 {
   plugin->config.red = plugin->get_red ();
   plugin->config.green = plugin->get_green ();
@@ -515,41 +523,6 @@ ChromaKeyUseColorPicker::handle_event ()
 
 
 
-ChromaKeySpillThreshold::ChromaKeySpillThreshold (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
-           y,
-           0,
-           xS(200), yS(200), (float) 0, (float) 100, plugin->config.spill_threshold)
-{
-  this->plugin = plugin;
-  set_precision (0.01);
-}
-
-int
-ChromaKeySpillThreshold::handle_event ()
-{
-  plugin->config.spill_threshold = get_value ();
-  plugin->send_configure_change ();
-  return 1;
-}
-
-ChromaKeySpillAmount::ChromaKeySpillAmount (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
-           y,
-           0, xS(200), yS(200), (float) 0, (float) 100, plugin->config.spill_amount)
-{
-  this->plugin = plugin;
-  set_precision (0.01);
-}
-
-int
-ChromaKeySpillAmount::handle_event ()
-{
-  plugin->config.spill_amount = get_value ();
-  plugin->send_configure_change ();
-  return 1;
-}
-
-
-
 
 
 
@@ -560,8 +533,7 @@ ChromaKeyColorThread::ChromaKeyColorThread (ChromaKeyHSV * plugin, ChromaKeyWind
   this->gui = gui;
 }
 
-int
-ChromaKeyColorThread::handle_new_color (int output, int alpha)
+int ChromaKeyColorThread::handle_new_color (int output, int alpha)
 {
   plugin->config.red = (float) (output & 0xff0000) / 0xff0000;
   plugin->config.green = (float) (output & 0xff00) / 0xff00;
@@ -592,8 +564,7 @@ ChromaKeyServer::ChromaKeyServer (ChromaKeyHSV * plugin):LoadServer (plugin->Plu
   this->plugin = plugin;
 }
 
-void
-ChromaKeyServer::init_packages ()
+void ChromaKeyServer::init_packages ()
 {
   for (int i = 0; i < get_total_packages (); i++)
     {
@@ -1007,26 +978,85 @@ void ChromaKeyHSV::update_gui()
                load_configuration();
                ChromaKeyWindow *window = (ChromaKeyWindow*)thread->window;
                window->lock_window();
-               window->update_gui();
+               window->update_gui(RESET_ALL);
                window->unlock_window();
        }
 }
 
-void ChromaKeyWindow::update_gui()
+void ChromaKeyWindow::update_gui(int clear)
 {
        ChromaKeyConfig &config = plugin->config;
-       min_brightness->update(config.min_brightness);
-       max_brightness->update(config.max_brightness);
-       saturation->update(config.saturation);
-       min_saturation->update(config.min_saturation);
-       tolerance->update(config.tolerance);
-       in_slope->update(config.in_slope);
-       out_slope->update(config.out_slope);
-       alpha_offset->update(config.alpha_offset);
-       spill_threshold->update(config.spill_threshold);
-       spill_amount->update(config.spill_amount);
-       show_mask->update(config.show_mask);
-       update_sample();
+       switch(clear) {
+               case RESET_RGB :
+                       update_sample();
+                       break;
+               case RESET_MIN_BRIGHTNESS :
+                       min_brightness_text->update(config.min_brightness);
+                       min_brightness_slider->update(config.min_brightness);
+                       break;
+               case RESET_MAX_BRIGHTNESS :
+                       max_brightness_text->update(config.max_brightness);
+                       max_brightness_slider->update(config.max_brightness);
+                       break;
+               case RESET_TOLERANCE :
+                       tolerance_text->update(config.tolerance);
+                       tolerance_slider->update(config.tolerance);
+                       break;
+               case RESET_SATURATION :
+                       saturation_text->update(config.saturation);
+                       saturation_slider->update(config.saturation);
+                       break;
+               case RESET_MIN_SATURATION :
+                       min_saturation_text->update(config.min_saturation);
+                       min_saturation_slider->update(config.min_saturation);
+                       break;
+               case RESET_IN_SLOPE :
+                       in_slope_text->update(config.in_slope);
+                       in_slope_slider->update(config.in_slope);
+                       break;
+               case RESET_OUT_SLOPE :
+                       out_slope_text->update(config.out_slope);
+                       out_slope_slider->update(config.out_slope);
+                       break;
+               case RESET_ALPHA_OFFSET :
+                       alpha_offset_text->update(config.alpha_offset);
+                       alpha_offset_slider->update(config.alpha_offset);
+                       break;
+               case RESET_SPILL_THRESHOLD :
+                       spill_threshold_text->update(config.spill_threshold);
+                       spill_threshold_slider->update(config.spill_threshold);
+                       break;
+               case RESET_SPILL_AMOUNT :
+                       spill_amount_text->update(config.spill_amount);
+                       spill_amount_slider->update(config.spill_amount);
+                       break;
+               case RESET_ALL :
+               case RESET_DEFAULT_SETTINGS :
+               default:
+                       min_brightness_text->update(config.min_brightness);
+                       min_brightness_slider->update(config.min_brightness);
+                       max_brightness_text->update(config.max_brightness);
+                       max_brightness_slider->update(config.max_brightness);
+                       tolerance_text->update(config.tolerance);
+                       tolerance_slider->update(config.tolerance);
+                       saturation_text->update(config.saturation);
+                       saturation_slider->update(config.saturation);
+                       min_saturation_text->update(config.min_saturation);
+                       min_saturation_slider->update(config.min_saturation);
+                       in_slope_text->update(config.in_slope);
+                       in_slope_slider->update(config.in_slope);
+                       out_slope_text->update(config.out_slope);
+                       out_slope_slider->update(config.out_slope);
+                       alpha_offset_text->update(config.alpha_offset);
+                       alpha_offset_slider->update(config.alpha_offset);
+                       spill_threshold_text->update(config.spill_threshold);
+                       spill_threshold_slider->update(config.spill_threshold);
+                       spill_amount_text->update(config.spill_amount);
+                       spill_amount_slider->update(config.spill_amount);
+                       show_mask->update(config.show_mask);
+                       update_sample();
+                       break;
+       }
 }
 
 
index 51bcbcf75ab52997fb0580494f06d3d59da4befb..d5bff9af4b3017889b38f0aeece5d3dc52f70941 100644 (file)
 
 #include "colorpicker.h"
 #include "guicast.h"
+#include "theme.h"
 #include "loadbalance.h"
 #include "pluginvclient.h"
 
+#define RESET_DEFAULT_SETTINGS 20
+#define RESET_ALL               0
+#define RESET_RGB               1
+#define RESET_MIN_BRIGHTNESS    2
+#define RESET_MAX_BRIGHTNESS    3
+#define RESET_TOLERANCE         4
+#define RESET_SATURATION        5
+#define RESET_MIN_SATURATION    6
+#define RESET_IN_SLOPE          7
+#define RESET_OUT_SLOPE         8
+#define RESET_ALPHA_OFFSET      9
+#define RESET_SPILL_THRESHOLD  10
+#define RESET_SPILL_AMOUNT     11
+
+#define MIN_VALUE   0.00
+#define MAX_VALUE 100.00
+#define MIN_SLOPE   0.00
+#define MAX_SLOPE  20.00
+#define MAX_ALPHA 100.00
 
-class ChromaKeyHSV;
 class ChromaKeyHSV;
 class ChromaKeyWindow;
+class ChromaKeyFText;
+class ChromaKeyFSlider;
+class ChromaKeyReset;
+class ChromaKeyClr;
 
 enum {
        CHROMAKEY_POSTPROCESS_NONE,
@@ -45,7 +68,7 @@ class ChromaKeyConfig
 {
 public:
        ChromaKeyConfig();
-       void reset();
+       void reset(int clear);
        void copy_from(ChromaKeyConfig &src);
        int equivalent(ChromaKeyConfig &src);
        void interpolate(ChromaKeyConfig &prev,
@@ -99,89 +122,45 @@ public:
        ChromaKeyWindow *gui;
 };
 
-
-
-class ChromaKeyMinBrightness : public BC_FSlider
-{
-       public:
-               ChromaKeyMinBrightness(ChromaKeyHSV *plugin, int x, int y);
-               int handle_event();
-               ChromaKeyHSV *plugin;
-};
-
-class ChromaKeyMaxBrightness : public BC_FSlider
-{
-       public:
-               ChromaKeyMaxBrightness(ChromaKeyHSV *plugin, int x, int y);
-               int handle_event();
-               ChromaKeyHSV *plugin;
-};
-
-class ChromaKeySaturation : public BC_FSlider
-{
-       public:
-               ChromaKeySaturation(ChromaKeyHSV *plugin, int x, int y);
-               int handle_event();
-               ChromaKeyHSV *plugin;
-};
-
-class ChromaKeyMinSaturation : public BC_FSlider
-{
-       public:
-               ChromaKeyMinSaturation(ChromaKeyHSV *plugin, int x, int y);
-               int handle_event();
-               ChromaKeyHSV *plugin;
-};
-
-
-
-class ChromaKeyTolerance : public BC_FSlider
+class ChromaKeyFText : public BC_TumbleTextBox
 {
 public:
-       ChromaKeyTolerance(ChromaKeyHSV *plugin, int x, int y);
+       ChromaKeyFText(ChromaKeyHSV *plugin, ChromaKeyWindow *gui,
+               ChromaKeyFSlider *slider, float *output, int x, int y, float min, float max);
+       ~ChromaKeyFText();
        int handle_event();
        ChromaKeyHSV *plugin;
+       ChromaKeyWindow *gui;
+       ChromaKeyFSlider *slider;
+       float *output;
+       float min, max;
 };
 
-class ChromaKeyInSlope : public BC_FSlider
-{
-       public:
-               ChromaKeyInSlope(ChromaKeyHSV *plugin, int x, int y);
-               int handle_event();
-               ChromaKeyHSV *plugin;
-};
-
-class ChromaKeyOutSlope : public BC_FSlider
-{
-       public:
-               ChromaKeyOutSlope(ChromaKeyHSV *plugin, int x, int y);
-               int handle_event();
-               ChromaKeyHSV *plugin;
-};
-
-class ChromaKeyAlphaOffset : public BC_FSlider
-{
-       public:
-               ChromaKeyAlphaOffset(ChromaKeyHSV *plugin, int x, int y);
-               int handle_event();
-               ChromaKeyHSV *plugin;
-};
-
-class ChromaKeySpillThreshold : public BC_FSlider
+class ChromaKeyFSlider : public BC_FSlider
 {
 public:
-       ChromaKeySpillThreshold(ChromaKeyHSV *plugin, int x, int y);
+       ChromaKeyFSlider(ChromaKeyHSV *plugin,
+               ChromaKeyFText *text, float *output, int x, int y,
+               float min, float max, int w);
+       ~ChromaKeyFSlider();
        int handle_event();
        ChromaKeyHSV *plugin;
+       ChromaKeyFText *text;
+       float *output;
 };
-class ChromaKeySpillAmount : public BC_FSlider
+
+class ChromaKeyClr : public BC_Button
 {
 public:
-       ChromaKeySpillAmount(ChromaKeyHSV *plugin, int x, int y);
+       ChromaKeyClr(ChromaKeyHSV *plugin, ChromaKeyWindow *gui, int x, int y, int clear);
+       ~ChromaKeyClr();
        int handle_event();
        ChromaKeyHSV *plugin;
+       ChromaKeyWindow *gui;
+       int clear;
 };
 
+
 class ChromaKeyUseColorPicker : public BC_GenericButton
 {
 public:
@@ -219,21 +198,52 @@ public:
 
        void create_objects();
        void update_sample();
-       void update_gui();
+       void update_gui(int clear);
        void done_event(int result);
 
        ChromaKeyColor *color;
        ChromaKeyUseColorPicker *use_colorpicker;
-       ChromaKeyMinBrightness *min_brightness;
-       ChromaKeyMaxBrightness *max_brightness;
-       ChromaKeySaturation *saturation;
-       ChromaKeyMinSaturation *min_saturation;
-       ChromaKeyTolerance *tolerance;
-       ChromaKeyInSlope *in_slope;
-       ChromaKeyOutSlope *out_slope;
-       ChromaKeyAlphaOffset *alpha_offset;
-       ChromaKeySpillThreshold *spill_threshold;
-       ChromaKeySpillAmount *spill_amount;
+
+       ChromaKeyFText *min_brightness_text;
+       ChromaKeyFSlider *min_brightness_slider;
+       ChromaKeyClr *min_brightness_Clr;
+
+       ChromaKeyFText *max_brightness_text;
+       ChromaKeyFSlider *max_brightness_slider;
+       ChromaKeyClr *max_brightness_Clr;
+
+       ChromaKeyFText *saturation_text;
+       ChromaKeyFSlider *saturation_slider;
+       ChromaKeyClr *saturation_Clr;
+
+       ChromaKeyFText *min_saturation_text;
+       ChromaKeyFSlider *min_saturation_slider;
+       ChromaKeyClr *min_saturation_Clr;
+
+       ChromaKeyFText *tolerance_text;
+       ChromaKeyFSlider *tolerance_slider;
+       ChromaKeyClr *tolerance_Clr;
+
+       ChromaKeyFText *in_slope_text;
+       ChromaKeyFSlider *in_slope_slider;
+       ChromaKeyClr *in_slope_Clr;
+
+       ChromaKeyFText *out_slope_text;
+       ChromaKeyFSlider *out_slope_slider;
+       ChromaKeyClr *out_slope_Clr;
+
+       ChromaKeyFText *alpha_offset_text;
+       ChromaKeyFSlider *alpha_offset_slider;
+       ChromaKeyClr *alpha_offset_Clr;
+
+       ChromaKeyFText *spill_threshold_text;
+       ChromaKeyFSlider *spill_threshold_slider;
+       ChromaKeyClr *spill_threshold_Clr;
+
+       ChromaKeyFText *spill_amount_text;
+       ChromaKeyFSlider *spill_amount_slider;
+       ChromaKeyClr *spill_amount_Clr;
+
        ChromaKeyShowMask *show_mask;
        ChromaKeyReset *reset;
        BC_SubWindow *sample;
@@ -307,10 +317,3 @@ public:
 
 
 #endif
-
-
-
-
-
-
-
diff --git a/cinelerra-5.1/plugins/swatch/Makefile b/cinelerra-5.1/plugins/swatch/Makefile
new file mode 100644 (file)
index 0000000..4f1cb99
--- /dev/null
@@ -0,0 +1,8 @@
+include ../../plugin_defs
+
+PLUGIN = swatch
+OBJS = $(OBJDIR)/swatch.o
+
+include ../../plugin_config
+
+$(OBJDIR)/swatch.o: swatch.C
diff --git a/cinelerra-5.1/plugins/swatch/swatch.C b/cinelerra-5.1/plugins/swatch/swatch.C
new file mode 100644 (file)
index 0000000..2e9142e
--- /dev/null
@@ -0,0 +1,760 @@
+/*
+ * CINELERRA
+ * Copyright (C) 2024 Adam Williams <broadcast at earthling dot net>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ */
+
+
+// draw a color swatch for a given brightness or saturation
+// does not visualize but draws output to be processed
+
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "bcdisplayinfo.h"
+#include "bccolors.h"
+#include "clip.h"
+#include "bchash.h"
+#include "filexml.h"
+#include "keyframe.h"
+#include "language.h"
+#include "playback3d.h"
+#include "swatch.h"
+#include "vframe.h"
+
+
+
+
+REGISTER_PLUGIN(SwatchMain)
+
+
+#define MAX_VALUE 100
+
+
+
+SwatchConfig::SwatchConfig()
+{
+       brightness = MAX_VALUE;
+    saturation = MAX_VALUE;
+    fix_brightness = 0;
+    angle = 0;
+    draw_src = 0;
+}
+
+int SwatchConfig::equivalent(SwatchConfig &that)
+{
+       return brightness == that.brightness &&
+        saturation == that.saturation &&
+        fix_brightness == that.fix_brightness &&
+        angle == that.angle &&
+        draw_src == that.draw_src;
+}
+
+void SwatchConfig::copy_from(SwatchConfig &that)
+{
+       brightness = that.brightness;
+       saturation = that.saturation;
+    fix_brightness = that.fix_brightness;
+    angle = that.angle;
+    draw_src = that.draw_src;
+}
+
+void SwatchConfig::interpolate(SwatchConfig &prev, 
+       SwatchConfig &next, 
+       long prev_frame, 
+       long next_frame, 
+       long current_frame)
+{
+       double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
+       double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
+
+
+       this->brightness = (int)(prev.brightness * prev_scale + next.brightness * next_scale);
+       this->saturation = (int)(prev.saturation * prev_scale + next.saturation * next_scale);
+       this->angle = (int)(prev.angle * prev_scale + next.angle * next_scale);
+    fix_brightness = prev.fix_brightness;
+    draw_src = prev.draw_src;
+}
+
+
+
+
+SwatchSlider::SwatchSlider(SwatchMain *plugin, 
+                          SwatchWindow *gui,
+                          int x, 
+                          int y,
+                          int min,
+                          int max,
+                          int *output)
+  : BC_ISlider(x,
+              y,
+              0, 
+              gui->get_w() - xS(10) - x, 
+              gui->get_w() - xS(10) - x, 
+              min, 
+              max, 
+              *output)
+{
+  this->plugin = plugin;
+  this->output = output;
+}
+
+int SwatchSlider::handle_event ()
+{
+       *output = get_value();
+       plugin->send_configure_change();
+       return 1;
+}
+
+SwatchRadial::SwatchRadial(SwatchMain *plugin, 
+    SwatchWindow *gui, 
+    int x, 
+    int y, 
+    const char *text,
+    int fix_brightness)
+ : BC_Radial(x, 
+       y, 
+       (plugin->config.fix_brightness && fix_brightness), 
+       text)
+{
+       this->plugin = plugin;
+    this->gui = gui;
+    this->fix_brightness = fix_brightness;
+}
+
+int SwatchRadial::handle_event()
+{
+    plugin->config.fix_brightness = fix_brightness;
+    gui->update_fixed();
+    plugin->send_configure_change();
+    return 1;
+}
+
+
+SwatchCheck::SwatchCheck(SwatchMain *plugin, 
+    int x, 
+    int y, 
+    const char *text,
+    int *output)
+ : BC_CheckBox(x, 
+       y, 
+       *output, 
+       text)
+{
+       this->plugin = plugin;
+    this->output = output;
+}
+
+int SwatchCheck::handle_event()
+{
+    *output = get_value();
+    plugin->send_configure_change();
+    return 1;
+}
+
+
+
+
+SwatchWindow::SwatchWindow(SwatchMain *plugin)
+ : PluginClientWindow(plugin,
+       xS(350), 
+       yS(300), 
+       xS(350), 
+       yS(300), 
+       0)
+{
+       this->plugin = plugin;
+}
+
+SwatchWindow::~SwatchWindow()
+{
+}
+
+void SwatchWindow::create_objects()
+{
+       int margin = yS(10);
+       int x = margin, y = margin;
+       BC_Title *title;
+
+       add_subwindow(brightness_title = new BC_Title(x, y, _("Brightness:")));
+    y += brightness_title->get_h() + margin;
+       add_subwindow (brightness = new SwatchSlider(plugin, this, x, y, 0, MAX_VALUE, &plugin->config.brightness));
+    y += brightness->get_h() + margin;
+
+       add_subwindow(saturation_title = new BC_Title(x, y, _("Saturation:")));
+    y += saturation_title->get_h() + margin;
+       add_subwindow (saturation = new SwatchSlider(plugin, this, x, y, 0, MAX_VALUE, &plugin->config.saturation));
+    y += saturation->get_h() + margin;
+
+       add_subwindow(title = new BC_Title(x, y, _("Angle:")));
+    y += title->get_h() + margin;
+       add_subwindow (angle = new SwatchSlider(plugin, this, x, y, -180, 180, &plugin->config.angle));
+    y += saturation->get_h() + margin;
+
+    add_subwindow(fix_brightness = new SwatchRadial(plugin, 
+        this, 
+        x, 
+        y, 
+        _("Constant Brightness"),
+        1));
+    y += fix_brightness->get_h() + margin;
+    add_subwindow(fix_saturation = new SwatchRadial(plugin, 
+        this, 
+        x, 
+        y, 
+        _("Constant Saturation"),
+        0));
+    y += fix_saturation->get_h() + margin;
+    update_fixed();
+    
+    add_subwindow(draw_src = new SwatchCheck(plugin, 
+        x, 
+        y, 
+        _("Draw source"),
+        &plugin->config.draw_src));
+   
+    
+       show_window();
+}
+
+void SwatchWindow::update_fixed()
+{
+    fix_brightness->update(plugin->config.fix_brightness);
+    fix_saturation->update(!plugin->config.fix_brightness);
+    if(plugin->config.fix_brightness)
+    {
+        saturation_title->set_color(BC_WindowBase::get_resources()->disabled_text_color);
+        brightness_title->set_color(BC_WindowBase::get_resources()->default_text_color);
+        saturation->disable();
+        brightness->enable();
+    }
+    else
+    {
+        saturation_title->set_color(BC_WindowBase::get_resources()->default_text_color);
+        brightness_title->set_color(BC_WindowBase::get_resources()->disabled_text_color);
+        saturation->enable();
+        brightness->disable();
+    }
+}
+
+
+SwatchMain::SwatchMain(PluginServer *server)
+ : PluginVClient(server)
+{
+       need_reconfigure = 1;
+       engine = 0;
+    temp = 0;
+    src_temp = 0;
+}
+
+SwatchMain::~SwatchMain()
+{
+       if(engine) delete engine;
+    if(temp) delete temp;
+    if(src_temp) delete src_temp;
+}
+
+const char* SwatchMain::plugin_title() { return N_("Color Swatch"); }
+int SwatchMain::is_realtime() { return 1; }
+
+
+NEW_WINDOW_MACRO(SwatchMain, SwatchWindow)
+
+LOAD_CONFIGURATION_MACRO(SwatchMain, SwatchConfig)
+
+int SwatchMain::is_synthesis()
+{
+       return 1;
+}
+
+int SwatchMain::process_buffer(VFrame *frame,
+       int64_t start_position,
+       double frame_rate)
+{
+       need_reconfigure |= load_configuration();
+       int use_opengl = get_use_opengl();
+
+// have to draw output pixels out of order
+    if(config.draw_src) use_opengl = 0;
+
+       if(use_opengl) return run_opengl();
+
+       if(!engine) engine = new SwatchEngine(this,
+               get_project_smp() + 1,
+               get_project_smp() + 1);
+    if(config.draw_src)
+    {
+        if(!src_temp)
+            src_temp = new VFrame(0, 
+                       -1,
+                       frame->get_w(),
+                       frame->get_h(),
+                   frame->get_color_model(),
+                       -1);
+
+        read_frame(src_temp, 
+                   0, 
+                   start_position, 
+                   frame_rate,
+                   use_opengl);
+    }
+
+
+       if(!temp) temp = new VFrame(0, 
+               -1,
+               frame->get_w(),
+               frame->get_h(),
+           frame->get_color_model(),
+               -1);
+
+// draw pattern once
+    if(need_reconfigure)
+           engine->draw_pattern();
+
+// draw the pattern on the output
+    frame->copy_from(temp);
+// draw input on the pattern
+    if(config.draw_src)
+        engine->draw_src();
+
+//printf("SwatchMain::process_buffer %d %d\n", __LINE__, config.draw_src);
+       return 0;
+}
+
+
+void SwatchMain::update_gui()
+{
+       if(thread)
+       {
+               if(load_configuration())
+               {
+                       ((SwatchWindow*)thread->window)->lock_window("SwatchMain::update_gui");
+                       ((SwatchWindow*)thread->window)->brightness->update(config.brightness);
+                       ((SwatchWindow*)thread->window)->saturation->update(config.saturation);
+                       ((SwatchWindow*)thread->window)->angle->update(config.angle);
+                       ((SwatchWindow*)thread->window)->draw_src->update(config.draw_src);
+                       ((SwatchWindow*)thread->window)->update_fixed();
+                       ((SwatchWindow*)thread->window)->unlock_window();
+               }
+       }
+}
+
+
+
+
+
+void SwatchMain::save_data(KeyFrame *keyframe)
+{
+       FileXML output;
+
+// cause data to be stored directly in text
+       output.set_shared_output(keyframe->xbuf);
+       output.tag.set_title("SWATCH");
+
+       output.tag.set_property("BRIGHTNESS", config.brightness);
+       output.tag.set_property("SATURATION", config.saturation);
+       output.tag.set_property("ANGLE", config.angle);
+       output.tag.set_property("FIX_BRIGHTNESS", config.fix_brightness);
+       output.tag.set_property("DRAW_SRC", config.draw_src);
+       output.append_tag();
+       output.tag.set_title("/SWATCH");
+       output.append_tag();
+       output.append_newline();
+       output.terminate_string();
+//printf("SwatchMain::save_data %d %s\n", __LINE__, output.string);
+}
+
+void SwatchMain::read_data(KeyFrame *keyframe)
+{
+  FileXML input;
+
+  input.set_shared_input(keyframe->xbuf);
+
+  while( !input.read_tag() )
+  {
+    if(input.tag.title_is("SWATCH"))
+    {
+      config.brightness =
+       input.tag.get_property("BRIGHTNESS", config.brightness);
+      config.saturation =
+       input.tag.get_property("SATURATION", config.saturation);
+      config.angle =
+       input.tag.get_property("ANGLE", config.angle);
+      config.fix_brightness =
+       input.tag.get_property("FIX_BRIGHTNESS", config.fix_brightness);
+      config.draw_src =
+       input.tag.get_property("DRAW_SRC", config.draw_src);
+    }
+  }
+}
+
+int SwatchMain::handle_opengl()
+{
+#ifdef HAVE_GL
+       const char *head_frag =
+               "uniform mat3 yuv_to_rgb_matrix;\n"
+               "uniform mat3 rgb_to_yuv_matrix;\n"
+               "uniform float yminf;\n"
+               "uniform sampler2D tex;\n"
+               "uniform vec2 texture_extents;\n"
+               "uniform vec2 center_coord;\n"
+               "uniform float value;\n"
+               "uniform float saturation;\n"
+               "uniform float angle;\n"
+               "uniform bool fix_value;\n"
+               "\n"
+               "void main()\n"
+               "{\n"
+               "       vec2 out_coord = gl_TexCoord[0].st * texture_extents;\n"
+               "       vec2 in_coord = vec2(out_coord.x - center_coord.x, out_coord.y - center_coord.y);\n"
+        "   float max_s = center_coord.x;\n"
+        "   if(center_coord.y < max_s) max_s = center_coord.y;\n"
+        "   vec4 pixel;\n"
+        "   pixel.a = 1.0;\n"
+        "   pixel.r = atan(in_coord.x, in_coord.y) / 2.0 / 3.14159 * 360.0 + angle; // hue\n"
+        "   if(pixel.r < 0.0) pixel.r += 360.0;\n"
+        "   if(fix_value)\n"
+        "   {\n"
+        "       pixel.g = length(vec2(in_coord.x, in_coord.y)) / max_s; // saturation\n"
+        "       if(pixel.g > 1.0) pixel.g = 1.0; \n"
+        "       pixel.b = value;\n"
+        "   }\n"
+        "   else\n"
+        "   {\n"
+        "       pixel.g = saturation;\n"
+        "       pixel.b = length(vec2(in_coord.x, in_coord.y)) / max_s; // value\n"
+        "       if(pixel.b > 1.0) pixel.b = 1.0; \n"
+        "   }\n"
+        HSV_TO_RGB_FRAG("pixel");
+
+       static const char *put_yuv_frag =
+                       RGB_TO_YUV_FRAG("pixel")
+               "       gl_FragColor = pixel;\n"
+               "}\n";
+
+       static const char *put_rgb_frag =
+               "       gl_FragColor = pixel;\n"
+               "}\n";
+
+
+
+
+       const char *shader_stack[5] = { 0, 0, 0, 0, 0 };
+       shader_stack[0] = head_frag;
+       if(BC_CModels::is_yuv(get_output()->get_color_model()))
+         shader_stack[1] = put_yuv_frag;
+       else
+         shader_stack[1] = put_rgb_frag;
+
+       get_output()->set_opengl_state(VFrame::TEXTURE);
+       get_output()->to_texture();
+       get_output()->enable_opengl();
+       get_output()->init_screen();
+       get_output()->bind_texture(0);
+
+       unsigned int frag = VFrame::make_shader(0, 
+               shader_stack[0], 
+               shader_stack[1], 
+               0);
+
+       if(frag)
+       {
+               glUseProgram(frag);
+               float w = get_output()->get_w();
+               float h = get_output()->get_h();
+               float texture_w = get_output()->get_texture_w();
+               float texture_h = get_output()->get_texture_h();
+               glUniform1i(glGetUniformLocation(frag, "tex"), 0);
+               glUniform2f(glGetUniformLocation(frag, "center_coord"), 
+                               (GLfloat)w / 2,
+                               (GLfloat)h / 2);
+               glUniform2f(glGetUniformLocation(frag, "texture_extents"), 
+                               (GLfloat)texture_w,
+                               (GLfloat)texture_h);
+
+               glUniform1f(glGetUniformLocation(frag, "value"), (float)config.brightness / MAX_VALUE);
+               glUniform1f(glGetUniformLocation(frag, "saturation"), (float)config.saturation / MAX_VALUE);
+               glUniform1f(glGetUniformLocation(frag, "angle"), (float)config.angle);
+               glUniform1i(glGetUniformLocation(frag, "fix_value"), config.fix_brightness);
+               if(BC_CModels::is_yuv(get_output()->get_color_model()))
+                       BC_GL_COLORS(frag);
+       }
+
+       get_output()->draw_texture();
+       glUseProgram(0);
+       get_output()->set_opengl_state(VFrame::SCREEN);
+       
+#endif
+    return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+SwatchPackage::SwatchPackage()
+ : LoadPackage()
+{
+}
+
+
+
+
+SwatchUnit::SwatchUnit(SwatchEngine *server, SwatchMain *plugin)
+ : LoadClient(server)
+{
+       this->plugin = plugin;
+       this->server = server;
+}
+
+
+
+
+#define CREATE_SWATCH(type, components, max, is_yuv) \
+{ \
+       for(int i = pkg->y1; i < pkg->y2; i++) \
+       { \
+               type *out_row = (type*)plugin->temp->get_rows()[i]; \
+        for(int j = 0; j < w; j++) \
+        { \
+            float hue = atan2(j - center_x, i - center_y) * 360 / 2 / M_PI + angle; \
+            if(fix_brightness) \
+            { \
+                saturation = hypot(j - center_x, i - center_y) / max_s; \
+                if(saturation > 1) saturation = 1; \
+            } \
+            else \
+            { \
+                value = hypot(j - center_x, i - center_y) / max_s; \
+                if(value > 1) value = 1; \
+            } \
+            if(hue < 0) hue += 360; \
+            if(is_yuv) \
+            { \
+                int y, u, v; \
+                HSV::hsv_to_yuv(y, u, v, hue, saturation, value, max); \
+                out_row[0] = y; \
+                out_row[1] = u; \
+                out_row[2] = v; \
+            } \
+            else \
+            { \
+                float r, g, b; \
+                HSV::hsv_to_rgb(r, g, b, hue, saturation, value); \
+                out_row[0] = (type)(r * max); \
+                out_row[1] = (type)(g * max); \
+                out_row[2] = (type)(b * max); \
+            } \
+ \
+/* the alpha */ \
+            if(components == 4) out_row[3] = max; \
+            out_row += components; \
+        } \
+    } \
+}
+
+
+#define DRAW_SRC(type, components, max, is_yuv) \
+{ \
+    type **dst_rows = (type**)plugin->get_output()->get_rows(); \
+       for(int i = pkg->y1; i < pkg->y2; i++) \
+       { \
+               type *src_row = (type*)plugin->src_temp->get_rows()[i]; \
+        for(int j = 0; j < w; j++) \
+        { \
+/* the source values */ \
+            type r, g, b; \
+            float r2, g2, b2; \
+            if(is_yuv) \
+            { \
+               YUV::yuv.yuv_to_rgb_f (r2, g2, b2, src_row[0], src_row[1], src_row[2]); \
+            } \
+            else \
+            { \
+                r = src_row[0]; \
+                g = src_row[1]; \
+                b = src_row[2]; \
+                r2 = (float)r / max; \
+                g2 = (float)g / max; \
+                b2 = (float)b / max; \
+            } \
+            float hue, s, value; \
+            HSV::rgb_to_hsv(r2, g2, b2, hue, s, value); \
+            float h_rad = TO_RAD(hue - angle); \
+/* get coordinate of color in output */ \
+            int x_out, y_out; \
+            if(fix_brightness) \
+            { \
+                x_out = center_x + (int)(sin(h_rad) * s * max_s); \
+                y_out = center_y + (int)(cos(h_rad) * s * max_s); \
+            } \
+            else \
+            { \
+                x_out = center_x + (int)(sin(h_rad) * value * max_s); \
+                y_out = center_y + (int)(cos(h_rad) * value * max_s); \
+            } \
+            if(x_out >= 0 && x_out < w && y_out >= 0 && y_out < h) \
+            { \
+                type *dst = dst_rows[y_out] + x_out * components; \
+                if(is_yuv) \
+                { \
+                    dst[0] = src_row[0]; \
+                    dst[1] = src_row[1]; \
+                    dst[2] = src_row[2]; \
+                } \
+                else \
+                { \
+                    dst[0] = r; \
+                    dst[1] = g; \
+                    dst[2] = b; \
+                } \
+            } \
+ \
+            src_row += components; \
+        } \
+    } \
+}
+
+#define DRAW_PATTERN_MODE 0
+#define DRAW_SRC_MODE 1
+
+void SwatchUnit::process_package(LoadPackage *package)
+{
+       SwatchPackage *pkg = (SwatchPackage*)package;
+       int h = plugin->temp->get_h();
+       int w = plugin->temp->get_w();
+       int center_x = w / 2;
+       int center_y = h / 2;
+       int cmodel = plugin->temp->get_color_model();
+// maximum saturation
+    float max_s = center_x;
+    if(center_y < max_s) max_s = center_y;
+    int fix_brightness = plugin->config.fix_brightness;
+    float saturation = (float)plugin->config.saturation / MAX_VALUE;
+    float value = (float)plugin->config.brightness / MAX_VALUE;
+    float angle = plugin->config.angle;
+
+    if(server->mode == DRAW_PATTERN_MODE)
+    {
+           switch(cmodel)
+           {
+                   case BC_RGB888:
+                           CREATE_SWATCH(unsigned char, 3, 0xff, 0)
+                           break;
+                   case BC_RGBA8888:
+                           CREATE_SWATCH(unsigned char, 4, 0xff, 0)
+                           break;
+                   case BC_RGB_FLOAT:
+                           CREATE_SWATCH(float, 3, 1.0, 0)
+                           break;
+                   case BC_RGBA_FLOAT:
+                           CREATE_SWATCH(float, 4, 1.0, 0)
+                           break;
+                   case BC_YUV888:
+                           CREATE_SWATCH(unsigned char, 3, 0xff, 1)
+                           break;
+                   case BC_YUVA8888:
+                           CREATE_SWATCH(unsigned char, 4, 0xff, 1)
+                           break;
+           }
+    }
+    else
+    {
+           switch(cmodel)
+           {
+                   case BC_RGB888:
+                           DRAW_SRC(unsigned char, 3, 0xff, 0)
+                           break;
+                   case BC_RGBA8888:
+                           DRAW_SRC(unsigned char, 4, 0xff, 0)
+                           break;
+                   case BC_RGB_FLOAT:
+                           DRAW_SRC(float, 3, 1.0, 0)
+                           break;
+                   case BC_RGBA_FLOAT:
+                           DRAW_SRC(float, 4, 1.0, 0)
+                           break;
+                   case BC_YUV888:
+                           DRAW_SRC(unsigned char, 3, 0xff, 1)
+                           break;
+                   case BC_YUVA8888:
+                           DRAW_SRC(unsigned char, 4, 0xff, 1)
+                           break;
+           }
+    }
+}
+
+
+
+
+
+
+SwatchEngine::SwatchEngine(SwatchMain *plugin, 
+       int total_clients, 
+       int total_packages)
+ : LoadServer(total_clients, total_packages)
+{
+       this->plugin = plugin;
+}
+
+void SwatchEngine::draw_pattern()
+{
+    mode = DRAW_PATTERN_MODE;
+    process_packages();
+}
+
+void SwatchEngine::draw_src()
+{
+    mode = DRAW_SRC_MODE;
+    process_packages();
+}
+
+void SwatchEngine::init_packages()
+{
+       for(int i = 0; i < get_total_packages(); i++)
+       {
+               SwatchPackage *package = (SwatchPackage*)get_package(i);
+               package->y1 = plugin->temp->get_h() * 
+                       i / 
+                       get_total_packages();
+               package->y2 = plugin->temp->get_h() * 
+                       (i + 1) /
+                       get_total_packages();
+       }
+}
+
+LoadClient* SwatchEngine::new_client()
+{
+       return new SwatchUnit(this, plugin);
+}
+
+LoadPackage* SwatchEngine::new_package()
+{
+       return new SwatchPackage;
+}
+
+
+
+
+
diff --git a/cinelerra-5.1/plugins/swatch/swatch.h b/cinelerra-5.1/plugins/swatch/swatch.h
new file mode 100644 (file)
index 0000000..d4551c9
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * CINELERRA
+ * Copyright (C) 2024 Adam Williams <broadcast at earthling dot net>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ */
+
+#ifndef SWATCH_H
+#define SWATCH_H
+
+class SwatchMain;
+class SwatchEngine;
+class SwatchWindow;
+class SwatchEngine;
+
+
+
+#include "guicast.h"
+#include "loadbalance.h"
+#include "pluginvclient.h"
+
+class SwatchConfig
+{
+public:
+       SwatchConfig();
+
+       int equivalent(SwatchConfig &that);
+       void copy_from(SwatchConfig &that);
+       void interpolate(SwatchConfig &prev, 
+               SwatchConfig &next, 
+               long prev_frame, 
+               long next_frame, 
+               long current_frame);
+
+    int brightness;
+    int saturation;
+    int fix_brightness;
+    int draw_src;
+    int angle;
+};
+
+
+class SwatchSlider : public BC_ISlider
+{
+public:
+       SwatchSlider(SwatchMain *plugin, 
+        SwatchWindow *gui, 
+        int x, 
+        int y, 
+        int min,
+        int max,
+        int *output);
+       int handle_event();
+       SwatchMain *plugin;
+    int *output;
+};
+
+class SwatchRadial : public BC_Radial
+{
+public:
+    SwatchRadial(SwatchMain *plugin, 
+        SwatchWindow *gui, 
+        int x, 
+        int y, 
+        const char *text,
+        int fix_brightness);
+       int handle_event();
+       SwatchMain *plugin;
+    SwatchWindow *gui;
+    int fix_brightness;
+};
+
+class SwatchCheck : public BC_CheckBox
+{
+public:
+    SwatchCheck(SwatchMain *plugin, 
+        int x, 
+        int y, 
+        const char *text,
+        int *output);
+       int handle_event();
+       SwatchMain *plugin;
+    SwatchWindow *gui;
+    int *output;
+};
+
+class SwatchWindow : public PluginClientWindow
+{
+public:
+       SwatchWindow(SwatchMain *plugin);
+       ~SwatchWindow();
+       
+       void create_objects();
+    void update_fixed();
+
+       SwatchMain *plugin;
+    SwatchSlider *brightness;
+    SwatchSlider *saturation;
+    SwatchSlider *angle;
+    SwatchRadial *fix_brightness;
+    SwatchRadial *fix_saturation;
+    SwatchCheck *draw_src;
+    BC_Title *brightness_title;
+    BC_Title *saturation_title;
+};
+
+
+
+
+
+
+
+class SwatchMain : public PluginVClient
+{
+public:
+       SwatchMain(PluginServer *server);
+       ~SwatchMain();
+
+       int process_buffer(VFrame *frame,
+               int64_t start_position,
+               double frame_rate);
+       int is_realtime();
+       void save_data(KeyFrame *keyframe);
+       void read_data(KeyFrame *keyframe);
+       void update_gui();
+       int is_synthesis();
+       int handle_opengl();
+
+       PLUGIN_CLASS_MEMBERS(SwatchConfig)
+
+       int need_reconfigure;
+
+       VFrame *temp;
+    VFrame *src_temp;
+       SwatchEngine *engine;
+};
+
+class SwatchPackage : public LoadPackage
+{
+public:
+       SwatchPackage();
+       int y1;
+       int y2;
+};
+
+class SwatchUnit : public LoadClient
+{
+public:
+       SwatchUnit(SwatchEngine *server, SwatchMain *plugin);
+       void process_package(LoadPackage *package);
+       SwatchEngine *server;
+       SwatchMain *plugin;
+       YUV yuv;
+};
+
+class SwatchEngine : public LoadServer
+{
+public:
+       SwatchEngine(SwatchMain *plugin, int total_clients, int total_packages);
+       void init_packages();
+
+    void draw_pattern();
+    void draw_src();
+
+       LoadClient* new_client();
+       LoadPackage* new_package();
+       SwatchMain *plugin;
+    int mode;
+};
+
+
+
+#endif
index 1d89f8f3e867a447e35425d451fc0983582752ae..40b579ed7013964dd8003da483f2879297a0b133 100644 (file)
@@ -30,12 +30,14 @@ https://sourceforge.net/projects/twolame/ = GitHub / Releases = Source Code=twol
 http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.7.tar.xz
 http://downloads.xiph.org/releases/ogg/libogg-1.3.5.tar.gz
 http://downloads.xiph.org/releases/theora/libtheora-1.1.1.tar.bz2
+# Added 0.7.4 in at least 2016; no new updates; not in HV or CV
+https://repology.org/project/a52dec/information
 https://sourceforge.net/projects/lame/files/latest/download?source=directory = 3.100
 https://download.osgeo.org/libtiff/tiff-4.6.0.tar.xz
 https://sourceforge.net/projects/libuuid/files/latest/download?source=directory - 1.0.3
 https://code.videolan.org/videolan/x264/-/tree/stable/x264-stable.tar.gz (Jan. 2023 version r3106)
 https://bitbucket.org/multicoreware/x265_git/downloads/x265_3.5.tar.gz (snapshot 17122023)
-https://ffmpeg.org/releases/ffmpeg-6.1.tar.bz2
+https://ffmpeg.org/releases/ffmpeg-7.0.tar.xz
 https://github.com/webmproject/libvpx/archive/v1.13.1.tar.gz
 https://code.videolan.org/videolan/dav1d/-/archive/0.5.1/dav1d-0.5.1.tar.gz
 https://github.com/swh/ladspa/releases/tag/v0.4.17, plugin.org.uk
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch1 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch1
new file mode 100644 (file)
index 0000000..c2cd10b
--- /dev/null
@@ -0,0 +1,11 @@
+--- a/fftools/cmdutils.c
++++ b/fftools/cmdutils.c
+@@ -60,7 +60,7 @@
+ AVDictionary *swr_opts;
+ AVDictionary *format_opts, *codec_opts;
+-int hide_banner = 0;
++int hide_banner = 1;
+ void uninit_opts(void)
+ {
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch10 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch10
new file mode 100644 (file)
index 0000000..a790fdd
--- /dev/null
@@ -0,0 +1,16 @@
+--- a/libavutil/hwcontext_cuda.c
++++ b/libavutil/hwcontext_cuda.c
+@@ -363,11 +363,13 @@
+                                                     hwctx->internal->cuda_device));
+         if (ret < 0)
+             return ret;
++#if 0
+     } else if (flags & AV_CUDA_USE_CURRENT_CONTEXT) {
+         ret = CHECK_CU(cu->cuCtxGetCurrent(&hwctx->cuda_ctx));
+         if (ret < 0)
+             return ret;
+         av_log(device_ctx, AV_LOG_INFO, "Using current CUDA context.\n");
++#endif
+     } else {
+         ret = CHECK_CU(cu->cuCtxCreate(&hwctx->cuda_ctx, desired_flags,
+                                        hwctx->internal->cuda_device));
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch2 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch2
new file mode 100644 (file)
index 0000000..fc9c74b
--- /dev/null
@@ -0,0 +1,414 @@
+--- a/libavformat/mpegtsenc.c
++++ b/libavformat/mpegtsenc.c
+@@ -89,9 +89,11 @@
+     int64_t pat_period; /* PAT/PMT period in PCR time base */
+     int64_t nit_period; /* NIT period in PCR time base */
+     int nb_services;
+-    int64_t first_pcr;
+     int first_dts_checked;
+-    int64_t next_pcr;
++    int64_t pcr_pos, pcr;
++    int64_t first_pcr, next_pcr;
++    int64_t delay;
++    int pcr_stream_pid;
+     int mux_rate; ///< set to 1 when VBR
+     int pes_payload_size;
+     int64_t total_size;
+@@ -258,7 +260,7 @@
+     int data_st_warning;
+     int64_t pcr_period; /* PCR period in PCR time base */
+-    int64_t last_pcr;
++    int64_t pcr_timer;
+     /* For Opus */
+     int opus_queued_samples;
+@@ -959,18 +961,18 @@
+     return 0;
+ }
+-static int64_t get_pcr(const MpegTSWrite *ts)
++static int64_t get_pcr(const MpegTSWrite *ts, AVIOContext *pb)
+ {
+-    return av_rescale(ts->total_size + 11, 8 * PCR_TIME_BASE, ts->mux_rate) +
+-           ts->first_pcr;
++    int64_t pos = avio_tell(pb) + 11;
++    return ts->pcr + (ts->mux_rate == 1 ? (pos - ts->pcr_pos) * 8 :
++        av_rescale(pos - ts->pcr_pos, 8 * PCR_TIME_BASE, ts->mux_rate));
+ }
+ static void write_packet(AVFormatContext *s, const uint8_t *packet)
+ {
+     MpegTSWrite *ts = s->priv_data;
+     if (ts->m2ts_mode) {
+-        int64_t pcr = get_pcr(s->priv_data);
+-        uint32_t tp_extra_header = pcr % 0x3fffffff;
++        uint32_t tp_extra_header = get_pcr(ts, s->pb) % 0x3fffffff;
+         tp_extra_header = AV_RB32(&tp_extra_header);
+         avio_write(s->pb, (unsigned char *) &tp_extra_header,
+                    sizeof(tp_extra_header));
+@@ -1056,9 +1058,6 @@
+         else
+             ts_st->pcr_period = 1;
+     }
+-
+-    // output a PCR as soon as possible
+-    ts_st->last_pcr   = ts->first_pcr - ts_st->pcr_period;
+ }
+ static void select_pcr_streams(AVFormatContext *s)
+@@ -1121,6 +1120,7 @@
+     if (s->max_delay < 0) /* Not set by the caller */
+         s->max_delay = 0;
++    ts->delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE);
+     // round up to a whole number of TS packets
+     ts->pes_payload_size = (ts->pes_payload_size + 14 + 183) / 184 * 184 - 14;
+@@ -1180,7 +1180,9 @@
+         /* MPEG pid values < 16 are reserved. Applications which set st->id in
+          * this range are assigned a calculated pid. */
+         if (st->id < 16) {
+-            if (ts->m2ts_mode) {
++            if (ts->start_pid >= 0)
++                ts_st->pid = ts->start_pid + i;
++            else if (ts->m2ts_mode) {
+                 switch (st->codecpar->codec_type) {
+                 case AVMEDIA_TYPE_VIDEO:
+                     ts_st->pid = ts->m2ts_video_pid++;
+@@ -1207,9 +1209,9 @@
+                     av_log(s, AV_LOG_ERROR, "Cannot automatically assign PID for stream %d\n", st->index);
+                     return AVERROR(EINVAL);
+                 }
+-            } else {
+-                ts_st->pid = ts->start_pid + i;
+             }
++            else
++                ts_st->pid = START_PID + i;
+         } else {
+             ts_st->pid = st->id;
+         }
+@@ -1277,9 +1279,14 @@
+     ts->last_pat_ts = AV_NOPTS_VALUE;
+     ts->last_sdt_ts = AV_NOPTS_VALUE;
+     ts->last_nit_ts = AV_NOPTS_VALUE;
+-    ts->pat_period = av_rescale(ts->pat_period_us, PCR_TIME_BASE, AV_TIME_BASE);
+-    ts->sdt_period = av_rescale(ts->sdt_period_us, PCR_TIME_BASE, AV_TIME_BASE);
+-    ts->nit_period = av_rescale(ts->nit_period_us, PCR_TIME_BASE, AV_TIME_BASE);
++    ts->pat_period = ts->pat_period_us < 0 ? -1 :
++        av_rescale(ts->pat_period_us, PCR_TIME_BASE, AV_TIME_BASE);
++    ts->sdt_period = ts->sdt_period_us < 0 ? -1 :
++        av_rescale(ts->sdt_period_us, PCR_TIME_BASE, AV_TIME_BASE);
++    ts->nit_period = ts->nit_period_us < 0 ? -1 :
++        av_rescale(ts->nit_period_us, PCR_TIME_BASE, AV_TIME_BASE);
++    ts->pcr = 0;
++    ts->pcr_pos = 0;
+     /* assign provider name */
+     provider = av_dict_get(s->metadata, "service_provider", NULL, 0);
+@@ -1295,8 +1302,8 @@
+         av_log(s, AV_LOG_VERBOSE, "muxrate %d, ", ts->mux_rate);
+     av_log(s, AV_LOG_VERBOSE,
+            "sdt every %"PRId64" ms, pat/pmt every %"PRId64" ms",
+-           av_rescale(ts->sdt_period, 1000, PCR_TIME_BASE),
+-           av_rescale(ts->pat_period, 1000, PCR_TIME_BASE));
++           ts->sdt_period < 0 ? -1 : av_rescale(ts->sdt_period, 1000, PCR_TIME_BASE),
++           ts->pat_period < 0 ? -1 : av_rescale(ts->pat_period, 1000, PCR_TIME_BASE));
+     if (ts->flags & MPEGTS_FLAG_NIT)
+         av_log(s, AV_LOG_VERBOSE, ", nit every %"PRId64" ms", av_rescale(ts->nit_period, 1000, PCR_TIME_BASE));
+     av_log(s, AV_LOG_VERBOSE, "\n");
+@@ -1305,36 +1312,40 @@
+ }
+ /* send SDT, NIT, PAT and PMT tables regularly */
+-static void retransmit_si_info(AVFormatContext *s, int force_pat, int force_sdt, int force_nit, int64_t pcr)
++static void retransmit_si_info(AVFormatContext *s, int force_pat, int force_sdt, int force_nit)
+ {
+     MpegTSWrite *ts = s->priv_data;
+     int i;
+-    if ((pcr != AV_NOPTS_VALUE && ts->last_sdt_ts == AV_NOPTS_VALUE) ||
+-        (pcr != AV_NOPTS_VALUE && pcr - ts->last_sdt_ts >= ts->sdt_period) ||
+-        force_sdt
+-    ) {
+-        if (pcr != AV_NOPTS_VALUE)
+-            ts->last_sdt_ts = FFMAX(pcr, ts->last_sdt_ts);
+-        mpegts_write_sdt(s);
+-    }
+-    if ((pcr != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) ||
+-        (pcr != AV_NOPTS_VALUE && pcr - ts->last_pat_ts >= ts->pat_period) ||
+-        force_pat) {
+-        if (pcr != AV_NOPTS_VALUE)
+-            ts->last_pat_ts = FFMAX(pcr, ts->last_pat_ts);
+-        mpegts_write_pat(s);
+-        for (i = 0; i < ts->nb_services; i++)
+-            mpegts_write_pmt(s, ts->services[i]);
+-    }
+-    if ((pcr != AV_NOPTS_VALUE && ts->last_nit_ts == AV_NOPTS_VALUE) ||
+-        (pcr != AV_NOPTS_VALUE && pcr - ts->last_nit_ts >= ts->nit_period) ||
+-        force_nit
+-    ) {
+-        if (pcr != AV_NOPTS_VALUE)
+-            ts->last_nit_ts = FFMAX(pcr, ts->last_nit_ts);
++    if (ts->sdt_period >= 0) {
++        int64_t pcr = get_pcr(ts, s->pb);
++        if (ts->last_sdt_ts == AV_NOPTS_VALUE || pcr >= ts->last_sdt_ts + ts->sdt_period)
++            force_sdt = 1;
++        if (force_sdt) {
++            ts->last_sdt_ts = pcr;
++            mpegts_write_sdt(s);
++        }
++    }
++    if (ts->pat_period >= 0) {
++        int64_t pcr = get_pcr(ts, s->pb);
++        if (ts->last_pat_ts == AV_NOPTS_VALUE || pcr >= ts->last_pat_ts + ts->pat_period)
++            force_pat = 1;
++        if (force_pat) {
++            ts->last_pat_ts = pcr;
++            mpegts_write_pat(s);
++            for (i = 0; i < ts->nb_services; i++)
++                mpegts_write_pmt(s, ts->services[i]);
++        }
++    }
++    if (ts->nit_period >= 0) {
++        int64_t pcr = get_pcr(ts, s->pb);
++        if (ts->last_nit_ts == AV_NOPTS_VALUE || pcr >= ts->last_nit_ts + ts->nit_period)
++            force_nit = 1;
++        if (force_nit) {
++            ts->last_nit_ts = pcr;
+         if (ts->flags & MPEGTS_FLAG_NIT)
+             mpegts_write_nit(s);
++        }
+     }
+ }
+@@ -1371,25 +1382,29 @@
+ static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st)
+ {
+     MpegTSWrite *ts = s->priv_data;
+-    MpegTSWriteStream *ts_st = st->priv_data;
++    int64_t pcr = get_pcr(ts, s->pb);
++    MpegTSWriteStream *ts_st = st ? st->priv_data : 0;
++    uint32_t pcr_pid = ts_st ? ts_st->pid : ts->pcr_stream_pid;
+     uint8_t *q;
+     uint8_t buf[TS_PACKET_SIZE];
+     q    = buf;
+     *q++ = 0x47;
+-    *q++ = ts_st->pid >> 8;
+-    *q++ = ts_st->pid;
+-    *q++ = 0x20 | ts_st->cc;   /* Adaptation only */
++    *q++ = pcr_pid >> 8;
++    *q++ = pcr_pid;
++    uint32_t flags =  0x20;    /* Adaptation only */
+     /* Continuity Count field does not increment (see 13818-1 section 2.4.3.3) */
++    if(ts_st) flags |= ts_st->cc;
++    *q++ = flags;
+     *q++ = TS_PACKET_SIZE - 5; /* Adaptation Field Length */
+     *q++ = 0x10;               /* Adaptation flags: PCR present */
+-    if (ts_st->discontinuity) {
++    if (ts_st && ts_st->discontinuity) {
+         q[-1] |= 0x80;
+         ts_st->discontinuity = 0;
+     }
+     /* PCR coded into 6 bytes */
+-    q += write_pcr_bits(q, get_pcr(ts));
++    q += write_pcr_bits(q, pcr);
+     /* stuffing bytes */
+     memset(q, 0xFF, TS_PACKET_SIZE - (q - buf));
+@@ -1490,9 +1505,9 @@
+     int afc_len, stuffing_len;
+     int is_dvb_subtitle = (st->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE);
+     int is_dvb_teletext = (st->codecpar->codec_id == AV_CODEC_ID_DVB_TELETEXT);
+-    int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE);
+     int force_pat = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key;
+     int force_sdt = 0;
++    int64_t pcr;
+     int force_nit = 0;
+     av_assert0(ts_st->payload != buf || st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO);
+@@ -1509,21 +1524,19 @@
+     is_start = 1;
+     while (payload_size > 0) {
+-        int64_t pcr = AV_NOPTS_VALUE;
+-        if (ts->mux_rate > 1)
+-            pcr = get_pcr(ts);
+-        else if (dts != AV_NOPTS_VALUE)
+-            pcr = (dts - delay) * 300;
+-
+-        retransmit_si_info(s, force_pat, force_sdt, force_nit, pcr);
+-        force_pat = 0;
+-        force_sdt = 0;
+-        force_nit = 0;
++        // add 11, pcr references the last byte of program clock reference base
++        ts->pcr_pos = avio_tell(s->pb) + 11;
++        pcr = ts->pcr = ts->mux_rate != 1 ?
++            av_rescale(ts->pcr_pos, 8 * PCR_TIME_BASE, ts->mux_rate) :
++            (dts == AV_NOPTS_VALUE ? 0 : (dts - ts->delay) * 300);
++        if (force_pat || force_sdt || force_nit) {
++            retransmit_si_info(s, force_pat, force_sdt, force_nit);
++            force_pat = force_sdt = force_nit = 0;
++        }
+         write_pcr = 0;
+         if (ts->mux_rate > 1) {
+             /* Send PCR packets for all PCR streams if needed */
+-            pcr = get_pcr(ts);
+             if (pcr >= ts->next_pcr) {
+                 int64_t next_pcr = INT64_MAX;
+                 for (int i = 0; i < s->nb_streams; i++) {
+@@ -1533,36 +1546,43 @@
+                     AVStream *st2 = s->streams[st2_index];
+                     MpegTSWriteStream *ts_st2 = st2->priv_data;
+                     if (ts_st2->pcr_period) {
+-                        if (pcr - ts_st2->last_pcr >= ts_st2->pcr_period) {
+-                            ts_st2->last_pcr = FFMAX(pcr - ts_st2->pcr_period, ts_st2->last_pcr + ts_st2->pcr_period);
+-                            if (st2 != st) {
++                        if (pcr >= ts_st2->pcr_timer) {
++                            ts_st2->pcr_timer = pcr + ts_st2->pcr_period;
++                            if (st2 != st) { 
+                                 mpegts_insert_pcr_only(s, st2);
+-                                pcr = get_pcr(ts);
+                             } else {
+                                 write_pcr = 1;
+                             }
+                         }
+-                        next_pcr = FFMIN(next_pcr, ts_st2->last_pcr + ts_st2->pcr_period);
++                        next_pcr = FFMIN(next_pcr, ts_st2->pcr_timer);
+                     }
+                 }
+                 ts->next_pcr = next_pcr;
+             }
+-            if (dts != AV_NOPTS_VALUE && (dts - pcr / 300) > delay) {
+-                /* pcr insert gets priority over null packet insert */
+-                if (write_pcr)
+-                    mpegts_insert_pcr_only(s, st);
+-                else
+-                    mpegts_insert_null_packet(s);
+-                /* recalculate write_pcr and possibly retransmit si_info */
+-                continue;
+-            }
+-        } else if (ts_st->pcr_period && pcr != AV_NOPTS_VALUE) {
+-            if (pcr - ts_st->last_pcr >= ts_st->pcr_period && is_start) {
+-                ts_st->last_pcr = FFMAX(pcr - ts_st->pcr_period, ts_st->last_pcr + ts_st->pcr_period);
++        }
++        else if (ts_st->pcr_period) {
++            if (pcr >= ts_st->pcr_timer) {
++                ts_st->pcr_timer = pcr + ts_st->pcr_period;
+                 write_pcr = 1;
+             }
+         }
++        if (write_pcr && ts->pcr_stream_pid >= 0) {
++           mpegts_insert_pcr_only(s, 0);
++           continue;
++        }
++
++        if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE &&
++               (dts - pcr / 300) > ts->delay) {
++           /* pcr insert gets priority over null packet insert */
++           if (write_pcr)
++               mpegts_insert_pcr_only(s, st);
++            else
++               mpegts_insert_null_packet(s);
++            /* recalculate write_pcr and possibly retransimit si_info */
++            continue;
++        }
++
+         /* prepare packet header */
+         q    = buf;
+         *q++ = 0x47;
+@@ -1592,7 +1612,6 @@
+         if (write_pcr) {
+             set_af_flag(buf, 0x10);
+             q = get_ts_payload_start(buf);
+-            // add 11, pcr references the last byte of program clock reference base
+             if (dts != AV_NOPTS_VALUE && dts < pcr / 300)
+                 av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n");
+             extend_af(buf, write_pcr_bits(q, pcr));
+@@ -1864,8 +1883,8 @@
+     uint8_t *data = NULL;
+     MpegTSWrite *ts = s->priv_data;
+     MpegTSWriteStream *ts_st = st->priv_data;
+-    const int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE) * 2;
+-    const int64_t max_audio_delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE) / 2;
++    const int64_t delay_ticks2 = ts->delay * 2;
++    const int64_t max_audio_delay = ts->delay / 2;
+     int64_t dts = pkt->dts, pts = pkt->pts;
+     int opus_samples = 0;
+     size_t side_data_size;
+@@ -1885,9 +1904,9 @@
+     if (ts->copyts < 1) {
+         if (pts != AV_NOPTS_VALUE)
+-            pts += delay;
++            pts += delay_ticks2;
+         if (dts != AV_NOPTS_VALUE)
+-            dts += delay;
++            dts += delay_ticks2;
+     }
+     if (!ts_st->first_timestamp_checked && (pts == AV_NOPTS_VALUE || dts == AV_NOPTS_VALUE)) {
+@@ -2354,8 +2373,10 @@
+       0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_HEVC_DIGITAL_HDTV }, 0x01, 0xff, ENC, .unit = "mpegts_service_type" },
+     { "mpegts_pmt_start_pid", "Set the first pid of the PMT.",
+       OFFSET(pmt_start_pid), AV_OPT_TYPE_INT, { .i64 = 0x1000 }, FIRST_OTHER_PID, LAST_OTHER_PID, ENC },
++    { "mpegts_pcr_stream_pid", "create seperate PCR stream on this pid.",
++      OFFSET(pcr_stream_pid), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 0x1f00, ENC },
+     { "mpegts_start_pid", "Set the first pid.",
+-      OFFSET(start_pid), AV_OPT_TYPE_INT, { .i64 = 0x0100 }, FIRST_OTHER_PID, LAST_OTHER_PID, ENC },
++      OFFSET(start_pid), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, LAST_OTHER_PID, ENC },
+     { "mpegts_m2ts_mode", "Enable m2ts mode.", OFFSET(m2ts_mode), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, ENC },
+     { "muxrate", NULL, OFFSET(mux_rate), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, ENC },
+     { "pes_payload_size", "Minimum PES packet payload in bytes",
+@@ -2381,10 +2402,10 @@
+       OFFSET(omit_video_pes_length), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, ENC },
+     { "pcr_period", "PCR retransmission time in milliseconds",
+       OFFSET(pcr_period_ms), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, ENC },
+-    { "pat_period", "PAT/PMT retransmission time limit in seconds",
++    { "pat_period", "PAT/PMT retransmission time limit in ms, -1 no pat",
+       OFFSET(pat_period_us), AV_OPT_TYPE_DURATION, { .i64 = PAT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC },
+-    { "sdt_period", "SDT retransmission time limit in seconds",
+-      OFFSET(sdt_period_us), AV_OPT_TYPE_DURATION, { .i64 = SDT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC },
++    { "sdt_period", "SDT retransmission time limit in ms, -1 no sdt",
++      OFFSET(sdt_period_us), AV_OPT_TYPE_INT64, { .i64 = SDT_RETRANS_TIME * 1000LL }, -1, INT64_MAX, ENC },
+     { "nit_period", "NIT retransmission time limit in seconds",
+       OFFSET(nit_period_us), AV_OPT_TYPE_DURATION, { .i64 = NIT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC },
+     { NULL },
+--- a/libavformat/mpegts.h
++++ b/libavformat/mpegts.h
+@@ -64,6 +64,7 @@
+ /* PID from 0x1FFC to 0x1FFE may be assigned as needed to PMT, elementary
+  * streams and other data tables */
+ #define NULL_PID        0x1FFF /* Null packet (used for fixed bandwidth padding) */
++#define START_PID     0x0400
+ /* m2ts pids */
+ #define M2TS_PMT_PID                      0x0100
+--- a/libavformat/bluray.c
++++ b/libavformat/bluray.c
+@@ -27,7 +27,7 @@
+ #include "libavutil/opt.h"
+ #define BLURAY_PROTO_PREFIX     "bluray:"
+-#define MIN_PLAYLIST_LENGTH     180     /* 3 min */
++#define MIN_PLAYLIST_LENGTH     0
+ typedef struct {
+     const AVClass *class;
+
+--- a/doc/muxers.texi
++++ b/doc/muxers.texi
+@@ -2920,7 +2920,8 @@
+ Maximum time in seconds between PAT/PMT tables. Default is @code{0.1}.
+ @item sdt_period @var{duration}
+-Maximum time in seconds between SDT tables. Default is @code{0.5}.
++Maximum time in seconds between SDT tables. Default is @code{0.5}. Regardless
++of this setting no SDT is written in m2ts mode.
+ @item nit_period @var{duration}
+ Maximum time in seconds between NIT tables. Default is @code{0.5}.
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch3 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch3
new file mode 100644 (file)
index 0000000..25b062b
--- /dev/null
@@ -0,0 +1,68 @@
+--- a/libavformat/avformat.h
++++ b/libavformat/avformat.h
+@@ -499,6 +499,9 @@
+                                         The user or muxer can override this through
+                                         AVFormatContext.avoid_negative_ts
+                                         */
++#define AVFMT_SEEK_NOSTREAMS  0x80000 /**< Stream index ignored by seek,
++                                           or some streams fail to seek
++                                           */
+ #define AVFMT_SEEK_TO_PTS   0x4000000 /**< Seeking is based on PTS */
+@@ -562,7 +565,8 @@
+     /**
+      * Can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS,
+      * AVFMT_NOTIMESTAMPS, AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH,
+-     * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS.
++     * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS,
++     * AVFMT_SEEK_NOSTREAMS
+      */
+     int flags;
+--- a/libavformat/dv.c
++++ b/libavformat/dv.c
+@@ -713,6 +713,7 @@
+ const FFInputFormat ff_dv_demuxer = {
+     .p.name         = "dv",
+     .p.long_name    = NULL_IF_CONFIG_SMALL("DV (Digital Video)"),
++    .p.flags       = AVFMT_SEEK_NOSTREAMS,
+     .p.extensions   = "dv,dif",
+     .priv_data_size = sizeof(RawDVContext),
+     .read_probe     = dv_probe,
+
+--- a/libavformat/matroskadec.c
++++ b/libavformat/matroskadec.c
+@@ -4794,6 +4794,7 @@
+ const FFInputFormat ff_webm_dash_manifest_demuxer = {
+     .p.name         = "webm_dash_manifest",
+     .p.long_name    = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"),
++    .p.flags        = AVFMT_SEEK_NOSTREAMS,
+     .p.priv_class   = &webm_dash_class,
+     .priv_data_size = sizeof(MatroskaDemuxContext),
+     .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP,
+@@ -4806,6 +4807,7 @@
+ const FFInputFormat ff_matroska_demuxer = {
+     .p.name         = "matroska,webm",
+     .p.long_name    = NULL_IF_CONFIG_SMALL("Matroska / WebM"),
++    .p.flags        = AVFMT_SEEK_NOSTREAMS,
+     .p.extensions   = "mkv,mk3d,mka,mks,webm",
+     .p.mime_type    = "audio/webm,audio/x-matroska,video/webm,video/x-matroska",
+     .priv_data_size = sizeof(MatroskaDemuxContext),
+
+--- a/libavformat/seek.c
++++ b/libavformat/seek.c
+@@ -605,6 +605,13 @@
+         return seek_frame_byte(s, stream_index, timestamp, flags);
+     }
++    if (stream_index != -1 && (s->iformat->flags & AVFMT_SEEK_NOSTREAMS)) {
++            timestamp = av_rescale_q(timestamp,
++                    s->streams[stream_index]->time_base,
++                    AV_TIME_BASE_Q);
++            stream_index = -1;
++    }
++
+     if (stream_index < 0) {
+         stream_index = av_find_default_stream_index(s);
+         if (stream_index < 0)
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch4 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch4
new file mode 100644 (file)
index 0000000..9e472ce
--- /dev/null
@@ -0,0 +1,10 @@
+--- a/libavformat/avidec.c
++++ b/libavformat/avidec.c
+@@ -2020,6 +2020,7 @@
+     .p.name         = "avi",
+     .p.long_name    = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"),
+     .p.extensions   = "avi",
++    .p.flags        = AVFMT_SEEK_NOSTREAMS,
+     .p.priv_class   = &demuxer_class,
+     .priv_data_size = sizeof(AVIContext),
+     .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP,
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch5 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch5
new file mode 100644 (file)
index 0000000..64ee262
--- /dev/null
@@ -0,0 +1,25 @@
+--- a/libavfilter/formats.c
++++ b/libavfilter/formats.c
+@@ -110,11 +110,13 @@
+        possibly causing a lossy conversion elsewhere in the graph.
+        To avoid that, pretend that there are no common formats to force the
+        insertion of a conversion filter. */
+-    if (type == AVMEDIA_TYPE_VIDEO)
++    if (type == AVMEDIA_TYPE_VIDEO) {
+         for (i = 0; i < a->nb_formats; i++) {
+             const AVPixFmtDescriptor *const adesc = av_pix_fmt_desc_get(a->formats[i]);
++            if( !adesc ) continue;
+             for (j = 0; j < b->nb_formats; j++) {
+                 const AVPixFmtDescriptor *bdesc = av_pix_fmt_desc_get(b->formats[j]);
++                if( !bdesc ) continue;
+                 alpha2 |= adesc->flags & bdesc->flags & AV_PIX_FMT_FLAG_ALPHA;
+                 chroma2|= adesc->nb_components > 1 && bdesc->nb_components > 1;
+                 if (a->formats[i] == b->formats[j]) {
+@@ -123,6 +125,7 @@
+                 }
+             }
+         }
++    }
+     // If chroma or alpha can be lost through merging then do not merge
+     if (alpha2 > alpha1 || chroma2 > chroma1)
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch7 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch7
new file mode 100644 (file)
index 0000000..379ddb6
--- /dev/null
@@ -0,0 +1,10 @@
+--- a/libavcodec/vdpau_mpeg12.c
++++ b/libavcodec/vdpau_mpeg12.c
+@@ -117,6 +117,7 @@
+     .frame_priv_data_size = sizeof(struct vdpau_picture_context),
+     .init           = vdpau_mpeg1_init,
+     .uninit         = ff_vdpau_common_uninit,
++    .frame_params   = ff_vdpau_common_frame_params,
+     .priv_data_size = sizeof(VDPAUContext),
+     .caps_internal  = HWACCEL_CAP_ASYNC_SAFE,
+ };
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch8 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch8
new file mode 100644 (file)
index 0000000..fcafebb
--- /dev/null
@@ -0,0 +1,11 @@
+--- a/libavcodec/h263dec.c
++++ b/libavcodec/h263dec.c
+@@ -623,7 +623,7 @@
+     if (CONFIG_MPEG4_DECODER && avctx->codec_id == AV_CODEC_ID_MPEG4)
+         ff_mpeg4_frame_end(avctx, buf, buf_size);
+-    if (!s->divx_packed && avctx->hwaccel)
++    if (s->divx_packed && avctx->hwaccel)
+         ff_thread_finish_setup(avctx);
+     av_assert1(s->current_picture.f->pict_type == s->current_picture_ptr->f->pict_type);
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch9 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch9
new file mode 100644 (file)
index 0000000..721fd09
--- /dev/null
@@ -0,0 +1,14 @@
+--- a/libavformat/mpegenc.c
++++ b/libavformat/mpegenc.c
+@@ -987,9 +987,9 @@
+         PacketDesc *pkt_desc;
+         while ((pkt_desc = stream->predecode_packet) &&
++               pkt_desc != stream->premux_packet &&
+                scr > pkt_desc->dts) { // FIXME: > vs >=
+-            if (stream->buffer_index < pkt_desc->size ||
+-                stream->predecode_packet == stream->premux_packet) {
++            if (stream->buffer_index < pkt_desc->size) {
+                 av_log(ctx, AV_LOG_ERROR,
+                        "buffer underflow st=%d bufi=%d size=%d\n",
+                        i, stream->buffer_index, pkt_desc->size);
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchB b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchB
new file mode 100644 (file)
index 0000000..89e9989
--- /dev/null
@@ -0,0 +1,22 @@
+--- a/libavutil/hwcontext_vdpau.c
++++ b/libavutil/hwcontext_vdpau.c
+@@ -47,6 +47,11 @@
+     { 0,                     AV_PIX_FMT_NONE,   },
+ };
++static const VDPAUPixFmtMap pix_fmts_420j[] = {
++    { VDP_YCBCR_FORMAT_YV12, AV_PIX_FMT_YUVJ420P },
++    { 0,                     AV_PIX_FMT_NONE,   },
++};
++
+ static const VDPAUPixFmtMap pix_fmts_422[] = {
+     { VDP_YCBCR_FORMAT_NV12, AV_PIX_FMT_NV16    },
+     { VDP_YCBCR_FORMAT_YV12, AV_PIX_FMT_YUV422P },
+@@ -71,6 +76,7 @@
+     const VDPAUPixFmtMap *map;
+ } vdpau_pix_fmts[] = {
+     { VDP_CHROMA_TYPE_420, AV_PIX_FMT_YUV420P, pix_fmts_420 },
++    { VDP_CHROMA_TYPE_420, AV_PIX_FMT_YUVJ420P, pix_fmts_420j },
+     { VDP_CHROMA_TYPE_422, AV_PIX_FMT_YUV422P, pix_fmts_422 },
+     { VDP_CHROMA_TYPE_444, AV_PIX_FMT_YUV444P, pix_fmts_444 },
+ #ifdef VDP_YCBCR_FORMAT_P016
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchC b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchC
new file mode 100644 (file)
index 0000000..b89b3af
--- /dev/null
@@ -0,0 +1,41 @@
+--- a/libavcodec/encode.c
++++ b/libavcodec/encode.c
+@@ -320,7 +320,7 @@
+     }
+     if (!frame->buf[0]) {
+-        if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY ||
++        if (avci->draining && !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY ||
+               avci->frame_thread_encoder))
+             return AVERROR_EOF;
+@@ -339,8 +339,10 @@
+         ret = ff_encode_encode_cb(avctx, avpkt, frame, &got_packet);
+     }
+-    if (avci->draining && !got_packet)
++    if (avci->draining && !got_packet) {
++       fflush(stderr);
+         avci->draining_done = 1;
++    }
+     return ret;
+ }
+@@ -515,10 +517,16 @@
+     if (avci->draining)
+         return AVERROR_EOF;
+-    if (avci->buffer_frame->buf[0])
++    if (avci->buffer_frame->buf[0]) {
++        if (!frame) {
++           fflush(stderr);
++            av_frame_unref(avci->buffer_frame);
++       }
+         return AVERROR(EAGAIN);
++    }
+     if (!frame) {
++       fflush(stderr);
+         avci->draining = 1;
+     } else {
+         ret = encode_send_frame_internal(avctx, frame);
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchD b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchD
new file mode 100644 (file)
index 0000000..9cbef88
--- /dev/null
@@ -0,0 +1,24 @@
+--- a/libavcodec/pcm-dvdenc.c
++++ b/libavcodec/pcm-dvdenc.c
+@@ -38,6 +38,12 @@
+     int quant, freq, frame_size;
+     switch (avctx->sample_rate) {
++    case 32000:
++       freq = 3;
++       break;
++    case 44100:
++       freq = 2;
++       break;
+     case 48000:
+         freq = 0;
+         break;
+@@ -181,7 +187,7 @@
+     .priv_data_size = sizeof(PCMDVDContext),
+     .init           = pcm_dvd_encode_init,
+     FF_CODEC_ENCODE_CB(pcm_dvd_encode_frame),
+-    .p.supported_samplerates = (const int[]) { 48000, 96000, 0},
++    .p.supported_samplerates = (const int[]) { 32000, 44100, 48000, 96000, 0},
+     .p.ch_layouts   = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO,
+                                                   AV_CHANNEL_LAYOUT_STEREO,
+                                                   AV_CHANNEL_LAYOUT_5POINT1,
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchZ1 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchZ1
new file mode 100644 (file)
index 0000000..29cfb62
--- /dev/null
@@ -0,0 +1,49 @@
+--- a/libavcodec/wrapped_avframe.c
++++ b/libavcodec/wrapped_avframe.c
+@@ -33,6 +33,38 @@
+ #include "libavutil/buffer.h"
+ #include "libavutil/pixdesc.h"
++
++
++static const enum AVPixelFormat pix_fmts_all[] = {
++    AV_PIX_FMT_YUV411P,
++    AV_PIX_FMT_YUV420P,
++    AV_PIX_FMT_YUVJ420P,
++    AV_PIX_FMT_YUV422P,
++    AV_PIX_FMT_YUVJ422P,
++    AV_PIX_FMT_YUV444P,
++    AV_PIX_FMT_YUVJ444P,
++    AV_PIX_FMT_YUV420P10,
++    AV_PIX_FMT_YUV422P10,
++    AV_PIX_FMT_YUV444P10,
++    AV_PIX_FMT_YUV420P12,
++    AV_PIX_FMT_YUV422P12,
++    AV_PIX_FMT_YUV444P12,
++    AV_PIX_FMT_YUV420P14,
++    AV_PIX_FMT_YUV422P14,
++    AV_PIX_FMT_YUV444P14,
++    AV_PIX_FMT_YUV420P16,
++    AV_PIX_FMT_YUV422P16,
++    AV_PIX_FMT_YUV444P16,
++    AV_PIX_FMT_GRAY8,
++    AV_PIX_FMT_GRAY9,
++    AV_PIX_FMT_GRAY10,
++    AV_PIX_FMT_GRAY12,
++    AV_PIX_FMT_GRAY16,
++    AV_PIX_FMT_NONE
++};
++
++
++
+ static void wrapped_avframe_release_buffer(void *unused, uint8_t *data)
+ {
+     AVFrame *frame = (AVFrame *)data;
+@@ -111,6 +143,7 @@
+     .p.id           = AV_CODEC_ID_WRAPPED_AVFRAME,
+     .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
+     FF_CODEC_ENCODE_CB(wrapped_avframe_encode),
++    .p.pix_fmts       = pix_fmts_all,
+ };
+ const FFCodec ff_wrapped_avframe_decoder = {
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchZ2 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchZ2
new file mode 100644 (file)
index 0000000..a883b14
--- /dev/null
@@ -0,0 +1,11 @@
+--- a/libavformat/yuv4mpegenc.c
++++ b/libavformat/yuv4mpegenc.c
+@@ -268,7 +268,7 @@
+             av_log(s, AV_LOG_ERROR, "'%s' is not an official yuv4mpegpipe pixel format. "
+                    "Use '-strict -1' to encode to this pixel format.\n",
+                    av_get_pix_fmt_name(s->streams[0]->codecpar->format));
+-            return AVERROR(EINVAL);
++            //return AVERROR(EINVAL);
+         }
+         av_log(s, AV_LOG_WARNING, "Warning: generating non standard YUV stream. "
+                "Mjpegtools will not work.\n");
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.tar.xz b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.tar.xz
new file mode 100644 (file)
index 0000000..5eff274
Binary files /dev/null and b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.tar.xz differ