diff options
author | Sergey Sharybin <sergey.vfx@gmail.com> | 2014-07-19 20:16:10 +0400 |
---|---|---|
committer | Sergey Sharybin <sergey.vfx@gmail.com> | 2014-07-19 20:17:22 +0400 |
commit | 3b2f6dbf9847476f9491218cbed948c1c0aa6c7d (patch) | |
tree | 80b5c2ded3821c54f99d7b0ef26dc76bf21e7306 /source/blender/blenkernel/intern/seqeffects.c | |
parent | 00b29156e087bba3675c7247ffe64a1696a90558 (diff) |
Sequencer: Add gaussian blur effect
Currently 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.
Another thing we can do is to use SSE2 instructions here.
Diffstat (limited to 'source/blender/blenkernel/intern/seqeffects.c')
-rw-r--r-- | source/blender/blenkernel/intern/seqeffects.c | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c index 2b14b92217b..4c02b1b5e8b 100644 --- a/source/blender/blenkernel/intern/seqeffects.c +++ b/source/blender/blenkernel/intern/seqeffects.c @@ -52,6 +52,11 @@ #include "RNA_access.h" +/* TODO(sergey): Could be considered a bad level call, but + * need this for gaussian table. + */ +#include "RE_pipeline.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) @@ -2579,6 +2584,275 @@ static void do_overdrop_effect(const SeqRenderData *context, Sequence *UNUSED(se } } +/*********************** 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 *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1)) +{ + 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(Sequence *seq, + int start_line, + int x, int y, + int 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_x = (int) (data->size_x + 0.5f), + size_y = (int) (data->size_y + 0.5f); + int i, j; + + /* Make gaussian weight tabke. */ + float *gausstab_x, *gausstab_y; + gausstab_x = make_gaussian_blur_kernel(data->size_x, size_x); + if (data->size_x == data->size_y) { + gausstab_y = gausstab_x; + } + else { + 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); + int current_x, current_y; + float accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float accum_weight = 0.0f; + for (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; + } + + for (current_x = j - size_x; + current_x < j + size_x; + ++current_x) + { + float weight; + int index = INDEX(current_x, current_y + start_line); + if (current_x < 0 || current_x >= frame_width) { + /* Out of bounds. */ + continue; + } + BLI_assert(index >= 0); + BLI_assert(index < frame_width * frame_height * 4); + + weight = gausstab_x[current_x - j + size_x] * + 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; + } + } + out[out_index + 0] = accum[0] / accum_weight; + out[out_index + 1] = accum[1] / accum_weight; + out[out_index + 2] = accum[2] / accum_weight; + out[out_index + 3] = accum[3] / accum_weight; + } + } + + MEM_freeN(gausstab_x); + if (gausstab_x != gausstab_y) { + MEM_freeN(gausstab_y); + } +#undef INDEX +} + +static void do_gaussian_blur_effect_float(Sequence *seq, + int start_line, + int x, int y, + int frame_width, int 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), + size_y = (int) (data->size_y + 0.5f); + int i, j; + + /* Make gaussian weight tabke. */ + float *gausstab_x, *gausstab_y; + gausstab_x = make_gaussian_blur_kernel(data->size_x, size_x); + if (data->size_x == data->size_y) { + gausstab_y = gausstab_x; + } + else { + 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); + int current_x, current_y; + float accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float accum_weight = 0.0f; + for (current_y = i - size_y; + current_y < i + size_y; + ++current_y) + { + float weight; + if (current_y < -start_line || + current_y + start_line >= frame_height) + { + /* Out of bounds. */ + continue; + } + + for (current_x = j - size_x; + current_x < j + size_x; + ++current_x) + { + int index = INDEX(current_x, current_y + start_line); + if (current_x < 0 || current_x >= frame_width) { + /* Out of bounds. */ + continue; + } + + weight = gausstab_x[current_x - j + size_x] * + 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_x); + if (gausstab_x != gausstab_y) { + MEM_freeN(gausstab_y); + } +#undef INDEX +} + +static void do_gaussian_blur_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) +{ + 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_gaussian_blur_effect_float(seq, + start_line, + context->rectx, + total_lines, + context->rectx, + context->recty, + ibuf1->rect_float, + 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_gaussian_blur_effect_byte(seq, + start_line, + context->rectx, + total_lines, + context->rectx, + context->recty, + (unsigned char *) ibuf1->rect, + rect_out); + } +} + /*********************** sequence effect factory *************************/ static void init_noop(Sequence *UNUSED(seq)) @@ -2767,6 +3041,15 @@ static struct SeqEffectHandle get_sequence_effect_impl(int seq_type) rval.early_out = early_out_adjustment; rval.execute = do_adjustment; break; + case SEQ_TYPE_GAUSSIAN_BLUR: + rval.multithreaded = true; + 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_slice = do_gaussian_blur_effect; + break; } return rval; |