Vertex paint color operations

D2050 by @metaraptor with edits

Adds levels, brightness/contrast, hsv & invert operations.
This commit is contained in:
Campbell Barton 2016-06-17 01:45:40 +10:00
parent 3f744a16c4
commit 42e2398ae3
Notes: blender-bot 2023-02-14 08:45:09 +01:00
Referenced by issue #47438, Vertex Paint Mode: Basic color operations  (brightness-contrast, levels, HSV...)
4 changed files with 296 additions and 0 deletions

View File

@ -1726,6 +1726,13 @@ class VIEW3D_MT_paint_vertex(Menu):
layout.operator("paint.vertex_color_smooth")
layout.operator("paint.vertex_color_dirt")
layout.separator()
layout.operator("paint.vertex_color_invert", text="Invert")
layout.operator("paint.vertex_color_levels", text="Levels")
layout.operator("paint.vertex_color_hsv", text="Hue Saturation Value")
layout.operator("paint.vertex_color_brightness_contrast", text="Bright/Contrast")
class VIEW3D_MT_hook(Menu):
bl_label = "Hooks"

View File

@ -101,6 +101,10 @@ bool ED_wpaint_fill(struct VPaint *wp, struct Object *ob, float paintweight);
bool ED_vpaint_smooth(struct Object *ob);
typedef void (*VPaintTransform_Callback)(const float col[3], const void *user_data, float r_col[3]);
bool ED_vpaint_color_transform(struct Object *ob, VPaintTransform_Callback vpaint_tx_fn, const void *user_data);
void PAINT_OT_weight_paint_toggle(struct wmOperatorType *ot);
void PAINT_OT_weight_paint(struct wmOperatorType *ot);
void PAINT_OT_weight_set(struct wmOperatorType *ot);

View File

@ -315,6 +315,241 @@ static void PAINT_OT_vertex_color_smooth(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \name Vertex Color Transformations
* \{ */
struct VPaintTx_BrightContrastData {
/* pre-calculated */
float gain;
float offset;
};
static void vpaint_tx_brightness_contrast(const float col[3], const void *user_data, float r_col[3])
{
const struct VPaintTx_BrightContrastData *data = user_data;
for (int i = 0; i < 3; i++) {
r_col[i] = data->gain * col[i] + data->offset;
}
}
static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
float gain, offset;
{
float brightness = RNA_float_get(op->ptr, "brightness");
float contrast = RNA_float_get(op->ptr, "contrast");
brightness /= 100.0f;
float delta = contrast / 200.0f;
gain = 1.0f - delta * 2.0f;
/*
* The algorithm is by Werner D. Streidt
* (http://visca.com/ffactory/archives/5-99/msg00021.html)
* Extracted of OpenCV demhist.c
*/
if (contrast > 0) {
gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
offset = gain * (brightness - delta);
}
else {
delta *= -1;
offset = gain * (brightness + delta);
}
}
const struct VPaintTx_BrightContrastData user_data = {
.gain = gain,
.offset = offset,
};
if (ED_vpaint_color_transform(obact, vpaint_tx_brightness_contrast, &user_data)) {
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
static void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Vertex Paint Bright/Contrast";
ot->idname = "PAINT_OT_vertex_color_brightness_contrast";
ot->description = "Adjust vertex color brightness/contrast";
/* api callbacks */
ot->exec = vertex_color_brightness_contrast_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* params */
const float min = -100, max = +100;
prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max);
prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max);
RNA_def_property_ui_range(prop, min, max, 1, 1);
}
struct VPaintTx_HueSatData {
float hue;
float sat;
float val;
};
static void vpaint_tx_hsv(const float col[3], const void *user_data, float r_col[3])
{
const struct VPaintTx_HueSatData *data = user_data;
float hsv[3];
rgb_to_hsv_v(col, hsv);
hsv[0] += (data->hue - 0.5f);
if (hsv[0] > 1.0f) {
hsv[0] -= 1.0f;
}
else if (hsv[0] < 0.0f) {
hsv[0] += 1.0f;
}
hsv[1] *= data->sat;
hsv[2] *= data->val;
hsv_to_rgb_v(hsv, r_col);
}
static int vertex_color_hsv_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
const struct VPaintTx_HueSatData user_data = {
.hue = RNA_float_get(op->ptr, "h"),
.sat = RNA_float_get(op->ptr, "s"),
.val = RNA_float_get(op->ptr, "v"),
};
if (ED_vpaint_color_transform(obact, vpaint_tx_hsv, &user_data)) {
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
static void PAINT_OT_vertex_color_hsv(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Vertex Paint Hue Saturation Value";
ot->idname = "PAINT_OT_vertex_color_hsv";
ot->description = "Adjust vertex color HSV values";
/* api callbacks */
ot->exec = vertex_color_hsv_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* params */
RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f);
RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f);
RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f);
}
static void vpaint_tx_invert(const float col[3], const void *UNUSED(user_data), float r_col[3])
{
for (int i = 0; i < 3; i++) {
r_col[i] = 1.0f - col[i];
}
}
static int vertex_color_invert_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *obact = CTX_data_active_object(C);
if (ED_vpaint_color_transform(obact, vpaint_tx_invert, NULL)) {
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
static void PAINT_OT_vertex_color_invert(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Vertex Paint Invert";
ot->idname = "PAINT_OT_vertex_color_invert";
ot->description = "Invert RGB values";
/* api callbacks */
ot->exec = vertex_color_invert_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
struct VPaintTx_LevelsData {
float gain;
float offset;
};
static void vpaint_tx_levels(const float col[3], const void *user_data, float r_col[3])
{
const struct VPaintTx_LevelsData *data = user_data;
for (int i = 0; i < 3; i++) {
r_col[i] = data->gain * (col[i] + data->offset);
}
}
static int vertex_color_levels_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
const struct VPaintTx_LevelsData user_data = {
.gain = RNA_float_get(op->ptr, "gain"),
.offset = RNA_float_get(op->ptr, "offset"),
};
if (ED_vpaint_color_transform(obact, vpaint_tx_levels, &user_data)) {
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
static void PAINT_OT_vertex_color_levels(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Vertex Paint Levels";
ot->idname = "PAINT_OT_vertex_color_levels";
ot->description = "Adjust levels of vertex colors";
/* api callbacks */
ot->exec = vertex_color_levels_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* params */
RNA_def_float(ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f);
RNA_def_float(ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f);
}
/** \} */
static int brush_reset_exec(bContext *C, wmOperator *UNUSED(op))
{
Paint *paint = BKE_paint_get_active_from_context(C);
@ -1112,6 +1347,11 @@ void ED_operatortypes_paint(void)
WM_operatortype_append(PAINT_OT_vertex_color_set);
WM_operatortype_append(PAINT_OT_vertex_color_smooth);
WM_operatortype_append(PAINT_OT_vertex_color_brightness_contrast);
WM_operatortype_append(PAINT_OT_vertex_color_hsv);
WM_operatortype_append(PAINT_OT_vertex_color_invert);
WM_operatortype_append(PAINT_OT_vertex_color_levels);
/* face-select */
WM_operatortype_append(PAINT_OT_face_select_linked);
WM_operatortype_append(PAINT_OT_face_select_linked_pick);

View File

@ -502,6 +502,51 @@ bool ED_vpaint_smooth(Object *ob)
return true;
}
/**
* Apply callback to each vertex of the active vertex color layer.
*/
bool ED_vpaint_color_transform(
struct Object *ob,
VPaintTransform_Callback vpaint_tx_fn,
const void *user_data)
{
Mesh *me;
const MPoly *mp;
if (((me = BKE_mesh_from_object(ob)) == NULL) ||
(me->mloopcol == NULL && (make_vertexcol(ob) == false)))
{
return false;
}
const bool do_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
mp = me->mpoly;
for (int i = 0; i < me->totpoly; i++, mp++) {
MLoopCol *lcol = &me->mloopcol[mp->loopstart];
if (do_face_sel && !(mp->flag & ME_FACE_SEL)) {
continue;
}
for (int j = 0; j < mp->totloop; j++, lcol++) {
float col[3];
rgb_uchar_to_float(col, &lcol->r);
vpaint_tx_fn(col, user_data, col);
rgb_float_to_uchar(&lcol->r, col);
}
}
/* remove stale me->mcol, will be added later */
BKE_mesh_tessface_clear(me);
DAG_id_tag_update(&me->id, 0);
return true;
}
/* XXX: should be re-implemented as a vertex/weight paint 'color correct' operator */
#if 0
void vpaint_dogamma(Scene *scene)