Blender  V3.3
gpencil_vertex_ops.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2015 Blender Foundation. */
3 
9 #include "MEM_guardedalloc.h"
10 
11 #include "BLI_blenlib.h"
12 #include "BLI_ghash.h"
13 #include "BLI_math.h"
14 
15 #include "DNA_brush_types.h"
16 #include "DNA_gpencil_types.h"
17 #include "DNA_material_types.h"
18 
19 #include "BKE_context.h"
20 #include "BKE_main.h"
21 #include "BKE_material.h"
22 #include "BKE_paint.h"
23 #include "BKE_report.h"
24 
25 #include "WM_api.h"
26 #include "WM_types.h"
27 
28 #include "RNA_access.h"
29 #include "RNA_define.h"
30 
31 #include "ED_gpencil.h"
32 #include "ED_screen.h"
33 
34 #include "DEG_depsgraph.h"
35 
36 #include "gpencil_intern.h"
37 
39  {GPPAINT_MODE_STROKE, "STROKE", 0, "Stroke", ""},
40  {GPPAINT_MODE_FILL, "FILL", 0, "Fill", ""},
41  {GPPAINT_MODE_BOTH, "BOTH", 0, "Stroke & Fill", ""},
42  {0, NULL, 0, NULL, NULL},
43 };
44 
45 /* Helper: Check if any stroke is selected. */
46 static bool is_any_stroke_selected(bContext *C, const bool is_multiedit, const bool is_curve_edit)
47 {
48  bool is_selected = false;
49 
50  /* If not enabled any mask mode, the strokes are considered as not selected. */
53  return false;
54  }
55 
56  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
57  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
58  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
59  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
60  if (gpf == NULL) {
61  continue;
62  }
63  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
64  /* skip strokes that are invalid for current view */
65  if (ED_gpencil_stroke_can_use(C, gps) == false) {
66  continue;
67  }
68 
69  if (is_curve_edit) {
70  if (gps->editcurve == NULL) {
71  continue;
72  }
73  bGPDcurve *gpc = gps->editcurve;
74  if (gpc->flag & GP_CURVE_SELECT) {
75  is_selected = true;
76  break;
77  }
78  }
79  else {
80  if (gps->flag & GP_STROKE_SELECT) {
81  is_selected = true;
82  break;
83  }
84  }
85  }
86  /* If not multi-edit, exit loop. */
87  if (!is_multiedit) {
88  break;
89  }
90  }
91  }
92  }
94 
95  return is_selected;
96 }
97 
98 /* Poll callback for stroke vertex paint operator. */
100 {
102  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
103  return false;
104  }
105 
106  bGPdata *gpd = (bGPdata *)ob->data;
107  if (GPENCIL_VERTEX_MODE(gpd)) {
108  /* Any data to use. */
109  if (gpd->layers.first) {
110  return true;
111  }
112  }
113 
114  return false;
115 }
116 
118 {
120  bGPdata *gpd = (bGPdata *)ob->data;
121  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
122  const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
123  const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
124 
125  float gain, offset;
126  {
127  float brightness = RNA_float_get(op->ptr, "brightness");
128  float contrast = RNA_float_get(op->ptr, "contrast");
129  brightness /= 100.0f;
130  float delta = contrast / 200.0f;
131  /*
132  * The algorithm is by Werner D. Streidt
133  * (http://visca.com/ffactory/archives/5-99/msg00021.html)
134  * Extracted of OpenCV demhist.c
135  */
136  if (contrast > 0) {
137  gain = 1.0f - delta * 2.0f;
138  gain = 1.0f / max_ff(gain, FLT_EPSILON);
139  offset = gain * (brightness - delta);
140  }
141  else {
142  delta *= -1;
143  gain = max_ff(1.0f - delta * 2.0f, 0.0f);
144  offset = gain * brightness + delta;
145  }
146  }
147 
148  /* Loop all selected strokes. */
149  bool changed = false;
150  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
151  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
152 
153  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
154  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
155  if (gpf == NULL) {
156  continue;
157  }
158 
159  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
160  /* skip strokes that are invalid for current view */
161  if (ED_gpencil_stroke_can_use(C, gps) == false) {
162  continue;
163  }
164 
165  if ((!any_selected) || (gps->flag & GP_STROKE_SELECT)) {
166  /* Fill color. */
167  if (mode != GPPAINT_MODE_STROKE) {
168  if (gps->vert_color_fill[3] > 0.0f) {
169  changed = true;
170  for (int i2 = 0; i2 < 3; i2++) {
171  gps->vert_color_fill[i2] = gain * gps->vert_color_fill[i2] + offset;
172  }
173  }
174  }
175  /* Stroke points. */
176  if (mode != GPPAINT_MODE_FILL) {
177  changed = true;
178  int i;
179  bGPDspoint *pt;
180  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
181  if (((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) &&
182  (pt->vert_color[3] > 0.0f)) {
183  for (int i2 = 0; i2 < 3; i2++) {
184  pt->vert_color[i2] = gain * pt->vert_color[i2] + offset;
185  }
186  }
187  }
188  }
189  }
190  }
191  /* If not multi-edit, exit loop. */
192  if (!is_multiedit) {
193  break;
194  }
195  }
196  }
197  }
198  CTX_DATA_END;
199 
200  /* notifiers */
201  if (changed) {
204  }
205 
206  return OPERATOR_FINISHED;
207 }
208 
210 {
211  PropertyRNA *prop;
212 
213  /* identifiers */
214  ot->name = "Vertex Paint Brightness/Contrast";
215  ot->idname = "GPENCIL_OT_vertex_color_brightness_contrast";
216  ot->description = "Adjust vertex color brightness/contrast";
217 
218  /* api callbacks */
221 
222  /* flags */
224 
225  /* params */
226  ot->prop = RNA_def_enum(
228  const float min = -100, max = +100;
229  prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max);
230  prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max);
231  RNA_def_property_ui_range(prop, min, max, 1, 1);
232 }
233 
235 {
237  bGPdata *gpd = (bGPdata *)ob->data;
238 
239  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
240  const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
241  const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
242  float hue = RNA_float_get(op->ptr, "h");
243  float sat = RNA_float_get(op->ptr, "s");
244  float val = RNA_float_get(op->ptr, "v");
245 
246  bool changed = false;
247  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
248  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
249 
250  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
251  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
252  if (gpf == NULL) {
253  continue;
254  }
255 
256  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
257  /* skip strokes that are invalid for current view */
258  if (ED_gpencil_stroke_can_use(C, gps) == false) {
259  continue;
260  }
261 
262  if ((!any_selected) || (gps->flag & GP_STROKE_SELECT)) {
263  float hsv[3];
264 
265  /* Fill color. */
266  if (mode != GPPAINT_MODE_STROKE) {
267  if (gps->vert_color_fill[3] > 0.0f) {
268  changed = true;
269 
270  rgb_to_hsv_v(gps->vert_color_fill, hsv);
271 
272  hsv[0] += (hue - 0.5f);
273  if (hsv[0] > 1.0f) {
274  hsv[0] -= 1.0f;
275  }
276  else if (hsv[0] < 0.0f) {
277  hsv[0] += 1.0f;
278  }
279  hsv[1] *= sat;
280  hsv[2] *= val;
281 
282  hsv_to_rgb_v(hsv, gps->vert_color_fill);
283  }
284  }
285 
286  /* Stroke points. */
287  if (mode != GPPAINT_MODE_FILL) {
288  changed = true;
289  int i;
290  bGPDspoint *pt;
291  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
292  if (((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) &&
293  (pt->vert_color[3] > 0.0f)) {
294  rgb_to_hsv_v(pt->vert_color, hsv);
295 
296  hsv[0] += (hue - 0.5f);
297  if (hsv[0] > 1.0f) {
298  hsv[0] -= 1.0f;
299  }
300  else if (hsv[0] < 0.0f) {
301  hsv[0] += 1.0f;
302  }
303  hsv[1] *= sat;
304  hsv[2] *= val;
305 
306  hsv_to_rgb_v(hsv, pt->vert_color);
307  }
308  }
309  }
310  }
311  }
312  /* If not multi-edit, exit loop. */
313  if (!is_multiedit) {
314  break;
315  }
316  }
317  }
318  }
319 
320  CTX_DATA_END;
321 
322  /* notifiers */
323  if (changed) {
326  }
327 
328  return OPERATOR_FINISHED;
329 }
330 
332 {
333  /* identifiers */
334  ot->name = "Vertex Paint Hue Saturation Value";
335  ot->idname = "GPENCIL_OT_vertex_color_hsv";
336  ot->description = "Adjust vertex color HSV values";
337 
338  /* api callbacks */
341 
342  /* flags */
344 
345  /* params */
346  ot->prop = RNA_def_enum(
348  RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f);
349  RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f);
350  RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f);
351 }
352 
354 {
356  bGPdata *gpd = (bGPdata *)ob->data;
357 
358  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
359  const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
360  const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
361 
362  bool changed = false;
363  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
364  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
365 
366  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
367  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
368  if (gpf == NULL) {
369  continue;
370  }
371 
372  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
373  /* skip strokes that are invalid for current view */
374  if (ED_gpencil_stroke_can_use(C, gps) == false) {
375  continue;
376  }
377 
378  if ((!any_selected) || (gps->flag & GP_STROKE_SELECT)) {
379  /* Fill color. */
380  if (mode != GPPAINT_MODE_STROKE) {
381  if (gps->vert_color_fill[3] > 0.0f) {
382  changed = true;
383  for (int i2 = 0; i2 < 3; i2++) {
384  gps->vert_color_fill[i2] = 1.0f - gps->vert_color_fill[i2];
385  }
386  }
387  }
388 
389  /* Stroke points. */
390  if (mode != GPPAINT_MODE_FILL) {
391  changed = true;
392  int i;
393  bGPDspoint *pt;
394 
395  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
396  if (((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) &&
397  (pt->vert_color[3] > 0.0f)) {
398  for (int i2 = 0; i2 < 3; i2++) {
399  pt->vert_color[i2] = 1.0f - pt->vert_color[i2];
400  }
401  }
402  }
403  }
404  }
405  }
406  /* If not multi-edit, exit loop. */
407  if (!is_multiedit) {
408  break;
409  }
410  }
411  }
412  }
413  CTX_DATA_END;
414 
415  /* notifiers */
416  if (changed) {
419  }
420 
421  return OPERATOR_FINISHED;
422 }
423 
425 {
426  /* identifiers */
427  ot->name = "Vertex Paint Invert";
428  ot->idname = "GPENCIL_OT_vertex_color_invert";
429  ot->description = "Invert RGB values";
430 
431  /* api callbacks */
434 
435  /* flags */
437 
438  /* params */
439  ot->prop = RNA_def_enum(
441 }
442 
444 {
446  bGPdata *gpd = (bGPdata *)ob->data;
447 
448  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
449  const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
450  const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
451  float gain = RNA_float_get(op->ptr, "gain");
452  float offset = RNA_float_get(op->ptr, "offset");
453 
454  bool changed = false;
455  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
456  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
457 
458  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
459  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
460  if (gpf == NULL) {
461  continue;
462  }
463 
464  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
465  /* skip strokes that are invalid for current view */
466  if (ED_gpencil_stroke_can_use(C, gps) == false) {
467  continue;
468  }
469 
470  if ((!any_selected) || (gps->flag & GP_STROKE_SELECT)) {
471  /* Fill color. */
472  if (mode != GPPAINT_MODE_STROKE) {
473  if (gps->vert_color_fill[3] > 0.0f) {
474  changed = true;
475  for (int i2 = 0; i2 < 3; i2++) {
476  gps->vert_color_fill[i2] = gain * (gps->vert_color_fill[i2] + offset);
477  }
478  }
479  }
480  /* Stroke points. */
481  if (mode != GPPAINT_MODE_FILL) {
482  changed = true;
483  int i;
484  bGPDspoint *pt;
485 
486  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
487  if (((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) &&
488  (pt->vert_color[3] > 0.0f)) {
489  for (int i2 = 0; i2 < 3; i2++) {
490  pt->vert_color[i2] = gain * (pt->vert_color[i2] + offset);
491  }
492  }
493  }
494  }
495  }
496  }
497  /* If not multi-edit, exit loop. */
498  if (!is_multiedit) {
499  break;
500  }
501  }
502  }
503  }
504  CTX_DATA_END;
505 
506  /* notifiers */
507  if (changed) {
510  }
511 
512  return OPERATOR_FINISHED;
513 }
514 
516 {
517 
518  /* identifiers */
519  ot->name = "Vertex Paint Levels";
520  ot->idname = "GPENCIL_OT_vertex_color_levels";
521  ot->description = "Adjust levels of vertex colors";
522 
523  /* api callbacks */
526 
527  /* flags */
529 
530  /* params */
531  ot->prop = RNA_def_enum(
533 
535  ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f);
537  ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f);
538 }
539 
541 {
544  bGPdata *gpd = (bGPdata *)ob->data;
545  Paint *paint = &ts->gp_vertexpaint->paint;
546  Brush *brush = paint->brush;
547 
548  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
549  const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
550  const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
551  float factor = RNA_float_get(op->ptr, "factor");
552 
553  bool changed = false;
554  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
555  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
556 
557  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
558  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
559  if (gpf == NULL) {
560  continue;
561  }
562 
563  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
564  /* skip strokes that are invalid for current view */
565  if (ED_gpencil_stroke_can_use(C, gps) == false) {
566  continue;
567  }
568 
569  if ((!any_selected) || (gps->flag & GP_STROKE_SELECT)) {
570  /* Fill color. */
571  if (mode != GPPAINT_MODE_STROKE) {
572  changed = true;
573  copy_v3_v3(gps->vert_color_fill, brush->rgb);
574  gps->vert_color_fill[3] = factor;
575  srgb_to_linearrgb_v4(gps->vert_color_fill, gps->vert_color_fill);
576  }
577 
578  /* Stroke points. */
579  if (mode != GPPAINT_MODE_FILL) {
580  changed = true;
581  int i;
582  bGPDspoint *pt;
583 
584  float color[4];
585  copy_v3_v3(color, brush->rgb);
586  color[3] = factor;
588  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
589  if ((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) {
591  }
592  }
593  }
594  }
595  }
596  /* If not multi-edit, exit loop. */
597  if (!is_multiedit) {
598  break;
599  }
600  }
601  }
602  }
603  CTX_DATA_END;
604 
605  /* notifiers */
606  if (changed) {
609  }
610 
611  return OPERATOR_FINISHED;
612 }
613 
615 {
616 
617  /* identifiers */
618  ot->name = "Vertex Paint Set Color";
619  ot->idname = "GPENCIL_OT_vertex_color_set";
620  ot->description = "Set active color to all selected vertex";
621 
622  /* api callbacks */
625 
626  /* flags */
628 
629  /* params */
630  ot->prop = RNA_def_enum(
632  RNA_def_float(ot->srna, "factor", 1.0f, 0.001f, 1.0f, "Factor", "Mix Factor", 0.001f, 1.0f);
633 }
634 
635 /* Helper to extract color from vertex color to create a palette. */
637  const bool selected,
638  const int threshold)
639 {
640  Main *bmain = CTX_data_main(C);
642  bool done = false;
643  const float range = pow(10.0f, threshold);
644  float col[3];
645 
646  GHash *color_table = BLI_ghash_int_new(__func__);
647 
648  /* Extract all colors. */
649  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
650  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
651  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
652  if (ED_gpencil_stroke_can_use(C, gps) == false) {
653  continue;
654  }
655  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
656  continue;
657  }
658  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
659  if (gp_style == NULL) {
660  continue;
661  }
662 
663  if ((selected) && ((gps->flag & GP_STROKE_SELECT) == 0)) {
664  continue;
665  }
666 
667  bool use_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW);
668  bool use_fill = (gp_style->flag & GP_MATERIAL_FILL_SHOW);
669 
670  /* Material is disabled. */
671  if ((!use_fill) && (!use_stroke)) {
672  continue;
673  }
674 
675  /* Only solid strokes or stencil. */
676  if ((use_stroke) && ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) &&
677  ((gp_style->flag & GP_MATERIAL_STROKE_PATTERN) == 0))) {
678  continue;
679  }
680 
681  /* Only solid fill. */
682  if ((use_fill) && (gp_style->fill_style != GP_MATERIAL_FILL_STYLE_SOLID)) {
683  continue;
684  }
685 
686  /* Fill color. */
687  if (gps->vert_color_fill[3] > 0.0f) {
688  col[0] = truncf(gps->vert_color_fill[0] * range) / range;
689  col[1] = truncf(gps->vert_color_fill[1] * range) / range;
690  col[2] = truncf(gps->vert_color_fill[2] * range) / range;
691 
692  uint key = rgb_to_cpack(col[0], col[1], col[2]);
693 
694  if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) {
695  BLI_ghash_insert(color_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
696  }
697  }
698 
699  /* Read all points to get all colors. */
700  bGPDspoint *pt;
701  int i;
702  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
703  col[0] = truncf(pt->vert_color[0] * range) / range;
704  col[1] = truncf(pt->vert_color[1] * range) / range;
705  col[2] = truncf(pt->vert_color[2] * range) / range;
706 
707  uint key = rgb_to_cpack(col[0], col[1], col[2]);
708  if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) {
709  BLI_ghash_insert(color_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
710  }
711  }
712  }
713  }
714  }
715  CTX_DATA_END;
716 
717  /* Create the Palette. */
718  done = BKE_palette_from_hash(bmain, color_table, ob->id.name + 2, true);
719 
720  /* Free memory. */
721  BLI_ghash_free(color_table, NULL, NULL);
722 
723  return done;
724 }
725 
726 /* Convert Materials to Vertex Color. */
727 typedef struct GPMatArray {
730  int index;
732 
734  bool use_stroke,
735  bool use_fill,
736  char *name)
737 {
738  uint r_i = 0;
739  if ((use_stroke) && (use_fill)) {
740  switch (gp_style->mode) {
741  case GP_MATERIAL_MODE_LINE: {
742  r_i = 1;
743  strcpy(name, "Line Stroke-Fill");
744  break;
745  }
746  case GP_MATERIAL_MODE_DOT: {
747  r_i = 2;
748  strcpy(name, "Dots Stroke-Fill");
749  break;
750  }
752  r_i = 3;
753  strcpy(name, "Squares Stroke-Fill");
754  break;
755  }
756  default:
757  break;
758  }
759  }
760  else if (use_stroke) {
761  switch (gp_style->mode) {
762  case GP_MATERIAL_MODE_LINE: {
763  r_i = 4;
764  strcpy(name, "Line Stroke");
765  break;
766  }
767  case GP_MATERIAL_MODE_DOT: {
768  r_i = 5;
769  strcpy(name, "Dots Stroke");
770  break;
771  }
773  r_i = 6;
774  strcpy(name, "Squares Stroke");
775  break;
776  }
777  default:
778  break;
779  }
780  }
781  else {
782  r_i = 7;
783  strcpy(name, "Solid Fill");
784  }
785 
786  /* Create key TSSSSFFFF (T: Type S: Stroke Alpha F: Fill Alpha) */
787  r_i *= 1e8;
788  if (use_stroke) {
789  r_i += gp_style->stroke_rgba[3] * 1e7;
790  }
791  if (use_fill) {
792  r_i += gp_style->fill_rgba[3] * 1e3;
793  }
794 
795  return r_i;
796 }
797 
799 {
800  /* only supported with grease pencil objects */
802  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
803  return false;
804  }
805 
806  return true;
807 }
808 
810 {
811  Main *bmain = CTX_data_main(C);
813  bGPdata *gpd = (bGPdata *)ob->data;
814  const bool remove = RNA_boolean_get(op->ptr, "remove");
815  const bool palette = RNA_boolean_get(op->ptr, "palette");
816  const bool selected = RNA_boolean_get(op->ptr, "selected");
817 
818  char name[32] = "";
819  Material *ma = NULL;
820  GPMatArray *mat_elm = NULL;
821 
822  bool changed = false;
823 
824  short *totcol = BKE_object_material_len_p(ob);
825  if (totcol == 0) {
826  return OPERATOR_CANCELLED;
827  }
828 
829  /* These arrays hold all materials and index in the material slots for all combinations. */
830  int totmat = *totcol;
831  GPMatArray *mat_table = MEM_calloc_arrayN(totmat, sizeof(GPMatArray), __func__);
832 
833  /* Update stroke material index. */
834  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
835  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
836  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
837  if (ED_gpencil_stroke_can_use(C, gps) == false) {
838  continue;
839  }
840  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
841  continue;
842  }
843 
844  if ((selected) && ((gps->flag & GP_STROKE_SELECT) == 0)) {
845  continue;
846  }
847 
848  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
849  if (gp_style == NULL) {
850  continue;
851  }
852 
853  bool use_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) &&
854  (gp_style->stroke_rgba[3] > 0.0f));
855  bool use_fill = ((gp_style->flag & GP_MATERIAL_FILL_SHOW) &&
856  (gp_style->fill_rgba[3] > 0.0f));
857  bool is_stencil = ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) &&
858  (gp_style->flag & GP_MATERIAL_STROKE_PATTERN));
859  /* Material is disabled. */
860  if ((!use_fill) && (!use_stroke)) {
861  continue;
862  }
863 
864  /* Only solid strokes or stencil. */
865  if ((use_stroke) && ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) &&
866  ((gp_style->flag & GP_MATERIAL_STROKE_PATTERN) == 0))) {
867  continue;
868  }
869 
870  /* Only solid fill. */
871  if ((use_fill) && (gp_style->fill_style != GP_MATERIAL_FILL_STYLE_SOLID)) {
872  continue;
873  }
874 
875  /* Only for no Stencil materials. */
876  if (!is_stencil) {
877  /* Create material type unique key by type and alpha. */
878  uint key = get_material_type(gp_style, use_stroke, use_fill, name);
879 
880  /* Check if material exist. */
881  bool found = false;
882  int i;
883  for (i = 0; i < totmat; i++) {
884  mat_elm = &mat_table[i];
885  if (mat_elm->ma == NULL) {
886  break;
887  }
888  if (key == mat_elm->key) {
889  found = true;
890  break;
891  }
892  }
893 
894  /* If not found create a new material. */
895  if (!found) {
896  ma = BKE_gpencil_material_add(bmain, name);
897  if (use_stroke) {
899  }
900  else {
902  }
903 
904  if (use_fill) {
906  }
907  else {
909  }
910 
911  ma->gp_style->stroke_rgba[3] = gp_style->stroke_rgba[3];
912  ma->gp_style->fill_rgba[3] = gp_style->fill_rgba[3];
913 
914  BKE_object_material_slot_add(bmain, ob);
916 
917  mat_elm->key = key;
918  mat_elm->ma = ma;
919  mat_elm->index = ob->totcol - 1;
920  }
921  else {
922  mat_elm = &mat_table[i];
923  }
924 
925  /* Update stroke */
926  gps->mat_nr = mat_elm->index;
927  }
928 
929  changed = true;
930  copy_v3_v3(gps->vert_color_fill, gp_style->fill_rgba);
931  gps->vert_color_fill[3] = 1.0f;
932 
933  /* Update all points. */
934  bGPDspoint *pt;
935  int i;
936  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
937  copy_v3_v3(pt->vert_color, gp_style->stroke_rgba);
938  pt->vert_color[3] = 1.0f;
939  }
940  }
941  }
942  }
943  CTX_DATA_END;
944 
945  /* notifiers */
946  if (changed) {
949  }
950 
951  /* Free memory. */
952  MEM_SAFE_FREE(mat_table);
953 
954  /* Generate a Palette. */
955  if (palette) {
957  }
958 
959  /* Clean unused materials. */
960  if (remove) {
962  C, "OBJECT_OT_material_slot_remove_unused", WM_OP_INVOKE_REGION_WIN, NULL, NULL);
963  }
964 
965  return OPERATOR_FINISHED;
966 }
967 
969 {
970  /* identifiers */
971  ot->name = "Convert Stroke Materials to Vertex Color";
972  ot->idname = "GPENCIL_OT_material_to_vertex_color";
973  ot->description = "Replace materials in strokes with Vertex Color";
974 
975  /* api callbacks */
978 
979  /* flags */
981 
982  /* properties */
984  "remove",
985  true,
986  "Remove Unused Materials",
987  "Remove any unused material after the conversion");
988  RNA_def_boolean(ot->srna, "palette", true, "Create Palette", "Create a new palette with colors");
989  RNA_def_boolean(ot->srna, "selected", false, "Only Selected", "Convert only selected strokes");
990  RNA_def_int(ot->srna, "threshold", 3, 1, 4, "Threshold", "", 1, 4);
991 }
992 
993 /* Extract Palette from Vertex Color. */
995 {
996  /* only supported with grease pencil objects */
998  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
999  return false;
1000  }
1001 
1002  return true;
1003 }
1004 
1006 {
1007  const bool selected = RNA_boolean_get(op->ptr, "selected");
1008  const int threshold = RNA_int_get(op->ptr, "threshold");
1009 
1011  BKE_reportf(op->reports, RPT_INFO, "Palette created");
1012  }
1013  else {
1014  BKE_reportf(op->reports, RPT_ERROR, "Unable to find Vertex Information to create palette");
1015  }
1016 
1017  return OPERATOR_FINISHED;
1018 }
1019 
1021 {
1022  /* identifiers */
1023  ot->name = "Extract Palette from Vertex Color";
1024  ot->idname = "GPENCIL_OT_extract_palette_vertex";
1025  ot->description = "Extract all colors used in Grease Pencil Vertex and create a Palette";
1026 
1027  /* api callbacks */
1030 
1031  /* flags */
1033 
1034  /* properties */
1035  ot->prop = RNA_def_boolean(
1036  ot->srna, "selected", false, "Only Selected", "Convert only selected strokes");
1037  RNA_def_int(ot->srna, "threshold", 1, 1, 4, "Threshold", "", 1, 4);
1038 }
1039 
1040 /* -------------------------------------------------------------------- */
1045 {
1046  if (mode != GPPAINT_MODE_STROKE) {
1047  zero_v4(gps->vert_color_fill);
1048  }
1049 
1050  if (mode != GPPAINT_MODE_FILL) {
1051  bGPDspoint *pt;
1052  for (int i = 0; i < gps->totpoints; i++) {
1053  pt = &gps->points[i];
1054  zero_v4(pt->vert_color);
1055  }
1056  }
1057 }
1058 
1060 {
1061  Object *obact = CTX_data_active_object(C);
1062  bGPdata *gpd = (bGPdata *)obact->data;
1063  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1064  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
1065  const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
1066 
1067  /* First need to check if there are something selected. If not, apply to all strokes. */
1068  const bool any_selected = is_any_stroke_selected(C, is_multiedit, is_curve_edit);
1069 
1070  /* Reset Vertex colors. */
1071  bool changed = false;
1072  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
1073  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
1074 
1075  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
1076  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
1077  if (gpf == NULL) {
1078  continue;
1079  }
1080 
1081  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
1082  /* skip strokes that are invalid for current view */
1083  if (ED_gpencil_stroke_can_use(C, gps) == false) {
1084  continue;
1085  }
1086 
1087  if (is_curve_edit) {
1088  if (gps->editcurve == NULL) {
1089  continue;
1090  }
1091  bGPDcurve *gpc = gps->editcurve;
1092  if ((!any_selected) || (gpc->flag & GP_CURVE_SELECT)) {
1093  gpencil_reset_vertex(gps, mode);
1094  }
1095  }
1096  else {
1097  if ((!any_selected) || (gps->flag & GP_STROKE_SELECT)) {
1098  gpencil_reset_vertex(gps, mode);
1099  }
1100  }
1101 
1102  changed = true;
1103  }
1104  /* If not multi-edit, exit loop. */
1105  if (!is_multiedit) {
1106  break;
1107  }
1108  }
1109  }
1110  }
1111  CTX_DATA_END;
1112 
1113  if (changed) {
1114  /* updates */
1115  DEG_id_tag_update(&gpd->id,
1119  }
1120 
1121  return OPERATOR_FINISHED;
1122 }
1123 
1125 {
1126  static EnumPropertyItem mode_types_items[] = {
1127  {GPPAINT_MODE_STROKE, "STROKE", 0, "Stroke", "Reset Vertex Color to Stroke only"},
1128  {GPPAINT_MODE_FILL, "FILL", 0, "Fill", "Reset Vertex Color to Fill only"},
1129  {GPPAINT_MODE_BOTH, "BOTH", 0, "Stroke & Fill", "Reset Vertex Color to Stroke and Fill"},
1130  {0, NULL, 0, NULL, NULL},
1131  };
1132 
1133  /* identifiers */
1134  ot->name = "Reset Vertex Color";
1135  ot->idname = "GPENCIL_OT_stroke_reset_vertex_color";
1136  ot->description = "Reset vertex color for all or selected strokes";
1137 
1138  /* callbacks */
1141 
1142  /* flags */
1144 
1145  /* properties */
1146  ot->prop = RNA_def_enum(ot->srna, "mode", mode_types_items, GPPAINT_MODE_BOTH, "Mode", "");
1147 }
1148 
#define CTX_DATA_BEGIN(C, Type, instance, member)
Definition: BKE_context.h:269
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1353
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
struct ToolSettings * CTX_data_tool_settings(const bContext *C)
Definition: context.c:1282
#define CTX_DATA_END
Definition: BKE_context.h:278
General operations, lookup, etc. for materials.
struct MaterialGPencilStyle * BKE_gpencil_material_settings(struct Object *ob, short act)
Definition: material.c:805
struct Material * BKE_gpencil_material_add(struct Main *bmain, const char *name)
Definition: material.c:298
void BKE_object_material_assign(struct Main *bmain, struct Object *ob, struct Material *ma, short act, int assign_type)
Definition: material.c:1047
bool BKE_object_material_slot_add(struct Main *bmain, struct Object *ob)
Definition: material.c:1232
short * BKE_object_material_len_p(struct Object *ob)
Definition: material.c:344
@ BKE_MAT_ASSIGN_USERPREF
Definition: BKE_material.h:80
bool BKE_palette_from_hash(struct Main *bmain, struct GHash *color_table, const char *name, bool linear)
Definition: paint.c:918
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
bool BLI_ghash_haskey(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:822
GHash * BLI_ghash_int_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition: BLI_ghash.c:710
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:863
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
MINLINE float max_ff(float a, float b)
void hsv_to_rgb_v(const float hsv[3], float r_rgb[3])
Definition: math_color.c:49
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
Definition: math_color.c:232
unsigned int rgb_to_cpack(float r, float g, float b)
Definition: math_color.c:348
MINLINE void srgb_to_linearrgb_v4(float linear[4], const float srgb[4])
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void zero_v4(float r[4])
unsigned int uint
Definition: BLI_sys_types.h:67
#define POINTER_FROM_INT(i)
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_TRANSFORM
Definition: DNA_ID.h:771
@ ID_RECALC_COPY_ON_WRITE
Definition: DNA_ID.h:834
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
eGp_Vertex_Mode
@ GPPAINT_MODE_STROKE
@ GPPAINT_MODE_FILL
@ GPPAINT_MODE_BOTH
#define GPENCIL_ANY_VERTEX_MASK(flag)
#define GPENCIL_VERTEX_MODE(gpd)
@ GP_CURVE_SELECT
@ GP_STROKE_SELECT
#define GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)
@ GP_FRAME_SELECT
#define GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)
@ GP_SPOINT_SELECT
@ GP_MATERIAL_FILL_STYLE_SOLID
@ GP_MATERIAL_STROKE_STYLE_TEXTURE
@ GP_MATERIAL_STROKE_PATTERN
@ GP_MATERIAL_STROKE_SHOW
@ GP_MATERIAL_FILL_SHOW
@ GP_MATERIAL_MODE_SQUARE
@ GP_MATERIAL_MODE_DOT
@ GP_MATERIAL_MODE_LINE
@ OB_GPENCIL
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Sky Generate a procedural sky texture Noise Generate fractal Perlin noise Wave Generate procedural bands or rings with noise Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its hue
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a color
#define C
Definition: RandGen.cpp:25
@ OPTYPE_UNDO
Definition: WM_types.h:148
@ OPTYPE_REGISTER
Definition: WM_types.h:146
#define ND_DATA
Definition: WM_types.h:456
#define NA_EDITED
Definition: WM_types.h:523
#define NC_GPENCIL
Definition: WM_types.h:349
@ WM_OP_INVOKE_REGION_WIN
Definition: WM_types.h:202
bool ED_gpencil_stroke_material_editable(Object *ob, const bGPDlayer *gpl, const bGPDstroke *gps)
bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps)
static int gpencil_material_to_vertex_exec(bContext *C, wmOperator *op)
static bool gpencil_vertexpaint_mode_poll(bContext *C)
static int gpencil_vertexpaint_hsv_exec(bContext *C, wmOperator *op)
static int gpencil_vertexpaint_set_exec(bContext *C, wmOperator *op)
static int gpencil_vertexpaint_brightness_contrast_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem gpencil_modesEnumPropertyItem_mode[]
static int gpencil_stroke_reset_vertex_color_exec(bContext *C, wmOperator *op)
static bool gpencil_extract_palette_vertex_poll(bContext *C)
static int gpencil_vertexpaint_invert_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_stroke_reset_vertex_color(wmOperatorType *ot)
static uint get_material_type(MaterialGPencilStyle *gp_style, bool use_stroke, bool use_fill, char *name)
static int gpencil_vertexpaint_levels_exec(bContext *C, wmOperator *op)
static void gpencil_reset_vertex(bGPDstroke *gps, eGp_Vertex_Mode mode)
void GPENCIL_OT_vertex_color_levels(wmOperatorType *ot)
static bool gpencil_material_to_vertex_poll(bContext *C)
void GPENCIL_OT_vertex_color_invert(wmOperatorType *ot)
void GPENCIL_OT_vertex_color_hsv(wmOperatorType *ot)
void GPENCIL_OT_vertex_color_brightness_contrast(wmOperatorType *ot)
static bool gpencil_extract_palette_from_vertex(bContext *C, const bool selected, const int threshold)
static bool is_any_stroke_selected(bContext *C, const bool is_multiedit, const bool is_curve_edit)
void GPENCIL_OT_vertex_color_set(wmOperatorType *ot)
struct GPMatArray GPMatArray
void GPENCIL_OT_material_to_vertex_color(wmOperatorType *ot)
static int gpencil_extract_palette_vertex_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_extract_palette_vertex(wmOperatorType *ot)
uint col
ccl_gpu_kernel_postfix ccl_global float int int int int float threshold
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void *(* MEM_calloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:32
ccl_device_inline float3 pow(float3 v, float e)
Definition: math_float3.h:533
bool remove(void *owner, const AttributeIDRef &attribute_id)
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4910
float RNA_float_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4957
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, float default_value, float hardmin, float hardmax, const char *ui_name, const char *ui_description, float softmin, float softmax)
Definition: rna_define.c:3836
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3493
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, int default_value, int hardmin, int hardmax, const char *ui_name, const char *ui_description, int softmin, int softmax)
Definition: rna_define.c:3597
void RNA_def_property_ui_range(PropertyRNA *prop, double min, double max, double step, int precision)
Definition: rna_define.c:1664
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, int default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3783
#define min(a, b)
Definition: sort.c:35
char name[66]
Definition: DNA_ID.h:378
void * first
Definition: DNA_listBase.h:31
Definition: BKE_main.h:121
struct MaterialGPencilStyle * gp_style
void * data
char gpencil_selectmode_vertex
GpVertexPaint * gp_vertexpaint
struct bGPDframe * next
float vert_color[4]
bGPDspoint * points
float vert_color_fill[4]
ListBase layers
const char * name
Definition: WM_types.h:888
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:903
PropertyRNA * prop
Definition: WM_types.h:981
struct ReportList * reports
struct PointerRNA * ptr
float max
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
int WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
wmOperatorType * ot
Definition: wm_files.c:3479