Workbench: API Changes
[blender.git] / source / blender / editors / interface / interface_eyedropper_color.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2009 Blender Foundation.
19  * All rights reserved.
20  *
21  * ***** END GPL LICENSE BLOCK *****
22  */
23
24 /** \file blender/editors/interface/interface_eyedropper_color.c
25  *  \ingroup edinterface
26  *
27  * Eyedropper (RGB Color)
28  *
29  * Defines:
30  * - #UI_OT_eyedropper_color
31  */
32
33 #include "MEM_guardedalloc.h"
34
35 #include "DNA_space_types.h"
36 #include "DNA_screen_types.h"
37
38 #include "BLI_math_vector.h"
39
40 #include "BKE_context.h"
41 #include "BKE_screen.h"
42
43 #include "RNA_access.h"
44
45 #include "BIF_gl.h"
46
47 #include "UI_interface.h"
48
49 #include "IMB_colormanagement.h"
50
51 #include "WM_api.h"
52 #include "WM_types.h"
53
54 #include "interface_intern.h"
55
56 #include "ED_image.h"
57 #include "ED_node.h"
58 #include "ED_clip.h"
59
60 #include "interface_eyedropper_intern.h"
61
62 typedef struct Eyedropper {
63         struct ColorManagedDisplay *display;
64
65         PointerRNA ptr;
66         PropertyRNA *prop;
67         int index;
68
69         float init_col[3]; /* for resetting on cancel */
70
71         bool  accum_start; /* has mouse been pressed */
72         float accum_col[3];
73         int   accum_tot;
74 } Eyedropper;
75
76 static bool eyedropper_init(bContext *C, wmOperator *op)
77 {
78         Scene *scene = CTX_data_scene(C);
79         Eyedropper *eye;
80
81         op->customdata = eye = MEM_callocN(sizeof(Eyedropper), "Eyedropper");
82
83         UI_context_active_but_prop_get(C, &eye->ptr, &eye->prop, &eye->index);
84
85         if ((eye->ptr.data == NULL) ||
86             (eye->prop == NULL) ||
87             (RNA_property_editable(&eye->ptr, eye->prop) == false) ||
88             (RNA_property_array_length(&eye->ptr, eye->prop) < 3) ||
89             (RNA_property_type(eye->prop) != PROP_FLOAT))
90         {
91                 return false;
92         }
93
94         if (RNA_property_subtype(eye->prop) != PROP_COLOR) {
95                 const char *display_device;
96                 float col[4];
97
98                 display_device = scene->display_settings.display_device;
99                 eye->display = IMB_colormanagement_display_get_named(display_device);
100
101                 /* store inital color */
102                 RNA_property_float_get_array(&eye->ptr, eye->prop, col);
103                 if (eye->display) {
104                         IMB_colormanagement_display_to_scene_linear_v3(col, eye->display);
105                 }
106                 copy_v3_v3(eye->init_col, col);
107         }
108
109         return true;
110 }
111
112 static void eyedropper_exit(bContext *C, wmOperator *op)
113 {
114         WM_cursor_modal_restore(CTX_wm_window(C));
115
116         if (op->customdata) {
117                 MEM_freeN(op->customdata);
118                 op->customdata = NULL;
119         }
120 }
121
122 /* *** eyedropper_color_ helper functions *** */
123
124 /**
125  * \brief get the color from the screen.
126  *
127  * Special check for image or nodes where we MAY have HDR pixels which don't display.
128  *
129  * \note Exposed by 'interface_eyedropper_intern.h' for use with color band picking.
130  */
131 void eyedropper_color_sample_fl(bContext *C, int mx, int my, float r_col[3])
132 {
133         /* we could use some clever */
134         bScreen *screen = CTX_wm_screen(C);
135         ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, mx, my);
136         const char *display_device = CTX_data_scene(C)->display_settings.display_device;
137         struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
138
139         if (sa) {
140                 if (sa->spacetype == SPACE_IMAGE) {
141                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
142                         if (ar) {
143                                 SpaceImage *sima = sa->spacedata.first;
144                                 int mval[2] = {mx - ar->winrct.xmin,
145                                                my - ar->winrct.ymin};
146
147                                 if (ED_space_image_color_sample(sima, ar, mval, r_col)) {
148                                         return;
149                                 }
150                         }
151                 }
152                 else if (sa->spacetype == SPACE_NODE) {
153                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
154                         if (ar) {
155                                 SpaceNode *snode = sa->spacedata.first;
156                                 int mval[2] = {mx - ar->winrct.xmin,
157                                                my - ar->winrct.ymin};
158
159                                 if (ED_space_node_color_sample(snode, ar, mval, r_col)) {
160                                         return;
161                                 }
162                         }
163                 }
164                 else if (sa->spacetype == SPACE_CLIP) {
165                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
166                         if (ar) {
167                                 SpaceClip *sc = sa->spacedata.first;
168                                 int mval[2] = {mx - ar->winrct.xmin,
169                                                my - ar->winrct.ymin};
170
171                                 if (ED_space_clip_color_sample(sc, ar, mval, r_col)) {
172                                         return;
173                                 }
174                         }
175                 }
176         }
177
178         /* fallback to simple opengl picker */
179         glReadBuffer(GL_FRONT);
180         glReadPixels(mx, my, 1, 1, GL_RGB, GL_FLOAT, r_col);
181         glReadBuffer(GL_BACK);
182
183         IMB_colormanagement_display_to_scene_linear_v3(r_col, display);
184 }
185
186 /* sets the sample color RGB, maintaining A */
187 static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3])
188 {
189         float col_conv[4];
190
191         /* to maintain alpha */
192         RNA_property_float_get_array(&eye->ptr, eye->prop, col_conv);
193
194         /* convert from linear rgb space to display space */
195         if (eye->display) {
196                 copy_v3_v3(col_conv, col);
197                 IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display);
198         }
199         else {
200                 copy_v3_v3(col_conv, col);
201         }
202
203         RNA_property_float_set_array(&eye->ptr, eye->prop, col_conv);
204
205         RNA_property_update(C, &eye->ptr, eye->prop);
206 }
207
208 /* set sample from accumulated values */
209 static void eyedropper_color_set_accum(bContext *C, Eyedropper *eye)
210 {
211         float col[3];
212         mul_v3_v3fl(col, eye->accum_col, 1.0f / (float)eye->accum_tot);
213         eyedropper_color_set(C, eye, col);
214 }
215
216 /* single point sample & set */
217 static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my)
218 {
219         float col[3];
220         eyedropper_color_sample_fl(C, mx, my, col);
221         eyedropper_color_set(C, eye, col);
222 }
223
224 static void eyedropper_color_sample_accum(bContext *C, Eyedropper *eye, int mx, int my)
225 {
226         float col[3];
227         eyedropper_color_sample_fl(C, mx, my, col);
228         /* delay linear conversion */
229         add_v3_v3(eye->accum_col, col);
230         eye->accum_tot++;
231 }
232
233 static void eyedropper_cancel(bContext *C, wmOperator *op)
234 {
235         Eyedropper *eye = op->customdata;
236         eyedropper_color_set(C, eye, eye->init_col);
237         eyedropper_exit(C, op);
238 }
239
240 /* main modal status check */
241 static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
242 {
243         Eyedropper *eye = (Eyedropper *)op->customdata;
244
245         /* handle modal keymap */
246         if (event->type == EVT_MODAL_MAP) {
247                 switch (event->val) {
248                         case EYE_MODAL_CANCEL:
249                                 eyedropper_cancel(C, op);
250                                 return OPERATOR_CANCELLED;
251                         case EYE_MODAL_SAMPLE_CONFIRM:
252                                 if (eye->accum_tot == 0) {
253                                         eyedropper_color_sample(C, eye, event->x, event->y);
254                                 }
255                                 else {
256                                         eyedropper_color_set_accum(C, eye);
257                                 }
258                                 eyedropper_exit(C, op);
259                                 return OPERATOR_FINISHED;
260                         case EYE_MODAL_SAMPLE_BEGIN:
261                                 /* enable accum and make first sample */
262                                 eye->accum_start = true;
263                                 eyedropper_color_sample_accum(C, eye, event->x, event->y);
264                                 break;
265                         case EYE_MODAL_SAMPLE_RESET:
266                                 eye->accum_tot = 0;
267                                 zero_v3(eye->accum_col);
268                                 eyedropper_color_sample_accum(C, eye, event->x, event->y);
269                                 eyedropper_color_set_accum(C, eye);
270                                 break;
271                 }
272         }
273         else if (event->type == MOUSEMOVE) {
274                 if (eye->accum_start) {
275                         /* button is pressed so keep sampling */
276                         eyedropper_color_sample_accum(C, eye, event->x, event->y);
277                         eyedropper_color_set_accum(C, eye);
278                 }
279         }
280
281         return OPERATOR_RUNNING_MODAL;
282 }
283
284 /* Modal Operator init */
285 static int eyedropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
286 {
287         /* init */
288         if (eyedropper_init(C, op)) {
289                 WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
290
291                 /* add temp handler */
292                 WM_event_add_modal_handler(C, op);
293
294                 return OPERATOR_RUNNING_MODAL;
295         }
296         else {
297                 eyedropper_exit(C, op);
298                 return OPERATOR_CANCELLED;
299         }
300 }
301
302 /* Repeat operator */
303 static int eyedropper_exec(bContext *C, wmOperator *op)
304 {
305         /* init */
306         if (eyedropper_init(C, op)) {
307
308                 /* do something */
309
310                 /* cleanup */
311                 eyedropper_exit(C, op);
312
313                 return OPERATOR_FINISHED;
314         }
315         else {
316                 return OPERATOR_CANCELLED;
317         }
318 }
319
320 static int eyedropper_poll(bContext *C)
321 {
322         PointerRNA ptr;
323         PropertyRNA *prop;
324         int index_dummy;
325         uiBut *but;
326
327         /* Only color buttons */
328         if ((CTX_wm_window(C) != NULL) &&
329             (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) &&
330             (but->type == UI_BTYPE_COLOR))
331         {
332                 return 1;
333         }
334
335         return 0;
336 }
337
338 void UI_OT_eyedropper_color(wmOperatorType *ot)
339 {
340         /* identifiers */
341         ot->name = "Eyedropper";
342         ot->idname = "UI_OT_eyedropper_color";
343         ot->description = "Sample a color from the Blender Window to store in a property";
344
345         /* api callbacks */
346         ot->invoke = eyedropper_invoke;
347         ot->modal = eyedropper_modal;
348         ot->cancel = eyedropper_cancel;
349         ot->exec = eyedropper_exec;
350         ot->poll = eyedropper_poll;
351
352         /* flags */
353         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
354
355         /* properties */
356 }