From 57f52692892b375273249525896cfd2004c77ab3 Mon Sep 17 00:00:00 2001 From: niels <> Date: Tue, 12 May 2026 00:46:16 +0200 Subject: [PATCH] add chrono vein fx --- .../veejay-server/libvje/effects/chronovein.c | 996 ++++++++++++++++++ .../veejay-server/libvje/effects/chronovein.h | 36 + 2 files changed, 1032 insertions(+) create mode 100644 veejay-current/veejay-server/libvje/effects/chronovein.c create mode 100644 veejay-current/veejay-server/libvje/effects/chronovein.h diff --git a/veejay-current/veejay-server/libvje/effects/chronovein.c b/veejay-current/veejay-server/libvje/effects/chronovein.c new file mode 100644 index 00000000..981b1445 --- /dev/null +++ b/veejay-current/veejay-server/libvje/effects/chronovein.c @@ -0,0 +1,996 @@ +/* + * Linux VeeJay + * + * Copyright(C)2026 Niels Elburg + * + * 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 "common.h" +#include +#include +#include +#include + +#define CHRONOVEIN_PARAMS 8 + +#define P_THRESHOLD 0 +#define P_GROWTH 1 +#define P_CONDUCTIVITY 2 +#define P_DECAY 3 +#define P_BRANCH 4 +#define P_SOURCE_BLEED 5 +#define P_COLOR_MODE 6 +#define P_PULSE 7 + +#define CV_COLOR_POLARITY 0 +#define CV_COLOR_THERMAL 1 +#define CV_COLOR_SOURCE 2 +#define CV_COLOR_ELECTRIC 3 +#define CV_COLOR_WHITE 4 + +typedef struct { + int w; + int h; + int len; + int seeded; + int frame; + int n_threads; + + uint8_t *prev_y; + uint8_t *ref_y; + + uint8_t *field; + uint8_t *next_field; + + uint8_t *polarity; + uint8_t *next_polarity; + + uint8_t event_lut[256]; + uint8_t decay_lut[256]; + uint8_t conduct_lut[256]; + uint8_t branch_lut[256]; + uint8_t bleed_y_lut[256]; + uint8_t bleed_uv_lut[256]; + uint8_t adapt_lut[256]; + + int lut_valid; + int last_threshold; + int last_growth; + int last_conductivity; + int last_decay; + int last_branch; + int last_source_bleed; +} chronovein_t; + +static inline int cv_clampi(int v, int lo, int hi) +{ + return v < lo ? lo : (v > hi ? hi : v); +} + +static inline int cv_absi(int v) +{ + return v < 0 ? -v : v; +} + +static inline uint8_t cv_blend_fast_u8(uint8_t a, uint8_t b, int amount) +{ + return (uint8_t) (((int) a * (256 - amount) + (int) b * amount) >> 8); +} + +vj_effect *chronovein_init(int w, int h) +{ + vj_effect *ve = (vj_effect *) vj_calloc(sizeof(vj_effect)); + if(!ve) + return NULL; + + ve->num_params = CHRONOVEIN_PARAMS; + + ve->defaults = (int *) vj_calloc(sizeof(int) * ve->num_params); + ve->limits[0] = (int *) vj_calloc(sizeof(int) * ve->num_params); + ve->limits[1] = (int *) vj_calloc(sizeof(int) * ve->num_params); + + if(!ve->defaults || !ve->limits[0] || !ve->limits[1]) { + if(ve->defaults) + free(ve->defaults); + if(ve->limits[0]) + free(ve->limits[0]); + if(ve->limits[1]) + free(ve->limits[1]); + free(ve); + return NULL; + } + + ve->limits[0][P_THRESHOLD] = 0; + ve->limits[1][P_THRESHOLD] = 255; + ve->defaults[P_THRESHOLD] = 18; + + ve->limits[0][P_GROWTH] = 0; + ve->limits[1][P_GROWTH] = 255; + ve->defaults[P_GROWTH] = 180; + + ve->limits[0][P_CONDUCTIVITY] = 0; + ve->limits[1][P_CONDUCTIVITY] = 255; + ve->defaults[P_CONDUCTIVITY] = 150; + + ve->limits[0][P_DECAY] = 0; + ve->limits[1][P_DECAY] = 255; + ve->defaults[P_DECAY] = 185; + + ve->limits[0][P_BRANCH] = 0; + ve->limits[1][P_BRANCH] = 255; + ve->defaults[P_BRANCH] = 96; + + ve->limits[0][P_SOURCE_BLEED] = 0; + ve->limits[1][P_SOURCE_BLEED] = 255; + ve->defaults[P_SOURCE_BLEED] = 12; + + ve->limits[0][P_COLOR_MODE] = 0; + ve->limits[1][P_COLOR_MODE] = 4; + ve->defaults[P_COLOR_MODE] = CV_COLOR_ELECTRIC; + + ve->limits[0][P_PULSE] = 0; + ve->limits[1][P_PULSE] = 255; + ve->defaults[P_PULSE] = 64; + + ve->description = "Chronovein"; + ve->sub_format = 1; + ve->extra_frame = 0; + ve->parallel = 0; + ve->has_user = 0; + + ve->param_description = vje_build_param_list( + ve->num_params, + "Threshold", + "Growth", + "Conductivity", + "Decay", + "Branch", + "Source Bleed", + "Color Mode", + "Pulse" + ); + + return ve; +} + +void *chronovein_malloc(int w, int h) +{ + chronovein_t *c; + + if(w <= 0 || h <= 0) + return NULL; + + c = (chronovein_t *) vj_calloc(sizeof(chronovein_t)); + if(!c) + return NULL; + + c->w = w; + c->h = h; + c->len = w * h; + c->seeded = 0; + c->frame = 0; + c->lut_valid = 0; + + c->n_threads = vje_advise_num_threads(w * h); + if(c->n_threads <= 0) + c->n_threads = 1; + + c->prev_y = (uint8_t *) vj_calloc(sizeof(uint8_t) * (size_t) c->len); + c->ref_y = (uint8_t *) vj_calloc(sizeof(uint8_t) * (size_t) c->len); + + c->field = (uint8_t *) vj_calloc(sizeof(uint8_t) * (size_t) c->len); + c->next_field = (uint8_t *) vj_calloc(sizeof(uint8_t) * (size_t) c->len); + + c->polarity = (uint8_t *) vj_calloc(sizeof(uint8_t) * (size_t) c->len); + c->next_polarity = (uint8_t *) vj_calloc(sizeof(uint8_t) * (size_t) c->len); + + if(!c->prev_y || !c->ref_y || !c->field || !c->next_field || + !c->polarity || !c->next_polarity) { + if(c->prev_y) + free(c->prev_y); + if(c->ref_y) + free(c->ref_y); + if(c->field) + free(c->field); + if(c->next_field) + free(c->next_field); + if(c->polarity) + free(c->polarity); + if(c->next_polarity) + free(c->next_polarity); + + free(c); + return NULL; + } + + return (void *) c; +} + +void chronovein_free(void *ptr) +{ + chronovein_t *c = (chronovein_t *) ptr; + + if(!c) + return; + + if(c->prev_y) + free(c->prev_y); + if(c->ref_y) + free(c->ref_y); + if(c->field) + free(c->field); + if(c->next_field) + free(c->next_field); + if(c->polarity) + free(c->polarity); + if(c->next_polarity) + free(c->next_polarity); + + free(c); +} + +static void cv_seed(chronovein_t *c, VJFrame *frame) +{ + uint8_t *Y = frame->data[0]; + + int i; + int len = c->len; + +#pragma omp parallel for schedule(static) num_threads(c->n_threads) + for(i = 0; i < len; i++) { + c->prev_y[i] = Y[i]; + c->ref_y[i] = Y[i]; + + c->field[i] = 0; + c->next_field[i] = 0; + + c->polarity[i] = 128; + c->next_polarity[i] = 128; + } + + c->seeded = 1; +} + +static void cv_build_luts_if_needed(chronovein_t *c, + int threshold, + int growth, + int conductivity, + int decay, + int branch, + int source_bleed) +{ + int i; + int denom; + + int growth_scale; + int conduct_power; + int branch_power; + + if(c->lut_valid && + c->last_threshold == threshold && + c->last_growth == growth && + c->last_conductivity == conductivity && + c->last_decay == decay && + c->last_branch == branch && + c->last_source_bleed == source_bleed) { + return; + } + + denom = 255 - threshold; + if(denom < 1) + denom = 1; + + growth_scale = (growth * 320 + 127) / 255; /* 0..320 */ + + conduct_power = (conductivity * decay + 127) / 255; + branch_power = (branch * decay + 127) / 255; + + conduct_power = (conduct_power * growth_scale + 128) >> 8; + branch_power = (branch_power * growth_scale + 128) >> 8; + + if(conduct_power > 255) + conduct_power = 255; + + if(branch_power > 255) + branch_power = 255; + + for(i = 0; i < 256; i++) { + int event_strength; + int excess; + int gain; + int mem; + + if(i > threshold) { + excess = i - threshold; + + gain = 128 + (growth >> 1); + + event_strength = (excess * gain + denom / 2) / denom; + if(event_strength > 255) + event_strength = 255; + } + else { + event_strength = 0; + } + + c->event_lut[i] = (uint8_t) event_strength; + c->decay_lut[i] = (uint8_t) ((i * decay + 127) / 255); + + c->conduct_lut[i] = (uint8_t) ((i * conduct_power + 127) / 255); + c->branch_lut[i] = (uint8_t) ((i * branch_power + 127) / 255); + + c->bleed_y_lut[i] = + (uint8_t) ((i * source_bleed + 127) / 255); + + c->bleed_uv_lut[i] = + (uint8_t) ((128 * (255 - source_bleed) + i * source_bleed + 127) / 255); + + mem = 8 + ((255 - decay) >> 3) + (i >> 4); + if(mem > 255) + mem = 255; + + c->adapt_lut[i] = (uint8_t) mem; + } + + c->last_threshold = threshold; + c->last_growth = growth; + c->last_conductivity = conductivity; + c->last_decay = decay; + c->last_branch = branch; + c->last_source_bleed = source_bleed; + c->lut_valid = 1; +} + +static inline int cv_source_edge_safe(uint8_t *restrict Y, + int w, + int h, + int x, + int y, + int pos) +{ + int gx = 0; + int gy = 0; + + if(x > 0 && x + 1 < w) + gx = cv_absi((int) Y[pos - 1] - (int) Y[pos + 1]); + + if(y > 0 && y + 1 < h) + gy = cv_absi((int) Y[pos - w] - (int) Y[pos + w]); + + return gx > gy ? gx : gy; +} + +static inline void cv_compute_one_safe(chronovein_t *c, + uint8_t *restrict Y, + int x, + int y, + int pos, + int use_conduct, + int use_branch) +{ + int w = c->w; + int h = c->h; + + uint8_t *restrict F = c->field; + uint8_t *restrict NF = c->next_field; + uint8_t *restrict P = c->polarity; + uint8_t *restrict NP = c->next_polarity; + uint8_t *restrict REF = c->ref_y; + + uint8_t cy = Y[pos]; + + int ref = REF[pos]; + int diff = (int) cy - ref; + int ad = diff < 0 ? -diff : diff; + + int event_strength = c->event_lut[ad]; + int event_for_adapt = event_strength; + + int base = c->decay_lut[F[pos]]; + int out_pol = P[pos]; + + if(use_conduct) { + int best_src = pos; + int best_v = F[pos]; + int v; + + if(x > 0) { + v = F[pos - 1]; + if(v > best_v) { + best_v = v; + best_src = pos - 1; + } + } + + if(x + 1 < w) { + v = F[pos + 1]; + if(v > best_v) { + best_v = v; + best_src = pos + 1; + } + } + + if(y > 0) { + v = F[pos - w]; + if(v > best_v) { + best_v = v; + best_src = pos - w; + } + } + + if(y + 1 < h) { + v = F[pos + w]; + if(v > best_v) { + best_v = v; + best_src = pos + w; + } + } + + v = c->conduct_lut[best_v]; + if(v > base) { + base = v; + out_pol = P[best_src]; + } + } + + if(use_branch) { + int best_src = pos; + int best_v = 0; + int flip = (x ^ y ^ (c->frame >> 1)) & 1; + int v; + + if(flip) { + if(x > 0 && y > 0) { + v = F[pos - w - 1]; + if(v > best_v) { + best_v = v; + best_src = pos - w - 1; + } + } + + if(x + 1 < w && y + 1 < h) { + v = F[pos + w + 1]; + if(v > best_v) { + best_v = v; + best_src = pos + w + 1; + } + } + } + else { + if(x + 1 < w && y > 0) { + v = F[pos - w + 1]; + if(v > best_v) { + best_v = v; + best_src = pos - w + 1; + } + } + + if(x > 0 && y + 1 < h) { + v = F[pos + w - 1]; + if(v > best_v) { + best_v = v; + best_src = pos + w - 1; + } + } + } + + v = c->branch_lut[best_v]; + if(v > base) { + base = v; + out_pol = P[best_src]; + } + } + + if(event_strength > 0) { + int edge = cv_source_edge_safe(Y, w, h, x, y, pos); + + event_strength += (event_strength * edge) >> 7; + if(event_strength > 255) + event_strength = 255; + + base += event_strength; + if(base > 255) + base = 255; + + out_pol = diff >= 0 ? 255 : 0; + } + + NF[pos] = (uint8_t) base; + NP[pos] = (uint8_t) out_pol; + + REF[pos] = cv_blend_fast_u8( + (uint8_t) ref, + cy, + c->adapt_lut[event_for_adapt] + ); +} + +static void cv_compute_safe_border(chronovein_t *c, + uint8_t *restrict Y, + int use_conduct, + int use_branch) +{ + int w = c->w; + int h = c->h; + int y; + + if(h <= 2 || w <= 2) { +#pragma omp parallel for schedule(static) num_threads(c->n_threads) + for(y = 0; y < h; y++) { + int x; + int pos = y * w; + + for(x = 0; x < w; x++, pos++) { + cv_compute_one_safe(c, Y, x, y, pos, use_conduct, use_branch); + } + } + + return; + } + +#pragma omp parallel for schedule(static) num_threads(c->n_threads) + for(y = 0; y < h; y++) { + int x; + + if(y == 0 || y == h - 1) { + int pos = y * w; + + for(x = 0; x < w; x++, pos++) { + cv_compute_one_safe(c, Y, x, y, pos, use_conduct, use_branch); + } + } + else { + int pos_l = y * w; + int pos_r = y * w + (w - 1); + + cv_compute_one_safe(c, Y, 0, y, pos_l, use_conduct, use_branch); + cv_compute_one_safe(c, Y, w - 1, y, pos_r, use_conduct, use_branch); + } + } +} + +static void cv_compute(chronovein_t *c, + VJFrame *frame, + int use_conduct, + int use_branch) +{ + uint8_t *restrict Y = frame->data[0]; + + uint8_t *restrict F = c->field; + uint8_t *restrict NF = c->next_field; + uint8_t *restrict P = c->polarity; + uint8_t *restrict NP = c->next_polarity; + uint8_t *restrict REF = c->ref_y; + + uint8_t *restrict EVENT = c->event_lut; + uint8_t *restrict DECAY = c->decay_lut; + uint8_t *restrict CONDUCT = c->conduct_lut; + uint8_t *restrict BRANCH = c->branch_lut; + uint8_t *restrict ADAPT = c->adapt_lut; + + int w = c->w; + int h = c->h; + int frame_phase = c->frame >> 1; + + int y; + + if(w <= 2 || h <= 2) { + cv_compute_safe_border(c, Y, use_conduct, use_branch); + return; + } + +#pragma omp parallel for schedule(static) num_threads(c->n_threads) + for(y = 1; y < h - 1; y++) { + int x; + int pos = y * w + 1; + + for(x = 1; x < w - 1; x++, pos++) { + uint8_t cy = Y[pos]; + + int ref = REF[pos]; + int diff = (int) cy - ref; + int ad = diff < 0 ? -diff : diff; + + int event_strength = EVENT[ad]; + int event_for_adapt = event_strength; + + int gx = cv_absi((int) Y[pos - 1] - (int) Y[pos + 1]); + int gy = cv_absi((int) Y[pos - w] - (int) Y[pos + w]); + + int base = DECAY[F[pos]]; + int out_pol = P[pos]; + + if(use_conduct) { + int src_a; + int src_b; + int src; + int v; + + if(gx > gy) { + src_a = pos - w; + src_b = pos + w; + } + else { + src_a = pos - 1; + src_b = pos + 1; + } + + src = F[src_a] > F[src_b] ? src_a : src_b; + v = CONDUCT[F[src]]; + + if(v > base) { + base = v; + out_pol = P[src]; + } + } + + if(use_branch) { + int flip = (x ^ y ^ frame_phase) & 1; + int src_a = flip ? (pos - w - 1) : (pos - w + 1); + int src_b = flip ? (pos + w + 1) : (pos + w - 1); + int src = F[src_a] > F[src_b] ? src_a : src_b; + int v = BRANCH[F[src]]; + + if(v > base) { + base = v; + out_pol = P[src]; + } + } + + if(event_strength > 0) { + int edge = gx > gy ? gx : gy; + + event_strength += (event_strength * edge) >> 7; + event_strength = event_strength > 255 ? 255 : event_strength; + + base += event_strength; + base = base > 255 ? 255 : base; + + out_pol = diff >= 0 ? 255 : 0; + } + + NF[pos] = (uint8_t) base; + NP[pos] = (uint8_t) out_pol; + + REF[pos] = cv_blend_fast_u8( + (uint8_t) ref, + cy, + ADAPT[event_for_adapt] + ); + } + } + + cv_compute_safe_border(c, Y, use_conduct, use_branch); +} + +static inline int cv_pulse_gain(chronovein_t *c, int pulse) +{ + int phase; + int tri; + + if(pulse <= 0) + return 256; + + phase = (c->frame * (1 + (pulse >> 5))) & 63; + tri = phase < 32 ? phase : 63 - phase; + + return 256 + ((pulse * tri) >> 5); +} + +static void cv_render_const(chronovein_t *c, + VJFrame *frame, + int source_bleed, + int pulse_gain, + int on_u, + int on_v, + int off_u, + int off_v) +{ + uint8_t *restrict Y = frame->data[0]; + uint8_t *restrict U = frame->data[1]; + uint8_t *restrict V = frame->data[2]; + + uint8_t *restrict PREV = c->prev_y; + uint8_t *restrict F = c->field; + uint8_t *restrict P = c->polarity; + + uint8_t *restrict BLEEDY = c->bleed_y_lut; + uint8_t *restrict BLEEDUV = c->bleed_uv_lut; + + int len = c->len; + int i; + +#pragma omp parallel for schedule(static) num_threads(c->n_threads) + for(i = 0; i < len; i++) { + uint8_t src_y = Y[i]; + + int ev = F[i]; + int pol = P[i]; + + int base_y; + uint8_t base_u; + uint8_t base_v; + + int ev_u; + int ev_v; + int yy; + + PREV[i] = src_y; + + if(source_bleed > 0) { + base_y = BLEEDY[src_y]; + base_u = BLEEDUV[U[i]]; + base_v = BLEEDUV[V[i]]; + } + else { + base_y = 0; + base_u = 128; + base_v = 128; + } + + if(ev <= 0) { + Y[i] = (uint8_t) base_y; + U[i] = base_u; + V[i] = base_v; + continue; + } + + ev = (ev * pulse_gain + 128) >> 8; + ev = ev > 255 ? 255 : ev; + + ev_u = cv_blend_fast_u8((uint8_t) off_u, (uint8_t) on_u, pol); + ev_v = cv_blend_fast_u8((uint8_t) off_v, (uint8_t) on_v, pol); + + yy = base_y + ev; + + Y[i] = (uint8_t) (yy > 255 ? 255 : yy); + U[i] = cv_blend_fast_u8(base_u, (uint8_t) ev_u, ev); + V[i] = cv_blend_fast_u8(base_v, (uint8_t) ev_v, ev); + } +} + +static void cv_render_source(chronovein_t *c, + VJFrame *frame, + int source_bleed, + int pulse_gain) +{ + uint8_t *restrict Y = frame->data[0]; + uint8_t *restrict U = frame->data[1]; + uint8_t *restrict V = frame->data[2]; + + uint8_t *restrict PREV = c->prev_y; + uint8_t *restrict F = c->field; + uint8_t *restrict P = c->polarity; + + uint8_t *restrict BLEEDY = c->bleed_y_lut; + uint8_t *restrict BLEEDUV = c->bleed_uv_lut; + + int len = c->len; + int i; + +#pragma omp parallel for schedule(static) num_threads(c->n_threads) + for(i = 0; i < len; i++) { + uint8_t src_y = Y[i]; + uint8_t src_u = U[i]; + uint8_t src_v = V[i]; + + int ev = F[i]; + int pol = P[i]; + + int base_y; + uint8_t base_u; + uint8_t base_v; + + int ev_u; + int ev_v; + int yy; + + PREV[i] = src_y; + + if(source_bleed > 0) { + base_y = BLEEDY[src_y]; + base_u = BLEEDUV[src_u]; + base_v = BLEEDUV[src_v]; + } + else { + base_y = 0; + base_u = 128; + base_v = 128; + } + + if(ev <= 0) { + Y[i] = (uint8_t) base_y; + U[i] = base_u; + V[i] = base_v; + continue; + } + + ev = (ev * pulse_gain + 128) >> 8; + ev = ev > 255 ? 255 : ev; + + ev_u = cv_blend_fast_u8((uint8_t) (255 - src_u), src_u, pol); + ev_v = cv_blend_fast_u8((uint8_t) (255 - src_v), src_v, pol); + + yy = base_y + ev; + + Y[i] = (uint8_t) (yy > 255 ? 255 : yy); + U[i] = cv_blend_fast_u8(base_u, (uint8_t) ev_u, ev); + V[i] = cv_blend_fast_u8(base_v, (uint8_t) ev_v, ev); + } +} + +static void cv_render_white(chronovein_t *c, + VJFrame *frame, + int source_bleed, + int pulse_gain) +{ + uint8_t *restrict Y = frame->data[0]; + uint8_t *restrict U = frame->data[1]; + uint8_t *restrict V = frame->data[2]; + + uint8_t *restrict PREV = c->prev_y; + uint8_t *restrict F = c->field; + + uint8_t *restrict BLEEDY = c->bleed_y_lut; + uint8_t *restrict BLEEDUV = c->bleed_uv_lut; + + int len = c->len; + int i; + +#pragma omp parallel for schedule(static) num_threads(c->n_threads) + for(i = 0; i < len; i++) { + uint8_t src_y = Y[i]; + + int ev = F[i]; + + int base_y; + uint8_t base_u; + uint8_t base_v; + int yy; + + PREV[i] = src_y; + + if(source_bleed > 0) { + base_y = BLEEDY[src_y]; + base_u = BLEEDUV[U[i]]; + base_v = BLEEDUV[V[i]]; + } + else { + base_y = 0; + base_u = 128; + base_v = 128; + } + + ev = (ev * pulse_gain + 128) >> 8; + ev = ev > 255 ? 255 : ev; + + yy = base_y + ev; + + Y[i] = (uint8_t) (yy > 255 ? 255 : yy); + U[i] = base_u; + V[i] = base_v; + } +} + +static void cv_render(chronovein_t *c, + VJFrame *frame, + int source_bleed, + int color_mode, + int pulse) +{ + int pulse_gain = cv_pulse_gain(c, pulse); + + switch(color_mode) { + case CV_COLOR_WHITE: + cv_render_white(c, frame, source_bleed, pulse_gain); + break; + + case CV_COLOR_THERMAL: + cv_render_const(c, frame, source_bleed, pulse_gain, 84, 220, 212, 84); + break; + + case CV_COLOR_SOURCE: + cv_render_source(c, frame, source_bleed, pulse_gain); + break; + + case CV_COLOR_ELECTRIC: + cv_render_const(c, frame, source_bleed, pulse_gain, 54, 196, 210, 54); + break; + + case CV_COLOR_POLARITY: + default: + cv_render_const(c, frame, source_bleed, pulse_gain, 92, 226, 226, 92); + break; + } +} + +static void cv_swap_fields(chronovein_t *c) +{ + uint8_t *t; + + t = c->field; + c->field = c->next_field; + c->next_field = t; + + t = c->polarity; + c->polarity = c->next_polarity; + c->next_polarity = t; +} + +void chronovein_apply(void *ptr, VJFrame *frame, int *args) +{ + chronovein_t *c = (chronovein_t *) ptr; + + int threshold; + int growth; + int conductivity; + int decay; + int branch; + int source_bleed; + int color_mode; + int pulse; + + int use_conduct; + int use_branch; + + if(!c->seeded) + cv_seed(c, frame); + + threshold = cv_clampi(args[P_THRESHOLD], 0, 255); + growth = cv_clampi(args[P_GROWTH], 0, 255); + conductivity = cv_clampi(args[P_CONDUCTIVITY], 0, 255); + decay = cv_clampi(args[P_DECAY], 0, 255); + branch = cv_clampi(args[P_BRANCH], 0, 255); + source_bleed = cv_clampi(args[P_SOURCE_BLEED], 0, 255); + color_mode = cv_clampi(args[P_COLOR_MODE], 0, 4); + pulse = cv_clampi(args[P_PULSE], 0, 255); + + cv_build_luts_if_needed( + c, + threshold, + growth, + conductivity, + decay, + branch, + source_bleed + ); + + { + int growth_scale = (growth * 320 + 127) / 255; + int conduct_power = (conductivity * decay + 127) / 255; + int branch_power = (branch * decay + 127) / 255; + + conduct_power = (conduct_power * growth_scale + 128) >> 8; + branch_power = (branch_power * growth_scale + 128) >> 8; + + use_conduct = (conduct_power > 0); + use_branch = (branch_power > 0); + } + + cv_compute(c, frame, use_conduct, use_branch); + + cv_swap_fields(c); + + cv_render( + c, + frame, + source_bleed, + color_mode, + pulse + ); + + c->frame++; +} \ No newline at end of file diff --git a/veejay-current/veejay-server/libvje/effects/chronovein.h b/veejay-current/veejay-server/libvje/effects/chronovein.h new file mode 100644 index 00000000..27bc7bbf --- /dev/null +++ b/veejay-current/veejay-server/libvje/effects/chronovein.h @@ -0,0 +1,36 @@ +/* + * Linux VeeJay + * + * Copyright(C)2026 Niels Elburg + * + * 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 CHRONOVEIN_H +#define CHRONOVEIN_H + +#include +#include +#include + +#include + +vj_effect *chronovein_init(int w, int h); +void *chronovein_malloc(int w, int h); +void chronovein_free(void *ptr); +void chronovein_apply(void *ptr, VJFrame *frame, int *args); + +#endif