Fix T70143: GPencil Multiframe selection is not updated when use Box select in Dopesheet
[blender.git] / source / blender / editors / gpencil / drawgpencil.c
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) 2008, Blender Foundation
17  * This is a new part of Blender
18  */
19
20 /** \file
21  * \ingroup edgpencil
22  */
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <stddef.h>
28 #include <math.h>
29 #include <float.h>
30
31 #include "MEM_guardedalloc.h"
32
33 #include "BLI_sys_types.h"
34
35 #include "BLI_math.h"
36 #include "BLI_utildefines.h"
37 #include "BLI_polyfill_2d.h"
38
39 #include "BLF_api.h"
40 #include "BLT_translation.h"
41
42 #include "DNA_brush_types.h"
43 #include "DNA_gpencil_types.h"
44 #include "DNA_scene_types.h"
45 #include "DNA_screen_types.h"
46 #include "DNA_space_types.h"
47 #include "DNA_view3d_types.h"
48 #include "DNA_userdef_types.h"
49 #include "DNA_object_types.h"
50
51 #include "BKE_context.h"
52 #include "BKE_brush.h"
53 #include "BKE_global.h"
54 #include "BKE_paint.h"
55 #include "BKE_gpencil.h"
56 #include "BKE_image.h"
57
58 #include "DEG_depsgraph.h"
59
60 #include "WM_api.h"
61
62 #include "BIF_glutil.h"
63
64 #include "GPU_immediate.h"
65 #include "GPU_state.h"
66
67 #include "ED_gpencil.h"
68 #include "ED_screen.h"
69 #include "ED_view3d.h"
70 #include "ED_space_api.h"
71
72 #include "UI_interface_icons.h"
73 #include "UI_resources.h"
74
75 #include "IMB_imbuf_types.h"
76
77 #include "gpencil_intern.h"
78
79 /* ************************************************** */
80 /* GREASE PENCIL DRAWING */
81
82 /* ----- General Defines ------ */
83 /* flags for sflag */
84 typedef enum eDrawStrokeFlags {
85   /** don't draw status info */
86   GP_DRAWDATA_NOSTATUS = (1 << 0),
87   /** only draw 3d-strokes */
88   GP_DRAWDATA_ONLY3D = (1 << 1),
89   /** only draw 'canvas' strokes */
90   GP_DRAWDATA_ONLYV2D = (1 << 2),
91   /** only draw 'image' strokes */
92   GP_DRAWDATA_ONLYI2D = (1 << 3),
93   /** special hack for drawing strokes in Image Editor (weird coordinates) */
94   GP_DRAWDATA_IEDITHACK = (1 << 4),
95   /** don't draw xray in 3D view (which is default) */
96   GP_DRAWDATA_NO_XRAY = (1 << 5),
97   /** no onionskins should be drawn (for animation playback) */
98   GP_DRAWDATA_NO_ONIONS = (1 << 6),
99   /** draw strokes as "volumetric" circular billboards */
100   GP_DRAWDATA_VOLUMETRIC = (1 << 7),
101   /** fill insides/bounded-regions of strokes */
102   GP_DRAWDATA_FILL = (1 << 8),
103 } eDrawStrokeFlags;
104
105 /* thickness above which we should use special drawing */
106 #if 0
107 #  define GP_DRAWTHICKNESS_SPECIAL 3
108 #endif
109
110 /* conversion utility (float --> normalized unsigned byte) */
111 #define F2UB(x) (uchar)(255.0f * x)
112
113 /* ----- Tool Buffer Drawing ------ */
114 /* helper functions to set color of buffer point */
115
116 static void gp_set_point_uniform_color(const bGPDspoint *pt, const float ink[4])
117 {
118   float alpha = ink[3] * pt->strength;
119   CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f);
120   immUniformColor3fvAlpha(ink, alpha);
121 }
122
123 static void gp_set_point_varying_color(const bGPDspoint *pt,
124                                        const float ink[4],
125                                        uint attr_id,
126                                        bool fix_strength)
127 {
128   float alpha = ink[3] * pt->strength;
129   if ((fix_strength) && (alpha >= 0.1f)) {
130     alpha = 1.0f;
131   }
132   CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f);
133   immAttr4ub(attr_id, F2UB(ink[0]), F2UB(ink[1]), F2UB(ink[2]), F2UB(alpha));
134 }
135
136 /* --------- 2D Stroke Drawing Helpers --------- */
137 /* change in parameter list */
138 static void gp_calc_2d_stroke_fxy(
139     const float pt[3], short sflag, int offsx, int offsy, int winx, int winy, float r_co[2])
140 {
141   if (sflag & GP_STROKE_2DSPACE) {
142     r_co[0] = pt[0];
143     r_co[1] = pt[1];
144   }
145   else if (sflag & GP_STROKE_2DIMAGE) {
146     const float x = (float)((pt[0] * winx) + offsx);
147     const float y = (float)((pt[1] * winy) + offsy);
148
149     r_co[0] = x;
150     r_co[1] = y;
151   }
152   else {
153     const float x = (float)(pt[0] / 100 * winx) + offsx;
154     const float y = (float)(pt[1] / 100 * winy) + offsy;
155
156     r_co[0] = x;
157     r_co[1] = y;
158   }
159 }
160 /* ----------- Volumetric Strokes --------------- */
161
162 /* draw a 2D strokes in "volumetric" style */
163 static void gp_draw_stroke_volumetric_2d(const bGPDspoint *points,
164                                          int totpoints,
165                                          short thickness,
166                                          short UNUSED(dflag),
167                                          short sflag,
168                                          int offsx,
169                                          int offsy,
170                                          int winx,
171                                          int winy,
172                                          const float diff_mat[4][4],
173                                          const float ink[4])
174 {
175   GPUVertFormat *format = immVertexFormat();
176   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
177   uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
178   uint color = GPU_vertformat_attr_add(
179       format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
180
181   immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR);
182   GPU_program_point_size(true);
183   immBegin(GPU_PRIM_POINTS, totpoints);
184
185   const bGPDspoint *pt = points;
186   for (int i = 0; i < totpoints; i++, pt++) {
187     /* transform position to 2D */
188     float co[2];
189     float fpt[3];
190
191     mul_v3_m4v3(fpt, diff_mat, &pt->x);
192     gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co);
193
194     gp_set_point_varying_color(pt, ink, color, false);
195     immAttr1f(size, pt->pressure * thickness); /* TODO: scale based on view transform */
196     immVertex2f(pos, co[0], co[1]);
197   }
198
199   immEnd();
200   immUnbindProgram();
201   GPU_program_point_size(false);
202 }
203
204 /* draw a 3D stroke in "volumetric" style */
205 static void gp_draw_stroke_volumetric_3d(const bGPDspoint *points,
206                                          int totpoints,
207                                          short thickness,
208                                          const float ink[4])
209 {
210   GPUVertFormat *format = immVertexFormat();
211   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
212   uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
213   uint color = GPU_vertformat_attr_add(
214       format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
215
216   immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR);
217   GPU_program_point_size(true);
218   immBegin(GPU_PRIM_POINTS, totpoints);
219
220   const bGPDspoint *pt = points;
221   for (int i = 0; i < totpoints && pt; i++, pt++) {
222     gp_set_point_varying_color(pt, ink, color, false);
223     /* TODO: scale based on view transform */
224     immAttr1f(size, pt->pressure * thickness);
225     /* we can adjust size in vertex shader based on view/projection! */
226     immVertex3fv(pos, &pt->x);
227   }
228
229   immEnd();
230   immUnbindProgram();
231   GPU_program_point_size(false);
232 }
233
234 /* --------------- Stroke Fills ----------------- */
235 /* calc bounding box in 2d using flat projection data */
236 static void gp_calc_2d_bounding_box(
237     const float (*points2d)[2], int totpoints, float minv[2], float maxv[2], bool expand)
238 {
239   copy_v2_v2(minv, points2d[0]);
240   copy_v2_v2(maxv, points2d[0]);
241
242   for (int i = 1; i < totpoints; i++) {
243     /* min */
244     if (points2d[i][0] < minv[0]) {
245       minv[0] = points2d[i][0];
246     }
247     if (points2d[i][1] < minv[1]) {
248       minv[1] = points2d[i][1];
249     }
250     /* max */
251     if (points2d[i][0] > maxv[0]) {
252       maxv[0] = points2d[i][0];
253     }
254     if (points2d[i][1] > maxv[1]) {
255       maxv[1] = points2d[i][1];
256     }
257   }
258   /* If not expanded, use a perfect square */
259   if (expand == false) {
260     if (maxv[0] > maxv[1]) {
261       maxv[1] = maxv[0];
262     }
263     else {
264       maxv[0] = maxv[1];
265     }
266   }
267 }
268
269 /* calc texture coordinates using flat projected points */
270 static void gp_calc_stroke_text_coordinates(const float (*points2d)[2],
271                                             int totpoints,
272                                             const float minv[2],
273                                             float maxv[2],
274                                             float (*r_uv)[2])
275 {
276   float d[2];
277   d[0] = maxv[0] - minv[0];
278   d[1] = maxv[1] - minv[1];
279   for (int i = 0; i < totpoints; i++) {
280     r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0];
281     r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1];
282   }
283 }
284
285 /* Triangulate stroke for high quality fill
286  * (this is done only if cache is null or stroke was modified). */
287 static void gp_triangulate_stroke_fill(bGPDstroke *gps)
288 {
289   BLI_assert(gps->totpoints >= 3);
290
291   /* allocate memory for temporary areas */
292   gps->tot_triangles = gps->totpoints - 2;
293   uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles,
294                                         "GP Stroke temp triangulation");
295   float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints,
296                                     "GP Stroke temp 2d points");
297   float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data");
298
299   int direction = 0;
300
301   /* convert to 2d and triangulate */
302   BKE_gpencil_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction);
303   BLI_polyfill_calc(points2d, (uint)gps->totpoints, direction, tmp_triangles);
304
305   /* calc texture coordinates automatically */
306   float minv[2];
307   float maxv[2];
308   /* first needs bounding box data */
309   gp_calc_2d_bounding_box((const float(*)[2])points2d, gps->totpoints, minv, maxv, false);
310   /* calc uv data */
311   gp_calc_stroke_text_coordinates((const float(*)[2])points2d, gps->totpoints, minv, maxv, uv);
312
313   /* Number of triangles */
314   gps->tot_triangles = gps->totpoints - 2;
315   /* save triangulation data in stroke cache */
316   if (gps->tot_triangles > 0) {
317     if (gps->triangles == NULL) {
318       gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles,
319                                    "GP Stroke triangulation");
320     }
321     else {
322       gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles);
323     }
324
325     for (int i = 0; i < gps->tot_triangles; i++) {
326       bGPDtriangle *stroke_triangle = &gps->triangles[i];
327       memcpy(stroke_triangle->verts, tmp_triangles[i], sizeof(uint[3]));
328       /* copy texture coordinates */
329       copy_v2_v2(stroke_triangle->uv[0], uv[tmp_triangles[i][0]]);
330       copy_v2_v2(stroke_triangle->uv[1], uv[tmp_triangles[i][1]]);
331       copy_v2_v2(stroke_triangle->uv[2], uv[tmp_triangles[i][2]]);
332     }
333   }
334   else {
335     /* No triangles needed - Free anything allocated previously */
336     if (gps->triangles) {
337       MEM_freeN(gps->triangles);
338     }
339
340     gps->triangles = NULL;
341   }
342
343   /* disable recalculation flag */
344   if (gps->flag & GP_STROKE_RECALC_GEOMETRY) {
345     gps->flag &= ~GP_STROKE_RECALC_GEOMETRY;
346   }
347
348   /* clear memory */
349   MEM_SAFE_FREE(tmp_triangles);
350   MEM_SAFE_FREE(points2d);
351   MEM_SAFE_FREE(uv);
352 }
353
354 /* add a new fill point and texture coordinates to vertex buffer */
355 static void gp_add_filldata_tobuffer(const bGPDspoint *pt,
356                                      const float uv[2],
357                                      uint pos,
358                                      uint texcoord,
359                                      short flag,
360                                      int offsx,
361                                      int offsy,
362                                      int winx,
363                                      int winy,
364                                      const float diff_mat[4][4])
365 {
366   float fpt[3];
367   float co[2];
368
369   mul_v3_m4v3(fpt, diff_mat, &pt->x);
370   /* if 2d, need conversion */
371   if (!(flag & GP_STROKE_3DSPACE)) {
372     gp_calc_2d_stroke_fxy(fpt, flag, offsx, offsy, winx, winy, co);
373     copy_v2_v2(fpt, co);
374     fpt[2] = 0.0f; /* 2d always is z=0.0f */
375   }
376
377   immAttr2f(texcoord, uv[0], uv[1]); /* texture coordinates */
378   immVertex3fv(pos, fpt);            /* position */
379 }
380
381 #if 0 /* GPXX disabled, not used in annotations */
382 /* assign image texture for filling stroke */
383 static int gp_set_filling_texture(Image *image, short flag)
384 {
385   ImBuf *ibuf;
386   uint *bind = &image->bindcode[TEXTARGET_TEXTURE_2D];
387   int error = GL_NO_ERROR;
388   ImageUser iuser = {NULL};
389   void *lock;
390
391   iuser.ok = true;
392
393   ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
394
395   if (ibuf == NULL || ibuf->rect == NULL) {
396     BKE_image_release_ibuf(image, ibuf, NULL);
397     return (int)GL_INVALID_OPERATION;
398   }
399
400   GPU_create_gl_tex(
401       bind, ibuf->rect, ibuf->rect_float, ibuf->x, ibuf->y, GL_TEXTURE_2D, false, false, image);
402
403   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
404   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
405   if (flag & GP_STYLE_COLOR_TEX_CLAMP) {
406     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
407     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
408   }
409   else {
410     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
411     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
412   }
413   BKE_image_release_ibuf(image, ibuf, NULL);
414
415   return error;
416 }
417 #endif
418
419 /* draw fills for shapes */
420 static void gp_draw_stroke_fill(bGPdata *gpd,
421                                 bGPDstroke *gps,
422                                 int offsx,
423                                 int offsy,
424                                 int winx,
425                                 int winy,
426                                 const float diff_mat[4][4],
427                                 const float color[4])
428 {
429   BLI_assert(gps->totpoints >= 3);
430   Material *ma = gpd->mat[gps->mat_nr];
431   MaterialGPencilStyle *gp_style = (ma) ? ma->gp_style : NULL;
432
433   /* Calculate triangles cache for filling area (must be done only after changes) */
434   if ((gps->flag & GP_STROKE_RECALC_GEOMETRY) || (gps->tot_triangles == 0) ||
435       (gps->triangles == NULL)) {
436     gp_triangulate_stroke_fill(gps);
437   }
438   BLI_assert(gps->tot_triangles >= 1);
439
440   GPUVertFormat *format = immVertexFormat();
441   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
442   uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
443   immBindBuiltinProgram(GPU_SHADER_GPENCIL_FILL);
444
445   immUniformColor4fv(color);
446   immUniform4fv("color2", gp_style->mix_rgba);
447   immUniform1i("fill_type", gp_style->fill_style);
448   immUniform1f("mix_factor", gp_style->mix_factor);
449
450   immUniform1f("gradient_angle", gp_style->gradient_angle);
451   immUniform1f("gradient_radius", gp_style->gradient_radius);
452   immUniform1f("pattern_gridsize", gp_style->pattern_gridsize);
453   immUniform2fv("gradient_scale", gp_style->gradient_scale);
454   immUniform2fv("gradient_shift", gp_style->gradient_shift);
455
456   immUniform1f("texture_angle", gp_style->texture_angle);
457   immUniform2fv("texture_scale", gp_style->texture_scale);
458   immUniform2fv("texture_offset", gp_style->texture_offset);
459   immUniform1f("texture_opacity", gp_style->texture_opacity);
460   immUniform1i("t_mix", (gp_style->flag & GP_STYLE_FILL_TEX_MIX) != 0);
461   immUniform1i("t_flip", (gp_style->flag & GP_STYLE_COLOR_FLIP_FILL) != 0);
462 #if 0 /* GPXX disabled, not used in annotations */
463   /* image texture */
464   if ((gp_style->fill_style == GP_STYLE_FILL_STYLE_TEXTURE) ||
465       (gp_style->flag & GP_STYLE_COLOR_TEX_MIX)) {
466     gp_set_filling_texture(gp_style->ima, gp_style->flag);
467   }
468 #endif
469   /* Draw all triangles for filling the polygon (cache must be calculated before) */
470   immBegin(GPU_PRIM_TRIS, gps->tot_triangles * 3);
471   /* TODO: use batch instead of immediate mode, to share vertices */
472
473   const bGPDtriangle *stroke_triangle = gps->triangles;
474   for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) {
475     for (int j = 0; j < 3; j++) {
476       gp_add_filldata_tobuffer(&gps->points[stroke_triangle->verts[j]],
477                                stroke_triangle->uv[j],
478                                pos,
479                                texcoord,
480                                gps->flag,
481                                offsx,
482                                offsy,
483                                winx,
484                                winy,
485                                diff_mat);
486     }
487   }
488
489   immEnd();
490   immUnbindProgram();
491 }
492
493 /* ----- Existing Strokes Drawing (3D and Point) ------ */
494
495 /* draw a given stroke - just a single dot (only one point) */
496 static void gp_draw_stroke_point(const bGPDspoint *points,
497                                  short thickness,
498                                  short UNUSED(dflag),
499                                  short sflag,
500                                  int offsx,
501                                  int offsy,
502                                  int winx,
503                                  int winy,
504                                  const float diff_mat[4][4],
505                                  const float ink[4])
506 {
507   const bGPDspoint *pt = points;
508
509   /* get final position using parent matrix */
510   float fpt[3];
511   mul_v3_m4v3(fpt, diff_mat, &pt->x);
512
513   GPUVertFormat *format = immVertexFormat();
514   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
515
516   if (sflag & GP_STROKE_3DSPACE) {
517     immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
518   }
519   else {
520     immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
521
522     /* get 2D coordinates of point */
523     float co[3] = {0.0f};
524     gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co);
525     copy_v3_v3(fpt, co);
526   }
527
528   gp_set_point_uniform_color(pt, ink);
529   /* set point thickness (since there's only one of these) */
530   immUniform1f("size", (float)(thickness + 2) * pt->pressure);
531
532   immBegin(GPU_PRIM_POINTS, 1);
533   immVertex3fv(pos, fpt);
534   immEnd();
535
536   immUnbindProgram();
537 }
538
539 /* draw a given stroke in 3d (i.e. in 3d-space) */
540 static void gp_draw_stroke_3d(tGPDdraw *tgpw, short thickness, const float ink[4], bool cyclic)
541 {
542   bGPDspoint *points = tgpw->gps->points;
543   int totpoints = tgpw->gps->totpoints;
544
545   const float viewport[2] = {(float)tgpw->winx, (float)tgpw->winy};
546   float curpressure = points[0].pressure;
547   float fpt[3];
548
549   /* if cyclic needs more vertex */
550   int cyclic_add = (cyclic) ? 1 : 0;
551
552   GPUVertFormat *format = immVertexFormat();
553   const struct {
554     uint pos, color, thickness;
555   } attr_id = {
556       .pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT),
557       .color = GPU_vertformat_attr_add(
558           format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT),
559       .thickness = GPU_vertformat_attr_add(format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT),
560   };
561
562   immBindBuiltinProgram(GPU_SHADER_GPENCIL_STROKE);
563   immUniform2fv("Viewport", viewport);
564   immUniform1f("pixsize", tgpw->rv3d->pixsize);
565   float obj_scale = tgpw->ob ?
566                         (tgpw->ob->scale[0] + tgpw->ob->scale[1] + tgpw->ob->scale[2]) / 3.0f :
567                         1.0f;
568
569   immUniform1f("objscale", obj_scale);
570   int keep_size = (int)((tgpw->gpd) && (tgpw->gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS));
571   immUniform1i("keep_size", keep_size);
572   immUniform1f("pixfactor", tgpw->gpd->pixfactor);
573   /* xray mode always to 3D space to avoid wrong zdepth calculation (T60051) */
574   immUniform1i("xraymode", GP_XRAY_3DSPACE);
575   immUniform1i("caps_start", (int)tgpw->gps->caps[0]);
576   immUniform1i("caps_end", (int)tgpw->gps->caps[1]);
577   immUniform1i("fill_stroke", (int)tgpw->is_fill_stroke);
578
579   /* draw stroke curve */
580   GPU_line_width(max_ff(curpressure * thickness, 1.0f));
581   immBeginAtMost(GPU_PRIM_LINE_STRIP_ADJ, totpoints + cyclic_add + 2);
582   const bGPDspoint *pt = points;
583
584   for (int i = 0; i < totpoints; i++, pt++) {
585     /* first point for adjacency (not drawn) */
586     if (i == 0) {
587       gp_set_point_varying_color(points, ink, attr_id.color, (bool)tgpw->is_fill_stroke);
588
589       if ((cyclic) && (totpoints > 2)) {
590         immAttr1f(attr_id.thickness, max_ff((points + totpoints - 1)->pressure * thickness, 1.0f));
591         mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 1)->x);
592       }
593       else {
594         immAttr1f(attr_id.thickness, max_ff((points + 1)->pressure * thickness, 1.0f));
595         mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x);
596       }
597       immVertex3fv(attr_id.pos, fpt);
598     }
599     /* set point */
600     gp_set_point_varying_color(pt, ink, attr_id.color, (bool)tgpw->is_fill_stroke);
601     immAttr1f(attr_id.thickness, max_ff(pt->pressure * thickness, 1.0f));
602     mul_v3_m4v3(fpt, tgpw->diff_mat, &pt->x);
603     immVertex3fv(attr_id.pos, fpt);
604   }
605
606   if (cyclic && totpoints > 2) {
607     /* draw line to first point to complete the cycle */
608     immAttr1f(attr_id.thickness, max_ff(points->pressure * thickness, 1.0f));
609     mul_v3_m4v3(fpt, tgpw->diff_mat, &points->x);
610     immVertex3fv(attr_id.pos, fpt);
611
612     /* now add adjacency point (not drawn) */
613     immAttr1f(attr_id.thickness, max_ff((points + 1)->pressure * thickness, 1.0f));
614     mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x);
615     immVertex3fv(attr_id.pos, fpt);
616   }
617   /* last adjacency point (not drawn) */
618   else {
619     gp_set_point_varying_color(
620         points + totpoints - 2, ink, attr_id.color, (bool)tgpw->is_fill_stroke);
621
622     immAttr1f(attr_id.thickness, max_ff((points + totpoints - 2)->pressure * thickness, 1.0f));
623     mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 2)->x);
624     immVertex3fv(attr_id.pos, fpt);
625   }
626
627   immEnd();
628   immUnbindProgram();
629 }
630
631 /* ----- Fancy 2D-Stroke Drawing ------ */
632
633 /* draw a given stroke in 2d */
634 static void gp_draw_stroke_2d(const bGPDspoint *points,
635                               int totpoints,
636                               short thickness_s,
637                               short dflag,
638                               short sflag,
639                               bool UNUSED(debug),
640                               int offsx,
641                               int offsy,
642                               int winx,
643                               int winy,
644                               const float diff_mat[4][4],
645                               const float ink[4])
646 {
647   /* otherwise thickness is twice that of the 3D view */
648   float thickness = (float)thickness_s * 0.5f;
649
650   /* strokes in Image Editor need a scale factor, since units there are not pixels! */
651   float scalefac = 1.0f;
652   if ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) {
653     scalefac = 0.001f;
654   }
655
656   /* TODO: fancy++ with the magic of shaders */
657
658   /* tessellation code - draw stroke as series of connected quads (triangle strips in fact)
659    * with connection edges rotated to minimize shrinking artifacts, and rounded endcaps.
660    */
661   {
662     const bGPDspoint *pt1, *pt2;
663     float s0[2], s1[2]; /* segment 'center' points */
664     float pm[2];        /* normal from previous segment. */
665     int i;
666     float fpt[3];
667
668     GPUVertFormat *format = immVertexFormat();
669     const struct {
670       uint pos, color;
671     } attr_id = {
672         .pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT),
673         .color = GPU_vertformat_attr_add(
674             format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT),
675     };
676
677     immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
678     immBegin(GPU_PRIM_TRI_STRIP, totpoints * 2 + 4);
679
680     /* get x and y coordinates from first point */
681     mul_v3_m4v3(fpt, diff_mat, &points->x);
682     gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0);
683
684     for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) {
685       float t0[2], t1[2]; /* tessellated coordinates */
686       float m1[2], m2[2]; /* gradient and normal */
687       float mt[2], sc[2]; /* gradient for thickness, point for end-cap */
688       float pthick;       /* thickness at segment point */
689
690       /* Get x and y coordinates from point2
691        * (point1 has already been computed in previous iteration). */
692       mul_v3_m4v3(fpt, diff_mat, &pt2->x);
693       gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s1);
694
695       /* calculate gradient and normal - 'angle'=(ny/nx) */
696       m1[1] = s1[1] - s0[1];
697       m1[0] = s1[0] - s0[0];
698       normalize_v2(m1);
699       m2[1] = -m1[0];
700       m2[0] = m1[1];
701
702       /* always use pressure from first point here */
703       pthick = (pt1->pressure * thickness * scalefac);
704
705       /* color of point */
706       gp_set_point_varying_color(pt1, ink, attr_id.color, false);
707
708       /* if the first segment, start of segment is segment's normal */
709       if (i == 0) {
710         /* draw start cap first
711          * - make points slightly closer to center (about halfway across)
712          */
713         mt[0] = m2[0] * pthick * 0.5f;
714         mt[1] = m2[1] * pthick * 0.5f;
715         sc[0] = s0[0] - (m1[0] * pthick * 0.75f);
716         sc[1] = s0[1] - (m1[1] * pthick * 0.75f);
717
718         t0[0] = sc[0] - mt[0];
719         t0[1] = sc[1] - mt[1];
720         t1[0] = sc[0] + mt[0];
721         t1[1] = sc[1] + mt[1];
722
723         /* First two points of cap. */
724         immVertex2fv(attr_id.pos, t0);
725         immVertex2fv(attr_id.pos, t1);
726
727         /* calculate points for start of segment */
728         mt[0] = m2[0] * pthick;
729         mt[1] = m2[1] * pthick;
730
731         t0[0] = s0[0] - mt[0];
732         t0[1] = s0[1] - mt[1];
733         t1[0] = s0[0] + mt[0];
734         t1[1] = s0[1] + mt[1];
735
736         /* Last two points of start cap (and first two points of first segment). */
737         immVertex2fv(attr_id.pos, t0);
738         immVertex2fv(attr_id.pos, t1);
739       }
740       /* if not the first segment, use bisector of angle between segments */
741       else {
742         float mb[2];        /* bisector normal */
743         float athick, dfac; /* actual thickness, difference between thicknesses */
744
745         /* calculate gradient of bisector (as average of normals) */
746         mb[0] = (pm[0] + m2[0]) / 2;
747         mb[1] = (pm[1] + m2[1]) / 2;
748         normalize_v2(mb);
749
750         /* calculate gradient to apply
751          * - as basis, use just pthick * bisector gradient
752          * - if cross-section not as thick as it should be, add extra padding to fix it
753          */
754         mt[0] = mb[0] * pthick;
755         mt[1] = mb[1] * pthick;
756         athick = len_v2(mt);
757         dfac = pthick - (athick * 2);
758
759         if (((athick * 2.0f) < pthick) && (IS_EQF(athick, pthick) == 0)) {
760           mt[0] += (mb[0] * dfac);
761           mt[1] += (mb[1] * dfac);
762         }
763
764         /* calculate points for start of segment */
765         t0[0] = s0[0] - mt[0];
766         t0[1] = s0[1] - mt[1];
767         t1[0] = s0[0] + mt[0];
768         t1[1] = s0[1] + mt[1];
769
770         /* Last two points of previous segment, and first two points of current segment. */
771         immVertex2fv(attr_id.pos, t0);
772         immVertex2fv(attr_id.pos, t1);
773       }
774
775       /* if last segment, also draw end of segment (defined as segment's normal) */
776       if (i == totpoints - 2) {
777         /* for once, we use second point's pressure (otherwise it won't be drawn) */
778         pthick = (pt2->pressure * thickness * scalefac);
779
780         /* color of point */
781         gp_set_point_varying_color(pt2, ink, attr_id.color, false);
782
783         /* calculate points for end of segment */
784         mt[0] = m2[0] * pthick;
785         mt[1] = m2[1] * pthick;
786
787         t0[0] = s1[0] - mt[0];
788         t0[1] = s1[1] - mt[1];
789         t1[0] = s1[0] + mt[0];
790         t1[1] = s1[1] + mt[1];
791
792         /* Last two points of last segment (and first two points of end cap). */
793         immVertex2fv(attr_id.pos, t0);
794         immVertex2fv(attr_id.pos, t1);
795
796         /* draw end cap as last step
797          * - make points slightly closer to center (about halfway across)
798          */
799         mt[0] = m2[0] * pthick * 0.5f;
800         mt[1] = m2[1] * pthick * 0.5f;
801         sc[0] = s1[0] + (m1[0] * pthick * 0.75f);
802         sc[1] = s1[1] + (m1[1] * pthick * 0.75f);
803
804         t0[0] = sc[0] - mt[0];
805         t0[1] = sc[1] - mt[1];
806         t1[0] = sc[0] + mt[0];
807         t1[1] = sc[1] + mt[1];
808
809         /* Last two points of end cap. */
810         immVertex2fv(attr_id.pos, t0);
811         immVertex2fv(attr_id.pos, t1);
812       }
813
814       /* store computed point2 coordinates as point1 ones of next segment. */
815       copy_v2_v2(s0, s1);
816       /* store stroke's 'natural' normal for next stroke to use */
817       copy_v2_v2(pm, m2);
818     }
819
820     immEnd();
821     immUnbindProgram();
822   }
823 }
824
825 /* ----- Strokes Drawing ------ */
826
827 /* Helper for doing all the checks on whether a stroke can be drawn */
828 static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag)
829 {
830   /* skip stroke if it isn't in the right display space for this drawing context */
831   /* 1) 3D Strokes */
832   if ((dflag & GP_DRAWDATA_ONLY3D) && !(gps->flag & GP_STROKE_3DSPACE)) {
833     return false;
834   }
835   if (!(dflag & GP_DRAWDATA_ONLY3D) && (gps->flag & GP_STROKE_3DSPACE)) {
836     return false;
837   }
838
839   /* 2) Screen Space 2D Strokes */
840   if ((dflag & GP_DRAWDATA_ONLYV2D) && !(gps->flag & GP_STROKE_2DSPACE)) {
841     return false;
842   }
843   if (!(dflag & GP_DRAWDATA_ONLYV2D) && (gps->flag & GP_STROKE_2DSPACE)) {
844     return false;
845   }
846
847   /* 3) Image Space (2D) */
848   if ((dflag & GP_DRAWDATA_ONLYI2D) && !(gps->flag & GP_STROKE_2DIMAGE)) {
849     return false;
850   }
851   if (!(dflag & GP_DRAWDATA_ONLYI2D) && (gps->flag & GP_STROKE_2DIMAGE)) {
852     return false;
853   }
854
855   /* skip stroke if it doesn't have any valid data */
856   if ((gps->points == NULL) || (gps->totpoints < 1)) {
857     return false;
858   }
859
860   /* stroke can be drawn */
861   return true;
862 }
863
864 /* draw a set of strokes */
865 static void gp_draw_strokes(tGPDdraw *tgpw)
866 {
867   float tcolor[4];
868   float tfill[4];
869   short sthickness;
870   float ink[4];
871   const bool is_unique = (tgpw->gps != NULL);
872
873   GPU_program_point_size(true);
874
875   bGPDstroke *gps_init = (tgpw->gps) ? tgpw->gps : tgpw->t_gpf->strokes.first;
876
877   for (bGPDstroke *gps = gps_init; gps; gps = gps->next) {
878     /* check if stroke can be drawn */
879     if (gp_can_draw_stroke(gps, tgpw->dflag) == false) {
880       continue;
881     }
882     /* check if the color is visible */
883     Material *ma = tgpw->gpd->mat[gps->mat_nr];
884     MaterialGPencilStyle *gp_style = (ma) ? ma->gp_style : NULL;
885
886     if ((gp_style == NULL) || (gp_style->flag & GP_STYLE_COLOR_HIDE) ||
887         /* if onion and ghost flag do not draw*/
888         (tgpw->onion && (gp_style->flag & GP_STYLE_COLOR_ONIONSKIN))) {
889       continue;
890     }
891
892     /* if disable fill, the colors with fill must be omitted too except fill boundary strokes */
893     if ((tgpw->disable_fill == 1) && (gp_style->fill_rgba[3] > 0.0f) &&
894         ((gps->flag & GP_STROKE_NOFILL) == 0) && (gp_style->flag & GP_STYLE_FILL_SHOW)) {
895       continue;
896     }
897
898     /* calculate thickness */
899     sthickness = gps->thickness + tgpw->lthick;
900
901     if (tgpw->is_fill_stroke) {
902       sthickness = (short)max_ii(1, sthickness / 2);
903     }
904
905     if (sthickness <= 0) {
906       continue;
907     }
908
909     /* check which stroke-drawer to use */
910     if (tgpw->dflag & GP_DRAWDATA_ONLY3D) {
911       const int no_xray = (tgpw->dflag & GP_DRAWDATA_NO_XRAY);
912       int mask_orig = 0;
913
914       if (no_xray) {
915         glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig);
916         glDepthMask(0);
917         GPU_depth_test(true);
918
919         /* first arg is normally rv3d->dist, but this isn't
920          * available here and seems to work quite well without */
921         bglPolygonOffset(1.0f, 1.0f);
922       }
923
924       /* 3D Fill */
925       // if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) {
926       if ((gps->totpoints >= 3) && (tgpw->disable_fill != 1)) {
927         /* set color using material, tint color and opacity */
928         interp_v3_v3v3(tfill, gp_style->fill_rgba, tgpw->tintcolor, tgpw->tintcolor[3]);
929         tfill[3] = gp_style->fill_rgba[3] * tgpw->opacity;
930         if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) {
931           const float *color;
932           if (!tgpw->onion) {
933             color = tfill;
934           }
935           else {
936             if (tgpw->custonion) {
937               color = tgpw->tintcolor;
938             }
939             else {
940               ARRAY_SET_ITEMS(tfill, UNPACK3(gp_style->fill_rgba), tgpw->tintcolor[3]);
941               color = tfill;
942             }
943           }
944           gp_draw_stroke_fill(tgpw->gpd,
945                               gps,
946                               tgpw->offsx,
947                               tgpw->offsy,
948                               tgpw->winx,
949                               tgpw->winy,
950                               tgpw->diff_mat,
951                               color);
952         }
953       }
954
955       /* 3D Stroke */
956       /* set color using material tint color and opacity */
957       if (!tgpw->onion) {
958         interp_v3_v3v3(tcolor, gp_style->stroke_rgba, tgpw->tintcolor, tgpw->tintcolor[3]);
959         tcolor[3] = gp_style->stroke_rgba[3] * tgpw->opacity;
960         copy_v4_v4(ink, tcolor);
961       }
962       else {
963         if (tgpw->custonion) {
964           copy_v4_v4(ink, tgpw->tintcolor);
965         }
966         else {
967           ARRAY_SET_ITEMS(tcolor, UNPACK3(gp_style->stroke_rgba), tgpw->opacity);
968           copy_v4_v4(ink, tcolor);
969         }
970       }
971
972       /* if used for fill, set opacity to 1 */
973       if (tgpw->is_fill_stroke) {
974         if (ink[3] >= GPENCIL_ALPHA_OPACITY_THRESH) {
975           ink[3] = 1.0f;
976         }
977       }
978
979       if (gp_style->mode == GP_STYLE_MODE_DOTS) {
980         /* volumetric stroke drawing */
981         if (tgpw->disable_fill != 1) {
982           gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, ink);
983         }
984       }
985       else {
986         /* 3D Lines - OpenGL primitives-based */
987         if (gps->totpoints == 1) {
988           if (tgpw->disable_fill != 1) {
989             gp_draw_stroke_point(gps->points,
990                                  sthickness,
991                                  tgpw->dflag,
992                                  gps->flag,
993                                  tgpw->offsx,
994                                  tgpw->offsy,
995                                  tgpw->winx,
996                                  tgpw->winy,
997                                  tgpw->diff_mat,
998                                  ink);
999           }
1000         }
1001         else {
1002           tgpw->gps = gps;
1003           gp_draw_stroke_3d(tgpw, sthickness, ink, gps->flag & GP_STROKE_CYCLIC);
1004         }
1005       }
1006       if (no_xray) {
1007         glDepthMask(mask_orig);
1008         GPU_depth_test(false);
1009
1010         bglPolygonOffset(0.0, 0.0);
1011       }
1012     }
1013     else {
1014       /* 2D - Fill */
1015       if (gps->totpoints >= 3) {
1016         /* set color using material, tint color and opacity */
1017         interp_v3_v3v3(tfill, gp_style->fill_rgba, tgpw->tintcolor, tgpw->tintcolor[3]);
1018         tfill[3] = gp_style->fill_rgba[3] * tgpw->opacity;
1019         if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) {
1020           const float *color;
1021           if (!tgpw->onion) {
1022             color = tfill;
1023           }
1024           else {
1025             if (tgpw->custonion) {
1026               color = tgpw->tintcolor;
1027             }
1028             else {
1029               ARRAY_SET_ITEMS(tfill, UNPACK3(gp_style->fill_rgba), tgpw->tintcolor[3]);
1030               color = tfill;
1031             }
1032           }
1033           gp_draw_stroke_fill(tgpw->gpd,
1034                               gps,
1035                               tgpw->offsx,
1036                               tgpw->offsy,
1037                               tgpw->winx,
1038                               tgpw->winy,
1039                               tgpw->diff_mat,
1040                               color);
1041         }
1042       }
1043
1044       /* 2D Strokes... */
1045       /* set color using material, tint color and opacity */
1046       if (!tgpw->onion) {
1047         interp_v3_v3v3(tcolor, gp_style->stroke_rgba, tgpw->tintcolor, tgpw->tintcolor[3]);
1048         tcolor[3] = gp_style->stroke_rgba[3] * tgpw->opacity;
1049         copy_v4_v4(ink, tcolor);
1050       }
1051       else {
1052         if (tgpw->custonion) {
1053           copy_v4_v4(ink, tgpw->tintcolor);
1054         }
1055         else {
1056           ARRAY_SET_ITEMS(tcolor, UNPACK3(gp_style->stroke_rgba), tgpw->opacity);
1057           copy_v4_v4(ink, tcolor);
1058         }
1059       }
1060       if (gp_style->mode == GP_STYLE_MODE_DOTS) {
1061         /* blob/disk-based "volumetric" drawing */
1062         gp_draw_stroke_volumetric_2d(gps->points,
1063                                      gps->totpoints,
1064                                      sthickness,
1065                                      tgpw->dflag,
1066                                      gps->flag,
1067                                      tgpw->offsx,
1068                                      tgpw->offsy,
1069                                      tgpw->winx,
1070                                      tgpw->winy,
1071                                      tgpw->diff_mat,
1072                                      ink);
1073       }
1074       else {
1075         /* normal 2D strokes */
1076         if (gps->totpoints == 1) {
1077           gp_draw_stroke_point(gps->points,
1078                                sthickness,
1079                                tgpw->dflag,
1080                                gps->flag,
1081                                tgpw->offsx,
1082                                tgpw->offsy,
1083                                tgpw->winx,
1084                                tgpw->winy,
1085                                tgpw->diff_mat,
1086                                ink);
1087         }
1088         else {
1089           gp_draw_stroke_2d(gps->points,
1090                             gps->totpoints,
1091                             sthickness,
1092                             tgpw->dflag,
1093                             gps->flag,
1094                             false,
1095                             tgpw->offsx,
1096                             tgpw->offsy,
1097                             tgpw->winx,
1098                             tgpw->winy,
1099                             tgpw->diff_mat,
1100                             ink);
1101         }
1102       }
1103     }
1104     /* if only one stroke, exit from loop */
1105     if (is_unique) {
1106       break;
1107     }
1108   }
1109
1110   GPU_program_point_size(false);
1111 }
1112
1113 /* ----- General Drawing ------ */
1114
1115 /* draw interpolate strokes (used only while operator is running) */
1116 void ED_gp_draw_interpolation(const bContext *C, tGPDinterpolate *tgpi, const int type)
1117 {
1118   tGPDdraw tgpw;
1119   ARegion *ar = CTX_wm_region(C);
1120   RegionView3D *rv3d = ar->regiondata;
1121   tGPDinterpolate_layer *tgpil;
1122   Object *obact = CTX_data_active_object(C);
1123   /* Drawing code is expected to run with fully evaluated depsgraph. */
1124   Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C);
1125
1126   float color[4];
1127
1128   UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, color);
1129   color[3] = 0.6f;
1130   int dflag = 0;
1131   /* if 3d stuff, enable flags */
1132   if (type == REGION_DRAW_POST_VIEW) {
1133     dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS);
1134   }
1135
1136   tgpw.rv3d = rv3d;
1137   tgpw.depsgraph = depsgraph;
1138   tgpw.ob = obact;
1139   tgpw.gpd = tgpi->gpd;
1140   tgpw.offsx = 0;
1141   tgpw.offsy = 0;
1142   tgpw.winx = tgpi->ar->winx;
1143   tgpw.winy = tgpi->ar->winy;
1144   tgpw.dflag = dflag;
1145
1146   /* turn on alpha-blending */
1147   GPU_blend(true);
1148   for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) {
1149     /* calculate parent position */
1150     ED_gpencil_parent_location(depsgraph, obact, tgpi->gpd, tgpil->gpl, tgpw.diff_mat);
1151     if (tgpil->interFrame) {
1152       tgpw.gpl = tgpil->gpl;
1153       tgpw.gpf = tgpil->interFrame;
1154       tgpw.t_gpf = tgpil->interFrame;
1155       tgpw.gps = NULL;
1156
1157       tgpw.lthick = tgpil->gpl->line_change;
1158       tgpw.opacity = 1.0;
1159       copy_v4_v4(tgpw.tintcolor, color);
1160       tgpw.onion = true;
1161       tgpw.custonion = true;
1162
1163       gp_draw_strokes(&tgpw);
1164     }
1165   }
1166   GPU_blend(false);
1167 }
1168
1169 /* wrapper to draw strokes for filling operator */
1170 void ED_gp_draw_fill(tGPDdraw *tgpw)
1171 {
1172   gp_draw_strokes(tgpw);
1173 }
1174
1175 /* draw a short status message in the top-right corner */
1176 static void UNUSED_FUNCTION(gp_draw_status_text)(const bGPdata *gpd, ARegion *ar)
1177 {
1178
1179   /* Cannot draw any status text when drawing OpenGL Renders */
1180   if (G.f & G_FLAG_RENDER_VIEWPORT) {
1181     return;
1182   }
1183
1184   /* Get bounds of region - Necessary to avoid problems with region overlap. */
1185   const rcti *rect = ED_region_visible_rect(ar);
1186
1187   /* for now, this should only be used to indicate when we are in stroke editmode */
1188   if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
1189     const char *printable = IFACE_("GPencil Stroke Editing");
1190     float printable_size[2];
1191
1192     int font_id = BLF_default();
1193
1194     BLF_width_and_height(
1195         font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]);
1196
1197     int xco = (rect->xmax - U.widget_unit) - (int)printable_size[0];
1198     int yco = (rect->ymax - U.widget_unit);
1199
1200     /* text label */
1201     UI_FontThemeColor(font_id, TH_TEXT_HI);
1202 #ifdef WITH_INTERNATIONAL
1203     BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
1204 #else
1205     BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
1206 #endif
1207
1208     /* grease pencil icon... */
1209     // XXX: is this too intrusive?
1210     GPU_blend_set_func_separate(
1211         GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
1212     GPU_blend(true);
1213
1214     xco -= U.widget_unit;
1215     yco -= (int)printable_size[1] / 2;
1216
1217     UI_icon_draw(xco, yco, ICON_GREASEPENCIL);
1218
1219     GPU_blend(false);
1220   }
1221 }