1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2009 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup edinterface
22 *
23 * Eyedropper (Color Band).
24 *
25 * Operates by either:
26 * - Dragging a straight line, sampling pixels formed by the line to extract a gradient.
27 * - Clicking on points, adding each color to the end of the color-band.
28 *
29 * Defines:
30 * - #UI_OT_eyedropper_colorramp
31 * - #UI_OT_eyedropper_colorramp_point
32 */
33
34 #include "MEM_guardedalloc.h"
35
36 #include "DNA_screen_types.h"
37
38 #include "BLI_bitmap_draw_2d.h"
39 #include "BLI_math_vector.h"
40
41 #include "BKE_colorband.h"
42 #include "BKE_context.h"
43
44 #include "RNA_access.h"
45
46 #include "UI_interface.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50
51 #include "interface_intern.h"
52
53 #include "interface_eyedropper_intern.h"
54
55 typedef struct Colorband_RNAUpdateCb {
56 PointerRNA ptr;
57 PropertyRNA *prop;
58 } Colorband_RNAUpdateCb;
59
60 typedef struct EyedropperColorband {
61 int last_x, last_y;
62 /* Alpha is currently fixed at 1.0, may support in future. */
63 float (*color_buffer)[4];
64 int color_buffer_alloc;
65 int color_buffer_len;
66 bool sample_start;
67 ColorBand init_color_band;
68 ColorBand *color_band;
69 PointerRNA ptr;
70 PropertyRNA *prop;
71 bool is_undo;
72 bool is_set;
73 } EyedropperColorband;
74
75 /* For user-data only. */
76 struct EyedropperColorband_Context {
77 bContext *context;
78 EyedropperColorband *eye;
79 };
80
eyedropper_colorband_init(bContext * C,wmOperator * op)81 static bool eyedropper_colorband_init(bContext *C, wmOperator *op)
82 {
83 ColorBand *band = NULL;
84
85 uiBut *but = UI_context_active_but_get(C);
86
87 PointerRNA rna_update_ptr = PointerRNA_NULL;
88 PropertyRNA *rna_update_prop = NULL;
89 bool is_undo = true;
90
91 if (but == NULL) {
92 /* pass */
93 }
94 else {
95 if (but->type == UI_BTYPE_COLORBAND) {
96 /* When invoked with a hotkey, we can find the band in 'but->poin'. */
97 band = (ColorBand *)but->poin;
98 }
99 else {
100 /* When invoked from a button it's in custom_data field. */
101 band = (ColorBand *)but->custom_data;
102 }
103
104 if (band) {
105 rna_update_ptr = ((Colorband_RNAUpdateCb *)but->func_argN)->ptr;
106 rna_update_prop = ((Colorband_RNAUpdateCb *)but->func_argN)->prop;
107 is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO);
108 }
109 }
110
111 if (!band) {
112 const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp);
113 if (ptr.data != NULL) {
114 band = ptr.data;
115
116 /* Set this to a sub-member of the property to trigger an update. */
117 extern PropertyRNA rna_ColorRamp_color_mode;
118 rna_update_ptr = ptr;
119 rna_update_prop = &rna_ColorRamp_color_mode;
120 is_undo = RNA_struct_undo_check(ptr.type);
121 }
122 }
123
124 if (!band) {
125 return false;
126 }
127
128 EyedropperColorband *eye = MEM_callocN(sizeof(EyedropperColorband), __func__);
129 eye->color_buffer_alloc = 16;
130 eye->color_buffer = MEM_mallocN(sizeof(*eye->color_buffer) * eye->color_buffer_alloc, __func__);
131 eye->color_buffer_len = 0;
132 eye->color_band = band;
133 eye->init_color_band = *eye->color_band;
134 eye->ptr = rna_update_ptr;
135 eye->prop = rna_update_prop;
136 eye->is_undo = is_undo;
137
138 op->customdata = eye;
139
140 return true;
141 }
142
eyedropper_colorband_sample_point(bContext * C,EyedropperColorband * eye,int mx,int my)143 static void eyedropper_colorband_sample_point(bContext *C,
144 EyedropperColorband *eye,
145 int mx,
146 int my)
147 {
148 if (eye->last_x != mx || eye->last_y != my) {
149 float col[4];
150 col[3] = 1.0f; /* TODO: sample alpha */
151 eyedropper_color_sample_fl(C, mx, my, col);
152 if (eye->color_buffer_len + 1 == eye->color_buffer_alloc) {
153 eye->color_buffer_alloc *= 2;
154 eye->color_buffer = MEM_reallocN(eye->color_buffer,
155 sizeof(*eye->color_buffer) * eye->color_buffer_alloc);
156 }
157 copy_v4_v4(eye->color_buffer[eye->color_buffer_len], col);
158 eye->color_buffer_len += 1;
159 eye->last_x = mx;
160 eye->last_y = my;
161 eye->is_set = true;
162 }
163 }
164
eyedropper_colorband_sample_callback(int mx,int my,void * userdata)165 static bool eyedropper_colorband_sample_callback(int mx, int my, void *userdata)
166 {
167 struct EyedropperColorband_Context *data = userdata;
168 bContext *C = data->context;
169 EyedropperColorband *eye = data->eye;
170 eyedropper_colorband_sample_point(C, eye, mx, my);
171 return true;
172 }
173
eyedropper_colorband_sample_segment(bContext * C,EyedropperColorband * eye,int mx,int my)174 static void eyedropper_colorband_sample_segment(bContext *C,
175 EyedropperColorband *eye,
176 int mx,
177 int my)
178 {
179 /* Since the mouse tends to move rather rapidly we use #BLI_bitmap_draw_2d_line_v2v2i
180 * to interpolate between the reported coordinates */
181 struct EyedropperColorband_Context userdata = {C, eye};
182 const int p1[2] = {eye->last_x, eye->last_y};
183 const int p2[2] = {mx, my};
184 BLI_bitmap_draw_2d_line_v2v2i(p1, p2, eyedropper_colorband_sample_callback, &userdata);
185 }
186
eyedropper_colorband_exit(bContext * C,wmOperator * op)187 static void eyedropper_colorband_exit(bContext *C, wmOperator *op)
188 {
189 WM_cursor_modal_restore(CTX_wm_window(C));
190
191 if (op->customdata) {
192 EyedropperColorband *eye = op->customdata;
193 MEM_freeN(eye->color_buffer);
194 MEM_freeN(eye);
195 op->customdata = NULL;
196 }
197 }
198
eyedropper_colorband_apply(bContext * C,wmOperator * op)199 static void eyedropper_colorband_apply(bContext *C, wmOperator *op)
200 {
201 EyedropperColorband *eye = op->customdata;
202 /* Always filter, avoids noise in resulting color-band. */
203 const bool filter_samples = true;
204 BKE_colorband_init_from_table_rgba(
205 eye->color_band, eye->color_buffer, eye->color_buffer_len, filter_samples);
206 eye->is_set = true;
207 if (eye->prop) {
208 RNA_property_update(C, &eye->ptr, eye->prop);
209 }
210 }
211
eyedropper_colorband_cancel(bContext * C,wmOperator * op)212 static void eyedropper_colorband_cancel(bContext *C, wmOperator *op)
213 {
214 EyedropperColorband *eye = op->customdata;
215 if (eye->is_set) {
216 *eye->color_band = eye->init_color_band;
217 if (eye->prop) {
218 RNA_property_update(C, &eye->ptr, eye->prop);
219 }
220 }
221 eyedropper_colorband_exit(C, op);
222 }
223
224 /* main modal status check */
eyedropper_colorband_modal(bContext * C,wmOperator * op,const wmEvent * event)225 static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent *event)
226 {
227 EyedropperColorband *eye = op->customdata;
228 /* handle modal keymap */
229 if (event->type == EVT_MODAL_MAP) {
230 switch (event->val) {
231 case EYE_MODAL_CANCEL:
232 eyedropper_colorband_cancel(C, op);
233 return OPERATOR_CANCELLED;
234 case EYE_MODAL_SAMPLE_CONFIRM: {
235 const bool is_undo = eye->is_undo;
236 eyedropper_colorband_sample_segment(C, eye, event->x, event->y);
237 eyedropper_colorband_apply(C, op);
238 eyedropper_colorband_exit(C, op);
239 /* Could support finished & undo-skip. */
240 return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
241 }
242 case EYE_MODAL_SAMPLE_BEGIN:
243 /* enable accum and make first sample */
244 eye->sample_start = true;
245 eyedropper_colorband_sample_point(C, eye, event->x, event->y);
246 eyedropper_colorband_apply(C, op);
247 eye->last_x = event->x;
248 eye->last_y = event->y;
249 break;
250 case EYE_MODAL_SAMPLE_RESET:
251 break;
252 }
253 }
254 else if (event->type == MOUSEMOVE) {
255 if (eye->sample_start) {
256 eyedropper_colorband_sample_segment(C, eye, event->x, event->y);
257 eyedropper_colorband_apply(C, op);
258 }
259 }
260 return OPERATOR_RUNNING_MODAL;
261 }
262
eyedropper_colorband_point_modal(bContext * C,wmOperator * op,const wmEvent * event)263 static int eyedropper_colorband_point_modal(bContext *C, wmOperator *op, const wmEvent *event)
264 {
265 EyedropperColorband *eye = op->customdata;
266 /* handle modal keymap */
267 if (event->type == EVT_MODAL_MAP) {
268 switch (event->val) {
269 case EYE_MODAL_POINT_CANCEL:
270 eyedropper_colorband_cancel(C, op);
271 return OPERATOR_CANCELLED;
272 case EYE_MODAL_POINT_CONFIRM:
273 eyedropper_colorband_apply(C, op);
274 eyedropper_colorband_exit(C, op);
275 return OPERATOR_FINISHED;
276 case EYE_MODAL_POINT_REMOVE_LAST:
277 if (eye->color_buffer_len > 0) {
278 eye->color_buffer_len -= 1;
279 eyedropper_colorband_apply(C, op);
280 }
281 break;
282 case EYE_MODAL_POINT_SAMPLE:
283 eyedropper_colorband_sample_point(C, eye, event->x, event->y);
284 eyedropper_colorband_apply(C, op);
285 if (eye->color_buffer_len == MAXCOLORBAND) {
286 eyedropper_colorband_exit(C, op);
287 return OPERATOR_FINISHED;
288 }
289 break;
290 case EYE_MODAL_SAMPLE_RESET:
291 *eye->color_band = eye->init_color_band;
292 if (eye->prop) {
293 RNA_property_update(C, &eye->ptr, eye->prop);
294 }
295 eye->color_buffer_len = 0;
296 break;
297 }
298 }
299 return OPERATOR_RUNNING_MODAL;
300 }
301
302 /* Modal Operator init */
eyedropper_colorband_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))303 static int eyedropper_colorband_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
304 {
305 /* init */
306 if (eyedropper_colorband_init(C, op)) {
307 wmWindow *win = CTX_wm_window(C);
308 /* Workaround for de-activating the button clearing the cursor, see T76794 */
309 UI_context_active_but_clear(C, win, CTX_wm_region(C));
310 WM_cursor_modal_set(win, WM_CURSOR_EYEDROPPER);
311
312 /* add temp handler */
313 WM_event_add_modal_handler(C, op);
314
315 return OPERATOR_RUNNING_MODAL;
316 }
317 return OPERATOR_CANCELLED;
318 }
319
320 /* Repeat operator */
eyedropper_colorband_exec(bContext * C,wmOperator * op)321 static int eyedropper_colorband_exec(bContext *C, wmOperator *op)
322 {
323 /* init */
324 if (eyedropper_colorband_init(C, op)) {
325
326 /* do something */
327
328 /* cleanup */
329 eyedropper_colorband_exit(C, op);
330
331 return OPERATOR_FINISHED;
332 }
333 return OPERATOR_CANCELLED;
334 }
335
eyedropper_colorband_poll(bContext * C)336 static bool eyedropper_colorband_poll(bContext *C)
337 {
338 uiBut *but = UI_context_active_but_get(C);
339 if (but && but->type == UI_BTYPE_COLORBAND) {
340 return true;
341 }
342 const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp);
343 if (ptr.data != NULL) {
344 return true;
345 }
346 return false;
347 }
348
UI_OT_eyedropper_colorramp(wmOperatorType * ot)349 void UI_OT_eyedropper_colorramp(wmOperatorType *ot)
350 {
351 /* identifiers */
352 ot->name = "Eyedropper colorband";
353 ot->idname = "UI_OT_eyedropper_colorramp";
354 ot->description = "Sample a color band";
355
356 /* api callbacks */
357 ot->invoke = eyedropper_colorband_invoke;
358 ot->modal = eyedropper_colorband_modal;
359 ot->cancel = eyedropper_colorband_cancel;
360 ot->exec = eyedropper_colorband_exec;
361 ot->poll = eyedropper_colorband_poll;
362
363 /* flags */
364 ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL;
365
366 /* properties */
367 }
368
UI_OT_eyedropper_colorramp_point(wmOperatorType * ot)369 void UI_OT_eyedropper_colorramp_point(wmOperatorType *ot)
370 {
371 /* identifiers */
372 ot->name = "Eyedropper colorband (points)";
373 ot->idname = "UI_OT_eyedropper_colorramp_point";
374 ot->description = "Point-sample a color band";
375
376 /* api callbacks */
377 ot->invoke = eyedropper_colorband_invoke;
378 ot->modal = eyedropper_colorband_point_modal;
379 ot->cancel = eyedropper_colorband_cancel;
380 ot->exec = eyedropper_colorband_exec;
381 ot->poll = eyedropper_colorband_poll;
382
383 /* flags */
384 ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL;
385
386 /* properties */
387 }
388