Blender V4.3
eyedropper_grease_pencil_color.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
14#include "MEM_guardedalloc.h"
15
16#include "BLI_listbase.h"
18#include "BLI_string.h"
19
20#include "BLT_translation.hh"
21
22#include "DNA_brush_types.h"
23#include "DNA_material_types.h"
24#include "DNA_space_types.h"
25
26#include "BKE_brush.hh"
27#include "BKE_context.hh"
28#include "BKE_grease_pencil.hh"
29#include "BKE_lib_id.hh"
30#include "BKE_material.h"
31#include "BKE_paint.hh"
32
33#include "UI_interface.hh"
34
36
37#include "WM_api.hh"
38#include "WM_types.hh"
39
40#include "RNA_access.hh"
41#include "RNA_define.hh"
42
43#include "ED_screen.hh"
44#include "ED_undo.hh"
45
47
48#include "eyedropper_intern.hh"
49#include "interface_intern.hh"
50
52
53enum class EyeMode : int8_t {
54 Material = 0,
55 Palette = 1,
56 Brush = 2,
57};
58
59enum class MaterialMode : int8_t {
60 Stroke = 0,
61 Fill = 1,
62 Both = 2,
63};
64
78
79/* Helper: Draw status message while the user is running the operator */
81 wmOperator *op,
82 const wmEvent *event)
83{
84 std::string header;
85 header += IFACE_("Current: ");
86
87 const bool is_ctrl = (event->modifier & KM_CTRL) != 0;
88 const bool is_shift = (event->modifier & KM_SHIFT) != 0;
89
91
92 MaterialMode mat_mode = eye->mat_mode;
93 if (is_ctrl && !is_shift) {
94 mat_mode = MaterialMode::Stroke;
95 }
96 if (is_shift && !is_ctrl) {
97 mat_mode = MaterialMode::Fill;
98 }
99 if (is_ctrl && is_shift) {
100 mat_mode = MaterialMode::Both;
101 }
102
103 switch (mat_mode) {
105 header += IFACE_("Stroke");
106 break;
107 }
108 case MaterialMode::Fill: {
109 header += IFACE_("Fill");
110 break;
111 }
112 case MaterialMode::Both: {
113 header += IFACE_("Both");
114 break;
115 }
116 }
117
118 header += IFACE_(", Ctrl: Stroke, Shift: Fill, Shift+Ctrl: Both");
119
120 ED_workspace_status_text(C, header.c_str());
121}
122
124{
125 EyedropperGreasePencil *eye = MEM_new<EyedropperGreasePencil>(__func__);
126
127 op->customdata = eye;
128 Scene *scene = CTX_data_scene(C);
129
130 const char *display_device;
131 display_device = scene->display_settings.display_device;
132 eye->display = IMB_colormanagement_display_get_named(display_device);
133
134 eye->accum_start = true;
135 eye->mode = EyeMode(RNA_enum_get(op->ptr, "mode"));
136 eye->mat_mode = MaterialMode(RNA_enum_get(op->ptr, "material_mode"));
137 return true;
138}
139
141{
142 /* Clear status message area. */
143 ED_workspace_status_text(C, nullptr);
144
146
147 MEM_delete<EyedropperGreasePencil>(eye);
148 /* Clear pointer. */
149 op->customdata = nullptr;
150}
151
153 const float3 col_conv,
154 const MaterialMode mat_mode)
155{
156 Main *bmain = CTX_data_main(C);
158 Material *ma = nullptr;
159
160 bool found = false;
161
162 /* Look for a similar material in grease pencil slots. */
163 short *totcol = BKE_object_material_len_p(ob);
164 for (short i = 0; i < *totcol; i++) {
165 ma = BKE_object_material_get(ob, i + 1);
166 if (ma == nullptr) {
167 continue;
168 }
169
170 MaterialGPencilStyle *gp_style = ma->gp_style;
171 if (gp_style != nullptr) {
172 /* Check stroke color. */
173 bool found_stroke = compare_v3v3(gp_style->stroke_rgba, col_conv, 0.01f) &&
174 (gp_style->flag & GP_MATERIAL_STROKE_SHOW);
175 /* Check fill color. */
176 bool found_fill = compare_v3v3(gp_style->fill_rgba, col_conv, 0.01f) &&
177 (gp_style->flag & GP_MATERIAL_FILL_SHOW);
178
179 if ((mat_mode == MaterialMode::Stroke) && (found_stroke) &&
180 ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0))
181 {
182 found = true;
183 }
184 else if ((mat_mode == MaterialMode::Fill) && found_fill &&
185 ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0))
186 {
187 found = true;
188 }
189 else if ((mat_mode == MaterialMode::Both) && found_stroke && found_fill) {
190 found = true;
191 }
192
193 /* Found existing material. */
194 if (found) {
195 ob->actcol = i + 1;
198 return;
199 }
200 }
201 }
202
203 /* If material was not found add a new material with stroke and/or fill color
204 * depending of the secondary key (LMB: Stroke, Shift: Fill, Shift+Ctrl: Stroke/Fill)
205 */
206 int idx;
207 Material *ma_new = BKE_grease_pencil_object_material_new(bmain, ob, "Material", &idx);
211
212 BLI_assert(ma_new != nullptr);
213
214 MaterialGPencilStyle *gp_style_new = ma_new->gp_style;
215 BLI_assert(gp_style_new != nullptr);
216
217 /* Only create Stroke (default option). */
218 if (mat_mode == MaterialMode::Stroke) {
219 /* Stroke color. */
220 gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW;
221 gp_style_new->flag &= ~GP_MATERIAL_FILL_SHOW;
222 copy_v3_v3(gp_style_new->stroke_rgba, col_conv);
223 zero_v4(gp_style_new->fill_rgba);
224 }
225 /* Fill Only. */
226 else if (mat_mode == MaterialMode::Fill) {
227 /* Fill color. */
228 gp_style_new->flag &= ~GP_MATERIAL_STROKE_SHOW;
229 gp_style_new->flag |= GP_MATERIAL_FILL_SHOW;
230 zero_v4(gp_style_new->stroke_rgba);
231 copy_v3_v3(gp_style_new->fill_rgba, col_conv);
232 }
233 /* Stroke and Fill. */
234 else if (mat_mode == MaterialMode::Both) {
236 copy_v3_v3(gp_style_new->stroke_rgba, col_conv);
237 copy_v3_v3(gp_style_new->fill_rgba, col_conv);
238 }
239 /* Push undo for new created material. */
240 ED_undo_push(C, "Add Grease Pencil Material");
241}
242
243/* Create a new palette color and palette if needed. */
244static void eyedropper_add_palette_color(bContext *C, const float3 col_conv)
245{
246 Main *bmain = CTX_data_main(C);
247 Scene *scene = CTX_data_scene(C);
248 ToolSettings *ts = scene->toolsettings;
249 GpPaint *gp_paint = ts->gp_paint;
250 GpVertexPaint *gp_vertexpaint = ts->gp_vertexpaint;
251 Paint *paint = &gp_paint->paint;
252 Paint *vertexpaint = &gp_vertexpaint->paint;
253
254 /* Check for Palette in Draw and Vertex Paint Mode. */
255 if (paint->palette == nullptr) {
256 Palette *palette = BKE_palette_add(bmain, "Grease Pencil");
257 id_us_min(&palette->id);
258
259 BKE_paint_palette_set(paint, palette);
260
261 if (vertexpaint->palette == nullptr) {
262 BKE_paint_palette_set(vertexpaint, palette);
263 }
264 }
265
266 /* Check if the color exist already. */
267 Palette *palette = paint->palette;
268 int i;
269 LISTBASE_FOREACH_INDEX (PaletteColor *, palcolor, &palette->colors, i) {
270 if (compare_v3v3(palcolor->rgb, col_conv, 0.01f)) {
271 palette->active_color = i;
272 return;
273 }
274 }
275
276 /* Create Colors. */
277 PaletteColor *palcol = BKE_palette_color_add(palette);
278 if (palcol) {
279 palette->active_color = BLI_listbase_count(&palette->colors) - 1;
280 copy_v3_v3(palcol->rgb, col_conv);
281 }
282}
283
284/* Set the active brush's color. */
285static void eyedropper_set_brush_color(bContext *C, const float3 &col_conv)
286{
287 Scene *scene = CTX_data_scene(C);
288 ToolSettings *ts = scene->toolsettings;
289 Paint *paint = &ts->gp_paint->paint;
290 Brush *brush = BKE_paint_brush(paint);
291 if (brush == nullptr) {
292 return;
293 }
294
295 copy_v3_v3(brush->rgb, col_conv);
297}
298
299/* Set the material or the palette color. */
301 const wmEvent *event,
303{
304 const bool is_ctrl = (event->modifier & KM_CTRL) != 0;
305 const bool is_shift = (event->modifier & KM_SHIFT) != 0;
306
307 MaterialMode mat_mode = eye->mat_mode;
308 if (is_ctrl && !is_shift) {
309 mat_mode = MaterialMode::Stroke;
310 }
311 if (is_shift && !is_ctrl) {
312 mat_mode = MaterialMode::Fill;
313 }
314 if (is_ctrl && is_shift) {
315 mat_mode = MaterialMode::Both;
316 }
317
318 float3 col_conv = eye->color;
319
320 /* Convert from linear rgb space to display space because palette and brush colors are in display
321 * space, and this conversion is needed to undo the conversion to linear performed by
322 * eyedropper_color_sample_fl. */
323 if (eye->display && ELEM(eye->mode, EyeMode::Palette, EyeMode::Brush)) {
325 }
326
327 switch (eye->mode) {
329 eyedropper_add_material(C, col_conv, mat_mode);
330 break;
331 case EyeMode::Palette:
332 eyedropper_add_palette_color(C, col_conv);
333 break;
334 case EyeMode::Brush:
335 eyedropper_set_brush_color(C, col_conv);
336 break;
337 }
338}
339
340/* Sample the color below cursor. */
343 const int m_xy[2])
344{
345 /* Accumulate color. */
346 float3 col;
347 eyedropper_color_sample_fl(C, nullptr, m_xy, col);
348
349 eye->accum_col += col;
350 eye->accum_tot++;
351
352 eye->color = eye->accum_col;
353 if (eye->accum_tot > 1) {
354 eye->color = eye->accum_col / float(eye->accum_tot);
355 }
356}
357
362
363/* Main modal status check. */
365{
368
369 /* Handle modal keymap */
370 switch (event->type) {
371 case EVT_MODAL_MAP: {
372 switch (event->val) {
374 /* enable accum and make first sample */
375 eye->accum_start = true;
377 break;
379 eye->accum_tot = 0;
380 eye->accum_col = float3(0.0f, 0.0f, 0.0f);
382 break;
383 case EYE_MODAL_CANCEL: {
385 return OPERATOR_CANCELLED;
386 }
389
390 /* Create material. */
393
395 return OPERATOR_FINISHED;
396 }
397 default: {
398 break;
399 }
400 }
401 break;
402 }
403 case MOUSEMOVE:
404 case INBETWEEN_MOUSEMOVE: {
405 if (eye->accum_start) {
406 /* button is pressed so keep sampling */
408 }
409 break;
410 }
411 default: {
412 break;
413 }
414 }
415
417}
418
420{
422 /* Add modal temp handler. */
424 /* Status message. */
426
428 }
430}
431
432/* Repeat operator */
434{
436
437 /* cleanup */
439
440 return OPERATOR_FINISHED;
441 }
443}
444
446{
447 /* Only valid if the current active object is grease pencil. */
448 Object *obact = CTX_data_active_object(C);
449 if ((obact == nullptr) || (obact->type != OB_GREASE_PENCIL)) {
450 return false;
451 }
452
453 /* Test we have a window below. */
454 return (CTX_wm_window(C) != nullptr);
455}
456
457} // namespace blender::ui::greasepencil
458
460{
461 using namespace blender::ui::greasepencil;
462 static const EnumPropertyItem items_mode[] = {
463 {int(EyeMode::Material), "MATERIAL", 0, "Material", ""},
464 {int(EyeMode::Palette), "PALETTE", 0, "Palette", ""},
465 {int(EyeMode::Brush), "BRUSH", 0, "Brush", ""},
466 {0, nullptr, 0, nullptr, nullptr},
467 };
468
469 static const EnumPropertyItem items_material_mode[] = {
470 {int(MaterialMode::Stroke), "STROKE", 0, "Stroke", ""},
471 {int(MaterialMode::Fill), "FILL", 0, "Fill", ""},
472 {int(MaterialMode::Both), "BOTH", 0, "Both", ""},
473 {0, nullptr, 0, nullptr, nullptr},
474 };
475
476 /* Identifiers. */
477 ot->name = "Grease Pencil Eyedropper";
478 ot->idname = "UI_OT_eyedropper_grease_pencil_color";
479 ot->description = "Sample a color from the Blender Window and create Grease Pencil material";
480
481 /* Api callbacks. */
482 ot->invoke = eyedropper_grease_pencil_invoke;
483 ot->modal = eyedropper_grease_pencil_modal;
484 ot->cancel = eyedropper_grease_pencil_cancel;
485 ot->exec = eyedropper_grease_pencil_exec;
486 ot->poll = eyedropper_grease_pencil_poll;
487
488 /* Flags. */
490
491 /* Properties. */
492 ot->prop = RNA_def_enum(ot->srna, "mode", items_mode, int(EyeMode::Material), "Mode", "");
494 "material_mode",
495 items_material_mode,
496 int(MaterialMode::Stroke),
497 "Material Mode",
498 "");
499}
void BKE_brush_tag_unsaved_changes(Brush *brush)
Definition brush.cc:621
wmWindow * CTX_wm_window(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
Low-level operations for grease pencil.
Material * BKE_grease_pencil_object_material_new(Main *bmain, Object *ob, const char *name, int *r_index)
void id_us_min(ID *id)
Definition lib_id.cc:359
General operations, lookup, etc. for materials.
struct Material * BKE_object_material_get(struct Object *ob, short act)
short * BKE_object_material_len_p(struct Object *ob)
PaletteColor * BKE_palette_color_add(Palette *palette)
Definition paint.cc:1392
Palette * BKE_palette_add(Main *bmain, const char *name)
Definition paint.cc:1386
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:649
void BKE_paint_palette_set(Paint *paint, Palette *palette)
Definition paint.cc:1350
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void zero_v4(float r[4])
MINLINE bool compare_v3v3(const float v1[3], const float v2[3], float limit) ATTR_WARN_UNUSED_RESULT
#define ELEM(...)
#define IFACE_(msgid)
void DEG_relations_tag_update(Main *bmain)
@ GP_MATERIAL_STROKE_SHOW
@ GP_MATERIAL_FILL_SHOW
@ OB_GREASE_PENCIL
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:966
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:104
ColorManagedDisplay * IMB_colormanagement_display_get_named(const char *name)
void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], ColorManagedDisplay *display)
Read Guarded memory(de)allocation.
@ OPTYPE_BLOCKING
Definition WM_types.hh:164
@ OPTYPE_UNDO
Definition WM_types.hh:162
#define ND_DATA
Definition WM_types.hh:475
#define NA_EDITED
Definition WM_types.hh:550
#define NC_MATERIAL
Definition WM_types.hh:347
#define NC_GPENCIL
Definition WM_types.hh:366
@ KM_CTRL
Definition WM_types.hh:256
@ KM_SHIFT
Definition WM_types.hh:255
#define ND_OB_SHADING
Definition WM_types.hh:424
#define ND_SPACE_VIEW3D
Definition WM_types.hh:494
#define NC_OBJECT
Definition WM_types.hh:346
#define ND_SHADING_LINKS
Definition WM_types.hh:446
#define NC_SPACE
Definition WM_types.hh:359
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void eyedropper_color_sample_fl(bContext *C, Eyedropper *eye, const int event_xy[2], float r_col[3])
void UI_OT_eyedropper_grease_pencil_color(wmOperatorType *ot)
@ EYE_MODAL_SAMPLE_BEGIN
@ EYE_MODAL_SAMPLE_RESET
@ EYE_MODAL_CANCEL
@ EYE_MODAL_SAMPLE_CONFIRM
uint col
static int eyedropper_grease_pencil_modal(bContext *C, wmOperator *op, const wmEvent *event)
static int eyedropper_grease_pencil_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool eyedropper_grease_pencil_poll(bContext *C)
static void eyedropper_add_palette_color(bContext *C, const float3 col_conv)
static void eyedropper_set_brush_color(bContext *C, const float3 &col_conv)
static void eyedropper_grease_pencil_color_sample(bContext *C, EyedropperGreasePencil *eye, const int m_xy[2])
static void eyedropper_grease_pencil_exit(bContext *C, wmOperator *op)
static bool eyedropper_grease_pencil_init(bContext *C, wmOperator *op)
static void eyedropper_grease_pencil_status_indicators(bContext *C, wmOperator *op, const wmEvent *event)
static void eyedropper_grease_pencil_cancel(bContext *C, wmOperator *op)
static void eyedropper_add_material(bContext *C, const float3 col_conv, const MaterialMode mat_mode)
static void eyedropper_grease_pencil_color_set(bContext *C, const wmEvent *event, EyedropperGreasePencil *eye)
static int eyedropper_grease_pencil_exec(bContext *C, wmOperator *op)
VecBase< float, 3 > float3
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
signed char int8_t
Definition stdint.h:75
float rgb[3]
struct MaterialGPencilStyle * gp_style
struct Palette * palette
ListBase colors
GpVertexPaint * gp_vertexpaint
short val
Definition WM_types.hh:724
int xy[2]
Definition WM_types.hh:726
short type
Definition WM_types.hh:722
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct PointerRNA * ptr
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
@ EVT_MODAL_MAP
@ MOUSEMOVE
@ INBETWEEN_MOUSEMOVE
wmOperatorType * ot
Definition wm_files.cc:4125