GPencil Drawing: Enable polygon smoothing
[blender.git] / source / blender / editors / gpencil / drawgpencil.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008, Blender Foundation
19  * This is a new part of Blender
20  *
21  * Contributor(s): Joshua Leung
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/gpencil/drawgpencil.c
27  *  \ingroup edgpencil
28  */
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stddef.h>
34 #include <math.h>
35 #include <float.h>
36
37 #include "BLI_sys_types.h"
38
39 #include "BLI_math.h"
40 #include "BLI_utildefines.h"
41
42 #include "DNA_gpencil_types.h"
43 #include "DNA_scene_types.h"
44 #include "DNA_screen_types.h"
45 #include "DNA_space_types.h"
46 #include "DNA_view3d_types.h"
47 #include "DNA_userdef_types.h"
48
49 #include "BKE_context.h"
50 #include "BKE_global.h"
51 #include "BKE_gpencil.h"
52
53 #include "WM_api.h"
54
55 #include "BIF_gl.h"
56 #include "BIF_glutil.h"
57
58 #include "ED_gpencil.h"
59 #include "ED_view3d.h"
60
61 #include "UI_resources.h"
62
63 #include "gpencil_intern.h"
64
65 /* ************************************************** */
66 /* GREASE PENCIL DRAWING */
67
68 /* ----- General Defines ------ */
69
70 /* flags for sflag */
71 typedef enum eDrawStrokeFlags {
72         GP_DRAWDATA_NOSTATUS    = (1 << 0),   /* don't draw status info */
73         GP_DRAWDATA_ONLY3D      = (1 << 1),   /* only draw 3d-strokes */
74         GP_DRAWDATA_ONLYV2D     = (1 << 2),   /* only draw 'canvas' strokes */
75         GP_DRAWDATA_ONLYI2D     = (1 << 3),   /* only draw 'image' strokes */
76         GP_DRAWDATA_IEDITHACK   = (1 << 4),   /* special hack for drawing strokes in Image Editor (weird coordinates) */
77         GP_DRAWDATA_NO_XRAY     = (1 << 5),   /* don't draw xray in 3D view (which is default) */
78         GP_DRAWDATA_NO_ONIONS   = (1 << 6),       /* no onionskins should be drawn (for animation playback) */
79         GP_DRAWDATA_VOLUMETRIC  = (1 << 7),   /* draw strokes as "volumetric" circular billboards */
80         GP_DRAWDATA_FILL        = (1 << 8),   /* fill insides/bounded-regions of strokes */
81 } eDrawStrokeFlags;
82
83
84
85 /* thickness above which we should use special drawing */
86 #define GP_DRAWTHICKNESS_SPECIAL    3
87
88 /* ----- Tool Buffer Drawing ------ */
89
90 /* draw stroke defined in buffer (simple ogl lines/points for now, as dotted lines) */
91 static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickness, short dflag, short sflag)
92 {
93         tGPspoint *pt;
94         int i;
95         
96         /* error checking */
97         if ((points == NULL) || (totpoints <= 0))
98                 return;
99         
100         /* check if buffer can be drawn */
101         if (dflag & (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_ONLYV2D))
102                 return;
103         
104         /* if drawing a single point, draw it larger */
105         if (totpoints == 1) {
106                 /* draw point */
107                 glBegin(GL_POINTS);
108                 glVertex2iv(&points->x);
109                 glEnd();
110         }
111         else if (sflag & GP_STROKE_ERASER) {
112                 /* don't draw stroke at all! */
113         }
114         else {
115                 float oldpressure = points[0].pressure;
116                 
117                 /* draw stroke curve */
118                 if (G.debug & G_DEBUG) setlinestyle(2);
119
120                 glLineWidth(oldpressure * thickness);
121                 glBegin(GL_LINE_STRIP);
122
123                 for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
124                         /* if there was a significant pressure change, stop the curve, change the thickness of the stroke,
125                          * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP)
126                          */
127                         if (fabsf(pt->pressure - oldpressure) > 0.2f) {
128                                 glEnd();
129                                 glLineWidth(pt->pressure * thickness);
130                                 glBegin(GL_LINE_STRIP);
131                                 
132                                 /* need to roll-back one point to ensure that there are no gaps in the stroke */
133                                 if (i != 0) glVertex2iv(&(pt - 1)->x);
134
135                                 /* now the point we want... */
136                                 glVertex2iv(&pt->x);
137                                 
138                                 oldpressure = pt->pressure;
139                         }
140                         else
141                                 glVertex2iv(&pt->x);
142                 }
143                 glEnd();
144
145                 /* reset for predictable OpenGL context */
146                 glLineWidth(1.0f);
147                 
148                 if (G.debug & G_DEBUG) setlinestyle(0);
149         }
150 }
151
152 /* --------- 2D Stroke Drawing Helpers --------- */
153
154 /* helper function to calculate x-y drawing coordinates for 2D points */
155 static void gp_calc_2d_stroke_xy(bGPDspoint *pt, short sflag, int offsx, int offsy, int winx, int winy, float r_co[2])
156 {
157         if (sflag & GP_STROKE_2DSPACE) {
158                 r_co[0] = pt->x;
159                 r_co[1] = pt->y;
160         }
161         else if (sflag & GP_STROKE_2DIMAGE) {
162                 const float x = (float)((pt->x * winx) + offsx);
163                 const float y = (float)((pt->y * winy) + offsy);
164                 
165                 r_co[0] = x;
166                 r_co[1] = y;
167         }
168         else {
169                 const float x = (float)(pt->x / 100 * winx) + offsx;
170                 const float y = (float)(pt->y / 100 * winy) + offsy;
171                 
172                 r_co[0] = x;
173                 r_co[1] = y;
174         }
175 }
176
177 /* ----------- Volumetric Strokes --------------- */
178
179 /* draw a 2D buffer stroke in "volumetric" style
180  * NOTE: the stroke buffer doesn't have any coordinate offsets/transforms
181  */
182 static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, short thickness,
183                                              short dflag, short UNUSED(sflag))
184 {
185         GLUquadricObj *qobj = gluNewQuadric();
186         float modelview[4][4];
187         
188         tGPspoint *pt;
189         int i;
190         
191         /* error checking */
192         if ((points == NULL) || (totpoints <= 0))
193                 return;
194         
195         /* check if buffer can be drawn */
196         if (dflag & (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_ONLYV2D))
197                 return;
198         
199         /* get basic matrix - should be camera space (i.e "identity") */
200         glGetFloatv(GL_MODELVIEW_MATRIX, (float *)modelview);
201         
202         /* draw points */
203         glPushMatrix();
204         
205         for (i = 0, pt = points; i < totpoints; i++, pt++) {
206                 /* set the transformed position */
207                 // TODO: scale should change based on zoom level, which requires proper translation mult too!
208                 modelview[3][0] = pt->x;
209                 modelview[3][1] = pt->y;
210                 
211                 glLoadMatrixf((float *)modelview);
212                 
213                 /* draw the disk using the current state... */
214                 gluDisk(qobj, 0.0,  pt->pressure * thickness, 32, 1);
215                 
216                 
217                 modelview[3][0] = modelview[3][1] = 0.0f;
218         }
219         
220         glPopMatrix();
221         gluDeleteQuadric(qobj);
222 }
223
224 /* draw a 2D strokes in "volumetric" style */
225 static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, short thickness,
226                                          short dflag, short sflag,
227                                          int offsx, int offsy, int winx, int winy)
228 {
229         GLUquadricObj *qobj = gluNewQuadric();
230         float modelview[4][4];
231         float baseloc[3];
232         float scalefac = 1.0f;
233         
234         bGPDspoint *pt;
235         int i;
236         
237         
238         /* HACK: We need a scale factor for the drawing in the image editor,
239          * which seems to use 1 unit as it's maximum size, whereas everything
240          * else assumes 1 unit = 1 pixel. Otherwise, we only get a massive blob.
241          */
242         if ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) {
243                 scalefac = 0.001f;
244         }
245         
246         /* get basic matrix */
247         glGetFloatv(GL_MODELVIEW_MATRIX, (float *)modelview);
248         copy_v3_v3(baseloc, modelview[3]);
249         
250         /* draw points */
251         glPushMatrix();
252         
253         for (i = 0, pt = points; i < totpoints; i++, pt++) {
254                 /* set the transformed position */
255                 float co[2];
256                 
257                 gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co);
258                 translate_m4(modelview, co[0], co[1], 0.0f);
259                 
260                 glLoadMatrixf((float *)modelview);
261                 
262                 /* draw the disk using the current state... */
263                 gluDisk(qobj, 0.0,  pt->pressure * thickness * scalefac, 32, 1);
264                 
265                 /* restore matrix */
266                 copy_v3_v3(modelview[3], baseloc);
267         }
268         
269         glPopMatrix();
270         gluDeleteQuadric(qobj);
271 }
272
273 /* draw a 3D stroke in "volumetric" style */
274 static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, short thickness,
275                                          short UNUSED(dflag), short UNUSED(sflag))
276 {
277         GLUquadricObj *qobj = gluNewQuadric();
278         
279         float base_modelview[4][4], modelview[4][4];
280         float base_loc[3];
281         
282         bGPDspoint *pt;
283         int i;
284         
285         
286         /* Get the basic modelview matrix we use for performing calculations */
287         glGetFloatv(GL_MODELVIEW_MATRIX, (float *)base_modelview);
288         copy_v3_v3(base_loc, base_modelview[3]);
289         
290         /* Create the basic view-aligned billboard matrix we're going to actually draw qobj with:
291          * - We need to knock out the rotation so that we are 
292          *   simply left with a camera-facing billboard
293          * - The scale factors here are chosen so that the thickness
294          *   is relatively reasonable. Otherwise, it gets far too
295          *   large!
296          */
297         scale_m4_fl(modelview, 0.1f);
298         
299         /* draw each point as a disk... */
300         glPushMatrix();
301         
302         for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
303                 /* apply translation to base_modelview, so that the translated point is put in the right place */
304                 translate_m4(base_modelview, pt->x, pt->y, pt->z);
305                 
306                 /* copy the translation component to the billboard matrix we're going to use,
307                  * then reset the base matrix to the original values so that we can do the same
308                  * for the next point without accumulation/pollution effects
309                  */
310                 copy_v3_v3(modelview[3], base_modelview[3]); /* copy offset value */
311                 copy_v3_v3(base_modelview[3], base_loc);     /* restore */
312                 
313                 /* apply our billboard matrix for drawing... */
314                 glLoadMatrixf((float *)modelview);
315                 
316                 /* draw the disk using the current state... */
317                 gluDisk(qobj, 0.0,  pt->pressure * thickness, 32, 1);
318         }
319         
320         glPopMatrix();
321         gluDeleteQuadric(qobj);
322 }
323
324
325 /* --------------- Stroke Fills ----------------- */
326
327 /* draw fills for shapes */
328 static void gp_draw_stroke_fill(bGPDspoint *points, int totpoints, short UNUSED(thickness),
329                                 short UNUSED(dflag), short sflag,
330                                 int offsx, int offsy, int winx, int winy)
331 {
332         bGPDspoint *pt;
333         int i;
334         
335         BLI_assert(totpoints >= 3);
336         
337         /* As an initial implementation, we use the OpenGL filled polygon drawing 
338          * here since it's the easiest option to implement for this case. It does
339          * come with limitations (notably for concave shapes), though it shouldn't
340          * be much of an issue in most cases.
341          */
342         glBegin(GL_POLYGON);
343         
344         for (i = 0, pt = points; i < totpoints; i++, pt++) {
345                 if (sflag & GP_STROKE_3DSPACE) {
346                         glVertex3fv(&pt->x);
347                 }
348                 else {
349                         float co[2];
350                         
351                         gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co);
352                         glVertex2fv(co);
353                 }
354         }
355         
356         glEnd();
357 }
358
359 /* ----- Existing Strokes Drawing (3D and Point) ------ */
360
361 /* draw a given stroke - just a single dot (only one point) */
362 static void gp_draw_stroke_point(bGPDspoint *points, short thickness, short dflag, short sflag,
363                                  int offsx, int offsy, int winx, int winy)
364 {
365         /* draw point */
366         if (sflag & GP_STROKE_3DSPACE) {
367                 glBegin(GL_POINTS);
368                 glVertex3fv(&points->x);
369                 glEnd();
370         }
371         else {
372                 float co[2];
373                 
374                 /* get coordinates of point */
375                 gp_calc_2d_stroke_xy(points, sflag, offsx, offsy, winx, winy, co);
376                 
377                 /* if thickness is less than GP_DRAWTHICKNESS_SPECIAL, simple dot looks ok
378                  *  - also mandatory in if Image Editor 'image-based' dot
379                  */
380                 if ((thickness < GP_DRAWTHICKNESS_SPECIAL) ||
381                     ((dflag & GP_DRAWDATA_IEDITHACK) && (sflag & GP_STROKE_2DSPACE)))
382                 {
383                         glBegin(GL_POINTS);
384                         glVertex2fv(co);
385                         glEnd();
386                 }
387                 else {
388                         /* draw filled circle as is done in circf (but without the matrix push/pops which screwed things up) */
389                         GLUquadricObj *qobj = gluNewQuadric(); 
390                         
391                         gluQuadricDrawStyle(qobj, GLU_FILL); 
392                         
393                         /* need to translate drawing position, but must reset after too! */
394                         glTranslatef(co[0], co[1], 0.0);
395                         gluDisk(qobj, 0.0,  thickness, 32, 1); 
396                         glTranslatef(-co[0], -co[1], 0.0);
397                         
398                         gluDeleteQuadric(qobj);
399                 }
400         }
401 }
402
403 /* draw a given stroke in 3d (i.e. in 3d-space), using simple ogl lines */
404 static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness, bool debug, short UNUSED(sflag))
405 {
406         bGPDspoint *pt;
407         float curpressure = points[0].pressure;
408         int i;
409         
410         /* draw stroke curve */
411         glLineWidth(curpressure * thickness);
412         glBegin(GL_LINE_STRIP);
413         for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
414                 /* if there was a significant pressure change, stop the curve, change the thickness of the stroke,
415                  * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP)
416                  * Note: we want more visible levels of pressures when thickness is bigger.
417                  */
418                 if (fabsf(pt->pressure - curpressure) > 0.2f / (float)thickness) {
419                         glEnd();
420                         curpressure = pt->pressure;
421                         glLineWidth(curpressure * thickness);
422                         glBegin(GL_LINE_STRIP);
423                         
424                         /* need to roll-back one point to ensure that there are no gaps in the stroke */
425                         if (i != 0) glVertex3fv(&(pt - 1)->x);
426                         
427                         /* now the point we want... */
428                         glVertex3fv(&pt->x);
429                 }
430                 else {
431                         glVertex3fv(&pt->x);
432                 }
433         }
434         glEnd();
435         
436         /* draw debug points of curve on top? */
437         /* XXX: for now, we represent "selected" strokes in the same way as debug, which isn't used anymore */
438         if (debug) {
439                 glBegin(GL_POINTS);
440                 for (i = 0, pt = points; i < totpoints && pt; i++, pt++)
441                         glVertex3fv(&pt->x);
442                 glEnd();
443         }
444 }
445
446 /* ----- Fancy 2D-Stroke Drawing ------ */
447
448 /* draw a given stroke in 2d */
449 static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag,
450                               bool debug, int offsx, int offsy, int winx, int winy)
451 {
452         /* otherwise thickness is twice that of the 3D view */
453         float thickness = (float)thickness_s * 0.5f;
454         
455         /* strokes in Image Editor need a scale factor, since units there are not pixels! */
456         float scalefac  = 1.0f;
457         if ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) {
458                 scalefac = 0.001f;
459         }
460         
461         
462         /* tessellation code - draw stroke as series of connected quads with connection
463          * edges rotated to minimize shrinking artifacts, and rounded endcaps
464          */
465         {
466                 bGPDspoint *pt1, *pt2;
467                 float pm[2];
468                 int i;
469                 
470                 glShadeModel(GL_FLAT);
471                 glBegin(GL_QUADS);
472                 
473                 for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) {
474                         float s0[2], s1[2];     /* segment 'center' points */
475                         float t0[2], t1[2];     /* tessellated coordinates */
476                         float m1[2], m2[2];     /* gradient and normal */
477                         float mt[2], sc[2];     /* gradient for thickness, point for end-cap */
478                         float pthick;           /* thickness at segment point */
479                         
480                         /* get x and y coordinates from points */
481                         gp_calc_2d_stroke_xy(pt1, sflag, offsx, offsy, winx, winy, s0);
482                         gp_calc_2d_stroke_xy(pt2, sflag, offsx, offsy, winx, winy, s1);
483                         
484                         /* calculate gradient and normal - 'angle'=(ny/nx) */
485                         m1[1] = s1[1] - s0[1];
486                         m1[0] = s1[0] - s0[0];
487                         normalize_v2(m1);
488                         m2[1] = -m1[0];
489                         m2[0] = m1[1];
490                         
491                         /* always use pressure from first point here */
492                         pthick = (pt1->pressure * thickness * scalefac);
493                         
494                         /* if the first segment, start of segment is segment's normal */
495                         if (i == 0) {
496                                 /* draw start cap first 
497                                  *      - make points slightly closer to center (about halfway across) 
498                                  */
499                                 mt[0] = m2[0] * pthick * 0.5f;
500                                 mt[1] = m2[1] * pthick * 0.5f;
501                                 sc[0] = s0[0] - (m1[0] * pthick * 0.75f);
502                                 sc[1] = s0[1] - (m1[1] * pthick * 0.75f);
503                                 
504                                 t0[0] = sc[0] - mt[0];
505                                 t0[1] = sc[1] - mt[1];
506                                 t1[0] = sc[0] + mt[0];
507                                 t1[1] = sc[1] + mt[1];
508                                 
509                                 glVertex2fv(t0);
510                                 glVertex2fv(t1);
511                                 
512                                 /* calculate points for start of segment */
513                                 mt[0] = m2[0] * pthick;
514                                 mt[1] = m2[1] * pthick;
515                                 
516                                 t0[0] = s0[0] - mt[0];
517                                 t0[1] = s0[1] - mt[1];
518                                 t1[0] = s0[0] + mt[0];
519                                 t1[1] = s0[1] + mt[1];
520                                 
521                                 /* draw this line twice (first to finish off start cap, then for stroke) */
522                                 glVertex2fv(t1);
523                                 glVertex2fv(t0);
524                                 glVertex2fv(t0);
525                                 glVertex2fv(t1);
526                         }
527                         /* if not the first segment, use bisector of angle between segments */
528                         else {
529                                 float mb[2];         /* bisector normal */
530                                 float athick, dfac;  /* actual thickness, difference between thicknesses */
531                                 
532                                 /* calculate gradient of bisector (as average of normals) */
533                                 mb[0] = (pm[0] + m2[0]) / 2;
534                                 mb[1] = (pm[1] + m2[1]) / 2;
535                                 normalize_v2(mb);
536                                 
537                                 /* calculate gradient to apply 
538                                  *  - as basis, use just pthick * bisector gradient
539                                  *      - if cross-section not as thick as it should be, add extra padding to fix it
540                                  */
541                                 mt[0] = mb[0] * pthick;
542                                 mt[1] = mb[1] * pthick;
543                                 athick = len_v2(mt);
544                                 dfac = pthick - (athick * 2);
545                                 
546                                 if (((athick * 2.0f) < pthick) && (IS_EQF(athick, pthick) == 0)) {
547                                         mt[0] += (mb[0] * dfac);
548                                         mt[1] += (mb[1] * dfac);
549                                 }
550                                 
551                                 /* calculate points for start of segment */
552                                 t0[0] = s0[0] - mt[0];
553                                 t0[1] = s0[1] - mt[1];
554                                 t1[0] = s0[0] + mt[0];
555                                 t1[1] = s0[1] + mt[1];
556                                 
557                                 /* draw this line twice (once for end of current segment, and once for start of next) */
558                                 glVertex2fv(t1);
559                                 glVertex2fv(t0);
560                                 glVertex2fv(t0);
561                                 glVertex2fv(t1);
562                         }
563                         
564                         /* if last segment, also draw end of segment (defined as segment's normal) */
565                         if (i == totpoints - 2) {
566                                 /* for once, we use second point's pressure (otherwise it won't be drawn) */
567                                 pthick = (pt2->pressure * thickness * scalefac);
568                                 
569                                 /* calculate points for end of segment */
570                                 mt[0] = m2[0] * pthick;
571                                 mt[1] = m2[1] * pthick;
572                                 
573                                 t0[0] = s1[0] - mt[0];
574                                 t0[1] = s1[1] - mt[1];
575                                 t1[0] = s1[0] + mt[0];
576                                 t1[1] = s1[1] + mt[1];
577                                 
578                                 /* draw this line twice (once for end of stroke, and once for endcap)*/
579                                 glVertex2fv(t1);
580                                 glVertex2fv(t0);
581                                 glVertex2fv(t0);
582                                 glVertex2fv(t1);
583                                 
584                                 
585                                 /* draw end cap as last step 
586                                  *      - make points slightly closer to center (about halfway across) 
587                                  */
588                                 mt[0] = m2[0] * pthick * 0.5f;
589                                 mt[1] = m2[1] * pthick * 0.5f;
590                                 sc[0] = s1[0] + (m1[0] * pthick * 0.75f);
591                                 sc[1] = s1[1] + (m1[1] * pthick * 0.75f);
592                                 
593                                 t0[0] = sc[0] - mt[0];
594                                 t0[1] = sc[1] - mt[1];
595                                 t1[0] = sc[0] + mt[0];
596                                 t1[1] = sc[1] + mt[1];
597                                 
598                                 glVertex2fv(t1);
599                                 glVertex2fv(t0);
600                         }
601                         
602                         /* store stroke's 'natural' normal for next stroke to use */
603                         copy_v2_v2(pm, m2);
604                 }
605                 
606                 glEnd();
607         }
608         
609         /* draw debug points of curve on top? (original stroke points) */
610         if (debug) {
611                 bGPDspoint *pt;
612                 int i;
613                 
614                 glBegin(GL_POINTS);
615                 for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
616                         float co[2];
617                         
618                         gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co);
619                         glVertex2fv(co);
620                 }
621                 glEnd();
622         }
623 }
624
625 /* ----- Strokes Drawing ------ */
626
627 /* Helper for doing all the checks on whether a stroke can be drawn */
628 static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag)
629 {
630         /* skip stroke if it isn't in the right display space for this drawing context */
631         /* 1) 3D Strokes */
632         if ((dflag & GP_DRAWDATA_ONLY3D) && !(gps->flag & GP_STROKE_3DSPACE))
633                 return false;
634         if (!(dflag & GP_DRAWDATA_ONLY3D) && (gps->flag & GP_STROKE_3DSPACE))
635                 return false;
636                 
637         /* 2) Screen Space 2D Strokes */
638         if ((dflag & GP_DRAWDATA_ONLYV2D) && !(gps->flag & GP_STROKE_2DSPACE))
639                 return false;
640         if (!(dflag & GP_DRAWDATA_ONLYV2D) && (gps->flag & GP_STROKE_2DSPACE))
641                 return false;
642                 
643         /* 3) Image Space (2D) */
644         if ((dflag & GP_DRAWDATA_ONLYI2D) && !(gps->flag & GP_STROKE_2DIMAGE))
645                 return false;
646         if (!(dflag & GP_DRAWDATA_ONLYI2D) && (gps->flag & GP_STROKE_2DIMAGE))
647                 return false;
648                 
649                 
650         /* skip stroke if it doesn't have any valid data */
651         if ((gps->points == NULL) || (gps->totpoints < 1))
652                 return false;
653                 
654         /* stroke can be drawn */
655         return true;
656 }
657
658 /* draw a set of strokes */
659 static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag,
660                             bool debug, short lthick, const float color[4], const float fill_color[4])
661 {
662         bGPDstroke *gps;
663         
664         for (gps = gpf->strokes.first; gps; gps = gps->next) {
665                 /* check if stroke can be drawn */
666                 if (gp_can_draw_stroke(gps, dflag) == false)
667                         continue;
668                 
669                 /* check which stroke-drawer to use */
670                 if (dflag & GP_DRAWDATA_ONLY3D) {
671                         const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY);
672                         int mask_orig = 0;
673                         
674                         if (no_xray) {
675                                 glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig);
676                                 glDepthMask(0);
677                                 glEnable(GL_DEPTH_TEST);
678                                 
679                                 /* first arg is normally rv3d->dist, but this isn't
680                                  * available here and seems to work quite well without */
681                                 bglPolygonOffset(1.0f, 1.0f);
682 #if 0
683                                 glEnable(GL_POLYGON_OFFSET_LINE);
684                                 glPolygonOffset(-1.0f, -1.0f);
685 #endif
686                         }
687                         
688                         /* 3D Fill */
689                         if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) {
690                                 glColor4fv(fill_color);
691                                 gp_draw_stroke_fill(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
692                         }
693                         
694                         /* 3D Stroke */
695                         glColor4fv(color);
696                         
697                         if (dflag & GP_DRAWDATA_VOLUMETRIC) {
698                                 /* volumetric stroke drawing */
699                                 gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, lthick, dflag, gps->flag);
700                         }
701                         else {
702                                 /* 3D Lines - OpenGL primitives-based */
703                                 if (gps->totpoints == 1) {
704                                         gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
705                                 }
706                                 else {
707                                         gp_draw_stroke_3d(gps->points, gps->totpoints, lthick, debug, gps->flag);
708                                 }
709                         }
710                         
711                         if (no_xray) {
712                                 glDepthMask(mask_orig);
713                                 glDisable(GL_DEPTH_TEST);
714                                 
715                                 bglPolygonOffset(0.0, 0.0);
716 #if 0
717                                 glDisable(GL_POLYGON_OFFSET_LINE);
718                                 glPolygonOffset(0, 0);
719 #endif
720                         }
721                 }
722                 else {
723                         /* 2D - Fill */
724                         if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) {
725                                 glColor4fv(fill_color);
726                                 gp_draw_stroke_fill(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
727                         }
728                         
729                         /* 2D Strokes... */
730                         glColor4fv(color);
731                         
732                         if (dflag & GP_DRAWDATA_VOLUMETRIC) {
733                                 /* blob/disk-based "volumetric" drawing */
734                                 gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
735                         }
736                         else {
737                                 /* normal 2D strokes */
738                                 if (gps->totpoints == 1) {
739                                         gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
740                                 }
741                                 else {
742                                         gp_draw_stroke_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, offsx, offsy, winx, winy);
743                                 }
744                         }
745                 }
746         }
747 }
748
749 /* Draw selected verts for strokes being edited */
750 static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, short dflag, const float tcolor[3])
751 {
752         bGPDstroke *gps;
753         
754         const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY);
755         int mask_orig = 0;
756         
757         /* set up depth masks... */
758         if (dflag & GP_DRAWDATA_ONLY3D) {
759                 if (no_xray) {
760                         glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig);
761                         glDepthMask(0);
762                         glEnable(GL_DEPTH_TEST);
763                         
764                         /* first arg is normally rv3d->dist, but this isn't
765                          * available here and seems to work quite well without */
766                         bglPolygonOffset(1.0f, 1.0f);
767 #if 0
768                         glEnable(GL_POLYGON_OFFSET_LINE);
769                         glPolygonOffset(-1.0f, -1.0f);
770 #endif
771                 }
772         }
773         
774         
775         /* draw stroke verts */
776         for (gps = gpf->strokes.first; gps; gps = gps->next) {
777                 bGPDspoint *pt;
778                 float vsize, bsize;
779                 int i;
780                 
781                 /* check if stroke can be drawn */
782                 if (gp_can_draw_stroke(gps, dflag) == false)
783                         continue;
784                 
785                 /* Optimisation: only draw points for selected strokes
786                  * We assume that selected points can only occur in
787                  * strokes that are selected too.
788                  */
789                 if ((gps->flag & GP_STROKE_SELECT) == 0)
790                         continue;
791                         
792                 /* Get size of verts:
793                  * - The selected state needs to be larger than the unselected state so that
794                  *   they stand out more.
795                  * - We use the theme setting for size of the unselected verts
796                  */
797                 bsize = UI_GetThemeValuef(TH_VERTEX_SIZE);
798                 if ((int)bsize > 8) {
799                         vsize = 10.0f;
800                         bsize = 8.0f;
801                 }
802                 else {
803                         vsize = bsize + 2;
804                 }
805                 
806                 /* First Pass: Draw all the verts (i.e. these become the unselected state) */
807                 if (tcolor != NULL) {
808                         /* for now, we assume that the base color of the points is not too close to the real color */
809                         glColor3fv(tcolor);
810                 }
811                 else {
812                         /* this doesn't work well with the default theme and black strokes... */
813                         UI_ThemeColor(TH_VERTEX);
814                 }
815                 glPointSize(bsize);
816                 
817                 glBegin(GL_POINTS);
818                 for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
819                         if (gps->flag & GP_STROKE_3DSPACE) {
820                                 glVertex3fv(&pt->x);
821                         }
822                         else {
823                                 float co[2];
824                                 
825                                 gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
826                                 glVertex2fv(co);
827                         }
828                 }
829                 glEnd();
830                 
831                 
832                 /* Second Pass: Draw only verts which are selected */
833                 UI_ThemeColor(TH_VERTEX_SELECT);
834                 glPointSize(vsize);
835                 
836                 glBegin(GL_POINTS);
837                 for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
838                         if (pt->flag & GP_SPOINT_SELECT) {
839                                 if (gps->flag & GP_STROKE_3DSPACE) {
840                                         glVertex3fv(&pt->x);
841                                 }
842                                 else {
843                                         float co[2];
844                                         
845                                         gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
846                                         glVertex2fv(co);
847                                 }
848                         }
849                 }
850                 glEnd();
851         }
852         
853         
854         /* clear depth mask */
855         if (dflag & GP_DRAWDATA_ONLY3D) {
856                 if (no_xray) {
857                         glDepthMask(mask_orig);
858                         glDisable(GL_DEPTH_TEST);
859                         
860                         bglPolygonOffset(0.0, 0.0);
861 #if 0
862                         glDisable(GL_POLYGON_OFFSET_LINE);
863                         glPolygonOffset(0, 0);
864 #endif
865                 }
866         }
867 }
868
869 /* ----- General Drawing ------ */
870
871 /* draw onion-skinning for a layer */
872 static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, 
873                                int UNUSED(cfra), int dflag, short debug, short lthick)
874 {
875         const float alpha = gpl->color[3];
876         float color[4];
877         
878         /* 1) Draw Previous Frames First */
879         if (gpl->flag & GP_LAYER_GHOST_PREVCOL) {
880                 copy_v3_v3(color, gpl->gcolor_prev);
881         }
882         else {
883                 copy_v3_v3(color, gpl->color);
884         }
885         
886         if (gpl->gstep) {
887                 bGPDframe *gf;
888                 float fac;
889                 
890                 /* draw previous frames first */
891                 for (gf = gpf->prev; gf; gf = gf->prev) {
892                         /* check if frame is drawable */
893                         if ((gpf->framenum - gf->framenum) <= gpl->gstep) {
894                                 /* alpha decreases with distance from curframe index */
895                                 fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(gpl->gstep + 1));
896                                 color[3] = alpha * fac * 0.66f;
897                                 gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
898                         }
899                         else 
900                                 break;
901                 }
902         }
903         else {
904                 /* draw the strokes for the ghost frames (at half of the alpha set by user) */
905                 if (gpf->prev) {
906                         color[3] = (alpha / 7);
907                         gp_draw_strokes(gpf->prev, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
908                 }
909         }
910         
911         
912         /* 2) Now draw next frames */
913         if (gpl->flag & GP_LAYER_GHOST_NEXTCOL) {
914                 copy_v3_v3(color, gpl->gcolor_next);
915         }
916         else {
917                 copy_v3_v3(color, gpl->color);
918         }
919         
920         if (gpl->gstep_next) {
921                 bGPDframe *gf;
922                 float fac;
923                 
924                 /* now draw next frames */
925                 for (gf = gpf->next; gf; gf = gf->next) {
926                         /* check if frame is drawable */
927                         if ((gf->framenum - gpf->framenum) <= gpl->gstep_next) {
928                                 /* alpha decreases with distance from curframe index */
929                                 fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(gpl->gstep_next + 1));
930                                 color[3] = alpha * fac * 0.66f;
931                                 gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
932                         }
933                         else 
934                                 break;
935                 }
936         }
937         else {
938                 /* draw the strokes for the ghost frames (at half of the alpha set by user) */
939                 if (gpf->next) {
940                         color[3] = (alpha / 4);
941                         gp_draw_strokes(gpf->next, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
942                 }
943         }
944         
945         /* 3) restore alpha */
946         glColor4fv(gpl->color);
947 }
948
949 /* draw grease-pencil datablock */
950 static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag)
951 {
952         bGPDlayer *gpl;
953         
954         /* reset line drawing style (in case previous user didn't reset) */
955         setlinestyle(0);
956         
957         /* turn on smooth lines (i.e. anti-aliasing) */
958         glEnable(GL_LINE_SMOOTH);
959         
960         glEnable(GL_POLYGON_SMOOTH);
961         glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
962         
963         /* turn on alpha-blending */
964         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
965         glEnable(GL_BLEND);
966                 
967         /* loop over layers, drawing them */
968         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
969                 bGPDframe *gpf;
970                 
971                 bool debug = (gpl->flag & GP_LAYER_DRAWDEBUG) ? true : false;
972                 short lthick = gpl->thickness;
973                 
974                 /* don't draw layer if hidden */
975                 if (gpl->flag & GP_LAYER_HIDE) 
976                         continue;
977                 
978                 /* get frame to draw */
979                 gpf = gpencil_layer_getframe(gpl, cfra, 0);
980                 if (gpf == NULL) 
981                         continue;
982                 
983                 /* set color, stroke thickness, and point size */
984                 glLineWidth(lthick);
985                 glPointSize((float)(gpl->thickness + 2));
986                 
987                 /* Add layer drawing settings to the set of "draw flags" 
988                  * NOTE: If the setting doesn't apply, it *must* be cleared,
989                  *       as dflag's carry over from the previous layer
990                  */
991 #define GP_DRAWFLAG_APPLY(condition, draw_flag_value)     { \
992                         if (condition) dflag |= (draw_flag_value);      \
993                         else           dflag &= ~(draw_flag_value);     \
994                 } (void)0
995                 
996                 /* xray... */
997                 GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_NO_XRAY), GP_DRAWDATA_NO_XRAY);
998                 
999                 /* volumetric strokes... */
1000                 GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_VOLUMETRIC), GP_DRAWDATA_VOLUMETRIC);
1001                 
1002                 /* fill strokes... */
1003                 // XXX: this is not a very good limit
1004                 GP_DRAWFLAG_APPLY((gpl->fill[3] > 0.001f), GP_DRAWDATA_FILL);
1005 #undef GP_DRAWFLAG_APPLY
1006                 
1007                 /* draw 'onionskins' (frame left + right) */
1008                 if ((gpl->flag & GP_LAYER_ONIONSKIN) && !(dflag & GP_DRAWDATA_NO_ONIONS)) {
1009                         /* Drawing method - only immediately surrounding (gstep = 0),
1010                          * or within a frame range on either side (gstep > 0)
1011                          */
1012                         gp_draw_onionskins(gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, lthick);
1013                 }
1014                 
1015                 /* draw the strokes already in active frame */
1016                 gp_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, debug, lthick, gpl->color, gpl->fill);
1017                 
1018                 /* Draw verts of selected strokes 
1019                  *  - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering
1020                  *      - locked layers can't be edited, so there's no point showing these verts
1021                  *    as they will have no bearings on what gets edited
1022                  *  - only show when in editmode, since operators shouldn't work otherwise
1023                  *    (NOTE: doing it this way means that the toggling editmode shows visible change immediately)
1024                  */
1025                 /* XXX: perhaps we don't want to show these when users are drawing... */
1026                 if ((G.f & G_RENDER_OGL) == 0 &&
1027                     (gpl->flag & GP_LAYER_LOCKED) == 0 && 
1028                     (gpd->flag & GP_DATA_STROKE_EDITMODE))
1029                 {
1030                         gp_draw_strokes_edit(gpf, offsx, offsy, winx, winy, dflag, 
1031                                              (gpl->color[3] < 0.95f) ? gpl->color : NULL);
1032                 }
1033                 
1034                 /* Check if may need to draw the active stroke cache, only if this layer is the active layer
1035                  * that is being edited. (Stroke buffer is currently stored in gp-data)
1036                  */
1037                 if (ED_gpencil_session_active() && (gpl->flag & GP_LAYER_ACTIVE) &&
1038                     (gpf->flag & GP_FRAME_PAINT))
1039                 {
1040                         /* Set color for drawing buffer stroke - since this may not be set yet */
1041                         glColor4fv(gpl->color);
1042                         
1043                         /* Buffer stroke needs to be drawn with a different linestyle
1044                          * to help differentiate them from normal strokes.
1045                          * 
1046                          * It should also be noted that sbuffer contains temporary point types
1047                          * i.e. tGPspoints NOT bGPDspoints
1048                          */
1049                         if (gpl->flag & GP_LAYER_VOLUMETRIC) {
1050                                 gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag);
1051                         }
1052                         else {
1053                                 gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag);
1054                         }
1055                 }
1056         }
1057         
1058         /* turn off alpha blending, then smooth lines */
1059         glDisable(GL_BLEND); // alpha blending
1060         glDisable(GL_LINE_SMOOTH); // smooth lines
1061         glDisable(GL_POLYGON_SMOOTH); // smooth poly lines
1062                 
1063         /* restore initial gl conditions */
1064         glLineWidth(1.0);
1065         glPointSize(1.0);
1066         glColor4f(0, 0, 0, 1);
1067 }
1068
1069 /* ----- Grease Pencil Sketches Drawing API ------ */
1070
1071 /* ............................
1072  * XXX
1073  *      We need to review the calls below, since they may be/are not that suitable for
1074  *      the new ways that we intend to be drawing data...
1075  * ............................ */
1076
1077 /* draw grease-pencil sketches to specified 2d-view that uses ibuf corrections */
1078 void ED_gpencil_draw_2dimage(const bContext *C)
1079 {
1080         ScrArea *sa = CTX_wm_area(C);
1081         ARegion *ar = CTX_wm_region(C);
1082         Scene *scene = CTX_data_scene(C);
1083         bGPdata *gpd;
1084         int offsx, offsy, sizex, sizey;
1085         int dflag = GP_DRAWDATA_NOSTATUS;
1086         
1087         gpd = ED_gpencil_data_get_active(C); // XXX
1088         if (gpd == NULL) return;
1089         
1090         /* calculate rect */
1091         switch (sa->spacetype) {
1092                 case SPACE_IMAGE: /* image */
1093                 case SPACE_CLIP: /* clip */
1094                 {
1095                         
1096                         /* just draw using standard scaling (settings here are currently ignored anyways) */
1097                         /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */
1098                         offsx = 0;
1099                         offsy = 0;
1100                         sizex = ar->winx;
1101                         sizey = ar->winy;
1102                         
1103                         wmOrtho2(ar->v2d.cur.xmin, ar->v2d.cur.xmax, ar->v2d.cur.ymin, ar->v2d.cur.ymax);
1104                         
1105                         dflag |= GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_IEDITHACK;
1106                         break;
1107                 }
1108                 case SPACE_SEQ: /* sequence */
1109                 {
1110                         /* just draw using standard scaling (settings here are currently ignored anyways) */
1111                         offsx = 0;
1112                         offsy = 0;
1113                         sizex = ar->winx;
1114                         sizey = ar->winy;
1115                         
1116                         /* NOTE: I2D was used in 2.4x, but the old settings for that have been deprecated 
1117                          * and everything moved to standard View2d 
1118                          */
1119                         dflag |= GP_DRAWDATA_ONLYV2D;
1120                         break;
1121                 }
1122                 default: /* for spacetype not yet handled */
1123                         offsx = 0;
1124                         offsy = 0;
1125                         sizex = ar->winx;
1126                         sizey = ar->winy;
1127                         
1128                         dflag |= GP_DRAWDATA_ONLYI2D;
1129                         break;
1130         }
1131         
1132         
1133         /* draw it! */
1134         gp_draw_data(gpd, offsx, offsy, sizex, sizey, CFRA, dflag);
1135 }
1136
1137 /* draw grease-pencil sketches to specified 2d-view assuming that matrices are already set correctly 
1138  * Note: this gets called twice - first time with onlyv2d=1 to draw 'canvas' strokes,
1139  * second time with onlyv2d=0 for screen-aligned strokes */
1140 void ED_gpencil_draw_view2d(const bContext *C, bool onlyv2d)
1141 {
1142         ScrArea *sa = CTX_wm_area(C);
1143         ARegion *ar = CTX_wm_region(C);
1144         Scene *scene = CTX_data_scene(C);
1145         bGPdata *gpd;
1146         int dflag = 0;
1147         
1148         /* check that we have grease-pencil stuff to draw */
1149         if (sa == NULL) return;
1150         gpd = ED_gpencil_data_get_active(C); // XXX
1151         if (gpd == NULL) return;
1152         
1153         /* special hack for Image Editor */
1154         /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */
1155         if (ELEM(sa->spacetype, SPACE_IMAGE, SPACE_CLIP))
1156                 dflag |= GP_DRAWDATA_IEDITHACK;
1157         
1158         /* draw it! */
1159         if (onlyv2d) dflag |= (GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_NOSTATUS);
1160         gp_draw_data(gpd, 0, 0, ar->winx, ar->winy, CFRA, dflag);
1161 }
1162
1163 /* draw grease-pencil sketches to specified 3d-view assuming that matrices are already set correctly 
1164  * Note: this gets called twice - first time with only3d=1 to draw 3d-strokes,
1165  * second time with only3d=0 for screen-aligned strokes */
1166 void ED_gpencil_draw_view3d(Scene *scene, View3D *v3d, ARegion *ar, bool only3d)
1167 {
1168         bGPdata *gpd;
1169         int dflag = 0;
1170         RegionView3D *rv3d = ar->regiondata;
1171         int offsx,  offsy,  winx,  winy;
1172
1173         /* check that we have grease-pencil stuff to draw */
1174         gpd = ED_gpencil_data_get_active_v3d(scene, v3d);
1175         if (gpd == NULL) return;
1176
1177         /* when rendering to the offscreen buffer we don't want to
1178          * deal with the camera border, otherwise map the coords to the camera border. */
1179         if ((rv3d->persp == RV3D_CAMOB) && !(G.f & G_RENDER_OGL)) {
1180                 rctf rectf;
1181                 ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &rectf, true); /* no shift */
1182
1183                 offsx = iroundf(rectf.xmin);
1184                 offsy = iroundf(rectf.ymin);
1185                 winx  = iroundf(rectf.xmax - rectf.xmin);
1186                 winy  = iroundf(rectf.ymax - rectf.ymin);
1187         }
1188         else {
1189                 offsx = 0;
1190                 offsy = 0;
1191                 winx  = ar->winx;
1192                 winy  = ar->winy;
1193         }
1194         
1195         /* draw it! */
1196         if (only3d) dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS);
1197
1198         gp_draw_data(gpd, offsx, offsy, winx, winy, CFRA, dflag);
1199 }
1200
1201 void ED_gpencil_draw_ex(bGPdata *gpd, int winx, int winy, const int cfra)
1202 {
1203         int dflag = GP_DRAWDATA_NOSTATUS | GP_DRAWDATA_ONLYV2D;
1204
1205         gp_draw_data(gpd, 0, 0, winx, winy, cfra, dflag);
1206 }
1207
1208 /* ************************************************** */