Blender  V2.93
gpencil_vertex_ops.c
Go to the documentation of this file.
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) 2015, Blender Foundation
17  * This is a new part of Blender
18  * Brush based operators for editing Grease Pencil strokes
19  */
20 
25 #include "MEM_guardedalloc.h"
26 
27 #include "BLI_blenlib.h"
28 #include "BLI_ghash.h"
29 #include "BLI_math.h"
30 
31 #include "DNA_brush_types.h"
32 #include "DNA_gpencil_types.h"
33 #include "DNA_material_types.h"
34 
35 #include "BKE_context.h"
36 #include "BKE_main.h"
37 #include "BKE_material.h"
38 #include "BKE_paint.h"
39 #include "BKE_report.h"
40 
41 #include "WM_api.h"
42 #include "WM_types.h"
43 
44 #include "RNA_access.h"
45 #include "RNA_define.h"
46 
47 #include "ED_gpencil.h"
48 #include "ED_screen.h"
49 
50 #include "DEG_depsgraph.h"
51 
52 #include "gpencil_intern.h"
53 
55  {GPPAINT_MODE_STROKE, "STROKE", 0, "Stroke", ""},
56  {GPPAINT_MODE_FILL, "FILL", 0, "Fill", ""},
57  {GPPAINT_MODE_BOTH, "BOTH", 0, "Stroke and Fill", ""},
58  {0, NULL, 0, NULL, NULL},
59 };
60 
61 /* Helper: Check if any stroke is selected. */
62 static bool is_any_stroke_selected(bContext *C, const bool is_multiedit, const bool is_curve_edit)
63 {
64  bool is_selected = false;
65 
66  /* If not enabled any mask mode, the strokes are considered as not selected. */
69  return false;
70  }
71 
72  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
73  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
74  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
75  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
76  if (gpf == NULL) {
77  continue;
78  }
79  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
80  /* skip strokes that are invalid for current view */
81  if (ED_gpencil_stroke_can_use(C, gps) == false) {
82  continue;
83  }
84 
85  if (is_curve_edit) {
86  if (gps->editcurve == NULL) {
87  continue;
88  }
89  bGPDcurve *gpc = gps->editcurve;
90  if (gpc->flag & GP_CURVE_SELECT) {
91  is_selected = true;
92  break;
93  }
94  }
95  else {
96  if (gps->flag & GP_STROKE_SELECT) {
97  is_selected = true;
98  break;
99  }
100  }
101  }
102  /* if not multiedit, exit loop*/
103  if (!is_multiedit) {
104  break;
105  }
106  }
107  }
108  }
109  CTX_DATA_END;
110 
111  return is_selected;
112 }
113 
114 /* Poll callback for stroke vertex paint operator. */
116 {
118  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
119  return false;
120  }
121 
122  bGPdata *gpd = (bGPdata *)ob->data;
123  if (GPENCIL_VERTEX_MODE(gpd)) {
124  /* Any data to use. */
125  if (gpd->layers.first) {
126  return true;
127  }
128  }
129 
130  return false;
131 }
132 
134 {
136  bGPdata *gpd = (bGPdata *)ob->data;
137  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
138  const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
139  const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
140 
141  float gain, offset;
142  {
143  float brightness = RNA_float_get(op->ptr, "brightness");
144  float contrast = RNA_float_get(op->ptr, "contrast");
145  brightness /= 100.0f;
146  float delta = contrast / 200.0f;
147  /*
148  * The algorithm is by Werner D. Streidt
149  * (http://visca.com/ffactory/archives/5-99/msg00021.html)
150  * Extracted of OpenCV demhist.c
151  */
152  if (contrast > 0) {
153  gain = 1.0f - delta * 2.0f;
154  gain = 1.0f / max_ff(gain, FLT_EPSILON);
155  offset = gain * (brightness - delta);
156  }
157  else {
158  delta *= -1;
159  gain = max_ff(1.0f - delta * 2.0f, 0.0f);
160  offset = gain * brightness + delta;
161  }
162  }
163 
164  /* Loop all selected strokes. */
165  bool changed = false;
166  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
167  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
168 
169  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
170  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
171  if (gpf == NULL) {
172  continue;
173  }
174 
175  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
176  /* skip strokes that are invalid for current view */
177  if (ED_gpencil_stroke_can_use(C, gps) == false) {
178  continue;
179  }
180 
181  if ((!any_selected) || (gps->flag & GP_STROKE_SELECT)) {
182  /* Fill color. */
183  if (mode != GPPAINT_MODE_STROKE) {
184  if (gps->vert_color_fill[3] > 0.0f) {
185  changed = true;
186  for (int i2 = 0; i2 < 3; i2++) {
187  gps->vert_color_fill[i2] = gain * gps->vert_color_fill[i2] + offset;
188  }
189  }
190  }
191  /* Stroke points. */
192  if (mode != GPPAINT_MODE_FILL) {
193  changed = true;
194  int i;
195  bGPDspoint *pt;
196  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
197  if (((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) &&
198  (pt->vert_color[3] > 0.0f)) {
199  for (int i2 = 0; i2 < 3; i2++) {
200  pt->vert_color[i2] = gain * pt->vert_color[i2] + offset;
201  }
202  }
203  }
204  }
205  }
206  }
207  /* if not multiedit, exit loop*/
208  if (!is_multiedit) {
209  break;
210  }
211  }
212  }
213  }
214  CTX_DATA_END;
215 
216  /* notifiers */
217  if (changed) {
220  }
221 
222  return OPERATOR_FINISHED;
223 }
224 
226 {
227  PropertyRNA *prop;
228 
229  /* identifiers */
230  ot->name = "Vertex Paint Brightness/Contrast";
231  ot->idname = "GPENCIL_OT_vertex_color_brightness_contrast";
232  ot->description = "Adjust vertex color brightness/contrast";
233 
234  /* api callbacks */
237 
238  /* flags */
240 
241  /* params */
242  ot->prop = RNA_def_enum(
244  const float min = -100, max = +100;
245  prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max);
246  prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max);
247  RNA_def_property_ui_range(prop, min, max, 1, 1);
248 }
249 
251 {
253  bGPdata *gpd = (bGPdata *)ob->data;
254 
255  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
256  const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
257  const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
258  float hue = RNA_float_get(op->ptr, "h");
259  float sat = RNA_float_get(op->ptr, "s");
260  float val = RNA_float_get(op->ptr, "v");
261 
262  bool changed = false;
263  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
264  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
265 
266  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
267  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
268  if (gpf == NULL) {
269  continue;
270  }
271 
272  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
273  /* skip strokes that are invalid for current view */
274  if (ED_gpencil_stroke_can_use(C, gps) == false) {
275  continue;
276  }
277 
278  if ((!any_selected) || (gps->flag & GP_STROKE_SELECT)) {
279  float hsv[3];
280 
281  /* Fill color. */
282  if (mode != GPPAINT_MODE_STROKE) {
283  if (gps->vert_color_fill[3] > 0.0f) {
284  changed = true;
285 
286  rgb_to_hsv_v(gps->vert_color_fill, hsv);
287 
288  hsv[0] += (hue - 0.5f);
289  if (hsv[0] > 1.0f) {
290  hsv[0] -= 1.0f;
291  }
292  else if (hsv[0] < 0.0f) {
293  hsv[0] += 1.0f;
294  }
295  hsv[1] *= sat;
296  hsv[2] *= val;
297 
298  hsv_to_rgb_v(hsv, gps->vert_color_fill);
299  }
300  }
301 
302  /* Stroke points. */
303  if (mode != GPPAINT_MODE_FILL) {
304  changed = true;
305  int i;
306  bGPDspoint *pt;
307  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
308  if (((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) &&
309  (pt->vert_color[3] > 0.0f)) {
310  rgb_to_hsv_v(pt->vert_color, hsv);
311 
312  hsv[0] += (hue - 0.5f);
313  if (hsv[0] > 1.0f) {
314  hsv[0] -= 1.0f;
315  }
316  else if (hsv[0] < 0.0f) {
317  hsv[0] += 1.0f;
318  }
319  hsv[1] *= sat;
320  hsv[2] *= val;
321 
322  hsv_to_rgb_v(hsv, pt->vert_color);
323  }
324  }
325  }
326  }
327  }
328  /* if not multiedit, exit loop*/
329  if (!is_multiedit) {
330  break;
331  }
332  }
333  }
334  }
335 
336  CTX_DATA_END;
337 
338  /* notifiers */
339  if (changed) {
342  }
343 
344  return OPERATOR_FINISHED;
345 }
346 
348 {
349  /* identifiers */
350  ot->name = "Vertex Paint Hue Saturation Value";
351  ot->idname = "GPENCIL_OT_vertex_color_hsv";
352  ot->description = "Adjust vertex color HSV values";
353 
354  /* api callbacks */
357 
358  /* flags */
360 
361  /* params */
362  ot->prop = RNA_def_enum(
364  RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f);
365  RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f);
366  RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f);
367 }
368 
370 {
372  bGPdata *gpd = (bGPdata *)ob->data;
373 
374  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
375  const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
376  const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
377 
378  bool changed = false;
379  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
380  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
381 
382  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
383  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
384  if (gpf == NULL) {
385  continue;
386  }
387 
388  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
389  /* skip strokes that are invalid for current view */
390  if (ED_gpencil_stroke_can_use(C, gps) == false) {
391  continue;
392  }
393 
394  if ((!any_selected) || (gps->flag & GP_STROKE_SELECT)) {
395  /* Fill color. */
396  if (mode != GPPAINT_MODE_STROKE) {
397  if (gps->vert_color_fill[3] > 0.0f) {
398  changed = true;
399  for (int i2 = 0; i2 < 3; i2++) {
400  gps->vert_color_fill[i2] = 1.0f - gps->vert_color_fill[i2];
401  }
402  }
403  }
404 
405  /* Stroke points. */
406  if (mode != GPPAINT_MODE_FILL) {
407  changed = true;
408  int i;
409  bGPDspoint *pt;
410 
411  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
412  if (((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) &&
413  (pt->vert_color[3] > 0.0f)) {
414  for (int i2 = 0; i2 < 3; i2++) {
415  pt->vert_color[i2] = 1.0f - pt->vert_color[i2];
416  }
417  }
418  }
419  }
420  }
421  }
422  /* if not multiedit, exit loop*/
423  if (!is_multiedit) {
424  break;
425  }
426  }
427  }
428  }
429  CTX_DATA_END;
430 
431  /* notifiers */
432  if (changed) {
435  }
436 
437  return OPERATOR_FINISHED;
438 }
439 
441 {
442  /* identifiers */
443  ot->name = "Vertex Paint Invert";
444  ot->idname = "GPENCIL_OT_vertex_color_invert";
445  ot->description = "Invert RGB values";
446 
447  /* api callbacks */
450 
451  /* flags */
453 
454  /* params */
455  ot->prop = RNA_def_enum(
457 }
458 
460 {
462  bGPdata *gpd = (bGPdata *)ob->data;
463 
464  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
465  const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
466  const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
467  float gain = RNA_float_get(op->ptr, "gain");
468  float offset = RNA_float_get(op->ptr, "offset");
469 
470  bool changed = false;
471  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
472  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
473 
474  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
475  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
476  if (gpf == NULL) {
477  continue;
478  }
479 
480  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
481  /* skip strokes that are invalid for current view */
482  if (ED_gpencil_stroke_can_use(C, gps) == false) {
483  continue;
484  }
485 
486  if ((!any_selected) || (gps->flag & GP_STROKE_SELECT)) {
487  /* Fill color. */
488  if (mode != GPPAINT_MODE_STROKE) {
489  if (gps->vert_color_fill[3] > 0.0f) {
490  changed = true;
491  for (int i2 = 0; i2 < 3; i2++) {
492  gps->vert_color_fill[i2] = gain * (gps->vert_color_fill[i2] + offset);
493  }
494  }
495  }
496  /* Stroke points. */
497  if (mode != GPPAINT_MODE_FILL) {
498  changed = true;
499  int i;
500  bGPDspoint *pt;
501 
502  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
503  if (((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) &&
504  (pt->vert_color[3] > 0.0f)) {
505  for (int i2 = 0; i2 < 3; i2++) {
506  pt->vert_color[i2] = gain * (pt->vert_color[i2] + offset);
507  }
508  }
509  }
510  }
511  }
512  }
513  /* if not multiedit, exit loop*/
514  if (!is_multiedit) {
515  break;
516  }
517  }
518  }
519  }
520  CTX_DATA_END;
521 
522  /* notifiers */
523  if (changed) {
526  }
527 
528  return OPERATOR_FINISHED;
529 }
530 
532 {
533 
534  /* identifiers */
535  ot->name = "Vertex Paint Levels";
536  ot->idname = "GPENCIL_OT_vertex_color_levels";
537  ot->description = "Adjust levels of vertex colors";
538 
539  /* api callbacks */
542 
543  /* flags */
545 
546  /* params */
547  ot->prop = RNA_def_enum(
549 
551  ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f);
553  ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f);
554 }
555 
557 {
560  bGPdata *gpd = (bGPdata *)ob->data;
561  Paint *paint = &ts->gp_vertexpaint->paint;
562  Brush *brush = paint->brush;
563 
564  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
565  const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
566  const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
567  float factor = RNA_float_get(op->ptr, "factor");
568 
569  bool changed = false;
570  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
571  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
572 
573  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
574  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
575  if (gpf == NULL) {
576  continue;
577  }
578 
579  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
580  /* skip strokes that are invalid for current view */
581  if (ED_gpencil_stroke_can_use(C, gps) == false) {
582  continue;
583  }
584 
585  if ((!any_selected) || (gps->flag & GP_STROKE_SELECT)) {
586  /* Fill color. */
587  if (mode != GPPAINT_MODE_STROKE) {
588  changed = true;
589  copy_v3_v3(gps->vert_color_fill, brush->rgb);
590  gps->vert_color_fill[3] = factor;
591  }
592 
593  /* Stroke points. */
594  if (mode != GPPAINT_MODE_FILL) {
595  changed = true;
596  int i;
597  bGPDspoint *pt;
598 
599  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
600  if ((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) {
601  copy_v3_v3(pt->vert_color, brush->rgb);
602  pt->vert_color[3] = factor;
603  }
604  }
605  }
606  }
607  }
608  /* if not multiedit, exit loop*/
609  if (!is_multiedit) {
610  break;
611  }
612  }
613  }
614  }
615  CTX_DATA_END;
616 
617  /* notifiers */
618  if (changed) {
621  }
622 
623  return OPERATOR_FINISHED;
624 }
625 
627 {
628 
629  /* identifiers */
630  ot->name = "Vertex Paint Set Color";
631  ot->idname = "GPENCIL_OT_vertex_color_set";
632  ot->description = "Set active color to all selected vertex";
633 
634  /* api callbacks */
637 
638  /* flags */
640 
641  /* params */
642  ot->prop = RNA_def_enum(
644  RNA_def_float(ot->srna, "factor", 1.0f, 0.001f, 1.0f, "Factor", "Mix Factor", 0.001f, 1.0f);
645 }
646 
647 /* Helper to extract color from vertex color to create a palette. */
649  const bool selected,
650  const int threshold)
651 {
652  Main *bmain = CTX_data_main(C);
654  bool done = false;
655  const float range = pow(10.0f, threshold);
656  float col[3];
657 
658  GHash *color_table = BLI_ghash_int_new(__func__);
659 
660  /* Extract all colors. */
661  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
662  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
663  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
664  if (ED_gpencil_stroke_can_use(C, gps) == false) {
665  continue;
666  }
667  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
668  continue;
669  }
670  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
671  if (gp_style == NULL) {
672  continue;
673  }
674 
675  if ((selected) && ((gps->flag & GP_STROKE_SELECT) == 0)) {
676  continue;
677  }
678 
679  bool use_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW);
680  bool use_fill = (gp_style->flag & GP_MATERIAL_FILL_SHOW);
681 
682  /* Material is disabled. */
683  if ((!use_fill) && (!use_stroke)) {
684  continue;
685  }
686 
687  /* Only solid strokes or stencil. */
688  if ((use_stroke) && ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) &&
689  ((gp_style->flag & GP_MATERIAL_STROKE_PATTERN) == 0))) {
690  continue;
691  }
692 
693  /* Only solid fill. */
694  if ((use_fill) && (gp_style->fill_style != GP_MATERIAL_FILL_STYLE_SOLID)) {
695  continue;
696  }
697 
698  /* Fill color. */
699  if (gps->vert_color_fill[3] > 0.0f) {
700  col[0] = truncf(gps->vert_color_fill[0] * range) / range;
701  col[1] = truncf(gps->vert_color_fill[1] * range) / range;
702  col[2] = truncf(gps->vert_color_fill[2] * range) / range;
703 
704  uint key = rgb_to_cpack(col[0], col[1], col[2]);
705 
706  if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) {
707  BLI_ghash_insert(color_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
708  }
709  }
710 
711  /* Read all points to get all colors. */
712  bGPDspoint *pt;
713  int i;
714  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
715  col[0] = truncf(pt->vert_color[0] * range) / range;
716  col[1] = truncf(pt->vert_color[1] * range) / range;
717  col[2] = truncf(pt->vert_color[2] * range) / range;
718 
719  uint key = rgb_to_cpack(col[0], col[1], col[2]);
720  if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) {
721  BLI_ghash_insert(color_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
722  }
723  }
724  }
725  }
726  }
727  CTX_DATA_END;
728 
729  /* Create the Palette. */
730  done = BKE_palette_from_hash(bmain, color_table, ob->id.name + 2, true);
731 
732  /* Free memory. */
733  BLI_ghash_free(color_table, NULL, NULL);
734 
735  return done;
736 }
737 
738 /* Convert Materials to Vertex Color. */
739 typedef struct GPMatArray {
742  int index;
744 
746  bool use_stroke,
747  bool use_fill,
748  char *name)
749 {
750  uint r_i = 0;
751  if ((use_stroke) && (use_fill)) {
752  switch (gp_style->mode) {
753  case GP_MATERIAL_MODE_LINE: {
754  r_i = 1;
755  strcpy(name, "Line Stroke-Fill");
756  break;
757  }
758  case GP_MATERIAL_MODE_DOT: {
759  r_i = 2;
760  strcpy(name, "Dots Stroke-Fill");
761  break;
762  }
764  r_i = 3;
765  strcpy(name, "Squares Stroke-Fill");
766  break;
767  }
768  default:
769  break;
770  }
771  }
772  else if (use_stroke) {
773  switch (gp_style->mode) {
774  case GP_MATERIAL_MODE_LINE: {
775  r_i = 4;
776  strcpy(name, "Line Stroke");
777  break;
778  }
779  case GP_MATERIAL_MODE_DOT: {
780  r_i = 5;
781  strcpy(name, "Dots Stroke");
782  break;
783  }
785  r_i = 6;
786  strcpy(name, "Squares Stroke");
787  break;
788  }
789  default:
790  break;
791  }
792  }
793  else {
794  r_i = 7;
795  strcpy(name, "Solid Fill");
796  }
797 
798  /* Create key TSSSSFFFF (T: Type S: Stroke Alpha F: Fill Alpha) */
799  r_i *= 1e8;
800  if (use_stroke) {
801  r_i += gp_style->stroke_rgba[3] * 1e7;
802  }
803  if (use_fill) {
804  r_i += gp_style->fill_rgba[3] * 1e3;
805  }
806 
807  return r_i;
808 }
809 
811 {
812  /* only supported with grease pencil objects */
814  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
815  return false;
816  }
817 
818  return true;
819 }
820 
822 {
823  Main *bmain = CTX_data_main(C);
825  bGPdata *gpd = (bGPdata *)ob->data;
826  const bool remove = RNA_boolean_get(op->ptr, "remove");
827  const bool palette = RNA_boolean_get(op->ptr, "palette");
828  const bool selected = RNA_boolean_get(op->ptr, "selected");
829 
830  char name[32] = "";
831  Material *ma = NULL;
832  GPMatArray *mat_elm = NULL;
833 
834  bool changed = false;
835 
836  short *totcol = BKE_object_material_len_p(ob);
837  if (totcol == 0) {
838  return OPERATOR_CANCELLED;
839  }
840 
841  /* These arrays hold all materials and index in the material slots for all combinations. */
842  int totmat = *totcol;
843  GPMatArray *mat_table = MEM_calloc_arrayN(totmat, sizeof(GPMatArray), __func__);
844 
845  /* Update stroke material index. */
846  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
847  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
848  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
849  if (ED_gpencil_stroke_can_use(C, gps) == false) {
850  continue;
851  }
852  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
853  continue;
854  }
855 
856  if ((selected) && ((gps->flag & GP_STROKE_SELECT) == 0)) {
857  continue;
858  }
859 
860  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
861  if (gp_style == NULL) {
862  continue;
863  }
864 
865  bool use_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) &&
866  (gp_style->stroke_rgba[3] > 0.0f));
867  bool use_fill = ((gp_style->flag & GP_MATERIAL_FILL_SHOW) &&
868  (gp_style->fill_rgba[3] > 0.0f));
869  bool is_stencil = ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) &&
870  (gp_style->flag & GP_MATERIAL_STROKE_PATTERN));
871  /* Material is disabled. */
872  if ((!use_fill) && (!use_stroke)) {
873  continue;
874  }
875 
876  /* Only solid strokes or stencil. */
877  if ((use_stroke) && ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) &&
878  ((gp_style->flag & GP_MATERIAL_STROKE_PATTERN) == 0))) {
879  continue;
880  }
881 
882  /* Only solid fill. */
883  if ((use_fill) && (gp_style->fill_style != GP_MATERIAL_FILL_STYLE_SOLID)) {
884  continue;
885  }
886 
887  /* Only for no Stencil materials. */
888  if (!is_stencil) {
889  /* Create material type unique key by type and alpha. */
890  uint key = get_material_type(gp_style, use_stroke, use_fill, name);
891 
892  /* Check if material exist. */
893  bool found = false;
894  int i;
895  for (i = 0; i < totmat; i++) {
896  mat_elm = &mat_table[i];
897  if (mat_elm->ma == NULL) {
898  break;
899  }
900  if (key == mat_elm->key) {
901  found = true;
902  break;
903  }
904  }
905 
906  /* If not found create a new material. */
907  if (!found) {
908  ma = BKE_gpencil_material_add(bmain, name);
909  if (use_stroke) {
911  }
912  else {
914  }
915 
916  if (use_fill) {
918  }
919  else {
921  }
922 
923  ma->gp_style->stroke_rgba[3] = gp_style->stroke_rgba[3];
924  ma->gp_style->fill_rgba[3] = gp_style->fill_rgba[3];
925 
926  BKE_object_material_slot_add(bmain, ob);
928 
929  mat_elm->key = key;
930  mat_elm->ma = ma;
931  mat_elm->index = ob->totcol - 1;
932  }
933  else {
934  mat_elm = &mat_table[i];
935  }
936 
937  /* Update stroke */
938  gps->mat_nr = mat_elm->index;
939  }
940 
941  changed = true;
942  copy_v3_v3(gps->vert_color_fill, gp_style->fill_rgba);
943  gps->vert_color_fill[3] = 1.0f;
944 
945  /* Update all points. */
946  bGPDspoint *pt;
947  int i;
948  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
949  copy_v3_v3(pt->vert_color, gp_style->stroke_rgba);
950  pt->vert_color[3] = 1.0f;
951  }
952  }
953  }
954  }
955  CTX_DATA_END;
956 
957  /* notifiers */
958  if (changed) {
961  }
962 
963  /* Free memory. */
964  MEM_SAFE_FREE(mat_table);
965 
966  /* Generate a Palette. */
967  if (palette) {
969  }
970 
971  /* Clean unused materials. */
972  if (remove) {
974  C, "OBJECT_OT_material_slot_remove_unused", WM_OP_INVOKE_REGION_WIN, NULL);
975  }
976 
977  return OPERATOR_FINISHED;
978 }
979 
981 {
982  /* identifiers */
983  ot->name = "Convert Stroke Materials to Vertex Color";
984  ot->idname = "GPENCIL_OT_material_to_vertex_color";
985  ot->description = "Replace materials in strokes with Vertex Color";
986 
987  /* api callbacks */
990 
991  /* flags */
993 
994  /* properties */
996  "remove",
997  true,
998  "Remove Unused Materials",
999  "Remove any unused material after the conversion");
1000  RNA_def_boolean(ot->srna, "palette", true, "Create Palette", "Create a new palette with colors");
1001  RNA_def_boolean(ot->srna, "selected", false, "Only Selected", "Convert only selected strokes");
1002  RNA_def_int(ot->srna, "threshold", 3, 1, 4, "Threshold", "", 1, 4);
1003 }
1004 
1005 /* Extract Palette from Vertex Color. */
1007 {
1008  /* only supported with grease pencil objects */
1010  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
1011  return false;
1012  }
1013 
1014  return true;
1015 }
1016 
1018 {
1019  const bool selected = RNA_boolean_get(op->ptr, "selected");
1020  const int threshold = RNA_int_get(op->ptr, "threshold");
1021 
1022  if (gpencil_extract_palette_from_vertex(C, selected, threshold)) {
1023  BKE_reportf(op->reports, RPT_INFO, "Palette created");
1024  }
1025  else {
1026  BKE_reportf(op->reports, RPT_ERROR, "Unable to find Vertex Information to create palette");
1027  }
1028 
1029  return OPERATOR_FINISHED;
1030 }
1031 
1033 {
1034  /* identifiers */
1035  ot->name = "Extract Palette from Vertex Color";
1036  ot->idname = "GPENCIL_OT_extract_palette_vertex";
1037  ot->description = "Extract all colors used in Grease Pencil Vertex and create a Palette";
1038 
1039  /* api callbacks */
1042 
1043  /* flags */
1045 
1046  /* properties */
1047  ot->prop = RNA_def_boolean(
1048  ot->srna, "selected", false, "Only Selected", "Convert only selected strokes");
1049  RNA_def_int(ot->srna, "threshold", 1, 1, 4, "Threshold", "", 1, 4);
1050 }
1051 
1052 /* -------------------------------------------------------------------- */
1057 {
1058  if (mode != GPPAINT_MODE_STROKE) {
1059  zero_v4(gps->vert_color_fill);
1060  }
1061 
1062  if (mode != GPPAINT_MODE_FILL) {
1063  bGPDspoint *pt;
1064  for (int i = 0; i < gps->totpoints; i++) {
1065  pt = &gps->points[i];
1066  zero_v4(pt->vert_color);
1067  }
1068  }
1069 }
1070 
1072 {
1073  Object *obact = CTX_data_active_object(C);
1074  bGPdata *gpd = (bGPdata *)obact->data;
1075  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1076  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
1077  const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
1078 
1079  /* First need to check if there are something selected. If not, apply to all strokes. */
1080  const bool any_selected = is_any_stroke_selected(C, is_multiedit, is_curve_edit);
1081 
1082  /* Reset Vertex colors. */
1083  bool changed = false;
1084  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
1085  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
1086 
1087  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
1088  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
1089  if (gpf == NULL) {
1090  continue;
1091  }
1092 
1093  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
1094  /* skip strokes that are invalid for current view */
1095  if (ED_gpencil_stroke_can_use(C, gps) == false) {
1096  continue;
1097  }
1098 
1099  if (is_curve_edit) {
1100  if (gps->editcurve == NULL) {
1101  continue;
1102  }
1103  bGPDcurve *gpc = gps->editcurve;
1104  if ((!any_selected) || (gpc->flag & GP_CURVE_SELECT)) {
1105  gpencil_reset_vertex(gps, mode);
1106  }
1107  }
1108  else {
1109  if ((!any_selected) || (gps->flag & GP_STROKE_SELECT)) {
1110  gpencil_reset_vertex(gps, mode);
1111  }
1112  }
1113 
1114  changed = true;
1115  }
1116  /* if not multiedit, exit loop*/
1117  if (!is_multiedit) {
1118  break;
1119  }
1120  }
1121  }
1122  }
1123  CTX_DATA_END;
1124 
1125  if (changed) {
1126  /* updates */
1127  DEG_id_tag_update(&gpd->id,
1131  }
1132 
1133  return OPERATOR_FINISHED;
1134 }
1135 
1137 {
1138  static EnumPropertyItem mode_types_items[] = {
1139  {GPPAINT_MODE_STROKE, "STROKE", 0, "Stroke", "Reset Vertex Color to Stroke only"},
1140  {GPPAINT_MODE_FILL, "FILL", 0, "Fill", "Reset Vertex Color to Fill only"},
1141  {GPPAINT_MODE_BOTH, "BOTH", 0, "Stroke and Fill", "Reset Vertex Color to Stroke and Fill"},
1142  {0, NULL, 0, NULL, NULL},
1143  };
1144 
1145  /* identifiers */
1146  ot->name = "Reset Vertex Color";
1147  ot->idname = "GPENCIL_OT_stroke_reset_vertex_color";
1148  ot->description = "Reset vertex color for all or selected strokes";
1149 
1150  /* callbacks */
1153 
1154  /* flags */
1156 
1157  /* properties */
1158  ot->prop = RNA_def_enum(ot->srna, "mode", mode_types_items, GPPAINT_MODE_BOTH, "Mode", "");
1159 }
1160 
#define CTX_DATA_BEGIN(C, Type, instance, member)
Definition: BKE_context.h:252
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1279
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1018
struct ToolSettings * CTX_data_tool_settings(const bContext *C)
Definition: context.c:1208
#define CTX_DATA_END
Definition: BKE_context.h:260
General operations, lookup, etc. for materials.
struct MaterialGPencilStyle * BKE_gpencil_material_settings(struct Object *ob, short act)
Definition: material.c:713
@ BKE_MAT_ASSIGN_USERPREF
Definition: BKE_material.h:71
struct Material * BKE_gpencil_material_add(struct Main *bmain, const char *name)
Definition: material.c:310
void BKE_object_material_assign(struct Main *bmain, struct Object *ob, struct Material *ma, short act, int assign_type)
Definition: material.c:850
bool BKE_object_material_slot_add(struct Main *bmain, struct Object *ob)
Definition: material.c:1091
short * BKE_object_material_len_p(struct Object *ob)
Definition: material.c:356
bool BKE_palette_from_hash(struct Main *bmain, struct GHash *color_table, const char *name, const bool linear)
Definition: paint.c:905
void BKE_reportf(ReportList *reports, ReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
bool BLI_ghash_haskey(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:941
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:756
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:1008
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
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:68
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
Definition: math_color.c:254
unsigned int rgb_to_cpack(float r, float g, float b)
Definition: math_color.c:379
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:83
#define POINTER_FROM_INT(i)
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_TRANSFORM
Definition: DNA_ID.h:599
@ ID_RECALC_COPY_ON_WRITE
Definition: DNA_ID.h:654
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:611
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_STROKE_PATTERN
@ GP_MATERIAL_STROKE_SHOW
@ GP_MATERIAL_FILL_SHOW
@ GP_MATERIAL_MODE_SQUARE
@ GP_MATERIAL_MODE_DOT
@ GP_MATERIAL_MODE_LINE
@ GP_MATERIAL_STROKE_STYLE_TEXTURE
@ GP_MATERIAL_FILL_STYLE_SOLID
@ OB_GPENCIL
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define C
Definition: RandGen.cpp:39
@ OPTYPE_UNDO
Definition: WM_types.h:155
@ OPTYPE_REGISTER
Definition: WM_types.h:153
#define ND_DATA
Definition: WM_types.h:408
@ WM_OP_INVOKE_REGION_WIN
Definition: WM_types.h:198
#define NA_EDITED
Definition: WM_types.h:462
#define NC_GPENCIL
Definition: WM_types.h:300
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
void *(* MEM_calloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:46
INLINE Rall1d< T, V, S > pow(const Rall1d< T, V, S > &arg, double m)
Definition: rall1d.h:359
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6308
float RNA_float_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6355
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6261
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6402
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:3825
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3481
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:3585
void RNA_def_property_ui_range(PropertyRNA *prop, double min, double max, double step, int precision)
Definition: rna_define.c:1706
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:3771
#define min(a, b)
Definition: sort.c:51
char name[66]
Definition: DNA_ID.h:283
void * first
Definition: DNA_listBase.h:47
Definition: BKE_main.h:116
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:721
const char * idname
Definition: WM_types.h:723
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:776
struct StructRNA * srna
Definition: WM_types.h:802
const char * description
Definition: WM_types.h:726
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:736
PropertyRNA * prop
Definition: WM_types.h:814
struct ReportList * reports
struct PointerRNA * ptr
float max
int WM_operator_name_call(bContext *C, const char *opstring, short context, PointerRNA *properties)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition: wm_files.c:3156