tornavis/source/blender/blenkernel/intern/seqeffects.c

3862 lines
98 KiB
C

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* Contributor(s):
* - Blender Foundation, 2003-2009
* - Peter Schlaile <peter [at] schlaile [dot] de> 2005/2006
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenkernel/intern/seqeffects.c
* \ingroup bke
*/
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "MEM_guardedalloc.h"
#include "BLI_math.h" /* windows needs for M_PI */
#include "BLI_utildefines.h"
#include "BLI_rect.h"
#include "BLI_string.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "DNA_anim_types.h"
#include "DNA_space_types.h"
#include "BKE_fcurve.h"
#include "BKE_sequencer.h"
#include "IMB_imbuf_types.h"
#include "IMB_imbuf.h"
#include "IMB_colormanagement.h"
#include "IMB_metadata.h"
#include "BLI_math_color_blend.h"
#include "RNA_access.h"
#include "RE_pipeline.h"
#include "BLF_api.h"
static void slice_get_byte_buffers(
const SeqRenderData *context, const ImBuf *ibuf1, const ImBuf *ibuf2,
const ImBuf *ibuf3, const ImBuf *out, int start_line, unsigned char **rect1,
unsigned char **rect2, unsigned char **rect3, unsigned char **rect_out)
{
int offset = 4 * start_line * context->rectx;
*rect1 = (unsigned char *)ibuf1->rect + offset;
*rect_out = (unsigned char *)out->rect + offset;
if (ibuf2)
*rect2 = (unsigned char *)ibuf2->rect + offset;
if (ibuf3)
*rect3 = (unsigned char *)ibuf3->rect + offset;
}
static void slice_get_float_buffers(
const SeqRenderData *context, const ImBuf *ibuf1, const ImBuf *ibuf2,
const ImBuf *ibuf3, const ImBuf *out, int start_line,
float **rect1, float **rect2, float **rect3, float **rect_out)
{
int offset = 4 * start_line * context->rectx;
*rect1 = ibuf1->rect_float + offset;
*rect_out = out->rect_float + offset;
if (ibuf2)
*rect2 = ibuf2->rect_float + offset;
if (ibuf3)
*rect3 = ibuf3->rect_float + offset;
}
/*********************** Glow effect *************************/
enum {
GlowR = 0,
GlowG = 1,
GlowB = 2,
GlowA = 3
};
static ImBuf *prepare_effect_imbufs(const SeqRenderData *context, ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *ibuf3)
{
ImBuf *out;
Scene *scene = context->scene;
int x = context->rectx;
int y = context->recty;
if (!ibuf1 && !ibuf2 && !ibuf3) {
/* hmmm, global float option ? */
out = IMB_allocImBuf(x, y, 32, IB_rect);
}
else if ((ibuf1 && ibuf1->rect_float) ||
(ibuf2 && ibuf2->rect_float) ||
(ibuf3 && ibuf3->rect_float))
{
/* if any inputs are rectfloat, output is float too */
out = IMB_allocImBuf(x, y, 32, IB_rectfloat);
}
else {
out = IMB_allocImBuf(x, y, 32, IB_rect);
}
if (out->rect_float) {
if (ibuf1 && !ibuf1->rect_float) {
BKE_sequencer_imbuf_to_sequencer_space(scene, ibuf1, true);
}
if (ibuf2 && !ibuf2->rect_float) {
BKE_sequencer_imbuf_to_sequencer_space(scene, ibuf2, true);
}
if (ibuf3 && !ibuf3->rect_float) {
BKE_sequencer_imbuf_to_sequencer_space(scene, ibuf3, true);
}
IMB_colormanagement_assign_float_colorspace(out, scene->sequencer_colorspace_settings.name);
}
else {
if (ibuf1 && !ibuf1->rect) {
IMB_rect_from_float(ibuf1);
}
if (ibuf2 && !ibuf2->rect) {
IMB_rect_from_float(ibuf2);
}
if (ibuf3 && !ibuf3->rect) {
IMB_rect_from_float(ibuf3);
}
}
/* If effect only affecting a single channel, forward input's metadata to the output. */
if (ibuf1 != NULL && ibuf1 == ibuf2 && ibuf2 == ibuf3) {
IMB_metadata_copy(out, ibuf1);
}
return out;
}
/*********************** Alpha Over *************************/
static void init_alpha_over_or_under(Sequence *seq)
{
Sequence *seq1 = seq->seq1;
Sequence *seq2 = seq->seq2;
seq->seq2 = seq1;
seq->seq1 = seq2;
}
static void do_alphaover_effect_byte(
float facf0, float facf1, int x, int y,
unsigned char *rect1, unsigned char *rect2, unsigned char *out)
{
float fac2, mfac, fac, fac4;
int xo;
unsigned char *cp1, *cp2, *rt;
float tempc[4], rt1[4], rt2[4];
xo = x;
cp1 = rect1;
cp2 = rect2;
rt = out;
fac2 = facf0;
fac4 = facf1;
while (y--) {
x = xo;
while (x--) {
/* rt = rt1 over rt2 (alpha from rt1) */
straight_uchar_to_premul_float(rt1, cp1);
straight_uchar_to_premul_float(rt2, cp2);
fac = fac2;
mfac = 1.0f - fac2 * rt1[3];
if (fac <= 0.0f) *((unsigned int *) rt) = *((unsigned int *) cp2);
else if (mfac <= 0.0f) *((unsigned int *) rt) = *((unsigned int *) cp1);
else {
tempc[0] = fac * rt1[0] + mfac * rt2[0];
tempc[1] = fac * rt1[1] + mfac * rt2[1];
tempc[2] = fac * rt1[2] + mfac * rt2[2];
tempc[3] = fac * rt1[3] + mfac * rt2[3];
premul_float_to_straight_uchar(rt, tempc);
}
cp1 += 4; cp2 += 4; rt += 4;
}
if (y == 0) break;
y--;
x = xo;
while (x--) {
straight_uchar_to_premul_float(rt1, cp1);
straight_uchar_to_premul_float(rt2, cp2);
fac = fac4;
mfac = 1.0f - (fac4 * rt1[3]);
if (fac <= 0.0f) *((unsigned int *) rt) = *((unsigned int *) cp2);
else if (mfac <= 0.0f) *((unsigned int *) rt) = *((unsigned int *) cp1);
else {
tempc[0] = fac * rt1[0] + mfac * rt2[0];
tempc[1] = fac * rt1[1] + mfac * rt2[1];
tempc[2] = fac * rt1[2] + mfac * rt2[2];
tempc[3] = fac * rt1[3] + mfac * rt2[3];
premul_float_to_straight_uchar(rt, tempc);
}
cp1 += 4; cp2 += 4; rt += 4;
}
}
}
static void do_alphaover_effect_float(
float facf0, float facf1, int x, int y,
float *rect1, float *rect2, float *out)
{
float fac2, mfac, fac, fac4;
int xo;
float *rt1, *rt2, *rt;
xo = x;
rt1 = rect1;
rt2 = rect2;
rt = out;
fac2 = facf0;
fac4 = facf1;
while (y--) {
x = xo;
while (x--) {
/* rt = rt1 over rt2 (alpha from rt1) */
fac = fac2;
mfac = 1.0f - (fac2 * rt1[3]);
if (fac <= 0.0f) {
memcpy(rt, rt2, 4 * sizeof(float));
}
else if (mfac <= 0) {
memcpy(rt, rt1, 4 * sizeof(float));
}
else {
rt[0] = fac * rt1[0] + mfac * rt2[0];
rt[1] = fac * rt1[1] + mfac * rt2[1];
rt[2] = fac * rt1[2] + mfac * rt2[2];
rt[3] = fac * rt1[3] + mfac * rt2[3];
}
rt1 += 4; rt2 += 4; rt += 4;
}
if (y == 0)
break;
y--;
x = xo;
while (x--) {
fac = fac4;
mfac = 1.0f - (fac4 * rt1[3]);
if (fac <= 0.0f) {
memcpy(rt, rt2, 4 * sizeof(float));
}
else if (mfac <= 0.0f) {
memcpy(rt, rt1, 4 * sizeof(float));
}
else {
rt[0] = fac * rt1[0] + mfac * rt2[0];
rt[1] = fac * rt1[1] + mfac * rt2[1];
rt[2] = fac * rt1[2] + mfac * rt2[2];
rt[3] = fac * rt1[3] + mfac * rt2[3];
}
rt1 += 4; rt2 += 4; rt += 4;
}
}
}
static void do_alphaover_effect(
const SeqRenderData *context, Sequence *UNUSED(seq), float UNUSED(cfra), float facf0,
float facf1, ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *UNUSED(ibuf3),
int start_line, int total_lines, ImBuf *out)
{
if (out->rect_float) {
float *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_float_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_alphaover_effect_float(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
else {
unsigned char *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_byte_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_alphaover_effect_byte(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
}
/*********************** Alpha Under *************************/
static void do_alphaunder_effect_byte(
float facf0, float facf1, int x, int y,
unsigned char *rect1, unsigned char *rect2, unsigned char *out)
{
float fac2, fac, fac4;
int xo;
unsigned char *cp1, *cp2, *rt;
float tempc[4], rt1[4], rt2[4];
xo = x;
cp1 = rect1;
cp2 = rect2;
rt = out;
fac2 = facf0;
fac4 = facf1;
while (y--) {
x = xo;
while (x--) {
/* rt = rt1 under rt2 (alpha from rt2) */
straight_uchar_to_premul_float(rt1, cp1);
straight_uchar_to_premul_float(rt2, cp2);
/* this complex optimization is because the
* 'skybuf' can be crossed in
*/
if (rt2[3] <= 0.0f && fac2 >= 1.0f) *((unsigned int *) rt) = *((unsigned int *) cp1);
else if (rt2[3] >= 1.0f) *((unsigned int *) rt) = *((unsigned int *) cp2);
else {
fac = (fac2 * (1.0f - rt2[3]));
if (fac <= 0) *((unsigned int *) rt) = *((unsigned int *) cp2);
else {
tempc[0] = (fac * rt1[0] + rt2[0]);
tempc[1] = (fac * rt1[1] + rt2[1]);
tempc[2] = (fac * rt1[2] + rt2[2]);
tempc[3] = (fac * rt1[3] + rt2[3]);
premul_float_to_straight_uchar(rt, tempc);
}
}
cp1 += 4; cp2 += 4; rt += 4;
}
if (y == 0)
break;
y--;
x = xo;
while (x--) {
straight_uchar_to_premul_float(rt1, cp1);
straight_uchar_to_premul_float(rt2, cp2);
if (rt2[3] <= 0.0f && fac4 >= 1.0f) *((unsigned int *) rt) = *((unsigned int *) cp1);
else if (rt2[3] >= 1.0f) *((unsigned int *) rt) = *((unsigned int *) cp2);
else {
fac = (fac4 * (1.0f - rt2[3]));
if (fac <= 0) *((unsigned int *)rt) = *((unsigned int *)cp2);
else {
tempc[0] = (fac * rt1[0] + rt2[0]);
tempc[1] = (fac * rt1[1] + rt2[1]);
tempc[2] = (fac * rt1[2] + rt2[2]);
tempc[3] = (fac * rt1[3] + rt2[3]);
premul_float_to_straight_uchar(rt, tempc);
}
}
cp1 += 4; cp2 += 4; rt += 4;
}
}
}
static void do_alphaunder_effect_float(
float facf0, float facf1, int x, int y,
float *rect1, float *rect2, float *out)
{
float fac2, fac, fac4;
int xo;
float *rt1, *rt2, *rt;
xo = x;
rt1 = rect1;
rt2 = rect2;
rt = out;
fac2 = facf0;
fac4 = facf1;
while (y--) {
x = xo;
while (x--) {
/* rt = rt1 under rt2 (alpha from rt2) */
/* this complex optimization is because the
* 'skybuf' can be crossed in
*/
if (rt2[3] <= 0 && fac2 >= 1.0f) {
memcpy(rt, rt1, 4 * sizeof(float));
}
else if (rt2[3] >= 1.0f) {
memcpy(rt, rt2, 4 * sizeof(float));
}
else {
fac = fac2 * (1.0f - rt2[3]);
if (fac == 0) {
memcpy(rt, rt2, 4 * sizeof(float));
}
else {
rt[0] = fac * rt1[0] + rt2[0];
rt[1] = fac * rt1[1] + rt2[1];
rt[2] = fac * rt1[2] + rt2[2];
rt[3] = fac * rt1[3] + rt2[3];
}
}
rt1 += 4; rt2 += 4; rt += 4;
}
if (y == 0)
break;
y--;
x = xo;
while (x--) {
if (rt2[3] <= 0 && fac4 >= 1.0f) {
memcpy(rt, rt1, 4 * sizeof(float));
}
else if (rt2[3] >= 1.0f) {
memcpy(rt, rt2, 4 * sizeof(float));
}
else {
fac = fac4 * (1.0f - rt2[3]);
if (fac == 0) {
memcpy(rt, rt2, 4 * sizeof(float));
}
else {
rt[0] = fac * rt1[0] + rt2[0];
rt[1] = fac * rt1[1] + rt2[1];
rt[2] = fac * rt1[2] + rt2[2];
rt[3] = fac * rt1[3] + rt2[3];
}
}
rt1 += 4; rt2 += 4; rt += 4;
}
}
}
static void do_alphaunder_effect(
const SeqRenderData *context, Sequence *UNUSED(seq), float UNUSED(cfra),
float facf0, float facf1, ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *UNUSED(ibuf3),
int start_line, int total_lines, ImBuf *out)
{
if (out->rect_float) {
float *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_float_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_alphaunder_effect_float(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
else {
unsigned char *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_byte_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_alphaunder_effect_byte(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
}
/*********************** Cross *************************/
static void do_cross_effect_byte(
float facf0, float facf1, int x, int y,
unsigned char *rect1, unsigned char *rect2, unsigned char *out)
{
int fac1, fac2, fac3, fac4;
int xo;
unsigned char *rt1, *rt2, *rt;
xo = x;
rt1 = rect1;
rt2 = rect2;
rt = out;
fac2 = (int) (256.0f * facf0);
fac1 = 256 - fac2;
fac4 = (int) (256.0f * facf1);
fac3 = 256 - fac4;
while (y--) {
x = xo;
while (x--) {
rt[0] = (fac1 * rt1[0] + fac2 * rt2[0]) >> 8;
rt[1] = (fac1 * rt1[1] + fac2 * rt2[1]) >> 8;
rt[2] = (fac1 * rt1[2] + fac2 * rt2[2]) >> 8;
rt[3] = (fac1 * rt1[3] + fac2 * rt2[3]) >> 8;
rt1 += 4; rt2 += 4; rt += 4;
}
if (y == 0)
break;
y--;
x = xo;
while (x--) {
rt[0] = (fac3 * rt1[0] + fac4 * rt2[0]) >> 8;
rt[1] = (fac3 * rt1[1] + fac4 * rt2[1]) >> 8;
rt[2] = (fac3 * rt1[2] + fac4 * rt2[2]) >> 8;
rt[3] = (fac3 * rt1[3] + fac4 * rt2[3]) >> 8;
rt1 += 4; rt2 += 4; rt += 4;
}
}
}
static void do_cross_effect_float(float facf0, float facf1, int x, int y, float *rect1, float *rect2, float *out)
{
float fac1, fac2, fac3, fac4;
int xo;
float *rt1, *rt2, *rt;
xo = x;
rt1 = rect1;
rt2 = rect2;
rt = out;
fac2 = facf0;
fac1 = 1.0f - fac2;
fac4 = facf1;
fac3 = 1.0f - fac4;
while (y--) {
x = xo;
while (x--) {
rt[0] = fac1 * rt1[0] + fac2 * rt2[0];
rt[1] = fac1 * rt1[1] + fac2 * rt2[1];
rt[2] = fac1 * rt1[2] + fac2 * rt2[2];
rt[3] = fac1 * rt1[3] + fac2 * rt2[3];
rt1 += 4; rt2 += 4; rt += 4;
}
if (y == 0)
break;
y--;
x = xo;
while (x--) {
rt[0] = fac3 * rt1[0] + fac4 * rt2[0];
rt[1] = fac3 * rt1[1] + fac4 * rt2[1];
rt[2] = fac3 * rt1[2] + fac4 * rt2[2];
rt[3] = fac3 * rt1[3] + fac4 * rt2[3];
rt1 += 4; rt2 += 4; rt += 4;
}
}
}
static void do_cross_effect(
const SeqRenderData *context, Sequence *UNUSED(seq), float UNUSED(cfra),
float facf0, float facf1, ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *UNUSED(ibuf3),
int start_line, int total_lines, ImBuf *out)
{
if (out->rect_float) {
float *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_float_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_cross_effect_float(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
else {
unsigned char *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_byte_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_cross_effect_byte(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
}
/*********************** Gamma Cross *************************/
/* copied code from initrender.c */
static unsigned short gamtab[65536];
static unsigned short igamtab1[256];
static bool gamma_tabs_init = false;
#define RE_GAMMA_TABLE_SIZE 400
static float gamma_range_table[RE_GAMMA_TABLE_SIZE + 1];
static float gamfactor_table[RE_GAMMA_TABLE_SIZE];
static float inv_gamma_range_table[RE_GAMMA_TABLE_SIZE + 1];
static float inv_gamfactor_table[RE_GAMMA_TABLE_SIZE];
static float color_domain_table[RE_GAMMA_TABLE_SIZE + 1];
static float color_step;
static float inv_color_step;
static float valid_gamma;
static float valid_inv_gamma;
static void makeGammaTables(float gamma)
{
/* we need two tables: one forward, one backward */
int i;
valid_gamma = gamma;
valid_inv_gamma = 1.0f / gamma;
color_step = 1.0f / RE_GAMMA_TABLE_SIZE;
inv_color_step = (float) RE_GAMMA_TABLE_SIZE;
/* We could squeeze out the two range tables to gain some memory */
for (i = 0; i < RE_GAMMA_TABLE_SIZE; i++) {
color_domain_table[i] = i * color_step;
gamma_range_table[i] = pow(color_domain_table[i], valid_gamma);
inv_gamma_range_table[i] = pow(color_domain_table[i], valid_inv_gamma);
}
/* The end of the table should match 1.0 carefully. In order to avoid
* rounding errors, we just set this explicitly. The last segment may
* have a different length than the other segments, but our
* interpolation is insensitive to that
*/
color_domain_table[RE_GAMMA_TABLE_SIZE] = 1.0;
gamma_range_table[RE_GAMMA_TABLE_SIZE] = 1.0;
inv_gamma_range_table[RE_GAMMA_TABLE_SIZE] = 1.0;
/* To speed up calculations, we make these calc factor tables. They are
* multiplication factors used in scaling the interpolation
*/
for (i = 0; i < RE_GAMMA_TABLE_SIZE; i++) {
gamfactor_table[i] = inv_color_step * (gamma_range_table[i + 1] - gamma_range_table[i]);
inv_gamfactor_table[i] = inv_color_step * (inv_gamma_range_table[i + 1] - inv_gamma_range_table[i]);
}
}
static float gammaCorrect(float c)
{
int i;
float res;
i = floorf(c * inv_color_step);
/* Clip to range [0, 1]: outside, just do the complete calculation.
* We may have some performance problems here. Stretching up the LUT
* may help solve that, by exchanging LUT size for the interpolation.
* Negative colors are explicitly handled.
*/
if (UNLIKELY(i < 0)) res = -powf(-c, valid_gamma);
else if (i >= RE_GAMMA_TABLE_SIZE) res = powf(c, valid_gamma);
else res = gamma_range_table[i] +
((c - color_domain_table[i]) * gamfactor_table[i]);
return res;
}
/* ------------------------------------------------------------------------- */
static float invGammaCorrect(float c)
{
int i;
float res = 0.0;
i = floorf(c * inv_color_step);
/* Negative colors are explicitly handled */
if (UNLIKELY(i < 0)) res = -powf(-c, valid_inv_gamma);
else if (i >= RE_GAMMA_TABLE_SIZE) res = powf(c, valid_inv_gamma);
else res = inv_gamma_range_table[i] +
((c - color_domain_table[i]) * inv_gamfactor_table[i]);
return res;
}
static void gamtabs(float gamma)
{
float val, igamma = 1.0f / gamma;
int a;
/* gamtab: in short, out short */
for (a = 0; a < 65536; a++) {
val = a;
val /= 65535.0f;
if (gamma == 2.0f)
val = sqrtf(val);
else if (gamma != 1.0f)
val = powf(val, igamma);
gamtab[a] = (65535.99f * val);
}
/* inverse gamtab1 : in byte, out short */
for (a = 1; a <= 256; a++) {
if (gamma == 2.0f) igamtab1[a - 1] = a * a - 1;
else if (gamma == 1.0f) igamtab1[a - 1] = 256 * a - 1;
else {
val = a / 256.0f;
igamtab1[a - 1] = (65535.0 * pow(val, gamma)) - 1;
}
}
}
static void build_gammatabs(void)
{
if (gamma_tabs_init == false) {
gamtabs(2.0f);
makeGammaTables(2.0f);
gamma_tabs_init = true;
}
}
static void init_gammacross(Sequence *UNUSED(seq))
{
}
static void load_gammacross(Sequence *UNUSED(seq))
{
}
static void free_gammacross(Sequence *UNUSED(seq))
{
}
static void do_gammacross_effect_byte(
float facf0, float UNUSED(facf1), int x, int y, unsigned char *rect1,
unsigned char *rect2, unsigned char *out)
{
float fac1, fac2;
int xo;
unsigned char *cp1, *cp2, *rt;
float rt1[4], rt2[4], tempc[4];
xo = x;
cp1 = rect1;
cp2 = rect2;
rt = out;
fac2 = facf0;
fac1 = 1.0f - fac2;
while (y--) {
x = xo;
while (x--) {
straight_uchar_to_premul_float(rt1, cp1);
straight_uchar_to_premul_float(rt2, cp2);
tempc[0] = gammaCorrect(fac1 * invGammaCorrect(rt1[0]) + fac2 * invGammaCorrect(rt2[0]));
tempc[1] = gammaCorrect(fac1 * invGammaCorrect(rt1[1]) + fac2 * invGammaCorrect(rt2[1]));
tempc[2] = gammaCorrect(fac1 * invGammaCorrect(rt1[2]) + fac2 * invGammaCorrect(rt2[2]));
tempc[3] = gammaCorrect(fac1 * invGammaCorrect(rt1[3]) + fac2 * invGammaCorrect(rt2[3]));
premul_float_to_straight_uchar(rt, tempc);
cp1 += 4; cp2 += 4; rt += 4;
}
if (y == 0)
break;
y--;
x = xo;
while (x--) {
straight_uchar_to_premul_float(rt1, cp1);
straight_uchar_to_premul_float(rt2, cp2);
tempc[0] = gammaCorrect(fac1 * invGammaCorrect(rt1[0]) + fac2 * invGammaCorrect(rt2[0]));
tempc[1] = gammaCorrect(fac1 * invGammaCorrect(rt1[1]) + fac2 * invGammaCorrect(rt2[1]));
tempc[2] = gammaCorrect(fac1 * invGammaCorrect(rt1[2]) + fac2 * invGammaCorrect(rt2[2]));
tempc[3] = gammaCorrect(fac1 * invGammaCorrect(rt1[3]) + fac2 * invGammaCorrect(rt2[3]));
premul_float_to_straight_uchar(rt, tempc);
cp1 += 4; cp2 += 4; rt += 4;
}
}
}
static void do_gammacross_effect_float(
float facf0, float UNUSED(facf1), int x, int y, float *rect1,
float *rect2, float *out)
{
float fac1, fac2;
int xo;
float *rt1, *rt2, *rt;
xo = x;
rt1 = rect1;
rt2 = rect2;
rt = out;
fac2 = facf0;
fac1 = 1.0f - fac2;
while (y--) {
x = xo * 4;
while (x--) {
*rt = gammaCorrect(fac1 * invGammaCorrect(*rt1) + fac2 * invGammaCorrect(*rt2));
rt1++; rt2++; rt++;
}
if (y == 0)
break;
y--;
x = xo * 4;
while (x--) {
*rt = gammaCorrect(fac1 * invGammaCorrect(*rt1) + fac2 * invGammaCorrect(*rt2));
rt1++; rt2++; rt++;
}
}
}
static struct ImBuf *gammacross_init_execution(const SeqRenderData *context, ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *ibuf3)
{
ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);
build_gammatabs();
return out;
}
static void do_gammacross_effect(
const SeqRenderData *context, Sequence *UNUSED(seq), float UNUSED(cfra),
float facf0, float facf1, ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *UNUSED(ibuf3),
int start_line, int total_lines, ImBuf *out)
{
if (out->rect_float) {
float *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_float_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_gammacross_effect_float(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
else {
unsigned char *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_byte_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_gammacross_effect_byte(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
}
/*********************** Add *************************/
static void do_add_effect_byte(
float facf0, float facf1, int x, int y,
unsigned char *rect1, unsigned char *rect2, unsigned char *out)
{
int xo, fac1, fac3;
unsigned char *cp1, *cp2, *rt;
xo = x;
cp1 = rect1;
cp2 = rect2;
rt = out;
fac1 = (int)(256.0f * facf0);
fac3 = (int)(256.0f * facf1);
while (y--) {
x = xo;
while (x--) {
const int m = fac1 * (int)cp2[3];
rt[0] = min_ii(cp1[0] + ((m * cp2[0]) >> 16), 255);
rt[1] = min_ii(cp1[1] + ((m * cp2[1]) >> 16), 255);
rt[2] = min_ii(cp1[2] + ((m * cp2[2]) >> 16), 255);
rt[3] = cp1[3];
cp1 += 4; cp2 += 4; rt += 4;
}
if (y == 0)
break;
y--;
x = xo;
while (x--) {
const int m = fac3 * (int)cp2[3];
rt[0] = min_ii(cp1[0] + ((m * cp2[0]) >> 16), 255);
rt[1] = min_ii(cp1[1] + ((m * cp2[1]) >> 16), 255);
rt[2] = min_ii(cp1[2] + ((m * cp2[2]) >> 16), 255);
rt[3] = cp1[3];
cp1 += 4; cp2 += 4; rt += 4;
}
}
}
static void do_add_effect_float(float facf0, float facf1, int x, int y, float *rect1, float *rect2, float *out)
{
int xo;
float fac1, fac3;
float *rt1, *rt2, *rt;
xo = x;
rt1 = rect1;
rt2 = rect2;
rt = out;
fac1 = facf0;
fac3 = facf1;
while (y--) {
x = xo;
while (x--) {
const float m = (1.0f - (rt1[3] * (1.0f - fac1))) * rt2[3];
rt[0] = rt1[0] + m * rt2[0];
rt[1] = rt1[1] + m * rt2[1];
rt[2] = rt1[2] + m * rt2[2];
rt[3] = rt1[3];
rt1 += 4; rt2 += 4; rt += 4;
}
if (y == 0)
break;
y--;
x = xo;
while (x--) {
const float m = (1.0f - (rt1[3] * (1.0f - fac3))) * rt2[3];
rt[0] = rt1[0] + m * rt2[0];
rt[1] = rt1[1] + m * rt2[1];
rt[2] = rt1[2] + m * rt2[2];
rt[3] = rt1[3];
rt1 += 4; rt2 += 4; rt += 4;
}
}
}
static void do_add_effect(
const SeqRenderData *context, Sequence *UNUSED(seq), float UNUSED(cfra), float facf0, float facf1,
ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *UNUSED(ibuf3), int start_line, int total_lines, ImBuf *out)
{
if (out->rect_float) {
float *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_float_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_add_effect_float(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
else {
unsigned char *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_byte_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_add_effect_byte(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
}
/*********************** Sub *************************/
static void do_sub_effect_byte(
float facf0, float facf1, int x, int y,
unsigned char *rect1, unsigned char *rect2, unsigned char *out)
{
int xo, fac1, fac3;
unsigned char *cp1, *cp2, *rt;
xo = x;
cp1 = rect1;
cp2 = rect2;
rt = out;
fac1 = (int) (256.0f * facf0);
fac3 = (int) (256.0f * facf1);
while (y--) {
x = xo;
while (x--) {
const int m = fac1 * (int)cp2[3];
rt[0] = max_ii(cp1[0] - ((m * cp2[0]) >> 16), 0);
rt[1] = max_ii(cp1[1] - ((m * cp2[1]) >> 16), 0);
rt[2] = max_ii(cp1[2] - ((m * cp2[2]) >> 16), 0);
rt[3] = cp1[3];
cp1 += 4; cp2 += 4; rt += 4;
}
if (y == 0)
break;
y--;
x = xo;
while (x--) {
const int m = fac3 * (int)cp2[3];
rt[0] = max_ii(cp1[0] - ((m * cp2[0]) >> 16), 0);
rt[1] = max_ii(cp1[1] - ((m * cp2[1]) >> 16), 0);
rt[2] = max_ii(cp1[2] - ((m * cp2[2]) >> 16), 0);
rt[3] = cp1[3];
cp1 += 4; cp2 += 4; rt += 4;
}
}
}
static void do_sub_effect_float(
float UNUSED(facf0), float facf1, int x, int y,
float *rect1, float *rect2, float *out)
{
int xo;
float /* fac1, */ fac3_inv;
float *rt1, *rt2, *rt;
xo = x;
rt1 = rect1;
rt2 = rect2;
rt = out;
/* UNUSED */
// fac1 = facf0;
fac3_inv = 1.0f - facf1;
while (y--) {
x = xo;
while (x--) {
const float m = (1.0f - (rt1[3] * fac3_inv)) * rt2[3];
rt[0] = max_ff(rt1[0] - m * rt2[0], 0.0f);
rt[1] = max_ff(rt1[1] - m * rt2[1], 0.0f);
rt[2] = max_ff(rt1[2] - m * rt2[2], 0.0f);
rt[3] = rt1[3];
rt1 += 4; rt2 += 4; rt += 4;
}
if (y == 0)
break;
y--;
x = xo;
while (x--) {
const float m = (1.0f - (rt1[3] * fac3_inv)) * rt2[3];
rt[0] = max_ff(rt1[0] - m * rt2[0], 0.0f);
rt[1] = max_ff(rt1[1] - m * rt2[1], 0.0f);
rt[2] = max_ff(rt1[2] - m * rt2[2], 0.0f);
rt[3] = rt1[3];
rt1 += 4; rt2 += 4; rt += 4;
}
}
}
static void do_sub_effect(
const SeqRenderData *context, Sequence *UNUSED(seq), float UNUSED(cfra), float facf0, float facf1,
ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *UNUSED(ibuf3), int start_line, int total_lines, ImBuf *out)
{
if (out->rect_float) {
float *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_float_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_sub_effect_float(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
else {
unsigned char *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_byte_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_sub_effect_byte(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
}
/*********************** Drop *************************/
/* Must be > 0 or add precopy, etc to the function */
#define XOFF 8
#define YOFF 8
static void do_drop_effect_byte(
float facf0, float facf1, int x, int y,
unsigned char *rect2i, unsigned char *rect1i, unsigned char *outi)
{
int temp, fac, fac1, fac2;
unsigned char *rt1, *rt2, *out;
int field = 1;
const int width = x;
const int height = y;
const int xoff = min_ii(XOFF, width);
const int yoff = min_ii(YOFF, height);
fac1 = (int) (70.0f * facf0);
fac2 = (int) (70.0f * facf1);
rt2 = rect2i + yoff * 4 * width;
rt1 = rect1i;
out = outi;
for (y = 0; y < height - yoff; y++) {
if (field) fac = fac1;
else fac = fac2;
field = !field;
memcpy(out, rt1, sizeof(*out) * xoff * 4);
rt1 += xoff * 4;
out += xoff * 4;
for (x = xoff; x < width; x++) {
temp = ((fac * rt2[3]) >> 8);
*(out++) = MAX2(0, *rt1 - temp); rt1++;
*(out++) = MAX2(0, *rt1 - temp); rt1++;
*(out++) = MAX2(0, *rt1 - temp); rt1++;
*(out++) = MAX2(0, *rt1 - temp); rt1++;
rt2 += 4;
}
rt2 += xoff * 4;
}
memcpy(out, rt1, sizeof(*out) * yoff * 4 * width);
}
static void do_drop_effect_float(
float facf0, float facf1, int x, int y,
float *rect2i, float *rect1i, float *outi)
{
float temp, fac, fac1, fac2;
float *rt1, *rt2, *out;
int field = 1;
const int width = x;
const int height = y;
const int xoff = min_ii(XOFF, width);
const int yoff = min_ii(YOFF, height);
fac1 = 70.0f * facf0;
fac2 = 70.0f * facf1;
rt2 = rect2i + yoff * 4 * width;
rt1 = rect1i;
out = outi;
for (y = 0; y < height - yoff; y++) {
if (field) fac = fac1;
else fac = fac2;
field = !field;
memcpy(out, rt1, sizeof(*out) * xoff * 4);
rt1 += xoff * 4;
out += xoff * 4;
for (x = xoff; x < width; x++) {
temp = fac * rt2[3];
*(out++) = MAX2(0.0f, *rt1 - temp); rt1++;
*(out++) = MAX2(0.0f, *rt1 - temp); rt1++;
*(out++) = MAX2(0.0f, *rt1 - temp); rt1++;
*(out++) = MAX2(0.0f, *rt1 - temp); rt1++;
rt2 += 4;
}
rt2 += xoff * 4;
}
memcpy(out, rt1, sizeof(*out) * yoff * 4 * width);
}
/*********************** Mul *************************/
static void do_mul_effect_byte(
float facf0, float facf1, int x, int y,
unsigned char *rect1, unsigned char *rect2, unsigned char *out)
{
int xo, fac1, fac3;
unsigned char *rt1, *rt2, *rt;
xo = x;
rt1 = rect1;
rt2 = rect2;
rt = out;
fac1 = (int)(256.0f * facf0);
fac3 = (int)(256.0f * facf1);
/* formula:
* fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + axaux = c * px + py * s; //+centx
* yaux = -s * px + c * py; //+centy
*/
while (y--) {
x = xo;
while (x--) {
rt[0] = rt1[0] + ((fac1 * rt1[0] * (rt2[0] - 255)) >> 16);
rt[1] = rt1[1] + ((fac1 * rt1[1] * (rt2[1] - 255)) >> 16);
rt[2] = rt1[2] + ((fac1 * rt1[2] * (rt2[2] - 255)) >> 16);
rt[3] = rt1[3] + ((fac1 * rt1[3] * (rt2[3] - 255)) >> 16);
rt1 += 4; rt2 += 4; rt += 4;
}
if (y == 0) break;
y--;
x = xo;
while (x--) {
rt[0] = rt1[0] + ((fac3 * rt1[0] * (rt2[0] - 255)) >> 16);
rt[1] = rt1[1] + ((fac3 * rt1[1] * (rt2[1] - 255)) >> 16);
rt[2] = rt1[2] + ((fac3 * rt1[2] * (rt2[2] - 255)) >> 16);
rt[3] = rt1[3] + ((fac3 * rt1[3] * (rt2[3] - 255)) >> 16);
rt1 += 4; rt2 += 4; rt += 4;
}
}
}
static void do_mul_effect_float(
float facf0, float facf1, int x, int y,
float *rect1, float *rect2, float *out)
{
int xo;
float fac1, fac3;
float *rt1, *rt2, *rt;
xo = x;
rt1 = rect1;
rt2 = rect2;
rt = out;
fac1 = facf0;
fac3 = facf1;
/* formula:
* fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + a
*/
while (y--) {
x = xo;
while (x--) {
rt[0] = rt1[0] + fac1 * rt1[0] * (rt2[0] - 1.0f);
rt[1] = rt1[1] + fac1 * rt1[1] * (rt2[1] - 1.0f);
rt[2] = rt1[2] + fac1 * rt1[2] * (rt2[2] - 1.0f);
rt[3] = rt1[3] + fac1 * rt1[3] * (rt2[3] - 1.0f);
rt1 += 4; rt2 += 4; rt += 4;
}
if (y == 0)
break;
y--;
x = xo;
while (x--) {
rt[0] = rt1[0] + fac3 * rt1[0] * (rt2[0] - 1.0f);
rt[1] = rt1[1] + fac3 * rt1[1] * (rt2[1] - 1.0f);
rt[2] = rt1[2] + fac3 * rt1[2] * (rt2[2] - 1.0f);
rt[3] = rt1[3] + fac3 * rt1[3] * (rt2[3] - 1.0f);
rt1 += 4; rt2 += 4; rt += 4;
}
}
}
static void do_mul_effect(
const SeqRenderData *context, Sequence *UNUSED(seq), float UNUSED(cfra), float facf0, float facf1,
ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *UNUSED(ibuf3), int start_line, int total_lines, ImBuf *out)
{
if (out->rect_float) {
float *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_float_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_mul_effect_float(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
else {
unsigned char *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_byte_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_mul_effect_byte(facf0, facf1, context->rectx, total_lines, rect1, rect2, rect_out);
}
}
/*********************** Blend Mode ***************************************/
typedef void (*IMB_blend_func_byte)(unsigned char *dst, const unsigned char *src1, const unsigned char *src2);
typedef void (*IMB_blend_func_float)(float *dst, const float *src1, const float *src2);
BLI_INLINE void apply_blend_function_byte(
float facf0, float facf1, int x, int y,
unsigned char *rect1, unsigned char *rect2, unsigned char *out, IMB_blend_func_byte blend_function)
{
int xo;
unsigned char *rt1, *rt2, *rt;
unsigned int achannel;
xo = x;
rt1 = rect1;
rt2 = rect2;
rt = out;
while (y--) {
for (x = xo; x > 0; x--) {
achannel = rt2[3];
rt2[3] = (unsigned int) achannel * facf0;
blend_function(rt, rt1, rt2);
rt2[3] = achannel;
rt[3] = rt2[3];
rt1 += 4;
rt2 += 4;
rt += 4;
}
if (y == 0) {
break;
}
y--;
for (x = xo; x > 0; x--) {
achannel = rt2[3];
rt2[3] = (unsigned int) achannel * facf1;
blend_function(rt, rt1, rt2);
rt2[3] = achannel;
rt[3] = rt2[3];
rt1 += 4;
rt2 += 4;
rt += 4;
}
}
}
BLI_INLINE void apply_blend_function_float(
float facf0, float facf1, int x, int y,
float *rect1, float *rect2, float *out, IMB_blend_func_float blend_function)
{
int xo;
float *rt1, *rt2, *rt;
float achannel;
xo = x;
rt1 = rect1;
rt2 = rect2;
rt = out;
while (y--) {
for (x = xo; x > 0; x--) {
achannel = rt2[3];
rt2[3] = achannel * facf0;
blend_function(rt, rt1, rt2);
rt2[3] = achannel;
rt[3] = rt2[3];
rt1 += 4;
rt2 += 4;
rt += 4;
}
if (y == 0) {
break;
}
y--;
for (x = xo; x > 0; x--) {
achannel = rt2[3];
rt2[3] = achannel * facf1;
blend_function(rt, rt1, rt2);
rt2[3] = achannel;
rt[3] = rt2[3];
rt1 += 4;
rt2 += 4;
rt += 4;
}
}
}
static void do_blend_effect_float(
float facf0, float facf1, int x, int y,
float *rect1, float *rect2, int btype, float *out)
{
switch (btype) {
case SEQ_TYPE_ADD:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_add_float);
break;
case SEQ_TYPE_SUB:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_sub_float);
break;
case SEQ_TYPE_MUL:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_mul_float);
break;
case SEQ_TYPE_DARKEN:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_darken_float);
break;
case SEQ_TYPE_BURN:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_burn_float);
break;
case SEQ_TYPE_LINEAR_BURN:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_linearburn_float);
break;
case SEQ_TYPE_SCREEN:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_screen_float);
break;
case SEQ_TYPE_LIGHTEN:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_lighten_float);
break;
case SEQ_TYPE_DODGE:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_dodge_float);
break;
case SEQ_TYPE_OVERLAY:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_overlay_float);
break;
case SEQ_TYPE_SOFT_LIGHT:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_softlight_float);
break;
case SEQ_TYPE_HARD_LIGHT:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_hardlight_float);
break;
case SEQ_TYPE_PIN_LIGHT:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_pinlight_float);
break;
case SEQ_TYPE_LIN_LIGHT:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_linearlight_float);
break;
case SEQ_TYPE_VIVID_LIGHT:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_vividlight_float);
break;
case SEQ_TYPE_BLEND_COLOR:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_color_float);
break;
case SEQ_TYPE_HUE:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_hue_float);
break;
case SEQ_TYPE_SATURATION:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_saturation_float);
break;
case SEQ_TYPE_VALUE:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_luminosity_float);
break;
case SEQ_TYPE_DIFFERENCE:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_difference_float);
break;
case SEQ_TYPE_EXCLUSION:
apply_blend_function_float(facf0, facf1, x, y, rect1, rect2, out, blend_color_exclusion_float);
break;
default:
break;
}
}
static void do_blend_effect_byte(
float facf0, float facf1, int x, int y,
unsigned char *rect1, unsigned char *rect2, int btype, unsigned char *out)
{
switch (btype) {
case SEQ_TYPE_ADD:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_add_byte);
break;
case SEQ_TYPE_SUB:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_sub_byte);
break;
case SEQ_TYPE_MUL:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_mul_byte);
break;
case SEQ_TYPE_DARKEN:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_darken_byte);
break;
case SEQ_TYPE_BURN:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_burn_byte);
break;
case SEQ_TYPE_LINEAR_BURN:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_linearburn_byte);
break;
case SEQ_TYPE_SCREEN:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_screen_byte);
break;
case SEQ_TYPE_LIGHTEN:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_lighten_byte);
break;
case SEQ_TYPE_DODGE:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_dodge_byte);
break;
case SEQ_TYPE_OVERLAY:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_overlay_byte);
break;
case SEQ_TYPE_SOFT_LIGHT:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_softlight_byte);
break;
case SEQ_TYPE_HARD_LIGHT:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_hardlight_byte);
break;
case SEQ_TYPE_PIN_LIGHT:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_pinlight_byte);
break;
case SEQ_TYPE_LIN_LIGHT:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_linearlight_byte);
break;
case SEQ_TYPE_VIVID_LIGHT:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_vividlight_byte);
break;
case SEQ_TYPE_BLEND_COLOR:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_color_byte);
break;
case SEQ_TYPE_HUE:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_hue_byte);
break;
case SEQ_TYPE_SATURATION:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_saturation_byte);
break;
case SEQ_TYPE_VALUE:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_luminosity_byte);
break;
case SEQ_TYPE_DIFFERENCE:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_difference_byte);
break;
case SEQ_TYPE_EXCLUSION:
apply_blend_function_byte(facf0, facf1, x, y, rect1, rect2, out, blend_color_exclusion_byte);
break;
default:
break;
}
}
static void do_blend_mode_effect(
const SeqRenderData *context, Sequence *seq, float UNUSED(cfra), float facf0, float facf1,
ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *UNUSED(ibuf3), int start_line, int total_lines, ImBuf *out)
{
if (out->rect_float) {
float *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_float_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_blend_effect_float(facf0, facf1, context->rectx, total_lines, rect1, rect2, seq->blend_mode, rect_out);
}
else {
unsigned char *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_byte_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_blend_effect_byte(facf0, facf1, context->rectx, total_lines, rect1, rect2, seq->blend_mode, rect_out);
}
}
/*********************** Color Mix Effect *************************/
static void init_colormix_effect(Sequence *seq)
{
ColorMixVars *data;
if (seq->effectdata) {
MEM_freeN(seq->effectdata);
}
seq->effectdata = MEM_callocN(sizeof(ColorMixVars), "colormixvars");
data = (ColorMixVars *) seq->effectdata;
data->blend_effect = SEQ_TYPE_OVERLAY;
data->factor = 1.0f;
}
static void do_colormix_effect(
const SeqRenderData *context, Sequence *seq, float UNUSED(cfra), float UNUSED(facf0), float UNUSED(facf1),
ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *UNUSED(ibuf3), int start_line, int total_lines, ImBuf *out)
{
float facf;
ColorMixVars *data = seq->effectdata;
facf = data->factor;
if (out->rect_float) {
float *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_float_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_blend_effect_float(facf, facf, context->rectx, total_lines, rect1, rect2, data->blend_effect, rect_out);
}
else {
unsigned char *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_byte_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_blend_effect_byte(facf, facf, context->rectx, total_lines, rect1, rect2, data->blend_effect, rect_out);
}
}
/*********************** Wipe *************************/
typedef struct WipeZone {
float angle;
int flip;
int xo, yo;
int width;
float pythangle;
} WipeZone;
static void precalc_wipe_zone(WipeZone *wipezone, WipeVars *wipe, int xo, int yo)
{
wipezone->flip = (wipe->angle < 0.0f);
wipezone->angle = tanf(fabsf(wipe->angle));
wipezone->xo = xo;
wipezone->yo = yo;
wipezone->width = (int)(wipe->edgeWidth * ((xo + yo) / 2.0f));
wipezone->pythangle = 1.0f / sqrtf(wipezone->angle * wipezone->angle + 1.0f);
}
/* This function calculates the blur band for the wipe effects */
static float in_band(float width, float dist, int side, int dir)
{
float alpha;
if (width == 0)
return (float)side;
if (width < dist)
return (float)side;
if (side == 1)
alpha = (dist + 0.5f * width) / (width);
else
alpha = (0.5f * width - dist) / (width);
if (dir == 0)
alpha = 1 - alpha;
return alpha;
}
static float check_zone(WipeZone *wipezone, int x, int y, Sequence *seq, float facf0)
{
float posx, posy, hyp, hyp2, angle, hwidth, b1, b2, b3, pointdist;
/* some future stuff */
/* float hyp3, hyp4, b4, b5 */
float temp1, temp2, temp3, temp4; /* some placeholder variables */
int xo = wipezone->xo;
int yo = wipezone->yo;
float halfx = xo * 0.5f;
float halfy = yo * 0.5f;
float widthf, output = 0;
WipeVars *wipe = (WipeVars *)seq->effectdata;
int width;
if (wipezone->flip) x = xo - x;
angle = wipezone->angle;
if (wipe->forward) {
posx = facf0 * xo;
posy = facf0 * yo;
}
else {
posx = xo - facf0 * xo;
posy = yo - facf0 * yo;
}
switch (wipe->wipetype) {
case DO_SINGLE_WIPE:
width = min_ii(wipezone->width, facf0 * yo);
width = min_ii(width, yo - facf0 * yo);
if (angle == 0.0f) {
b1 = posy;
b2 = y;
hyp = fabsf(y - posy);
}
else {
b1 = posy - (-angle) * posx;
b2 = y - (-angle) * x;
hyp = fabsf(angle * x + y + (-posy - angle * posx)) * wipezone->pythangle;
}
if (angle < 0) {
temp1 = b1;
b1 = b2;
b2 = temp1;
}
if (wipe->forward) {
if (b1 < b2)
output = in_band(width, hyp, 1, 1);
else
output = in_band(width, hyp, 0, 1);
}
else {
if (b1 < b2)
output = in_band(width, hyp, 0, 1);
else
output = in_band(width, hyp, 1, 1);
}
break;
case DO_DOUBLE_WIPE:
if (!wipe->forward)
facf0 = 1.0f - facf0; /* Go the other direction */
width = wipezone->width; /* calculate the blur width */
hwidth = width * 0.5f;
if (angle == 0) {
b1 = posy * 0.5f;
b3 = yo - posy * 0.5f;
b2 = y;
hyp = fabsf(y - posy * 0.5f);
hyp2 = fabsf(y - (yo - posy * 0.5f));
}
else {
b1 = posy * 0.5f - (-angle) * posx * 0.5f;
b3 = (yo - posy * 0.5f) - (-angle) * (xo - posx * 0.5f);
b2 = y - (-angle) * x;
hyp = fabsf(angle * x + y + (-posy * 0.5f - angle * posx * 0.5f)) * wipezone->pythangle;
hyp2 = fabsf(angle * x + y + (-(yo - posy * 0.5f) - angle * (xo - posx * 0.5f))) * wipezone->pythangle;
}
hwidth = min_ff(hwidth, fabsf(b3 - b1) / 2.0f);
if (b2 < b1 && b2 < b3) {
output = in_band(hwidth, hyp, 0, 1);
}
else if (b2 > b1 && b2 > b3) {
output = in_band(hwidth, hyp2, 0, 1);
}
else {
if (hyp < hwidth && hyp2 > hwidth)
output = in_band(hwidth, hyp, 1, 1);
else if (hyp > hwidth && hyp2 < hwidth)
output = in_band(hwidth, hyp2, 1, 1);
else
output = in_band(hwidth, hyp2, 1, 1) * in_band(hwidth, hyp, 1, 1);
}
if (!wipe->forward) output = 1 - output;
break;
case DO_CLOCK_WIPE:
/*
* temp1: angle of effect center in rads
* temp2: angle of line through (halfx, halfy) and (x, y) in rads
* temp3: angle of low side of blur
* temp4: angle of high side of blur
*/
output = 1.0f - facf0;
widthf = wipe->edgeWidth * 2.0f * (float)M_PI;
temp1 = 2.0f * (float)M_PI * facf0;
if (wipe->forward) {
temp1 = 2.0f * (float)M_PI - temp1;
}
x = x - halfx;
y = y - halfy;
temp2 = asin(abs(y) / hypot(x, y));
if (x <= 0 && y >= 0) temp2 = (float)M_PI - temp2;
else if (x <= 0 && y <= 0) temp2 += (float)M_PI;
else if (x >= 0 && y <= 0) temp2 = 2.0f * (float)M_PI - temp2;
if (wipe->forward) {
temp3 = temp1 - (widthf * 0.5f) * facf0;
temp4 = temp1 + (widthf * 0.5f) * (1 - facf0);
}
else {
temp3 = temp1 - (widthf * 0.5f) * (1 - facf0);
temp4 = temp1 + (widthf * 0.5f) * facf0;
}
if (temp3 < 0) temp3 = 0;
if (temp4 > 2.0f * (float)M_PI) temp4 = 2.0f * (float)M_PI;
if (temp2 < temp3) output = 0;
else if (temp2 > temp4) output = 1;
else output = (temp2 - temp3) / (temp4 - temp3);
if (x == 0 && y == 0) output = 1;
if (output != output) output = 1;
if (wipe->forward) output = 1 - output;
break;
/* BOX WIPE IS NOT WORKING YET */
/* case DO_CROSS_WIPE: */
/* BOX WIPE IS NOT WORKING YET */
#if 0
case DO_BOX_WIPE:
if (!wipe->forward)
facf0 = 1.0f - facf0; /* Go the other direction */
width = (int)(wipe->edgeWidth * ((xo + yo) / 2.0));
hwidth = (float)width / 2.0;
if (angle == 0) angle = 0.000001;
b1 = posy / 2 - (-angle) * posx / 2;
b3 = (yo - posy / 2) - (-angle) * (xo - posx / 2);
b2 = y - (-angle) * x;
hyp = abs(angle * x + y + (-posy / 2 - angle * posx / 2)) * wipezone->pythangle;
hyp2 = abs(angle * x + y + (-(yo - posy / 2) - angle * (xo - posx / 2))) * wipezone->pythangle;
temp1 = xo * (1 - facf0 / 2) - xo * facf0 / 2;
temp2 = yo * (1 - facf0 / 2) - yo * facf0 / 2;
pointdist = hypot(temp1, temp2);
if (b2 < b1 && b2 < b3) {
if (hwidth < pointdist)
output = in_band(hwidth, hyp, 0, 1);
}
else if (b2 > b1 && b2 > b3) {
if (hwidth < pointdist)
output = in_band(hwidth, hyp2, 0, 1);
}
else {
if (hyp < hwidth && hyp2 > hwidth)
output = in_band(hwidth, hyp, 1, 1);
else if (hyp > hwidth && hyp2 < hwidth)
output = in_band(hwidth, hyp2, 1, 1);
else
output = in_band(hwidth, hyp2, 1, 1) * in_band(hwidth, hyp, 1, 1);
}
if (!wipe->forward) {
facf0 = 1.0f - facf0; /* Go the other direction */
}
angle = -1 / angle;
b1 = posy / 2 - (-angle) * posx / 2;
b3 = (yo - posy / 2) - (-angle) * (xo - posx / 2);
b2 = y - (-angle) * x;
hyp = abs(angle * x + y + (-posy / 2 - angle * posx / 2)) * wipezone->pythangle;
hyp2 = abs(angle * x + y + (-(yo - posy / 2) - angle * (xo - posx / 2))) * wipezone->pythangle;
if (b2 < b1 && b2 < b3) {
if (hwidth < pointdist)
output *= in_band(hwidth, hyp, 0, 1);
}
else if (b2 > b1 && b2 > b3) {
if (hwidth < pointdist)
output *= in_band(hwidth, hyp2, 0, 1);
}
else {
if (hyp < hwidth && hyp2 > hwidth)
output *= in_band(hwidth, hyp, 1, 1);
else if (hyp > hwidth && hyp2 < hwidth)
output *= in_band(hwidth, hyp2, 1, 1);
else
output *= in_band(hwidth, hyp2, 1, 1) * in_band(hwidth, hyp, 1, 1);
}
break;
#endif
case DO_IRIS_WIPE:
if (xo > yo) yo = xo;
else xo = yo;
if (!wipe->forward) facf0 = 1 - facf0;
width = wipezone->width;
hwidth = width * 0.5f;
temp1 = (halfx - (halfx) * facf0);
pointdist = hypotf(temp1, temp1);
temp2 = hypotf(halfx - x, halfy - y);
if (temp2 > pointdist) output = in_band(hwidth, fabsf(temp2 - pointdist), 0, 1);
else output = in_band(hwidth, fabsf(temp2 - pointdist), 1, 1);
if (!wipe->forward) output = 1 - output;
break;
}
if (output < 0) output = 0;
else if (output > 1) output = 1;
return output;
}
static void init_wipe_effect(Sequence *seq)
{
if (seq->effectdata)
MEM_freeN(seq->effectdata);
seq->effectdata = MEM_callocN(sizeof(WipeVars), "wipevars");
}
static int num_inputs_wipe(void)
{
return 2;
}
static void free_wipe_effect(Sequence *seq)
{
if (seq->effectdata)
MEM_freeN(seq->effectdata);
seq->effectdata = NULL;
}
static void copy_wipe_effect(Sequence *dst, Sequence *src)
{
dst->effectdata = MEM_dupallocN(src->effectdata);
}
static void do_wipe_effect_byte(
Sequence *seq, float facf0, float UNUSED(facf1), int x, int y,
unsigned char *rect1, unsigned char *rect2, unsigned char *out)
{
WipeZone wipezone;
WipeVars *wipe = (WipeVars *)seq->effectdata;
int xo, yo;
unsigned char *cp1, *cp2, *rt;
precalc_wipe_zone(&wipezone, wipe, x, y);
cp1 = rect1;
cp2 = rect2;
rt = out;
xo = x;
yo = y;
for (y = 0; y < yo; y++) {
for (x = 0; x < xo; x++) {
float check = check_zone(&wipezone, x, y, seq, facf0);
if (check) {
if (cp1) {
float rt1[4], rt2[4], tempc[4];
straight_uchar_to_premul_float(rt1, cp1);
straight_uchar_to_premul_float(rt2, cp2);
tempc[0] = rt1[0] * check + rt2[0] * (1 - check);
tempc[1] = rt1[1] * check + rt2[1] * (1 - check);
tempc[2] = rt1[2] * check + rt2[2] * (1 - check);
tempc[3] = rt1[3] * check + rt2[3] * (1 - check);
premul_float_to_straight_uchar(rt, tempc);
}
else {
rt[0] = 0;
rt[1] = 0;
rt[2] = 0;
rt[3] = 255;
}
}
else {
if (cp2) {
rt[0] = cp2[0];
rt[1] = cp2[1];
rt[2] = cp2[2];
rt[3] = cp2[3];
}
else {
rt[0] = 0;
rt[1] = 0;
rt[2] = 0;
rt[3] = 255;
}
}
rt += 4;
if (cp1 != NULL) {
cp1 += 4;
}
if (cp2 != NULL) {
cp2 += 4;
}
}
}
}
static void do_wipe_effect_float(
Sequence *seq, float facf0, float UNUSED(facf1), int x, int y,
float *rect1, float *rect2, float *out)
{
WipeZone wipezone;
WipeVars *wipe = (WipeVars *)seq->effectdata;
int xo, yo;
float *rt1, *rt2, *rt;
precalc_wipe_zone(&wipezone, wipe, x, y);
rt1 = rect1;
rt2 = rect2;
rt = out;
xo = x;
yo = y;
for (y = 0; y < yo; y++) {
for (x = 0; x < xo; x++) {
float check = check_zone(&wipezone, x, y, seq, facf0);
if (check) {
if (rt1) {
rt[0] = rt1[0] * check + rt2[0] * (1 - check);
rt[1] = rt1[1] * check + rt2[1] * (1 - check);
rt[2] = rt1[2] * check + rt2[2] * (1 - check);
rt[3] = rt1[3] * check + rt2[3] * (1 - check);
}
else {
rt[0] = 0;
rt[1] = 0;
rt[2] = 0;
rt[3] = 1.0;
}
}
else {
if (rt2) {
rt[0] = rt2[0];
rt[1] = rt2[1];
rt[2] = rt2[2];
rt[3] = rt2[3];
}
else {
rt[0] = 0;
rt[1] = 0;
rt[2] = 0;
rt[3] = 1.0;
}
}
rt += 4;
if (rt1 != NULL) {
rt1 += 4;
}
if (rt2 != NULL) {
rt2 += 4;
}
}
}
}
static ImBuf *do_wipe_effect(
const SeqRenderData *context, Sequence *seq, float UNUSED(cfra), float facf0, float facf1,
ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *ibuf3)
{
ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);
if (out->rect_float) {
do_wipe_effect_float(
seq, facf0, facf1, context->rectx, context->recty, ibuf1->rect_float,
ibuf2->rect_float, out->rect_float);
}
else {
do_wipe_effect_byte(
seq, facf0, facf1, context->rectx, context->recty, (unsigned char *) ibuf1->rect,
(unsigned char *) ibuf2->rect, (unsigned char *) out->rect);
}
return out;
}
/*********************** Transform *************************/
static void init_transform_effect(Sequence *seq)
{
TransformVars *transform;
if (seq->effectdata)
MEM_freeN(seq->effectdata);
seq->effectdata = MEM_callocN(sizeof(TransformVars), "transformvars");
transform = (TransformVars *) seq->effectdata;
transform->ScalexIni = 1.0f;
transform->ScaleyIni = 1.0f;
transform->xIni = 0.0f;
transform->yIni = 0.0f;
transform->rotIni = 0.0f;
transform->interpolation = 1;
transform->percent = 1;
transform->uniform_scale = 0;
}
static int num_inputs_transform(void)
{
return 1;
}
static void free_transform_effect(Sequence *seq)
{
if (seq->effectdata) MEM_freeN(seq->effectdata);
seq->effectdata = NULL;
}
static void copy_transform_effect(Sequence *dst, Sequence *src)
{
dst->effectdata = MEM_dupallocN(src->effectdata);
}
static void transform_image(
int x, int y, ImBuf *ibuf1, ImBuf *out, float scale_x, float scale_y,
float translate_x, float translate_y, float rotate, int interpolation)
{
int xo, yo, xi, yi;
float xt, yt, xr, yr;
float s, c;
xo = x;
yo = y;
/* Rotate */
s = sinf(rotate);
c = cosf(rotate);
for (yi = 0; yi < yo; yi++) {
for (xi = 0; xi < xo; xi++) {
/* translate point */
xt = xi - translate_x;
yt = yi - translate_y;
/* rotate point with center ref */
xr = c * xt + s * yt;
yr = -s * xt + c * yt;
/* scale point with center ref */
xt = xr / scale_x;
yt = yr / scale_y;
/* undo reference center point */
xt += (xo / 2.0f);
yt += (yo / 2.0f);
/* interpolate */
switch (interpolation) {
case 0:
nearest_interpolation(ibuf1, out, xt, yt, xi, yi);
break;
case 1:
bilinear_interpolation(ibuf1, out, xt, yt, xi, yi);
break;
case 2:
bicubic_interpolation(ibuf1, out, xt, yt, xi, yi);
break;
}
}
}
}
static void do_transform(Scene *scene, Sequence *seq, float UNUSED(facf0), int x, int y, ImBuf *ibuf1, ImBuf *out)
{
TransformVars *transform = (TransformVars *) seq->effectdata;
float scale_x, scale_y, translate_x, translate_y, rotate_radians;
/* Scale */
if (transform->uniform_scale) {
scale_x = scale_y = transform->ScalexIni;
}
else {
scale_x = transform->ScalexIni;
scale_y = transform->ScaleyIni;
}
/* Translate */
if (!transform->percent) {
float rd_s = (scene->r.size / 100.0f);
translate_x = transform->xIni * rd_s + (x / 2.0f);
translate_y = transform->yIni * rd_s + (y / 2.0f);
}
else {
translate_x = x * (transform->xIni / 100.0f) + (x / 2.0f);
translate_y = y * (transform->yIni / 100.0f) + (y / 2.0f);
}
/* Rotate */
rotate_radians = DEG2RADF(transform->rotIni);
transform_image(x, y, ibuf1, out, scale_x, scale_y, translate_x, translate_y, rotate_radians, transform->interpolation);
}
static ImBuf *do_transform_effect(
const SeqRenderData *context, Sequence *seq, float UNUSED(cfra), float facf0,
float UNUSED(facf1), ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *ibuf3)
{
ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);
do_transform(context->scene, seq, facf0, context->rectx, context->recty, ibuf1, out);
return out;
}
/*********************** Glow *************************/
static void RVBlurBitmap2_float(float *map, int width, int height, float blur, int quality)
/* MUUUCCH better than the previous blur. */
/* We do the blurring in two passes which is a whole lot faster. */
/* I changed the math arount to implement an actual Gaussian */
/* distribution. */
/* */
/* Watch out though, it tends to misbehaven with large blur values on */
/* a small bitmap. Avoid avoid avoid. */
/*=============================== */
{
float *temp = NULL, *swap;
float *filter = NULL;
int x, y, i, fx, fy;
int index, ix, halfWidth;
float fval, k, curColor[4], curColor2[4], weight = 0;
/* If we're not really blurring, bail out */
if (blur <= 0)
return;
/* Allocate memory for the tempmap and the blur filter matrix */
temp = MEM_mallocN((width * height * 4 * sizeof(float)), "blurbitmaptemp");
if (!temp)
return;
/* Allocate memory for the filter elements */
halfWidth = ((quality + 1) * blur);
filter = (float *)MEM_mallocN(sizeof(float) * halfWidth * 2, "blurbitmapfilter");
if (!filter) {
MEM_freeN(temp);
return;
}
/* Apparently we're calculating a bell curve based on the standard deviation (or radius)
* This code is based on an example posted to comp.graphics.algorithms by
* Blancmange (bmange@airdmhor.gen.nz)
*/
k = -1.0f / (2.0f * (float)M_PI * blur * blur);
for (ix = 0; ix < halfWidth; ix++) {
weight = (float)exp(k * (ix * ix));
filter[halfWidth - ix] = weight;
filter[halfWidth + ix] = weight;
}
filter[0] = weight;
/* Normalize the array */
fval = 0;
for (ix = 0; ix < halfWidth * 2; ix++)
fval += filter[ix];
for (ix = 0; ix < halfWidth * 2; ix++)
filter[ix] /= fval;
/* Blur the rows */
for (y = 0; y < height; y++) {
/* Do the left & right strips */
for (x = 0; x < halfWidth; x++) {
fx = 0;
zero_v4(curColor);
zero_v4(curColor2);
for (i = x - halfWidth; i < x + halfWidth; i++) {
if ((i >= 0) && (i < width)) {
index = (i + y * width) * 4;
madd_v4_v4fl(curColor, map + index, filter[fx]);
index = (width - 1 - i + y * width) * 4;
madd_v4_v4fl(curColor2, map + index, filter[fx]);
}
fx++;
}
index = (x + y * width) * 4;
copy_v4_v4(temp + index, curColor);
index = (width - 1 - x + y * width) * 4;
copy_v4_v4(temp + index, curColor2);
}
/* Do the main body */
for (x = halfWidth; x < width - halfWidth; x++) {
fx = 0;
zero_v4(curColor);
for (i = x - halfWidth; i < x + halfWidth; i++) {
index = (i + y * width) * 4;
madd_v4_v4fl(curColor, map + index, filter[fx]);
fx++;
}
index = (x + y * width) * 4;
copy_v4_v4(temp + index, curColor);
}
}
/* Swap buffers */
swap = temp; temp = map; map = swap;
/* Blur the columns */
for (x = 0; x < width; x++) {
/* Do the top & bottom strips */
for (y = 0; y < halfWidth; y++) {
fy = 0;
zero_v4(curColor);
zero_v4(curColor2);
for (i = y - halfWidth; i < y + halfWidth; i++) {
if ((i >= 0) && (i < height)) {
/* Bottom */
index = (x + i * width) * 4;
madd_v4_v4fl(curColor, map + index, filter[fy]);
/* Top */
index = (x + (height - 1 - i) * width) * 4;
madd_v4_v4fl(curColor2, map + index, filter[fy]);
}
fy++;
}
index = (x + y * width) * 4;
copy_v4_v4(temp + index, curColor);
index = (x + (height - 1 - y) * width) * 4;
copy_v4_v4(temp + index, curColor2);
}
/* Do the main body */
for (y = halfWidth; y < height - halfWidth; y++) {
fy = 0;
zero_v4(curColor);
for (i = y - halfWidth; i < y + halfWidth; i++) {
index = (x + i * width) * 4;
madd_v4_v4fl(curColor, map + index, filter[fy]);
fy++;
}
index = (x + y * width) * 4;
copy_v4_v4(temp + index, curColor);
}
}
/* Swap buffers */
swap = temp; temp = map; /* map = swap; */ /* UNUSED */
/* Tidy up */
MEM_freeN(filter);
MEM_freeN(temp);
}
static void RVAddBitmaps_float(float *a, float *b, float *c, int width, int height)
{
int x, y, index;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
index = (x + y * width) * 4;
c[index + GlowR] = min_ff(1.0f, a[index + GlowR] + b[index + GlowR]);
c[index + GlowG] = min_ff(1.0f, a[index + GlowG] + b[index + GlowG]);
c[index + GlowB] = min_ff(1.0f, a[index + GlowB] + b[index + GlowB]);
c[index + GlowA] = min_ff(1.0f, a[index + GlowA] + b[index + GlowA]);
}
}
}
static void RVIsolateHighlights_float(
float *in, float *out, int width, int height,
float threshold, float boost, float clamp)
{
int x, y, index;
float intensity;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
index = (x + y * width) * 4;
/* Isolate the intensity */
intensity = (in[index + GlowR] + in[index + GlowG] + in[index + GlowB] - threshold);
if (intensity > 0) {
out[index + GlowR] = min_ff(clamp, (in[index + GlowR] * boost * intensity));
out[index + GlowG] = min_ff(clamp, (in[index + GlowG] * boost * intensity));
out[index + GlowB] = min_ff(clamp, (in[index + GlowB] * boost * intensity));
out[index + GlowA] = min_ff(clamp, (in[index + GlowA] * boost * intensity));
}
else {
out[index + GlowR] = 0;
out[index + GlowG] = 0;
out[index + GlowB] = 0;
out[index + GlowA] = 0;
}
}
}
}
static void init_glow_effect(Sequence *seq)
{
GlowVars *glow;
if (seq->effectdata)
MEM_freeN(seq->effectdata);
seq->effectdata = MEM_callocN(sizeof(GlowVars), "glowvars");
glow = (GlowVars *)seq->effectdata;
glow->fMini = 0.25;
glow->fClamp = 1.0;
glow->fBoost = 0.5;
glow->dDist = 3.0;
glow->dQuality = 3;
glow->bNoComp = 0;
}
static int num_inputs_glow(void)
{
return 1;
}
static void free_glow_effect(Sequence *seq)
{
if (seq->effectdata)
MEM_freeN(seq->effectdata);
seq->effectdata = NULL;
}
static void copy_glow_effect(Sequence *dst, Sequence *src)
{
dst->effectdata = MEM_dupallocN(src->effectdata);
}
static void do_glow_effect_byte(
Sequence *seq, int render_size, float facf0, float UNUSED(facf1), int x, int y,
unsigned char *rect1, unsigned char *UNUSED(rect2), unsigned char *out)
{
float *outbuf, *inbuf;
GlowVars *glow = (GlowVars *)seq->effectdata;
inbuf = MEM_mallocN(4 * sizeof(float) * x * y, "glow effect input");
outbuf = MEM_mallocN(4 * sizeof(float) * x * y, "glow effect output");
IMB_buffer_float_from_byte(inbuf, rect1, IB_PROFILE_SRGB, IB_PROFILE_SRGB, false, x, y, x, x);
IMB_buffer_float_premultiply(inbuf, x, y);
RVIsolateHighlights_float(inbuf, outbuf, x, y, glow->fMini * 3.0f, glow->fBoost * facf0, glow->fClamp);
RVBlurBitmap2_float(outbuf, x, y, glow->dDist * (render_size / 100.0f), glow->dQuality);
if (!glow->bNoComp)
RVAddBitmaps_float(inbuf, outbuf, outbuf, x, y);
IMB_buffer_float_unpremultiply(outbuf, x, y);
IMB_buffer_byte_from_float(out, outbuf, 4, 0.0f, IB_PROFILE_SRGB, IB_PROFILE_SRGB, false, x, y, x, x);
MEM_freeN(inbuf);
MEM_freeN(outbuf);
}
static void do_glow_effect_float(
Sequence *seq, int render_size, float facf0, float UNUSED(facf1), int x, int y,
float *rect1, float *UNUSED(rect2), float *out)
{
float *outbuf = out;
float *inbuf = rect1;
GlowVars *glow = (GlowVars *)seq->effectdata;
RVIsolateHighlights_float(inbuf, outbuf, x, y, glow->fMini * 3.0f, glow->fBoost * facf0, glow->fClamp);
RVBlurBitmap2_float(outbuf, x, y, glow->dDist * (render_size / 100.0f), glow->dQuality);
if (!glow->bNoComp)
RVAddBitmaps_float(inbuf, outbuf, outbuf, x, y);
}
static ImBuf *do_glow_effect(
const SeqRenderData *context, Sequence *seq, float UNUSED(cfra), float facf0, float facf1,
ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *ibuf3)
{
ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);
int render_size = 100 * context->rectx / context->scene->r.xsch;
if (out->rect_float) {
do_glow_effect_float(
seq, render_size, facf0, facf1, context->rectx, context->recty,
ibuf1->rect_float, ibuf2->rect_float, out->rect_float);
}
else {
do_glow_effect_byte(
seq, render_size, facf0, facf1, context->rectx, context->recty,
(unsigned char *) ibuf1->rect, (unsigned char *) ibuf2->rect, (unsigned char *) out->rect);
}
return out;
}
/*********************** Solid color *************************/
static void init_solid_color(Sequence *seq)
{
SolidColorVars *cv;
if (seq->effectdata)
MEM_freeN(seq->effectdata);
seq->effectdata = MEM_callocN(sizeof(SolidColorVars), "solidcolor");
cv = (SolidColorVars *)seq->effectdata;
cv->col[0] = cv->col[1] = cv->col[2] = 0.5;
}
static int num_inputs_color(void)
{
return 0;
}
static void free_solid_color(Sequence *seq)
{
if (seq->effectdata)
MEM_freeN(seq->effectdata);
seq->effectdata = NULL;
}
static void copy_solid_color(Sequence *dst, Sequence *src)
{
dst->effectdata = MEM_dupallocN(src->effectdata);
}
static int early_out_color(Sequence *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1))
{
return EARLY_NO_INPUT;
}
static ImBuf *do_solid_color(
const SeqRenderData *context, Sequence *seq, float UNUSED(cfra), float facf0, float facf1,
ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *ibuf3)
{
ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);
SolidColorVars *cv = (SolidColorVars *)seq->effectdata;
unsigned char *rect;
float *rect_float;
int x; /*= context->rectx;*/ /*UNUSED*/
int y; /*= context->recty;*/ /*UNUSED*/
if (out->rect) {
unsigned char col0[3];
unsigned char col1[3];
col0[0] = facf0 * cv->col[0] * 255;
col0[1] = facf0 * cv->col[1] * 255;
col0[2] = facf0 * cv->col[2] * 255;
col1[0] = facf1 * cv->col[0] * 255;
col1[1] = facf1 * cv->col[1] * 255;
col1[2] = facf1 * cv->col[2] * 255;
rect = (unsigned char *)out->rect;
for (y = 0; y < out->y; y++) {
for (x = 0; x < out->x; x++, rect += 4) {
rect[0] = col0[0];
rect[1] = col0[1];
rect[2] = col0[2];
rect[3] = 255;
}
y++;
if (y < out->y) {
for (x = 0; x < out->x; x++, rect += 4) {
rect[0] = col1[0];
rect[1] = col1[1];
rect[2] = col1[2];
rect[3] = 255;
}
}
}
}
else if (out->rect_float) {
float col0[3];
float col1[3];
col0[0] = facf0 * cv->col[0];
col0[1] = facf0 * cv->col[1];
col0[2] = facf0 * cv->col[2];
col1[0] = facf1 * cv->col[0];
col1[1] = facf1 * cv->col[1];
col1[2] = facf1 * cv->col[2];
rect_float = out->rect_float;
for (y = 0; y < out->y; y++) {
for (x = 0; x < out->x; x++, rect_float += 4) {
rect_float[0] = col0[0];
rect_float[1] = col0[1];
rect_float[2] = col0[2];
rect_float[3] = 1.0;
}
y++;
if (y < out->y) {
for (x = 0; x < out->x; x++, rect_float += 4) {
rect_float[0] = col1[0];
rect_float[1] = col1[1];
rect_float[2] = col1[2];
rect_float[3] = 1.0;
}
}
}
}
return out;
}
/*********************** Mulitcam *************************/
/* no effect inputs for multicam, we use give_ibuf_seq */
static int num_inputs_multicam(void)
{
return 0;
}
static int early_out_multicam(Sequence *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1))
{
return EARLY_NO_INPUT;
}
static ImBuf *do_multicam(
const SeqRenderData *context, Sequence *seq, float cfra, float UNUSED(facf0), float UNUSED(facf1),
ImBuf *UNUSED(ibuf1), ImBuf *UNUSED(ibuf2), ImBuf *UNUSED(ibuf3))
{
ImBuf *i;
ImBuf *out;
Editing *ed;
ListBase *seqbasep;
if (seq->multicam_source == 0 || seq->multicam_source >= seq->machine) {
return NULL;
}
ed = context->scene->ed;
if (!ed) {
return NULL;
}
seqbasep = BKE_sequence_seqbase(&ed->seqbase, seq);
if (!seqbasep) {
return NULL;
}
i = BKE_sequencer_give_ibuf_seqbase(context, cfra, seq->multicam_source, seqbasep);
if (!i) {
return NULL;
}
if (BKE_sequencer_input_have_to_preprocess(context, seq, cfra)) {
out = IMB_dupImBuf(i);
IMB_freeImBuf(i);
}
else {
out = i;
}
return out;
}
/*********************** Adjustment *************************/
/* no effect inputs for adjustment, we use give_ibuf_seq */
static int num_inputs_adjustment(void)
{
return 0;
}
static int early_out_adjustment(Sequence *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1))
{
return EARLY_NO_INPUT;
}
static ImBuf *do_adjustment_impl(const SeqRenderData *context, Sequence *seq, float cfra)
{
Editing *ed;
ListBase *seqbasep;
ImBuf *i = NULL;
ed = context->scene->ed;
seqbasep = BKE_sequence_seqbase(&ed->seqbase, seq);
if (seq->machine > 1) {
i = BKE_sequencer_give_ibuf_seqbase(context, cfra, seq->machine - 1, seqbasep);
}
/* found nothing? so let's work the way up the metastrip stack, so
* that it is possible to group a bunch of adjustment strips into
* a metastrip and have that work on everything below the metastrip
*/
if (!i) {
Sequence *meta;
meta = BKE_sequence_metastrip(&ed->seqbase, NULL, seq);
if (meta) {
i = do_adjustment_impl(context, meta, cfra);
}
}
return i;
}
static ImBuf *do_adjustment(
const SeqRenderData *context, Sequence *seq, float cfra, float UNUSED(facf0), float UNUSED(facf1),
ImBuf *UNUSED(ibuf1), ImBuf *UNUSED(ibuf2), ImBuf *UNUSED(ibuf3))
{
ImBuf *i = NULL;
ImBuf *out;
Editing *ed;
ed = context->scene->ed;
if (!ed) {
return NULL;
}
i = do_adjustment_impl(context, seq, cfra);
if (BKE_sequencer_input_have_to_preprocess(context, seq, cfra)) {
out = IMB_dupImBuf(i);
if (out) {
IMB_metadata_copy(out, i);
}
IMB_freeImBuf(i);
}
else {
out = i;
}
return out;
}
/*********************** Speed *************************/
static void init_speed_effect(Sequence *seq)
{
SpeedControlVars *v;
if (seq->effectdata)
MEM_freeN(seq->effectdata);
seq->effectdata = MEM_callocN(sizeof(SpeedControlVars), "speedcontrolvars");
v = (SpeedControlVars *)seq->effectdata;
v->globalSpeed = 1.0;
v->frameMap = NULL;
v->flags |= SEQ_SPEED_INTEGRATE; /* should be default behavior */
v->length = 0;
}
static void load_speed_effect(Sequence *seq)
{
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
v->frameMap = NULL;
v->length = 0;
}
static int num_inputs_speed(void)
{
return 1;
}
static void free_speed_effect(Sequence *seq)
{
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
if (v->frameMap)
MEM_freeN(v->frameMap);
if (seq->effectdata)
MEM_freeN(seq->effectdata);
seq->effectdata = NULL;
}
static void copy_speed_effect(Sequence *dst, Sequence *src)
{
SpeedControlVars *v;
dst->effectdata = MEM_dupallocN(src->effectdata);
v = (SpeedControlVars *)dst->effectdata;
v->frameMap = NULL;
v->length = 0;
}
static int early_out_speed(Sequence *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1))
{
return EARLY_USE_INPUT_1;
}
static void store_icu_yrange_speed(Sequence *seq, short UNUSED(adrcode), float *ymin, float *ymax)
{
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
/* if not already done, load / initialize data */
BKE_sequence_get_effect(seq);
if ((v->flags & SEQ_SPEED_INTEGRATE) != 0) {
*ymin = -100.0;
*ymax = 100.0;
}
else {
if (v->flags & SEQ_SPEED_COMPRESS_IPO_Y) {
*ymin = 0.0;
*ymax = 1.0;
}
else {
*ymin = 0.0;
*ymax = seq->len;
}
}
}
void BKE_sequence_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool force)
{
int cfra;
float fallback_fac = 1.0f;
SpeedControlVars *v = (SpeedControlVars *) seq->effectdata;
FCurve *fcu = NULL;
int flags = v->flags;
/* if not already done, load / initialize data */
BKE_sequence_get_effect(seq);
if ((force == false) &&
(seq->len == v->length) &&
(v->frameMap != NULL))
{
return;
}
if ((seq->seq1 == NULL) || (seq->len < 1)) {
/* make coverity happy and check for (CID 598) input strip ... */
return;
}
/* XXX - new in 2.5x. should we use the animation system this way?
* The fcurve is needed because many frames need evaluating at once - campbell */
fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL);
if (!v->frameMap || v->length != seq->len) {
if (v->frameMap) MEM_freeN(v->frameMap);
v->length = seq->len;
v->frameMap = MEM_callocN(sizeof(float) * v->length, "speedcontrol frameMap");
}
fallback_fac = 1.0;
if (seq->flag & SEQ_USE_EFFECT_DEFAULT_FADE) {
if ((seq->seq1->enddisp != seq->seq1->start) &&
(seq->seq1->len != 0))
{
fallback_fac = (float) seq->seq1->len / (float) (seq->seq1->enddisp - seq->seq1->start);
flags = SEQ_SPEED_INTEGRATE;
fcu = NULL;
}
}
else {
/* if there is no fcurve, use value as simple multiplier */
if (!fcu) {
fallback_fac = seq->speed_fader; /* same as speed_factor in rna*/
}
}
if (flags & SEQ_SPEED_INTEGRATE) {
float cursor = 0;
float facf;
v->frameMap[0] = 0;
v->lastValidFrame = 0;
for (cfra = 1; cfra < v->length; cfra++) {
if (fcu) {
facf = evaluate_fcurve(fcu, seq->startdisp + cfra);
}
else {
facf = fallback_fac;
}
facf *= v->globalSpeed;
cursor += facf;
if (cursor >= seq->seq1->len) {
v->frameMap[cfra] = seq->seq1->len - 1;
}
else {
v->frameMap[cfra] = cursor;
v->lastValidFrame = cfra;
}
}
}
else {
float facf;
v->lastValidFrame = 0;
for (cfra = 0; cfra < v->length; cfra++) {
if (fcu) {
facf = evaluate_fcurve(fcu, seq->startdisp + cfra);
}
else {
facf = fallback_fac;
}
if (flags & SEQ_SPEED_COMPRESS_IPO_Y) {
facf *= seq->seq1->len;
}
facf *= v->globalSpeed;
if (facf >= seq->seq1->len) {
facf = seq->seq1->len - 1;
}
else {
v->lastValidFrame = cfra;
}
v->frameMap[cfra] = facf;
}
}
}
static ImBuf *do_speed_effect(
const SeqRenderData *context, Sequence *UNUSED(seq), float UNUSED(cfra),
float facf0, float facf1, ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *ibuf3)
{
ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);
if (out->rect_float) {
do_cross_effect_float(
facf0, facf1, context->rectx, context->recty,
ibuf1->rect_float, ibuf2->rect_float, out->rect_float);
}
else {
do_cross_effect_byte(
facf0, facf1, context->rectx, context->recty,
(unsigned char *) ibuf1->rect, (unsigned char *) ibuf2->rect, (unsigned char *) out->rect);
}
return out;
}
/*********************** overdrop *************************/
static void do_overdrop_effect(
const SeqRenderData *context, Sequence *UNUSED(seq), float UNUSED(cfra), float facf0, float facf1,
ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *UNUSED(ibuf3), int start_line, int total_lines, ImBuf *out)
{
int x = context->rectx;
int y = total_lines;
if (out->rect_float) {
float *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_float_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_drop_effect_float(facf0, facf1, x, y, rect1, rect2, rect_out);
do_alphaover_effect_float(facf0, facf1, x, y, rect1, rect2, rect_out);
}
else {
unsigned char *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_byte_buffers(context, ibuf1, ibuf2, NULL, out, start_line, &rect1, &rect2, NULL, &rect_out);
do_drop_effect_byte(facf0, facf1, x, y, rect1, rect2, rect_out);
do_alphaover_effect_byte(facf0, facf1, x, y, rect1, rect2, rect_out);
}
}
/*********************** Gaussian Blur *************************/
/* NOTE: This gaussian blur implementation accumulates values in the square
* kernel rather that doing X direction and then Y direction because of the
* lack of using multiple-staged filters.
*
* Once we can we'll implement a way to apply filter as multiple stages we
* can optimize hell of a lot in here.
*/
static void init_gaussian_blur_effect(Sequence *seq)
{
if (seq->effectdata)
MEM_freeN(seq->effectdata);
seq->effectdata = MEM_callocN(sizeof(WipeVars), "wipevars");
}
static int num_inputs_gaussian_blur(void)
{
return 1;
}
static void free_gaussian_blur_effect(Sequence *seq)
{
if (seq->effectdata)
MEM_freeN(seq->effectdata);
seq->effectdata = NULL;
}
static void copy_gaussian_blur_effect(Sequence *dst, Sequence *src)
{
dst->effectdata = MEM_dupallocN(src->effectdata);
}
static int early_out_gaussian_blur(Sequence *seq, float UNUSED(facf0), float UNUSED(facf1))
{
GaussianBlurVars *data = seq->effectdata;
if (data->size_x == 0.0f && data->size_y == 0) {
return EARLY_USE_INPUT_1;
}
return EARLY_DO_EFFECT;
}
/* TODO(sergey): De-duplicate with compositor. */
static float *make_gaussian_blur_kernel(float rad, int size)
{
float *gausstab, sum, val;
float fac;
int i, n;
n = 2 * size + 1;
gausstab = (float *)MEM_mallocN(sizeof(float) * n, __func__);
sum = 0.0f;
fac = (rad > 0.0f ? 1.0f / rad : 0.0f);
for (i = -size; i <= size; i++) {
val = RE_filter_value(R_FILTER_GAUSS, (float)i * fac);
sum += val;
gausstab[i + size] = val;
}
sum = 1.0f / sum;
for (i = 0; i < n; i++)
gausstab[i] *= sum;
return gausstab;
}
static void do_gaussian_blur_effect_byte_x(
Sequence *seq,
int start_line,
int x, int y,
int frame_width,
int UNUSED(frame_height),
unsigned char *rect,
unsigned char *out)
{
#define INDEX(_x, _y) (((_y) * (x) + (_x)) * 4)
GaussianBlurVars *data = seq->effectdata;
const int size_x = (int) (data->size_x + 0.5f);
int i, j;
/* Make gaussian weight tabke. */
float *gausstab_x;
gausstab_x = make_gaussian_blur_kernel(data->size_x, size_x);
for (i = 0; i < y; ++i) {
for (j = 0; j < x; ++j) {
int out_index = INDEX(j, i);
float accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float accum_weight = 0.0f;
for (int current_x = j - size_x;
current_x <= j + size_x;
++current_x)
{
if (current_x < 0 || current_x >= frame_width) {
/* Out of bounds. */
continue;
}
int index = INDEX(current_x, i + start_line);
float weight = gausstab_x[current_x - j + size_x];
accum[0] += rect[index] * weight;
accum[1] += rect[index + 1] * weight;
accum[2] += rect[index + 2] * weight;
accum[3] += rect[index + 3] * weight;
accum_weight += weight;
}
float inv_accum_weight = 1.0f / accum_weight;
out[out_index + 0] = accum[0] * inv_accum_weight;
out[out_index + 1] = accum[1] * inv_accum_weight;
out[out_index + 2] = accum[2] * inv_accum_weight;
out[out_index + 3] = accum[3] * inv_accum_weight;
}
}
MEM_freeN(gausstab_x);
#undef INDEX
}
static void do_gaussian_blur_effect_byte_y(
Sequence *seq,
int start_line,
int x, int y,
int UNUSED(frame_width),
int frame_height,
unsigned char *rect,
unsigned char *out)
{
#define INDEX(_x, _y) (((_y) * (x) + (_x)) * 4)
GaussianBlurVars *data = seq->effectdata;
const int size_y = (int) (data->size_y + 0.5f);
int i, j;
/* Make gaussian weight tabke. */
float *gausstab_y;
gausstab_y = make_gaussian_blur_kernel(data->size_y, size_y);
for (i = 0; i < y; ++i) {
for (j = 0; j < x; ++j) {
int out_index = INDEX(j, i);
float accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float accum_weight = 0.0f;
for (int current_y = i - size_y;
current_y <= i + size_y;
++current_y)
{
if (current_y < -start_line ||
current_y + start_line >= frame_height)
{
/* Out of bounds. */
continue;
}
int index = INDEX(j, current_y + start_line);
float weight = gausstab_y[current_y - i + size_y];
accum[0] += rect[index] * weight;
accum[1] += rect[index + 1] * weight;
accum[2] += rect[index + 2] * weight;
accum[3] += rect[index + 3] * weight;
accum_weight += weight;
}
float inv_accum_weight = 1.0f / accum_weight;
out[out_index + 0] = accum[0] * inv_accum_weight;
out[out_index + 1] = accum[1] * inv_accum_weight;
out[out_index + 2] = accum[2] * inv_accum_weight;
out[out_index + 3] = accum[3] * inv_accum_weight;
}
}
MEM_freeN(gausstab_y);
#undef INDEX
}
static void do_gaussian_blur_effect_float_x(
Sequence *seq,
int start_line,
int x, int y,
int frame_width,
int UNUSED(frame_height),
float *rect,
float *out)
{
#define INDEX(_x, _y) (((_y) * (x) + (_x)) * 4)
GaussianBlurVars *data = seq->effectdata;
const int size_x = (int) (data->size_x + 0.5f);
int i, j;
/* Make gaussian weight tabke. */
float *gausstab_x;
gausstab_x = make_gaussian_blur_kernel(data->size_x, size_x);
for (i = 0; i < y; ++i) {
for (j = 0; j < x; ++j) {
int out_index = INDEX(j, i);
float accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float accum_weight = 0.0f;
for (int current_x = j - size_x;
current_x <= j + size_x;
++current_x)
{
if (current_x < 0 || current_x >= frame_width) {
/* Out of bounds. */
continue;
}
int index = INDEX(current_x, i + start_line);
float weight = gausstab_x[current_x - j + size_x];
madd_v4_v4fl(accum, &rect[index], weight);
accum_weight += weight;
}
mul_v4_v4fl(&out[out_index], accum, 1.0f / accum_weight);
}
}
MEM_freeN(gausstab_x);
#undef INDEX
}
static void do_gaussian_blur_effect_float_y(
Sequence *seq,
int start_line,
int x, int y,
int UNUSED(frame_width),
int frame_height,
float *rect,
float *out)
{
#define INDEX(_x, _y) (((_y) * (x) + (_x)) * 4)
GaussianBlurVars *data = seq->effectdata;
const int size_y = (int) (data->size_y + 0.5f);
int i, j;
/* Make gaussian weight tabke. */
float *gausstab_y;
gausstab_y = make_gaussian_blur_kernel(data->size_y, size_y);
for (i = 0; i < y; ++i) {
for (j = 0; j < x; ++j) {
int out_index = INDEX(j, i);
float accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float accum_weight = 0.0f;
for (int current_y = i - size_y;
current_y <= i + size_y;
++current_y)
{
if (current_y < -start_line ||
current_y + start_line >= frame_height)
{
/* Out of bounds. */
continue;
}
int index = INDEX(j, current_y + start_line);
float weight = gausstab_y[current_y - i + size_y];
madd_v4_v4fl(accum, &rect[index], weight);
accum_weight += weight;
}
mul_v4_v4fl(&out[out_index], accum, 1.0f / accum_weight);
}
}
MEM_freeN(gausstab_y);
#undef INDEX
}
static void do_gaussian_blur_effect_x_cb(
const SeqRenderData *context,
Sequence *seq,
ImBuf *ibuf,
int start_line,
int total_lines,
ImBuf *out)
{
if (out->rect_float) {
float *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_float_buffers(
context,
ibuf,
NULL,
NULL,
out,
start_line,
&rect1,
&rect2,
NULL,
&rect_out);
do_gaussian_blur_effect_float_x(
seq,
start_line,
context->rectx,
total_lines,
context->rectx,
context->recty,
ibuf->rect_float,
rect_out);
}
else {
unsigned char *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_byte_buffers(
context,
ibuf,
NULL,
NULL,
out,
start_line,
&rect1,
&rect2,
NULL,
&rect_out);
do_gaussian_blur_effect_byte_x(
seq,
start_line,
context->rectx,
total_lines,
context->rectx,
context->recty,
(unsigned char *) ibuf->rect,
rect_out);
}
}
static void do_gaussian_blur_effect_y_cb(
const SeqRenderData *context,
Sequence *seq,
ImBuf *ibuf,
int start_line,
int total_lines,
ImBuf *out)
{
if (out->rect_float) {
float *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_float_buffers(
context,
ibuf,
NULL,
NULL,
out,
start_line,
&rect1,
&rect2,
NULL,
&rect_out);
do_gaussian_blur_effect_float_y(
seq,
start_line,
context->rectx,
total_lines,
context->rectx,
context->recty,
ibuf->rect_float,
rect_out);
}
else {
unsigned char *rect1 = NULL, *rect2 = NULL, *rect_out = NULL;
slice_get_byte_buffers(
context,
ibuf,
NULL,
NULL,
out,
start_line,
&rect1,
&rect2,
NULL,
&rect_out);
do_gaussian_blur_effect_byte_y(
seq,
start_line,
context->rectx,
total_lines,
context->rectx,
context->recty,
(unsigned char *) ibuf->rect,
rect_out);
}
}
typedef struct RenderGaussianBlurEffectInitData {
const SeqRenderData *context;
Sequence *seq;
ImBuf *ibuf;
ImBuf *out;
} RenderGaussianBlurEffectInitData;
typedef struct RenderGaussianBlurEffectThread {
const SeqRenderData *context;
Sequence *seq;
ImBuf *ibuf;
ImBuf *out;
int start_line, tot_line;
} RenderGaussianBlurEffectThread;
static void render_effect_execute_init_handle(
void *handle_v,
int start_line,
int tot_line,
void *init_data_v)
{
RenderGaussianBlurEffectThread *handle = (RenderGaussianBlurEffectThread *) handle_v;
RenderGaussianBlurEffectInitData *init_data = (RenderGaussianBlurEffectInitData *) init_data_v;
handle->context = init_data->context;
handle->seq = init_data->seq;
handle->ibuf = init_data->ibuf;
handle->out = init_data->out;
handle->start_line = start_line;
handle->tot_line = tot_line;
}
static void *render_effect_execute_do_x_thread(void *thread_data_v)
{
RenderGaussianBlurEffectThread *thread_data = (RenderGaussianBlurEffectThread *) thread_data_v;
do_gaussian_blur_effect_x_cb(
thread_data->context,
thread_data->seq,
thread_data->ibuf,
thread_data->start_line,
thread_data->tot_line,
thread_data->out);
return NULL;
}
static void *render_effect_execute_do_y_thread(void *thread_data_v)
{
RenderGaussianBlurEffectThread *thread_data = (RenderGaussianBlurEffectThread *) thread_data_v;
do_gaussian_blur_effect_y_cb(
thread_data->context,
thread_data->seq,
thread_data->ibuf,
thread_data->start_line,
thread_data->tot_line,
thread_data->out);
return NULL;
}
static ImBuf *do_gaussian_blur_effect(
const SeqRenderData *context,
Sequence *seq,
float UNUSED(cfra),
float UNUSED(facf0),
float UNUSED(facf1),
ImBuf *ibuf1,
ImBuf *UNUSED(ibuf2),
ImBuf *UNUSED(ibuf3))
{
ImBuf *out = prepare_effect_imbufs(context, ibuf1, NULL, NULL);
RenderGaussianBlurEffectInitData init_data;
init_data.context = context;
init_data.seq = seq;
init_data.ibuf = ibuf1;
init_data.out = out;
IMB_processor_apply_threaded(
out->y,
sizeof(RenderGaussianBlurEffectThread),
&init_data,
render_effect_execute_init_handle,
render_effect_execute_do_x_thread);
ibuf1 = out;
init_data.ibuf = ibuf1;
out = prepare_effect_imbufs(context, ibuf1, NULL, NULL);
init_data.out = out;
IMB_processor_apply_threaded(
out->y,
sizeof(RenderGaussianBlurEffectThread),
&init_data,
render_effect_execute_init_handle,
render_effect_execute_do_y_thread);
IMB_freeImBuf(ibuf1);
return out;
}
/*********************** text *************************/
static void init_text_effect(Sequence *seq)
{
TextVars *data;
if (seq->effectdata)
MEM_freeN(seq->effectdata);
data = seq->effectdata = MEM_callocN(sizeof(TextVars), "textvars");
data->text_size = 30;
copy_v4_fl(data->color, 1.0f);
data->shadow_color[3] = 1.0f;
BLI_strncpy(data->text, "Text", sizeof(data->text));
data->loc[0] = 0.5f;
data->align = SEQ_TEXT_ALIGN_X_CENTER;
data->align_y = SEQ_TEXT_ALIGN_Y_BOTTOM;
}
static int num_inputs_text(void)
{
return 0;
}
static int early_out_text(Sequence *seq, float UNUSED(facf0), float UNUSED(facf1))
{
TextVars *data = seq->effectdata;
if (data->text[0] == 0 || data->text_size < 1 ||
((data->color[3] == 0.0f) && (data->shadow_color[3] == 0.0f || (data->flag & SEQ_TEXT_SHADOW) == 0)))
{
return EARLY_USE_INPUT_1;
}
return EARLY_NO_INPUT;
}
static ImBuf *do_text_effect(
const SeqRenderData *context, Sequence *seq, float UNUSED(cfra), float UNUSED(facf0), float UNUSED(facf1),
ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *ibuf3)
{
ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);
TextVars *data = seq->effectdata;
int width = out->x;
int height = out->y;
struct ColorManagedDisplay *display;
const char *display_device;
const int mono = blf_mono_font_render; // XXX
int line_height;
int y_ofs, x, y;
float proxy_size_comp;
display_device = context->scene->display_settings.display_device;
display = IMB_colormanagement_display_get_named(display_device);
/* Compensate text size for preview render size. */
if (ELEM(context->preview_render_size, SEQ_PROXY_RENDER_SIZE_SCENE, SEQ_PROXY_RENDER_SIZE_FULL)) {
proxy_size_comp = context->scene->r.size / 100.0f;
}
else if (context->preview_render_size == SEQ_PROXY_RENDER_SIZE_100) {
proxy_size_comp = 1.0f;
}
else {
proxy_size_comp = context->preview_render_size / 100.0f;
}
/* set before return */
BLF_size(mono, proxy_size_comp * data->text_size, 72);
BLF_enable(mono, BLF_WORD_WRAP);
/* use max width to enable newlines only */
BLF_wordwrap(mono, (data->wrap_width != 0.0f) ? data->wrap_width * width : -1);
BLF_buffer(mono, out->rect_float, (unsigned char *)out->rect, width, height, out->channels, display);
line_height = BLF_height_max(mono);
y_ofs = -BLF_descender(mono);
x = (data->loc[0] * width);
y = (data->loc[1] * height) + y_ofs;
if ((data->align == SEQ_TEXT_ALIGN_X_LEFT) &&
(data->align_y == SEQ_TEXT_ALIGN_Y_TOP))
{
y -= line_height;
}
else {
/* vars for calculating wordwrap */
struct {
struct ResultBLF info;
rctf rect;
} wrap;
BLF_boundbox_ex(mono, data->text, sizeof(data->text), &wrap.rect, &wrap.info);
if (data->align == SEQ_TEXT_ALIGN_X_RIGHT) {
x -= BLI_rctf_size_x(&wrap.rect);
}
else if (data->align == SEQ_TEXT_ALIGN_X_CENTER) {
x -= BLI_rctf_size_x(&wrap.rect) / 2;
}
if (data->align_y == SEQ_TEXT_ALIGN_Y_TOP) {
y -= line_height;
}
else if (data->align_y == SEQ_TEXT_ALIGN_Y_BOTTOM) {
y += (wrap.info.lines - 1) * line_height;
}
else if (data->align_y == SEQ_TEXT_ALIGN_Y_CENTER) {
y += (((wrap.info.lines - 1) / 2) * line_height) - (line_height / 2);
}
}
/* BLF_SHADOW won't work with buffers, instead use cheap shadow trick */
if (data->flag & SEQ_TEXT_SHADOW) {
int fontx, fonty;
fontx = BLF_width_max(mono);
fonty = line_height;
BLF_position(mono, x + max_ii(fontx / 25, 1), y + max_ii(fonty / 25, 1), 0.0f);
BLF_buffer_col(mono, data->shadow_color);
BLF_draw_buffer(mono, data->text, BLF_DRAW_STR_DUMMY_MAX);
}
BLF_position(mono, x, y, 0.0f);
BLF_buffer_col(mono, data->color);
BLF_draw_buffer(mono, data->text, BLF_DRAW_STR_DUMMY_MAX);
BLF_buffer(mono, NULL, NULL, 0, 0, 0, NULL);
BLF_disable(mono, BLF_WORD_WRAP);
return out;
}
/*********************** sequence effect factory *************************/
static void init_noop(Sequence *UNUSED(seq))
{
}
static void load_noop(Sequence *UNUSED(seq))
{
}
static void free_noop(Sequence *UNUSED(seq))
{
}
static int num_inputs_default(void)
{
return 2;
}
static void copy_effect_default(Sequence *dst, Sequence *src)
{
dst->effectdata = MEM_dupallocN(src->effectdata);
}
static void free_effect_default(Sequence *seq)
{
if (seq->effectdata)
MEM_freeN(seq->effectdata);
seq->effectdata = NULL;
}
static int early_out_noop(Sequence *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1))
{
return EARLY_DO_EFFECT;
}
static int early_out_fade(Sequence *UNUSED(seq), float facf0, float facf1)
{
if (facf0 == 0.0f && facf1 == 0.0f) {
return EARLY_USE_INPUT_1;
}
else if (facf0 == 1.0f && facf1 == 1.0f) {
return EARLY_USE_INPUT_2;
}
return EARLY_DO_EFFECT;
}
static int early_out_mul_input2(Sequence *UNUSED(seq), float facf0, float facf1)
{
if (facf0 == 0.0f && facf1 == 0.0f) {
return EARLY_USE_INPUT_1;
}
return EARLY_DO_EFFECT;
}
static void store_icu_yrange_noop(Sequence *UNUSED(seq), short UNUSED(adrcode), float *UNUSED(ymin), float *UNUSED(ymax))
{
/* defaults are fine */
}
static void get_default_fac_noop(Sequence *UNUSED(seq), float UNUSED(cfra), float *facf0, float *facf1)
{
*facf0 = *facf1 = 1.0;
}
static void get_default_fac_fade(Sequence *seq, float cfra, float *facf0, float *facf1)
{
*facf0 = (float)(cfra - seq->startdisp);
*facf1 = (float)(*facf0 + 0.5f);
*facf0 /= seq->len;
*facf1 /= seq->len;
}
static struct ImBuf *init_execution(const SeqRenderData *context, ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *ibuf3)
{
ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);
return out;
}
static struct SeqEffectHandle get_sequence_effect_impl(int seq_type)
{
struct SeqEffectHandle rval;
int sequence_type = seq_type;
rval.multithreaded = false;
rval.supports_mask = false;
rval.init = init_noop;
rval.num_inputs = num_inputs_default;
rval.load = load_noop;
rval.free = free_noop;
rval.early_out = early_out_noop;
rval.get_default_fac = get_default_fac_noop;
rval.store_icu_yrange = store_icu_yrange_noop;
rval.execute = NULL;
rval.init_execution = init_execution;
rval.execute_slice = NULL;
rval.copy = NULL;
switch (sequence_type) {
case SEQ_TYPE_CROSS:
rval.multithreaded = true;
rval.execute_slice = do_cross_effect;
rval.early_out = early_out_fade;
rval.get_default_fac = get_default_fac_fade;
break;
case SEQ_TYPE_GAMCROSS:
rval.multithreaded = true;
rval.init = init_gammacross;
rval.load = load_gammacross;
rval.free = free_gammacross;
rval.early_out = early_out_fade;
rval.get_default_fac = get_default_fac_fade;
rval.init_execution = gammacross_init_execution;
rval.execute_slice = do_gammacross_effect;
break;
case SEQ_TYPE_ADD:
rval.multithreaded = true;
rval.execute_slice = do_add_effect;
rval.early_out = early_out_mul_input2;
break;
case SEQ_TYPE_SUB:
rval.multithreaded = true;
rval.execute_slice = do_sub_effect;
rval.early_out = early_out_mul_input2;
break;
case SEQ_TYPE_MUL:
rval.multithreaded = true;
rval.execute_slice = do_mul_effect;
rval.early_out = early_out_mul_input2;
break;
case SEQ_TYPE_SCREEN:
case SEQ_TYPE_OVERLAY:
case SEQ_TYPE_BURN:
case SEQ_TYPE_LINEAR_BURN:
case SEQ_TYPE_DARKEN:
case SEQ_TYPE_LIGHTEN:
case SEQ_TYPE_DODGE:
case SEQ_TYPE_SOFT_LIGHT:
case SEQ_TYPE_HARD_LIGHT:
case SEQ_TYPE_PIN_LIGHT:
case SEQ_TYPE_LIN_LIGHT:
case SEQ_TYPE_VIVID_LIGHT:
case SEQ_TYPE_BLEND_COLOR:
case SEQ_TYPE_HUE:
case SEQ_TYPE_SATURATION:
case SEQ_TYPE_VALUE:
case SEQ_TYPE_DIFFERENCE:
case SEQ_TYPE_EXCLUSION:
rval.multithreaded = true;
rval.execute_slice = do_blend_mode_effect;
rval.early_out = early_out_mul_input2;
break;
case SEQ_TYPE_COLORMIX:
rval.multithreaded = true;
rval.init = init_colormix_effect;
rval.free = free_effect_default;
rval.copy = copy_effect_default;
rval.execute_slice = do_colormix_effect;
rval.early_out = early_out_mul_input2;
break;
case SEQ_TYPE_ALPHAOVER:
rval.multithreaded = true;
rval.init = init_alpha_over_or_under;
rval.execute_slice = do_alphaover_effect;
break;
case SEQ_TYPE_OVERDROP:
rval.multithreaded = true;
rval.execute_slice = do_overdrop_effect;
break;
case SEQ_TYPE_ALPHAUNDER:
rval.multithreaded = true;
rval.init = init_alpha_over_or_under;
rval.execute_slice = do_alphaunder_effect;
break;
case SEQ_TYPE_WIPE:
rval.init = init_wipe_effect;
rval.num_inputs = num_inputs_wipe;
rval.free = free_wipe_effect;
rval.copy = copy_wipe_effect;
rval.early_out = early_out_fade;
rval.get_default_fac = get_default_fac_fade;
rval.execute = do_wipe_effect;
break;
case SEQ_TYPE_GLOW:
rval.init = init_glow_effect;
rval.num_inputs = num_inputs_glow;
rval.free = free_glow_effect;
rval.copy = copy_glow_effect;
rval.execute = do_glow_effect;
break;
case SEQ_TYPE_TRANSFORM:
rval.init = init_transform_effect;
rval.num_inputs = num_inputs_transform;
rval.free = free_transform_effect;
rval.copy = copy_transform_effect;
rval.execute = do_transform_effect;
break;
case SEQ_TYPE_SPEED:
rval.init = init_speed_effect;
rval.num_inputs = num_inputs_speed;
rval.load = load_speed_effect;
rval.free = free_speed_effect;
rval.copy = copy_speed_effect;
rval.execute = do_speed_effect;
rval.early_out = early_out_speed;
rval.store_icu_yrange = store_icu_yrange_speed;
break;
case SEQ_TYPE_COLOR:
rval.init = init_solid_color;
rval.num_inputs = num_inputs_color;
rval.early_out = early_out_color;
rval.free = free_solid_color;
rval.copy = copy_solid_color;
rval.execute = do_solid_color;
break;
case SEQ_TYPE_MULTICAM:
rval.num_inputs = num_inputs_multicam;
rval.early_out = early_out_multicam;
rval.execute = do_multicam;
break;
case SEQ_TYPE_ADJUSTMENT:
rval.supports_mask = true;
rval.num_inputs = num_inputs_adjustment;
rval.early_out = early_out_adjustment;
rval.execute = do_adjustment;
break;
case SEQ_TYPE_GAUSSIAN_BLUR:
rval.init = init_gaussian_blur_effect;
rval.num_inputs = num_inputs_gaussian_blur;
rval.free = free_gaussian_blur_effect;
rval.copy = copy_gaussian_blur_effect;
rval.early_out = early_out_gaussian_blur;
rval.execute = do_gaussian_blur_effect;
break;
case SEQ_TYPE_TEXT:
rval.num_inputs = num_inputs_text;
rval.init = init_text_effect;
rval.free = free_effect_default;
rval.copy = copy_effect_default;
rval.early_out = early_out_text;
rval.execute = do_text_effect;
break;
}
return rval;
}
struct SeqEffectHandle BKE_sequence_get_effect(Sequence *seq)
{
struct SeqEffectHandle rval = {false, false, NULL};
if (seq->type & SEQ_TYPE_EFFECT) {
rval = get_sequence_effect_impl(seq->type);
if ((seq->flag & SEQ_EFFECT_NOT_LOADED) != 0) {
rval.load(seq);
seq->flag &= ~SEQ_EFFECT_NOT_LOADED;
}
}
return rval;
}
struct SeqEffectHandle BKE_sequence_get_blend(Sequence *seq)
{
struct SeqEffectHandle rval = {false, false, NULL};
if (seq->blend_mode != 0) {
rval = get_sequence_effect_impl(seq->blend_mode);
if ((seq->flag & SEQ_EFFECT_NOT_LOADED) != 0) {
rval.load(seq);
seq->flag &= ~SEQ_EFFECT_NOT_LOADED;
}
}
return rval;
}
int BKE_sequence_effect_get_num_inputs(int seq_type)
{
struct SeqEffectHandle rval = get_sequence_effect_impl(seq_type);
int cnt = rval.num_inputs();
if (rval.execute || (rval.execute_slice && rval.init_execution)) {
return cnt;
}
return 0;
}
int BKE_sequence_effect_get_supports_mask(int seq_type)
{
struct SeqEffectHandle rval = get_sequence_effect_impl(seq_type);
return rval.supports_mask;
}