Add eyedropper to color-ramp widget

D2886 by @LazyDodo with edit's by @campbellbarton

The line drawn with the eyedropper is used to fill the color-ramp.
This commit is contained in:
Ray molenkamp 2017-12-12 10:19:55 +11:00 committed by Campbell Barton
parent 43f33ea300
commit 7ae4c3a019
6 changed files with 406 additions and 3 deletions

View File

@ -36,6 +36,8 @@ struct ColorBand;
#define MAXCOLORBAND 32
void BKE_colorband_init(struct ColorBand *coba, bool rangetype);
void BKE_colorband_init_from_table_rgba(
struct ColorBand *coba, const float (*array)[4], const int array_len);
struct ColorBand *BKE_colorband_add(bool rangetype);
bool BKE_colorband_evaluate(const struct ColorBand *coba, float in, float out[4]);
void BKE_colorband_evaluate_table_rgba(const struct ColorBand *coba, float **array, int *size);

View File

@ -80,6 +80,71 @@ void BKE_colorband_init(ColorBand *coba, bool rangetype)
coba->color_mode = COLBAND_BLEND_RGB;
}
static void colorband_init_from_table_rgba_simple(
ColorBand *coba,
const float (*array)[4], const int array_len)
{
/* No Re-sample, just de-duplicate. */
const float eps = (1.0f / 255.0f) + 1e-6f;
BLI_assert(array_len < MAXCOLORBAND);
int stops = min_ii(MAXCOLORBAND, array_len);
if (stops) {
const float step_size = 1.0f / (float)max_ii(stops - 1, 1);
int i_curr = -1;
for (int i_step = 0; i_step < stops; i_step++) {
if ((i_curr != -1) && compare_v4v4(&coba->data[i_curr].r, array[i_step], eps)) {
continue;
}
i_curr += 1;
copy_v4_v4(&coba->data[i_curr].r, array[i_step]);
coba->data[i_curr].pos = i_step * step_size;
coba->data[i_curr].cur = i_curr;
}
coba->tot = i_curr + 1;
coba->cur = 0;
}
else {
/* coba is empty, set 1 black stop */
zero_v3(&coba->data[0].r);
coba->data[0].a = 1.0f;
coba->cur = 0;
coba->tot = 1;
}
}
static void colorband_init_from_table_rgba_resample(
ColorBand *coba,
const float (*array)[4], const int array_len)
{
/* TODO: more optimal method of color simplification,
* for now just pick evenly spaced colors. */
BLI_assert(array_len >= MAXCOLORBAND);
float step = array_len / (float)MAXCOLORBAND;
float color_step = 1.0f / (MAXCOLORBAND - 1);
for (int i = 0; i < MAXCOLORBAND; i++) {
int cur_color = (int)(step * i);
copy_v4_v4(&coba->data[i].r, array[cur_color]);
coba->data[i].pos = i * color_step;
coba->data[i].cur = i;
}
coba->tot = MAXCOLORBAND;
coba->cur = 0;
}
void BKE_colorband_init_from_table_rgba(
ColorBand *coba,
const float (*array)[4], const int array_len)
{
if (array_len < MAXCOLORBAND) {
/* No Re-sample, just de-duplicate. */
colorband_init_from_table_rgba_simple(coba, array, array_len);
}
else {
/* Re-sample */
colorband_init_from_table_rgba_resample(coba, array, array_len);
}
}
ColorBand *BKE_colorband_add(bool rangetype)
{
ColorBand *coba;

View File

@ -74,6 +74,9 @@
/* for Driver eyedropper */
#include "ED_keyframing.h"
/* for colorband eyedropper*/
#include "BLI_bitmap_draw_2d.h"
#include "BKE_colorband.h"
/* -------------------------------------------------------------------- */
/* Keymap
@ -88,6 +91,16 @@ enum {
EYE_MODAL_SAMPLE_RESET,
};
/* Color-band point sample. */
enum {
EYE_MODAL_POINT_CANCEL = 1,
EYE_MODAL_POINT_SAMPLE,
EYE_MODAL_POINT_CONFIRM,
EYE_MODAL_POINT_RESET,
EYE_MODAL_POINT_REMOVE_LAST,
};
wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf)
{
static const EnumPropertyItem modal_items[] = {
@ -116,6 +129,7 @@ wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf)
WM_modalkeymap_add_item(keymap, SPACEKEY, KM_RELEASE, KM_ANY, 0, EYE_MODAL_SAMPLE_RESET);
/* assign to operators */
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_colorband");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_color");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_id");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_depth");
@ -124,6 +138,37 @@ wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf)
return keymap;
}
wmKeyMap *eyedropper_colorband_modal_keymap(wmKeyConfig *keyconf)
{
static const EnumPropertyItem modal_items_point[] = {
{EYE_MODAL_POINT_CANCEL, "CANCEL", 0, "Cancel", ""},
{EYE_MODAL_POINT_SAMPLE, "SAMPLE_SAMPLE", 0, "Sample a point", ""},
{EYE_MODAL_POINT_CONFIRM, "SAMPLE_CONFIRM", 0, "Confirm Sampling", ""},
{EYE_MODAL_POINT_RESET, "SAMPLE_RESET", 0, "Reset Sampling", ""},
{0, NULL, 0, NULL, NULL}
};
wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Eyedropper ColorBand PointSampling Map");
if (keymap && keymap->modal_items)
return keymap;
keymap = WM_modalkeymap_add(keyconf, "Eyedropper ColorBand PointSampling Map", modal_items_point);
/* items for modal map */
WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, EYE_MODAL_CANCEL);
WM_modalkeymap_add_item(keymap, BACKSPACEKEY, KM_PRESS, KM_ANY, 0, EYE_MODAL_POINT_REMOVE_LAST);
WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, EYE_MODAL_POINT_CONFIRM);
WM_modalkeymap_add_item(keymap, RETKEY, KM_RELEASE, KM_ANY, 0, EYE_MODAL_POINT_CONFIRM);
WM_modalkeymap_add_item(keymap, PADENTER, KM_RELEASE, KM_ANY, 0, EYE_MODAL_POINT_CONFIRM);
WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, EYE_MODAL_POINT_SAMPLE);
WM_modalkeymap_add_item(keymap, SPACEKEY, KM_RELEASE, KM_ANY, 0, EYE_MODAL_POINT_RESET);
/* assign to operators */
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_colorband_point");
return keymap;
}
/** \} */
@ -260,7 +305,7 @@ static void eyedropper_exit(bContext *C, wmOperator *op)
*
* Special check for image or nodes where we MAY have HDR pixels which don't display.
*/
static void eyedropper_color_sample_fl(bContext *C, Eyedropper *UNUSED(eye), int mx, int my, float r_col[3])
static void eyedropper_color_sample_fl(bContext *C, int mx, int my, float r_col[3])
{
/* we could use some clever */
@ -350,14 +395,14 @@ static void eyedropper_color_set_accum(bContext *C, Eyedropper *eye)
static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my)
{
float col[3];
eyedropper_color_sample_fl(C, eye, mx, my, col);
eyedropper_color_sample_fl(C, mx, my, col);
eyedropper_color_set(C, eye, col);
}
static void eyedropper_color_sample_accum(bContext *C, Eyedropper *eye, int mx, int my)
{
float col[3];
eyedropper_color_sample_fl(C, eye, mx, my, col);
eyedropper_color_sample_fl(C, mx, my, col);
/* delay linear conversion */
add_v3_v3(eye->accum_col, col);
eye->accum_tot++;
@ -490,6 +535,284 @@ void UI_OT_eyedropper_color(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Eyedropper (RGB Color Band)
*
* Operates by either:
* - Dragging a straight line, sampling pixels formed by the line to extract a gradient.
* - Clicking on points, adding each color to the end of the color-band.
* \{ */
typedef struct Colorband_RNAUpdateCb {
PointerRNA ptr;
PropertyRNA *prop;
} Colorband_RNAUpdateCb;
typedef struct EyedropperColorband {
int last_x, last_y;
/* Alpha is currently fixed at 1.0, may support in future. */
float (*color_buffer)[4];
int color_buffer_alloc;
int color_buffer_len;
bool sample_start;
ColorBand init_color_band;
ColorBand *color_band;
PointerRNA ptr;
PropertyRNA *prop;
} EyedropperColorband;
/* For user-data only. */
struct EyedropperColorband_Context {
bContext *context;
EyedropperColorband *eye;
};
static bool eyedropper_colorband_init(bContext *C, wmOperator *op)
{
ColorBand *band = NULL;
EyedropperColorband *eye;
uiBut *but = UI_context_active_but_get(C);
if (but == NULL) {
/* pass */
}
else if (but->type == UI_BTYPE_COLORBAND) {
/* When invoked with a hotkey, we can find the band in 'but->poin'. */
band = (ColorBand *)but->poin;
}
else {
/* When invoked from a button it's in custom_data field. */
band = (ColorBand *)but->custom_data;
}
if (!band)
return false;
op->customdata = eye = MEM_callocN(sizeof(EyedropperColorband), __func__);
eye->color_buffer_alloc = 16;
eye->color_buffer = MEM_mallocN(sizeof(*eye->color_buffer) * eye->color_buffer_alloc, __func__);
eye->color_buffer_len = 0;
eye->color_band = band;
eye->init_color_band = *eye->color_band;
eye->ptr = ((Colorband_RNAUpdateCb *)but->func_argN)->ptr;
eye->prop = ((Colorband_RNAUpdateCb *)but->func_argN)->prop;
return true;
}
static void eyedropper_colorband_sample_point(bContext *C, EyedropperColorband *eye, int mx, int my)
{
if (eye->last_x != mx || eye->last_y != my) {
float col[4];
col[3] = 1.0f; /* TODO: sample alpha */
eyedropper_color_sample_fl(C, mx, my, col);
if (eye->color_buffer_len + 1 == eye->color_buffer_alloc) {
eye->color_buffer_alloc *= 2;
eye->color_buffer = MEM_reallocN(eye->color_buffer, sizeof(*eye->color_buffer) * eye->color_buffer_alloc);
}
copy_v4_v4(eye->color_buffer[eye->color_buffer_len], col);
eye->color_buffer_len += 1;
eye->last_x = mx;
eye->last_y = my;
}
}
static bool eyedropper_colorband_sample_callback(int mx, int my, void *userdata)
{
struct EyedropperColorband_Context *data = userdata;
bContext *C = data->context;
EyedropperColorband *eye = data->eye;
eyedropper_colorband_sample_point(C, eye, mx, my);
return true;
}
static void eyedropper_colorband_sample_segment(bContext *C, EyedropperColorband *eye, int mx, int my)
{
/* Since the mouse tends to move rather rapidly we use #BLI_bitmap_draw_2d_line_v2v2i
* to interpolate between the reported coordinates */
struct EyedropperColorband_Context userdata = {C, eye};
int p1[2] = {eye->last_x, eye->last_y};
int p2[2] = {mx, my};
BLI_bitmap_draw_2d_line_v2v2i(p1, p2, eyedropper_colorband_sample_callback, &userdata);
}
static void eyedropper_colorband_exit(bContext *C, wmOperator *op)
{
WM_cursor_modal_restore(CTX_wm_window(C));
if (op->customdata) {
EyedropperColorband *eye = op->customdata;
MEM_freeN(eye->color_buffer);
MEM_freeN(eye);
op->customdata = NULL;
}
}
static void eyedropper_colorband_apply(bContext *C, wmOperator *op)
{
EyedropperColorband *eye = op->customdata;
BKE_colorband_init_from_table_rgba(eye->color_band, eye->color_buffer, eye->color_buffer_len);
RNA_property_update(C, &eye->ptr, eye->prop);
}
static void eyedropper_colorband_cancel(bContext *C, wmOperator *op)
{
EyedropperColorband *eye = op->customdata;
*eye->color_band = eye->init_color_band;
RNA_property_update(C, &eye->ptr, eye->prop);
eyedropper_colorband_exit(C, op);
}
/* main modal status check */
static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
EyedropperColorband *eye = op->customdata;
/* handle modal keymap */
if (event->type == EVT_MODAL_MAP) {
switch (event->val) {
case EYE_MODAL_CANCEL:
eyedropper_colorband_cancel(C, op);
return OPERATOR_CANCELLED;
case EYE_MODAL_SAMPLE_CONFIRM:
eyedropper_colorband_sample_segment(C, eye, event->x, event->y);
eyedropper_colorband_apply(C, op);
eyedropper_colorband_exit(C, op);
return OPERATOR_FINISHED;
case EYE_MODAL_SAMPLE_BEGIN:
/* enable accum and make first sample */
eye->sample_start = true;
eyedropper_colorband_sample_point(C, eye, event->x, event->y);
eyedropper_colorband_apply(C, op);
eye->last_x = event->x;
eye->last_y = event->y;
break;
case EYE_MODAL_SAMPLE_RESET:
break;
}
}
else if (event->type == MOUSEMOVE) {
if (eye->sample_start) {
eyedropper_colorband_sample_segment(C, eye, event->x, event->y);
eyedropper_colorband_apply(C, op);
}
}
return OPERATOR_RUNNING_MODAL;
}
static int eyedropper_colorband_point_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
EyedropperColorband *eye = op->customdata;
/* handle modal keymap */
if (event->type == EVT_MODAL_MAP) {
switch (event->val) {
case EYE_MODAL_POINT_CANCEL:
eyedropper_colorband_cancel(C, op);
return OPERATOR_CANCELLED;
case EYE_MODAL_POINT_CONFIRM:
eyedropper_colorband_apply(C, op);
eyedropper_colorband_exit(C, op);
return OPERATOR_FINISHED;
case EYE_MODAL_POINT_REMOVE_LAST:
if (eye->color_buffer_len > 0) {
eye->color_buffer_len -= 1;
eyedropper_colorband_apply(C, op);
}
break;
case EYE_MODAL_POINT_SAMPLE:
eyedropper_colorband_sample_point(C, eye, event->x, event->y);
eyedropper_colorband_apply(C, op);
if (eye->color_buffer_len == MAXCOLORBAND ) {
eyedropper_colorband_exit(C, op);
return OPERATOR_FINISHED;
}
break;
case EYE_MODAL_SAMPLE_RESET:
*eye->color_band = eye->init_color_band;
RNA_property_update(C, &eye->ptr, eye->prop);
eye->color_buffer_len = 0;
break;
}
}
return OPERATOR_RUNNING_MODAL;
}
/* Modal Operator init */
static int eyedropper_colorband_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
/* init */
if (eyedropper_colorband_init(C, op)) {
WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
/* add temp handler */
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
else {
eyedropper_colorband_exit(C, op);
return OPERATOR_CANCELLED;
}
}
/* Repeat operator */
static int eyedropper_colorband_exec(bContext *C, wmOperator *op)
{
/* init */
if (eyedropper_colorband_init(C, op)) {
/* do something */
/* cleanup */
eyedropper_colorband_exit(C, op);
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
void UI_OT_eyedropper_colorband(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Eyedropper colorband";
ot->idname = "UI_OT_eyedropper_colorband";
ot->description = "Sample a color band";
/* api callbacks */
ot->invoke = eyedropper_colorband_invoke;
ot->modal = eyedropper_colorband_modal;
ot->cancel = eyedropper_colorband_cancel;
ot->exec = eyedropper_colorband_exec;
/* flags */
ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
/* properties */
}
void UI_OT_eyedropper_colorband_point(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Eyedropper colorband (points)";
ot->idname = "UI_OT_eyedropper_colorband_point";
ot->description = "Pointsample a color band";
/* api callbacks */
ot->invoke = eyedropper_colorband_invoke;
ot->modal = eyedropper_colorband_point_modal;
ot->cancel = eyedropper_colorband_cancel;
ot->exec = eyedropper_colorband_exec;
/* flags */
ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
/* properties */
}
/** \} */
/* -------------------------------------------------------------------- */
/* Data Dropper */

View File

@ -753,7 +753,10 @@ void ui_but_anim_autokey(struct bContext *C, uiBut *but, struct Scene *scene, fl
/* interface_eyedropper.c */
struct wmKeyMap *eyedropper_modal_keymap(struct wmKeyConfig *keyconf);
struct wmKeyMap *eyedropper_colorband_modal_keymap(struct wmKeyConfig *keyconf);
void UI_OT_eyedropper_color(struct wmOperatorType *ot);
void UI_OT_eyedropper_colorband(struct wmOperatorType *ot);
void UI_OT_eyedropper_colorband_point(struct wmOperatorType *ot);
void UI_OT_eyedropper_id(struct wmOperatorType *ot);
void UI_OT_eyedropper_depth(struct wmOperatorType *ot);
void UI_OT_eyedropper_driver(struct wmOperatorType *ot);

View File

@ -1128,6 +1128,8 @@ void ED_operatortypes_ui(void)
/* external */
WM_operatortype_append(UI_OT_eyedropper_color);
WM_operatortype_append(UI_OT_eyedropper_colorband);
WM_operatortype_append(UI_OT_eyedropper_colorband_point);
WM_operatortype_append(UI_OT_eyedropper_id);
WM_operatortype_append(UI_OT_eyedropper_depth);
WM_operatortype_append(UI_OT_eyedropper_driver);
@ -1144,6 +1146,8 @@ void ED_keymap_ui(wmKeyConfig *keyconf)
/* eyedroppers - notice they all have the same shortcut, but pass the event
* through until a suitable eyedropper for the active button is found */
WM_keymap_add_item(keymap, "UI_OT_eyedropper_color", EKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "UI_OT_eyedropper_colorband", EKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "UI_OT_eyedropper_colorband_point", EKEY, KM_PRESS , KM_ALT, 0);
WM_keymap_add_item(keymap, "UI_OT_eyedropper_id", EKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "UI_OT_eyedropper_depth", EKEY, KM_PRESS, 0, 0);
@ -1166,4 +1170,5 @@ void ED_keymap_ui(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "ANIM_OT_keyingset_button_remove", KKEY, KM_PRESS, KM_ALT, 0);
eyedropper_modal_keymap(keyconf);
eyedropper_colorband_modal_keymap(keyconf);
}

View File

@ -1532,6 +1532,11 @@ static void colorband_buttons_layout(
bt = uiDefIconTextBut(block, UI_BTYPE_BUT, 0, ICON_ARROW_LEFTRIGHT, "", xs + 4.0f * unit, ys + UI_UNIT_Y, 2.0f * unit, UI_UNIT_Y,
NULL, 0, 0, 0, 0, TIP_("Flip the color ramp"));
UI_but_funcN_set(bt, colorband_flip_cb, MEM_dupallocN(cb), coba);
bt = uiDefIconButO(block, UI_BTYPE_BUT, "UI_OT_eyedropper_colorband", WM_OP_INVOKE_DEFAULT, ICON_EYEDROPPER, xs + 6.0f * unit, ys + UI_UNIT_Y, UI_UNIT_X, UI_UNIT_Y, NULL);
bt->custom_data = coba;
bt->func_argN = MEM_dupallocN(cb);
UI_block_align_end(block);
UI_block_emboss_set(block, UI_EMBOSS);