]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/ShivaVG/src/shPipeline.c
Fix a Clang warning in Shiva.
[flightgear.git] / src / 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     glStencilFunc(GL_ALWAYS, 0, 0);
336     glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
337     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
338     shDrawVertices(p, GL_TRIANGLE_FAN);
339     
340     /* Setup blending */
341     updateBlendingStateGL(context,
342                           fill->type == VG_PAINT_TYPE_COLOR &&
343                           fill->color.a == 1.0f);
344     
345     /* Draw paint where stencil odd */
346     glStencilFunc(GL_EQUAL, 1, 1);
347     glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
348     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
349     shDrawPaintMesh(context, &p->min, &p->max, VG_FILL_PATH, GL_TEXTURE0);
350
351     /* Clear stencil for sure */
352     /* TODO: Is there any way to do this safely along
353        with the paint generation pass?? */
354     glDisable(GL_BLEND);
355     glDisable(GL_MULTISAMPLE);
356     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
357     shDrawBoundBox(context, p, VG_FILL_PATH);
358     
359     /* Reset state */
360     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
361     glDisable(GL_STENCIL_TEST);
362     glDisable(GL_BLEND);
363   }
364   
365   /* TODO: Turn antialiasing on/off */
366   glDisable(GL_LINE_SMOOTH);
367   glDisable(GL_POLYGON_SMOOTH);
368   glEnable(GL_MULTISAMPLE);
369   
370   if ((paintModes & VG_STROKE_PATH) &&
371       context->strokeLineWidth > 0.0f) {
372     
373     if (1) {/*context->strokeLineWidth > 1.0f) {*/
374
375       if (shIsStrokeCacheValid( context, p ) == VG_FALSE)
376       {
377         /* Generate stroke triangles in user space */
378         shVector2ArrayClear(&p->stroke);
379         shStrokePath(context, p);
380       }
381
382       /* Stroke into stencil */
383       glEnable(GL_STENCIL_TEST);
384       glStencilFunc(GL_NOTEQUAL, 1, 1);
385       glStencilOp(GL_KEEP, GL_INCR, GL_INCR);
386       glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
387       shDrawStroke(p);
388
389       /* Setup blending */
390       updateBlendingStateGL(context,
391                             stroke->type == VG_PAINT_TYPE_COLOR &&
392                             stroke->color.a == 1.0f);
393
394       /* Draw paint where stencil odd */
395       glStencilFunc(GL_EQUAL, 1, 1);
396       glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
397       glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
398       shDrawPaintMesh(context, &p->min, &p->max, VG_STROKE_PATH, GL_TEXTURE0);
399       
400       /* Clear stencil for sure */
401       glDisable(GL_BLEND);
402       glDisable(GL_MULTISAMPLE);
403       glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
404       shDrawBoundBox(context, p, VG_STROKE_PATH);
405       
406       /* Reset state */
407       glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
408       glDisable(GL_STENCIL_TEST);
409       glDisable(GL_BLEND);
410       
411     }else{
412       
413       /* Simulate thin stroke by alpha */
414       SHColor c = stroke->color;
415       if (context->strokeLineWidth < 1.0f)
416         c.a *= context->strokeLineWidth;
417       
418       /* Draw contour as a line */
419       glDisable(GL_MULTISAMPLE);
420       glEnable(GL_BLEND);
421       glEnable(GL_LINE_SMOOTH);
422       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
423       glColor4fv((GLfloat*)&c);
424       shDrawVertices(p, GL_LINE_STRIP);
425       
426       glDisable(GL_BLEND);
427       glDisable(GL_LINE_SMOOTH);
428     }
429   }
430   
431   
432   glPopMatrix();
433   
434   if (context->scissoring == VG_TRUE)
435     glDisable( GL_SCISSOR_TEST );
436
437   VG_RETURN(VG_NO_RETVAL);
438 }
439
440 VG_API_CALL void vgDrawImage(VGImage image)
441 {
442   SHImage *i;
443   SHfloat mgl[16];
444   SHfloat texGenS[4] = {0,0,0,0};
445   SHfloat texGenT[4] = {0,0,0,0};
446   SHPaint *fill;
447   SHVector2 min, max;
448   SHRectangle *rect;
449   
450   VG_GETCONTEXT(VG_NO_RETVAL);
451   
452   VG_RETURN_ERR_IF(!shIsValidImage(context, image),
453                    VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
454
455   /* TODO: check if image is current render target */
456   
457   /* Check whether scissoring is enabled and scissor
458      rectangle is valid */
459   if (context->scissoring == VG_TRUE) {
460     rect = &context->scissor.items[0];
461     if (context->scissor.size == 0) VG_RETURN( VG_NO_RETVAL );
462     if (rect->w <= 0.0f || rect->h <= 0.0f) VG_RETURN( VG_NO_RETVAL );
463     glScissor( (GLint)rect->x, (GLint)rect->y, (GLint)rect->w, (GLint)rect->h );
464     glEnable( GL_SCISSOR_TEST );
465   }
466   
467   /* Apply image-user-to-surface transformation */
468   i = (SHImage*)image;
469   shMatrixToGL(&context->imageTransform, mgl);
470   glMatrixMode(GL_MODELVIEW);
471   glPushMatrix();
472   glMultMatrixf(mgl);
473   
474   /* Clamp to edge for proper filtering, modulate for multiply mode */
475   glActiveTexture(GL_TEXTURE0);
476   glBindTexture(GL_TEXTURE_2D, i->texture);
477   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
478   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
479   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
480   
481   /* Adjust antialiasing to settings */
482   if (context->imageQuality == VG_IMAGE_QUALITY_NONANTIALIASED) {
483     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
484     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
485     glDisable(GL_MULTISAMPLE);
486   }else{
487     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
488     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
489     glEnable(GL_MULTISAMPLE);
490   }
491   
492   /* Generate image texture coords automatically */
493   texGenS[0] = 1.0f / i->texwidth;
494   texGenT[1] = 1.0f / i->texheight;
495   glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
496   glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
497   glTexGenfv(GL_S, GL_OBJECT_PLANE, texGenS);
498   glTexGenfv(GL_T, GL_OBJECT_PLANE, texGenT);
499   glEnable(GL_TEXTURE_GEN_S);
500   glEnable(GL_TEXTURE_GEN_T);
501   
502   /* Pick fill paint */
503   fill = (context->fillPaint ? context->fillPaint : &context->defaultPaint);
504   
505   /* Use paint color when multiplying with a color-paint */
506   if (context->imageMode == VG_DRAW_IMAGE_MULTIPLY &&
507       fill->type == VG_PAINT_TYPE_COLOR)
508       glColor4fv((GLfloat*)&fill->color);
509   else glColor4f(1,1,1,1);
510   
511   
512   /* Check image drawing mode */
513   if (context->imageMode == VG_DRAW_IMAGE_MULTIPLY &&
514       fill->type != VG_PAINT_TYPE_COLOR) {
515     
516     /* Draw image quad into stencil */
517     glDisable(GL_BLEND);
518     glDisable(GL_TEXTURE_2D);
519     glEnable(GL_STENCIL_TEST);
520     glStencilFunc(GL_ALWAYS, 1, 1);
521     glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
522     glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
523     
524     glBegin(GL_QUADS);
525     glVertex2i(0, 0);
526     glVertex2i(i->width, 0);
527     glVertex2i(i->width, i->height);
528     glVertex2i(0, i->height);
529     glEnd();
530
531     /* Setup blending */
532     updateBlendingStateGL(context, 0);
533     
534     /* Draw gradient mesh where stencil 1*/
535     glEnable(GL_TEXTURE_2D);
536     glStencilFunc(GL_EQUAL, 1, 1);
537     glStencilOp(GL_ZERO,GL_ZERO,GL_ZERO);
538     glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
539     
540     SET2(min,0,0);
541     SET2(max, (SHfloat)i->width, (SHfloat)i->height);
542     if (fill->type == VG_PAINT_TYPE_RADIAL_GRADIENT) {
543       shDrawRadialGradientMesh(fill, &min, &max, VG_FILL_PATH, GL_TEXTURE1);
544     }else if (fill->type == VG_PAINT_TYPE_LINEAR_GRADIENT) {
545       shDrawLinearGradientMesh(fill, &min, &max, VG_FILL_PATH, GL_TEXTURE1);
546     }else if (fill->type == VG_PAINT_TYPE_PATTERN) {
547       shDrawPatternMesh(fill, &min, &max, VG_FILL_PATH, GL_TEXTURE1); }
548     
549     glActiveTexture(GL_TEXTURE0);
550     glDisable(GL_TEXTURE_2D);
551     glDisable(GL_STENCIL_TEST);
552     
553   }else if (context->imageMode == VG_DRAW_IMAGE_STENCIL) {
554     
555     
556   }else{/* Either normal mode or multiplying with a color-paint */
557     
558     /* Setup blending */
559     updateBlendingStateGL(context, 0);
560
561     /* Draw textured quad */
562     glEnable(GL_TEXTURE_2D);
563     
564     glBegin(GL_QUADS);
565     glVertex2i(0, 0);
566     glVertex2i(i->width, 0);
567     glVertex2i(i->width, i->height);
568     glVertex2i(0, i->height);
569     glEnd();
570     
571     glDisable(GL_TEXTURE_2D);
572   }
573   
574   
575   glDisable(GL_TEXTURE_GEN_S);
576   glDisable(GL_TEXTURE_GEN_T);
577   glPopMatrix();
578
579   if (context->scissoring == VG_TRUE)
580     glDisable( GL_SCISSOR_TEST );
581   
582   VG_RETURN(VG_NO_RETVAL);
583 }