1 /*
2  * Copyright (c) 2007 Ivan Leben
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library in the file COPYING;
16  * if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20 
21 #include <vg/openvg.h>
22 #include "shDefs.h"
23 #include "shExtensions.h"
24 #include "shContext.h"
25 #include "shPath.h"
26 #include "shImage.h"
27 #include "shGeometry.h"
28 #include "shPaint.h"
29 
shPremultiplyFramebuffer()30 void shPremultiplyFramebuffer()
31 {
32   /* Multiply target color with its own alpha */
33   glBlendFunc(GL_ZERO, GL_DST_ALPHA);
34 }
35 
shUnpremultiplyFramebuffer()36 void shUnpremultiplyFramebuffer()
37 {
38   /* TODO: hmmmm..... any idea? */
39 }
40 
updateBlendingStateGL(VGContext * c,int alphaIsOne)41 void updateBlendingStateGL(VGContext *c, int alphaIsOne)
42 {
43   /* Most common drawing mode (SRC_OVER with alpha=1)
44      as well as SRC is optimized by turning OpenGL
45      blending off. In other cases its turned on. */
46 
47   switch (c->blendMode)
48   {
49   case VG_BLEND_SRC:
50     glBlendFunc(GL_ONE, GL_ZERO);
51     glDisable(GL_BLEND); break;
52 
53   case VG_BLEND_SRC_IN:
54     glBlendFunc(GL_DST_ALPHA, GL_ZERO);
55     glEnable(GL_BLEND); break;
56 
57   case VG_BLEND_DST_IN:
58     glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
59     glEnable(GL_BLEND); break;
60 
61   case VG_BLEND_SRC_OUT_SH:
62     glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
63     glEnable(GL_BLEND); break;
64 
65   case VG_BLEND_DST_OUT_SH:
66     glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
67     glEnable(GL_BLEND); break;
68 
69   case VG_BLEND_SRC_ATOP_SH:
70     glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
71     glEnable(GL_BLEND); break;
72 
73   case VG_BLEND_DST_ATOP_SH:
74     glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
75     glEnable(GL_BLEND); break;
76 
77   case VG_BLEND_DST_OVER:
78     glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
79     glEnable(GL_BLEND); break;
80 
81   case VG_BLEND_SRC_OVER: default:
82     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
83     if (alphaIsOne) {
84       glDisable(GL_BLEND);
85     } else {
86       glEnable(GL_BLEND);
87     }
88     break;
89   };
90 }
91 
92 /*-----------------------------------------------------------
93  * Draws the triangles representing the stroke of a path.
94  *-----------------------------------------------------------*/
95 
shDrawStroke(SHPath * p)96 static void shDrawStroke(SHPath *p)
97 {
98   glEnableClientState(GL_VERTEX_ARRAY);
99   glVertexPointer(2, GL_FLOAT, 0, p->stroke.items);
100   glDrawArrays(GL_TRIANGLES, 0, p->stroke.size);
101   glDisableClientState(GL_VERTEX_ARRAY);
102 }
103 
104 /*-----------------------------------------------------------
105  * Draws the subdivided vertices in the OpenGL mode given
106  * (this could be VG_TRIANGLE_FAN or VG_LINE_STRIP).
107  *-----------------------------------------------------------*/
108 
shDrawVertices(SHPath * p,GLenum mode)109 static void shDrawVertices(SHPath *p, GLenum mode)
110 {
111   int start = 0;
112   int size = 0;
113 
114   /* We separate vertex arrays by contours to properly
115      handle the fill modes */
116   glEnableClientState(GL_VERTEX_ARRAY);
117   glVertexPointer(2, GL_FLOAT, sizeof(SHVertex), p->vertices.items);
118 
119   while (start < p->vertices.size) {
120     size = p->vertices.items[start].flags;
121     glDrawArrays(mode, start, size);
122     start += size;
123   }
124 
125   glDisableClientState(GL_VERTEX_ARRAY);
126 }
127 
128 /*-------------------------------------------------------------
129  * Draw a single quad that covers the bounding box of a path
130  *-------------------------------------------------------------*/
131 
shDrawBoundBox(VGContext * c,SHPath * p,VGPaintMode mode)132 static void shDrawBoundBox(VGContext *c, SHPath *p, VGPaintMode mode)
133 {
134   SHfloat K = 1.0f;
135   if (mode == VG_STROKE_PATH)
136     K = SH_CEIL(c->strokeMiterLimit * c->strokeLineWidth) + 1.0f;
137 
138   glBegin(GL_QUADS);
139   glVertex2f(p->min.x-K, p->min.y-K);
140   glVertex2f(p->max.x+K, p->min.y-K);
141   glVertex2f(p->max.x+K, p->max.y+K);
142   glVertex2f(p->min.x-K, p->max.y+K);
143   glEnd();
144 }
145 
146 /*--------------------------------------------------------------
147  * Constructs & draws colored OpenGL primitives that cover the
148  * given bounding box to represent the currently selected
149  * stroke or fill paint
150  *--------------------------------------------------------------*/
151 
shDrawPaintMesh(VGContext * c,SHVector2 * min,SHVector2 * max,VGPaintMode mode,GLenum texUnit)152 static void shDrawPaintMesh(VGContext *c, SHVector2 *min, SHVector2 *max,
153                             VGPaintMode mode, GLenum texUnit)
154 {
155   SHPaint *p = 0;
156   SHVector2 pmin, pmax;
157   SHfloat K = 1.0f;
158 
159   /* Pick the right paint */
160   if (mode == VG_FILL_PATH) {
161     p = (c->fillPaint ? c->fillPaint : &c->defaultPaint);
162   }else if (mode == VG_STROKE_PATH) {
163     p = (c->strokePaint ? c->strokePaint : &c->defaultPaint);
164     K = SH_CEIL(c->strokeMiterLimit * c->strokeLineWidth) + 1.0f;
165   }
166 
167   /* We want to be sure to cover every pixel of this path so better
168      take a pixel more than leave some out (multisampling is tricky). */
169   SET2V(pmin, (*min)); SUB2(pmin, K,K);
170   SET2V(pmax, (*max)); ADD2(pmax, K,K);
171 
172   /* Construct appropriate OpenGL primitives so as
173      to fill the stencil mask with select paint */
174 
175   switch (p->type) {
176   case VG_PAINT_TYPE_LINEAR_GRADIENT:
177     shDrawLinearGradientMesh(p, min, max, mode, texUnit);
178     break;
179 
180   case VG_PAINT_TYPE_RADIAL_GRADIENT:
181     shDrawRadialGradientMesh(p, min, max, mode, texUnit);
182     break;
183 
184   case VG_PAINT_TYPE_PATTERN:
185     if (p->pattern != VG_INVALID_HANDLE) {
186       shDrawPatternMesh(p, min, max, mode, texUnit);
187       break;
188     }/* else behave as a color paint */
189 
190   case VG_PAINT_TYPE_COLOR:
191     glColor4fv((GLfloat*)&p->color);
192     glBegin(GL_QUADS);
193     glVertex2f(pmin.x, pmin.y);
194     glVertex2f(pmax.x, pmin.y);
195     glVertex2f(pmax.x, pmax.y);
196     glVertex2f(pmin.x, pmax.y);
197     glEnd();
198     break;
199   }
200 }
201 
shIsTessCacheValid(VGContext * c,SHPath * p)202 VGboolean shIsTessCacheValid (VGContext *c, SHPath *p)
203 {
204   SHfloat nX, nY;
205   SHVector2 X, Y;
206   SHMatrix3x3 mi;//, mchange;
207   VGboolean valid = VG_TRUE;
208 
209   if (p->cacheDataValid == VG_FALSE) {
210     valid = VG_FALSE;
211   }
212   else if (p->cacheTransformInit == VG_FALSE) {
213     valid = VG_FALSE;
214   }
215   else if (shInvertMatrix( &p->cacheTransform, &mi ) == VG_FALSE) {
216     valid = VG_FALSE;
217   }
218   else
219   {
220     /* TODO: Compare change matrix for any scale or shear  */
221 //    MULMATMAT( c->pathTransform, mi, mchange );
222     SET2( X, mi.m[0][0], mi.m[1][0] );
223     SET2( Y, mi.m[0][1], mi.m[1][1] );
224     nX = NORM2( X ); nY = NORM2( Y );
225     if (nX > 1.01f || nX < 0.99 ||
226         nY > 1.01f || nY < 0.99)
227       valid = VG_FALSE;
228   }
229 
230   if (valid == VG_FALSE)
231   {
232     /* Update cache */
233     p->cacheDataValid = VG_TRUE;
234     p->cacheTransformInit = VG_TRUE;
235     p->cacheTransform = c->pathTransform;
236     p->cacheStrokeTessValid = VG_FALSE;
237   }
238 
239   return valid;
240 }
241 
shIsStrokeCacheValid(VGContext * c,SHPath * p)242 VGboolean shIsStrokeCacheValid (VGContext *c, SHPath *p)
243 {
244   VGboolean valid = VG_TRUE;
245 
246   if (p->cacheStrokeInit == VG_FALSE) {
247     valid = VG_FALSE;
248   }
249   else if (p->cacheStrokeTessValid == VG_FALSE) {
250     valid = VG_FALSE;
251   }
252   else if (c->strokeDashPattern.size > 0) {
253     valid = VG_FALSE;
254   }
255   else if (p->cacheStrokeLineWidth  != c->strokeLineWidth  ||
256            p->cacheStrokeCapStyle   != c->strokeCapStyle   ||
257            p->cacheStrokeJoinStyle  != c->strokeJoinStyle  ||
258            p->cacheStrokeMiterLimit != c->strokeMiterLimit) {
259     valid = VG_FALSE;
260   }
261 
262   if (valid == VG_FALSE)
263   {
264     /* Update cache */
265     p->cacheStrokeInit = VG_TRUE;
266     p->cacheStrokeTessValid = VG_TRUE;
267     p->cacheStrokeLineWidth  = c->strokeLineWidth;
268     p->cacheStrokeCapStyle   = c->strokeCapStyle;
269     p->cacheStrokeJoinStyle  = c->strokeJoinStyle;
270     p->cacheStrokeMiterLimit = c->strokeMiterLimit;
271   }
272 
273   return valid;
274 }
275 
276 /*-----------------------------------------------------------
277  * Tessellates / strokes the path and draws it according to
278  * VGContext state.
279  *-----------------------------------------------------------*/
280 
vgDrawPath(VGPath path,VGbitfield paintModes)281 VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
282 {
283   SHPath *p;
284   SHMatrix3x3 mi;
285   SHfloat mgl[16];
286   SHPaint *fill, *stroke;
287   SHRectangle *rect;
288 
289   VG_GETCONTEXT(VG_NO_RETVAL);
290 
291   VG_RETURN_ERR_IF(!shIsValidPath(context, path),
292                    VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
293 
294   VG_RETURN_ERR_IF(paintModes & (~(VG_STROKE_PATH | VG_FILL_PATH)),
295                    VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
296 
297   /* Check whether scissoring is enabled and scissor
298      rectangle is valid */
299   if (context->scissoring == VG_TRUE) {
300     rect = &context->scissor.items[0];
301     if (context->scissor.size == 0) VG_RETURN( VG_NO_RETVAL );
302     if (rect->w <= 0.0f || rect->h <= 0.0f) VG_RETURN( VG_NO_RETVAL );
303     glScissor( (GLint)rect->x, (GLint)rect->y, (GLint)rect->w, (GLint)rect->h );
304     glEnable( GL_SCISSOR_TEST );
305   }
306 
307   p = (SHPath*)path;
308 
309   /* If user-to-surface matrix invertible tessellate in
310      surface space for better path resolution */
311   if (shIsTessCacheValid( context, p ) == VG_FALSE)
312   {
313     if (shInvertMatrix(&context->pathTransform, &mi)) {
314       shFlattenPath(p, 1);
315       shTransformVertices(&mi, p);
316     }else shFlattenPath(p, 0);
317     shFindBoundbox(p);
318   }
319 
320   /* TODO: Turn antialiasing on/off */
321 //  glDisable(GL_LINE_SMOOTH);
322 //  glDisable(GL_POLYGON_SMOOTH);
323 //  glEnable(GL_MULTISAMPLE);
324 
325   /* Pick paint if available or default*/
326   fill = (context->fillPaint ? context->fillPaint : &context->defaultPaint);
327   stroke = (context->strokePaint ? context->strokePaint : &context->defaultPaint);
328 
329   /* Apply transformation */
330   shMatrixToGL(&context->pathTransform, mgl);
331   glMatrixMode(GL_MODELVIEW);
332   glPushMatrix();
333   glMultMatrixf(mgl);
334 
335   if (paintModes & VG_FILL_PATH) {
336 
337     /* Tesselate into stencil */
338     glEnable(GL_STENCIL_TEST);
339 
340     if( context->fillRule == VG_EVEN_ODD )
341     {
342       glStencilFunc(GL_ALWAYS, 0, 0);
343       glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
344     }
345     else
346     {
347       // pseudo non-zero fill rule. Fill everything at least covered once, don't
348       // care for possible decrements.
349       // TODO implement real non-zero fill-rule
350       glStencilFunc(GL_ALWAYS, 1, 1);
351       glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
352     }
353     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
354     shDrawVertices(p, GL_TRIANGLE_FAN);
355 
356     /* Setup blending */
357     updateBlendingStateGL(context,
358                           fill->type == VG_PAINT_TYPE_COLOR &&
359                           fill->color.a == 1.0f);
360 
361     /* Draw paint where stencil odd */
362     glStencilFunc(GL_EQUAL, 1, 1);
363     glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
364     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
365     shDrawPaintMesh(context, &p->min, &p->max, VG_FILL_PATH, GL_TEXTURE0);
366 
367     /* Clear stencil for sure */
368     /* TODO: Is there any way to do this safely along
369        with the paint generation pass?? */
370     glDisable(GL_BLEND);
371 //    glDisable(GL_MULTISAMPLE);
372     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
373     shDrawBoundBox(context, p, VG_FILL_PATH);
374 
375     /* Reset state */
376     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
377     glDisable(GL_STENCIL_TEST);
378 //    glDisable(GL_BLEND);
379   }
380 
381   /* TODO: Turn antialiasing on/off */
382 //  glDisable(GL_LINE_SMOOTH);
383 //  glDisable(GL_POLYGON_SMOOTH);
384 //  glEnable(GL_MULTISAMPLE);
385 
386   if ((paintModes & VG_STROKE_PATH) &&
387       context->strokeLineWidth > 0.0f) {
388 
389 #if 0
390     if (1) {/*context->strokeLineWidth > 1.0f) {*/
391 #endif
392       if (shIsStrokeCacheValid( context, p ) == VG_FALSE)
393       {
394         /* Generate stroke triangles in user space */
395         shVector2ArrayClear(&p->stroke);
396         shStrokePath(context, p);
397       }
398 
399       /* Stroke into stencil */
400       glEnable(GL_STENCIL_TEST);
401       glStencilFunc(GL_NOTEQUAL, 1, 1);
402       glStencilOp(GL_KEEP, GL_INCR, GL_INCR);
403       glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
404       shDrawStroke(p);
405 
406       /* Setup blending */
407       updateBlendingStateGL(context,
408                             stroke->type == VG_PAINT_TYPE_COLOR &&
409                             stroke->color.a == 1.0f);
410 
411       /* Draw paint where stencil odd */
412       glStencilFunc(GL_EQUAL, 1, 1);
413       glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
414       glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
415       shDrawPaintMesh(context, &p->min, &p->max, VG_STROKE_PATH, GL_TEXTURE0);
416 
417       /* Clear stencil for sure */
418       glDisable(GL_BLEND);
419 //      glDisable(GL_MULTISAMPLE);
420       glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
421       shDrawBoundBox(context, p, VG_STROKE_PATH);
422 
423       /* Reset state */
424       glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
425       glDisable(GL_STENCIL_TEST);
426 //      glDisable(GL_BLEND);
427 #if 0
428     }else{
429 
430       /* Simulate thin stroke by alpha */
431       SHColor c = stroke->color;
432       if (context->strokeLineWidth < 1.0f)
433         c.a *= context->strokeLineWidth;
434 
435       /* Draw contour as a line */
436       glDisable(GL_MULTISAMPLE);
437       glEnable(GL_BLEND);
438       glEnable(GL_LINE_SMOOTH);
439       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
440       glColor4fv((GLfloat*)&c);
441       shDrawVertices(p, GL_LINE_STRIP);
442 
443       glDisable(GL_BLEND);
444       glDisable(GL_LINE_SMOOTH);
445     }
446 #endif
447   }
448 
449 
450   glPopMatrix();
451 
452   if (context->scissoring == VG_TRUE)
453     glDisable( GL_SCISSOR_TEST );
454 
455   VG_RETURN(VG_NO_RETVAL);
456 }
457 
vgDrawImage(VGImage image)458 VG_API_CALL void vgDrawImage(VGImage image)
459 {
460   SHImage *i;
461   SHfloat mgl[16];
462   SHfloat texGenS[4] = {0,0,0,0};
463   SHfloat texGenT[4] = {0,0,0,0};
464   SHPaint *fill;
465   SHVector2 min, max;
466   SHRectangle *rect;
467 
468   VG_GETCONTEXT(VG_NO_RETVAL);
469 
470   VG_RETURN_ERR_IF(!shIsValidImage(context, image),
471                    VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
472 
473   /* TODO: check if image is current render target */
474 
475   /* Check whether scissoring is enabled and scissor
476      rectangle is valid */
477   if (context->scissoring == VG_TRUE) {
478     rect = &context->scissor.items[0];
479     if (context->scissor.size == 0) VG_RETURN( VG_NO_RETVAL );
480     if (rect->w <= 0.0f || rect->h <= 0.0f) VG_RETURN( VG_NO_RETVAL );
481     glScissor( (GLint)rect->x, (GLint)rect->y, (GLint)rect->w, (GLint)rect->h );
482     glEnable( GL_SCISSOR_TEST );
483   }
484 
485   /* Apply image-user-to-surface transformation */
486   i = (SHImage*)image;
487   shMatrixToGL(&context->imageTransform, mgl);
488   glMatrixMode(GL_MODELVIEW);
489   glPushMatrix();
490   glMultMatrixf(mgl);
491 
492   /* Clamp to edge for proper filtering, modulate for multiply mode */
493   glActiveTexture(GL_TEXTURE0);
494   glBindTexture(GL_TEXTURE_2D, i->texture);
495   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
496   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
497   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
498 
499   /* Adjust antialiasing to settings */
500   if (context->imageQuality == VG_IMAGE_QUALITY_NONANTIALIASED) {
501     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
502     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
503     glDisable(GL_MULTISAMPLE);
504   }else{
505     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
506     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
507     glEnable(GL_MULTISAMPLE);
508   }
509 
510   /* Generate image texture coords automatically */
511   texGenS[0] = 1.0f / i->texwidth;
512   texGenT[1] = 1.0f / i->texheight;
513   glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
514   glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
515   glTexGenfv(GL_S, GL_OBJECT_PLANE, texGenS);
516   glTexGenfv(GL_T, GL_OBJECT_PLANE, texGenT);
517   glEnable(GL_TEXTURE_GEN_S);
518   glEnable(GL_TEXTURE_GEN_T);
519 
520   /* Pick fill paint */
521   fill = (context->fillPaint ? context->fillPaint : &context->defaultPaint);
522 
523   /* Use paint color when multiplying with a color-paint */
524   if (context->imageMode == VG_DRAW_IMAGE_MULTIPLY &&
525       fill->type == VG_PAINT_TYPE_COLOR)
526       glColor4fv((GLfloat*)&fill->color);
527   else glColor4f(1,1,1,1);
528 
529 
530   /* Check image drawing mode */
531   if (context->imageMode == VG_DRAW_IMAGE_MULTIPLY &&
532       fill->type != VG_PAINT_TYPE_COLOR) {
533 
534     /* Draw image quad into stencil */
535     glDisable(GL_BLEND);
536     glDisable(GL_TEXTURE_2D);
537     glEnable(GL_STENCIL_TEST);
538     glStencilFunc(GL_ALWAYS, 1, 1);
539     glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
540     glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
541 
542     glBegin(GL_QUADS);
543     glVertex2i(0, 0);
544     glVertex2i(i->width, 0);
545     glVertex2i(i->width, i->height);
546     glVertex2i(0, i->height);
547     glEnd();
548 
549     /* Setup blending */
550     updateBlendingStateGL(context, 0);
551 
552     /* Draw gradient mesh where stencil 1*/
553     glEnable(GL_TEXTURE_2D);
554     glStencilFunc(GL_EQUAL, 1, 1);
555     glStencilOp(GL_ZERO,GL_ZERO,GL_ZERO);
556     glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
557 
558     SET2(min,0,0);
559     SET2(max, (SHfloat)i->width, (SHfloat)i->height);
560     if (fill->type == VG_PAINT_TYPE_RADIAL_GRADIENT) {
561       shDrawRadialGradientMesh(fill, &min, &max, VG_FILL_PATH, GL_TEXTURE1);
562     }else if (fill->type == VG_PAINT_TYPE_LINEAR_GRADIENT) {
563       shDrawLinearGradientMesh(fill, &min, &max, VG_FILL_PATH, GL_TEXTURE1);
564     }else if (fill->type == VG_PAINT_TYPE_PATTERN) {
565       shDrawPatternMesh(fill, &min, &max, VG_FILL_PATH, GL_TEXTURE1); }
566 
567     glActiveTexture(GL_TEXTURE0);
568     glDisable(GL_TEXTURE_2D);
569     glDisable(GL_STENCIL_TEST);
570 
571   }else if (context->imageMode == VG_DRAW_IMAGE_STENCIL) {
572 
573 
574   }else{/* Either normal mode or multiplying with a color-paint */
575 
576     /* Setup blending */
577     updateBlendingStateGL(context, 0);
578 
579     /* Draw textured quad */
580     glEnable(GL_TEXTURE_2D);
581 
582     glBegin(GL_QUADS);
583     glVertex2i(0, 0);
584     glVertex2i(i->width, 0);
585     glVertex2i(i->width, i->height);
586     glVertex2i(0, i->height);
587     glEnd();
588 
589     glDisable(GL_TEXTURE_2D);
590   }
591 
592 
593   glDisable(GL_TEXTURE_GEN_S);
594   glDisable(GL_TEXTURE_GEN_T);
595   glPopMatrix();
596 
597   if (context->scissoring == VG_TRUE)
598     glDisable( GL_SCISSOR_TEST );
599 
600   VG_RETURN(VG_NO_RETVAL);
601 }
602