Add eyedropper to color-ramp widget
authorRay Molenkamp <github@lazydodo.com>
Mon, 11 Dec 2017 23:19:55 +0000 (10:19 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Tue, 12 Dec 2017 02:11:38 +0000 (13:11 +1100)
D2886 by @LazyDodo with edit's by @campbellbarton

The line drawn with the eyedropper is used to fill the color-ramp.

source/blender/blenkernel/BKE_colorband.h
source/blender/blenkernel/intern/colorband.c
source/blender/editors/interface/interface_eyedropper.c
source/blender/editors/interface/interface_intern.h
source/blender/editors/interface/interface_ops.c
source/blender/editors/interface/interface_templates.c

index aa64e79333151c0f1b88020af5b2df3fdefff1a7..6841d94d3604340b8abc34528a680c936a55a678 100644 (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);
index a9f4ae083d0b2ae96dd07b9c9912cbe674db1994..bd02676015e80b6906ebd23aa74e26798510b2f6 100644 (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;
index 564b29d33437d5d5b3bf598c497fa129a78ace31..9cf40c07c0d1b47854de73dc8a69827fff52677e 100644 (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 */
index 638ab01f8ba2e503f2834c4e918ac9f2da15c6f8..6a1a8b5c629a20cc49aac1945bf4c1892970fda4 100644 (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);
index 47b7e9aa085b0d268685b96d9684d84f25f9b06c..9c72942a575848226c6d83340a1c875e2a1fdf24 100644 (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);
 }
index 1aadeb26103dc471a8c2372e0b5a58cdb031a2d6..e113098a7690f8b61c606fae069c23d10729b96a 100644 (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);