Blender V4.5
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
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_listbase.h"
18
19#include "BLT_translation.hh"
20
21#include "DNA_brush_types.h"
22#include "DNA_material_types.h"
23
24#include "BKE_brush.hh"
25#include "BKE_context.hh"
26#include "BKE_grease_pencil.hh"
27#include "BKE_lib_id.hh"
28#include "BKE_material.hh"
29#include "BKE_paint.hh"
30
31#include "UI_interface.hh"
32
34
35#include "WM_api.hh"
36#include "WM_types.hh"
37
38#include "RNA_access.hh"
39#include "RNA_define.hh"
40
41#include "ED_screen.hh"
42#include "ED_undo.hh"
43
45
46#include "eyedropper_intern.hh"
47#include "interface_intern.hh"
48
50
51enum class EyeMode : int8_t {
54 Brush = 2,
55};
56
57enum class MaterialMode : int8_t {
58 Stroke = 0,
59 Fill = 1,
60 Both = 2,
61};
62
76
77/* Helper: Draw status message while the user is running the operator */
79 wmOperator *op,
80 const wmEvent *event)
81{
82 std::string header;
83 header += IFACE_("Current: ");
84
85 const bool is_ctrl = (event->modifier & KM_CTRL) != 0;
86 const bool is_shift = (event->modifier & KM_SHIFT) != 0;
87
89
90 MaterialMode mat_mode = eye->mat_mode;
91 if (is_ctrl && !is_shift) {
92 mat_mode = MaterialMode::Stroke;
93 }
94 if (is_shift && !is_ctrl) {
95 mat_mode = MaterialMode::Fill;
96 }
97 if (is_ctrl && is_shift) {
98 mat_mode = MaterialMode::Both;
99 }
100
101 switch (mat_mode) {
103 header += IFACE_("Stroke");
104 break;
105 }
106 case MaterialMode::Fill: {
107 header += IFACE_("Fill");
108 break;
109 }
110 case MaterialMode::Both: {
111 header += IFACE_("Both");
112 break;
113 }
114 }
115
116 header += IFACE_(", Ctrl: Stroke, Shift: Fill, Shift+Ctrl: Both");
117
118 ED_workspace_status_text(C, header.c_str());
119}
120
122{
123 EyedropperGreasePencil *eye = MEM_new<EyedropperGreasePencil>(__func__);
124
125 op->customdata = eye;
126 Scene *scene = CTX_data_scene(C);
127
128 const char *display_device;
129 display_device = scene->display_settings.display_device;
130 eye->display = IMB_colormanagement_display_get_named(display_device);
131
132 eye->accum_start = true;
133 eye->mode = EyeMode(RNA_enum_get(op->ptr, "mode"));
134 eye->mat_mode = MaterialMode(RNA_enum_get(op->ptr, "material_mode"));
135 return true;
136}
137
139{
140 /* Clear status message area. */
141 ED_workspace_status_text(C, nullptr);
142
144
145 MEM_delete<EyedropperGreasePencil>(eye);
146 /* Clear pointer. */
147 op->customdata = nullptr;
148}
149
151 const float3 col_conv,
152 const MaterialMode mat_mode)
153{
154 Main *bmain = CTX_data_main(C);
156 Material *ma = nullptr;
157
158 bool found = false;
159
160 /* Look for a similar material in grease pencil slots. */
161 short *totcol = BKE_object_material_len_p(ob);
162 for (short i = 0; i < *totcol; i++) {
163 ma = BKE_object_material_get(ob, i + 1);
164 if (ma == nullptr) {
165 continue;
166 }
167
168 MaterialGPencilStyle *gp_style = ma->gp_style;
169 if (gp_style != nullptr) {
170 /* Check stroke color. */
171 bool found_stroke = compare_v3v3(gp_style->stroke_rgba, col_conv, 0.01f) &&
172 (gp_style->flag & GP_MATERIAL_STROKE_SHOW);
173 /* Check fill color. */
174 bool found_fill = compare_v3v3(gp_style->fill_rgba, col_conv, 0.01f) &&
175 (gp_style->flag & GP_MATERIAL_FILL_SHOW);
176
177 if ((mat_mode == MaterialMode::Stroke) && (found_stroke) &&
178 ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0))
179 {
180 found = true;
181 }
182 else if ((mat_mode == MaterialMode::Fill) && found_fill &&
183 ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0))
184 {
185 found = true;
186 }
187 else if ((mat_mode == MaterialMode::Both) && found_stroke && found_fill) {
188 found = true;
189 }
190
191 /* Found existing material. */
192 if (found) {
193 ob->actcol = i + 1;
196 return;
197 }
198 }
199 }
200
201 /* If material was not found add a new material with stroke and/or fill color
202 * depending of the secondary key (LMB: Stroke, Shift: Fill, Shift+Ctrl: Stroke/Fill)
203 */
204 int idx;
205 Material *ma_new = BKE_grease_pencil_object_material_new(bmain, ob, "Material", &idx);
209
210 BLI_assert(ma_new != nullptr);
211
212 MaterialGPencilStyle *gp_style_new = ma_new->gp_style;
213 BLI_assert(gp_style_new != nullptr);
214
215 /* Only create Stroke (default option). */
216 if (mat_mode == MaterialMode::Stroke) {
217 /* Stroke color. */
218 gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW;
219 gp_style_new->flag &= ~GP_MATERIAL_FILL_SHOW;
220 copy_v3_v3(gp_style_new->stroke_rgba, col_conv);
221 zero_v4(gp_style_new->fill_rgba);
222 }
223 /* Fill Only. */
224 else if (mat_mode == MaterialMode::Fill) {
225 /* Fill color. */
226 gp_style_new->flag &= ~GP_MATERIAL_STROKE_SHOW;
227 gp_style_new->flag |= GP_MATERIAL_FILL_SHOW;
228 zero_v4(gp_style_new->stroke_rgba);
229 copy_v3_v3(gp_style_new->fill_rgba, col_conv);
230 }
231 /* Stroke and Fill. */
232 else if (mat_mode == MaterialMode::Both) {
234 copy_v3_v3(gp_style_new->stroke_rgba, col_conv);
235 copy_v3_v3(gp_style_new->fill_rgba, col_conv);
236 }
237 /* Push undo for new created material. */
238 ED_undo_push(C, "Add Grease Pencil Material");
239}
240
241/* Create a new palette color and palette if needed. */
242static void eyedropper_add_palette_color(bContext *C, const float3 col_conv)
243{
244 Main *bmain = CTX_data_main(C);
245 Scene *scene = CTX_data_scene(C);
246 ToolSettings *ts = scene->toolsettings;
247 GpPaint *gp_paint = ts->gp_paint;
248 GpVertexPaint *gp_vertexpaint = ts->gp_vertexpaint;
249 Paint *paint = &gp_paint->paint;
250 Paint *vertexpaint = &gp_vertexpaint->paint;
251
252 /* Check for Palette in Draw and Vertex Paint Mode. */
253 if (paint->palette == nullptr) {
254 Palette *palette = BKE_palette_add(bmain, "Grease Pencil");
255 id_us_min(&palette->id);
256
257 BKE_paint_palette_set(paint, palette);
258
259 if (vertexpaint->palette == nullptr) {
260 BKE_paint_palette_set(vertexpaint, palette);
261 }
262 }
263
264 /* Check if the color exist already. */
265 Palette *palette = paint->palette;
266 int i;
267 LISTBASE_FOREACH_INDEX (PaletteColor *, palcolor, &palette->colors, i) {
268 if (compare_v3v3(palcolor->rgb, col_conv, 0.01f)) {
269 palette->active_color = i;
270 return;
271 }
272 }
273
274 /* Create Colors. */
275 PaletteColor *palcol = BKE_palette_color_add(palette);
276 if (palcol) {
277 palette->active_color = BLI_listbase_count(&palette->colors) - 1;
278 copy_v3_v3(palcol->rgb, col_conv);
279 }
280}
281
282/* Set the active brush's color. */
283static void eyedropper_set_brush_color(bContext *C, const float3 &col_conv)
284{
285 Scene *scene = CTX_data_scene(C);
286 ToolSettings *ts = scene->toolsettings;
287 Paint *paint = &ts->gp_paint->paint;
288 Brush *brush = BKE_paint_brush(paint);
289 if (brush == nullptr) {
290 return;
291 }
292
293 copy_v3_v3(brush->rgb, col_conv);
295}
296
297/* Set the material or the palette color. */
299 const wmEvent *event,
301{
302 const bool is_ctrl = (event->modifier & KM_CTRL) != 0;
303 const bool is_shift = (event->modifier & KM_SHIFT) != 0;
304
305 MaterialMode mat_mode = eye->mat_mode;
306 if (is_ctrl && !is_shift) {
307 mat_mode = MaterialMode::Stroke;
308 }
309 if (is_shift && !is_ctrl) {
310 mat_mode = MaterialMode::Fill;
311 }
312 if (is_ctrl && is_shift) {
313 mat_mode = MaterialMode::Both;
314 }
315
316 float3 col_conv = eye->color;
317
318 /* Convert from linear rgb space to display space because palette and brush colors are in display
319 * space, and this conversion is needed to undo the conversion to linear performed by
320 * eyedropper_color_sample_fl. */
321 if (eye->display && ELEM(eye->mode, EyeMode::Palette, EyeMode::Brush)) {
323 }
324
325 switch (eye->mode) {
327 eyedropper_add_material(C, col_conv, mat_mode);
328 break;
329 case EyeMode::Palette:
331 break;
332 case EyeMode::Brush:
333 eyedropper_set_brush_color(C, col_conv);
334 break;
335 }
336}
337
338/* Sample the color below cursor. */
341 const int m_xy[2])
342{
343 /* Accumulate color. */
344 float3 col;
345 eyedropper_color_sample_fl(C, nullptr, m_xy, col);
346
347 eye->accum_col += col;
348 eye->accum_tot++;
349
350 eye->color = eye->accum_col;
351 if (eye->accum_tot > 1) {
352 eye->color = eye->accum_col / float(eye->accum_tot);
353 }
354}
355
360
361/* Main modal status check. */
363 wmOperator *op,
364 const wmEvent *event)
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 wmOperator *op,
421 const wmEvent *event)
422{
424 /* Add modal temp handler. */
426 /* Status message. */
428
430 }
432}
433
434/* Repeat operator */
436{
438
439 /* cleanup */
441
442 return OPERATOR_FINISHED;
443 }
445}
446
448{
449 /* Only valid if the current active object is grease pencil. */
451 if ((obact == nullptr) || (obact->type != OB_GREASE_PENCIL)) {
452 return false;
453 }
454
455 /* Test we have a window below. */
456 return (CTX_wm_window(C) != nullptr);
457}
458
459} // namespace blender::ui::greasepencil
460
462{
463 using namespace blender::ui::greasepencil;
464 static const EnumPropertyItem items_mode[] = {
465 {int(EyeMode::Material), "MATERIAL", 0, "Material", ""},
466 {int(EyeMode::Palette), "PALETTE", 0, "Palette", ""},
467 {int(EyeMode::Brush), "BRUSH", 0, "Brush", ""},
468 {0, nullptr, 0, nullptr, nullptr},
469 };
470
471 static const EnumPropertyItem items_material_mode[] = {
472 {int(MaterialMode::Stroke), "STROKE", 0, "Stroke", ""},
473 {int(MaterialMode::Fill), "FILL", 0, "Fill", ""},
474 {int(MaterialMode::Both), "BOTH", 0, "Both", ""},
475 {0, nullptr, 0, nullptr, nullptr},
476 };
477
478 /* Identifiers. */
479 ot->name = "Grease Pencil Eyedropper";
480 ot->idname = "UI_OT_eyedropper_grease_pencil_color";
481 ot->description = "Sample a color from the Blender Window and create Grease Pencil material";
482
483 /* API callbacks. */
489
490 /* Flags. */
492
493 /* Properties. */
494 ot->prop = RNA_def_enum(ot->srna, "mode", items_mode, int(EyeMode::Material), "Mode", "");
495 ot->prop = RNA_def_enum(ot->srna,
496 "material_mode",
497 items_material_mode,
499 "Material Mode",
500 "");
501}
void BKE_brush_tag_unsaved_changes(Brush *brush)
Definition brush.cc:720
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:361
General operations, lookup, etc. for materials.
short * BKE_object_material_len_p(Object *ob)
Material * BKE_object_material_get(Object *ob, short act)
PaletteColor * BKE_palette_color_add(Palette *palette)
Definition paint.cc:1390
Palette * BKE_palette_add(Main *bmain, const char *name)
Definition paint.cc:1384
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:636
void BKE_paint_palette_set(Paint *paint, Palette *palette)
Definition paint.cc:1348
blender::ocio::Display ColorManagedDisplay
Definition BLF_api.hh:35
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
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_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1040
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:99
const ColorManagedDisplay * IMB_colormanagement_display_get_named(const char *name)
void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], const ColorManagedDisplay *display)
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
@ KM_CTRL
Definition WM_types.hh:276
@ KM_SHIFT
Definition WM_types.hh:275
#define ND_DATA
Definition WM_types.hh:506
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
#define NA_EDITED
Definition WM_types.hh:581
#define NC_MATERIAL
Definition WM_types.hh:377
#define NC_GPENCIL
Definition WM_types.hh:396
#define ND_OB_SHADING
Definition WM_types.hh:454
#define ND_SPACE_VIEW3D
Definition WM_types.hh:525
#define NC_OBJECT
Definition WM_types.hh:376
#define ND_SHADING_LINKS
Definition WM_types.hh:476
#define NC_SPACE
Definition WM_types.hh:389
bool 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 bool eyedropper_grease_pencil_poll(bContext *C)
static wmOperatorStatus eyedropper_grease_pencil_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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 wmOperatorStatus eyedropper_grease_pencil_modal(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus eyedropper_grease_pencil_exec(bContext *C, wmOperator *op)
static void eyedropper_grease_pencil_color_set(bContext *C, const wmEvent *event, EyedropperGreasePencil *eye)
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)
float rgb[3]
struct MaterialGPencilStyle * gp_style
struct Palette * palette
ListBase colors
struct ToolSettings * toolsettings
ColorManagedDisplaySettings display_settings
GpVertexPaint * gp_vertexpaint
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
int xy[2]
Definition WM_types.hh:758
struct PointerRNA * ptr
i
Definition text_draw.cc:230
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:4225