Compositor: Film-like curve
Film-like curves for the RGB Curve node (Compositor) and Curve Modifier (Sequencer) Film-like curves originated from Adobe. "It’s an RGB curve where the tone curve is applied on the largest and smallest value, and then the middle value is adapted to keep a constant hue as defined by RGB-HSL/HSV. In terms of look and saturation increase it’s very similar to a pure RGB curve, more so than a HSL-L curve or HSV-V curve, but some color shift problems are avoided." Other tools like Natron, Krita and RawTherapee have implemented this curve tone. Reviewers: brecht, campbellbarton Reviewed By: brecht Tags: #compositing, #video_sequencer Differential Revision: https://developer.blender.org/D3638
This commit is contained in:
@@ -1248,7 +1248,7 @@ class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel):
|
||||
box.prop(mod, "color_multiply")
|
||||
draw_color_balance(box, mod.color_balance)
|
||||
elif mod.type == 'CURVES':
|
||||
box.template_curve_mapping(mod, "curve_mapping", type='COLOR')
|
||||
box.template_curve_mapping(mod, "curve_mapping", type='COLOR', show_tone=True)
|
||||
elif mod.type == 'HUE_CORRECT':
|
||||
box.template_curve_mapping(mod, "curve_mapping", type='HUE')
|
||||
elif mod.type == 'BRIGHT_CONTRAST':
|
||||
|
||||
@@ -938,6 +938,21 @@ void curvemapping_evaluateRGBF(const CurveMapping *cumap, float vecout[3], const
|
||||
vecout[2] = curvemap_evaluateF(&cumap->cm[2], curvemap_evaluateF(&cumap->cm[3], vecin[2]));
|
||||
}
|
||||
|
||||
static void curvemapping_evaluateRGBF_filmlike(const CurveMapping *cumap, float vecout[3], const float vecin[3],
|
||||
const int channel_offset[3])
|
||||
{
|
||||
const float v0in = vecin[channel_offset[0]];
|
||||
const float v1in = vecin[channel_offset[1]];
|
||||
const float v2in = vecin[channel_offset[2]];
|
||||
|
||||
const float v0 = curvemap_evaluateF(&cumap->cm[channel_offset[0]], v0in);
|
||||
const float v2 = curvemap_evaluateF(&cumap->cm[channel_offset[2]], v2in);
|
||||
const float v1 = v2 + ((v0 - v2) * (v1in - v2in) / (v0in - v2in));
|
||||
|
||||
vecout[channel_offset[0]] = v0;
|
||||
vecout[channel_offset[1]] = v1;
|
||||
vecout[channel_offset[2]] = v2;
|
||||
}
|
||||
/** same as #curvemapping_evaluate_premulRGBF
|
||||
* but black/bwmul are passed as args for the compositor
|
||||
* where they can change per pixel.
|
||||
@@ -950,17 +965,70 @@ void curvemapping_evaluateRGBF(const CurveMapping *cumap, float vecout[3], const
|
||||
void curvemapping_evaluate_premulRGBF_ex(const CurveMapping *cumap, float vecout[3], const float vecin[3],
|
||||
const float black[3], const float bwmul[3])
|
||||
{
|
||||
vecout[0] = curvemap_evaluateF(&cumap->cm[0], (vecin[0] - black[0]) * bwmul[0]);
|
||||
vecout[1] = curvemap_evaluateF(&cumap->cm[1], (vecin[1] - black[1]) * bwmul[1]);
|
||||
vecout[2] = curvemap_evaluateF(&cumap->cm[2], (vecin[2] - black[2]) * bwmul[2]);
|
||||
const float r = (vecin[0] - black[0]) * bwmul[0];
|
||||
const float g = (vecin[1] - black[1]) * bwmul[1];
|
||||
const float b = (vecin[2] - black[2]) * bwmul[2];
|
||||
|
||||
switch (cumap->tone)
|
||||
{
|
||||
default:
|
||||
case CURVE_TONE_STANDARD:
|
||||
{
|
||||
vecout[0] = curvemap_evaluateF(&cumap->cm[0], r);
|
||||
vecout[1] = curvemap_evaluateF(&cumap->cm[1], g);
|
||||
vecout[2] = curvemap_evaluateF(&cumap->cm[2], b);
|
||||
break;
|
||||
}
|
||||
case CURVE_TONE_FILMLIKE:
|
||||
{
|
||||
if (r >= g) {
|
||||
if (g > b) {
|
||||
/* Case 1: r >= g > b */
|
||||
const int shuffeled_channels[] = {0, 1, 2};
|
||||
curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
|
||||
}
|
||||
else if (b > r) {
|
||||
/* Case 2: b > r >= g */
|
||||
const int shuffeled_channels[] = {2, 0, 1};
|
||||
curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
|
||||
}
|
||||
else if (b > g) {
|
||||
/* Case 3: r >= b > g */
|
||||
const int shuffeled_channels[] = {0, 2, 1};
|
||||
curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
|
||||
}
|
||||
else {
|
||||
/* Case 4: r >= g == b */
|
||||
copy_v2_fl2(vecout, curvemap_evaluateF(&cumap->cm[0], r), curvemap_evaluateF(&cumap->cm[1], g));
|
||||
vecout[2] = vecout[1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (r >= b) {
|
||||
/* Case 5: g > r >= b */
|
||||
const int shuffeled_channels[] = {1, 0, 2};
|
||||
curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
|
||||
}
|
||||
else if (b > g) {
|
||||
/* Case 6: b > g > r */
|
||||
const int shuffeled_channels[] = {2, 1, 0};
|
||||
curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
|
||||
}
|
||||
else {
|
||||
/* Case 7: g >= b > r */
|
||||
const int shuffeled_channels[] = {1, 2, 0};
|
||||
curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* RGB with black/white points and premult. tables are checked */
|
||||
void curvemapping_evaluate_premulRGBF(const CurveMapping *cumap, float vecout[3], const float vecin[3])
|
||||
{
|
||||
vecout[0] = curvemap_evaluateF(&cumap->cm[0], (vecin[0] - cumap->black[0]) * cumap->bwmul[0]);
|
||||
vecout[1] = curvemap_evaluateF(&cumap->cm[1], (vecin[1] - cumap->black[1]) * cumap->bwmul[1]);
|
||||
vecout[2] = curvemap_evaluateF(&cumap->cm[2], (vecin[2] - cumap->black[2]) * cumap->bwmul[2]);
|
||||
curvemapping_evaluate_premulRGBF_ex(cumap, vecout, vecin, cumap->black, cumap->bwmul);
|
||||
}
|
||||
|
||||
/* same as above, byte version */
|
||||
|
||||
@@ -1074,7 +1074,7 @@ void uiTemplateWaveform(uiLayout *layout, struct PointerRNA *ptr, const char *pr
|
||||
void uiTemplateVectorscope(uiLayout *layout, struct PointerRNA *ptr, const char *propname);
|
||||
void uiTemplateCurveMapping(
|
||||
uiLayout *layout, struct PointerRNA *ptr, const char *propname, int type,
|
||||
bool levels, bool brush, bool neg_slope);
|
||||
bool levels, bool brush, bool neg_slope, bool tone);
|
||||
void uiTemplateColorPicker(uiLayout *layout, struct PointerRNA *ptr, const char *propname, bool value_slider, bool lock, bool lock_luminosity, bool cubic);
|
||||
void uiTemplatePalette(uiLayout *layout, struct PointerRNA *ptr, const char *propname, bool color);
|
||||
void uiTemplateCryptoPicker(uiLayout *layout, struct PointerRNA *ptr, const char *propname);
|
||||
|
||||
@@ -2942,7 +2942,7 @@ static void curvemap_buttons_reset(bContext *C, void *cb_v, void *cumap_v)
|
||||
/* still unsure how this call evolves... we use labeltype for defining what curve-channels to show */
|
||||
static void curvemap_buttons_layout(
|
||||
uiLayout *layout, PointerRNA *ptr, char labeltype, bool levels,
|
||||
bool brush, bool neg_slope, RNAUpdateCb *cb)
|
||||
bool brush, bool neg_slope, bool tone, RNAUpdateCb *cb)
|
||||
{
|
||||
CurveMapping *cumap = ptr->data;
|
||||
CurveMap *cm = &cumap->cm[cumap->cur];
|
||||
@@ -2956,6 +2956,11 @@ static void curvemap_buttons_layout(
|
||||
|
||||
block = uiLayoutGetBlock(layout);
|
||||
|
||||
if (tone) {
|
||||
split = uiLayoutSplit(layout, 0.0f, false);
|
||||
uiItemR(uiLayoutRow(split, false), ptr, "tone", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
|
||||
}
|
||||
|
||||
/* curve chooser */
|
||||
row = uiLayoutRow(layout, false);
|
||||
|
||||
@@ -3107,7 +3112,7 @@ static void curvemap_buttons_layout(
|
||||
|
||||
void uiTemplateCurveMapping(
|
||||
uiLayout *layout, PointerRNA *ptr, const char *propname, int type,
|
||||
bool levels, bool brush, bool neg_slope)
|
||||
bool levels, bool brush, bool neg_slope, bool tone)
|
||||
{
|
||||
RNAUpdateCb *cb;
|
||||
PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
|
||||
@@ -3138,7 +3143,7 @@ void uiTemplateCurveMapping(
|
||||
id = cptr.id.data;
|
||||
UI_block_lock_set(block, (id && ID_IS_LINKED(id)), ERROR_LIBDATA_MESSAGE);
|
||||
|
||||
curvemap_buttons_layout(layout, &cptr, type, levels, brush, neg_slope, cb);
|
||||
curvemap_buttons_layout(layout, &cptr, type, levels, brush, neg_slope, tone, cb);
|
||||
|
||||
UI_block_lock_clear(block);
|
||||
|
||||
@@ -4797,7 +4802,7 @@ void uiTemplateColormanagedViewSettings(uiLayout *layout, bContext *UNUSED(C), P
|
||||
col = uiLayoutColumn(layout, false);
|
||||
uiItemR(col, &view_transform_ptr, "use_curve_mapping", 0, NULL, ICON_NONE);
|
||||
if (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES)
|
||||
uiTemplateCurveMapping(col, &view_transform_ptr, "curve_mapping", 'c', true, false, false);
|
||||
uiTemplateCurveMapping(col, &view_transform_ptr, "curve_mapping", 'c', true, false, false, false);
|
||||
}
|
||||
|
||||
/********************************* Component Menu *************************************/
|
||||
|
||||
@@ -144,7 +144,7 @@ static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *pt
|
||||
}
|
||||
#endif
|
||||
|
||||
uiTemplateCurveMapping(layout, ptr, "curve", 's', false, false, false);
|
||||
uiTemplateCurveMapping(layout, ptr, "curve", 's', false, false, false, false);
|
||||
|
||||
row = uiLayoutRow(layout, true);
|
||||
uiItemR(row, ptr, "frame_start", 0, IFACE_("Sta"), ICON_NONE);
|
||||
@@ -158,7 +158,7 @@ static void node_buts_colorramp(uiLayout *layout, bContext *UNUSED(C), PointerRN
|
||||
|
||||
static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
|
||||
{
|
||||
uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false);
|
||||
uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false);
|
||||
}
|
||||
|
||||
#define SAMPLE_FLT_ISNONE FLT_MAX
|
||||
@@ -186,7 +186,7 @@ static void node_buts_curvecol(uiLayout *layout, bContext *UNUSED(C), PointerRNA
|
||||
cumap->flag &= ~CUMA_DRAW_SAMPLE;
|
||||
}
|
||||
|
||||
uiTemplateCurveMapping(layout, ptr, "mapping", 'c', false, false, false);
|
||||
uiTemplateCurveMapping(layout, ptr, "mapping", 'c', false, false, false, true);
|
||||
}
|
||||
|
||||
static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
|
||||
@@ -1975,7 +1975,7 @@ static void node_composit_buts_huecorrect(uiLayout *layout, bContext *UNUSED(C),
|
||||
cumap->flag &= ~CUMA_DRAW_SAMPLE;
|
||||
}
|
||||
|
||||
uiTemplateCurveMapping(layout, ptr, "mapping", 'h', false, false, false);
|
||||
uiTemplateCurveMapping(layout, ptr, "mapping", 'h', false, false, false, false);
|
||||
}
|
||||
|
||||
static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
|
||||
|
||||
@@ -82,6 +82,9 @@ typedef struct CurveMapping {
|
||||
float bwmul[3]; /* black/white point multiply value, for speed */
|
||||
|
||||
float sample[3]; /* sample values, if flag set it draws line and intersection */
|
||||
|
||||
short tone;
|
||||
short pad[3];
|
||||
} CurveMapping;
|
||||
|
||||
/* cumapping->flag */
|
||||
@@ -102,6 +105,12 @@ typedef enum eCurveMappingPreset {
|
||||
CURVE_PRESET_GAUSS = 7,
|
||||
} eCurveMappingPreset;
|
||||
|
||||
/* CurveMapping->tone */
|
||||
typedef enum eCurveMappingTone {
|
||||
CURVE_TONE_STANDARD = 0,
|
||||
CURVE_TONE_FILMLIKE = 1,
|
||||
} eCurveMappingTone;
|
||||
|
||||
/* histogram->mode */
|
||||
enum {
|
||||
HISTO_MODE_LUMA = 0,
|
||||
|
||||
@@ -114,6 +114,12 @@ static void rna_CurveMapping_white_level_set(PointerRNA *ptr, const float *value
|
||||
curvemapping_set_black_white(cumap, NULL, NULL);
|
||||
}
|
||||
|
||||
static void rna_CurveMapping_tone_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
|
||||
{
|
||||
WM_main_add_notifier(NC_NODE | NA_EDITED, NULL);
|
||||
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL);
|
||||
}
|
||||
|
||||
static void rna_CurveMapping_clipminx_range(PointerRNA *ptr, float *min, float *max,
|
||||
float *UNUSED(softmin), float *UNUSED(softmax))
|
||||
{
|
||||
@@ -757,11 +763,24 @@ static void rna_def_curvemapping(BlenderRNA *brna)
|
||||
PropertyRNA *prop;
|
||||
FunctionRNA *func;
|
||||
|
||||
static const EnumPropertyItem tone_items[] = {
|
||||
{CURVE_TONE_STANDARD, "STANDARD", 0, "Standard", ""},
|
||||
{CURVE_TONE_FILMLIKE, "FILMLIKE", 0, "Film like", ""},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
srna = RNA_def_struct(brna, "CurveMapping", NULL);
|
||||
RNA_def_struct_ui_text(srna, "CurveMapping",
|
||||
"Curve mapping to map color, vector and scalar values to other values using "
|
||||
"a user defined curve");
|
||||
|
||||
prop = RNA_def_property(srna, "tone", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "tone");
|
||||
RNA_def_property_enum_items(prop, tone_items);
|
||||
RNA_def_property_ui_text(prop, "Tone", "Tone of the curve");
|
||||
RNA_def_property_update(prop, 0, "rna_CurveMapping_tone_update");
|
||||
|
||||
|
||||
prop = RNA_def_property(srna, "use_clip", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", CUMA_DO_CLIP);
|
||||
RNA_def_property_ui_text(prop, "Clip", "Force the curve view to fit a defined boundary");
|
||||
|
||||
@@ -883,6 +883,7 @@ void RNA_api_ui_layout(StructRNA *srna)
|
||||
RNA_def_boolean(func, "levels", false, "", "Show black/white levels");
|
||||
RNA_def_boolean(func, "brush", false, "", "Show brush options");
|
||||
RNA_def_boolean(func, "use_negative_slope", false, "", "Use a negative slope by default");
|
||||
RNA_def_boolean(func, "show_tone", false, "", "Show tone options");
|
||||
|
||||
func = RNA_def_function(srna, "template_color_ramp", "uiTemplateColorRamp");
|
||||
RNA_def_function_ui_description(func, "Item. A color ramp widget");
|
||||
|
||||
Reference in New Issue
Block a user