diff options
Diffstat (limited to 'source/blender/editors/interface/eyedroppers/eyedropper_colorband.c')
-rw-r--r-- | source/blender/editors/interface/eyedroppers/eyedropper_colorband.c | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c new file mode 100644 index 00000000000..3f63a8020ed --- /dev/null +++ b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c @@ -0,0 +1,367 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2009 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup edinterface + * + * Eyedropper (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. + * + * Defines: + * - #UI_OT_eyedropper_colorramp + * - #UI_OT_eyedropper_colorramp_point + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_screen_types.h" + +#include "BLI_bitmap_draw_2d.h" +#include "BLI_math_vector.h" + +#include "BKE_colorband.h" +#include "BKE_context.h" + +#include "RNA_access.h" +#include "RNA_prototypes.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "interface_intern.h" + +#include "eyedropper_intern.h" + +typedef struct Colorband_RNAUpdateCb { + PointerRNA ptr; + PropertyRNA *prop; +} Colorband_RNAUpdateCb; + +typedef struct EyedropperColorband { + int event_xy_last[2]; + /* 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; + bool is_undo; + bool is_set; +} EyedropperColorband; + +/* For user-data only. */ +struct EyedropperColorband_Context { + bContext *context; + EyedropperColorband *eye; +}; + +static bool eyedropper_colorband_init(bContext *C, wmOperator *op) +{ + ColorBand *band = NULL; + + uiBut *but = UI_context_active_but_get(C); + + PointerRNA rna_update_ptr = PointerRNA_NULL; + PropertyRNA *rna_update_prop = NULL; + bool is_undo = true; + + 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) { + rna_update_ptr = ((Colorband_RNAUpdateCb *)but->func_argN)->ptr; + rna_update_prop = ((Colorband_RNAUpdateCb *)but->func_argN)->prop; + is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO); + } + } + + if (!band) { + const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); + if (ptr.data != NULL) { + band = ptr.data; + + /* Set this to a sub-member of the property to trigger an update. */ + rna_update_ptr = ptr; + rna_update_prop = &rna_ColorRamp_color_mode; + is_undo = RNA_struct_undo_check(ptr.type); + } + } + + if (!band) { + return false; + } + + EyedropperColorband *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 = rna_update_ptr; + eye->prop = rna_update_prop; + eye->is_undo = is_undo; + + op->customdata = eye; + + return true; +} + +static void eyedropper_colorband_sample_point(bContext *C, + EyedropperColorband *eye, + const int m_xy[2]) +{ + if (eye->event_xy_last[0] != m_xy[0] || eye->event_xy_last[1] != m_xy[1]) { + float col[4]; + col[3] = 1.0f; /* TODO: sample alpha */ + eyedropper_color_sample_fl(C, m_xy, 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; + copy_v2_v2_int(eye->event_xy_last, m_xy); + eye->is_set = true; + } +} + +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; + const int cursor[2] = {mx, my}; + eyedropper_colorband_sample_point(C, eye, cursor); + return true; +} + +static void eyedropper_colorband_sample_segment(bContext *C, + EyedropperColorband *eye, + const int m_xy[2]) +{ + /* 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}; + BLI_bitmap_draw_2d_line_v2v2i( + eye->event_xy_last, m_xy, 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; + /* Always filter, avoids noise in resulting color-band. */ + const bool filter_samples = true; + BKE_colorband_init_from_table_rgba( + eye->color_band, eye->color_buffer, eye->color_buffer_len, filter_samples); + eye->is_set = true; + if (eye->prop) { + RNA_property_update(C, &eye->ptr, eye->prop); + } +} + +static void eyedropper_colorband_cancel(bContext *C, wmOperator *op) +{ + EyedropperColorband *eye = op->customdata; + if (eye->is_set) { + *eye->color_band = eye->init_color_band; + if (eye->prop) { + 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: { + const bool is_undo = eye->is_undo; + eyedropper_colorband_sample_segment(C, eye, event->xy); + eyedropper_colorband_apply(C, op); + eyedropper_colorband_exit(C, op); + /* Could support finished & undo-skip. */ + return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + } + case EYE_MODAL_SAMPLE_BEGIN: + /* enable accum and make first sample */ + eye->sample_start = true; + eyedropper_colorband_sample_point(C, eye, event->xy); + eyedropper_colorband_apply(C, op); + copy_v2_v2_int(eye->event_xy_last, event->xy); + break; + case EYE_MODAL_SAMPLE_RESET: + break; + } + } + else if (event->type == MOUSEMOVE) { + if (eye->sample_start) { + eyedropper_colorband_sample_segment(C, eye, event->xy); + 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->xy); + 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; + if (eye->prop) { + 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)) { + wmWindow *win = CTX_wm_window(C); + /* Workaround for de-activating the button clearing the cursor, see T76794 */ + UI_context_active_but_clear(C, win, CTX_wm_region(C)); + WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER); + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; + } + 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; + } + return OPERATOR_CANCELLED; +} + +static bool eyedropper_colorband_poll(bContext *C) +{ + uiBut *but = UI_context_active_but_get(C); + if (but && but->type == UI_BTYPE_COLORBAND) { + return true; + } + const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); + if (ptr.data != NULL) { + return true; + } + return false; +} + +void UI_OT_eyedropper_colorramp(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Eyedropper Colorband"; + ot->idname = "UI_OT_eyedropper_colorramp"; + 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; + ot->poll = eyedropper_colorband_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; + + /* properties */ +} + +void UI_OT_eyedropper_colorramp_point(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Eyedropper Colorband (Points)"; + ot->idname = "UI_OT_eyedropper_colorramp_point"; + ot->description = "Point-sample 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; + ot->poll = eyedropper_colorband_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL; + + /* properties */ +} |