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);
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);
341 updateBlendingStateGL(context,
342 fill->type == VG_PAINT_TYPE_COLOR &&
343 fill->color.a == 1.0f);
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);
351 /* Clear stencil for sure */
352 /* TODO: Is there any way to do this safely along
353 with the paint generation pass?? */
355 glDisable(GL_MULTISAMPLE);
356 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
357 shDrawBoundBox(context, p, VG_FILL_PATH);
360 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
361 glDisable(GL_STENCIL_TEST);
365 /* TODO: Turn antialiasing on/off */
366 glDisable(GL_LINE_SMOOTH);
367 glDisable(GL_POLYGON_SMOOTH);
368 glEnable(GL_MULTISAMPLE);
370 if ((paintModes & VG_STROKE_PATH) &&
371 context->strokeLineWidth > 0.0f) {
373 if (1) {/*context->strokeLineWidth > 1.0f) {*/
375 if (shIsStrokeCacheValid( context, p ) == VG_FALSE)
377 /* Generate stroke triangles in user space */
378 shVector2ArrayClear(&p->stroke);
379 shStrokePath(context, p);
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);
390 updateBlendingStateGL(context,
391 stroke->type == VG_PAINT_TYPE_COLOR &&
392 stroke->color.a == 1.0f);
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);
400 /* Clear stencil for sure */
402 glDisable(GL_MULTISAMPLE);
403 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
404 shDrawBoundBox(context, p, VG_STROKE_PATH);
407 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
408 glDisable(GL_STENCIL_TEST);
413 /* Simulate thin stroke by alpha */
414 SHColor c = stroke->color;
415 if (context->strokeLineWidth < 1.0f)
416 c.a *= context->strokeLineWidth;
418 /* Draw contour as a line */
419 glDisable(GL_MULTISAMPLE);
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);
427 glDisable(GL_LINE_SMOOTH);
434 if (context->scissoring == VG_TRUE)
435 glDisable( GL_SCISSOR_TEST );
437 VG_RETURN(VG_NO_RETVAL);
440 VG_API_CALL void vgDrawImage(VGImage image)
444 SHfloat texGenS[4] = {0,0,0,0};
445 SHfloat texGenT[4] = {0,0,0,0};
450 VG_GETCONTEXT(VG_NO_RETVAL);
452 VG_RETURN_ERR_IF(!shIsValidImage(context, image),
453 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
455 /* TODO: check if image is current render target */
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 );
467 /* Apply image-user-to-surface transformation */
469 shMatrixToGL(&context->imageTransform, mgl);
470 glMatrixMode(GL_MODELVIEW);
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);
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);
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);
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);
502 /* Pick fill paint */
503 fill = (context->fillPaint ? context->fillPaint : &context->defaultPaint);
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);
512 /* Check image drawing mode */
513 if (context->imageMode == VG_DRAW_IMAGE_MULTIPLY &&
514 fill->type != VG_PAINT_TYPE_COLOR) {
516 /* Draw image quad into stencil */
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);
526 glVertex2i(i->width, 0);
527 glVertex2i(i->width, i->height);
528 glVertex2i(0, i->height);
532 updateBlendingStateGL(context, 0);
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);
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); }
549 glActiveTexture(GL_TEXTURE0);
550 glDisable(GL_TEXTURE_2D);
551 glDisable(GL_STENCIL_TEST);
553 }else if (context->imageMode == VG_DRAW_IMAGE_STENCIL) {
556 }else{/* Either normal mode or multiplying with a color-paint */
559 updateBlendingStateGL(context, 0);
561 /* Draw textured quad */
562 glEnable(GL_TEXTURE_2D);
566 glVertex2i(i->width, 0);
567 glVertex2i(i->width, i->height);
568 glVertex2i(0, i->height);
571 glDisable(GL_TEXTURE_2D);
575 glDisable(GL_TEXTURE_GEN_S);
576 glDisable(GL_TEXTURE_GEN_T);
579 if (context->scissoring == VG_TRUE)
580 glDisable( GL_SCISSOR_TEST );
582 VG_RETURN(VG_NO_RETVAL);