#include <stdlib.h>
#include <math.h>
#include "frei0r.h"
#include "frei0r/math.h"
// let's try to walk through everything because i'm a moron
// this is the struct for the effect instance
typedef struct gateweave_instance
{
// image dimensions
unsigned int width;
unsigned int height;
// parameters
double interval;
double max_move_x;
double max_move_y;
// other variables that the effect uses to keep track of things
double next_key_x;
double next_key_y;
double prev_key_x;
double prev_key_y;
} gateweave_instance_t;
// these functions are for the effect
static double gateweave_random_range(double range, double last)
{
if(range <= 0)
{
return 0;
}
// the maximum shift is 10 pixels
// since range includes fractional values, we want to multiply it by 100
// this will generate an integer between -100 and 100 for the shift
// and then we divide by 100 to receive a decimal answer between -10.00 and 10.00
range *= 10;
int int_range = range * 100;
int_range = (rand() % (int_range * 2)) - int_range;
double ret = int_range / 100.0;
// we don't want to generate a value similar to one we already generated twice in a row
ret = CLAMP(ret, -range, range);
if((ret > 0 && ret >= last - 0.12) || (ret < 0 && ret <= last + 0.12))
{
ret *= -1;
}
return ret;
}
static inline double gateweave_lerp(double v0, double v1, double t)
{
return v0 + t * (v1 - v0);
}
// this function mixes the colors of two pixels
// a is the original pixel that will be changed
// b is the second pixel that will not be changed
// the amount is how much of the second pixel will be mixed
// an amount of 1 means pixel a will be equal to pixel b
// an amount of 0 means pixel a will be equal to pixel a (no change)
// an amount of 0.5 means pixel a will be equal to 0.5 * pixel a + 0.5 * pixel b
static uint32_t gateweave_blend_color(uint32_t a, uint32_t b, double amount)
{
uint8_t c_a;
uint8_t c_b;
uint8_t c_g;
uint8_t c_r;
uint32_t output;
// RRGGBBAA
// 0xFFFFFFFF
c_a = CLAMP0255(((a & 0xFF000000) >> 24) * (1 - amount) + ((b & 0xFF000000) >> 24) * (amount));
c_b = CLAMP0255(((a & 0x00FF0000) >> 16) * (1 - amount) + ((b & 0x00FF0000) >> 16) * (amount));
c_g = CLAMP0255(((a & 0x0000FF00) >> 8) * (1 - amount) + ((b & 0x0000FF00) >> 8) * (amount));
c_r = CLAMP0255(((a & 0x000000FF)) * (1 - amount) + ((b & 0x000000FF)) * (amount));
output = (output & 0xFFFFFF00) | (uint32_t)c_r;
output = (output & 0xFFFF00FF) | ((uint32_t)c_g << 8);
output = (output & 0xFF00FFFF) | ((uint32_t)c_b << 16);
output = (output & 0x00FFFFFF) | ((uint32_t)c_a << 24);
return output;
}
// this function is the one that ultimately manipulates the image
// it shifts the image and can do so to a value less than one pixel
static inline void gateweave_shift_picture(const uint32_t* in, uint32_t* out, double shift_x, double shift_y, int w, int h)
{
uint32_t* buf = (uint32_t*)calloc(w * h, sizeof(uint32_t));
// first we shift to the nearest whole pixel
int int_shift_x = shift_x;
int int_shift_y = shift_y;
int pixel_shift = int_shift_x + (int_shift_y * w);
for(unsigned int i = 0; i < w * h; i++)
{
if(i + pixel_shift < 0 || i + pixel_shift >= w * h)
{
*(buf + i) = 0;
}
else
{
*(buf + i) = *(in + i + pixel_shift);
}
}
// then we shift the fractional component
// let's think about how this works
// let's suppose we shift left 1.5 pixels and shift down 1.25 pixels
// after shifting 1 pixel left and 1 pixel down, we still have the 0.5 and 0.25 shifts left to do
// to do this we want each pixel to equal 50% the current pixel and 50% the pixel to its left
// we also want each pixel to equal 75% the current pixel and 25% the pixel below it
// but this isn't actually possible to do
shift_x -= int_shift_x;
shift_y -= int_shift_y;
int shift_component_x;
int shift_component_y;
char larger_x_comp = 0;
if(fabs(shift_x) > fabs(shift_y))
{
larger_x_comp = 1;
}
if(shift_x < 0)
{
shift_component_x = -1;
}
else
{
shift_component_x = 1;
}
if(shift_y < 0)
{
shift_component_y = -w;
}
else
{
shift_component_y = w;
}
uint32_t v = 0;
for(unsigned int i = 0; i < w * h; i++)
{
if(i + shift_component_x >= 0 && i + shift_component_x < w * h &&
i + shift_component_y >= 0 && i + shift_component_y < w * h &&
i + shift_component_x + shift_component_y >= 0 && i + shift_component_x + shift_component_y < w * h)
{
if(larger_x_comp)
{
v = gateweave_blend_color(*(buf + i + shift_component_x), *(buf + i + shift_component_x + shift_component_y), shift_y);
*(out + i) = gateweave_blend_color(*(buf + i), v, shift_x);
}
else
{
v = gateweave_blend_color(*(buf + i + shift_component_y), *(buf + i + shift_component_x + shift_component_y), shift_x);
*(out + i) = gateweave_blend_color(*(buf + i), v, shift_y);
}
}
}
free(buf);
}
// these functions are for frei0r
// mostly copy/paste/slightly modified from the other frei0r effects
int f0r_init()
{
return 1;
}
void f0r_deinit()
{}
void f0r_get_plugin_info(f0r_plugin_info_t* info)
{
info->name = "Gate Weave";
info->author = "esmane";
info->explanation = "Randomly moves frame around to simulate film gate weave.";
info->plugin_type = F0R_PLUGIN_TYPE_FILTER;
info->color_model = F0R_COLOR_MODEL_RGBA8888;
info->frei0r_version = FREI0R_MAJOR_VERSION;
info->major_version = 0;
info->minor_version = 2;
info->num_params = 3;
}
void f0r_get_param_info(f0r_param_info_t* info, int param_index)
{
switch(param_index)
{
case 0:
info->name = "Interval";
info->explanation = "The amount of time before the position is randomized again. The larger the number the slower the picture will move.";
info->type = F0R_PARAM_DOUBLE;
break;
case 1:
info->name = "Maximum Horizontal Movement";
info->explanation = "The maximum distance the picture could move left or right. The larger the number the more the picture moves and the less subtle the effect.";
info->type = F0R_PARAM_DOUBLE;
break;
case 2:
info->name = "Maximum Vertical Movement";
info->explanation = "The maximum distance the picture could move up or down. The larger the number the more the picture moves and the less subtle the effect.";
info->type = F0R_PARAM_DOUBLE;
break;
}
}
f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
{
gateweave_instance_t* inst = (gateweave_instance_t*)calloc(1, sizeof(*inst));
inst->width = width;
inst->height = height;
inst->interval = 0.6;
inst->max_move_x = 0.2;
inst->max_move_y = 0.2;
// other variables that the effect uses to keep track of things
inst->next_key_x = gateweave_random_range(inst->max_move_x, 0);
inst->next_key_y = gateweave_random_range(inst->max_move_y, 0);
inst->prev_key_x = 0;
inst->prev_key_y = 0;
return (f0r_instance_t)inst;
}
void f0r_destruct(f0r_instance_t instance)
{
gateweave_instance_t* inst = (gateweave_instance_t*)instance;
free(instance);
}
void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int param_index)
{
gateweave_instance_t* inst = (gateweave_instance_t*)instance;
switch(param_index)
{
case 0:
inst->interval = *((double*)param);
break;
case 1:
inst->max_move_x = *((double*)param);
break;
case 2:
inst->max_move_y = *((double*)param);
break;
}
}
void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_index)
{
gateweave_instance_t* inst = (gateweave_instance_t*)instance;
switch(param_index)
{
case 0:
*((double*)param) = inst->interval;
break;
case 1:
*((double*)param) = inst->max_move_x;
break;
case 2:
*((double*)param) = inst->max_move_y;
break;
}
}
void f0r_update(f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe)
{
gateweave_instance_t* inst = (gateweave_instance_t*)instance;
inst->next_key_x = gateweave_random_range(inst->max_move_x, inst->next_key_x);
inst->next_key_y = gateweave_random_range(inst->max_move_y, inst->next_key_y);
inst->prev_key_x = gateweave_lerp(inst->next_key_x, inst->prev_key_x, inst->interval);
inst->prev_key_y = gateweave_lerp(inst->next_key_y, inst->prev_key_y, inst->interval);
gateweave_shift_picture(inframe, outframe, inst->prev_key_x, inst->prev_key_y, inst->width, inst->height);
}