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