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_gpencil_color
27 */
28
29 #include "MEM_guardedalloc.h"
30
31 #include "BLI_listbase.h"
32 #include "BLI_string.h"
33
34 #include "BLT_translation.h"
35
36 #include "DNA_gpencil_types.h"
37 #include "DNA_space_types.h"
38
39 #include "BKE_context.h"
40 #include "BKE_gpencil.h"
41 #include "BKE_lib_id.h"
42 #include "BKE_main.h"
43 #include "BKE_material.h"
44 #include "BKE_paint.h"
45 #include "BKE_report.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 "RNA_access.h"
55 #include "RNA_define.h"
56
57 #include "ED_gpencil.h"
58 #include "ED_screen.h"
59 #include "ED_undo.h"
60
61 #include "DEG_depsgraph.h"
62 #include "DEG_depsgraph_build.h"
63
64 #include "interface_eyedropper_intern.h"
65 #include "interface_intern.h"
66
67 typedef struct EyedropperGPencil {
68 struct ColorManagedDisplay *display;
69 /** color under cursor RGB */
70 float color[3];
71 /** Mode */
72 int mode;
73 } EyedropperGPencil;
74
75 /* Helper: Draw status message while the user is running the operator */
eyedropper_gpencil_status_indicators(bContext * C)76 static void eyedropper_gpencil_status_indicators(bContext *C)
77 {
78 char msg_str[UI_MAX_DRAW_STR];
79 BLI_strncpy(
80 msg_str, TIP_("LMB: Stroke - Shift: Fill - Shift+Ctrl: Stroke + Fill"), UI_MAX_DRAW_STR);
81
82 ED_workspace_status_text(C, msg_str);
83 }
84
85 /* Initialize. */
eyedropper_gpencil_init(bContext * C,wmOperator * op)86 static bool eyedropper_gpencil_init(bContext *C, wmOperator *op)
87 {
88 EyedropperGPencil *eye = MEM_callocN(sizeof(EyedropperGPencil), __func__);
89
90 op->customdata = eye;
91 Scene *scene = CTX_data_scene(C);
92
93 const char *display_device;
94 display_device = scene->display_settings.display_device;
95 eye->display = IMB_colormanagement_display_get_named(display_device);
96
97 eye->mode = RNA_enum_get(op->ptr, "mode");
98 return true;
99 }
100
101 /* Exit and free memory. */
eyedropper_gpencil_exit(bContext * C,wmOperator * op)102 static void eyedropper_gpencil_exit(bContext *C, wmOperator *op)
103 {
104 /* Clear status message area. */
105 ED_workspace_status_text(C, NULL);
106
107 MEM_SAFE_FREE(op->customdata);
108 }
109
eyedropper_add_material(bContext * C,const float col_conv[4],const bool only_stroke,const bool only_fill,const bool both)110 static void eyedropper_add_material(bContext *C,
111 const float col_conv[4],
112 const bool only_stroke,
113 const bool only_fill,
114 const bool both)
115 {
116 Main *bmain = CTX_data_main(C);
117 Object *ob = CTX_data_active_object(C);
118 Material *ma = NULL;
119
120 bool found = false;
121
122 /* Look for a similar material in grease pencil slots. */
123 short *totcol = BKE_object_material_len_p(ob);
124 for (short i = 0; i < *totcol; i++) {
125 ma = BKE_object_material_get(ob, i + 1);
126 if (ma == NULL) {
127 continue;
128 }
129
130 MaterialGPencilStyle *gp_style = ma->gp_style;
131 if (gp_style != NULL) {
132 /* Check stroke color. */
133 bool found_stroke = compare_v3v3(gp_style->stroke_rgba, col_conv, 0.01f) &&
134 (gp_style->flag & GP_MATERIAL_STROKE_SHOW);
135 /* Check fill color. */
136 bool found_fill = compare_v3v3(gp_style->fill_rgba, col_conv, 0.01f) &&
137 (gp_style->flag & GP_MATERIAL_FILL_SHOW);
138
139 if ((only_stroke) && (found_stroke) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) {
140 found = true;
141 }
142 else if ((only_fill) && (found_fill) && ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0)) {
143 found = true;
144 }
145 else if ((both) && (found_stroke) && (found_fill)) {
146 found = true;
147 }
148
149 /* Found existing material. */
150 if (found) {
151 ob->actcol = i + 1;
152 WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, NULL);
153 WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, NULL);
154 return;
155 }
156 }
157 }
158
159 /* If material was not found add a new material with stroke and/or fill color
160 * depending of the secondary key (LMB: Stroke, Shift: Fill, Shift+Ctrl: Stroke/Fill)
161 */
162 int idx;
163 Material *ma_new = BKE_gpencil_object_material_new(bmain, ob, "Material", &idx);
164 WM_main_add_notifier(NC_OBJECT | ND_OB_SHADING, &ob->id);
165 WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, NULL);
166 DEG_relations_tag_update(bmain);
167
168 BLI_assert(ma_new != NULL);
169
170 MaterialGPencilStyle *gp_style_new = ma_new->gp_style;
171 BLI_assert(gp_style_new != NULL);
172
173 /* Only create Stroke (default option). */
174 if (only_stroke) {
175 /* Stroke color. */
176 gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW;
177 gp_style_new->flag &= ~GP_MATERIAL_FILL_SHOW;
178 copy_v3_v3(gp_style_new->stroke_rgba, col_conv);
179 zero_v4(gp_style_new->fill_rgba);
180 }
181 /* Fill Only. */
182 else if (only_fill) {
183 /* Fill color. */
184 gp_style_new->flag &= ~GP_MATERIAL_STROKE_SHOW;
185 gp_style_new->flag |= GP_MATERIAL_FILL_SHOW;
186 zero_v4(gp_style_new->stroke_rgba);
187 copy_v3_v3(gp_style_new->fill_rgba, col_conv);
188 }
189 /* Stroke and Fill. */
190 else if (both) {
191 gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW | GP_MATERIAL_FILL_SHOW;
192 copy_v3_v3(gp_style_new->stroke_rgba, col_conv);
193 copy_v3_v3(gp_style_new->fill_rgba, col_conv);
194 }
195 /* Push undo for new created material. */
196 ED_undo_push(C, "Add Grease Pencil Material");
197 }
198
199 /* Create a new palette color and palette if needed. */
eyedropper_add_palette_color(bContext * C,const float col_conv[4])200 static void eyedropper_add_palette_color(bContext *C, const float col_conv[4])
201 {
202 Main *bmain = CTX_data_main(C);
203 Scene *scene = CTX_data_scene(C);
204 ToolSettings *ts = scene->toolsettings;
205 GpPaint *gp_paint = ts->gp_paint;
206 GpVertexPaint *gp_vertexpaint = ts->gp_vertexpaint;
207 Paint *paint = &gp_paint->paint;
208 Paint *vertexpaint = &gp_vertexpaint->paint;
209
210 /* Check for Palette in Draw and Vertex Paint Mode. */
211 if (paint->palette == NULL) {
212 Palette *palette = BKE_palette_add(bmain, "Grease Pencil");
213 id_us_min(&palette->id);
214
215 BKE_paint_palette_set(paint, palette);
216
217 if (vertexpaint->palette == NULL) {
218 BKE_paint_palette_set(vertexpaint, palette);
219 }
220 }
221 /* Check if the color exist already. */
222 Palette *palette = paint->palette;
223 LISTBASE_FOREACH (PaletteColor *, palcolor, &palette->colors) {
224 if (compare_v3v3(palcolor->rgb, col_conv, 0.01f)) {
225 return;
226 }
227 }
228
229 /* Create Colors. */
230 PaletteColor *palcol = BKE_palette_color_add(palette);
231 if (palcol) {
232 copy_v3_v3(palcol->rgb, col_conv);
233 }
234 }
235
236 /* Set the material or the palette color. */
eyedropper_gpencil_color_set(bContext * C,const wmEvent * event,EyedropperGPencil * eye)237 static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye)
238 {
239
240 const bool only_stroke = ((!event->ctrl) && (!event->shift));
241 const bool only_fill = ((!event->ctrl) && (event->shift));
242 const bool both = ((event->ctrl) && (event->shift));
243
244 float col_conv[4];
245
246 /* Convert from linear rgb space to display space because grease pencil colors are in display
247 * space, and this conversion is needed to undo the conversion to linear performed by
248 * eyedropper_color_sample_fl. */
249 if (eye->display) {
250 copy_v3_v3(col_conv, eye->color);
251 IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display);
252 }
253 else {
254 copy_v3_v3(col_conv, eye->color);
255 }
256
257 /* Add material or Palette color*/
258 if (eye->mode == 0) {
259 eyedropper_add_material(C, col_conv, only_stroke, only_fill, both);
260 }
261 else {
262 eyedropper_add_palette_color(C, col_conv);
263 }
264 }
265
266 /* Sample the color below cursor. */
eyedropper_gpencil_color_sample(bContext * C,EyedropperGPencil * eye,int mx,int my)267 static void eyedropper_gpencil_color_sample(bContext *C, EyedropperGPencil *eye, int mx, int my)
268 {
269 eyedropper_color_sample_fl(C, mx, my, eye->color);
270 }
271
272 /* Cancel operator. */
eyedropper_gpencil_cancel(bContext * C,wmOperator * op)273 static void eyedropper_gpencil_cancel(bContext *C, wmOperator *op)
274 {
275 eyedropper_gpencil_exit(C, op);
276 }
277
278 /* Main modal status check. */
eyedropper_gpencil_modal(bContext * C,wmOperator * op,const wmEvent * event)279 static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent *event)
280 {
281 EyedropperGPencil *eye = (EyedropperGPencil *)op->customdata;
282 /* Handle modal keymap */
283 switch (event->type) {
284 case EVT_MODAL_MAP: {
285 switch (event->val) {
286 case EYE_MODAL_SAMPLE_BEGIN: {
287 return OPERATOR_RUNNING_MODAL;
288 }
289 case EYE_MODAL_CANCEL: {
290 eyedropper_gpencil_cancel(C, op);
291 return OPERATOR_CANCELLED;
292 }
293 case EYE_MODAL_SAMPLE_CONFIRM: {
294 eyedropper_gpencil_color_sample(C, eye, event->x, event->y);
295
296 /* Create material. */
297 eyedropper_gpencil_color_set(C, event, eye);
298 WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
299
300 eyedropper_gpencil_exit(C, op);
301 return OPERATOR_FINISHED;
302 break;
303 }
304 default: {
305 break;
306 }
307 }
308 break;
309 }
310 case MOUSEMOVE:
311 case INBETWEEN_MOUSEMOVE: {
312 eyedropper_gpencil_color_sample(C, eye, event->x, event->y);
313 break;
314 }
315 default: {
316 break;
317 }
318 }
319
320 return OPERATOR_RUNNING_MODAL;
321 }
322
323 /* Modal Operator init */
eyedropper_gpencil_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))324 static int eyedropper_gpencil_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
325 {
326 /* Init. */
327 if (eyedropper_gpencil_init(C, op)) {
328 /* Add modal temp handler. */
329 WM_event_add_modal_handler(C, op);
330 /* Status message. */
331 eyedropper_gpencil_status_indicators(C);
332
333 return OPERATOR_RUNNING_MODAL;
334 }
335 return OPERATOR_PASS_THROUGH;
336 }
337
338 /* Repeat operator */
eyedropper_gpencil_exec(bContext * C,wmOperator * op)339 static int eyedropper_gpencil_exec(bContext *C, wmOperator *op)
340 {
341 /* init */
342 if (eyedropper_gpencil_init(C, op)) {
343
344 /* cleanup */
345 eyedropper_gpencil_exit(C, op);
346
347 return OPERATOR_FINISHED;
348 }
349 return OPERATOR_PASS_THROUGH;
350 }
351
eyedropper_gpencil_poll(bContext * C)352 static bool eyedropper_gpencil_poll(bContext *C)
353 {
354 /* Only valid if the current active object is grease pencil. */
355 Object *obact = CTX_data_active_object(C);
356 if ((obact == NULL) || (obact->type != OB_GPENCIL)) {
357 return false;
358 }
359
360 /* Test we have a window below. */
361 return (CTX_wm_window(C) != NULL);
362 }
363
UI_OT_eyedropper_gpencil_color(wmOperatorType * ot)364 void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot)
365 {
366 static const EnumPropertyItem items_mode[] = {
367 {0, "MATERIAL", 0, "Material", ""},
368 {1, "PALETTE", 0, "Palette", ""},
369 {0, NULL, 0, NULL, NULL},
370 };
371
372 /* identifiers */
373 ot->name = "Grease Pencil Eyedropper";
374 ot->idname = "UI_OT_eyedropper_gpencil_color";
375 ot->description = "Sample a color from the Blender Window and create Grease Pencil material";
376
377 /* api callbacks */
378 ot->invoke = eyedropper_gpencil_invoke;
379 ot->modal = eyedropper_gpencil_modal;
380 ot->cancel = eyedropper_gpencil_cancel;
381 ot->exec = eyedropper_gpencil_exec;
382 ot->poll = eyedropper_gpencil_poll;
383
384 /* flags */
385 ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
386
387 /* properties */
388 ot->prop = RNA_def_enum(ot->srna, "mode", items_mode, 0, "Mode", "");
389 }
390