doxygen: add newline after \file
[blender.git] / source / blender / editors / interface / interface_eyedropper_color.c
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 (RGB Color)
24  *
25  * Defines:
26  * - #UI_OT_eyedropper_color
27  */
28
29 #include "MEM_guardedalloc.h"
30
31 #include "DNA_space_types.h"
32 #include "DNA_screen_types.h"
33
34 #include "BLI_math_vector.h"
35
36 #include "BKE_context.h"
37 #include "BKE_main.h"
38 #include "BKE_screen.h"
39
40 #include "RNA_access.h"
41
42 #include "BIF_gl.h"
43
44 #include "UI_interface.h"
45
46 #include "IMB_colormanagement.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50
51 #include "RNA_define.h"
52
53 #include "interface_intern.h"
54
55 #include "ED_image.h"
56 #include "ED_node.h"
57 #include "ED_clip.h"
58
59 #include "interface_eyedropper_intern.h"
60
61 typedef struct Eyedropper {
62         struct ColorManagedDisplay *display;
63
64         PointerRNA ptr;
65         PropertyRNA *prop;
66         int index;
67         bool is_undo;
68
69         bool is_set;
70         float init_col[3]; /* for resetting on cancel */
71
72         bool  accum_start; /* has mouse been pressed */
73         float accum_col[3];
74         int   accum_tot;
75
76         bool use_accum;
77 } Eyedropper;
78
79 static bool eyedropper_init(bContext *C, wmOperator *op)
80 {
81         Eyedropper *eye = MEM_callocN(sizeof(Eyedropper), __func__);
82         eye->use_accum = RNA_boolean_get(op->ptr, "use_accumulate");
83
84         uiBut *but = UI_context_active_but_prop_get(C, &eye->ptr, &eye->prop, &eye->index);
85
86         if ((eye->ptr.data == NULL) ||
87             (eye->prop == NULL) ||
88             (RNA_property_editable(&eye->ptr, eye->prop) == false) ||
89             (RNA_property_array_length(&eye->ptr, eye->prop) < 3) ||
90             (RNA_property_type(eye->prop) != PROP_FLOAT))
91         {
92                 MEM_freeN(eye);
93                 return false;
94         }
95         op->customdata = eye;
96
97         eye->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO);
98
99         float col[4];
100         RNA_property_float_get_array(&eye->ptr, eye->prop, col);
101         if (RNA_property_subtype(eye->prop) != PROP_COLOR) {
102                 Scene *scene = CTX_data_scene(C);
103                 const char *display_device;
104
105                 display_device = scene->display_settings.display_device;
106                 eye->display = IMB_colormanagement_display_get_named(display_device);
107
108                 /* store initial color */
109                 if (eye->display) {
110                         IMB_colormanagement_display_to_scene_linear_v3(col, eye->display);
111                 }
112         }
113         copy_v3_v3(eye->init_col, col);
114
115         return true;
116 }
117
118 static void eyedropper_exit(bContext *C, wmOperator *op)
119 {
120         WM_cursor_modal_restore(CTX_wm_window(C));
121
122         if (op->customdata) {
123                 MEM_freeN(op->customdata);
124                 op->customdata = NULL;
125         }
126 }
127
128 /* *** eyedropper_color_ helper functions *** */
129
130 /**
131  * \brief get the color from the screen.
132  *
133  * Special check for image or nodes where we MAY have HDR pixels which don't display.
134  *
135  * \note Exposed by 'interface_eyedropper_intern.h' for use with color band picking.
136  */
137 void eyedropper_color_sample_fl(bContext *C, int mx, int my, float r_col[3])
138 {
139         /* we could use some clever */
140         Main *bmain = CTX_data_main(C);
141         bScreen *screen = CTX_wm_screen(C);
142         ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, mx, my);
143         const char *display_device = CTX_data_scene(C)->display_settings.display_device;
144         struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
145
146         if (sa) {
147                 if (sa->spacetype == SPACE_IMAGE) {
148                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
149                         if (ar) {
150                                 SpaceImage *sima = sa->spacedata.first;
151                                 int mval[2] = {mx - ar->winrct.xmin,
152                                                my - ar->winrct.ymin};
153
154                                 if (ED_space_image_color_sample(sima, ar, mval, r_col)) {
155                                         return;
156                                 }
157                         }
158                 }
159                 else if (sa->spacetype == SPACE_NODE) {
160                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
161                         if (ar) {
162                                 SpaceNode *snode = sa->spacedata.first;
163                                 int mval[2] = {mx - ar->winrct.xmin,
164                                                my - ar->winrct.ymin};
165
166                                 if (ED_space_node_color_sample(bmain, snode, ar, mval, r_col)) {
167                                         return;
168                                 }
169                         }
170                 }
171                 else if (sa->spacetype == SPACE_CLIP) {
172                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
173                         if (ar) {
174                                 SpaceClip *sc = sa->spacedata.first;
175                                 int mval[2] = {mx - ar->winrct.xmin,
176                                                my - ar->winrct.ymin};
177
178                                 if (ED_space_clip_color_sample(sc, ar, mval, r_col)) {
179                                         return;
180                                 }
181                         }
182                 }
183         }
184
185         /* fallback to simple opengl picker */
186         glReadBuffer(GL_FRONT);
187         glReadPixels(mx, my, 1, 1, GL_RGB, GL_FLOAT, r_col);
188         glReadBuffer(GL_BACK);
189
190         IMB_colormanagement_display_to_scene_linear_v3(r_col, display);
191 }
192
193 /* sets the sample color RGB, maintaining A */
194 static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3])
195 {
196         float col_conv[4];
197
198         /* to maintain alpha */
199         RNA_property_float_get_array(&eye->ptr, eye->prop, col_conv);
200
201         /* convert from linear rgb space to display space */
202         if (eye->display) {
203                 copy_v3_v3(col_conv, col);
204                 IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display);
205         }
206         else {
207                 copy_v3_v3(col_conv, col);
208         }
209
210         RNA_property_float_set_array(&eye->ptr, eye->prop, col_conv);
211         eye->is_set = true;
212
213         RNA_property_update(C, &eye->ptr, eye->prop);
214 }
215
216 static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my)
217 {
218         /* Accumulate color. */
219         float col[3];
220         eyedropper_color_sample_fl(C, mx, my, col);
221
222         if (eye->use_accum) {
223                 add_v3_v3(eye->accum_col, col);
224                 eye->accum_tot++;
225         }
226         else {
227                 copy_v3_v3(eye->accum_col, col);
228                 eye->accum_tot = 1;
229         }
230
231         /* Apply to property. */
232         float accum_col[3];
233         if (eye->accum_tot > 1) {
234                 mul_v3_v3fl(accum_col, eye->accum_col, 1.0f / (float)eye->accum_tot);
235         }
236         else {
237                 copy_v3_v3(accum_col, eye->accum_col);
238         }
239         eyedropper_color_set(C, eye, accum_col);
240 }
241
242 static void eyedropper_cancel(bContext *C, wmOperator *op)
243 {
244         Eyedropper *eye = op->customdata;
245         if (eye->is_set) {
246                 eyedropper_color_set(C, eye, eye->init_col);
247         }
248         eyedropper_exit(C, op);
249 }
250
251 /* main modal status check */
252 static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
253 {
254         Eyedropper *eye = (Eyedropper *)op->customdata;
255
256         /* handle modal keymap */
257         if (event->type == EVT_MODAL_MAP) {
258                 switch (event->val) {
259                         case EYE_MODAL_CANCEL:
260                                 eyedropper_cancel(C, op);
261                                 return OPERATOR_CANCELLED;
262                         case EYE_MODAL_SAMPLE_CONFIRM:
263                         {
264                                 const bool is_undo = eye->is_undo;
265                                 if (eye->accum_tot == 0) {
266                                         eyedropper_color_sample(C, eye, event->x, event->y);
267                                 }
268                                 eyedropper_exit(C, op);
269                                 /* Could support finished & undo-skip. */
270                                 return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
271                         }
272                         case EYE_MODAL_SAMPLE_BEGIN:
273                                 /* enable accum and make first sample */
274                                 eye->accum_start = true;
275                                 eyedropper_color_sample(C, eye, event->x, event->y);
276                                 break;
277                         case EYE_MODAL_SAMPLE_RESET:
278                                 eye->accum_tot = 0;
279                                 zero_v3(eye->accum_col);
280                                 eyedropper_color_sample(C, eye, event->x, event->y);
281                                 break;
282                 }
283         }
284         else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
285                 if (eye->accum_start) {
286                         /* button is pressed so keep sampling */
287                         eyedropper_color_sample(C, eye, event->x, event->y);
288                 }
289         }
290
291         return OPERATOR_RUNNING_MODAL;
292 }
293
294 /* Modal Operator init */
295 static int eyedropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
296 {
297         /* init */
298         if (eyedropper_init(C, op)) {
299                 WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
300
301                 /* add temp handler */
302                 WM_event_add_modal_handler(C, op);
303
304                 return OPERATOR_RUNNING_MODAL;
305         }
306         else {
307                 return OPERATOR_PASS_THROUGH;
308         }
309 }
310
311 /* Repeat operator */
312 static int eyedropper_exec(bContext *C, wmOperator *op)
313 {
314         /* init */
315         if (eyedropper_init(C, op)) {
316
317                 /* do something */
318
319                 /* cleanup */
320                 eyedropper_exit(C, op);
321
322                 return OPERATOR_FINISHED;
323         }
324         else {
325                 return OPERATOR_PASS_THROUGH;
326         }
327 }
328
329 static bool eyedropper_poll(bContext *C)
330 {
331         /* Actual test for active button happens later, since we don't
332          * know which one is active until mouse over. */
333         return (CTX_wm_window(C) != NULL);
334 }
335
336 void UI_OT_eyedropper_color(wmOperatorType *ot)
337 {
338         /* identifiers */
339         ot->name = "Eyedropper";
340         ot->idname = "UI_OT_eyedropper_color";
341         ot->description = "Sample a color from the Blender Window to store in a property";
342
343         /* api callbacks */
344         ot->invoke = eyedropper_invoke;
345         ot->modal = eyedropper_modal;
346         ot->cancel = eyedropper_cancel;
347         ot->exec = eyedropper_exec;
348         ot->poll = eyedropper_poll;
349
350         /* flags */
351         ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_INTERNAL;
352
353         /* properties */
354         PropertyRNA *prop;
355
356         /* Needed for color picking with crypto-matte. */
357         prop = RNA_def_boolean(ot->srna, "use_accumulate", true, "Accumulate", "");
358         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
359 }