From 3a6a3d0389d6e2cee0f91e721667853ba7fdafc3 Mon Sep 17 00:00:00 2001 From: Peter Schlaile Date: Sat, 2 Feb 2008 23:28:50 +0000 Subject: == Sequencer == New feature: color balance aka 3-way-color-correction aka lift/gamma/gain on input (folded into byte -> float conversion, so _very_ fast in that case). Interface is inspired from Rebel CC (but not as complete yet, you can't choose white and black points right now). Bugfix: clamp color seperated wave form display correctly. --- source/blender/blenloader/intern/readfile.c | 6 + source/blender/blenloader/intern/writefile.c | 3 + source/blender/makesdna/DNA_sequence_types.h | 14 ++ source/blender/src/buttons_scene.c | 101 ++++++++++++--- source/blender/src/editseq.c | 5 + source/blender/src/seqscopes.c | 6 +- source/blender/src/sequence.c | 185 ++++++++++++++++++++++++++- 7 files changed, 293 insertions(+), 27 deletions(-) (limited to 'source') diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index a382fa17fa5..a05683dfd10 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -3468,6 +3468,12 @@ static void direct_link_scene(FileData *fd, Scene *sce) } else { seq->strip->proxy = 0; } + if (seq->flag & SEQ_USE_COLOR_BALANCE) { + seq->strip->color_balance = newdataadr( + fd, seq->strip->color_balance); + } else { + seq->strip->color_balance = 0; + } } } END_SEQ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 8bcc2497dbd..6ff39d471df 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1505,6 +1505,9 @@ static void write_scenes(WriteData *wd, ListBase *scebase) if(seq->flag & SEQ_USE_PROXY && strip->proxy) { writestruct(wd, DATA, "StripProxy", 1, strip->proxy); } + if(seq->flag & SEQ_USE_COLOR_BALANCE && strip->color_balance) { + writestruct(wd, DATA, "StripColorBalance", 1, strip->color_balance); + } if(seq->type==SEQ_IMAGE) writestruct(wd, DATA, "StripElem", strip->len, strip->stripdata); else if(seq->type==SEQ_MOVIE || seq->type==SEQ_RAM_SOUND || seq->type == SEQ_HD_SOUND) diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 6798e726ce8..5e32476b4e0 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -69,6 +69,15 @@ typedef struct StripTransform { int yofs; } StripTransform; +typedef struct StripColorBalance { + float lift[3]; + float gamma[3]; + float gain[3]; + int flag; + float exposure; + float saturation; +} StripColorBalance; + typedef struct StripProxy { char dir[160]; int format; @@ -86,6 +95,7 @@ typedef struct Strip { StripCrop *crop; StripTransform *transform; StripProxy *proxy; + StripColorBalance *color_balance; TStripElem *tstripdata; TStripElem *tstripdata_startstill; TStripElem *tstripdata_endstill; @@ -248,7 +258,11 @@ typedef struct SpeedControlVars { #define SEQ_USE_PROXY 32768 #define SEQ_USE_TRANSFORM 65536 #define SEQ_USE_CROP 131072 +#define SEQ_USE_COLOR_BALANCE 262144 +#define SEQ_COLOR_BALANCE_INVERSE_GAIN 1 +#define SEQ_COLOR_BALANCE_INVERSE_GAMMA 2 +#define SEQ_COLOR_BALANCE_INVERSE_LIFT 4 /* seq->type WATCH IT: SEQ_EFFECT BIT is used to determine if this is an effect strip!!! */ #define SEQ_IMAGE 0 diff --git a/source/blender/src/buttons_scene.c b/source/blender/src/buttons_scene.c index 2470da27996..6eb26e43274 100644 --- a/source/blender/src/buttons_scene.c +++ b/source/blender/src/buttons_scene.c @@ -889,50 +889,113 @@ static void seq_panel_filter_video() uiDefButBitI(block, TOG, SEQ_MAKE_PREMUL, - B_SEQ_BUT_RELOAD, "Convert to Premul", - 10,110,150,19, &last_seq->flag, + B_SEQ_BUT_RELOAD, "Premul", + 10,110,80,19, &last_seq->flag, 0.0, 21.0, 100, 0, "Converts RGB values to become premultiplied with Alpha"); + uiDefButBitI(block, TOG, SEQ_MAKE_FLOAT, + B_SEQ_BUT_RELOAD, "Float", + 90,110,80,19, &last_seq->flag, + 0.0, 21.0, 100, 0, + "Convert input to float data"); + uiDefButBitI(block, TOG, SEQ_FILTERY, B_SEQ_BUT_RELOAD, "FilterY", - 10,90,75,19, &last_seq->flag, + 170,110,80,19, &last_seq->flag, 0.0, 21.0, 100, 0, "For video movies to remove fields"); - uiDefButBitI(block, TOG, SEQ_MAKE_FLOAT, - B_SEQ_BUT_RELOAD, "Make Float", - 85,90,75,19, &last_seq->flag, - 0.0, 21.0, 100, 0, - "Convert input to float data"); - uiDefButBitI(block, TOG, SEQ_FLIPX, B_SEQ_BUT_RELOAD, "FlipX", - 10,70,75,19, &last_seq->flag, + 10,90,80,19, &last_seq->flag, 0.0, 21.0, 100, 0, "Flip on the X axis"); uiDefButBitI(block, TOG, SEQ_FLIPY, B_SEQ_BUT_RELOAD, "FlipY", - 85,70,75,19, &last_seq->flag, + 90,90,80,19, &last_seq->flag, 0.0, 21.0, 100, 0, "Flip on the Y axis"); - - uiDefButF(block, NUM, B_SEQ_BUT_RELOAD, "Mul:", - 10,50,150,19, &last_seq->mul, - 0.001, 5.0, 100, 0, - "Multiply colors"); uiDefButBitI(block, TOG, SEQ_REVERSE_FRAMES, - B_SEQ_BUT_RELOAD, "Reverse Frames", - 10,30,150,19, &last_seq->flag, + B_SEQ_BUT_RELOAD, "Flip Time", + 170,90,80,19, &last_seq->flag, 0.0, 21.0, 100, 0, "Reverse frame order"); + + uiDefButF(block, NUM, B_SEQ_BUT_RELOAD, "Mul:", + 10,70,120,19, &last_seq->mul, + 0.001, 5.0, 0.1, 0, + "Multiply colors"); uiDefButF(block, NUM, B_SEQ_BUT_RELOAD, "Strobe:", - 10,10,150,19, &last_seq->strobe, + 130,70,120,19, &last_seq->strobe, 1.0, 30.0, 100, 0, "Only display every nth frame"); + uiDefButBitI(block, TOG, SEQ_USE_COLOR_BALANCE, + B_SEQ_BUT_RELOAD, "Use Color Balance", + 10,50,240,19, &last_seq->flag, + 0.0, 21.0, 100, 0, + "Activate Color Balance " + "(3-Way color correction) on input"); + + + if (last_seq->flag & SEQ_USE_COLOR_BALANCE) { + if (!last_seq->strip->color_balance) { + int c; + StripColorBalance * cb + = last_seq->strip->color_balance + = MEM_callocN( + sizeof(struct StripColorBalance), + "StripColorBalance"); + for (c = 0; c < 3; c++) { + cb->lift[c] = 1.0; + cb->gamma[c] = 1.0; + cb->gain[c] = 1.0; + } + } + + uiDefBut(block, LABEL, 0, "Lift", + 10,30,80,19, 0, 0, 0, 0, 0, ""); + uiDefBut(block, LABEL, 0, "Gamma", + 90,30,80,19, 0, 0, 0, 0, 0, ""); + uiDefBut(block, LABEL, 0, "Gain", + 170,30,80,19, 0, 0, 0, 0, 0, ""); + + uiDefButF(block, COL, B_SEQ_BUT_RELOAD, "Lift", + 10,10,80,19, last_seq->strip->color_balance->lift, + 0, 0, 0, 0, "Lift (shadows)"); + + uiDefButF(block, COL, B_SEQ_BUT_RELOAD, "Gamma", + 90,10,80,19, last_seq->strip->color_balance->gamma, + 0, 0, 0, 0, "Gamma (midtones)"); + + uiDefButF(block, COL, B_SEQ_BUT_RELOAD, "Gain", + 170,10,80,19, last_seq->strip->color_balance->gain, + 0, 0, 0, 0, "Gain (highlights)"); + + uiDefButBitI(block, TOG, SEQ_COLOR_BALANCE_INVERSE_LIFT, + B_SEQ_BUT_RELOAD, "Inv Lift", + 10,-10,80,19, + &last_seq->strip->color_balance->flag, + 0.0, 21.0, 100, 0, + "Inverse Lift"); + uiDefButBitI(block, TOG, SEQ_COLOR_BALANCE_INVERSE_GAMMA, + B_SEQ_BUT_RELOAD, "Inv Gamma", + 90,-10,80,19, + &last_seq->strip->color_balance->flag, + 0.0, 21.0, 100, 0, + "Inverse Gamma"); + uiDefButBitI(block, TOG, SEQ_COLOR_BALANCE_INVERSE_GAIN, + B_SEQ_BUT_RELOAD, "Inv Gain", + 170,-10,80,19, + &last_seq->strip->color_balance->flag, + 0.0, 21.0, 100, 0, + "Inverse Gain"); + } + + uiBlockEndAlign(block); } diff --git a/source/blender/src/editseq.c b/source/blender/src/editseq.c index e5e5a960f0c..4995e106a30 100644 --- a/source/blender/src/editseq.c +++ b/source/blender/src/editseq.c @@ -2190,6 +2190,11 @@ static Sequence *dupli_seq(Sequence *seq) if (seq->strip->proxy) { seqn->strip->proxy = MEM_dupallocN(seq->strip->proxy); } + + if (seq->strip->color_balance) { + seqn->strip->color_balance + = MEM_dupallocN(seq->strip->color_balance); + } if(seq->type==SEQ_META) { seqn->strip->stripdata = 0; diff --git a/source/blender/src/seqscopes.c b/source/blender/src/seqscopes.c index 84a92982ab5..b7b1d2436f8 100644 --- a/source/blender/src/seqscopes.c +++ b/source/blender/src/seqscopes.c @@ -323,7 +323,11 @@ static struct ImBuf *make_sep_waveform_view_from_ibuf_float( float * rgb = src + 4 * (ibuf->x * y + x); for (c = 0; c < 3; c++) { unsigned char * p = tgt; - p += 4 * (w * ((int) (rgb[c] * (h - 3)) + 1) + float v = rgb[c]; + + CLAMP(v, 0.0, 1.0); + + p += 4 * (w * ((int) (v * (h - 3)) + 1) + c * sw + x/3 + 1); scope_put_pixel_single(wtable, p, c); diff --git a/source/blender/src/sequence.c b/source/blender/src/sequence.c index 63e92feb07e..a570cc66cf0 100644 --- a/source/blender/src/sequence.c +++ b/source/blender/src/sequence.c @@ -127,6 +127,9 @@ void free_strip(Strip *strip) if (strip->proxy) { seq_proxy_free(strip->proxy); } + if (strip->color_balance) { + MEM_freeN(strip->color_balance); + } free_tstripdata(strip->len, strip->tstripdata); free_tstripdata(strip->endstill, strip->tstripdata_endstill); @@ -1010,6 +1013,164 @@ void set_meta_stripdata(Sequence *seqm) } } +static StripColorBalance calc_cb(StripColorBalance * cb_) +{ + StripColorBalance cb = *cb_; + int c; + + if (cb.flag & SEQ_COLOR_BALANCE_INVERSE_LIFT) { + for (c = 0; c < 3; c++) { + cb.lift[c] = 1.0 - cb.lift[c]; + } + } else { + for (c = 0; c < 3; c++) { + cb.lift[c] = -(1.0 - cb.lift[c]); + } + } + if (cb.flag & SEQ_COLOR_BALANCE_INVERSE_GAIN) { + for (c = 0; c < 3; c++) { + if (cb.gain[c] != 0.0) { + cb.gain[c] = 1.0/cb.gain[c]; + } else { + cb.gain[c] = 1000000; /* should be enough :) */ + } + } + } + + if (!(cb.flag & SEQ_COLOR_BALANCE_INVERSE_GAMMA)) { + for (c = 0; c < 3; c++) { + if (cb.gain[c] != 0.0) { + cb.gamma[c] = 1.0/cb.gamma[c]; + } else { + cb.gamma[c] = 1000000; /* should be enough :) */ + } + } + } + + return cb; +} + +static void make_cb_table_byte(float lift, float gain, float gamma, + unsigned char * table, float mul) +{ + int y; + + for (y = 0; y < 256; y++) { + float v = 1.0 * y / 255; + v += lift; + v *= gain; + v = pow(v, gamma); + v *= mul; + if ( v > 1.0) { + v = 1.0; + } else if (v < 0.0) { + v = 0.0; + } + table[y] = v * 255; + } + +} + +static void make_cb_table_float(float lift, float gain, float gamma, + float * table, float mul) +{ + int y; + + for (y = 0; y < 256; y++) { + float v = (float) y * 1.0 / 255.0; + v += lift; + v *= gain; + v = pow(v, gamma); + v *= mul; + table[y] = v; + } +} + +static void color_balance_byte_byte(Sequence * seq, TStripElem* se, + float mul) +{ + unsigned char cb_tab[3][256]; + int c; + unsigned char * p = (unsigned char*) se->ibuf->rect; + unsigned char * e = p + se->ibuf->x * 4 * se->ibuf->y; + + StripColorBalance cb = calc_cb(seq->strip->color_balance); + + for (c = 0; c < 3; c++) { + make_cb_table_byte(cb.lift[c], cb.gain[c], cb.gamma[c], + cb_tab[c], mul); + } + + while (p < e) { + p[0] = cb_tab[0][p[0]]; + p[1] = cb_tab[1][p[1]]; + p[2] = cb_tab[2][p[2]]; + + p += 4; + } +} + +static void color_balance_byte_float(Sequence * seq, TStripElem* se, + float mul) +{ + float cb_tab[4][256]; + int c,i; + unsigned char * p = (unsigned char*) se->ibuf->rect; + unsigned char * e = p + se->ibuf->x * 4 * se->ibuf->y; + float * o; + + imb_addrectfloatImBuf(se->ibuf); + + o = se->ibuf->rect_float; + + StripColorBalance cb = calc_cb(seq->strip->color_balance); + + for (c = 0; c < 3; c++) { + make_cb_table_float(cb.lift[c], cb.gain[c], cb.gamma[c], + cb_tab[c], mul); + } + + for (i = 0; i < 256; i++) { + cb_tab[3][i] = ((float)i)*(1.0f/255.0f); + } + + while (p < e) { + o[0] = cb_tab[0][p[0]]; + o[1] = cb_tab[1][p[1]]; + o[2] = cb_tab[2][p[2]]; + o[3] = cb_tab[3][p[3]]; + + p += 4; o += 4; + } +} + +static void color_balance_float_float(Sequence * seq, TStripElem* se, + float mul) +{ + float * p = se->ibuf->rect_float; + float * e = se->ibuf->rect_float + se->ibuf->x * 4* se->ibuf->y; + StripColorBalance cb = calc_cb(seq->strip->color_balance); + + while (p < e) { + int c; + for (c = 0; c < 3; c++) { + p[c] = pow((p[c] + cb.lift[c]) * cb.gain[c], + cb.gamma[c]) * mul; + } + } +} + +static void color_balance(Sequence * seq, TStripElem* se, float mul) +{ + if (se->ibuf->rect_float) { + color_balance_float_float(seq, se, mul); + } else if(seq->flag & SEQ_MAKE_FLOAT) { + color_balance_byte_float(seq, se, mul); + } else { + color_balance_byte_byte(seq, se, mul); + } +} + /* input preprocessing for SEQ_IMAGE, SEQ_MOVIE and SEQ_SCENE @@ -1022,6 +1183,9 @@ void set_meta_stripdata(Sequence *seqm) - Crop and transform in image source coordinate space - Flip X + Flip Y (could be done afterwards, backward compatibility) - Promote image to float data (affects pipeline operations afterwards) + - Color balance (is most efficient in the byte -> float + (future: half -> float should also work fine!) + case, if done on load, since we can use lookup tables) - Premultiply */ @@ -1092,13 +1256,6 @@ static void input_preprocess(Sequence * seq, TStripElem* se, int cfra) IMB_flipy(se->ibuf); } - if(seq->flag & SEQ_MAKE_FLOAT) { - if (!se->ibuf->rect_float) { - IMB_float_from_rect(se->ibuf); - imb_freerectImBuf(se->ibuf); - } - } - if(seq->mul == 0.0) { seq->mul = 1.0; } @@ -1113,6 +1270,20 @@ static void input_preprocess(Sequence * seq, TStripElem* se, int cfra) mul *= seq->blend_opacity / 100.0; } + if(seq->flag & SEQ_USE_COLOR_BALANCE && seq->strip->color_balance) { + color_balance(seq, se, mul); + mul = 1.0; + } + + if(seq->flag & SEQ_MAKE_FLOAT) { + if (!se->ibuf->rect_float) { + IMB_float_from_rect(se->ibuf); + } + if (se->ibuf->rect) { + imb_freerectImBuf(se->ibuf); + } + } + if(mul != 1.0) { multibuf(se->ibuf, mul); } -- cgit v1.2.3