From 1fb61c33b5d6ef5fdf9c9bd6c9ebc5f37a66c470 Mon Sep 17 00:00:00 2001 From: Good Guy Date: Mon, 17 Feb 2025 13:30:21 -0700 Subject: [PATCH] Credit SGE - new BlendAlgebra and BlendProgram plugins; major enhancement --- cinelerra-5.1/plugin_defs | 2 + cinelerra-5.1/plugins/Makefile | 5 + .../blendalgebra/BlendAlgebraCompile.pl | 289 +++ .../plugins/blendalgebra/BlendAlgebraStart | 174 ++ cinelerra-5.1/plugins/blendalgebra/Makefile | 75 + .../plugins/blendalgebra/arith_addition.ba | 25 + .../plugins/blendalgebra/arith_divide.ba | 25 + .../plugins/blendalgebra/arith_multiply.ba | 20 + .../plugins/blendalgebra/arith_replace.ba | 17 + .../plugins/blendalgebra/arith_subtract.ba | 22 + .../plugins/blendalgebra/blendalgebra.C | 2297 +++++++++++++++++ .../plugins/blendalgebra/blendalgebra.h | 463 ++++ .../plugins/blendalgebra/graphart_burn.ba | 29 + .../blendalgebra/graphart_difference.ba | 20 + .../plugins/blendalgebra/graphart_dodge.ba | 35 + .../blendalgebra/graphart_hardlight.ba | 29 + .../plugins/blendalgebra/graphart_overlay.ba | 29 + .../plugins/blendalgebra/graphart_screen.ba | 20 + .../blendalgebra/graphart_softlight.ba | 26 + .../plugins/blendalgebra/logical_and.ba | 23 + .../plugins/blendalgebra/logical_darken.ba | 20 + .../plugins/blendalgebra/logical_lighten.ba | 20 + .../plugins/blendalgebra/logical_max.ba | 20 + .../plugins/blendalgebra/logical_min.ba | 20 + .../plugins/blendalgebra/logical_or.ba | 20 + .../plugins/blendalgebra/logical_xor.ba | 22 + .../plugins/blendalgebra/ovl_normal.ba | 20 + .../plugins/blendalgebra/porterduff_dst.ba | 16 + .../blendalgebra/porterduff_dstatop.ba | 20 + .../plugins/blendalgebra/porterduff_dstin.ba | 20 + .../plugins/blendalgebra/porterduff_dstout.ba | 22 + .../blendalgebra/porterduff_dstover.ba | 20 + .../plugins/blendalgebra/porterduff_src.ba | 17 + .../blendalgebra/porterduff_srcatop.ba | 20 + .../plugins/blendalgebra/porterduff_srcin.ba | 20 + .../plugins/blendalgebra/porterduff_srcout.ba | 22 + .../blendalgebra/porterduff_srcover.ba | 20 + cinelerra-5.1/plugins/blendalgebra/ydiff.ba | 42 + .../blendprogram/BlendProgramCompile.pl | 289 +++ .../plugins/blendprogram/BlendProgramStart | 145 ++ cinelerra-5.1/plugins/blendprogram/Makefile | 46 + .../plugins/blendprogram/background.bp | 22 + .../plugins/blendprogram/blendprogram.C | 2171 ++++++++++++++++ .../plugins/blendprogram/blendprogram.h | 430 +++ .../plugins/blendprogram/chromakey.bp | 28 + 45 files changed, 7137 insertions(+) create mode 100755 cinelerra-5.1/plugins/blendalgebra/BlendAlgebraCompile.pl create mode 100644 cinelerra-5.1/plugins/blendalgebra/BlendAlgebraStart create mode 100644 cinelerra-5.1/plugins/blendalgebra/Makefile create mode 100644 cinelerra-5.1/plugins/blendalgebra/arith_addition.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/arith_divide.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/arith_multiply.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/arith_replace.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/arith_subtract.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/blendalgebra.C create mode 100644 cinelerra-5.1/plugins/blendalgebra/blendalgebra.h create mode 100644 cinelerra-5.1/plugins/blendalgebra/graphart_burn.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/graphart_difference.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/graphart_dodge.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/graphart_hardlight.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/graphart_overlay.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/graphart_screen.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/graphart_softlight.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/logical_and.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/logical_darken.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/logical_lighten.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/logical_max.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/logical_min.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/logical_or.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/logical_xor.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/ovl_normal.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/porterduff_dst.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/porterduff_dstatop.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/porterduff_dstin.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/porterduff_dstout.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/porterduff_dstover.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/porterduff_src.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/porterduff_srcatop.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/porterduff_srcin.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/porterduff_srcout.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/porterduff_srcover.ba create mode 100644 cinelerra-5.1/plugins/blendalgebra/ydiff.ba create mode 100755 cinelerra-5.1/plugins/blendprogram/BlendProgramCompile.pl create mode 100644 cinelerra-5.1/plugins/blendprogram/BlendProgramStart create mode 100644 cinelerra-5.1/plugins/blendprogram/Makefile create mode 100644 cinelerra-5.1/plugins/blendprogram/background.bp create mode 100644 cinelerra-5.1/plugins/blendprogram/blendprogram.C create mode 100644 cinelerra-5.1/plugins/blendprogram/blendprogram.h create mode 100644 cinelerra-5.1/plugins/blendprogram/chromakey.bp diff --git a/cinelerra-5.1/plugin_defs b/cinelerra-5.1/plugin_defs index ded3fd70..acf23a08 100644 --- a/cinelerra-5.1/plugin_defs +++ b/cinelerra-5.1/plugin_defs @@ -26,6 +26,8 @@ video := \ alpha \ bandslide \ bandwipe \ + blendalgebra \ + blendprogram \ bluebanana \ blur \ boxblur \ diff --git a/cinelerra-5.1/plugins/Makefile b/cinelerra-5.1/plugins/Makefile index 89f16466..fd403150 100644 --- a/cinelerra-5.1/plugins/Makefile +++ b/cinelerra-5.1/plugins/Makefile @@ -31,6 +31,8 @@ DIRS = $(OPENCV_OBJS) \ audioscope \ bandslide \ bandwipe \ + blendalgebra \ + blendprogram \ bluebanana \ blur \ boxblur \ @@ -220,7 +222,10 @@ clean: rm -rf $(foreach d,$(DIRS),$(d)/$(OBJDIR)) rm -rf $(PLUGIN_DIR) $(LADSPA_DIR) +# these plugins have something special to install install: + $(MAKE) -C blendalgebra $@ + $(MAKE) -C blendprogram $@ # dependencies for parallel build aging: libeffecttv diff --git a/cinelerra-5.1/plugins/blendalgebra/BlendAlgebraCompile.pl b/cinelerra-5.1/plugins/blendalgebra/BlendAlgebraCompile.pl new file mode 100755 index 00000000..ecc68df1 --- /dev/null +++ b/cinelerra-5.1/plugins/blendalgebra/BlendAlgebraCompile.pl @@ -0,0 +1,289 @@ +#!/usr/bin/perl + +# Helper script to compile Cinelerra blend algebra functions +# Calling: BlendAlgebraCompile.pl [options] +# The special option "-API" shows the numeric version of the script itself + +# Several important definitions + +# BlendAlgebraCompile.pl script API version. Must not be changed ! +$cin_ba_api = 1; + +# C compiler executable and options, can be redefined on user's demand +$cin_compiler = $ENV{'CIN_CC'}; +$cin_compiler = $ENV{'CC'} if $cin_compiler eq ''; +# a likely default compiler +$cin_compiler = 'gcc' if $cin_compiler eq ''; +# another possible compiler +#$cin_compiler = 'clang' if $cin_compiler eq ''; +# a fake compiler for debugging the script itself +#$cin_compiler = 'echo'; + +# Mandatory compiler options to build dynamically loadable object, don't change +$cin_compopts = '-fPIC'; +$cin_ldopts = '-shared'; +$cin_ldlibs = '-lm'; + +# Adjustable compiler options for optimization, debugging, warnings +$cin_optopts = '-O2'; +$cin_debopts = '-g'; +$cin_warnopts = '-Wall -W -Wno-unused-parameter'; +# more possible compiler options +#$cin_optopts = '-Ofast -march=native'; +#$cin_debopts = '-ggdb'; + +# Set these to 1 if want to optimize, debug, or verbose output by default +$opt_opt = 1; +$opt_deb = 0; +$opt_verb = 0; + +# Text editor executable, can be redefined on user's preferences +$cin_editor = $ENV{'CIN_EDITOR'}; +# a likely default editor +$cin_editor = 'emacs' if $cin_editor eq ''; +# another possible editors +#$cin_editor = 'konsole -e vim' if $cin_editor eq ''; +#$cin_editor = 'xterm -e vi' if $cin_editor eq ''; +#$cin_editor = 'xedit' if $cin_editor eq ''; + +# Background execution; set it empty to execute in foreground and block +$cin_bgr = ' &'; + +# Nothing to change below this line + +# Cinelerra installation path +$cin_dat = $ENV{'CIN_DAT'}; + +# Cinelerra user's config directory +$cin_config = $ENV{'CIN_CONFIG'}; +$cin_config = $ENV{'HOME'}.'/.bcast5' + if $cin_config eq '' && $ENV{'HOME'} ne ''; +$cin_config = '.' if $cin_config eq ''; +$me_config = "$cin_config/BlendAlgebraCompile.pl"; + +sub Usage +{ + print "\nCinelerraGG blend algebra compiler driver usage\n\n"; + print "$0 [options] compile blend algebra function \n"; + print "$0 -edit open blend function in editor\n"; + print "$0 -API output own API version\n"; + print "$0 -h, -help, -? output this help text\n"; + print "\nCinelerraGG additional blend algebra compiler options\n\n"; + print "-compile don't check modification time, compile unconditionally\n"; + print "-cfile don't remove intermediate .c file\n"; + print "-opt add optimizing options to compiler command line\n"; + print "-debug add debugging options to compiler command line\n"; + print "-warn add warning options to compiler command line\n"; + print "-edit open blend function source in text editor\n"; + print "-verbose verbose execution"; + print " (active by default)" if $opt_verb; + print "\n"; + print "-noapi don't copy $0 to $ENV{HOME}/.bcast5\n"; + print "\nCinelerraGG blend algebra compiler default configuration\n\n"; + print "Compiler command line: $cin_compiler $cin_compopts $cin_ldopts -o .so $cin_ldlibs\n"; + print "Optimizing options: $cin_optopts"; + print " (active by default)" if $opt_opt; + print "\n"; + print "Debugging options: $cin_debopts"; + print " (active by default)" if $opt_deb; + print "\n"; + print "Warning options: $cin_warnopts\n"; + print "Editor command line: $cin_editor $cin_bgr\n"; + print "\nRelevant environment variables\n\n"; + if ($ENV{'CIN_CC'} ne '') + { + print "CIN_CC=$ENV{CIN_CC}\n"; + } + else + { + print "\$CIN_CC: currently not set, fallback: $cin_compiler\n"; + } + if ($ENV{'CC'} ne '') + { + print "CC=$ENV{CC}\n"; + } + else + { + print "\$CC: currently not set, fallback: $cin_compiler\n"; + } + if ($ENV{'CIN_EDITOR'} ne '') + { + print "CIN_EDITOR=$ENV{CIN_EDITOR}\n"; + } + else + { + print "\$CIN_EDITOR: currently not set, fallback: $cin_editor\n"; + } + if ($ENV{'CIN_DAT'} ne '') + { + print "CIN_DAT=$ENV{CIN_DAT}\n"; + } + else + { + print "\$CIN_DAT: currently not set\n"; + } + if ($ENV{'CIN_CONFIG'} ne '') + { + print "CIN_CONFIG=$ENV{CIN_CONFIG}\n"; + } + else + { + print "\$CIN_CONFIG: currently not set, fallback: $ENV{HOME}/.bcast5\n"; + } + if ($ENV{'CIN_USERLIB'} ne '') + { + print "CIN_USERLIB=$ENV{CIN_USERLIB}\n"; + } + else + { + print "\$CIN_USERLIB: currently not set, fallback: $ENV{HOME}/.bcast5lib\n"; + } + exit 0; +} + +$opt_api = $opt_noapi = 0; +$opt_edit = $opt_compile = $opt_cfile = $opt_warn = 0; +$ba_src = ''; + +# Scan options... +foreach $arg (@ARGV) +{ + unless ($arg =~ /^-/) + { + $ba_src = $arg; # this is the source to compile + last; + } + if ($arg eq '-API') # print API version + { + $opt_api = 1; + } + elsif ($arg eq '-noapi') # don't copy script to ~/.bcast5 + { + $opt_noapi = 1; + } + elsif ($arg eq '-edit') # open in text editor + { + $opt_edit = 1; + push @opts, $arg; + } + elsif ($arg eq '-compile') # compile unconditionally + { + $opt_compile = 1; + push @opts, $arg; + } + elsif ($arg eq '-cfile') # don't remove .c file + { + $opt_cfile = 1; + push @opts, $arg; + } + elsif ($arg eq '-opt') # optimize + { + $opt_opt = 1; + push @opts, $arg; + } + elsif ($arg eq '-debug') # debug + { + $opt_deb = 1; + push @opts, $arg; + } + elsif ($arg eq '-warn') # warnings + { + $opt_warn = 1; + push @opts, $arg; + } + elsif ($arg eq '-verbose') # print executed commands + { + $opt_verb = 1; + push @opts, $arg; + } + else { Usage(); } # unknown option +} + +# A special internal request: output own API version +if ($opt_api) +{ + print "$cin_ba_api\n"; + exit 0; +} + +# If a system (not user's) script instance is executed, and the API versions +# of both scripts do not match, then copy the system script to the user's one +# (making a backup copy of the latter). Then execute it with the same argument. +if (! $opt_noapi && $0 ne $me_config) +{ + $me_api = 0; + $me_api = `\"$me_config\" -API` if -x $me_config; + #print "BlendAlgebraCompile: user script=$me_config API=$me_api, need $cin_ba_api\n"; + if ($me_api != $cin_ba_api) + { + print "BlendAlgebraCompile: copying $0 to $me_config\n"; + unlink "$me_config.bak" if -f "$me_config.bak"; + rename "$me_config", "$me_config.bak" if -f $me_config; + system "cp \"$0\" \"$me_config\""; + system "chmod +x \"$me_config\""; + } +} + +# Do nothing if nothing to compile +if ($ba_src eq '') { Usage(); } +#print "BlendAlgebraCompile: source=$ba_src\n"; +unless ($opt_edit || -f $ba_src) { Usage(); } + +# Redirection to user configurable script copy +$arg = join ' ', @opts; +if (! $opt_noapi && $0 ne $me_config) +{ + exec "\"$me_config\" $arg \"$ba_src\"" if -x $me_config; +} + +# If a user's script instance is executed, do everything by myself +print "BlendAlgebraCompile: executing $0 $arg $ba_src\n" if $opt_verb; + +# Call text editor +if ($opt_edit) +{ + print "BlendAlgebraCompile: executing $cin_editor $ba_src$cin_bgr\n"; + system "$cin_editor \"$ba_src\"$cin_bgr"; + exit 0; +} + +# Check if the function source is newer than the object +if ($cin_dat ne '') { $ba_start = "$cin_dat/dlfcn/BlendAlgebraStart"; } +else { $ba_start = "BlendAlgebraStart"; } +$mtime_start = -1; +($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime_start, + $ctime, $blksize, $blocks) = stat ($ba_start); + +($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime_src, + $ctime, $blksize, $blocks) = stat ($ba_src); +$mtime_src = $mtime_start if $mtime_src < $mtime_start; + +$mtime_so = -1; +($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime_so, + $ctime, $blksize, $blocks) = stat ("$ba_src.so") if -f "$ba_src.so"; + +if (! $opt_compile && $mtime_so > $mtime_src) +{ + print "BlendAlgebraCompile: $ba_src shared object up to date, exiting\n" + if $opt_verb; + exit 0; +} + +# Call the compiler now +$cin_compopts .= " $cin_optopts" if $opt_opt; +$cin_compopts .= " $cin_debopts" if $opt_deb; +$cin_compopts .= " $cin_warnopts" if $opt_warn; +unlink "$ba_src.c" if -f "$ba_src.c"; +unlink "$ba_src.so" if -f "$ba_src.so"; +print "BlendAlgebraCompile: executing cat $ba_start $ba_src > $ba_src.c\n" + if $opt_verb; +system "echo '# 1 \"$ba_start\"' > \"$ba_src.c\""; +system "cat \"$ba_start\" >> \"$ba_src.c\""; +system "echo '# 1 \"$ba_src\"' >> \"$ba_src.c\""; +system "cat \"$ba_src\" >> \"$ba_src.c\""; +print "BlendAlgebraCompile: executing $cin_compiler $cin_compopts $cin_ldopts -o $ba_src.so $ba_src.c $cin_ldlibs\n"; +system "$cin_compiler $cin_compopts $cin_ldopts -o \"$ba_src.so\" \"$ba_src.c\" $cin_ldlibs"; +unlink "$ba_src.c" if ! $opt_cfile && -f "$ba_src.c"; + +# And finally return to the caller +exit 0; diff --git a/cinelerra-5.1/plugins/blendalgebra/BlendAlgebraStart b/cinelerra-5.1/plugins/blendalgebra/BlendAlgebraStart new file mode 100644 index 00000000..251f1549 --- /dev/null +++ b/cinelerra-5.1/plugins/blendalgebra/BlendAlgebraStart @@ -0,0 +1,174 @@ +/***********************************************-*-C-*-**********/ + +/* Blend algebra header for Cinelerra Blend Algebra plugin */ + +#include +#include +#include +#include +#include +#include +#include + +/* These six must match enum BC_CModel from guicast/bccmodels.h */ +#define BC_RGB888 9 +#define BC_RGBA8888 10 +#define BC_YUV888 13 +#define BC_YUVA8888 14 +#define BC_RGB_FLOAT 29 +#define BC_RGBA_FLOAT 30 + +/* These five must match enum from plugins/blendalgebra/blendalgebra.h */ +#define AUTO 0 +#define RGB 1 +#define YUV 2 +#define HSV 3 +#define PROJECT 4 + +/* Universal math macros taken mostly from guicast/clip.h */ +#ifndef ABS +#define ABS(x) ((x) >= 0 ? (x) : -(x)) +#endif +#ifndef SQR +#define SQR(x) ((x) * (x)) +#endif +#ifndef MAX +#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#endif +#ifndef MIN +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#endif +#define TO_RAD(x) ((x) * 2 * M_PI / 360) +#define TO_DEG(x) ((x) * 360 / 2 / M_PI) + +/* CLIP returns value, CLAMP does assignment */ +#define CLIP(x,y,z) ((x) < (y) ? (y) : ((x) > (z) ? (z) : (x))) +#define CLAMP(x,y,z) ((x) = ((x) < (y) ? (y) : ((x) > (z) ? (z) : (x)))) + +/* Macros usable in the BLEND_ALGEBRA_INIT phase */ + +/* Specification of working color space inside user's function */ +#define COLORSPACE_RGB {*color_work = RGB;} +#define COLORSPACE_YUV {*color_work = YUV;} +#define COLORSPACE_HSV {*color_work = HSV;} + +/* Minimum no of tracks required by user's function */ +#define REQUIRE_TRACKS(n) {*MIN_tracks = (n);} + +/* Claim parallelization support and inquire if parallelism requested */ +#define PARALLEL_SAFE {*parallel_support = 1;} +#define PARALLEL_REQUEST parallel_request + +/* Dimensionality macros, can be used in both INIT and PROC phases */ + +/* Total number of tracks got to work on */ +#define TOTAL_TRACKS N_tracks + +/* 1 == has alpha channel */ +#define HAS_ALPHA HAS_alpha + +/* Frame dimensions in pixels */ +#define WIDTH FRAME_w +#define HEIGHT FRAME_h + +/* Macros usable in the BLEND_ALGEBRA_PROC phase */ + +/* Handy macros for pixel and key color components */ +#define R(i) ARG_r[i] +#define G(i) ARG_g[i] +#define B(i) ARG_b[i] +#define Y(i) ARG_r[i] +#define U(i) ARG_g[i] +#define V(i) ARG_b[i] +#define H(i) ARG_r[i] +#define S(i) ARG_g[i] +#define A(i) ARG_a[i] + +#define R_OUT (*OUT_r) +#define G_OUT (*OUT_g) +#define B_OUT (*OUT_b) +#define Y_OUT (*OUT_r) +#define U_OUT (*OUT_g) +#define V_OUT (*OUT_b) +#define H_OUT (*OUT_r) +#define S_OUT (*OUT_g) +#define A_OUT (*OUT_a) + +#define KEY_R KEY_r +#define KEY_G KEY_g +#define KEY_B KEY_b +#define KEY_Y KEY_r +#define KEY_U KEY_g +#define KEY_V KEY_b +#define KEY_H KEY_r +#define KEY_S KEY_g +#define KEY_A KEY_a + +/* Macros for pixel coordinates */ +#define PIX_X ARG_x +#define PIX_Y ARG_y + +/* Macros for various arts to clip pixels */ +#define CLIP_RGB(i) {CLAMP (ARG_r[i], 0, 1); \ + CLAMP (ARG_g[i], 0, 1); \ + CLAMP (ARG_b[i], 0, 1);} + +#define CLIP_YUV(i) {CLAMP (ARG_r[i], 0, 1); \ + CLAMP (ARG_g[i], -0.5, 0.5); \ + CLAMP (ARG_b[i], -0.5, 0.5);} + +#define CLIP_HSV(i) {if (ARG_r[i] < 0 || ARG_r[i] >= 360) \ + ARG_r[i] -= floor(ARG_r[i]/360)*360; \ + CLAMP (ARG_g[i], 0, 1); CLAMP (ARG_b[i], 0, 1);} + +#define CLIP_A(i) {CLAMP (ARG_a[i], 0, 1);} + +#define CLIP_RGBA(i) { CLIP_RGB(i) CLIP_A(i) } +#define CLIP_YUVA(i) { CLIP_YUV(i) CLIP_A(i) } +#define CLIP_HSVA(i) { CLIP_HSV(i) CLIP_A(i) } + +#define CLIP_RGB_ALL {int I_track; \ + for (I_track=0; I_track= 360) \ + *OUT_r -= floor(*OUT_r/360)*360; \ + CLAMP (*OUT_g, 0, 1); CLAMP (*OUT_b, 0, 1);} + +#define CLIP_A_OUT {CLAMP (*OUT_a, 0, 1);} + +#define CLIP_RGBA_OUT { CLIP_RGB_OUT CLIP_A_OUT } +#define CLIP_YUVA_OUT { CLIP_YUV_OUT CLIP_A_OUT } +#define CLIP_HSVA_OUT { CLIP_HSV_OUT CLIP_A_OUT } + +/* Mandatory separators between different user's function blocks */ +/* Interfaces must match that from plugins/blendalgebra/blendalgebra.h */ + +#define BLEND_ALGEBRA_INIT \ +void baInit (int *color_work, int color_proj, int *MIN_tracks, int N_tracks, \ +int *parallel_support, int parallel_request, int FRAME_w, int FRAME_h, \ +int HAS_alpha) { + +#define BLEND_ALGEBRA_PROC } \ +void baProc (int N_tracks, \ +float *ARG_r, float *ARG_g, float *ARG_b, float *ARG_a, \ +float KEY_r, float KEY_g, float KEY_b, float KEY_a, \ +float *OUT_r, float *OUT_g, float *OUT_b, float *OUT_a, \ +int ARG_x, int ARG_y, int FRAME_w, int FRAME_h, int HAS_alpha) { + +#define BLEND_ALGEBRA_STOP {return;} + +#define BLEND_ALGEBRA_END } diff --git a/cinelerra-5.1/plugins/blendalgebra/Makefile b/cinelerra-5.1/plugins/blendalgebra/Makefile new file mode 100644 index 00000000..308b4db4 --- /dev/null +++ b/cinelerra-5.1/plugins/blendalgebra/Makefile @@ -0,0 +1,75 @@ +include ../../plugin_defs + +LDFLAGS += -ldl + +OBJS = $(OBJDIR)/blendalgebra.o + +PLUGIN = blendalgebra + +all:: + +include ../../plugin_config + +CC ?= gcc + +FRONT = BlendAlgebraStart + +HELPER = BlendAlgebraCompile.pl + +DLFCN_DIR = $(BINDIR)/dlfcn +DLFCN_BA_DIR = $(DLFCN_DIR)/ba + +BA_OBJS = \ + ovl_normal.ba.so \ + arith_addition.ba.so \ + arith_subtract.ba.so \ + arith_multiply.ba.so \ + arith_divide.ba.so \ + arith_replace.ba.so \ + porterduff_dst.ba.so \ + porterduff_dstatop.ba.so \ + porterduff_dstin.ba.so \ + porterduff_dstout.ba.so \ + porterduff_dstover.ba.so \ + porterduff_src.ba.so \ + porterduff_srcatop.ba.so \ + porterduff_srcin.ba.so \ + porterduff_srcout.ba.so \ + porterduff_srcover.ba.so \ + logical_min.ba.so \ + logical_max.ba.so \ + logical_lighten.ba.so \ + logical_darken.ba.so \ + logical_and.ba.so \ + logical_or.ba.so \ + logical_xor.ba.so \ + graphart_overlay.ba.so \ + graphart_screen.ba.so \ + graphart_burn.ba.so \ + graphart_dodge.ba.so \ + graphart_difference.ba.so \ + graphart_hardlight.ba.so \ + graphart_softlight.ba.so \ + ydiff.ba.so + +BA_SRCS = $(BA_OBJS:.ba.so=.ba) + +$(OBJDIR)/blendalgebra.o: blendalgebra.C blendalgebra.h + +%.ba.so: %.ba $(FRONT) $(HELPER) + rm -f $@ + CIN_CC=$(CC) CIN_DAT= ./$(HELPER) -noapi -compile -opt -warn $< + +all:: $(OUTPUT) $(BA_OBJS) + +install:: $(BA_OBJS) + +install:: + mkdir -p $(DLFCN_DIR) + mkdir -p $(DLFCN_BA_DIR) + cp -a $(FRONT) $(HELPER) $(DLFCN_DIR) + chmod +x $(DLFCN_DIR)/$(HELPER) + cp -a $(BA_SRCS) $(BA_OBJS) $(DLFCN_BA_DIR) + +clean:: + rm -f $(BA_OBJS) *~ diff --git a/cinelerra-5.1/plugins/blendalgebra/arith_addition.ba b/cinelerra-5.1/plugins/blendalgebra/arith_addition.ba new file mode 100644 index 00000000..e2bfa204 --- /dev/null +++ b/cinelerra-5.1/plugins/blendalgebra/arith_addition.ba @@ -0,0 +1,25 @@ +/***********************************************-*-C-*-**********/ +/* Overlay mode: Arithmetic Addition, for any number of tracks */ + +BLEND_ALGEBRA_INIT + +COLORSPACE_RGB +PARALLEL_SAFE + +BLEND_ALGEBRA_PROC + +int i; + +R_OUT = G_OUT = B_OUT = A_OUT = 0; + +for (i=0; i + * + * 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 + * + */ + +#include "filexml.h" +#include "keyframe.h" +#include "language.h" +#include "mainerror.h" +#include "clip.h" +#include "bccolors.h" +#include "loadbalance.h" +#include "filesystem.h" +#include "vframe.h" +#include "mainsession.h" +#include "mwindow.h" +#include "pluginserver.h" +#include "blendalgebra.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +//#define DEBUG + +// Sorry for this global variable, it is needed to propagate the signal +// from the GUI instance to the processing instance of the plugin +// that some user function might have changed and need recompilation +static time_t BlendAlgebraTstamp = -1; + +REGISTER_PLUGIN(BlendAlgebra) + +LOAD_CONFIGURATION_MACRO(BlendAlgebra, BlendAlgebraConfig) + +NEW_WINDOW_MACRO(BlendAlgebra, BlendAlgebraWindow) + +const char *BlendAlgebra::plugin_title() { return N_("Blend Algebra"); } + +int BlendAlgebra::is_realtime() { return 1; } +int BlendAlgebra::is_multichannel() { return 1; } +int BlendAlgebra::is_synthesis() { return 1; } + +//////////////////////////////////////////// +// Plugin configuration class implementation +//////////////////////////////////////////// + +BlendAlgebraConfig::BlendAlgebraConfig() +{ + funcname[0] = 0; // no function per default + parallel = 1; // parallelize per default + clipcolors = 1; // clip colors per default + clear_input = 1; // like Overlay plugin does + direction = BlendAlgebraConfig::BOTTOM_FIRST; // as in Overlay plugin + output_track = BlendAlgebraConfig::TOP; // as in Overlay plugin + colorspace = BlendAlgebraConfig::AUTO; // requested from function + red = green = blue = 0; // black key color per default + alpha = 0; // transparent per default +} + +int BlendAlgebraConfig::equivalent(BlendAlgebraConfig &that) +{ + return + !strcmp (funcname, that.funcname) && + parallel == that.parallel && + clipcolors == that.clipcolors && + clear_input == that.clear_input && + direction == that.direction && + colorspace == that.colorspace && + EQUIV (red, that.red) && + EQUIV (green, that.green) && + EQUIV (blue, that.blue) && + EQUIV (alpha, that.alpha); +} + +void BlendAlgebraConfig::copy_from(BlendAlgebraConfig &that) +{ + strcpy (funcname, that.funcname); + parallel = that.parallel; + clipcolors = that.clipcolors; + clear_input = that.clear_input; + direction = that.direction; + colorspace = that.colorspace; + red = that.red; + green = that.green; + blue = that.blue; + alpha = that.alpha; +} + +void BlendAlgebraConfig::interpolate (BlendAlgebraConfig &prev, + BlendAlgebraConfig &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); + + red = prev.red * prev_scale + next.red * next_scale; + green = prev.green * prev_scale + next.green * next_scale; + blue = prev.blue * prev_scale + next.blue * next_scale; + alpha = prev.alpha * prev_scale + next.alpha * next_scale; + + strcpy (funcname, prev.funcname); + parallel = prev.parallel; + clipcolors = prev.clipcolors; + clear_input = prev.clear_input; + direction = prev.direction; + colorspace = prev.colorspace; +} + +const char *BlendAlgebraConfig::direction_to_text(int direction) +{ + switch(direction) + { + case BlendAlgebraConfig::BOTTOM_FIRST: return _("Bottom first"); + case BlendAlgebraConfig::TOP_FIRST: return _("Top first"); + } + return ""; +} + +const char *BlendAlgebraConfig::output_to_text(int output_track) +{ + switch(output_track) + { + case BlendAlgebraConfig::TOP: return _("Top"); + case BlendAlgebraConfig::BOTTOM: return _("Bottom"); + } + return ""; +} + +const char *BlendAlgebraConfig::colorspace_to_text(int colorspace) +{ + switch(colorspace) + { + case BlendAlgebraConfig::AUTO: return _("auto"); + case BlendAlgebraConfig::RGB: return _("RGB"); + case BlendAlgebraConfig::YUV: return _("YUV"); + case BlendAlgebraConfig::HSV: return _("HSV"); + case BlendAlgebraConfig::PROJECT: return _("of project"); + } + return ""; +} + +int BlendAlgebraConfig::get_key_color() +{ + int red = (int) (CLIP (this->red, 0, 1) * 255); + int green = (int) (CLIP (this->green, 0, 1) * 255); + int blue = (int) (CLIP (this->blue, 0, 1) * 255); + return (red << 16) | (green << 8) | blue; +} + +//////////////////////////////////////////// +// Plugin dialog window class implementation +//////////////////////////////////////////// + +BlendAlgebraFuncname::BlendAlgebraFuncname(BlendAlgebra *plugin, + const char *funcname, + BlendAlgebraWindow *gui, + int x, int y) + : BC_TextBox(x, y, gui->get_w()-x-xS(10), 1, funcname) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendAlgebraFuncname::handle_event() +{ + // Perhaps locking is not needed here + // as GUI is driven by a separate plugin instance + plugin->func_lock->lock("BlendAlgebraFuncname::handle_event"); + strncpy(plugin->config.funcname, get_text(), + sizeof(plugin->config.funcname)-1); + BlendAlgebraTstamp = time(NULL); // time of possible function change +#ifdef DEBUG + printf ("BlendAlgebraFuncname::handle_event setting function %s\n timestamp %s", + plugin->config.funcname, ctime(&BlendAlgebraTstamp)); +#endif + plugin->func_lock->unlock(); + plugin->send_configure_change(); + return 1; +} + +BlendAlgebraDetach::BlendAlgebraDetach (BlendAlgebra *plugin, + BlendAlgebraWindow *gui, + int x, int y) + : BC_GenericButton (x, y, _("Detach")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendAlgebraDetach::handle_event() +{ + if (! plugin->config.funcname[0]) return 1;// already detached, nothing to do + + plugin->func_lock->lock("BlendAlgebraDetach::handle_event"); + plugin->config.funcname[0] = 0; // clear function, inducing detach + BlendAlgebraTstamp = time(NULL); // force refresh of dlopen'd functions +#ifdef DEBUG + printf ("BlendAlgebraDetach::handle_event clearing function\n timestamp %s", + ctime(&BlendAlgebraTstamp)); +#endif + plugin->func_lock->unlock(); + + gui->lock_window("BlendAlgebraDetach::handle_event"); + gui->funcname->update(plugin->config.funcname); + gui->unlock_window(); + + plugin->send_configure_change(); + return 1; +} + +BlendAlgebraRefresh::BlendAlgebraRefresh (BlendAlgebra *plugin, + BlendAlgebraWindow *gui, + int x, int y) + : BC_GenericButton (x, y, _("Refresh")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendAlgebraRefresh::handle_event() +{ + plugin->func_lock->lock("BlendAlgebraRefresh::handle_event"); + BlendAlgebraTstamp = time(NULL); // force refresh of dlopen'd functions +#ifdef DEBUG + printf ("BlendAlgebraRefresh::handle_event timestamp %s", + ctime(&BlendAlgebraTstamp)); +#endif + plugin->func_lock->unlock(); + return 1; // just reattach all functions, without reconfiguration +} + +BlendAlgebraEdit::BlendAlgebraEdit (BlendAlgebra *plugin, + BlendAlgebraWindow *gui, + int x, int y) + : BC_GenericButton (x, y, _("Edit...")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendAlgebraEdit::handle_event() +{ + char fname[BCTEXTLEN], dir[BCTEXTLEN], str[2*BCTEXTLEN]; + + strcpy (fname, plugin->config.funcname); + if (! fname[0]) + { + eprintf (_("Blend Algebra: no source file to edit, select function first\n")); + return 1; + } + + // Evtl make function path absolute by prepending project path to it + if (fname[0] != '/') // fname is relative, prepend current project path + { + strcpy (dir, plugin->server->mwindow->session->filename); + if (dir[0]) + { + char *cp = strrchr (dir, '/'); + if (cp) + { + cp[1] = 0; // strip project filename off from project path + strcat (dir, fname); // concatenate obtained path with function name + strcpy (fname, dir); + } + } + } + + // This will run configured external editor via perl script + // If editor start is not backgrounded, GUI will block until editor exits + sprintf(str, "\"%s/dlfcn/BlendAlgebraCompile.pl\" -edit \"%s\"", + getenv("CIN_DAT"), fname); +#ifdef DEBUG + printf ("BlendAlgebraEdit::handle_event: executing:\n %s\n", str); +#endif + system (str); // runs configured external editor + + plugin->func_lock->lock("BlendAlgebraEdit::handle_event"); + BlendAlgebraTstamp = time(NULL); // force refresh of dlopen'd functions +#ifdef DEBUG + printf ("BlendAlgebraEdit::handle_event edited function %s\n timestamp %s", + fname, ctime(&BlendAlgebraTstamp)); +#endif + plugin->func_lock->unlock(); + + // Evtl functions will be recompiled, but no configure change + return 1; +} + +BlendAlgebraFileButton::BlendAlgebraFileButton(BlendAlgebra *plugin, + BlendAlgebraWindow *gui, + int x, int y) + : BC_GenericButton(x, y, _("Attach...")) +{ + this->plugin = plugin; + this->gui = gui; + this->file_box = 0; +} + +BlendAlgebraFileButton::~BlendAlgebraFileButton() +{ + stop(); +} + +int BlendAlgebraFileButton::handle_event() +{ + gui->editing_lock->lock(); + + if (! gui->editing) + { + gui->editing = 1; + gui->editing_lock->unlock(); + start(); + } + else + { + flicker(); + gui->editing_lock->unlock(); + } + + return 1; +} + +void BlendAlgebraFileButton::run() +{ + int result = 1; + const char *fpath; + char fname[BCTEXTLEN], dir[BCTEXTLEN]; + + strcpy (fname, plugin->config.funcname); + + // This infinite loop is exited after clicking OK or Cancel in FileBox. + // There are several special buttons which replace the FileBox initial path + // with another predefined path and close FileBox with reinit_path flag set. + // If reinit_path is set, the loop is repeated with that extracted path. + // reinit_path is cleared inside BlendAlgebraFileBox constructor. + + for (;;) // will exit when reinit_path == 0 + { // Evtl make function path absolute by prepending project path to it + if (fname[0] != '/') // fname is relative, prepend current project path + { + strcpy (dir, plugin->server->mwindow->session->filename); + if (dir[0]) + { + char *cp = strrchr (dir, '/'); + if (cp) + { + cp[1] = 0; // strip project filename off from project path + strcat (dir, fname); // concatenate obtained path with function name + strcpy (fname, dir); + } + } + } +#ifdef DEBUG + printf ("BlendAlgebraFileButton::run creating file_box (%s)\n", fname); +#endif + file_box = new BlendAlgebraFileBox (plugin, gui, fname); + file_box->update_history(); // otherwise actual dir can be forgotten + file_box->create_objects(); + file_box->lock_window ("BlendAlgebraFileButton::run"); + file_box->add_objects(); // add our special buttons + file_box->update_filter ("*.ba"); + file_box->unlock_window(); + result = file_box->run_window(); + if (file_box->reinit_path) // if set, a button was clicked + { + fpath = file_box->get_current_path(); // current, as set by buttons +#ifdef DEBUG + printf ("BlendAlgebraFileButton::run file_box returned %d reinit_path=%d\n fpath=%s\n", + result, file_box->reinit_path, fpath); +#endif + strncpy (fname, fpath ? fpath : "", sizeof(fname)-1); + delete file_box; + file_box = 0; + continue; // reinit_path will be cleared on repeat in FileBox constructor + } + fpath = file_box->get_submitted_path(); // submitted, as set by user +#ifdef DEBUG + printf ("BlendAlgebraFileButton::run file_box returned %d reinit_path=%d\n fpath=%s\n", + result, file_box->reinit_path, fpath); +#endif + strncpy (fname, fpath ? fpath : "", sizeof(fname)-1); + delete file_box; + file_box = 0; + break; // reinit_path remains cleared, exit loop + } // until reinit_path == 0 + + gui->editing_lock->lock(); + if (result) gui->editing = 0; + gui->editing_lock->unlock(); + if (! gui->editing) return; // Cancel pressed + + if (fname[0]) // selected function name not empty, canonicalize it + { // if function is under project's dir, strip dir and make path relative + strcpy (dir, plugin->server->mwindow->session->filename); + // another project location might be plugin->server->mwindow->edl->path + if (dir[0]) // project filename contains some path + { + char *cp = strrchr (dir, '/'); + if (cp) + { + cp[1] = 0; // the directory of current project with trailing slash + if (! strncmp (fname, dir, strlen(dir))) + { + strcpy (dir, fname+strlen(dir)); // strip project dir off + strcpy (fname, dir+strspn(dir,"/")); // ensure path is relative + } + } + } + if (strlen (fname) < 3 || strcmp (fname+strlen(fname)-3, ".ba")) + strcat (fname, ".ba"); // suggest '.ba' suffix for blend functions + } + + // Actualize selected function in config and in the main plugin dialog + plugin->func_lock->lock("BlendAlgebraFileButton::run"); + strcpy (plugin->config.funcname, fname); + BlendAlgebraTstamp = time(NULL); // time of possible function change +#ifdef DEBUG + printf ("BlendAlgebraFileButton::run setting function %s\n timestamp %s", + plugin->config.funcname, ctime(&BlendAlgebraTstamp)); +#endif + plugin->func_lock->unlock(); + gui->lock_window("BlendAlgebraFileButton::run"); + gui->funcname->update(plugin->config.funcname); + gui->unlock_window(); + gui->editing_lock->lock(); + gui->editing = 0; + gui->editing_lock->unlock(); + + plugin->send_configure_change(); +} + +void BlendAlgebraFileButton::stop() +{ + if (file_box) file_box->set_done(1); + join(); +} + +BlendAlgebraFileBox::BlendAlgebraFileBox(BlendAlgebra *plugin, + BlendAlgebraWindow *gui, + char *init_path) + : BC_FileBox(0, BC_WindowBase::get_resources()->filebox_h/2, init_path, + _("Blend Algebra: Select function source file"),"") +{ + this->plugin = plugin; + this->gui = gui; + + to_curdir = 0; + to_usrlib = 0; + to_syslib = 0; + copy_curdir = 0; + copy_usrlib = 0; + file_edit = 0; + reinit_path = 0; +} + +BlendAlgebraFileBox::~BlendAlgebraFileBox() +{ +} + +// We need several additional buttons not foreseen in the bare FileBox. +// We arrange them in the place of (empty) FileBox caption. +void BlendAlgebraFileBox::add_objects() +{ + int xs10 = xS(10), xs5 = xS(5); + int ys10 = yS(10); + int x = xs10, y = ys10, x2; + + add_subwindow(to_curdir = new BlendAlgebraToCurdir(this, x, y)); + x2 = x+to_curdir->get_w()+xs5; + add_subwindow(to_usrlib = new BlendAlgebraToUsrlib(this, x2, y)); + x2 += to_usrlib->get_w()+xs5; + add_subwindow(to_syslib = new BlendAlgebraToSyslib(this, x2, y)); + y = get_y_margin(); + add_subwindow(copy_curdir = new BlendAlgebraCopyCurdir(this, x, y)); + x2 = x+copy_curdir->get_w()+xs5; + add_subwindow(copy_usrlib = new BlendAlgebraCopyUsrlib(this, x2, y)); + x2 += copy_usrlib->get_w()+xs5; + add_subwindow(file_edit = new BlendAlgebraFileEdit(this, x2, y)); + flush(); +} + +int BlendAlgebraFileBox::resize_event(int w, int h) +{ + int xs10 = xS(10), xs5 = xS(5); + int x = xs10, y, x2; + + BC_FileBox::resize_event (w, h); + + y = get_y_margin(); + copy_curdir->reposition_window (x, y); + x2 = x+copy_curdir->get_w()+xs5; + copy_usrlib->reposition_window (x2, y); + x2 += copy_usrlib->get_w()+xs5; + file_edit->reposition_window (x2, y); + + flush(); + return 1; +} + +BlendAlgebraToCurdir::BlendAlgebraToCurdir(BlendAlgebraFileBox *file_box, + int x, int y) + : BC_GenericButton (x, y, _("=>Project")) +{ + this->file_box = file_box; +} + +int BlendAlgebraToCurdir::handle_event() +{ + char *cp, fname[BCTEXTLEN], dir[BCTEXTLEN], path[BCTEXTLEN]; + + strcpy (dir, file_box->plugin->server->mwindow->session->filename); + if (dir[0]) // first get current project directory + { + cp = strrchr (dir, '/'); + if (cp) *cp = 0; + else dir[0] = 0; + } + if (! dir[0]) // no project dir - get curdir as fallback + { + cp = getcwd (dir, sizeof(dir)); + if (! cp) dir[0] = 0; + } + if (! dir[0]) return 1; // no curdir accessible, nothing to change + + fname[0] = 0; + const char *spath = file_box->get_submitted_path();// get name entered so far + if (spath) file_box->fs->extract_name (fname, spath); // cut name from dir + + if (fname[0]) file_box->fs->join_names (path, dir, fname); + else strcpy (path, dir); // substitute old entered dir with project dir + + // Not exactly sure what operations on FileBox are really important + file_box->fs->change_dir (dir); // force it to recognize the new dir + + // This updates all paths, sets current_path and submitted_path of FileBox, + // but in memory only, text fields in the dialog are not actualized. + // file_box->refresh() does not help to refresh text fields either. + // Therefore we have to apply a trick with closing FileBox and + // reopening it with the new generated path. + file_box->update_paths (path); + + // Without updating history FileBox forgets our new dir + // and sets curdir to some old history item. + file_box->update_history(); + + file_box->reinit_path = 1; // set flag to reopen FileBox afterwards + file_box->set_done(1); // temporarily close FileBox, will be reopened later + + return 1; +} + +BlendAlgebraToUsrlib::BlendAlgebraToUsrlib(BlendAlgebraFileBox *file_box, + int x, int y) + : BC_GenericButton (x, y, _("=>Userlib")) +{ + this->file_box = file_box; +} + +int BlendAlgebraToUsrlib::handle_event() +{ + char *cp, fname[BCTEXTLEN], dir[BCTEXTLEN], path[BCTEXTLEN]; + + dir[0] = 0; + cp = getenv ("CIN_USERLIB"); // $HOME/.bcast5lib by default + if (cp) strcpy (dir, cp); + if (! dir[0]) + { + cp = getenv ("HOME"); // evtl resolve as default via $HOME + if (cp) strcpy (dir, cp); + if (dir[0]) strcat (dir, "/.bcast5lib"); + else + { + cp = getenv ("CIN_CONFIG"); // or via $CIN_CONFIG as fallback + if (cp) strcpy (dir, cp); + if (dir[0]) strcat (dir, "lib"); + } + } + if (! dir[0]) return 1; // no user libdir known, nothing to change + + // The default user libdir for blend functions is $HOME/.bcast5lib/dlfcn/ba + // Ensure it is a directory, evtl create dir, if not - do nothing else + if (! file_box->fs->is_dir (dir)) file_box->fs->create_dir (dir); + if (! file_box->fs->is_dir (dir)) return 1; + strcat (dir, "/dlfcn"); + if (! file_box->fs->is_dir (dir)) file_box->fs->create_dir (dir); + if (! file_box->fs->is_dir (dir)) return 1; + strcat (dir, "/ba"); + if (! file_box->fs->is_dir (dir)) file_box->fs->create_dir (dir); + if (! file_box->fs->is_dir (dir)) return 1; + + fname[0] = 0; + const char *spath = file_box->get_submitted_path();// get name entered so far + if (spath) file_box->fs->extract_name (fname, spath); // cut name from dir + if (fname[0]) file_box->fs->join_names (path, dir, fname); + else strcpy (path, dir); // substitute old entered dir with user libdir + + // Reinitialize FileBox with the modified path + file_box->fs->change_dir (dir); + file_box->update_paths (path); + file_box->update_history(); + file_box->reinit_path = 1; // set flag to reopen FileBox afterwards + file_box->set_done(1); // temporarily close FileBox, will be reopened later + + return 1; +} + +BlendAlgebraToSyslib::BlendAlgebraToSyslib(BlendAlgebraFileBox *file_box, + int x, int y) + : BC_GenericButton (x, y, _("=>Syslib")) +{ + this->file_box = file_box; +} + +int BlendAlgebraToSyslib::handle_event() +{ + char *cp, fname[BCTEXTLEN], dir[BCTEXTLEN], path[BCTEXTLEN]; + + dir[0] = 0; + cp = getenv ("CIN_DAT"); // Cinelerra installation directory (bin) + if (cp) strcpy (dir, cp); + if (! dir[0]) return 1; // there is no default + + // System libdir for blend functions is $CIN_DAT/dlfcn/ba (bin/dlfcn/ba). + // Ensure it is a directory, it must exist, if not - do nothing else + strcat (dir, "/dlfcn/ba"); + if (! file_box->fs->is_dir (dir)) return 1; + + fname[0] = 0; + const char *spath = file_box->get_submitted_path();// get name entered so far + if (spath) file_box->fs->extract_name (fname, spath); // cut name from dir + if (fname[0]) file_box->fs->join_names (path, dir, fname); + else strcpy (path, dir); // substitute that old dir with system libdir + + // Reinitialize FileBox with the modified path + file_box->fs->change_dir (dir); + file_box->update_paths (path); + file_box->update_history(); + file_box->reinit_path = 1; // set flag to reopen FileBox afterwards + file_box->set_done(1); // temporarily close FileBox, will be reopened later + + return 1; +} + +BlendAlgebraCopyCurdir::BlendAlgebraCopyCurdir(BlendAlgebraFileBox *file_box, + int x, int y) + : BC_GenericButton (x, y, _("Copy to project")) +{ + this->file_box = file_box; +} + +int BlendAlgebraCopyCurdir::handle_event() +{ + int ret; + char *cp, fname[BCTEXTLEN], dir[BCTEXTLEN], from_path[BCTEXTLEN], + to_path[BCTEXTLEN], cmd[3*BCTEXTLEN]; + + strcpy (dir, file_box->plugin->server->mwindow->session->filename); + if (dir[0]) // first get current project directory + { + cp = strrchr (dir, '/'); + if (cp) *cp = 0; + else dir[0] = 0; + } + if (! dir[0]) return 1; // no curdir accessible, no copy target + + fname[0] = from_path[0] = 0; + const char *spath = file_box->get_submitted_path();// get name entered so far + if (spath) + { + strcpy (from_path, spath); // this is copy source + file_box->fs->extract_name (fname, spath); // cut name from source dir + } + if (! (fname[0] && from_path[0])) return 1; // no copy source ?? + + file_box->fs->join_names (to_path, dir, fname); // this is copy target + + if (! strcmp (from_path, to_path)) return 1; // source and target identical + + if (file_box->fs->is_dir (from_path) || file_box->fs->is_dir (to_path)) + return 1; // source and target must not be directories + if (access (from_path, R_OK)) + { + eprintf (_("Blend Algebra: source file %s does not exist or not readable\n"), + from_path); + return 1; + } + if (! access (to_path, F_OK)) + { + eprintf (_("Blend Algebra: target file %s exists, overwriting not allowed\n"), + to_path); + return 1; + } + + // Now do copy operation + sprintf (cmd, "cp \"%s\" \"%s\"", from_path, to_path); +#ifdef DEBUG + printf ("BlendAlgebraCopyCurdir::handle_event: executing %s\n", cmd); +#endif + ret = system (cmd); + if (ret) + { + eprintf (_("Blend Algebra: copying %s to %s failed\nsee console printout for diagnostics\n"), + from_path, to_path); + return 1; + } + + // Copying successful, now change dir to the location of the target + file_box->fs->change_dir (dir); + file_box->update_paths (to_path); + file_box->update_history(); + file_box->reinit_path = 1; // set flag to reopen FileBox afterwards + file_box->set_done(1); // temporarily close FileBox, will be reopened later + + return 1; +} + +BlendAlgebraCopyUsrlib::BlendAlgebraCopyUsrlib(BlendAlgebraFileBox *file_box, + int x, int y) + : BC_GenericButton (x, y, _("Copy to userlib")) +{ + this->file_box = file_box; +} + +int BlendAlgebraCopyUsrlib::handle_event() +{ + int ret; + char *cp, fname[BCTEXTLEN], dir[BCTEXTLEN], from_path[BCTEXTLEN], + to_path[BCTEXTLEN], cmd[3*BCTEXTLEN]; + + dir[0] = 0; + cp = getenv ("CIN_USERLIB"); // $HOME/.bcast5lib by default + if (cp) strcpy (dir, cp); + if (! dir[0]) + { + cp = getenv ("HOME"); // evtl resolve as default via $HOME + if (cp) strcpy (dir, cp); + if (dir[0]) strcat (dir, "/.bcast5lib"); + else + { + cp = getenv ("CIN_CONFIG"); // or via $CIN_CONFIG as fallback + if (cp) strcpy (dir, cp); + if (dir[0]) strcat (dir, "lib"); + } + } + if (! dir[0]) return 1; // no user libdir known, no copy target + + // Evtl create user libdir, if not successful - do nothing else + if (! file_box->fs->is_dir (dir)) file_box->fs->create_dir (dir); + if (! file_box->fs->is_dir (dir)) return 1; + strcat (dir, "/dlfcn"); + if (! file_box->fs->is_dir (dir)) file_box->fs->create_dir (dir); + if (! file_box->fs->is_dir (dir)) return 1; + strcat (dir, "/ba"); + if (! file_box->fs->is_dir (dir)) file_box->fs->create_dir (dir); + if (! file_box->fs->is_dir (dir)) return 1; + + fname[0] = from_path[0] = 0; + const char *spath = file_box->get_submitted_path();// get name entered so far + if (spath) + { + strcpy (from_path, spath); // this is copy source + file_box->fs->extract_name (fname, spath); // cut name from source dir + } + if (! (fname[0] && from_path[0])) return 1; // no copy source ?? + + file_box->fs->join_names (to_path, dir, fname); // this is copy target + + if (! strcmp (from_path, to_path)) return 1; // source and target identical + + if (file_box->fs->is_dir (from_path) || file_box->fs->is_dir (to_path)) + return 1; // source and target must not be directories + if (access (from_path, R_OK)) + { + eprintf (_("Blend Algebra: source file %s does not exist or not readable\n"), + from_path); + return 1; + } + if (! access (to_path, F_OK)) + { + eprintf (_("Blend Algebra: target file %s exists, overwriting not allowed\n"), + to_path); + return 1; + } + + // Now do copy operation + sprintf (cmd, "cp \"%s\" \"%s\"", from_path, to_path); +#ifdef DEBUG + printf ("BlendAlgebraCopyUsrlib::handle_event: executing %s\n", cmd); +#endif + ret = system (cmd); + if (ret) + { + eprintf (_("Blend Algebra: copying %s to %s failed\nsee console printout for diagnostics\n"), + from_path, to_path); + return 1; + } + + return 1; // Copying successful, but don't change directory to user libdir +} + +BlendAlgebraFileEdit::BlendAlgebraFileEdit(BlendAlgebraFileBox *file_box, + int x, int y) + : BC_GenericButton (x, y, _("Edit...")) +{ + this->file_box = file_box; +} + +int BlendAlgebraFileEdit::handle_event() +{ + char fname[BCTEXTLEN], dir[BCTEXTLEN], str[2*BCTEXTLEN]; + + fname[0] = 0; + const char *spath = file_box->get_submitted_path();// get name entered so far + if (spath) strcpy (fname, spath); + if (! fname[0]) + { + eprintf (_("Blend Algebra: no function to edit, select source file first\n")); + return 1; + } + + // Evtl make function path absolute by prepending project path to it + if (fname[0] != '/') // fname is relative, prepend current project path + { + strcpy (dir, file_box->plugin->server->mwindow->session->filename); + if (dir[0]) + { + char *cp = strrchr (dir, '/'); + if (cp) + { + cp[1] = 0; // strip project filename off from project path + strcat (dir, fname); // concatenate obtained path with function name + strcpy (fname, dir); + } + } + } + if (file_box->fs->is_dir (fname)) + { + eprintf (_("Blend Algebra: cannot edit directory, select source file first\n")); + return 1; + } + + // This will run configured external editor via perl script + // If editor start is not backgrounded, GUI will block until editor exits + sprintf(str, "\"%s/dlfcn/BlendAlgebraCompile.pl\" -edit \"%s\"", + getenv("CIN_DAT"), fname); +#ifdef DEBUG + printf ("BlendAlgebraFileEdit::handle_event: executing:\n %s\n", str); +#endif + system (str); // runs configured external editor + + file_box->plugin->func_lock->lock("BlendAlgebraFileEdit::handle_event"); + BlendAlgebraTstamp = time(NULL); // force refresh of dlopen'd functions +#ifdef DEBUG + printf ("BlendAlgebraFileEdit::handle_event edited function %s\n timestamp %s", + fname, ctime(&BlendAlgebraTstamp)); +#endif + file_box->plugin->func_lock->unlock(); + + return 1; +} + +BlendAlgebraClipcolors::BlendAlgebraClipcolors(BlendAlgebra *plugin, + BlendAlgebraWindow *gui, + int x, int y) + : BC_CheckBox(x, y, plugin->config.clipcolors) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendAlgebraClipcolors::handle_event() +{ + plugin->config.clipcolors = get_value(); + plugin->send_configure_change(); + return 1; +} + +BlendAlgebraParallel::BlendAlgebraParallel(BlendAlgebra *plugin, + BlendAlgebraWindow *gui, + int x, int y) + : BC_CheckBox(x, y, plugin->config.parallel) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendAlgebraParallel::handle_event() +{ + plugin->config.parallel = get_value(); + plugin->send_configure_change(); + return 1; +} + +BlendAlgebraClearInput::BlendAlgebraClearInput(BlendAlgebra *plugin, + BlendAlgebraWindow *gui, + int x, int y) + : BC_CheckBox(x, y, plugin->config.clear_input) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendAlgebraClearInput::handle_event() +{ + plugin->config.clear_input = get_value(); + plugin->send_configure_change(); + return 1; +} + +BlendAlgebraDirection::BlendAlgebraDirection(BlendAlgebra *plugin, int x, int y) + : BC_PopupMenu(x, y, xS(150), + BlendAlgebraConfig::direction_to_text(plugin->config.direction), 1) +{ + this->plugin = plugin; +} + +void BlendAlgebraDirection::create_objects() +{ + add_item(new BC_MenuItem(BlendAlgebraConfig::direction_to_text( + BlendAlgebraConfig::TOP_FIRST))); + add_item(new BC_MenuItem(BlendAlgebraConfig::direction_to_text( + BlendAlgebraConfig::BOTTOM_FIRST))); +} + +int BlendAlgebraDirection::handle_event() +{ + char *text = get_text(); + + if(!strcmp(text, BlendAlgebraConfig::direction_to_text( + BlendAlgebraConfig::TOP_FIRST))) + plugin->config.direction = BlendAlgebraConfig::TOP_FIRST; + else if(!strcmp(text, BlendAlgebraConfig::direction_to_text( + BlendAlgebraConfig::BOTTOM_FIRST))) + plugin->config.direction = BlendAlgebraConfig::BOTTOM_FIRST; + + plugin->send_configure_change(); + return 1; +} + +BlendAlgebraOutput::BlendAlgebraOutput(BlendAlgebra *plugin, int x, int y) + : BC_PopupMenu(x, y, xS(100), + BlendAlgebraConfig::output_to_text(plugin->config.output_track), 1) +{ + this->plugin = plugin; +} + +void BlendAlgebraOutput::create_objects() +{ + add_item(new BC_MenuItem(BlendAlgebraConfig::output_to_text( + BlendAlgebraConfig::TOP))); + add_item(new BC_MenuItem(BlendAlgebraConfig::output_to_text( + BlendAlgebraConfig::BOTTOM))); +} + +int BlendAlgebraOutput::handle_event() +{ + char *text = get_text(); + + if(!strcmp(text, BlendAlgebraConfig::output_to_text( + BlendAlgebraConfig::TOP))) + plugin->config.output_track = BlendAlgebraConfig::TOP; + else if(!strcmp(text, BlendAlgebraConfig::output_to_text( + BlendAlgebraConfig::BOTTOM))) + plugin->config.output_track = BlendAlgebraConfig::BOTTOM; + + plugin->send_configure_change(); + return 1; +} + +BlendAlgebraColorspace::BlendAlgebraColorspace(BlendAlgebra *plugin, + int x, int y) + : BC_PopupMenu(x, y, xS(150), + BlendAlgebraConfig::colorspace_to_text(plugin->config.colorspace), 1) +{ + this->plugin = plugin; +} + +void BlendAlgebraColorspace::create_objects() +{ + add_item(new BC_MenuItem(BlendAlgebraConfig::colorspace_to_text( + BlendAlgebraConfig::AUTO))); + add_item(new BC_MenuItem(BlendAlgebraConfig::colorspace_to_text( + BlendAlgebraConfig::RGB))); + add_item(new BC_MenuItem(BlendAlgebraConfig::colorspace_to_text( + BlendAlgebraConfig::YUV))); + add_item(new BC_MenuItem(BlendAlgebraConfig::colorspace_to_text( + BlendAlgebraConfig::HSV))); + add_item(new BC_MenuItem(BlendAlgebraConfig::colorspace_to_text( + BlendAlgebraConfig::PROJECT))); +} + +int BlendAlgebraColorspace::handle_event() +{ + char *text = get_text(); + + if(!strcmp(text, BlendAlgebraConfig::colorspace_to_text( + BlendAlgebraConfig::AUTO))) + plugin->config.colorspace = BlendAlgebraConfig::AUTO; + else if(!strcmp(text, BlendAlgebraConfig::colorspace_to_text( + BlendAlgebraConfig::RGB))) + plugin->config.colorspace = BlendAlgebraConfig::RGB; + else if(!strcmp(text, BlendAlgebraConfig::colorspace_to_text( + BlendAlgebraConfig::YUV))) + plugin->config.colorspace = BlendAlgebraConfig::YUV; + else if(!strcmp(text, BlendAlgebraConfig::colorspace_to_text( + BlendAlgebraConfig::HSV))) + plugin->config.colorspace = BlendAlgebraConfig::HSV; + else if(!strcmp(text, BlendAlgebraConfig::colorspace_to_text( + BlendAlgebraConfig::PROJECT))) + plugin->config.colorspace = BlendAlgebraConfig::PROJECT; + + plugin->send_configure_change(); + return 1; +} + +BlendAlgebraKeyColor::BlendAlgebraKeyColor (BlendAlgebra *plugin, + BlendAlgebraWindow *gui, + int x, int y) + : BC_GenericButton (x, y, _("Select key color...")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendAlgebraKeyColor::handle_event() +{ + gui->color_thread->start_window (plugin->config.get_key_color(), 0xff); + return 1; +} + +BlendAlgebraColorPicker::BlendAlgebraColorPicker (BlendAlgebra *plugin, + BlendAlgebraWindow *gui, + int x, int y) + : BC_GenericButton (x, y, _("Get from color picker")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendAlgebraColorPicker::handle_event() +{ + plugin->config.red = plugin->get_red(); + plugin->config.green = plugin->get_green(); + plugin->config.blue = plugin->get_blue(); + + gui->update_key_sample(); + + plugin->send_configure_change(); + return 1; +} + +BlendAlgebraColorThread::BlendAlgebraColorThread (BlendAlgebra * plugin, + BlendAlgebraWindow * gui) + : ColorPicker (0, _("Select color")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendAlgebraColorThread::handle_new_color (int output, int alpha) +{ + plugin->config.red = (float) ((output & 0xff0000) >> 16) / 255; + plugin->config.green = (float) ((output & 0x00ff00) >> 8) / 255; + plugin->config.blue = (float) ((output & 0x0000ff) ) / 255; + + get_gui()->unlock_window(); + gui->lock_window("BlendAlgebraColorThread::handle_new_color"); + gui->update_key_sample(); + gui->unlock_window(); + get_gui()->lock_window("BlendAlgebraColorThread::handle_new_color"); + + plugin->send_configure_change(); + return 1; +} + +BlendAlgebraAlphaText::BlendAlgebraAlphaText(BlendAlgebra *plugin, + BlendAlgebraWindow *gui, + BlendAlgebraAlphaSlider *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); +} + +BlendAlgebraAlphaText::~BlendAlgebraAlphaText() +{ +} + +int BlendAlgebraAlphaText::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; +} + +BlendAlgebraAlphaSlider::BlendAlgebraAlphaSlider(BlendAlgebra *plugin, + BlendAlgebraAlphaText *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 +} + +BlendAlgebraAlphaSlider::~BlendAlgebraAlphaSlider() +{ +} + +int BlendAlgebraAlphaSlider::handle_event() +{ + *output = get_value(); + text->update(*output); + + plugin->send_configure_change(); + return 1; +} + +BlendAlgebraWindow::BlendAlgebraWindow(BlendAlgebra *plugin) + : PluginClientWindow(plugin, xS(450), yS(410), xS(450), yS(410), 0) +{ + this->plugin = plugin; + color_thread = 0; + editing_lock = new Mutex("BlendAlgebraWindow::editing_lock"); + editing = 0; +} + +BlendAlgebraWindow::~BlendAlgebraWindow() +{ + delete color_thread; + delete editing_lock; +} + +void BlendAlgebraWindow::create_objects() +{ + int xs5 = xS(5), xs10 = xS(10), xs20 = xS(20); + int ys5 = yS(5), ys10 = yS(10), ys20 = yS(20), ys30 = yS(30), ys40 = yS(40); + int x = xs10, y = ys10, x2; + BC_Title *title; + BC_TitleBar *title_bar; + + // Programming section + add_subwindow (title_bar = + new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, + _("Blend programming environment"))); + + y += ys30; + add_subwindow(title = new BC_Title(x, y, _("Function:"))); + add_subwindow(funcname = + new BlendAlgebraFuncname(plugin, plugin->config.funcname, this, + x + title->get_w() + xs5, y)); + + y += ys30; + add_subwindow(file_button = new BlendAlgebraFileButton(plugin, this, x, y)); + x2 = x+file_button->get_w()+xs5; + add_subwindow(edit_button = new BlendAlgebraEdit(plugin, this, x2, y)); + x2 += edit_button->get_w()+xs5; + add_subwindow(refresh_button = new BlendAlgebraRefresh(plugin, this, x2, y)); + x2 += refresh_button->get_w()+xs5; + add_subwindow(detach_button = new BlendAlgebraDetach(plugin, this, x2, y)); + + y += ys30; + add_subwindow(title = new BC_Title(x, y, _("Color space:"))); + add_subwindow(colorspace = + new BlendAlgebraColorspace(plugin, + x + title->get_w() + xs5, y)); + colorspace->create_objects(); + + x2 = x+title->get_w()+colorspace->get_w()+xs10+xs10; + add_subwindow(title = new BC_Title(x2, y, _("Parallelize processing"))); + add_subwindow(parallel = + new BlendAlgebraParallel(plugin, this, + x2 + title->get_w() + xs5, y)); + + // Supplementary color section + y += ys40; + add_subwindow (title_bar = + new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, + _("Supplementary color selection"))); + + y += ys30; + add_subwindow(title = + new BC_Title(x, y, _("Chroma key or substitution color:"))); + add_subwindow(key_sample = new BC_SubWindow(x + title->get_w() + xs5, y, + xS(150), yS(50))); + y += ys20+ys5; + add_subwindow(title = new BC_Title(x, y, _("Clip color values"))); + add_subwindow(clipcolors = + new BlendAlgebraClipcolors(plugin, this, + x + title->get_w() + xs5, y)); + + y += ys30+ys5; + add_subwindow(key_color = new BlendAlgebraKeyColor(plugin, this, x, y)); + x2 = x+key_color->get_w()+xs5; + add_subwindow(color_picker = + new BlendAlgebraColorPicker(plugin, this, x2, y)); + + y += ys30+ys5; + add_subwindow(title = new BC_Title(x, y, _("Substitution opacity:"))); + alpha_text = new BlendAlgebraAlphaText (plugin, this, 0, + x+title->get_w()+xs10+xs5+xS(210), + y, 0, 1, &plugin->config.alpha); + alpha_text->create_objects(); + key_alpha = new BlendAlgebraAlphaSlider (plugin, alpha_text, + x + title->get_w() + xs5, y, + xS(210), 0, 1, + &plugin->config.alpha); + add_subwindow(key_alpha); + alpha_text->slider = key_alpha; + + // Track arrangement section + y += ys40; + add_subwindow (title_bar = + new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, + _("Processed tracks arrangement"))); + + y += ys30; + add_subwindow(title = new BC_Title(x, y, _("Track order:"))); + add_subwindow(direction = + new BlendAlgebraDirection(plugin, + x + title->get_w() + xs5, y)); + direction->create_objects(); + x2 = x+title->get_w()+direction->get_w()+xs10; + add_subwindow(title = new BC_Title(x2, y, _("Output track:"))); + add_subwindow(output = + new BlendAlgebraOutput(plugin, x2 + title->get_w() + xs5, y)); + output->create_objects(); + + y += ys30; + add_subwindow(title = + new BC_Title(x, y, + _("Hide input tracks, use output exclusively"))); + add_subwindow(clear_input = + new BlendAlgebraClearInput(plugin, this, + x + title->get_w() + xs5, y)); + + color_thread = new BlendAlgebraColorThread(plugin, this); + + update_key_sample(); + show_window(); + flush(); +} + +void BlendAlgebraWindow::update_key_sample() +{ + key_sample->set_color (plugin->config.get_key_color()); + key_sample->draw_box (0, 0, key_sample->get_w(), key_sample->get_h()); + key_sample->set_color (BLACK); + key_sample->draw_rectangle (0, 0, key_sample->get_w(), key_sample->get_h()); + key_sample->flash (); +} + +void BlendAlgebraWindow::done_event() +{ + color_thread->close_window(); +} + +int BlendAlgebraWindow::close_event() +{ + color_thread->close_window(); + file_button->stop(); + set_done(1); + return 1; +} + +int BlendAlgebraWindow::hide_window (int flush) +{ + color_thread->close_window(); + file_button->stop(); + return BC_WindowBase::hide_window (flush); +} + +//////////////////////////////////////////// +// Plugin main class implementation +//////////////////////////////////////////// + +BlendAlgebraFunc::BlendAlgebraFunc() +{ + src[0] = 0; + handle = 0; + proc = 0; + init = 0; + tstamp = -1; +} + +BlendAlgebraFunc::~BlendAlgebraFunc() +{ + if (handle) + { +#ifdef DEBUG + printf ("BlendAlgebraFunc destructor detaching function dlclose(%s)\n", + src); +#endif + dlclose (handle); + } +} + +BlendAlgebra::BlendAlgebra(PluginServer *server) + : PluginVClient(server) +{ + BlendAlgebraTstamp = time(NULL); + inspect_configuration = 1; // force initial configuration + curr_func_no = -1; + func_lock = new Mutex("BlendAlgebra::func_lock"); + engine = 0; +#ifdef DEBUG + printf ("BlendAlgebra constructor timestamp %s", ctime(&BlendAlgebraTstamp)); +#endif +} + +BlendAlgebra::~BlendAlgebra() +{ + if (engine) delete engine; + delete func_lock; +#ifdef DEBUG + printf ("BlendAlgebra destructor removing all %d functions\n", + funclist.total); +#endif + funclist.remove_all_objects(); + curr_func.handle = 0; +} + +int BlendAlgebra::process_buffer(VFrame **frame, + int64_t start_position, + double frame_rate) +{ + BlendAlgebraFunc *ptr; + int refresh_eprintf = 0; + + // Mocking up with function shared object if it might get modified + // Not sure if plugin locking is needed for this separate processing instance + func_lock->lock("BlendAlgebra::process_buffer"); + + // First check if function name was changed + if (load_configuration() || inspect_configuration) // function might change + { + inspect_configuration = 0; // do once after change or after creation + if (strcmp (curr_func.src, config.funcname)) // function changed + { + curr_func.src[0] = 0; + curr_func.handle = 0; + curr_func.proc = 0; + curr_func.init = 0; + curr_func.tstamp = -1; + curr_func_no = -1; + if (config.funcname[0]) // function name not empty + { + refresh_eprintf = 1; // probably new function + strcpy (curr_func.src, config.funcname); +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer searching function %s out of %d\n", + curr_func.src, funclist.total); +#endif + for (int i=0; isrc)) // cached function found + { + curr_func.handle = ptr->handle; + curr_func.proc = ptr->proc; + curr_func.init = ptr->init; + curr_func.tstamp = ptr->tstamp; + curr_func_no = i; +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer cached function %s found: %d\n timestamp %s", + ptr->src, curr_func_no, ctime(&ptr->tstamp)); +#endif + break; + } // if cached function found + } // for funclist.total + } // if function name not empty + } // if function changed + } // if load_configuration() + + // Now ensure that function binary is up to date, evtl recompile/relink it + if (curr_func.src[0]) // current function not empty + { + if (curr_func.tstamp == -1 || BlendAlgebraTstamp > curr_func.tstamp) + { // function first seen or linked before last config change + char str[BCTEXTLEN*2], dir[BCTEXTLEN], path[BCTEXTLEN]; + time_t tstamp = -1; + + // Evtl make function path absolute by prepending project path to it + strcpy (path, curr_func.src); + if (path[0] != '/') // path is relative, prepend current project path + { + strcpy (dir, server->mwindow->session->filename); + if (dir[0]) + { + char *cp = strrchr (dir, '/'); + if (cp) + { + cp[1] = 0; // strip project filename off from project path + strcat (dir, path); // concatenate obtained path with function name + strcpy (path, dir); + } + } + } + + // Try to lock function filename against concurrent compiler runs + // This kind of locking seems definitely reasonable here + struct flock locks; + locks.l_whence = SEEK_SET; + locks.l_start = locks.l_len = 0; + int fd = open (path, O_RDWR); + if (fd > -1) // if lock cannot be set, ignore this for now + { + locks.l_type = F_WRLCK; + fcntl (fd, F_SETLKW, &locks); // try to wait for lock, ignoring errors + sprintf(str, "\"%s/dlfcn/BlendAlgebraCompile.pl\" \"%s\"", + getenv("CIN_DAT"), path); +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer\n curr_func.tstamp %s", + ctime(&curr_func.tstamp)); + printf (" global tstamp %s executing %s\n", + ctime(&BlendAlgebraTstamp), str); +#endif + system (str); // evtl recompile function if source newer than object + if (path[0] == '/') sprintf (str, "%s.so", path); + else sprintf (str, "./%s.so", path); // dlopen requires a slash + struct stat statbuf; + if (0 == stat (str, &statbuf)) tstamp = statbuf.st_mtime; + } + else // function source cannot be opened + { +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer cannot access function %s\n", + curr_func.src); +#endif + } + + // Now test if function relinking needed, make function cache consistent + if (tstamp == -1) + { // either function does not exist or compilation unsuccessful + if (fd > -1) + eprintf (_("Blend Algebra: compilation of function %s failed\nsee console printout for diagnostics\n"), + curr_func.src); + if (curr_func_no >= 0) + { // detach old function + if (funclist[curr_func_no]->handle) + { +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer detaching function %d dlclose(%s)\n", + curr_func_no, funclist[curr_func_no]->src); +#endif + dlclose (funclist[curr_func_no]->handle); + } +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer removing function %d (%s)\n", + curr_func_no, curr_func.src); +#endif + funclist[curr_func_no]->src[0] = 0; + funclist[curr_func_no]->handle = 0; + funclist[curr_func_no]->proc = 0; + funclist[curr_func_no]->init = 0; + funclist[curr_func_no]->tstamp = -1; + funclist.remove_object_number (curr_func_no); + curr_func_no = -1; + } + curr_func.handle = 0; + curr_func.proc = 0; + curr_func.init = 0; + curr_func.tstamp = time (NULL); + } + else if (curr_func.tstamp == -1 || tstamp > curr_func.tstamp) + { // function first seen or edited after linkage +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer\n curr_func.tstamp %s", + ctime(&curr_func.tstamp)); + printf (" tstamp %s relinking %s\n", ctime(&tstamp), str); +#endif + if (curr_func_no >= 0 && funclist[curr_func_no]->handle) + { // detach old function +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer detaching function %d dlclose(%s)\n", + curr_func_no, funclist[curr_func_no]->src); +#endif + dlclose (funclist[curr_func_no]->handle); + funclist[curr_func_no]->src[0] = 0; + funclist[curr_func_no]->handle = 0; + funclist[curr_func_no]->proc = 0; + funclist[curr_func_no]->init = 0; + funclist[curr_func_no]->tstamp = -1; + } + curr_func.proc = 0; + curr_func.init = 0; + curr_func.handle = dlopen (str, RTLD_NOW); // shared object handle +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer dlopen(%s)=%p\n", + str, curr_func.handle); +#endif + if (curr_func.handle) // inquire necessary extern entry points + { // baProc is mandatory, baInit optional + curr_func.init = (BAF_init) dlsym (curr_func.handle, "baInit"); + if (curr_func.init == NULL) // not a problem, we can continue + printf (_("Blend Algebra: optional entry point \"baInit\" for function %s not found:\n%s\n"), + str, dlerror()); + curr_func.proc = (BAF_proc) dlsym (curr_func.handle, "baProc"); +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer dlsym(%s) init=%p proc=%p\n", + curr_func.src, curr_func.init, curr_func.proc); +#endif + if (curr_func.proc == NULL) // nothing to do if this not working + { + eprintf (_("Blend Algebra: entry point \"baProc\" for function %s not found:\n%s\n"), + str, dlerror()); +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer dlclose(%s)\n", + curr_func.src); +#endif + dlclose (curr_func.handle); + curr_func.handle = 0; + curr_func.init = 0; + } + } + else + eprintf (_("Blend Algebra: dynamic load of function %s failed:\n%s\n"), + str, dlerror()); + curr_func.tstamp = time (NULL); + if (curr_func_no >= 0) // function was already in cache + { + if (curr_func.proc) // update object in cache + { + strcpy (funclist[curr_func_no]->src, curr_func.src); + funclist[curr_func_no]->handle = curr_func.handle; + funclist[curr_func_no]->proc = curr_func.proc; + funclist[curr_func_no]->init = curr_func.init; + funclist[curr_func_no]->tstamp = curr_func.tstamp; +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer function %d (%s) updated\n timestamp %s", + curr_func_no, curr_func.src, ctime(&curr_func.tstamp)); +#endif + } + else // remove outdated function + { +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer removing function %d (%s)\n", + curr_func_no, curr_func.src); +#endif + funclist.remove_object_number (curr_func_no); + curr_func_no = -1; + } + } + else if (curr_func.proc) // add new linked function to cache + { + curr_func_no = funclist.total; + ptr = new BlendAlgebraFunc; + funclist.append (ptr); + strcpy (ptr->src, curr_func.src); + ptr->handle = curr_func.handle; + ptr->proc = curr_func.proc; + ptr->init = curr_func.init; + ptr->tstamp = curr_func.tstamp; +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer function %d (%s) appended\n timestamp %s", + curr_func_no, ptr->src, ctime(&ptr->tstamp)); +#endif + } // if function in cache + } + else // function does not need relinking + { + curr_func.tstamp = time (NULL); + if (curr_func_no >= 0) // just update timestamp + funclist[curr_func_no]->tstamp = curr_func.tstamp; +#ifdef DEBUG + printf ("BlendAlgebra::process_buffer function %s does not need relinking\n cache number %d timestamp %s", + curr_func.src, curr_func_no, ctime(&curr_func.tstamp)); +#endif + } // if tstamp + + if (fd > -1) // unlock function + { + locks.l_type = F_UNLCK; + fcntl (fd, F_SETLK, &locks); + close (fd); + } + } // if function first seen or changed + } // if current function not empty + + func_lock->unlock(); // end mocking up with function shared object + + // Now prepare the important pars and read all involved frames... + layers = get_total_buffers(); + width = frame[0]->get_w(); + height = frame[0]->get_h(); + for (int l=0; lframe = frame; + + if (curr_func.proc == NULL) return 0; // no function, nothing to do + + color_proj = frame[0]->get_color_model(); // internal colorspace of project + if (color_proj == BC_RGBA_FLOAT || + color_proj == BC_RGBA8888 || + color_proj == BC_YUVA8888) + has_alpha = 1; // has alpha channel + else has_alpha = 0; + color_work = config.colorspace; // will be requested from the function + int color_arg = color_work; + int min_layers = layers; // function's min required no of tracks + int parallel = 0; // assumed not parallelized by default + if (curr_func.init != NULL) // ask function about important pars + curr_func.init (&color_arg, color_proj, &min_layers, layers, + ¶llel, config.parallel, width, height, has_alpha); + if (min_layers > layers) + { + if (refresh_eprintf) + eprintf (_("Blend Algebra: cannot execute function %s:\nrequires %d tracks to process, has only %d tracks\n"), + curr_func.src, min_layers, layers); + return 0; // too few tracks to do anything + } + if (color_work == BlendAlgebraConfig::AUTO) color_work = color_arg; + if (color_work == BlendAlgebraConfig::AUTO) + color_work = BlendAlgebraConfig::PROJECT; // still not defined, dont change + if (! config.parallel) parallel = 0; // parallelism not requested + + // In case of a fatal bug in the user defined function (SIGFPE, SIGSEGV,...) + // Cinelerra perhaps will crash. Unfortunately we cannot handle the signals + // here: signal handler as set by sigaction() is process-wide, the same + // for all threads. And Cinelerra already uses its own signal handler + // for debug purposes which we are not allowed to overwrite with our one. + // Infinities and NaN will be trapped and substituted with configured color. + // Here we prepare key color components in three possible color spaces + // from this configured color. Used to substitute NaN or infinities. + // Can be used also inside user's function like a chroma key. + rgb_r = config.red; // user's configured color is always RGB + rgb_g = config.green; + rgb_b = config.blue; + YUV::yuv.rgb_to_yuv_f (rgb_r, rgb_g, rgb_b, yuv_y, yuv_u, yuv_v); // make YUV + HSV::rgb_to_hsv (rgb_r, rgb_g, rgb_b, hsv_h, hsv_s, hsv_v); // make HSV + key_a = config.alpha; // user's configured alpha + + if (parallel) // parallelism desired, and supoported by the function + { + if (! engine) + engine = new BlendAlgebraEngine (this, + get_project_smp() + 1, + get_project_smp() + 1); + engine->process_packages(); + } + else process_frames (0, height); // process everything sequential + + return 0; // WHEW !!! +} + +// Now comes the whole math. User's function will get everything in float. +// If the project's color model is 8-bit, pixels will be converted to float. +// Then, if requested, pixels will be converted to working color space +// (RGB, YUV, or HSV) which is required by the function. After processing, +// all the conversions will be rolled back in the reverse order for the result. +// This universal function is called via loadbalance multithreading engine +// as well as directly if parallelism not requested or not supported + +void BlendAlgebra::process_frames (int y1, int y2) +{ + float r[layers], g[layers], b[layers], a[layers]; + float rk, gk, bk, yk, uk, vk, out_r, out_g, out_b, out_a; + int k, l, start, step, arg_out, trk_out; + + // start, step define function argument indices relative to track numbers + // trk_out defines result index relative to track numbers + // arg_out defines result index relative to function args (start, step) + // arg_out used before function call to preinitialize future result + // trk_out used after function call to place result into the right track + // if function does not set the result, output track stays unmodified + if (config.direction == BlendAlgebraConfig::BOTTOM_FIRST) + { + start = layers-1; + step = -1; + if (config.output_track == BlendAlgebraConfig::TOP) arg_out = layers-1; + else arg_out = 0; + } + else + { + start = 0; + step = 1; + if (config.output_track == BlendAlgebraConfig::TOP) arg_out = 0; + else arg_out = layers-1; + } + if (config.output_track == BlendAlgebraConfig::TOP) trk_out = 0; + else trk_out = layers-1; + + int clip_colors = config.clipcolors; // clipping floats is optional + yk = uk = vk = 0; // to make gcc -O2 happy + + switch (color_proj) + { + case BC_RGB_FLOAT: // RGB [ 0 .. 1 ], out of bounds possible + case BC_RGBA_FLOAT: // RGBA [ 0 .. 1 ], out of bounds possible + for (int i=y1; iget_rows()[i]; + for (int j=0; j= 360)) + out_r -= floor(out_r/360)*360; // cannot clamp infinity here + CLAMP (out_g, 0, 1); + CLAMP (out_b, 0, 1); + } + if (! (isfinite(out_r) && isfinite(out_g) && + isfinite(out_b) && isfinite(out_a))) + { // substitute NaN or unclipped infinity with user's color (HSV) + out_r = hsv_h; + out_g = hsv_s; + out_b = hsv_v; + out_a = key_a; + } + HSV::hsv_to_rgb (row[trk_out][0], row[trk_out][1], row[trk_out][2], + out_r, // H + out_g, // S + out_b); // V + } + else // either RGB or by PROJECT, no change, clip only + { + if (clip_colors) + { + CLAMP (out_r, 0, 1); + CLAMP (out_g, 0, 1); + CLAMP (out_b, 0, 1); + } + if (! (isfinite(out_r) && isfinite(out_g) && + isfinite(out_b) && isfinite(out_a))) + { // substitute NaN or unclipped infinity with user's color (RGB) + out_r = rgb_r; + out_g = rgb_g; + out_b = rgb_b; + out_a = key_a; + } + row[trk_out][0] = out_r; + row[trk_out][1] = out_g; + row[trk_out][2] = out_b; + } // if color_work + if (! has_alpha) // evtl simulate alpha channel + { + row[trk_out][0] *= out_a; + row[trk_out][1] *= out_a; + row[trk_out][2] *= out_a; + } // store real alpha channel + if (has_alpha) row[trk_out][3] = out_a; + for (l=0; lget_rows()[i]; + for (int j=0; j= 360)) + out_r -= floor(out_r/360)*360; // cannot clamp infinity here + CLAMP (out_g, 0, 1); + CLAMP (out_b, 0, 1); + } + if (! (isfinite(out_r) && isfinite(out_g) && + isfinite(out_b) && isfinite(out_a))) + { // substitute NaN or unclipped infinity with user's color (HSV) + out_r = hsv_h; + out_g = hsv_s; + out_b = hsv_v; + out_a = key_a; + } + HSV::hsv_to_rgb (rk, gk, bk, + out_r, // H + out_g, // S + out_b); // V + } + else // either RGB or by PROJECT, no change, clip only + { + if (clip_colors) + { + CLAMP (out_r, 0, 1); + CLAMP (out_g, 0, 1); + CLAMP (out_b, 0, 1); + } + if (! (isfinite(out_r) && isfinite(out_g) && + isfinite(out_b) && isfinite(out_a))) + { // substitute NaN or unclipped infinity with user's color (RGB) + out_r = rgb_r; + out_g = rgb_g; + out_b = rgb_b; + out_a = key_a; + } + rk = out_r; + gk = out_g; + bk = out_b; + } // if color_work + if (! has_alpha) // evtl simulate alpha channel + { + rk *= out_a; + gk *= out_a; + bk *= out_a; + } + row[trk_out][0] = (unsigned char) CLIP (rk*255, 0, 255);//reformat/clip + row[trk_out][1] = (unsigned char) CLIP (gk*255, 0, 255); + row[trk_out][2] = (unsigned char) CLIP (bk*255, 0, 255); + if (has_alpha) // store real alpha channel + row[trk_out][3] = (unsigned char) CLIP (out_a*255, 0, 255); + for (l=0; lget_rows()[i]; + for (int j=0; j= 360)) + out_r -= floor(out_r/360)*360; // cannot clamp infinity here + CLAMP (out_g, 0, 1); + CLAMP (out_b, 0, 1); + } + if (! (isfinite(out_r) && isfinite(out_g) && + isfinite(out_b) && isfinite(out_a))) + { // substitute NaN or unclipped infinity with user's color (HSV) + out_r = hsv_h; + out_g = hsv_s; + out_b = hsv_v; + out_a = key_a; + } // H S V + HSV::hsv_to_rgb (rk, gk, bk, out_r, out_g, out_b); + if (clip_colors) + { + CLAMP (out_r, 0, 1); + CLAMP (out_g, 0, 1); + CLAMP (out_b, 0, 1); + } + YUV::yuv.rgb_to_yuv_f (rk, gk, bk, yk, uk, vk); + } + else // either YUV or by PROJECT, no change, clip only + { + if (clip_colors) + { + CLAMP (out_r, 0, 1); + CLAMP (out_g, -0.5, 0.5); + CLAMP (out_b, -0.5, 0.5); + } + if (! (isfinite(out_r) && isfinite(out_g) && + isfinite(out_b) && isfinite(out_a))) + { // substitute NaN or unclipped infinity with user's color (YUV) + out_r = yuv_y; + out_g = yuv_u; + out_b = yuv_v; + out_a = key_a; + } + yk = out_r; + uk = out_g; + vk = out_b; + } // if color_work + if (! has_alpha) // evtl simulate alpha channel + { + yk *= out_a; + uk *= out_a; + vk *= out_a; + } + row[trk_out][0] = (unsigned char) CLIP (yk*255, 0, 255); + row[trk_out][1] = (unsigned char) CLIP ((uk+0.5)*256, 0, 255); + row[trk_out][2] = (unsigned char) CLIP ((vk+0.5)*256, 0, 255); + if (has_alpha) // store real alpha channel + row[trk_out][3] = (unsigned char) CLIP (out_a*255, 0, 255); + for (l=0; lxbuf); + + output.tag.set_title("BLEND_ALGEBRA"); + output.tag.set_property("FUNCNAME", config.funcname); + output.tag.set_property("PARALLEL", config.parallel); + output.tag.set_property("DIRECTION", config.direction); + output.tag.set_property("OUTPUT_TRACK", config.output_track); + output.tag.set_property("COLORSPACE", config.colorspace); + output.tag.set_property("CLIPCOLORS", config.clipcolors); + output.tag.set_property("CLEAR_INPUT", config.clear_input); + 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("ALPHA", config.alpha); + output.append_tag(); + output.tag.set_title("/BLEND_ALGEBRA"); + output.append_tag(); + output.append_newline(); + output.terminate_string(); +} + +void BlendAlgebra::read_data(KeyFrame *keyframe) +{ + FileXML input; + + input.set_shared_input(keyframe->xbuf); + + while(!input.read_tag()) + { + if(input.tag.title_is("BLEND_ALGEBRA")) + { + input.tag.get_property("FUNCNAME", config.funcname); + config.parallel = input.tag.get_property("PARALLEL", config.parallel); + config.direction = input.tag.get_property("DIRECTION", config.direction); + config.output_track = + input.tag.get_property("OUTPUT_TRACK", config.output_track); + config.colorspace = + input.tag.get_property("COLORSPACE", config.colorspace); + config.clipcolors = + input.tag.get_property("CLIPCOLORS", config.clipcolors); + config.clear_input = + input.tag.get_property("CLEAR_INPUT", config.clear_input); + 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.alpha = input.tag.get_property("ALPHA", config.alpha); + } + } +} + +void BlendAlgebra::update_gui() +{ + if( ! thread ) return; + if( ! (load_configuration() || inspect_configuration) ) return; + inspect_configuration = 0; // update once after change or after creation + thread->window->lock_window("BlendAlgebra::update_gui"); + BlendAlgebraWindow *window = (BlendAlgebraWindow*)thread->window; + + window->funcname->update(config.funcname); + window->parallel->update(config.parallel); + window->clipcolors->update(config.clipcolors); + window->clear_input->update(config.clear_input); + window->direction->set_text( + BlendAlgebraConfig::direction_to_text(config.direction)); + window->output->set_text( + BlendAlgebraConfig::output_to_text(config.output_track)); + window->colorspace->set_text( + BlendAlgebraConfig::colorspace_to_text(config.colorspace)); + window->update_key_sample(); + window->alpha_text->update(config.alpha); + window->key_alpha->update(config.alpha); + + thread->window->unlock_window(); +} + +//////////////////////////////////////////// +// Multithreaded processing stuff +//////////////////////////////////////////// + +BlendAlgebraEngine::BlendAlgebraEngine(BlendAlgebra *plugin, + int total_clients, + int total_packages) + : LoadServer(total_clients, total_packages) +{ + this->plugin = plugin; +} + +void BlendAlgebraEngine::init_packages () +{ + for (int i=0; iy1 = plugin->height * i / get_total_packages (); + pkg->y2 = plugin->height * (i + 1) / get_total_packages (); + } +} + +LoadClient *BlendAlgebraEngine::new_client () +{ + return new BlendAlgebraUnit (plugin, this); +} + +LoadPackage *BlendAlgebraEngine::new_package () +{ + return new BlendAlgebraPackage; +} + +BlendAlgebraPackage::BlendAlgebraPackage() + : LoadPackage() +{ +} + +BlendAlgebraUnit::BlendAlgebraUnit (BlendAlgebra *plugin, + BlendAlgebraEngine *engine) + : LoadClient (engine) +{ + this->plugin = plugin; + this->engine = engine; +} + +void BlendAlgebraUnit::process_package(LoadPackage *package) +{ + BlendAlgebraPackage *pkg = (BlendAlgebraPackage *) package; + + plugin->process_frames (pkg->y1, pkg->y2); +} diff --git a/cinelerra-5.1/plugins/blendalgebra/blendalgebra.h b/cinelerra-5.1/plugins/blendalgebra/blendalgebra.h new file mode 100644 index 00000000..78b90546 --- /dev/null +++ b/cinelerra-5.1/plugins/blendalgebra/blendalgebra.h @@ -0,0 +1,463 @@ +/* + * CINELERRA + * Copyright (C) 2008 Adam Williams + * + * 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 BLENDALGEBRA_H +#define BLENDALGEBRA_H + +#include "guicast.h" +#include "loadbalance.h" +#include "colorpicker.h" +#include "pluginvclient.h" + +// Several forward declarations + +class BlendAlgebra; +class BlendAlgebraConfig; +class BlendAlgebraWindow; +class BlendAlgebraAlphaText; +class BlendAlgebraAlphaSlider; +class BlendAlgebraFileBox; + +// Plugin configuration class definition + +class BlendAlgebraConfig +{ +public: + BlendAlgebraConfig(); + + int equivalent(BlendAlgebraConfig &that); + void copy_from(BlendAlgebraConfig &that); + void interpolate(BlendAlgebraConfig &prev, + BlendAlgebraConfig &next, + int64_t prev_frame, + int64_t next_frame, + int64_t current_frame); + + int get_key_color(); + + char funcname[BCTEXTLEN]; + int parallel; + int clipcolors; + int clear_input; + + static const char *direction_to_text(int direction); + int direction; + enum + { + BOTTOM_FIRST, + TOP_FIRST + }; + + static const char *output_to_text(int output_track); + int output_track; + enum + { + TOP, + BOTTOM + }; + + static const char *colorspace_to_text(int colorspace); + int colorspace; + enum + { + AUTO, // requested from function + RGB, + YUV, + HSV, + PROJECT // as defined in project settings + }; + + float red; // key color to substitute for NaN + float green; + float blue; + float alpha; // alpha to substitute for NaN +}; + +// Plugin dialog window class definition + +class BlendAlgebraFuncname : public BC_TextBox +{ +public: + BlendAlgebraFuncname(BlendAlgebra *plugin, const char *funcname, + BlendAlgebraWindow *gui, int x, int y); + int handle_event(); + BlendAlgebra *plugin; + BlendAlgebraWindow *gui; +}; + +class BlendAlgebraDetach : public BC_GenericButton +{ +public: + BlendAlgebraDetach(BlendAlgebra *plugin, BlendAlgebraWindow *gui, + int x, int y); + int handle_event(); + BlendAlgebra *plugin; + BlendAlgebraWindow *gui; +}; + +class BlendAlgebraEdit : public BC_GenericButton +{ +public: + BlendAlgebraEdit(BlendAlgebra *plugin, BlendAlgebraWindow *gui, + int x, int y); + int handle_event(); + BlendAlgebra *plugin; + BlendAlgebraWindow *gui; +}; + +class BlendAlgebraRefresh : public BC_GenericButton +{ +public: + BlendAlgebraRefresh(BlendAlgebra *plugin, BlendAlgebraWindow *gui, + int x, int y); + int handle_event(); + BlendAlgebra *plugin; + BlendAlgebraWindow *gui; +}; + +class BlendAlgebraToCurdir : public BC_GenericButton +{ +public: + BlendAlgebraToCurdir(BlendAlgebraFileBox *file_box, int x, int y); + int handle_event(); + BlendAlgebraFileBox *file_box; +}; + +class BlendAlgebraToUsrlib : public BC_GenericButton +{ +public: + BlendAlgebraToUsrlib(BlendAlgebraFileBox *file_box, int x, int y); + int handle_event(); + BlendAlgebraFileBox *file_box; +}; + +class BlendAlgebraToSyslib : public BC_GenericButton +{ +public: + BlendAlgebraToSyslib(BlendAlgebraFileBox *file_box, int x, int y); + int handle_event(); + BlendAlgebraFileBox *file_box; +}; + +class BlendAlgebraCopyCurdir : public BC_GenericButton +{ +public: + BlendAlgebraCopyCurdir(BlendAlgebraFileBox *file_box, int x, int y); + int handle_event(); + BlendAlgebraFileBox *file_box; +}; + +class BlendAlgebraCopyUsrlib : public BC_GenericButton +{ +public: + BlendAlgebraCopyUsrlib(BlendAlgebraFileBox *file_box, int x, int y); + int handle_event(); + BlendAlgebraFileBox *file_box; +}; + +class BlendAlgebraFileEdit : public BC_GenericButton +{ +public: + BlendAlgebraFileEdit(BlendAlgebraFileBox *file_box, int x, int y); + int handle_event(); + BlendAlgebraFileBox *file_box; +}; + +class BlendAlgebraFileBox : public BC_FileBox +{ +public: + BlendAlgebraFileBox(BlendAlgebra *plugin, BlendAlgebraWindow *gui, + char *init_path); + ~BlendAlgebraFileBox(); + void add_objects(); + int resize_event(int w, int h); + + BlendAlgebra *plugin; + BlendAlgebraWindow *gui; + BlendAlgebraToCurdir *to_curdir; + BlendAlgebraToUsrlib *to_usrlib; + BlendAlgebraToSyslib *to_syslib; + BlendAlgebraCopyCurdir *copy_curdir; + BlendAlgebraCopyUsrlib *copy_usrlib; + BlendAlgebraFileEdit *file_edit; + + int reinit_path; +}; + +class BlendAlgebraFileButton : public BC_GenericButton, public Thread +{ +public: + BlendAlgebraFileButton(BlendAlgebra *plugin, BlendAlgebraWindow *gui, + int x, int y); + ~BlendAlgebraFileButton(); + int handle_event(); + void run(); + void stop(); + BlendAlgebra *plugin; + BlendAlgebraWindow *gui; + BlendAlgebraFileBox *file_box; +}; + +class BlendAlgebraDirection : public BC_PopupMenu +{ +public: + BlendAlgebraDirection(BlendAlgebra *plugin, int x, int y); + void create_objects(); + int handle_event(); + BlendAlgebra *plugin; +}; + +class BlendAlgebraOutput : public BC_PopupMenu +{ +public: + BlendAlgebraOutput(BlendAlgebra *plugin, int x, int y); + void create_objects(); + int handle_event(); + BlendAlgebra *plugin; +}; + +class BlendAlgebraColorspace : public BC_PopupMenu +{ +public: + BlendAlgebraColorspace(BlendAlgebra *plugin, int x, int y); + void create_objects(); + int handle_event(); + BlendAlgebra *plugin; +}; + +class BlendAlgebraClipcolors : public BC_CheckBox +{ +public: + BlendAlgebraClipcolors(BlendAlgebra *plugin, BlendAlgebraWindow *gui, + int x, int y); + int handle_event(); + BlendAlgebra *plugin; + BlendAlgebraWindow *gui; +}; + +class BlendAlgebraParallel : public BC_CheckBox +{ +public: + BlendAlgebraParallel(BlendAlgebra *plugin, BlendAlgebraWindow *gui, + int x, int y); + int handle_event(); + BlendAlgebra *plugin; + BlendAlgebraWindow *gui; +}; + +class BlendAlgebraClearInput : public BC_CheckBox +{ +public: + BlendAlgebraClearInput(BlendAlgebra *plugin, BlendAlgebraWindow *gui, + int x, int y); + int handle_event(); + BlendAlgebra *plugin; + BlendAlgebraWindow *gui; +}; + +class BlendAlgebraKeyColor : public BC_GenericButton +{ +public: + BlendAlgebraKeyColor(BlendAlgebra *plugin, BlendAlgebraWindow *gui, + int x, int y); + int handle_event(); + BlendAlgebra *plugin; + BlendAlgebraWindow *gui; +}; + +class BlendAlgebraColorPicker : public BC_GenericButton +{ +public: + BlendAlgebraColorPicker(BlendAlgebra *plugin, BlendAlgebraWindow *gui, + int x, int y); + int handle_event(); + BlendAlgebra *plugin; + BlendAlgebraWindow *gui; +}; + +class BlendAlgebraColorThread : public ColorPicker +{ +public: + BlendAlgebraColorThread(BlendAlgebra *plugin, BlendAlgebraWindow *gui); + int handle_new_color(int output, int alpha); + BlendAlgebra *plugin; + BlendAlgebraWindow *gui; +}; + +class BlendAlgebraAlphaText : public BC_TumbleTextBox +{ +public: + BlendAlgebraAlphaText(BlendAlgebra *plugin, BlendAlgebraWindow *gui, + BlendAlgebraAlphaSlider *slider, int x, int y, + float min, float max, float *output); + ~BlendAlgebraAlphaText(); + int handle_event(); + BlendAlgebra *plugin; + BlendAlgebraWindow *gui; + BlendAlgebraAlphaSlider *slider; + float *output; + float min, max; +}; + +class BlendAlgebraAlphaSlider : public BC_FSlider +{ +public: + BlendAlgebraAlphaSlider(BlendAlgebra *plugin, BlendAlgebraAlphaText *text, + int x, int y, int w, float min, float max, + float *output); + ~BlendAlgebraAlphaSlider(); + int handle_event(); + BlendAlgebra *plugin; + BlendAlgebraAlphaText *text; + float *output; +}; + +class BlendAlgebraWindow : public PluginClientWindow +{ +public: + BlendAlgebraWindow(BlendAlgebra *plugin); + ~BlendAlgebraWindow(); + + void create_objects(); + void update_key_sample(); + void done_event(); + int close_event(); + int hide_window(int flush=1); + + BlendAlgebra *plugin; + BlendAlgebraFuncname *funcname; + BlendAlgebraDirection *direction; + BlendAlgebraOutput *output; + BlendAlgebraColorspace *colorspace; + BlendAlgebraClipcolors *clipcolors; + BlendAlgebraParallel *parallel; + BlendAlgebraClearInput *clear_input; + BC_SubWindow *key_sample; + BlendAlgebraKeyColor *key_color; + BlendAlgebraColorPicker *color_picker; + BlendAlgebraColorThread *color_thread; + BlendAlgebraAlphaText *alpha_text; + BlendAlgebraAlphaSlider *key_alpha; + BlendAlgebraFileButton *file_button; + BlendAlgebraDetach *detach_button; + BlendAlgebraEdit *edit_button; + BlendAlgebraRefresh *refresh_button; + + Mutex *editing_lock; + int editing; +}; + +// For multithreading processing engine + +class BlendAlgebraEngine : public LoadServer +{ +public: + BlendAlgebraEngine(BlendAlgebra *plugin, + int total_clients, int total_packages); + void init_packages(); + LoadClient* new_client(); + LoadPackage* new_package(); + + BlendAlgebra *plugin; +}; + +class BlendAlgebraPackage : public LoadPackage +{ +public: + BlendAlgebraPackage(); + + int y1, y2; +}; + +class BlendAlgebraUnit : public LoadClient +{ +public: + BlendAlgebraUnit(BlendAlgebra *plugin, BlendAlgebraEngine *engine); + void process_package(LoadPackage *package); + + BlendAlgebra *plugin; + BlendAlgebraEngine *engine; +}; + +// Plugin main class definition + +// User function prototypes, C style, processing and init entries +typedef void (*BAF_proc) (int, + float *, float *, float *, float *, + float, float, float, float, + float *, float *, float *, float *, + int, int, int, int, int); +typedef void (*BAF_init) (int *, int, int *, int, int *, int, int, int, int); + +class BlendAlgebraFunc +{ +public: + BlendAlgebraFunc(); + ~BlendAlgebraFunc(); + + char src[BCTEXTLEN]; // source filename + void *handle; // handle returned from dlopen() + BAF_proc proc; // main processing entry from dlsym() + BAF_init init; // optional initializing entry from dlsym() + time_t tstamp; // timestamp when function was last linked +}; + +class BlendAlgebra : public PluginVClient +{ +public: + BlendAlgebra(PluginServer *server); + ~BlendAlgebra(); + + PLUGIN_CLASS_MEMBERS(BlendAlgebraConfig); + + int process_buffer(VFrame **frame, int64_t start_position, double frame_rate); + int is_realtime(); + int is_multichannel(); + int is_synthesis(); + void save_data(KeyFrame *keyframe); + void read_data(KeyFrame *keyframe); + void update_gui(); + void process_frames(int y1, int y2); + + // this flag set in constructor, cleared after first load_configuration() + int inspect_configuration; + + int layers; // no of tracks + int width, height; // frame dimensions + int color_proj; // project color model + int color_work; // working color space in the function + int has_alpha; // 1 == has alpha channel + + // color components in three color spaces, used to substitute + // for NaN or infinity in invalid results, or as a chroma key + float rgb_r, rgb_g, rgb_b, yuv_y, yuv_u, yuv_v, hsv_h, hsv_s, hsv_v, key_a; + + BlendAlgebraFunc curr_func; // currently active entry point + int curr_func_no; // no of current entry in list + ArrayList funclist; // list of known entry points + + VFrame **frame; // pointer to frames to process + + Mutex *func_lock; + + BlendAlgebraEngine *engine; // for parallelized processing +}; + +#endif /* BLENDALGEBRA_H */ diff --git a/cinelerra-5.1/plugins/blendalgebra/graphart_burn.ba b/cinelerra-5.1/plugins/blendalgebra/graphart_burn.ba new file mode 100644 index 00000000..bfc7df8e --- /dev/null +++ b/cinelerra-5.1/plugins/blendalgebra/graphart_burn.ba @@ -0,0 +1,29 @@ +/***********************************************-*-C-*-**********/ +/* Overlay mode: Graphic Art Burn */ + +BLEND_ALGEBRA_INIT + +COLORSPACE_RGB +PARALLEL_SAFE +REQUIRE_TRACKS(2) + +BLEND_ALGEBRA_PROC + +#define s 1 +#define d 0 + +R_OUT = R(s) * (1-A(d)) + R(d) * (1-A(s)); +if (R(s) > 0 && R(s) * A(d) + R(d) * A(s) > A(s) * A(d)) + R_OUT += (R(s) * A(d) + R(d) * A(s) - A(s) * A(d)) * A(s) / R(s); + +G_OUT = G(s) * (1-A(d)) + G(d) * (1-A(s)); +if (G(s) > 0 && G(s) * A(d) + G(d) * A(s) > A(s) * A(d)) + G_OUT += (G(s) * A(d) + G(d) * A(s) - A(s) * A(d)) * A(s) / G(s); + +B_OUT = B(s) * (1-A(d)) + B(d) * (1-A(s)); +if (B(s) > 0 && B(s) * A(d) + B(d) * A(s) > A(s) * A(d)) + B_OUT += (B(s) * A(d) + B(d) * A(s) - A(s) * A(d)) * A(s) / B(s); + +A_OUT = A(s) + A(d) - A(s) * A(d); + +BLEND_ALGEBRA_END diff --git a/cinelerra-5.1/plugins/blendalgebra/graphart_difference.ba b/cinelerra-5.1/plugins/blendalgebra/graphart_difference.ba new file mode 100644 index 00000000..2c678e25 --- /dev/null +++ b/cinelerra-5.1/plugins/blendalgebra/graphart_difference.ba @@ -0,0 +1,20 @@ +/***********************************************-*-C-*-**********/ +/* Overlay mode: Graphic Art Difference */ + +BLEND_ALGEBRA_INIT + +COLORSPACE_RGB +PARALLEL_SAFE +REQUIRE_TRACKS(2) + +BLEND_ALGEBRA_PROC + +#define s 1 +#define d 0 + +R_OUT = R(s) * (1-A(d)) + R(d) * (1-A(s)) + ABS (R(s) * A(d) - R(d) * A(s)); +G_OUT = G(s) * (1-A(d)) + G(d) * (1-A(s)) + ABS (G(s) * A(d) - G(d) * A(s)); +B_OUT = B(s) * (1-A(d)) + B(d) * (1-A(s)) + ABS (B(s) * A(d) - B(d) * A(s)); +A_OUT = A(s) + A(d) - A(s) * A(d); + +BLEND_ALGEBRA_END diff --git a/cinelerra-5.1/plugins/blendalgebra/graphart_dodge.ba b/cinelerra-5.1/plugins/blendalgebra/graphart_dodge.ba new file mode 100644 index 00000000..2dfe5191 --- /dev/null +++ b/cinelerra-5.1/plugins/blendalgebra/graphart_dodge.ba @@ -0,0 +1,35 @@ +/***********************************************-*-C-*-**********/ +/* Overlay mode: Graphic Art Dodge */ + +BLEND_ALGEBRA_INIT + +COLORSPACE_RGB +PARALLEL_SAFE +REQUIRE_TRACKS(2) + +BLEND_ALGEBRA_PROC + +#define s 1 +#define d 0 + +R_OUT = R(s) * (1-A(d)) + R(d) * (1-A(s)); +if (A(s) <= R(s) || R(s) * A(d) + R(d) * A(s) >= A(s) * A(d)) + R_OUT += A(s) * A(d); +else + R_OUT += R(d) * A(s) / (1 - R(s)/A(s)); + +G_OUT = G(s) * (1-A(d)) + G(d) * (1-A(s)); +if (A(s) <= G(s) || G(s) * A(d) + G(d) * A(s) >= A(s) * A(d)) + G_OUT += A(s) * A(d); +else + G_OUT += G(d) * A(s) / (1 - G(s)/A(s)); + +B_OUT = B(s) * (1-A(d)) + B(d) * (1-A(s)); +if (A(s) <= B(s) || B(s) * A(d) + B(d) * A(s) >= A(s) * A(d)) + B_OUT += A(s) * A(d); +else + B_OUT += B(d) * A(s) / (1 - B(s)/A(s)); + +A_OUT = A(s) + A(d) - A(s) * A(d); + +BLEND_ALGEBRA_END diff --git a/cinelerra-5.1/plugins/blendalgebra/graphart_hardlight.ba b/cinelerra-5.1/plugins/blendalgebra/graphart_hardlight.ba new file mode 100644 index 00000000..c0cabbb6 --- /dev/null +++ b/cinelerra-5.1/plugins/blendalgebra/graphart_hardlight.ba @@ -0,0 +1,29 @@ +/***********************************************-*-C-*-**********/ +/* Overlay mode: Graphic Art Hardlight */ + +BLEND_ALGEBRA_INIT + +COLORSPACE_RGB +PARALLEL_SAFE +REQUIRE_TRACKS(2) + +BLEND_ALGEBRA_PROC + +#define s 1 +#define d 0 + +R_OUT = R(s) * (1-A(d)) + R(d) * (1-A(s)); +if (2 * R(s) < A(s)) R_OUT += 2 * R(s) * R(d); +else R_OUT += A(s) * A(d) - 2 * (A(d)-R(d)) * (A(s)-R(s)); + +G_OUT = G(s) * (1-A(d)) + G(d) * (1-A(s)); +if (2 * G(s) < A(s)) G_OUT += 2 * G(s) * G(d); +else G_OUT += A(s) * A(d) - 2 * (A(d)-G(d)) * (A(s)-G(s)); + +B_OUT = B(s) * (1-A(d)) + B(d) * (1-A(s)); +if (2 * B(s) < A(s)) B_OUT += 2 * B(s) * B(d); +else B_OUT += A(s) * A(d) - 2 * (A(d)-B(d)) * (A(s)-B(s)); + +A_OUT = A(s) + A(d) - A(s) * A(d); + +BLEND_ALGEBRA_END diff --git a/cinelerra-5.1/plugins/blendalgebra/graphart_overlay.ba b/cinelerra-5.1/plugins/blendalgebra/graphart_overlay.ba new file mode 100644 index 00000000..4228f220 --- /dev/null +++ b/cinelerra-5.1/plugins/blendalgebra/graphart_overlay.ba @@ -0,0 +1,29 @@ +/***********************************************-*-C-*-**********/ +/* Overlay mode: Graphic Art Overlay */ + +BLEND_ALGEBRA_INIT + +COLORSPACE_RGB +PARALLEL_SAFE +REQUIRE_TRACKS(2) + +BLEND_ALGEBRA_PROC + +#define s 1 +#define d 0 + +R_OUT = R(s) * (1-A(d)) + R(d) * (1-A(s)); +if (2 * R(d) < A(d)) R_OUT += 2 * R(s) * R(d); +else R_OUT += A(s) * A(d) - 2 * (A(d)-R(d)) * (A(s)-R(s)); + +G_OUT = G(s) * (1-A(d)) + G(d) * (1-A(s)); +if (2 * G(d) < A(d)) G_OUT += 2 * G(s) * G(d); +else G_OUT += A(s) * A(d) - 2 * (A(d)-G(d)) * (A(s)-G(s)); + +B_OUT = B(s) * (1-A(d)) + B(d) * (1-A(s)); +if (2 * B(d) < A(d)) B_OUT += 2 * B(s) * B(d); +else B_OUT += A(s) * A(d) - 2 * (A(d)-B(d)) * (A(s)-B(s)); + +A_OUT = A(s) + A(d) - A(s) * A(d); + +BLEND_ALGEBRA_END diff --git a/cinelerra-5.1/plugins/blendalgebra/graphart_screen.ba b/cinelerra-5.1/plugins/blendalgebra/graphart_screen.ba new file mode 100644 index 00000000..86cba9d6 --- /dev/null +++ b/cinelerra-5.1/plugins/blendalgebra/graphart_screen.ba @@ -0,0 +1,20 @@ +/***********************************************-*-C-*-**********/ +/* Overlay mode: Graphic Art Screen */ + +BLEND_ALGEBRA_INIT + +COLORSPACE_RGB +PARALLEL_SAFE +REQUIRE_TRACKS(2) + +BLEND_ALGEBRA_PROC + +#define s 1 +#define d 0 + +R_OUT = R(s) + R(d) - R(s) * R(d); +G_OUT = G(s) + G(d) - G(s) * G(d); +B_OUT = B(s) + B(d) - B(s) * B(d); +A_OUT = A(s) + A(d) - A(s) * A(d); + +BLEND_ALGEBRA_END diff --git a/cinelerra-5.1/plugins/blendalgebra/graphart_softlight.ba b/cinelerra-5.1/plugins/blendalgebra/graphart_softlight.ba new file mode 100644 index 00000000..00aea829 --- /dev/null +++ b/cinelerra-5.1/plugins/blendalgebra/graphart_softlight.ba @@ -0,0 +1,26 @@ +/***********************************************-*-C-*-**********/ +/* Overlay mode: Graphic Art Softlight */ + +BLEND_ALGEBRA_INIT + +COLORSPACE_RGB +PARALLEL_SAFE +REQUIRE_TRACKS(2) + +BLEND_ALGEBRA_PROC + +#define s 1 +#define d 0 + +R_OUT = R(s) * (1-A(d)) + R(d) * (1-A(s)); +if (A(d) > 0) R_OUT += (R(d) * A(s) + 2 * R(s) * (A(d)-R(d))) / A(d); + +G_OUT = G(s) * (1-A(d)) + G(d) * (1-A(s)); +if (A(d) > 0) G_OUT += (G(d) * A(s) + 2 * G(s) * (A(d)-G(d))) / A(d); + +B_OUT = B(s) * (1-A(d)) + B(d) * (1-A(s)); +if (A(d) > 0) B_OUT += (B(d) * A(s) + 2 * B(s) * (A(d)-B(d))) / A(d); + +A_OUT = A(s) + A(d) - A(s) * A(d); + +BLEND_ALGEBRA_END diff --git a/cinelerra-5.1/plugins/blendalgebra/logical_and.ba b/cinelerra-5.1/plugins/blendalgebra/logical_and.ba new file mode 100644 index 00000000..71fb662b --- /dev/null +++ b/cinelerra-5.1/plugins/blendalgebra/logical_and.ba @@ -0,0 +1,23 @@ +/***********************************************-*-C-*-**********/ +/* Overlay mode: Logical And, for any number of tracks */ + +BLEND_ALGEBRA_INIT + +COLORSPACE_RGB +PARALLEL_SAFE + +BLEND_ALGEBRA_PROC + +int i; + +R_OUT = G_OUT = B_OUT = A_OUT = 1; + +for (i=0; i 0) printf (" rel=%10g", sabs*256/npix); +printf ("\n"); +sum = sabs = 0; +npix = 0; +#endif + +BLEND_ALGEBRA_PROC + +#ifndef NO_PRINTOUT +float diff; +diff = Y(0)-Y(1); +sum += diff; +sabs += ABS (diff); +npix ++; +#endif + +Y_OUT = (Y(0)-Y(1))/2*exp((KEY_A-0.5)*14*M_LN2)+0.5; +U_OUT = V_OUT = 0; +A_OUT = 1; + +BLEND_ALGEBRA_END diff --git a/cinelerra-5.1/plugins/blendprogram/BlendProgramCompile.pl b/cinelerra-5.1/plugins/blendprogram/BlendProgramCompile.pl new file mode 100755 index 00000000..5e3646d9 --- /dev/null +++ b/cinelerra-5.1/plugins/blendprogram/BlendProgramCompile.pl @@ -0,0 +1,289 @@ +#!/usr/bin/perl + +# Helper script to compile Cinelerra blend programs +# Calling: BlendProgramCompile.pl [options] +# The special option "-API" shows the numeric version of the script itself + +# Several important definitions + +# BlendProgramCompile.pl script API version. Must not be changed ! +$cin_bp_api = 1; + +# C compiler executable and options, can be redefined on user's demand +$cin_compiler = $ENV{'CIN_CC'}; +$cin_compiler = $ENV{'CC'} if $cin_compiler eq ''; +# a likely default compiler +$cin_compiler = 'gcc' if $cin_compiler eq ''; +# another possible compiler +#$cin_compiler = 'clang' if $cin_compiler eq ''; +# a fake compiler for debugging the script itself +#$cin_compiler = 'echo'; + +# Mandatory compiler options to build dynamically loadable object, don't change +$cin_compopts = '-fPIC'; +$cin_ldopts = '-shared'; +$cin_ldlibs = '-lm'; + +# Adjustable compiler options for optimization, debugging, warnings +$cin_optopts = '-O2'; +$cin_debopts = '-g'; +$cin_warnopts = '-Wall -W -Wno-unused-parameter'; +# more possible compiler options +#$cin_optopts = '-Ofast -march=native'; +#$cin_debopts = '-ggdb'; + +# Set these to 1 if want to optimize, debug, or verbose output by default +$opt_opt = 1; +$opt_deb = 0; +$opt_verb = 0; + +# Text editor executable, can be redefined on user's preferences +$cin_editor = $ENV{'CIN_EDITOR'}; +# a likely default editor +$cin_editor = 'emacs' if $cin_editor eq ''; +# another possible editors +#$cin_editor = 'konsole -e vim' if $cin_editor eq ''; +#$cin_editor = 'xterm -e vi' if $cin_editor eq ''; +#$cin_editor = 'xedit' if $cin_editor eq ''; + +# Background execution; set it empty to execute in foreground and block +$cin_bgr = ' &'; + +# Nothing to change below this line + +# Cinelerra installation path +$cin_dat = $ENV{'CIN_DAT'}; + +# Cinelerra user's config directory +$cin_config = $ENV{'CIN_CONFIG'}; +$cin_config = $ENV{'HOME'}.'/.bcast5' + if $cin_config eq '' && $ENV{'HOME'} ne ''; +$cin_config = '.' if $cin_config eq ''; +$me_config = "$cin_config/BlendProgramCompile.pl"; + +sub Usage +{ + print "\nCinelerraGG blend program compiler driver usage\n\n"; + print "$0 [options] compile blend program \n"; + print "$0 -edit open blend program in editor\n"; + print "$0 -API output own API version\n"; + print "$0 -h, -help, -? output this help text\n"; + print "\nCinelerraGG additional blend program compiler options\n\n"; + print "-compile don't check modification time, compile unconditionally\n"; + print "-cfile don't remove intermediate .c file\n"; + print "-opt add optimizing options to compiler command line\n"; + print "-debug add debugging options to compiler command line\n"; + print "-warn add warning options to compiler command line\n"; + print "-edit open blend program source in text editor\n"; + print "-verbose verbose execution"; + print " (active by default)" if $opt_verb; + print "\n"; + print "-noapi don't copy $0 to $ENV{HOME}/.bcast5\n"; + print "\nCinelerraGG blend program compiler default configuration\n\n"; + print "Compiler command line: $cin_compiler $cin_compopts $cin_ldopts -o .so $cin_ldlibs\n"; + print "Optimizing options: $cin_optopts"; + print " (active by default)" if $opt_opt; + print "\n"; + print "Debugging options: $cin_debopts"; + print " (active by default)" if $opt_deb; + print "\n"; + print "Warning options: $cin_warnopts\n"; + print "Editor command line: $cin_editor $cin_bgr\n"; + print "\nRelevant environment variables\n\n"; + if ($ENV{'CIN_CC'} ne '') + { + print "CIN_CC=$ENV{CIN_CC}\n"; + } + else + { + print "\$CIN_CC: currently not set, fallback: $cin_compiler\n"; + } + if ($ENV{'CC'} ne '') + { + print "CC=$ENV{CC}\n"; + } + else + { + print "\$CC: currently not set, fallback: $cin_compiler\n"; + } + if ($ENV{'CIN_EDITOR'} ne '') + { + print "CIN_EDITOR=$ENV{CIN_EDITOR}\n"; + } + else + { + print "\$CIN_EDITOR: currently not set, fallback: $cin_editor\n"; + } + if ($ENV{'CIN_DAT'} ne '') + { + print "CIN_DAT=$ENV{CIN_DAT}\n"; + } + else + { + print "\$CIN_DAT: currently not set\n"; + } + if ($ENV{'CIN_CONFIG'} ne '') + { + print "CIN_CONFIG=$ENV{CIN_CONFIG}\n"; + } + else + { + print "\$CIN_CONFIG: currently not set, fallback: $ENV{HOME}/.bcast5\n"; + } + if ($ENV{'CIN_USERLIB'} ne '') + { + print "CIN_USERLIB=$ENV{CIN_USERLIB}\n"; + } + else + { + print "\$CIN_USERLIB: currently not set, fallback: $ENV{HOME}/.bcast5lib\n"; + } + exit 0; +} + +$opt_api = $opt_noapi = 0; +$opt_edit = $opt_compile = $opt_cfile = $opt_warn = 0; +$bp_src = ''; + +# Scan options... +foreach $arg (@ARGV) +{ + unless ($arg =~ /^-/) + { + $bp_src = $arg; # this is the source to compile + last; + } + if ($arg eq '-API') # print API version + { + $opt_api = 1; + } + elsif ($arg eq '-noapi') # don't copy script to ~/.bcast5 + { + $opt_noapi = 1; + } + elsif ($arg eq '-edit') # open in text editor + { + $opt_edit = 1; + push @opts, $arg; + } + elsif ($arg eq '-compile') # compile unconditionally + { + $opt_compile = 1; + push @opts, $arg; + } + elsif ($arg eq '-cfile') # don't remove .c file + { + $opt_cfile = 1; + push @opts, $arg; + } + elsif ($arg eq '-opt') # optimize + { + $opt_opt = 1; + push @opts, $arg; + } + elsif ($arg eq '-debug') # debug + { + $opt_deb = 1; + push @opts, $arg; + } + elsif ($arg eq '-warn') # warnings + { + $opt_warn = 1; + push @opts, $arg; + } + elsif ($arg eq '-verbose') # print executed commands + { + $opt_verb = 1; + push @opts, $arg; + } + else { Usage(); } # unknown option +} + +# A special internal request: output own API version +if ($opt_api) +{ + print "$cin_bp_api\n"; + exit 0; +} + +# If a system (not user's) script instance is executed, and the API versions +# of both scripts do not match, then copy the system script to the user's one +# (making a backup copy of the latter). Then execute it with the same argument. +if (! $opt_noapi && $0 ne $me_config) +{ + $me_api = 0; + $me_api = `\"$me_config\" -API` if -x $me_config; + #print "BlendProgramCompile: user script=$me_config API=$me_api, need $cin_bp_api\n"; + if ($me_api != $cin_bp_api) + { + print "BlendProgramCompile: copying $0 to $me_config\n"; + unlink "$me_config.bak" if -f "$me_config.bak"; + rename "$me_config", "$me_config.bak" if -f $me_config; + system "cp \"$0\" \"$me_config\""; + system "chmod +x \"$me_config\""; + } +} + +# Do nothing if nothing to compile +if ($bp_src eq '') { Usage(); } +#print "BlendProgramCompile: source=$bp_src\n"; +unless ($opt_edit || -f $bp_src) { Usage(); } + +# Redirection to user configurable script copy +$arg = join ' ', @opts; +if (! $opt_noapi && $0 ne $me_config) +{ + exec "\"$me_config\" $arg \"$bp_src\"" if -x $me_config; +} + +# If a user's script instance is executed, do everything by myself +print "BlendProgramCompile: executing $0 $arg $bp_src\n" if $opt_verb; + +# Call text editor +if ($opt_edit) +{ + print "BlendProgramCompile: executing $cin_editor $bp_src$cin_bgr\n"; + system "$cin_editor \"$bp_src\"$cin_bgr"; + exit 0; +} + +# Check if the program source is newer than the object +if ($cin_dat ne '') { $bp_start = "$cin_dat/dlfcn/BlendProgramStart"; } +else { $bp_start = "BlendProgramStart"; } +$mtime_start = -1; +($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime_start, + $ctime, $blksize, $blocks) = stat ($bp_start); + +($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime_src, + $ctime, $blksize, $blocks) = stat ($bp_src); +$mtime_src = $mtime_start if $mtime_src < $mtime_start; + +$mtime_so = -1; +($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime_so, + $ctime, $blksize, $blocks) = stat ("$bp_src.so") if -f "$bp_src.so"; + +if (! $opt_compile && $mtime_so > $mtime_src) +{ + print "BlendProgramCompile: $bp_src shared object up to date, exiting\n" + if $opt_verb; + exit 0; +} + +# Call the compiler now +$cin_compopts .= " $cin_optopts" if $opt_opt; +$cin_compopts .= " $cin_debopts" if $opt_deb; +$cin_compopts .= " $cin_warnopts" if $opt_warn; +unlink "$bp_src.c" if -f "$bp_src.c"; +unlink "$bp_src.so" if -f "$bp_src.so"; +print "BlendProgramCompile: executing cat $bp_start $bp_src > $bp_src.c\n" + if $opt_verb; +system "echo '# 1 \"$bp_start\"' > \"$bp_src.c\""; +system "cat \"$bp_start\" >> \"$bp_src.c\""; +system "echo '# 1 \"$bp_src\"' >> \"$bp_src.c\""; +system "cat \"$bp_src\" >> \"$bp_src.c\""; +print "BlendProgramCompile: executing $cin_compiler $cin_compopts $cin_ldopts -o $bp_src.so $bp_src.c $cin_ldlibs\n"; +system "$cin_compiler $cin_compopts $cin_ldopts -o \"$bp_src.so\" \"$bp_src.c\" $cin_ldlibs"; +unlink "$bp_src.c" if ! $opt_cfile && -f "$bp_src.c"; + +# And finally return to the caller +exit 0; diff --git a/cinelerra-5.1/plugins/blendprogram/BlendProgramStart b/cinelerra-5.1/plugins/blendprogram/BlendProgramStart new file mode 100644 index 00000000..1eae23c1 --- /dev/null +++ b/cinelerra-5.1/plugins/blendprogram/BlendProgramStart @@ -0,0 +1,145 @@ +/***********************************************-*-C-*-**********/ + +/* Blend program header for Cinelerra Blend Program plugin */ + +#include +#include +#include +#include +#include +#include +#include + +/* These six must match enum BC_CModel from guicast/bccmodels.h */ +#define BC_RGB888 9 +#define BC_RGBA8888 10 +#define BC_YUV888 13 +#define BC_YUVA8888 14 +#define BC_RGB_FLOAT 29 +#define BC_RGBA_FLOAT 30 + +/* These five must match enum from plugins/blendprogram/blendprogram.h */ +#define AUTO 0 +#define RGB 1 +#define YUV 2 +#define HSV 3 +#define PROJECT 4 + +/* Universal math macros taken mostly from guicast/clip.h */ +#ifndef ABS +#define ABS(x) ((x) >= 0 ? (x) : -(x)) +#endif +#ifndef SQR +#define SQR(x) ((x) * (x)) +#endif +#ifndef MAX +#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#endif +#ifndef MIN +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#endif +#define TO_RAD(x) ((x) * 2 * M_PI / 360) +#define TO_DEG(x) ((x) * 360 / 2 / M_PI) + +/* CLIP returns value, CLAMP does assignment */ +#define CLIP(x,y,z) ((x) < (y) ? (y) : ((x) > (z) ? (z) : (x))) +#define CLAMP(x,y,z) ((x) = ((x) < (y) ? (y) : ((x) > (z) ? (z) : (x)))) + +/* Macros usable in the BLEND_PROGRAM_INIT phase */ + +/* Specification of working color space inside user's program */ +#define COLORSPACE_RGB {*color_work = RGB;} +#define COLORSPACE_YUV {*color_work = YUV;} +#define COLORSPACE_HSV {*color_work = HSV;} + +/* Minimum no of tracks required by user's program */ +#define REQUIRE_TRACKS(n) {*MIN_tracks = (n);} + +/* Claim parallelization support and inquire if parallelism requested */ +#define PARALLEL_SAFE {*parallel_support = 1;} +#define PARALLEL_REQUEST parallel_request + +/* Dimensionality macros, can be used in both INIT and PROC phases */ + +/* Total number of tracks got to work on */ +#define TOTAL_TRACKS N_tracks + +/* 1 == has alpha channel */ +#define HAS_ALPHA HAS_alpha + +/* Frame dimensions in pixels */ +#define WIDTH FRAME_w +#define HEIGHT FRAME_h + +/* Macros usable in the BLEND_PROGRAM_PROC phase */ + +/* Handy macros for pixel and key color components */ +#define R(i) TRK_r[i] +#define G(i) TRK_g[i] +#define B(i) TRK_b[i] +#define Y(i) TRK_r[i] +#define U(i) TRK_g[i] +#define V(i) TRK_b[i] +#define H(i) TRK_r[i] +#define S(i) TRK_g[i] +#define A(i) TRK_a[i] + +#define KEY_R KEY_r +#define KEY_G KEY_g +#define KEY_B KEY_b +#define KEY_Y KEY_r +#define KEY_U KEY_g +#define KEY_V KEY_b +#define KEY_H KEY_r +#define KEY_S KEY_g +#define KEY_A KEY_a + +/* Macros for pixel coordinates */ +#define PIX_X TRK_x +#define PIX_Y TRK_y + +/* Macros for various arts to clip pixels */ +#define CLIP_RGB(i) {CLAMP (TRK_r[i], 0, 1); \ + CLAMP (TRK_g[i], 0, 1); \ + CLAMP (TRK_b[i], 0, 1);} + +#define CLIP_YUV(i) {CLAMP (TRK_r[i], 0, 1); \ + CLAMP (TRK_g[i], -0.5, 0.5); \ + CLAMP (TRK_b[i], -0.5, 0.5);} + +#define CLIP_HSV(i) {if (TRK_r[i] < 0 || TRK_r[i] >= 360) \ + TRK_r[i] -= floor(TRK_r[i]/360)*360; \ + CLAMP (TRK_g[i], 0, 1); CLAMP (TRK_b[i], 0, 1);} + +#define CLIP_A(i) {CLAMP (TRK_a[i], 0, 1);} + +#define CLIP_RGBA(i) { CLIP_RGB(i) CLIP_A(i) } +#define CLIP_YUVA(i) { CLIP_YUV(i) CLIP_A(i) } +#define CLIP_HSVA(i) { CLIP_HSV(i) CLIP_A(i) } + +#define CLIP_RGB_ALL {int I_track; \ + for (I_track=0; I_track + * + * 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 + * + */ + +#include "filexml.h" +#include "keyframe.h" +#include "language.h" +#include "mainerror.h" +#include "clip.h" +#include "bccolors.h" +#include "loadbalance.h" +#include "filesystem.h" +#include "vframe.h" +#include "mainsession.h" +#include "mwindow.h" +#include "pluginserver.h" +#include "blendprogram.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +//#define DEBUG + +// Sorry for this global variable, it is needed to propagate the signal +// from the GUI instance to the processing instance of the plugin +// that some user function might have changed and need recompilation +static time_t BlendProgramTstamp = -1; + +REGISTER_PLUGIN(BlendProgram) + +LOAD_CONFIGURATION_MACRO(BlendProgram, BlendProgramConfig) + +NEW_WINDOW_MACRO(BlendProgram, BlendProgramWindow) + +const char *BlendProgram::plugin_title() { return N_("Blend Program"); } + +int BlendProgram::is_realtime() { return 1; } +int BlendProgram::is_multichannel() { return 1; } +int BlendProgram::is_synthesis() { return 1; } + +//////////////////////////////////////////// +// Plugin configuration class implementation +//////////////////////////////////////////// + +BlendProgramConfig::BlendProgramConfig() +{ + funcname[0] = 0; // no function per default + parallel = 1; // parallelize per default + clipcolors = 1; // clip colors per default + direction = BlendProgramConfig::BOTTOM_FIRST; // as in Overlay plugin + colorspace = BlendProgramConfig::AUTO; // requested from function + red = green = blue = 0; // black key color per default + alpha = 0; // transparent per default +} + +int BlendProgramConfig::equivalent(BlendProgramConfig &that) +{ + return + !strcmp (funcname, that.funcname) && + parallel == that.parallel && + clipcolors == that.clipcolors && + direction == that.direction && + colorspace == that.colorspace && + EQUIV (red, that.red) && + EQUIV (green, that.green) && + EQUIV (blue, that.blue) && + EQUIV (alpha, that.alpha); +} + +void BlendProgramConfig::copy_from(BlendProgramConfig &that) +{ + strcpy (funcname, that.funcname); + parallel = that.parallel; + clipcolors = that.clipcolors; + direction = that.direction; + colorspace = that.colorspace; + red = that.red; + green = that.green; + blue = that.blue; + alpha = that.alpha; +} + +void BlendProgramConfig::interpolate (BlendProgramConfig &prev, + BlendProgramConfig &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); + + red = prev.red * prev_scale + next.red * next_scale; + green = prev.green * prev_scale + next.green * next_scale; + blue = prev.blue * prev_scale + next.blue * next_scale; + alpha = prev.alpha * prev_scale + next.alpha * next_scale; + + strcpy (funcname, prev.funcname); + parallel = prev.parallel; + clipcolors = prev.clipcolors; + direction = prev.direction; + colorspace = prev.colorspace; +} + +const char *BlendProgramConfig::direction_to_text(int direction) +{ + switch(direction) + { + case BlendProgramConfig::BOTTOM_FIRST: return _("Bottom first"); + case BlendProgramConfig::TOP_FIRST: return _("Top first"); + } + return ""; +} + +const char *BlendProgramConfig::colorspace_to_text(int colorspace) +{ + switch(colorspace) + { + case BlendProgramConfig::AUTO: return _("auto"); + case BlendProgramConfig::RGB: return _("RGB"); + case BlendProgramConfig::YUV: return _("YUV"); + case BlendProgramConfig::HSV: return _("HSV"); + case BlendProgramConfig::PROJECT: return _("of project"); + } + return ""; +} + +int BlendProgramConfig::get_key_color() +{ + int red = (int) (CLIP (this->red, 0, 1) * 255); + int green = (int) (CLIP (this->green, 0, 1) * 255); + int blue = (int) (CLIP (this->blue, 0, 1) * 255); + return (red << 16) | (green << 8) | blue; +} + +//////////////////////////////////////////// +// Plugin dialog window class implementation +//////////////////////////////////////////// + +BlendProgramFuncname::BlendProgramFuncname(BlendProgram *plugin, + const char *funcname, + BlendProgramWindow *gui, + int x, int y) + : BC_TextBox(x, y, gui->get_w()-x-xS(10), 1, funcname) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendProgramFuncname::handle_event() +{ + // Perhaps locking is not needed here + // as GUI is driven by a separate plugin instance + plugin->func_lock->lock("BlendProgramFuncname::handle_event"); + strncpy(plugin->config.funcname, get_text(), + sizeof(plugin->config.funcname)-1); + BlendProgramTstamp = time(NULL); // time of possible function change +#ifdef DEBUG + printf ("BlendProgramFuncname::handle_event setting function %s\n timestamp %s", + plugin->config.funcname, ctime(&BlendProgramTstamp)); +#endif + plugin->func_lock->unlock(); + plugin->send_configure_change(); + return 1; +} + +BlendProgramDetach::BlendProgramDetach (BlendProgram *plugin, + BlendProgramWindow *gui, + int x, int y) + : BC_GenericButton (x, y, _("Detach")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendProgramDetach::handle_event() +{ + if (! plugin->config.funcname[0]) return 1;// already detached, nothing to do + + plugin->func_lock->lock("BlendProgramDetach::handle_event"); + plugin->config.funcname[0] = 0; // clear function, inducing detach + BlendProgramTstamp = time(NULL); // force refresh of dlopen'd functions +#ifdef DEBUG + printf ("BlendProgramDetach::handle_event clearing function\n timestamp %s", + ctime(&BlendProgramTstamp)); +#endif + plugin->func_lock->unlock(); + + gui->lock_window("BlendProgramDetach::handle_event"); + gui->funcname->update(plugin->config.funcname); + gui->unlock_window(); + + plugin->send_configure_change(); + return 1; +} + +BlendProgramRefresh::BlendProgramRefresh (BlendProgram *plugin, + BlendProgramWindow *gui, + int x, int y) + : BC_GenericButton (x, y, _("Refresh")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendProgramRefresh::handle_event() +{ + plugin->func_lock->lock("BlendProgramRefresh::handle_event"); + BlendProgramTstamp = time(NULL); // force refresh of dlopen'd functions +#ifdef DEBUG + printf ("BlendProgramRefresh::handle_event timestamp %s", + ctime(&BlendProgramTstamp)); +#endif + plugin->func_lock->unlock(); + return 1; // just reattach all functions, without reconfiguration +} + +BlendProgramEdit::BlendProgramEdit (BlendProgram *plugin, + BlendProgramWindow *gui, + int x, int y) + : BC_GenericButton (x, y, _("Edit...")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendProgramEdit::handle_event() +{ + char fname[BCTEXTLEN], dir[BCTEXTLEN], str[2*BCTEXTLEN]; + + strcpy (fname, plugin->config.funcname); + if (! fname[0]) + { + eprintf (_("Blend Program: no source file to edit, select program first\n")); + return 1; + } + + // Evtl make function path absolute by prepending project path to it + if (fname[0] != '/') // fname is relative, prepend current project path + { + strcpy (dir, plugin->server->mwindow->session->filename); + if (dir[0]) + { + char *cp = strrchr (dir, '/'); + if (cp) + { + cp[1] = 0; // strip project filename off from project path + strcat (dir, fname); // concatenate obtained path with function name + strcpy (fname, dir); + } + } + } + + // This will run configured external editor via perl script + // If editor start is not backgrounded, GUI will block until editor exits + sprintf(str, "\"%s/dlfcn/BlendProgramCompile.pl\" -edit \"%s\"", + getenv("CIN_DAT"), fname); +#ifdef DEBUG + printf ("BlendProgramEdit::handle_event: executing:\n %s\n", str); +#endif + system (str); // runs configured external editor + + plugin->func_lock->lock("BlendProgramEdit::handle_event"); + BlendProgramTstamp = time(NULL); // force refresh of dlopen'd functions +#ifdef DEBUG + printf ("BlendProgramEdit::handle_event edited function %s\n timestamp %s", + fname, ctime(&BlendProgramTstamp)); +#endif + plugin->func_lock->unlock(); + + // Evtl functions will be recompiled, but no configure change + return 1; +} + +BlendProgramFileButton::BlendProgramFileButton(BlendProgram *plugin, + BlendProgramWindow *gui, + int x, int y) + : BC_GenericButton(x, y, _("Attach...")) +{ + this->plugin = plugin; + this->gui = gui; + this->file_box = 0; +} + +BlendProgramFileButton::~BlendProgramFileButton() +{ + stop(); +} + +int BlendProgramFileButton::handle_event() +{ + gui->editing_lock->lock(); + + if (! gui->editing) + { + gui->editing = 1; + gui->editing_lock->unlock(); + start(); + } + else + { + flicker(); + gui->editing_lock->unlock(); + } + + return 1; +} + +void BlendProgramFileButton::run() +{ + int result = 1; + const char *fpath; + char fname[BCTEXTLEN], dir[BCTEXTLEN]; + + strcpy (fname, plugin->config.funcname); + + // This infinite loop is exited after clicking OK or Cancel in FileBox. + // There are several special buttons which replace the FileBox initial path + // with another predefined path and close FileBox with reinit_path flag set. + // If reinit_path is set, the loop is repeated with that extracted path. + // reinit_path is cleared inside BlendProgramFileBox constructor. + + for (;;) // will exit when reinit_path == 0 + { // Evtl make function path absolute by prepending project path to it + if (fname[0] != '/') // fname is relative, prepend current project path + { + strcpy (dir, plugin->server->mwindow->session->filename); + if (dir[0]) + { + char *cp = strrchr (dir, '/'); + if (cp) + { + cp[1] = 0; // strip project filename off from project path + strcat (dir, fname); // concatenate obtained path with function name + strcpy (fname, dir); + } + } + } +#ifdef DEBUG + printf ("BlendProgramFileButton::run creating file_box (%s)\n", fname); +#endif + file_box = new BlendProgramFileBox (plugin, gui, fname); + file_box->update_history(); // otherwise actual dir can be forgotten + file_box->create_objects(); + file_box->lock_window ("BlendProgramFileButton::run"); + file_box->add_objects(); // add our special buttons + file_box->update_filter ("*.bp"); + file_box->unlock_window(); + result = file_box->run_window(); + if (file_box->reinit_path) // if set, a button was clicked + { + fpath = file_box->get_current_path(); // current, as set by buttons +#ifdef DEBUG + printf ("BlendProgramFileButton::run file_box returned %d reinit_path=%d\n fpath=%s\n", + result, file_box->reinit_path, fpath); +#endif + strncpy (fname, fpath ? fpath : "", sizeof(fname)-1); + delete file_box; + file_box = 0; + continue; // reinit_path will be cleared on repeat in FileBox constructor + } + fpath = file_box->get_submitted_path(); // submitted, as set by user +#ifdef DEBUG + printf ("BlendProgramFileButton::run file_box returned %d reinit_path=%d\n fpath=%s\n", + result, file_box->reinit_path, fpath); +#endif + strncpy (fname, fpath ? fpath : "", sizeof(fname)-1); + delete file_box; + file_box = 0; + break; // reinit_path remains cleared, exit loop + } // until reinit_path == 0 + + gui->editing_lock->lock(); + if (result) gui->editing = 0; + gui->editing_lock->unlock(); + if (! gui->editing) return; // Cancel pressed + + if (fname[0]) // selected function name not empty, canonicalize it + { // if function is under project's dir, strip dir and make path relative + strcpy (dir, plugin->server->mwindow->session->filename); + // another project location might be plugin->server->mwindow->edl->path + if (dir[0]) // project filename contains some path + { + char *cp = strrchr (dir, '/'); + if (cp) + { + cp[1] = 0; // the directory of current project with trailing slash + if (! strncmp (fname, dir, strlen(dir))) + { + strcpy (dir, fname+strlen(dir)); // strip project dir off + strcpy (fname, dir+strspn(dir,"/")); // ensure path is relative + } + } + } + if (strlen (fname) < 3 || strcmp (fname+strlen(fname)-3, ".bp")) + strcat (fname, ".bp"); // suggest '.bp' suffix for blend programs + } + + // Actualize selected function in config and in the main plugin dialog + plugin->func_lock->lock("BlendProgramFileButton::run"); + strcpy (plugin->config.funcname, fname); + BlendProgramTstamp = time(NULL); // time of possible function change +#ifdef DEBUG + printf ("BlendProgramFileButton::run setting function %s\n timestamp %s", + plugin->config.funcname, ctime(&BlendProgramTstamp)); +#endif + plugin->func_lock->unlock(); + gui->lock_window("BlendProgramFileButton::run"); + gui->funcname->update(plugin->config.funcname); + gui->unlock_window(); + gui->editing_lock->lock(); + gui->editing = 0; + gui->editing_lock->unlock(); + + plugin->send_configure_change(); +} + +void BlendProgramFileButton::stop() +{ + if (file_box) file_box->set_done(1); + join(); +} + +BlendProgramFileBox::BlendProgramFileBox(BlendProgram *plugin, + BlendProgramWindow *gui, + char *init_path) + : BC_FileBox(0, BC_WindowBase::get_resources()->filebox_h/2, init_path, + _("Blend Program: Select program source file"),"") +{ + this->plugin = plugin; + this->gui = gui; + + to_curdir = 0; + to_usrlib = 0; + to_syslib = 0; + copy_curdir = 0; + copy_usrlib = 0; + file_edit = 0; + reinit_path = 0; +} + +BlendProgramFileBox::~BlendProgramFileBox() +{ +} + +// We need several additional buttons not foreseen in the bare FileBox. +// We arrange them in the place of (empty) FileBox caption. +void BlendProgramFileBox::add_objects() +{ + int xs10 = xS(10), xs5 = xS(5); + int ys10 = yS(10); + int x = xs10, y = ys10, x2; + + add_subwindow(to_curdir = new BlendProgramToCurdir(this, x, y)); + x2 = x+to_curdir->get_w()+xs5; + add_subwindow(to_usrlib = new BlendProgramToUsrlib(this, x2, y)); + x2 += to_usrlib->get_w()+xs5; + add_subwindow(to_syslib = new BlendProgramToSyslib(this, x2, y)); + y = get_y_margin(); + add_subwindow(copy_curdir = new BlendProgramCopyCurdir(this, x, y)); + x2 = x+copy_curdir->get_w()+xs5; + add_subwindow(copy_usrlib = new BlendProgramCopyUsrlib(this, x2, y)); + x2 += copy_usrlib->get_w()+xs5; + add_subwindow(file_edit = new BlendProgramFileEdit(this, x2, y)); + flush(); +} + +int BlendProgramFileBox::resize_event(int w, int h) +{ + int xs10 = xS(10), xs5 = xS(5); + int x = xs10, y, x2; + + BC_FileBox::resize_event (w, h); + + y = get_y_margin(); + copy_curdir->reposition_window (x, y); + x2 = x+copy_curdir->get_w()+xs5; + copy_usrlib->reposition_window (x2, y); + x2 += copy_usrlib->get_w()+xs5; + file_edit->reposition_window (x2, y); + + flush(); + return 1; +} + +BlendProgramToCurdir::BlendProgramToCurdir(BlendProgramFileBox *file_box, + int x, int y) + : BC_GenericButton (x, y, _("=>Project")) +{ + this->file_box = file_box; +} + +int BlendProgramToCurdir::handle_event() +{ + char *cp, fname[BCTEXTLEN], dir[BCTEXTLEN], path[BCTEXTLEN]; + + strcpy (dir, file_box->plugin->server->mwindow->session->filename); + if (dir[0]) // first get current project directory + { + cp = strrchr (dir, '/'); + if (cp) *cp = 0; + else dir[0] = 0; + } + if (! dir[0]) // no project dir - get curdir as fallback + { + cp = getcwd (dir, sizeof(dir)); + if (! cp) dir[0] = 0; + } + if (! dir[0]) return 1; // no curdir accessible, nothing to change + + fname[0] = 0; + const char *spath = file_box->get_submitted_path();// get name entered so far + if (spath) file_box->fs->extract_name (fname, spath); // cut name from dir + + if (fname[0]) file_box->fs->join_names (path, dir, fname); + else strcpy (path, dir); // substitute old entered dir with project dir + + // Not exactly sure what operations on FileBox are really important + file_box->fs->change_dir (dir); // force it to recognize the new dir + + // This updates all paths, sets current_path and submitted_path of FileBox, + // but in memory only, text fields in the dialog are not actualized. + // file_box->refresh() does not help to refresh text fields either. + // Therefore we have to apply a trick with closing FileBox and + // reopening it with the new generated path. + file_box->update_paths (path); + + // Without updating history FileBox forgets our new dir + // and sets curdir to some old history item. + file_box->update_history(); + + file_box->reinit_path = 1; // set flag to reopen FileBox afterwards + file_box->set_done(1); // temporarily close FileBox, will be reopened later + + return 1; +} + +BlendProgramToUsrlib::BlendProgramToUsrlib(BlendProgramFileBox *file_box, + int x, int y) + : BC_GenericButton (x, y, _("=>Userlib")) +{ + this->file_box = file_box; +} + +int BlendProgramToUsrlib::handle_event() +{ + char *cp, fname[BCTEXTLEN], dir[BCTEXTLEN], path[BCTEXTLEN]; + + dir[0] = 0; + cp = getenv ("CIN_USERLIB"); // $HOME/.bcast5lib by default + if (cp) strcpy (dir, cp); + if (! dir[0]) + { + cp = getenv ("HOME"); // evtl resolve as default via $HOME + if (cp) strcpy (dir, cp); + if (dir[0]) strcat (dir, "/.bcast5lib"); + else + { + cp = getenv ("CIN_CONFIG"); // or via $CIN_CONFIG as fallback + if (cp) strcpy (dir, cp); + if (dir[0]) strcat (dir, "lib"); + } + } + if (! dir[0]) return 1; // no user libdir known, nothing to change + + // The default user libdir for blend programs is $HOME/.bcast5lib/dlfcn/bp + // Ensure it is a directory, evtl create dir, if not - do nothing else + if (! file_box->fs->is_dir (dir)) file_box->fs->create_dir (dir); + if (! file_box->fs->is_dir (dir)) return 1; + strcat (dir, "/dlfcn"); + if (! file_box->fs->is_dir (dir)) file_box->fs->create_dir (dir); + if (! file_box->fs->is_dir (dir)) return 1; + strcat (dir, "/bp"); + if (! file_box->fs->is_dir (dir)) file_box->fs->create_dir (dir); + if (! file_box->fs->is_dir (dir)) return 1; + + fname[0] = 0; + const char *spath = file_box->get_submitted_path();// get name entered so far + if (spath) file_box->fs->extract_name (fname, spath); // cut name from dir + if (fname[0]) file_box->fs->join_names (path, dir, fname); + else strcpy (path, dir); // substitute old entered dir with user libdir + + // Reinitialize FileBox with the modified path + file_box->fs->change_dir (dir); + file_box->update_paths (path); + file_box->update_history(); + file_box->reinit_path = 1; // set flag to reopen FileBox afterwards + file_box->set_done(1); // temporarily close FileBox, will be reopened later + + return 1; +} + +BlendProgramToSyslib::BlendProgramToSyslib(BlendProgramFileBox *file_box, + int x, int y) + : BC_GenericButton (x, y, _("=>Syslib")) +{ + this->file_box = file_box; +} + +int BlendProgramToSyslib::handle_event() +{ + char *cp, fname[BCTEXTLEN], dir[BCTEXTLEN], path[BCTEXTLEN]; + + dir[0] = 0; + cp = getenv ("CIN_DAT"); // Cinelerra installation directory (bin) + if (cp) strcpy (dir, cp); + if (! dir[0]) return 1; // there is no default + + // System libdir for blend programs is $CIN_DAT/dlfcn/bp (bin/dlfcn/bp). + // Ensure it is a directory, it must exist, if not - do nothing else + strcat (dir, "/dlfcn/bp"); + if (! file_box->fs->is_dir (dir)) return 1; + + fname[0] = 0; + const char *spath = file_box->get_submitted_path();// get name entered so far + if (spath) file_box->fs->extract_name (fname, spath); // cut name from dir + if (fname[0]) file_box->fs->join_names (path, dir, fname); + else strcpy (path, dir); // substitute that old dir with system libdir + + // Reinitialize FileBox with the modified path + file_box->fs->change_dir (dir); + file_box->update_paths (path); + file_box->update_history(); + file_box->reinit_path = 1; // set flag to reopen FileBox afterwards + file_box->set_done(1); // temporarily close FileBox, will be reopened later + + return 1; +} + +BlendProgramCopyCurdir::BlendProgramCopyCurdir(BlendProgramFileBox *file_box, + int x, int y) + : BC_GenericButton (x, y, _("Copy to project")) +{ + this->file_box = file_box; +} + +int BlendProgramCopyCurdir::handle_event() +{ + int ret; + char *cp, fname[BCTEXTLEN], dir[BCTEXTLEN], from_path[BCTEXTLEN], + to_path[BCTEXTLEN], cmd[3*BCTEXTLEN]; + + strcpy (dir, file_box->plugin->server->mwindow->session->filename); + if (dir[0]) // first get current project directory + { + cp = strrchr (dir, '/'); + if (cp) *cp = 0; + else dir[0] = 0; + } + if (! dir[0]) return 1; // no curdir accessible, no copy target + + fname[0] = from_path[0] = 0; + const char *spath = file_box->get_submitted_path();// get name entered so far + if (spath) + { + strcpy (from_path, spath); // this is copy source + file_box->fs->extract_name (fname, spath); // cut name from source dir + } + if (! (fname[0] && from_path[0])) return 1; // no copy source ?? + + file_box->fs->join_names (to_path, dir, fname); // this is copy target + + if (! strcmp (from_path, to_path)) return 1; // source and target identical + + if (file_box->fs->is_dir (from_path) || file_box->fs->is_dir (to_path)) + return 1; // source and target must not be directories + if (access (from_path, R_OK)) + { + eprintf (_("Blend Program: source file %s does not exist or not readable\n"), + from_path); + return 1; + } + if (! access (to_path, F_OK)) + { + eprintf (_("Blend Program: target file %s exists, overwriting not allowed\n"), + to_path); + return 1; + } + + // Now do copy operation + sprintf (cmd, "cp \"%s\" \"%s\"", from_path, to_path); +#ifdef DEBUG + printf ("BlendProgramCopyCurdir::handle_event: executing %s\n", cmd); +#endif + ret = system (cmd); + if (ret) + { + eprintf (_("Blend Program: copying %s to %s failed\nsee console printout for diagnostics\n"), + from_path, to_path); + return 1; + } + + // Copying successful, now change dir to the location of the target + file_box->fs->change_dir (dir); + file_box->update_paths (to_path); + file_box->update_history(); + file_box->reinit_path = 1; // set flag to reopen FileBox afterwards + file_box->set_done(1); // temporarily close FileBox, will be reopened later + + return 1; +} + +BlendProgramCopyUsrlib::BlendProgramCopyUsrlib(BlendProgramFileBox *file_box, + int x, int y) + : BC_GenericButton (x, y, _("Copy to userlib")) +{ + this->file_box = file_box; +} + +int BlendProgramCopyUsrlib::handle_event() +{ + int ret; + char *cp, fname[BCTEXTLEN], dir[BCTEXTLEN], from_path[BCTEXTLEN], + to_path[BCTEXTLEN], cmd[3*BCTEXTLEN]; + + dir[0] = 0; + cp = getenv ("CIN_USERLIB"); // $HOME/.bcast5lib by default + if (cp) strcpy (dir, cp); + if (! dir[0]) + { + cp = getenv ("HOME"); // evtl resolve as default via $HOME + if (cp) strcpy (dir, cp); + if (dir[0]) strcat (dir, "/.bcast5lib"); + else + { + cp = getenv ("CIN_CONFIG"); // or via $CIN_CONFIG as fallback + if (cp) strcpy (dir, cp); + if (dir[0]) strcat (dir, "lib"); + } + } + if (! dir[0]) return 1; // no user libdir known, no copy target + + // Evtl create user libdir, if not successful - do nothing else + if (! file_box->fs->is_dir (dir)) file_box->fs->create_dir (dir); + if (! file_box->fs->is_dir (dir)) return 1; + strcat (dir, "/dlfcn"); + if (! file_box->fs->is_dir (dir)) file_box->fs->create_dir (dir); + if (! file_box->fs->is_dir (dir)) return 1; + strcat (dir, "/bp"); + if (! file_box->fs->is_dir (dir)) file_box->fs->create_dir (dir); + if (! file_box->fs->is_dir (dir)) return 1; + + fname[0] = from_path[0] = 0; + const char *spath = file_box->get_submitted_path();// get name entered so far + if (spath) + { + strcpy (from_path, spath); // this is copy source + file_box->fs->extract_name (fname, spath); // cut name from source dir + } + if (! (fname[0] && from_path[0])) return 1; // no copy source ?? + + file_box->fs->join_names (to_path, dir, fname); // this is copy target + + if (! strcmp (from_path, to_path)) return 1; // source and target identical + + if (file_box->fs->is_dir (from_path) || file_box->fs->is_dir (to_path)) + return 1; // source and target must not be directories + if (access (from_path, R_OK)) + { + eprintf (_("Blend Program: source file %s does not exist or not readable\n"), + from_path); + return 1; + } + if (! access (to_path, F_OK)) + { + eprintf (_("Blend Program: target file %s exists, overwriting not allowed\n"), + to_path); + return 1; + } + + // Now do copy operation + sprintf (cmd, "cp \"%s\" \"%s\"", from_path, to_path); +#ifdef DEBUG + printf ("BlendProgramCopyUsrlib::handle_event: executing %s\n", cmd); +#endif + ret = system (cmd); + if (ret) + { + eprintf (_("Blend Program: copying %s to %s failed\nsee console printout for diagnostics\n"), + from_path, to_path); + return 1; + } + + return 1; // Copying successful, but don't change directory to user libdir +} + +BlendProgramFileEdit::BlendProgramFileEdit(BlendProgramFileBox *file_box, + int x, int y) + : BC_GenericButton (x, y, _("Edit...")) +{ + this->file_box = file_box; +} + +int BlendProgramFileEdit::handle_event() +{ + char fname[BCTEXTLEN], dir[BCTEXTLEN], str[2*BCTEXTLEN]; + + fname[0] = 0; + const char *spath = file_box->get_submitted_path();// get name entered so far + if (spath) strcpy (fname, spath); + if (! fname[0]) + { + eprintf (_("Blend Program: no program to edit, select source file first\n")); + return 1; + } + + // Evtl make function path absolute by prepending project path to it + if (fname[0] != '/') // fname is relative, prepend current project path + { + strcpy (dir, file_box->plugin->server->mwindow->session->filename); + if (dir[0]) + { + char *cp = strrchr (dir, '/'); + if (cp) + { + cp[1] = 0; // strip project filename off from project path + strcat (dir, fname); // concatenate obtained path with function name + strcpy (fname, dir); + } + } + } + if (file_box->fs->is_dir (fname)) + { + eprintf (_("Blend Program: cannot edit directory, select source file first\n")); + return 1; + } + + // This will run configured external editor via perl script + // If editor start is not backgrounded, GUI will block until editor exits + sprintf(str, "\"%s/dlfcn/BlendProgramCompile.pl\" -edit \"%s\"", + getenv("CIN_DAT"), fname); +#ifdef DEBUG + printf ("BlendProgramFileEdit::handle_event: executing:\n %s\n", str); +#endif + system (str); // runs configured external editor + + file_box->plugin->func_lock->lock("BlendProgramFileEdit::handle_event"); + BlendProgramTstamp = time(NULL); // force refresh of dlopen'd functions +#ifdef DEBUG + printf ("BlendProgramFileEdit::handle_event edited function %s\n timestamp %s", + fname, ctime(&BlendProgramTstamp)); +#endif + file_box->plugin->func_lock->unlock(); + + return 1; +} + +BlendProgramClipcolors::BlendProgramClipcolors(BlendProgram *plugin, + BlendProgramWindow *gui, + int x, int y) + : BC_CheckBox(x, y, plugin->config.clipcolors) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendProgramClipcolors::handle_event() +{ + plugin->config.clipcolors = get_value(); + plugin->send_configure_change(); + return 1; +} + +BlendProgramParallel::BlendProgramParallel(BlendProgram *plugin, + BlendProgramWindow *gui, + int x, int y) + : BC_CheckBox(x, y, plugin->config.parallel) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendProgramParallel::handle_event() +{ + plugin->config.parallel = get_value(); + plugin->send_configure_change(); + return 1; +} + +BlendProgramDirection::BlendProgramDirection(BlendProgram *plugin, int x, int y) + : BC_PopupMenu(x, y, xS(150), + BlendProgramConfig::direction_to_text(plugin->config.direction), 1) +{ + this->plugin = plugin; +} + +void BlendProgramDirection::create_objects() +{ + add_item(new BC_MenuItem(BlendProgramConfig::direction_to_text( + BlendProgramConfig::TOP_FIRST))); + add_item(new BC_MenuItem(BlendProgramConfig::direction_to_text( + BlendProgramConfig::BOTTOM_FIRST))); +} + +int BlendProgramDirection::handle_event() +{ + char *text = get_text(); + + if(!strcmp(text, BlendProgramConfig::direction_to_text( + BlendProgramConfig::TOP_FIRST))) + plugin->config.direction = BlendProgramConfig::TOP_FIRST; + else if(!strcmp(text, BlendProgramConfig::direction_to_text( + BlendProgramConfig::BOTTOM_FIRST))) + plugin->config.direction = BlendProgramConfig::BOTTOM_FIRST; + + plugin->send_configure_change(); + return 1; +} + +BlendProgramColorspace::BlendProgramColorspace(BlendProgram *plugin, + int x, int y) + : BC_PopupMenu(x, y, xS(150), + BlendProgramConfig::colorspace_to_text(plugin->config.colorspace), 1) +{ + this->plugin = plugin; +} + +void BlendProgramColorspace::create_objects() +{ + add_item(new BC_MenuItem(BlendProgramConfig::colorspace_to_text( + BlendProgramConfig::AUTO))); + add_item(new BC_MenuItem(BlendProgramConfig::colorspace_to_text( + BlendProgramConfig::RGB))); + add_item(new BC_MenuItem(BlendProgramConfig::colorspace_to_text( + BlendProgramConfig::YUV))); + add_item(new BC_MenuItem(BlendProgramConfig::colorspace_to_text( + BlendProgramConfig::HSV))); + add_item(new BC_MenuItem(BlendProgramConfig::colorspace_to_text( + BlendProgramConfig::PROJECT))); +} + +int BlendProgramColorspace::handle_event() +{ + char *text = get_text(); + + if(!strcmp(text, BlendProgramConfig::colorspace_to_text( + BlendProgramConfig::AUTO))) + plugin->config.colorspace = BlendProgramConfig::AUTO; + else if(!strcmp(text, BlendProgramConfig::colorspace_to_text( + BlendProgramConfig::RGB))) + plugin->config.colorspace = BlendProgramConfig::RGB; + else if(!strcmp(text, BlendProgramConfig::colorspace_to_text( + BlendProgramConfig::YUV))) + plugin->config.colorspace = BlendProgramConfig::YUV; + else if(!strcmp(text, BlendProgramConfig::colorspace_to_text( + BlendProgramConfig::HSV))) + plugin->config.colorspace = BlendProgramConfig::HSV; + else if(!strcmp(text, BlendProgramConfig::colorspace_to_text( + BlendProgramConfig::PROJECT))) + plugin->config.colorspace = BlendProgramConfig::PROJECT; + + plugin->send_configure_change(); + return 1; +} + +BlendProgramKeyColor::BlendProgramKeyColor (BlendProgram *plugin, + BlendProgramWindow *gui, + int x, int y) + : BC_GenericButton (x, y, _("Select key color...")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendProgramKeyColor::handle_event() +{ + gui->color_thread->start_window (plugin->config.get_key_color(), 0xff); + return 1; +} + +BlendProgramColorPicker::BlendProgramColorPicker (BlendProgram *plugin, + BlendProgramWindow *gui, + int x, int y) + : BC_GenericButton (x, y, _("Get from color picker")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendProgramColorPicker::handle_event() +{ + plugin->config.red = plugin->get_red(); + plugin->config.green = plugin->get_green(); + plugin->config.blue = plugin->get_blue(); + + gui->update_key_sample(); + + plugin->send_configure_change(); + return 1; +} + +BlendProgramColorThread::BlendProgramColorThread (BlendProgram * plugin, + BlendProgramWindow * gui) + : ColorPicker (0, _("Select color")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int BlendProgramColorThread::handle_new_color (int output, int alpha) +{ + plugin->config.red = (float) ((output & 0xff0000) >> 16) / 255; + plugin->config.green = (float) ((output & 0x00ff00) >> 8) / 255; + plugin->config.blue = (float) ((output & 0x0000ff) ) / 255; + + get_gui()->unlock_window(); + gui->lock_window("BlendProgramColorThread::handle_new_color"); + gui->update_key_sample(); + gui->unlock_window(); + get_gui()->lock_window("BlendProgramColorThread::handle_new_color"); + + plugin->send_configure_change(); + return 1; +} + +BlendProgramAlphaText::BlendProgramAlphaText(BlendProgram *plugin, + BlendProgramWindow *gui, + BlendProgramAlphaSlider *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); +} + +BlendProgramAlphaText::~BlendProgramAlphaText() +{ +} + +int BlendProgramAlphaText::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; +} + +BlendProgramAlphaSlider::BlendProgramAlphaSlider(BlendProgram *plugin, + BlendProgramAlphaText *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 +} + +BlendProgramAlphaSlider::~BlendProgramAlphaSlider() +{ +} + +int BlendProgramAlphaSlider::handle_event() +{ + *output = get_value(); + text->update(*output); + + plugin->send_configure_change(); + return 1; +} + +BlendProgramWindow::BlendProgramWindow(BlendProgram *plugin) + : PluginClientWindow(plugin, xS(450), yS(380), xS(450), yS(380), 0) +{ + this->plugin = plugin; + color_thread = 0; + editing_lock = new Mutex("BlendProgramWindow::editing_lock"); + editing = 0; +} + +BlendProgramWindow::~BlendProgramWindow() +{ + delete color_thread; + delete editing_lock; +} + +void BlendProgramWindow::create_objects() +{ + int xs5 = xS(5), xs10 = xS(10), xs20 = xS(20); + int ys5 = yS(5), ys10 = yS(10), ys20 = yS(20), ys30 = yS(30), ys40 = yS(40); + int x = xs10, y = ys10, x2; + BC_Title *title; + BC_TitleBar *title_bar; + + // Programming section + add_subwindow (title_bar = + new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, + _("Blend programming environment"))); + + y += ys30; + add_subwindow(title = new BC_Title(x, y, _("Program:"))); + add_subwindow(funcname = + new BlendProgramFuncname(plugin, plugin->config.funcname, this, + x + title->get_w() + xs5, y)); + + y += ys30; + add_subwindow(file_button = new BlendProgramFileButton(plugin, this, x, y)); + x2 = x+file_button->get_w()+xs5; + add_subwindow(edit_button = new BlendProgramEdit(plugin, this, x2, y)); + x2 += edit_button->get_w()+xs5; + add_subwindow(refresh_button = new BlendProgramRefresh(plugin, this, x2, y)); + x2 += refresh_button->get_w()+xs5; + add_subwindow(detach_button = new BlendProgramDetach(plugin, this, x2, y)); + + y += ys30; + add_subwindow(title = new BC_Title(x, y, _("Color space:"))); + add_subwindow(colorspace = + new BlendProgramColorspace(plugin, + x + title->get_w() + xs5, y)); + colorspace->create_objects(); + + x2 = x+title->get_w()+colorspace->get_w()+xs10+xs10; + add_subwindow(title = new BC_Title(x2, y, _("Parallelize processing"))); + add_subwindow(parallel = + new BlendProgramParallel(plugin, this, + x2 + title->get_w() + xs5, y)); + + // Supplementary color section + y += ys40; + add_subwindow (title_bar = + new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, + _("Supplementary color selection"))); + + y += ys30; + add_subwindow(title = + new BC_Title(x, y, _("Chroma key or substitution color:"))); + add_subwindow(key_sample = new BC_SubWindow(x + title->get_w() + xs5, y, + xS(150), yS(50))); + y += ys20+ys5; + add_subwindow(title = new BC_Title(x, y, _("Clip color values"))); + add_subwindow(clipcolors = + new BlendProgramClipcolors(plugin, this, + x + title->get_w() + xs5, y)); + + y += ys30+ys5; + add_subwindow(key_color = new BlendProgramKeyColor(plugin, this, x, y)); + x2 = x+key_color->get_w()+xs5; + add_subwindow(color_picker = + new BlendProgramColorPicker(plugin, this, x2, y)); + + y += ys30+ys5; + add_subwindow(title = new BC_Title(x, y, _("Substitution opacity:"))); + alpha_text = new BlendProgramAlphaText (plugin, this, 0, + x+title->get_w()+xs10+xs5+xS(210), + y, 0, 1, &plugin->config.alpha); + alpha_text->create_objects(); + key_alpha = new BlendProgramAlphaSlider (plugin, alpha_text, + x + title->get_w() + xs5, y, + xS(210), 0, 1, + &plugin->config.alpha); + add_subwindow(key_alpha); + alpha_text->slider = key_alpha; + + // Track arrangement section + y += ys40; + add_subwindow (title_bar = + new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, + _("Processed tracks arrangement"))); + + y += ys30; + add_subwindow(title = new BC_Title(x, y, _("Track order:"))); + add_subwindow(direction = + new BlendProgramDirection(plugin, + x + title->get_w() + xs5, y)); + direction->create_objects(); + + color_thread = new BlendProgramColorThread(plugin, this); + + update_key_sample(); + show_window(); + flush(); +} + +void BlendProgramWindow::update_key_sample() +{ + key_sample->set_color (plugin->config.get_key_color()); + key_sample->draw_box (0, 0, key_sample->get_w(), key_sample->get_h()); + key_sample->set_color (BLACK); + key_sample->draw_rectangle (0, 0, key_sample->get_w(), key_sample->get_h()); + key_sample->flash (); +} + +void BlendProgramWindow::done_event() +{ + color_thread->close_window(); +} + +int BlendProgramWindow::close_event() +{ + color_thread->close_window(); + file_button->stop(); + set_done(1); + return 1; +} + +int BlendProgramWindow::hide_window (int flush) +{ + color_thread->close_window(); + file_button->stop(); + return BC_WindowBase::hide_window (flush); +} + +//////////////////////////////////////////// +// Plugin main class implementation +//////////////////////////////////////////// + +BlendProgramFunc::BlendProgramFunc() +{ + src[0] = 0; + handle = 0; + proc = 0; + init = 0; + tstamp = -1; +} + +BlendProgramFunc::~BlendProgramFunc() +{ + if (handle) + { +#ifdef DEBUG + printf ("BlendProgramFunc destructor detaching function dlclose(%s)\n", + src); +#endif + dlclose (handle); + } +} + +BlendProgram::BlendProgram(PluginServer *server) + : PluginVClient(server) +{ + BlendProgramTstamp = time(NULL); + inspect_configuration = 1; // force initial configuration + curr_func_no = -1; + func_lock = new Mutex("BlendProgram::func_lock"); + engine = 0; +#ifdef DEBUG + printf ("BlendProgram constructor timestamp %s", ctime(&BlendProgramTstamp)); +#endif +} + +BlendProgram::~BlendProgram() +{ + if (engine) delete engine; + delete func_lock; +#ifdef DEBUG + printf ("BlendProgram destructor removing all %d functions\n", + funclist.total); +#endif + funclist.remove_all_objects(); + curr_func.handle = 0; +} + +int BlendProgram::process_buffer(VFrame **frame, + int64_t start_position, + double frame_rate) +{ + BlendProgramFunc *ptr; + int refresh_eprintf = 0; + + // Mocking up with function shared object if it might get modified + // Not sure if plugin locking is needed for this separate processing instance + func_lock->lock("BlendProgram::process_buffer"); + + // First check if function name was changed + if (load_configuration() || inspect_configuration) // function might change + { + inspect_configuration = 0; // do once after change or after creation + if (strcmp (curr_func.src, config.funcname)) // function changed + { + curr_func.src[0] = 0; + curr_func.handle = 0; + curr_func.proc = 0; + curr_func.init = 0; + curr_func.tstamp = -1; + curr_func_no = -1; + if (config.funcname[0]) // function name not empty + { + refresh_eprintf = 1; // probably new function + strcpy (curr_func.src, config.funcname); +#ifdef DEBUG + printf ("BlendProgram::process_buffer searching function %s out of %d\n", + curr_func.src, funclist.total); +#endif + for (int i=0; isrc)) // cached function found + { + curr_func.handle = ptr->handle; + curr_func.proc = ptr->proc; + curr_func.init = ptr->init; + curr_func.tstamp = ptr->tstamp; + curr_func_no = i; +#ifdef DEBUG + printf ("BlendProgram::process_buffer cached function %s found: %d\n timestamp %s", + ptr->src, curr_func_no, ctime(&ptr->tstamp)); +#endif + break; + } // if cached function found + } // for funclist.total + } // if function name not empty + } // if function changed + } // if load_configuration() + + // Now ensure that function binary is up to date, evtl recompile/relink it + if (curr_func.src[0]) // current function not empty + { + if (curr_func.tstamp == -1 || BlendProgramTstamp > curr_func.tstamp) + { // function first seen or linked before last config change + char str[BCTEXTLEN*2], dir[BCTEXTLEN], path[BCTEXTLEN]; + time_t tstamp = -1; + + // Evtl make function path absolute by prepending project path to it + strcpy (path, curr_func.src); + if (path[0] != '/') // path is relative, prepend current project path + { + strcpy (dir, server->mwindow->session->filename); + if (dir[0]) + { + char *cp = strrchr (dir, '/'); + if (cp) + { + cp[1] = 0; // strip project filename off from project path + strcat (dir, path); // concatenate obtained path with function name + strcpy (path, dir); + } + } + } + + // Try to lock function filename against concurrent compiler runs + // This kind of locking seems definitely reasonable here + struct flock locks; + locks.l_whence = SEEK_SET; + locks.l_start = locks.l_len = 0; + int fd = open (path, O_RDWR); + if (fd > -1) // if lock cannot be set, ignore this for now + { + locks.l_type = F_WRLCK; + fcntl (fd, F_SETLKW, &locks); // try to wait for lock, ignoring errors + sprintf(str, "\"%s/dlfcn/BlendProgramCompile.pl\" \"%s\"", + getenv("CIN_DAT"), path); +#ifdef DEBUG + printf ("BlendProgram::process_buffer\n curr_func.tstamp %s", + ctime(&curr_func.tstamp)); + printf (" global tstamp %s executing %s\n", + ctime(&BlendProgramTstamp), str); +#endif + system (str); // evtl recompile function if source newer than object + if (path[0] == '/') sprintf (str, "%s.so", path); + else sprintf (str, "./%s.so", path); // dlopen requires a slash + struct stat statbuf; + if (0 == stat (str, &statbuf)) tstamp = statbuf.st_mtime; + } + else // function source cannot be opened + { +#ifdef DEBUG + printf ("BlendProgram::process_buffer cannot access function %s\n", + curr_func.src); +#endif + } + + // Now test if function relinking needed, make function cache consistent + if (tstamp == -1) + { // either function does not exist or compilation unsuccessful + if (fd > -1) + eprintf (_("Blend Program: compilation of program %s failed\nsee console printout for diagnostics\n"), + curr_func.src); + if (curr_func_no >= 0) + { // detach old function + if (funclist[curr_func_no]->handle) + { +#ifdef DEBUG + printf ("BlendProgram::process_buffer detaching function %d dlclose(%s)\n", + curr_func_no, funclist[curr_func_no]->src); +#endif + dlclose (funclist[curr_func_no]->handle); + } +#ifdef DEBUG + printf ("BlendProgram::process_buffer removing function %d (%s)\n", + curr_func_no, curr_func.src); +#endif + funclist[curr_func_no]->src[0] = 0; + funclist[curr_func_no]->handle = 0; + funclist[curr_func_no]->proc = 0; + funclist[curr_func_no]->init = 0; + funclist[curr_func_no]->tstamp = -1; + funclist.remove_object_number (curr_func_no); + curr_func_no = -1; + } + curr_func.handle = 0; + curr_func.proc = 0; + curr_func.init = 0; + curr_func.tstamp = time (NULL); + } + else if (curr_func.tstamp == -1 || tstamp > curr_func.tstamp) + { // function first seen or edited after linkage +#ifdef DEBUG + printf ("BlendProgram::process_buffer\n curr_func.tstamp %s", + ctime(&curr_func.tstamp)); + printf (" tstamp %s relinking %s\n", ctime(&tstamp), str); +#endif + if (curr_func_no >= 0 && funclist[curr_func_no]->handle) + { // detach old function +#ifdef DEBUG + printf ("BlendProgram::process_buffer detaching function %d dlclose(%s)\n", + curr_func_no, funclist[curr_func_no]->src); +#endif + dlclose (funclist[curr_func_no]->handle); + funclist[curr_func_no]->src[0] = 0; + funclist[curr_func_no]->handle = 0; + funclist[curr_func_no]->proc = 0; + funclist[curr_func_no]->init = 0; + funclist[curr_func_no]->tstamp = -1; + } + curr_func.proc = 0; + curr_func.init = 0; + curr_func.handle = dlopen (str, RTLD_NOW); // shared object handle +#ifdef DEBUG + printf ("BlendProgram::process_buffer dlopen(%s)=%p\n", + str, curr_func.handle); +#endif + if (curr_func.handle) // inquire necessary extern entry points + { // bpProc is mandatory, bpInit optional + curr_func.init = (BPF_init) dlsym (curr_func.handle, "bpInit"); + if (curr_func.init == NULL) // not a problem, we can continue + printf (_("Blend Program: optional entry point \"bpInit\" for program %s not found:\n%s\n"), + str, dlerror()); + curr_func.proc = (BPF_proc) dlsym (curr_func.handle, "bpProc"); +#ifdef DEBUG + printf ("BlendProgram::process_buffer dlsym(%s) init=%p proc=%p\n", + curr_func.src, curr_func.init, curr_func.proc); +#endif + if (curr_func.proc == NULL) // nothing to do if this not working + { + eprintf (_("Blend Program: entry point \"bpProc\" for program %s not found:\n%s\n"), + str, dlerror()); +#ifdef DEBUG + printf ("BlendProgram::process_buffer dlclose(%s)\n", + curr_func.src); +#endif + dlclose (curr_func.handle); + curr_func.handle = 0; + curr_func.init = 0; + } + } + else + eprintf (_("Blend Program: dynamic load of program %s failed:\n%s\n"), + str, dlerror()); + curr_func.tstamp = time (NULL); + if (curr_func_no >= 0) // function was already in cache + { + if (curr_func.proc) // update object in cache + { + strcpy (funclist[curr_func_no]->src, curr_func.src); + funclist[curr_func_no]->handle = curr_func.handle; + funclist[curr_func_no]->proc = curr_func.proc; + funclist[curr_func_no]->init = curr_func.init; + funclist[curr_func_no]->tstamp = curr_func.tstamp; +#ifdef DEBUG + printf ("BlendProgram::process_buffer function %d (%s) updated\n timestamp %s", + curr_func_no, curr_func.src, ctime(&curr_func.tstamp)); +#endif + } + else // remove outdated function + { +#ifdef DEBUG + printf ("BlendProgram::process_buffer removing function %d (%s)\n", + curr_func_no, curr_func.src); +#endif + funclist.remove_object_number (curr_func_no); + curr_func_no = -1; + } + } + else if (curr_func.proc) // add new linked function to cache + { + curr_func_no = funclist.total; + ptr = new BlendProgramFunc; + funclist.append (ptr); + strcpy (ptr->src, curr_func.src); + ptr->handle = curr_func.handle; + ptr->proc = curr_func.proc; + ptr->init = curr_func.init; + ptr->tstamp = curr_func.tstamp; +#ifdef DEBUG + printf ("BlendProgram::process_buffer function %d (%s) appended\n timestamp %s", + curr_func_no, ptr->src, ctime(&ptr->tstamp)); +#endif + } // if function in cache + } + else // function does not need relinking + { + curr_func.tstamp = time (NULL); + if (curr_func_no >= 0) // just update timestamp + funclist[curr_func_no]->tstamp = curr_func.tstamp; +#ifdef DEBUG + printf ("BlendProgram::process_buffer function %s does not need relinking\n cache number %d timestamp %s", + curr_func.src, curr_func_no, ctime(&curr_func.tstamp)); +#endif + } // if tstamp + + if (fd > -1) // unlock function + { + locks.l_type = F_UNLCK; + fcntl (fd, F_SETLK, &locks); + close (fd); + } + } // if function first seen or changed + } // if current function not empty + + func_lock->unlock(); // end mocking up with function shared object + + // Now prepare the important pars and read all involved frames... + layers = get_total_buffers(); + width = frame[0]->get_w(); + height = frame[0]->get_h(); + for (int l=0; lframe = frame; + + if (curr_func.proc == NULL) return 0; // no function, nothing to do + + color_proj = frame[0]->get_color_model(); // internal colorspace of project + if (color_proj == BC_RGBA_FLOAT || + color_proj == BC_RGBA8888 || + color_proj == BC_YUVA8888) + has_alpha = 1; // has alpha channel + else has_alpha = 0; + color_work = config.colorspace; // will be requested from the function + int color_arg = color_work; + int min_layers = layers; // function's min required no of tracks + int parallel = 0; // assumed not parallelized by default + if (curr_func.init != NULL) // ask function about important pars + curr_func.init (&color_arg, color_proj, &min_layers, layers, + ¶llel, config.parallel, width, height, has_alpha); + if (min_layers > layers) + { + if (refresh_eprintf) + eprintf (_("Blend Program: cannot execute program %s:\nrequires %d tracks to process, has only %d tracks\n"), + curr_func.src, min_layers, layers); + return 0; // too few tracks to do anything + } + if (color_work == BlendProgramConfig::AUTO) color_work = color_arg; + if (color_work == BlendProgramConfig::AUTO) + color_work = BlendProgramConfig::PROJECT; // still not defined, dont change + if (! config.parallel) parallel = 0; // parallelism not requested + + // In case of a fatal bug in the user defined function (SIGFPE, SIGSEGV,...) + // Cinelerra perhaps will crash. Unfortunately we cannot handle the signals + // here: signal handler as set by sigaction() is process-wide, the same + // for all threads. And Cinelerra already uses its own signal handler + // for debug purposes which we are not allowed to overwrite with our one. + // Infinities and NaN will be trapped and substituted with configured color. + // Here we prepare key color components in three possible color spaces + // from this configured color. Used to substitute NaN or infinities. + // Can be used also inside user's function like a chroma key. + rgb_r = config.red; // user's configured color is always RGB + rgb_g = config.green; + rgb_b = config.blue; + YUV::yuv.rgb_to_yuv_f (rgb_r, rgb_g, rgb_b, yuv_y, yuv_u, yuv_v); // make YUV + HSV::rgb_to_hsv (rgb_r, rgb_g, rgb_b, hsv_h, hsv_s, hsv_v); // make HSV + key_a = config.alpha; // user's configured alpha + + if (parallel) // parallelism desired, and supoported by the function + { + if (! engine) + engine = new BlendProgramEngine (this, + get_project_smp() + 1, + get_project_smp() + 1); + engine->process_packages(); + } + else process_frames (0, height); // process everything sequential + + return 0; // WHEW !!! +} + +// Now comes the whole math. User's function will get everything in float. +// If the project's color model is 8-bit, pixels will be converted to float. +// Then, if requested, pixels will be converted to working color space +// (RGB, YUV, or HSV) which is required by the function. After processing, +// all the conversions will be rolled back in the reverse order. +// This universal function is called via loadbalance multithreading engine +// as well as directly if parallelism not requested or not supported + +void BlendProgram::process_frames (int y1, int y2) +{ + float r[layers], g[layers], b[layers], a[layers]; + float rk, gk, bk, yk, uk, vk; + int k, l, start, step; + + if (config.direction == BlendProgramConfig::BOTTOM_FIRST) + { + start = layers-1; + step = -1; + } + else + { + start = 0; + step = 1; + } + + int clip_colors = config.clipcolors; // clipping floats is optional + yk = uk = vk = 0; // to make gcc -O2 happy + + switch (color_proj) + { + case BC_RGB_FLOAT: // RGB [ 0 .. 1 ], out of bounds possible + case BC_RGBA_FLOAT: // RGBA [ 0 .. 1 ], out of bounds possible + for (int i=y1; iget_rows()[i]; + for (int j=0; j= 360)) + r[k] -= floor(r[k]/360)*360; // cannot clamp infinity here + CLAMP (g[k], 0, 1); + CLAMP (b[k], 0, 1); + } + if (! (isfinite(r[k]) && isfinite(g[k]) && + isfinite(b[k]) && isfinite(a[k]))) + { // substitute NaN or unclipped infinity with user's color (HSV) + r[k] = hsv_h; + g[k] = hsv_s; + b[k] = hsv_v; + a[k] = key_a; + } + HSV::hsv_to_rgb (row[l][0], row[l][1], row[l][2], + r[k], // H + g[k], // S + b[k]); // V + } + else // either RGB or by PROJECT, no change, clip only + { + if (clip_colors) + { + CLAMP (r[k], 0, 1); + CLAMP (g[k], 0, 1); + CLAMP (b[k], 0, 1); + } + if (! (isfinite(r[k]) && isfinite(g[k]) && + isfinite(b[k]) && isfinite(a[k]))) + { // substitute NaN or unclipped infinity with user's color (RGB) + r[k] = rgb_r; + g[k] = rgb_g; + b[k] = rgb_b; + a[k] = key_a; + } + row[l][0] = r[k]; + row[l][1] = g[k]; + row[l][2] = b[k]; + } // if color_work + if (! has_alpha) // evtl simulate alpha channel + { + row[l][0] *= a[k]; + row[l][1] *= a[k]; + row[l][2] *= a[k]; + } + if (has_alpha) // store real alpha channel + { + row[l][3] = a[k]; + row[l] += 4; + } + else row[l] += 3; // no alpha channel + k += step; + } // scan tracks for l = 0 .. layers + } // scan pixels for j = 0 .. width + } // scan rows for i = y1 .. y2 + break; + case BC_RGB888: // RGB [ 0 .. 1 ], must be in bounds + case BC_RGBA8888: // RGBA [ 0 .. 1 ], must be in bounds + for (int i=y1; iget_rows()[i]; + for (int j=0; j= 360)) + r[k] -= floor(r[k]/360)*360; // cannot clamp infinity here + CLAMP (g[k], 0, 1); + CLAMP (b[k], 0, 1); + } + if (! (isfinite(r[k]) && isfinite(g[k]) && + isfinite(b[k]) && isfinite(a[k]))) + { // substitute NaN or unclipped infinity with user's color (HSV) + r[k] = hsv_h; + g[k] = hsv_s; + b[k] = hsv_v; + a[k] = key_a; + } + HSV::hsv_to_rgb (rk, gk, bk, + r[k], // H + g[k], // S + b[k]); // V + } + else // either RGB or by PROJECT, no change, clip only + { + if (clip_colors) + { + CLAMP (r[k], 0, 1); + CLAMP (g[k], 0, 1); + CLAMP (b[k], 0, 1); + } + if (! (isfinite(r[k]) && isfinite(g[k]) && + isfinite(b[k]) && isfinite(a[k]))) + { // substitute NaN or unclipped infinity with user's color (RGB) + r[k] = rgb_r; + g[k] = rgb_g; + b[k] = rgb_b; + a[k] = key_a; + } + rk = r[k]; + gk = g[k]; + bk = b[k]; + } // if color_work + if (! has_alpha) // evtl simulate alpha channel + { + rk *= a[k]; + gk *= a[k]; + bk *= a[k]; + } + row[l][0] = (unsigned char) CLIP (rk*255, 0, 255); // reformat / clip + row[l][1] = (unsigned char) CLIP (gk*255, 0, 255); + row[l][2] = (unsigned char) CLIP (bk*255, 0, 255); + if (has_alpha) // store real alpha channel + { + row[l][3] = (unsigned char) CLIP (a[k]*255, 0, 255); + row[l] += 4; + } + else row[l] += 3; // no alpha channel + k += step; + } // scan tracks for l = 0 .. layers + } // scan pixels for j = 0 .. width + } // scan rows for i = y1 .. y2 + break; + case BC_YUV888: // R [ 0 .. 1 ], GB [ -0.5 .. 0.5 ], must be in bounds + case BC_YUVA8888: // RA [ 0 .. 1 ], GB [ -0.5 .. 0.5 ], must be in bounds + for (int i=y1; iget_rows()[i]; + for (int j=0; j= 360)) + r[k] -= floor(r[k]/360)*360; // cannot clamp infinity here + CLAMP (g[k], 0, 1); + CLAMP (b[k], 0, 1); + } + if (! (isfinite(r[k]) && isfinite(g[k]) && + isfinite(b[k]) && isfinite(a[k]))) + { // substitute NaN or unclipped infinity with user's color (HSV) + r[k] = hsv_h; + g[k] = hsv_s; + b[k] = hsv_v; + a[k] = key_a; + } // H S V + HSV::hsv_to_rgb (rk, gk, bk, r[k], g[k], b[k]); + if (clip_colors) + { + CLAMP (r[k], 0, 1); + CLAMP (g[k], 0, 1); + CLAMP (b[k], 0, 1); + } + YUV::yuv.rgb_to_yuv_f (rk, gk, bk, yk, uk, vk); + } + else // either YUV or by PROJECT, no change, clip only + { + if (clip_colors) + { + CLAMP (r[k], 0, 1); + CLAMP (g[k], -0.5, 0.5); + CLAMP (b[k], -0.5, 0.5); + } + if (! (isfinite(r[k]) && isfinite(g[k]) && + isfinite(b[k]) && isfinite(a[k]))) + { // substitute NaN or unclipped infinity with user's color (YUV) + r[k] = yuv_y; + g[k] = yuv_u; + b[k] = yuv_v; + a[k] = key_a; + } + yk = r[k]; + uk = g[k]; + vk = b[k]; + } // if color_work + if (! has_alpha) // evtl simulate alpha channel + { + yk *= a[k]; + uk *= a[k]; + vk *= a[k]; + } + row[l][0] = (unsigned char) CLIP (yk*255, 0, 255); // reformat + row[l][1] = (unsigned char) CLIP ((uk+0.5)*256, 0, 255); + row[l][2] = (unsigned char) CLIP ((vk+0.5)*256, 0, 255); + if (has_alpha) // store real alpha channel + { + row[l][3] = (unsigned char) CLIP (a[k]*255, 0, 255); + row[l] += 4; + } + else row[l] += 3; // no alpha channel + k += step; + } // scan tracks for l = 0 .. layers + } // scan pixels for j = 0 .. width + } // scan rows for i = y1 .. y2 + break; + default: + break; + } // switch color_proj +} // WHEW !!! + +void BlendProgram::save_data(KeyFrame *keyframe) +{ + FileXML output; + + output.set_shared_output(keyframe->xbuf); + + output.tag.set_title("BLEND_PROGRAM"); + output.tag.set_property("FUNCNAME", config.funcname); + output.tag.set_property("PARALLEL", config.parallel); + output.tag.set_property("DIRECTION", config.direction); + output.tag.set_property("COLORSPACE", config.colorspace); + output.tag.set_property("CLIPCOLORS", config.clipcolors); + 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("ALPHA", config.alpha); + output.append_tag(); + output.tag.set_title("/BLEND_PROGRAM"); + output.append_tag(); + output.append_newline(); + output.terminate_string(); +} + +void BlendProgram::read_data(KeyFrame *keyframe) +{ + FileXML input; + + input.set_shared_input(keyframe->xbuf); + + while(!input.read_tag()) + { + if(input.tag.title_is("BLEND_PROGRAM")) + { + input.tag.get_property("FUNCNAME", config.funcname); + config.parallel = input.tag.get_property("PARALLEL", config.parallel); + config.direction = input.tag.get_property("DIRECTION", config.direction); + config.colorspace = + input.tag.get_property("COLORSPACE", config.colorspace); + config.clipcolors = + input.tag.get_property("CLIPCOLORS", config.clipcolors); + 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.alpha = input.tag.get_property("ALPHA", config.alpha); + } + } +} + +void BlendProgram::update_gui() +{ + if( ! thread ) return; + if( ! (load_configuration() || inspect_configuration) ) return; + inspect_configuration = 0; // update once after change or after creation + thread->window->lock_window("BlendProgram::update_gui"); + BlendProgramWindow *window = (BlendProgramWindow*)thread->window; + + window->funcname->update(config.funcname); + window->parallel->update(config.parallel); + window->clipcolors->update(config.clipcolors); + window->direction->set_text( + BlendProgramConfig::direction_to_text(config.direction)); + window->colorspace->set_text( + BlendProgramConfig::colorspace_to_text(config.colorspace)); + window->update_key_sample(); + window->alpha_text->update(config.alpha); + window->key_alpha->update(config.alpha); + + thread->window->unlock_window(); +} + +//////////////////////////////////////////// +// Multithreaded processing stuff +//////////////////////////////////////////// + +BlendProgramEngine::BlendProgramEngine(BlendProgram *plugin, + int total_clients, + int total_packages) + : LoadServer(total_clients, total_packages) +{ + this->plugin = plugin; +} + +void BlendProgramEngine::init_packages () +{ + for (int i=0; iy1 = plugin->height * i / get_total_packages (); + pkg->y2 = plugin->height * (i + 1) / get_total_packages (); + } +} + +LoadClient *BlendProgramEngine::new_client () +{ + return new BlendProgramUnit (plugin, this); +} + +LoadPackage *BlendProgramEngine::new_package () +{ + return new BlendProgramPackage; +} + +BlendProgramPackage::BlendProgramPackage() + : LoadPackage() +{ +} + +BlendProgramUnit::BlendProgramUnit (BlendProgram *plugin, + BlendProgramEngine *engine) + : LoadClient (engine) +{ + this->plugin = plugin; + this->engine = engine; +} + +void BlendProgramUnit::process_package(LoadPackage *package) +{ + BlendProgramPackage *pkg = (BlendProgramPackage *) package; + + plugin->process_frames (pkg->y1, pkg->y2); +} diff --git a/cinelerra-5.1/plugins/blendprogram/blendprogram.h b/cinelerra-5.1/plugins/blendprogram/blendprogram.h new file mode 100644 index 00000000..12646582 --- /dev/null +++ b/cinelerra-5.1/plugins/blendprogram/blendprogram.h @@ -0,0 +1,430 @@ +/* + * CINELERRA + * Copyright (C) 2008 Adam Williams + * + * 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 BLENDPROGRAM_H +#define BLENDPROGRAM_H + +#include "guicast.h" +#include "loadbalance.h" +#include "colorpicker.h" +#include "pluginvclient.h" + +// Several forward declarations + +class BlendProgram; +class BlendProgramConfig; +class BlendProgramWindow; +class BlendProgramAlphaText; +class BlendProgramAlphaSlider; +class BlendProgramFileBox; + +// Plugin configuration class definition + +class BlendProgramConfig +{ +public: + BlendProgramConfig(); + + int equivalent(BlendProgramConfig &that); + void copy_from(BlendProgramConfig &that); + void interpolate(BlendProgramConfig &prev, + BlendProgramConfig &next, + int64_t prev_frame, + int64_t next_frame, + int64_t current_frame); + + int get_key_color(); + + char funcname[BCTEXTLEN]; + int parallel; + int clipcolors; + + static const char *direction_to_text(int direction); + int direction; + enum + { + BOTTOM_FIRST, + TOP_FIRST + }; + + static const char *colorspace_to_text(int colorspace); + int colorspace; + enum + { + AUTO, // requested from function + RGB, + YUV, + HSV, + PROJECT // as defined in project settings + }; + + float red; // key color to substitute for NaN + float green; + float blue; + float alpha; // alpha to substitute for NaN +}; + +// Plugin dialog window class definition + +class BlendProgramFuncname : public BC_TextBox +{ +public: + BlendProgramFuncname(BlendProgram *plugin, const char *funcname, + BlendProgramWindow *gui, int x, int y); + int handle_event(); + BlendProgram *plugin; + BlendProgramWindow *gui; +}; + +class BlendProgramDetach : public BC_GenericButton +{ +public: + BlendProgramDetach(BlendProgram *plugin, BlendProgramWindow *gui, + int x, int y); + int handle_event(); + BlendProgram *plugin; + BlendProgramWindow *gui; +}; + +class BlendProgramEdit : public BC_GenericButton +{ +public: + BlendProgramEdit(BlendProgram *plugin, BlendProgramWindow *gui, + int x, int y); + int handle_event(); + BlendProgram *plugin; + BlendProgramWindow *gui; +}; + +class BlendProgramRefresh : public BC_GenericButton +{ +public: + BlendProgramRefresh(BlendProgram *plugin, BlendProgramWindow *gui, + int x, int y); + int handle_event(); + BlendProgram *plugin; + BlendProgramWindow *gui; +}; + +class BlendProgramToCurdir : public BC_GenericButton +{ +public: + BlendProgramToCurdir(BlendProgramFileBox *file_box, int x, int y); + int handle_event(); + BlendProgramFileBox *file_box; +}; + +class BlendProgramToUsrlib : public BC_GenericButton +{ +public: + BlendProgramToUsrlib(BlendProgramFileBox *file_box, int x, int y); + int handle_event(); + BlendProgramFileBox *file_box; +}; + +class BlendProgramToSyslib : public BC_GenericButton +{ +public: + BlendProgramToSyslib(BlendProgramFileBox *file_box, int x, int y); + int handle_event(); + BlendProgramFileBox *file_box; +}; + +class BlendProgramCopyCurdir : public BC_GenericButton +{ +public: + BlendProgramCopyCurdir(BlendProgramFileBox *file_box, int x, int y); + int handle_event(); + BlendProgramFileBox *file_box; +}; + +class BlendProgramCopyUsrlib : public BC_GenericButton +{ +public: + BlendProgramCopyUsrlib(BlendProgramFileBox *file_box, int x, int y); + int handle_event(); + BlendProgramFileBox *file_box; +}; + +class BlendProgramFileEdit : public BC_GenericButton +{ +public: + BlendProgramFileEdit(BlendProgramFileBox *file_box, int x, int y); + int handle_event(); + BlendProgramFileBox *file_box; +}; + +class BlendProgramFileBox : public BC_FileBox +{ +public: + BlendProgramFileBox(BlendProgram *plugin, BlendProgramWindow *gui, + char *init_path); + ~BlendProgramFileBox(); + void add_objects(); + int resize_event(int w, int h); + + BlendProgram *plugin; + BlendProgramWindow *gui; + BlendProgramToCurdir *to_curdir; + BlendProgramToUsrlib *to_usrlib; + BlendProgramToSyslib *to_syslib; + BlendProgramCopyCurdir *copy_curdir; + BlendProgramCopyUsrlib *copy_usrlib; + BlendProgramFileEdit *file_edit; + + int reinit_path; +}; + +class BlendProgramFileButton : public BC_GenericButton, public Thread +{ +public: + BlendProgramFileButton(BlendProgram *plugin, BlendProgramWindow *gui, + int x, int y); + ~BlendProgramFileButton(); + int handle_event(); + void run(); + void stop(); + BlendProgram *plugin; + BlendProgramWindow *gui; + BlendProgramFileBox *file_box; +}; + +class BlendProgramDirection : public BC_PopupMenu +{ +public: + BlendProgramDirection(BlendProgram *plugin, int x, int y); + void create_objects(); + int handle_event(); + BlendProgram *plugin; +}; + +class BlendProgramColorspace : public BC_PopupMenu +{ +public: + BlendProgramColorspace(BlendProgram *plugin, int x, int y); + void create_objects(); + int handle_event(); + BlendProgram *plugin; +}; + +class BlendProgramClipcolors : public BC_CheckBox +{ +public: + BlendProgramClipcolors(BlendProgram *plugin, BlendProgramWindow *gui, + int x, int y); + int handle_event(); + BlendProgram *plugin; + BlendProgramWindow *gui; +}; + +class BlendProgramParallel : public BC_CheckBox +{ +public: + BlendProgramParallel(BlendProgram *plugin, BlendProgramWindow *gui, + int x, int y); + int handle_event(); + BlendProgram *plugin; + BlendProgramWindow *gui; +}; + +class BlendProgramKeyColor : public BC_GenericButton +{ +public: + BlendProgramKeyColor(BlendProgram *plugin, BlendProgramWindow *gui, + int x, int y); + int handle_event(); + BlendProgram *plugin; + BlendProgramWindow *gui; +}; + +class BlendProgramColorPicker : public BC_GenericButton +{ +public: + BlendProgramColorPicker(BlendProgram *plugin, BlendProgramWindow *gui, + int x, int y); + int handle_event(); + BlendProgram *plugin; + BlendProgramWindow *gui; +}; + +class BlendProgramColorThread : public ColorPicker +{ +public: + BlendProgramColorThread(BlendProgram *plugin, BlendProgramWindow *gui); + int handle_new_color(int output, int alpha); + BlendProgram *plugin; + BlendProgramWindow *gui; +}; + +class BlendProgramAlphaText : public BC_TumbleTextBox +{ +public: + BlendProgramAlphaText(BlendProgram *plugin, BlendProgramWindow *gui, + BlendProgramAlphaSlider *slider, int x, int y, + float min, float max, float *output); + ~BlendProgramAlphaText(); + int handle_event(); + BlendProgram *plugin; + BlendProgramWindow *gui; + BlendProgramAlphaSlider *slider; + float *output; + float min, max; +}; + +class BlendProgramAlphaSlider : public BC_FSlider +{ +public: + BlendProgramAlphaSlider(BlendProgram *plugin, BlendProgramAlphaText *text, + int x, int y, int w, float min, float max, + float *output); + ~BlendProgramAlphaSlider(); + int handle_event(); + BlendProgram *plugin; + BlendProgramAlphaText *text; + float *output; +}; + +class BlendProgramWindow : public PluginClientWindow +{ +public: + BlendProgramWindow(BlendProgram *plugin); + ~BlendProgramWindow(); + + void create_objects(); + void update_key_sample(); + void done_event(); + int close_event(); + int hide_window(int flush=1); + + BlendProgram *plugin; + BlendProgramFuncname *funcname; + BlendProgramDirection *direction; + BlendProgramColorspace *colorspace; + BlendProgramClipcolors *clipcolors; + BlendProgramParallel *parallel; + BC_SubWindow *key_sample; + BlendProgramKeyColor *key_color; + BlendProgramColorPicker *color_picker; + BlendProgramColorThread *color_thread; + BlendProgramAlphaText *alpha_text; + BlendProgramAlphaSlider *key_alpha; + BlendProgramFileButton *file_button; + BlendProgramDetach *detach_button; + BlendProgramEdit *edit_button; + BlendProgramRefresh *refresh_button; + + Mutex *editing_lock; + int editing; +}; + +// For multithreading processing engine + +class BlendProgramEngine : public LoadServer +{ +public: + BlendProgramEngine(BlendProgram *plugin, + int total_clients, int total_packages); + void init_packages(); + LoadClient* new_client(); + LoadPackage* new_package(); + + BlendProgram *plugin; +}; + +class BlendProgramPackage : public LoadPackage +{ +public: + BlendProgramPackage(); + + int y1, y2; +}; + +class BlendProgramUnit : public LoadClient +{ +public: + BlendProgramUnit(BlendProgram *plugin, BlendProgramEngine *engine); + void process_package(LoadPackage *package); + + BlendProgram *plugin; + BlendProgramEngine *engine; +}; + +// Plugin main class definition + +// User function prototypes, C style, processing and init entries +typedef void (*BPF_proc) (int, float *, float *, float *, float *, + float, float, float, float, int, int, int, int, int); +typedef void (*BPF_init) (int *, int, int *, int, int *, int, int, int, int); + +class BlendProgramFunc +{ +public: + BlendProgramFunc(); + ~BlendProgramFunc(); + + char src[BCTEXTLEN]; // source filename + void *handle; // handle returned from dlopen() + BPF_proc proc; // main processing entry from dlsym() + BPF_init init; // optional initializing entry from dlsym() + time_t tstamp; // timestamp when function was last linked +}; + +class BlendProgram : public PluginVClient +{ +public: + BlendProgram(PluginServer *server); + ~BlendProgram(); + + PLUGIN_CLASS_MEMBERS(BlendProgramConfig); + + int process_buffer(VFrame **frame, int64_t start_position, double frame_rate); + int is_realtime(); + int is_multichannel(); + int is_synthesis(); + void save_data(KeyFrame *keyframe); + void read_data(KeyFrame *keyframe); + void update_gui(); + void process_frames(int y1, int y2); + + // this flag set in constructor, cleared after first load_configuration() + int inspect_configuration; + + int layers; // no of tracks + int width, height; // frame dimensions + int color_proj; // project color model + int color_work; // working color space in the function + int has_alpha; // 1 == has alpha channel + + // color components in three color spaces, used to substitute + // for NaN or infinity in invalid results, or as a chroma key + float rgb_r, rgb_g, rgb_b, yuv_y, yuv_u, yuv_v, hsv_h, hsv_s, hsv_v, key_a; + + BlendProgramFunc curr_func; // currently active entry point + int curr_func_no; // no of current entry in list + ArrayList funclist; // list of known entry points + + VFrame **frame; // pointer to frames to process + + Mutex *func_lock; + + BlendProgramEngine *engine; // for parallelized processing +}; + +#endif /* BLENDPROGRAM_H */ diff --git a/cinelerra-5.1/plugins/blendprogram/chromakey.bp b/cinelerra-5.1/plugins/blendprogram/chromakey.bp new file mode 100644 index 00000000..2d61ad90 --- /dev/null +++ b/cinelerra-5.1/plugins/blendprogram/chromakey.bp @@ -0,0 +1,28 @@ +/***********************************************-*-C-*-**********/ +/* Chromakey imitation program + * 1) Select suitable chromakey color via eg Compositor's color picker + * 2) Using opacity slider adjust hue deviation parameter for a best look + * For masking effect to be seen, either ensure placing an additional track + * filled with opaque background under the chromakeyed track, + * or use the background.bp blend program after chromakey.bp + * + * The formula below works as follows: + * Hue deviation from key > slider * 100: change nothing + * Hue deviation < slider * 100: multiply alpha by squared deviation/slider/100 + * Where hue exactly equals key, alpha will be zeroed + * + * For another hue coefficient instead of 100, define COEFF correspondingly + */ + +#define COEFF 100 + +BLEND_PROGRAM_INIT + +COLORSPACE_HSV +PARALLEL_SAFE + +BLEND_PROGRAM_PROC + +if (ABS(H(0)-KEY_H) < KEY_A*COEFF) A(0) *= SQR((H(0)-KEY_H)/KEY_A/COEFF); + +BLEND_PROGRAM_END -- 2.26.2