2 * Copyright (c) 2007 Ivan Leben
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.
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.
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
21 #include <vg/openvg.h>
23 #include "shExtensions.h"
24 #include "shContext.h"
27 #include "shGeometry.h"
30 void shPremultiplyFramebuffer()
32 /* Multiply target color with its own alpha */
33 glBlendFunc(GL_ZERO, GL_DST_ALPHA);
36 void shUnpremultiplyFramebuffer()
38 /* TODO: hmmmm..... any idea? */
41 void updateBlendingStateGL(VGContext *c, int alphaIsOne)
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. */
50 glBlendFunc(GL_ONE, GL_ZERO);
51 glDisable(GL_BLEND); break;
54 glBlendFunc(GL_DST_ALPHA, GL_ZERO);
55 glEnable(GL_BLEND); break;
58 glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
59 glEnable(GL_BLEND); break;
61 case VG_BLEND_SRC_OUT_SH:
62 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
63 glEnable(GL_BLEND); break;
65 case VG_BLEND_DST_OUT_SH:
66 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
67 glEnable(GL_BLEND); break;
69 case VG_BLEND_SRC_ATOP_SH:
70 glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
71 glEnable(GL_BLEND); break;
73 case VG_BLEND_DST_ATOP_SH:
74 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
75 glEnable(GL_BLEND); break;
77 case VG_BLEND_DST_OVER:
78 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
79 glEnable(GL_BLEND); break;
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;
88 /*-----------------------------------------------------------
89 * Draws the triangles representing the stroke of a path.
90 *-----------------------------------------------------------*/
92 static void shDrawStroke(SHPath *p)
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);
100 /*-----------------------------------------------------------
101 * Draws the subdivided vertices in the OpenGL mode given
102 * (this could be VG_TRIANGLE_FAN or VG_LINE_STRIP).
103 *-----------------------------------------------------------*/
105 static void shDrawVertices(SHPath *p, GLenum mode)
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);
115 while (start < p->vertices.size) {
116 size = p->vertices.items[start].flags;
117 glDrawArrays(mode, start, size);
121 glDisableClientState(GL_VERTEX_ARRAY);
124 /*-------------------------------------------------------------
125 * Draw a single quad that covers the bounding box of a path
126 *-------------------------------------------------------------*/
128 static void shDrawBoundBox(VGContext *c, SHPath *p, VGPaintMode mode)
131 if (mode == VG_STROKE_PATH)
132 K = SH_CEIL(c->strokeMiterLimit * c->strokeLineWidth) + 1.0f;
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);
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 *--------------------------------------------------------------*/
148 static void shDrawPaintMesh(VGContext *c, SHVector2 *min, SHVector2 *max,
149 VGPaintMode mode, GLenum texUnit)
152 SHVector2 pmin, pmax;
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;
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);
168 /* Construct appropriate OpenGL primitives so as
169 to fill the stencil mask with select paint */
172 case VG_PAINT_TYPE_LINEAR_GRADIENT:
173 shDrawLinearGradientMesh(p, min, max, mode, texUnit);
176 case VG_PAINT_TYPE_RADIAL_GRADIENT:
177 shDrawRadialGradientMesh(p, min, max, mode, texUnit);
180 case VG_PAINT_TYPE_PATTERN:
181 if (p->pattern != VG_INVALID_HANDLE) {
182 shDrawPatternMesh(p, min, max, mode, texUnit);
184 }/* else behave as a color paint */
186 case VG_PAINT_TYPE_COLOR:
187 glColor4fv((GLfloat*)&p->color);
189 glVertex2f(pmin.x, pmin.y);
190 glVertex2f(pmax.x, pmin.y);
191 glVertex2f(pmax.x, pmax.y);
192 glVertex2f(pmin.x, pmax.y);
198 VGboolean shIsTessCacheValid (VGContext *c, SHPath *p)
202 SHMatrix3x3 mi;//, mchange;
203 VGboolean valid = VG_TRUE;
205 if (p->cacheDataValid == VG_FALSE) {
208 else if (p->cacheTransformInit == VG_FALSE) {
211 else if (shInvertMatrix( &p->cacheTransform, &mi ) == VG_FALSE) {
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)
226 if (valid == VG_FALSE)
229 p->cacheDataValid = VG_TRUE;
230 p->cacheTransformInit = VG_TRUE;
231 p->cacheTransform = c->pathTransform;
232 p->cacheStrokeTessValid = VG_FALSE;
238 VGboolean shIsStrokeCacheValid (VGContext *c, SHPath *p)
240 VGboolean valid = VG_TRUE;
242 if (p->cacheStrokeInit == VG_FALSE) {
245 else if (p->cacheStrokeTessValid == VG_FALSE) {
248 else if (c->strokeDashPattern.size > 0) {
251 else if (p->cacheStrokeLineWidth != c->strokeLineWidth ||
252 p->cacheStrokeCapStyle != c->strokeCapStyle ||
253 p->cacheStrokeJoinStyle != c->strokeJoinStyle ||
254 p->cacheStrokeMiterLimit != c->strokeMiterLimit) {
258 if (valid == VG_FALSE)
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;
272 /*-----------------------------------------------------------
273 * Tessellates / strokes the path and draws it according to
275 *-----------------------------------------------------------*/
277 VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
282 SHPaint *fill, *stroke;
285 VG_GETCONTEXT(VG_NO_RETVAL);
287 VG_RETURN_ERR_IF(!shIsValidPath(context, path),
288 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
290 VG_RETURN_ERR_IF(paintModes & (~(VG_STROKE_PATH | VG_FILL_PATH)),
291 VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
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 );
305 /* If user-to-surface matrix invertible tessellate in
306 surface space for better path resolution */
307 if (shIsTessCacheValid( context, p ) == VG_FALSE)
309 if (shInvertMatrix(&context->pathTransform, &mi)) {
311 shTransformVertices(&mi, p);
312 }else shFlattenPath(p, 0);
316 /* TODO: Turn antialiasing on/off */
317 // glDisable(GL_LINE_SMOOTH);
318 // glDisable(GL_POLYGON_SMOOTH);
319 // glEnable(GL_MULTISAMPLE);
321 /* Pick paint if available or default*/
322 fill = (context->fillPaint ? context->fillPaint : &context->defaultPaint);
323 stroke = (context->strokePaint ? context->strokePaint : &context->defaultPaint);
325 /* Apply transformation */
326 shMatrixToGL(&context->pathTransform, mgl);
327 glMatrixMode(GL_MODELVIEW);
331 if (paintModes & VG_FILL_PATH) {
333 /* Tesselate into stencil */
334 glEnable(GL_STENCIL_TEST);
336 if( context->fillRule == VG_EVEN_ODD )
338 glStencilFunc(GL_ALWAYS, 0, 0);
339 glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
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);
349 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
350 shDrawVertices(p, GL_TRIANGLE_FAN);
353 updateBlendingStateGL(context,
354 fill->type == VG_PAINT_TYPE_COLOR &&
355 fill->color.a == 1.0f);
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);
363 /* Clear stencil for sure */
364 /* TODO: Is there any way to do this safely along
365 with the paint generation pass?? */
367 // glDisable(GL_MULTISAMPLE);
368 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
369 shDrawBoundBox(context, p, VG_FILL_PATH);
372 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
373 glDisable(GL_STENCIL_TEST);
374 // glDisable(GL_BLEND);
377 /* TODO: Turn antialiasing on/off */
378 // glDisable(GL_LINE_SMOOTH);
379 // glDisable(GL_POLYGON_SMOOTH);
380 // glEnable(GL_MULTISAMPLE);
382 if ((paintModes & VG_STROKE_PATH) &&
383 context->strokeLineWidth > 0.0f) {
386 if (1) {/*context->strokeLineWidth > 1.0f) {*/
388 if (shIsStrokeCacheValid( context, p ) == VG_FALSE)
390 /* Generate stroke triangles in user space */
391 shVector2ArrayClear(&p->stroke);
392 shStrokePath(context, p);
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);
403 updateBlendingStateGL(context,
404 stroke->type == VG_PAINT_TYPE_COLOR &&
405 stroke->color.a == 1.0f);
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);
413 /* Clear stencil for sure */
415 // glDisable(GL_MULTISAMPLE);
416 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
417 shDrawBoundBox(context, p, VG_STROKE_PATH);
420 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
421 glDisable(GL_STENCIL_TEST);
422 // glDisable(GL_BLEND);
426 /* Simulate thin stroke by alpha */
427 SHColor c = stroke->color;
428 if (context->strokeLineWidth < 1.0f)
429 c.a *= context->strokeLineWidth;
431 /* Draw contour as a line */
432 glDisable(GL_MULTISAMPLE);
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);
440 glDisable(GL_LINE_SMOOTH);
448 if (context->scissoring == VG_TRUE)
449 glDisable( GL_SCISSOR_TEST );
451 VG_RETURN(VG_NO_RETVAL);
454 VG_API_CALL void vgDrawImage(VGImage image)
458 SHfloat texGenS[4] = {0,0,0,0};
459 SHfloat texGenT[4] = {0,0,0,0};
464 VG_GETCONTEXT(VG_NO_RETVAL);
466 VG_RETURN_ERR_IF(!shIsValidImage(context, image),
467 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
469 /* TODO: check if image is current render target */
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 );
481 /* Apply image-user-to-surface transformation */
483 shMatrixToGL(&context->imageTransform, mgl);
484 glMatrixMode(GL_MODELVIEW);
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);
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);
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);
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);
516 /* Pick fill paint */
517 fill = (context->fillPaint ? context->fillPaint : &context->defaultPaint);
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);
526 /* Check image drawing mode */
527 if (context->imageMode == VG_DRAW_IMAGE_MULTIPLY &&
528 fill->type != VG_PAINT_TYPE_COLOR) {
530 /* Draw image quad into stencil */
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);
540 glVertex2i(i->width, 0);
541 glVertex2i(i->width, i->height);
542 glVertex2i(0, i->height);
546 updateBlendingStateGL(context, 0);
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);
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); }
563 glActiveTexture(GL_TEXTURE0);
564 glDisable(GL_TEXTURE_2D);
565 glDisable(GL_STENCIL_TEST);
567 }else if (context->imageMode == VG_DRAW_IMAGE_STENCIL) {
570 }else{/* Either normal mode or multiplying with a color-paint */
573 updateBlendingStateGL(context, 0);
575 /* Draw textured quad */
576 glEnable(GL_TEXTURE_2D);
580 glVertex2i(i->width, 0);
581 glVertex2i(i->width, i->height);
582 glVertex2i(0, i->height);
585 glDisable(GL_TEXTURE_2D);
589 glDisable(GL_TEXTURE_GEN_S);
590 glDisable(GL_TEXTURE_GEN_T);
593 if (context->scissoring == VG_TRUE)
594 glDisable( GL_SCISSOR_TEST );
596 VG_RETURN(VG_NO_RETVAL);