Blender V5.0
grease_pencil/intern/grease_pencil_vertex_paint.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
8
9#include "BLI_math_color.h"
10
11#include "BKE_brush.hh"
12#include "BKE_context.hh"
13#include "BKE_curves.hh"
14#include "BKE_grease_pencil.hh"
15#include "BKE_paint.hh"
16
17#include "DEG_depsgraph.hh"
18
19#include "RNA_access.hh"
20#include "RNA_define.hh"
21
22#include "ED_curves.hh"
23#include "ED_grease_pencil.hh"
24
26
27enum class VertexColorMode : int8_t {
28 Stroke = 0,
29 Fill = 1,
30 Both = 2,
31};
32
34 {int(VertexColorMode::Stroke), "STROKE", 0, "Stroke", ""},
35 {int(VertexColorMode::Fill), "FILL", 0, "Fill", ""},
36 {int(VertexColorMode::Both), "BOTH", 0, "Stroke & Fill", ""},
37 {0, nullptr, 0, nullptr, nullptr},
38};
39
40template<typename Fn>
42 Object &object,
44 const bool use_selection_mask,
45 Fn &&fn)
46{
47 bool changed = false;
48 IndexMaskMemory memory;
49 using namespace ed::greasepencil;
51 if (info.drawing.strokes().attributes().contains("vertex_color")) {
52 const IndexMask points = use_selection_mask ?
54 object, info.drawing, info.layer_index, memory) :
56 object, info.drawing, info.layer_index, memory);
57 if (!points.is_empty()) {
59 points.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
60 ColorGeometry4f &color = vertex_colors[point_i];
61 if (color.a > 0.0f) {
62 color = fn(color);
63 }
64 });
65 changed = true;
66 }
67 }
68 }
70 if (info.drawing.strokes().attributes().contains("fill_color")) {
71 const IndexMask strokes = use_selection_mask ?
73 object, info.drawing, info.layer_index, memory) :
75 object, info.drawing, info.layer_index, memory);
76 if (!strokes.is_empty()) {
78 strokes.foreach_index(GrainSize(1024), [&](const int64_t curve_i) {
79 ColorGeometry4f &color = fill_colors[curve_i];
80 if (color.a > 0.0f) {
81 color = fn(color);
82 }
83 });
84 changed = true;
85 }
86 }
87 }
88 return changed;
89}
90
92 wmOperator *op)
93{
94 const Scene &scene = *CTX_data_scene(C);
96 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
97 const VertexColorMode mode = VertexColorMode(RNA_enum_get(op->ptr, "mode"));
98 const float brightness = RNA_float_get(op->ptr, "brightness");
99 const float contrast = RNA_float_get(op->ptr, "contrast");
100 float delta = contrast / 2.0f;
101 const bool use_selection_mask = ED_grease_pencil_any_vertex_mask_selection(scene.toolsettings);
102
103 /*
104 * The algorithm is by Werner D. Streidt
105 * (http://visca.com/ffactory/archives/5-99/msg00021.html)
106 * Extracted of OpenCV `demhist.c`.
107 */
108 float gain, offset;
109 if (contrast > 0.0f) {
110 gain = 1.0f - delta * 2.0f;
111 gain = 1.0f / math::max(gain, FLT_EPSILON);
112 offset = gain * (brightness - delta);
113 }
114 else {
115 delta *= -1.0f;
116 gain = math::max(1.0f - delta * 2.0f, 0.0f);
117 offset = gain * brightness + delta;
118 }
119
120 std::atomic<bool> any_changed;
121 Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
124 if (curves.is_empty()) {
125 return;
126 }
127 const bool changed = apply_color_operation_for_mode(
128 mode,
129 object,
130 info,
131 use_selection_mask,
132 [&](const ColorGeometry4f &color) -> ColorGeometry4f {
133 const float3 result = float3(color) * gain + offset;
134 return ColorGeometry4f(result[0], result[1], result[2], color.a);
135 });
136 any_changed.store(any_changed | changed, std::memory_order_relaxed);
137 });
138
139 if (any_changed) {
140 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
141 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
142 }
143 return OPERATOR_FINISHED;
144}
145
147{
148 /* identifiers */
149 ot->name = "Vertex Paint Brightness/Contrast";
150 ot->idname = "GREASE_PENCIL_OT_vertex_color_brightness_contrast";
151 ot->description = "Adjust vertex color brightness/contrast";
152
153 /* API callbacks. */
156
157 /* flags */
159
160 /* params */
161 ot->prop = RNA_def_enum(
162 ot->srna, "mode", prop_grease_pencil_vertex_mode, int(VertexColorMode::Both), "Mode", "");
163
164 RNA_def_float(ot->srna, "brightness", 0.0f, -1.0f, 1.0f, "Brightness", "", -1.0f, 1.0f);
165 RNA_def_float(ot->srna, "contrast", 0.0f, -1.0f, 1.0f, "Contrast", "", -1.0f, 1.0f);
166}
167
169{
170 const Scene &scene = *CTX_data_scene(C);
171 Object &object = *CTX_data_active_object(C);
172 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
173 const VertexColorMode mode = VertexColorMode(RNA_enum_get(op->ptr, "mode"));
174 const float hue = RNA_float_get(op->ptr, "h");
175 const float sat = RNA_float_get(op->ptr, "s");
176 const float val = RNA_float_get(op->ptr, "v");
177 const bool use_selection_mask = ED_grease_pencil_any_vertex_mask_selection(scene.toolsettings);
178
179 std::atomic<bool> any_changed;
180 Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
183 if (curves.is_empty()) {
184 return;
185 }
186 const bool changed = apply_color_operation_for_mode(
187 mode,
188 object,
189 info,
190 use_selection_mask,
191 [&](const ColorGeometry4f &color) -> ColorGeometry4f {
192 float3 hsv;
194
195 hsv[0] += (hue - 0.5f);
196 if (hsv[0] > 1.0f) {
197 hsv[0] -= 1.0f;
198 }
199 else if (hsv[0] < 0.0f) {
200 hsv[0] += 1.0f;
201 }
202 hsv[1] *= sat;
203 hsv[2] *= val;
204
205 float3 rgb_result;
206 hsv_to_rgb_v(hsv, rgb_result);
207 return ColorGeometry4f(rgb_result[0], rgb_result[1], rgb_result[2], color.a);
208 });
209 any_changed.store(any_changed | changed, std::memory_order_relaxed);
210 });
211
212 if (any_changed) {
213 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
214 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
215 }
216 return OPERATOR_FINISHED;
217}
218
220{
221 /* identifiers */
222 ot->name = "Vertex Paint Hue/Saturation/Value";
223 ot->idname = "GREASE_PENCIL_OT_vertex_color_hsv";
224 ot->description = "Adjust vertex color HSV values";
225
226 /* API callbacks. */
229
230 /* flags */
232
233 /* params */
234 ot->prop = RNA_def_enum(
235 ot->srna, "mode", prop_grease_pencil_vertex_mode, int(VertexColorMode::Both), "Mode", "");
236 RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f);
237 RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f);
238 RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f);
239}
240
242{
243 const Scene &scene = *CTX_data_scene(C);
244 Object &object = *CTX_data_active_object(C);
245 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
246 const VertexColorMode mode = VertexColorMode(RNA_enum_get(op->ptr, "mode"));
247 const bool use_selection_mask = ED_grease_pencil_any_vertex_mask_selection(scene.toolsettings);
248
249 std::atomic<bool> any_changed;
250 Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
253 if (curves.is_empty()) {
254 return;
255 }
256 const bool changed = apply_color_operation_for_mode(
257 mode,
258 object,
259 info,
260 use_selection_mask,
261 [&](const ColorGeometry4f &color) -> ColorGeometry4f {
262 /* Invert the color. */
263 return ColorGeometry4f(1.0f - color.r, 1.0f - color.g, 1.0f - color.b, color.a);
264 });
265 any_changed.store(any_changed | changed, std::memory_order_relaxed);
266 });
267
268 if (any_changed) {
269 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
270 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
271 }
272 return OPERATOR_FINISHED;
273}
274
276{
277 /* identifiers */
278 ot->name = "Vertex Paint Invert";
279 ot->idname = "GREASE_PENCIL_OT_vertex_color_invert";
280 ot->description = "Invert RGB values";
281
282 /* API callbacks. */
285
286 /* flags */
288
289 /* params */
290 ot->prop = RNA_def_enum(
291 ot->srna, "mode", prop_grease_pencil_vertex_mode, int(VertexColorMode::Both), "Mode", "");
292}
293
295{
296 const Scene &scene = *CTX_data_scene(C);
297 Object &object = *CTX_data_active_object(C);
298 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
299 const VertexColorMode mode = VertexColorMode(RNA_enum_get(op->ptr, "mode"));
300 const float gain = RNA_float_get(op->ptr, "gain");
301 const float offset = RNA_float_get(op->ptr, "offset");
302 const bool use_selection_mask = ED_grease_pencil_any_vertex_mask_selection(scene.toolsettings);
303
304 std::atomic<bool> any_changed;
305 Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
308 if (curves.is_empty()) {
309 return;
310 }
311 const bool changed = apply_color_operation_for_mode(
312 mode,
313 object,
314 info,
315 use_selection_mask,
316 [&](const ColorGeometry4f &color) -> ColorGeometry4f {
317 const float3 result = float3(color) * gain + offset;
318 return ColorGeometry4f(result[0], result[1], result[2], color.a);
319 });
320 any_changed.store(any_changed | changed, std::memory_order_relaxed);
321 });
322
323 if (any_changed) {
324 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
325 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
326 }
327 return OPERATOR_FINISHED;
328}
329
331{
332 /* identifiers */
333 ot->name = "Vertex Paint Levels";
334 ot->idname = "GREASE_PENCIL_OT_vertex_color_levels";
335 ot->description = "Adjust levels of vertex colors";
336
337 /* API callbacks. */
340
341 /* flags */
343
344 /* params */
345 ot->prop = RNA_def_enum(
346 ot->srna, "mode", prop_grease_pencil_vertex_mode, int(VertexColorMode::Both), "Mode", "");
347
349 ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f);
351 ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f);
352}
353
355{
356 const Scene &scene = *CTX_data_scene(C);
357 Object &object = *CTX_data_active_object(C);
359 const Brush &brush = *BKE_paint_brush(&paint);
360 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
361 const VertexColorMode mode = VertexColorMode(RNA_enum_get(op->ptr, "mode"));
362 const float factor = RNA_float_get(op->ptr, "factor");
363 const bool use_selection_mask = ED_grease_pencil_any_vertex_mask_selection(scene.toolsettings);
364
365 float3 color_linear = BKE_brush_color_get(&paint, &brush);
366 const ColorGeometry4f target_color(color_linear[0], color_linear[1], color_linear[2], 1.0f);
367
368 std::atomic<bool> any_changed;
369 Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
372 if (curves.is_empty()) {
373 return;
374 }
375 /* Create the color attributes if they don't exist. */
377 curves.attributes_for_write().add<ColorGeometry4f>(
379 }
381 curves.attributes_for_write().add<ColorGeometry4f>(
383 }
384 const bool changed = apply_color_operation_for_mode(
385 mode,
386 object,
387 info,
388 use_selection_mask,
389 [&](const ColorGeometry4f &color) -> ColorGeometry4f {
390 /* Mix in the target color based on the factor. */
391 return math::interpolate(color, target_color, factor);
392 });
393 any_changed.store(any_changed | changed, std::memory_order_relaxed);
394 });
395
396 if (any_changed) {
397 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
398 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
399 }
400 return OPERATOR_FINISHED;
401}
402
404{
405 /* identifiers */
406 ot->name = "Vertex Paint Set Color";
407 ot->idname = "GREASE_PENCIL_OT_vertex_color_set";
408 ot->description = "Set active color to all selected vertex";
409
410 /* API callbacks. */
413
414 /* flags */
416
417 /* params */
418 ot->prop = RNA_def_enum(
419 ot->srna, "mode", prop_grease_pencil_vertex_mode, int(VertexColorMode::Both), "Mode", "");
420 RNA_def_float(ot->srna, "factor", 1.0f, 0.0f, 1.0f, "Factor", "Mix Factor", 0.0f, 1.0f);
421}
422
424{
425 const Scene &scene = *CTX_data_scene(C);
426 Object &object = *CTX_data_active_object(C);
427 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
428 const VertexColorMode mode = VertexColorMode(RNA_enum_get(op->ptr, "mode"));
429 const bool use_selection_mask = ED_grease_pencil_any_vertex_mask_selection(scene.toolsettings);
430
431 std::atomic<bool> any_changed;
432 Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
435 if (curves.is_empty()) {
436 return;
437 }
438
439 bool changed = false;
440 if (use_selection_mask) {
442 mode,
443 object,
444 info,
445 use_selection_mask,
446 [&](const ColorGeometry4f & /*color*/) -> ColorGeometry4f {
447 return ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f);
448 });
449 any_changed.store(any_changed | changed, std::memory_order_relaxed);
450 return;
451 }
452
453 /* Remove the color attributes. */
455 changed |= curves.attributes_for_write().remove("vertex_color");
456 }
458 changed |= curves.attributes_for_write().remove("fill_color");
459 }
460 any_changed.store(any_changed | changed, std::memory_order_relaxed);
461 });
462
463 if (any_changed) {
464 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
465 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
466 }
467 return OPERATOR_FINISHED;
468}
469
471{
472 /* identifiers */
473 ot->name = "Reset Vertex Color";
474 ot->idname = "GREASE_PENCIL_OT_stroke_reset_vertex_color";
475 ot->description = "Reset vertex color for all or selected strokes";
476
477 /* callbacks */
480
481 /* flags */
483
484 /* properties */
485 ot->prop = RNA_def_enum(
486 ot->srna, "mode", prop_grease_pencil_vertex_mode, int(VertexColorMode::Both), "Mode", "");
487}
488
489} // namespace blender::ed::greasepencil
490
const float * BKE_brush_color_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1161
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Low-level operations for curves.
Low-level operations for grease pencil.
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:476
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:645
void hsv_to_rgb_v(const float hsv[3], float r_rgb[3])
Definition math_color.cc:57
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ OPERATOR_FINISHED
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
long long int int64_t
bool contains(StringRef attribute_id) const
AttributeAccessor attributes() const
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
MutableSpan< ColorGeometry4f > fill_colors_for_write()
MutableSpan< ColorGeometry4f > vertex_colors_for_write()
void foreach_index(Fn &&fn) const
bool ED_grease_pencil_any_vertex_mask_selection(const ToolSettings *tool_settings)
IndexMask retrieve_editable_and_selected_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
IndexMask retrieve_editable_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static void GREASE_PENCIL_OT_vertex_color_levels(wmOperatorType *ot)
bool grease_pencil_vertex_painting_poll(bContext *C)
static wmOperatorStatus grease_pencil_vertex_paint_set_exec(bContext *C, wmOperator *op)
static wmOperatorStatus grease_pencil_vertex_paint_invert_exec(bContext *C, wmOperator *op)
static wmOperatorStatus grease_pencil_vertex_paint_hsv_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_stroke_reset_vertex_color(wmOperatorType *ot)
static wmOperatorStatus grease_pencil_vertex_paint_brightness_contrast_exec(bContext *C, wmOperator *op)
IndexMask retrieve_editable_and_selected_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static void GREASE_PENCIL_OT_vertex_color_brightness_contrast(wmOperatorType *ot)
static void GREASE_PENCIL_OT_vertex_color_set(wmOperatorType *ot)
static wmOperatorStatus grease_pencil_vertex_paint_reset_exec(bContext *C, wmOperator *op)
static wmOperatorStatus grease_pencil_vertex_paint_levels_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_vertex_color_invert(wmOperatorType *ot)
static void GREASE_PENCIL_OT_vertex_color_hsv(wmOperatorType *ot)
static bool apply_color_operation_for_mode(const VertexColorMode mode, Object &object, MutableDrawingInfo &info, const bool use_selection_mask, Fn &&fn)
IndexMask retrieve_editable_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
Vector< MutableDrawingInfo > retrieve_editable_drawings(const Scene &scene, GreasePencil &grease_pencil)
T interpolate(const T &a, const T &b, const FactorT &t)
T max(const T &a, const T &b)
void parallel_for_each(Range &&range, const Function &function)
Definition BLI_task.hh:56
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
VecBase< float, 3 > float3
float RNA_float_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
#define FLT_MAX
Definition stdcycles.h:14
struct ToolSettings * toolsettings
struct PointerRNA * ptr
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))