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