void mat4_inversed(GLdouble* d, GLdouble* s) { GLfloat in[16], out[16]; GLint i; for (i = 0; i < 16; i++) { in[i] = (GLfloat)s[i]; } shInvertMatrix(out, in); for (i = 0; i < 16; i++) { d[i] = (GLdouble)out[i]; } }
VGboolean shIsTessCacheValid (VGContext *c, SHPath *p) { SHfloat nX, nY; SHVector2 X, Y; SHMatrix3x3 mi, mchange; VGboolean valid = VG_TRUE; if (p->cacheDataValid == VG_FALSE) { valid = VG_FALSE; } else if (p->cacheTransformInit == VG_FALSE) { valid = VG_FALSE; } else if (shInvertMatrix( &p->cacheTransform, &mi ) == VG_FALSE) { valid = VG_FALSE; } else { /* TODO: Compare change matrix for any scale or shear */ MULMATMAT( c->pathTransform, mi, mchange ); SET2( X, mi.m[0][0], mi.m[1][0] ); SET2( Y, mi.m[0][1], mi.m[1][1] ); nX = NORM2( X ); nY = NORM2( Y ); if (nX > 1.01f || nX < 0.99 || nY > 1.01f || nY < 0.99) valid = VG_FALSE; } if (valid == VG_FALSE) { /* Update cache */ p->cacheDataValid = VG_TRUE; p->cacheTransformInit = VG_TRUE; p->cacheTransform = c->pathTransform; p->cacheStrokeTessValid = VG_FALSE; } return valid; }
int shDrawPatternMesh(SHPaint *p, SHVector2 *min, SHVector2 *max, VGPaintMode mode, GLenum texUnit) { SHMatrix3x3 *m; SHMatrix3x3 mi; SHfloat migl[16]; SHint invertible; SHVector2 corners[4]; VGfloat sx, sy; SHImage *img; int i; /* Pick paint transform matrix */ SH_GETCONTEXT(0); if (mode == VG_FILL_PATH) m = &context->fillTransform; else if (mode == VG_STROKE_PATH) m = &context->strokeTransform; /* Boundbox corners */ SET2(corners[0], min->x, min->y); SET2(corners[1], max->x, min->y); SET2(corners[2], max->x, max->y); SET2(corners[3], min->x, max->y); /* Find inverse transformation (back to paint space) */ invertible = shInvertMatrix(m, &mi); if (!invertible) { /* Fill boundbox with tile fill color */ SHColor *c = &context->tileFillColor; glColor4fv((GLfloat*)c); glBegin(GL_QUADS); for (i=0; i<4; ++i) glVertex2fv((GLfloat*)&corners[i]); glEnd(); return 1; } /* Setup texture coordinate transform */ img = (SHImage*)p->pattern; sx = 1.0f/(VGfloat)img->texwidth; sy = 1.0f/(VGfloat)img->texheight; glActiveTexture(texUnit); shMatrixToGL(&mi, migl); glMatrixMode(GL_TEXTURE); glPushMatrix(); glScalef(sx, sy, 1.0f); glMultMatrixf(migl); /* Draw boundbox with same texture coordinates that will get transformed back to paint space */ shSetPatternTexGLState(p, context); glEnable(GL_TEXTURE_2D); glBegin(GL_QUADS); for (i=0; i<4; ++i) { glMultiTexCoord2f(texUnit, corners[i].x, corners[i].y); glVertex2fv((GLfloat*)&corners[i]); } glEnd(); glDisable(GL_TEXTURE_2D); glPopMatrix(); glMatrixMode(GL_MODELVIEW); return 1; }
int shDrawRadialGradientMesh(SHPaint *p, SHVector2 *min, SHVector2 *max, VGPaintMode mode, GLenum texUnit) { SHint i, j; float a, n; SHfloat cx = p->radialGradient[0]; SHfloat cy = p->radialGradient[1]; SHfloat fx = p->radialGradient[2]; SHfloat fy = p->radialGradient[3]; float r = p->radialGradient[4]; float fcx, fcy, rr, C; SHVector2 ux; SHVector2 uy; SHVector2 c, f; SHVector2 cf; SHMatrix3x3 *m; SHMatrix3x3 mi; SHint invertible; SHVector2 corners[4]; SHVector2 fcorners[4]; SHfloat minOffset=0.0f; SHfloat maxOffset=0.0f; SHint maxI=0, maxJ=0; SHfloat maxA=0.0f; SHfloat startA=0.0f; int numsteps = 100; float step = 2*PI/numsteps; SHVector2 tmin, tmax; SHVector2 min1, max1, min2, max2; /* Pick paint transform matrix */ SH_GETCONTEXT(0); if (mode == VG_FILL_PATH) m = &context->fillTransform; else if (mode == VG_STROKE_PATH) m = &context->strokeTransform; /* Move focus into circle if outside */ SET2(cf, fx,fy); SUB2(cf, cx,cy); n = NORM2(cf); if (n > r) { DIV2(cf, n); fx = cx + 0.995f * r * cf.x; fy = cy + 0.995f * r * cf.y; } /* Precalculations */ rr = r*r; fcx = fx - cx; fcy = fy - cy; C = fcx*fcx + fcy*fcy - rr; /* Apply paint-to-user transformation to focus and unit vectors */ SET2(f, fx, fy); SET2(c, cx, cy); SET2(ux, 1, 0); SET2(uy, 0, 1); ADD2(ux, cx, cy); ADD2(uy, cx, cy); TRANSFORM2(f, (*m)); TRANSFORM2(c, (*m)); TRANSFORM2(ux, (*m)); TRANSFORM2(uy, (*m)); SUB2V(ux, c); SUB2V(uy, c); /* Boundbox corners */ SET2(corners[0], min->x, min->y); SET2(corners[1], max->x, min->y); SET2(corners[2], max->x, max->y); SET2(corners[3], min->x, max->y); /* Find inverse transformation (back to paint space) */ invertible = shInvertMatrix(m, &mi); if (!invertible || r <= 0.0f) { /* Fill boundbox with color at offset 1 */ SHColor *c = &p->stops.items[p->stops.size-1].color; glColor4fv((GLfloat*)c); glBegin(GL_QUADS); for (i=0; i<4; ++i) glVertex2fv((GLfloat*)&corners[i]); glEnd(); return 1; } /*--------------------------------------------------------*/ /* Find min/max offset */ for (i=0; i<4; ++i) { /* Transform to paint space */ SHfloat ax,ay, A,B,D,t, off; TRANSFORM2TO(corners[i], mi, fcorners[i]); SUB2(fcorners[i], fx, fy); n = NORM2(fcorners[i]); if (n == 0.0f) { /* Avoid zero-length vectors */ off = 0.0f; }else{ /* Distance from focus to circle at corner angle */ DIV2(fcorners[i], n); ax = fcorners[i].x; ay = fcorners[i].y; A = ax*ax + ay*ay; B = 2 * (fcx*ax + fcy*ay); D = B*B - 4*A*C; t = (-B + SH_SQRT(D)) / (2*A); /* Relative offset of boundbox corner */ if (D <= 0.0f) off = 1.0f; else off = n / t; } /* Find smallest and largest offset */ if (off < minOffset || i==0) minOffset = off; if (off > maxOffset || i==0) maxOffset = off; } /* Is transformed focus inside original boundbox? */ if (f.x >= min->x && f.x <= max->x && f.y >= min->y && f.y <= max->y) { /* Draw whole circle */ minOffset = 0.0f; startA = 0.0f; maxA = 2*PI; }else{ /* Find most distant corner pair */ for (i=0; i<3; ++i) { if (ISZERO2(fcorners[i])) continue; for (j=i+1; j<4; ++j) { if (ISZERO2(fcorners[j])) continue; a = ANGLE2N(fcorners[i], fcorners[j]); if (a > maxA || maxA == 0.0f) {maxA=a; maxI=i; maxJ=j;} }} /* Pick starting angle */ if (CROSS2(fcorners[maxI],fcorners[maxJ]) > 0.0f) startA = shVectorOrientation(&fcorners[maxI]); else startA = shVectorOrientation(&fcorners[maxJ]); } /*---------------------------------------------------------*/ /* TODO: for minOffset we'd actually need to find minimum of the gradient function when X and Y are substitued with a line equation for each bound-box edge. As a workaround we use 0.0f for now. */ minOffset = 0.0f; step = PI/50; numsteps = (SHint)SH_CEIL(maxA / step) + 1; glActiveTexture(texUnit); shSetGradientTexGLState(p); glEnable(GL_TEXTURE_1D); glBegin(GL_QUADS); /* Walk the steps and draw gradient mesh */ for (i=0, a=startA; i<numsteps; ++i, a+=step) { /* Distance from focus to circle border at current angle (gradient space) */ float ax = SH_COS(a); float ay = SH_SIN(a); float A = ax*ax + ay*ay; float B = 2 * (fcx*ax + fcy*ay); float D = B*B - 4*A*C; float t = (-B + SH_SQRT(D)) / (2*A); if (D <= 0.0f) t = 0.0f; /* Vectors pointing towards minimum and maximum offset at current angle (gradient space) */ tmin.x = ax * t * minOffset; tmin.y = ay * t * minOffset; tmax.x = ax * t * maxOffset; tmax.y = ay * t * maxOffset; /* Transform back to user space */ min2.x = f.x + tmin.x * ux.x + tmin.y * uy.x; min2.y = f.y + tmin.x * ux.y + tmin.y * uy.y; max2.x = f.x + tmax.x * ux.x + tmax.y * uy.x; max2.y = f.y + tmax.x * ux.y + tmax.y * uy.y; /* Draw quad */ if (i!=0) { glMultiTexCoord1f(texUnit, minOffset); glVertex2fv((GLfloat*)&min1); glVertex2fv((GLfloat*)&min2); glMultiTexCoord1f(texUnit, maxOffset); glVertex2fv((GLfloat*)&max2); glVertex2fv((GLfloat*)&max1); } /* Save prev points */ min1 = min2; max1 = max2; } glEnd(); glDisable(GL_TEXTURE_1D); return 1; }
int shDrawLinearGradientMesh(SHPaint *p, SHVector2 *min, SHVector2 *max, VGPaintMode mode, GLenum texUnit) { SHint i; SHfloat n; SHfloat x1 = p->linearGradient[0]; SHfloat y1 = p->linearGradient[1]; SHfloat x2 = p->linearGradient[2]; SHfloat y2 = p->linearGradient[3]; SHVector2 c, ux, uy; SHVector2 cc, uux, uuy; SHMatrix3x3 *m; SHMatrix3x3 mi; SHint invertible; SHVector2 corners[4]; SHfloat minOffset = 0.0f; SHfloat maxOffset = 0.0f; SHfloat left = 0.0f; SHfloat right = 0.0f; SHVector2 l1,r1,l2,r2; /* Pick paint transform matrix */ SH_GETCONTEXT(0); if (mode == VG_FILL_PATH) m = &context->fillTransform; else if (mode == VG_STROKE_PATH) m = &context->strokeTransform; /* Gradient center and unit vectors */ SET2(c, x1, y1); SET2(ux, x2-x1, y2-y1); SET2(uy, -ux.y, ux.x); n = NORM2(ux); DIV2(ux, n); NORMALIZE2(uy); /* Apply paint-to-user transformation */ ADD2V(ux, c); ADD2V(uy, c); TRANSFORM2TO(c, (*m), cc); TRANSFORM2TO(ux, (*m), uux); TRANSFORM2TO(uy, (*m), uuy); SUB2V(ux,c); SUB2V(uy,c); SUB2V(uux,cc); SUB2V(uuy,cc); /* Boundbox corners */ SET2(corners[0], min->x, min->y); SET2(corners[1], max->x, min->y); SET2(corners[2], max->x, max->y); SET2(corners[3], min->x, max->y); /* Find inverse transformation (back to paint space) */ invertible = shInvertMatrix(m, &mi); if (!invertible || n==0.0f) { /* Fill boundbox with color at offset 1 */ SHColor *c = &p->stops.items[p->stops.size-1].color; glColor4fv((GLfloat*)c); glBegin(GL_QUADS); for (i=0; i<4; ++i) glVertex2fv((GLfloat*)&corners[i]); glEnd(); return 1; } /*--------------------------------------------------------*/ for (i=0; i<4; ++i) { /* Find min/max offset and perpendicular span */ SHfloat o, s; TRANSFORM2(corners[i], mi); SUB2V(corners[i], c); o = DOT2(corners[i], ux) / n; s = DOT2(corners[i], uy); if (o < minOffset || i==0) minOffset = o; if (o > maxOffset || i==0) maxOffset = o; if (s < left || i==0) left = s; if (s > right || i==0) right = s; } /*---------------------------------------------------------*/ /* Corners of boundbox in gradient system */ SET2V(l1, cc); SET2V(r1, cc); SET2V(l2, cc); SET2V(r2, cc); OFFSET2V(l1, uuy, left); OFFSET2V(l1, uux, minOffset * n); OFFSET2V(r1, uuy, right); OFFSET2V(r1, uux, minOffset * n); OFFSET2V(l2, uuy, left); OFFSET2V(l2, uux, maxOffset * n); OFFSET2V(r2, uuy, right); OFFSET2V(r2, uux, maxOffset * n); /* Draw quad using color-ramp texture */ glActiveTexture(texUnit); shSetGradientTexGLState(p); glEnable(GL_TEXTURE_1D); glBegin(GL_QUAD_STRIP); glMultiTexCoord1f(texUnit, minOffset); glVertex2fv((GLfloat*)&r1); glVertex2fv((GLfloat*)&l1); glMultiTexCoord1f(texUnit, maxOffset); glVertex2fv((GLfloat*)&r2); glVertex2fv((GLfloat*)&l2); glEnd(); glDisable(GL_TEXTURE_1D); return 1; }
VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes) { SHPath *p; SHMatrix3x3 mi; SHfloat mgl[16]; SHPaint *fill, *stroke; SHRectangle *rect; VG_GETCONTEXT(VG_NO_RETVAL); VG_RETURN_ERR_IF(!shIsValidPath(context, path), VG_BAD_HANDLE_ERROR, VG_NO_RETVAL); VG_RETURN_ERR_IF(paintModes & (~(VG_STROKE_PATH | VG_FILL_PATH)), VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL); /* Check whether scissoring is enabled and scissor rectangle is valid */ if (context->scissoring == VG_TRUE) { rect = &context->scissor.items[0]; if (context->scissor.size == 0) VG_RETURN( VG_NO_RETVAL ); if (rect->w <= 0.0f || rect->h <= 0.0f) VG_RETURN( VG_NO_RETVAL ); glScissor( (GLint)rect->x, (GLint)rect->y, (GLint)rect->w, (GLint)rect->h ); glEnable( GL_SCISSOR_TEST ); } p = (SHPath*)path; /* If user-to-surface matrix invertible tessellate in surface space for better path resolution */ if (shIsTessCacheValid( context, p ) == VG_FALSE) { if (shInvertMatrix(&context->pathTransform, &mi)) { shFlattenPath(p, 1); shTransformVertices(&mi, p); }else shFlattenPath(p, 0); shFindBoundbox(p); } /* TODO: Turn antialiasing on/off */ glDisable(GL_LINE_SMOOTH); glDisable(GL_POLYGON_SMOOTH); glEnable(GL_MULTISAMPLE); /* Pick paint if available or default*/ fill = (context->fillPaint ? context->fillPaint : &context->defaultPaint); stroke = (context->strokePaint ? context->strokePaint : &context->defaultPaint); /* Apply transformation */ shMatrixToGL(&context->pathTransform, mgl); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMultMatrixf(mgl); if (paintModes & VG_FILL_PATH) { /* Tesselate into stencil */ glEnable(GL_STENCIL_TEST); /* Clear the stencil buffer first */ glStencilFunc(GL_ALWAYS, 0, 0); glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); shDrawBoundBox(context, p, VG_FILL_PATH); glStencilFunc(GL_ALWAYS, 0, 0); glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); shDrawVertices(p, GL_TRIANGLE_FAN); /* Setup blending */ updateBlendingStateGL(context, fill->type == VG_PAINT_TYPE_COLOR && fill->color.a == 1.0f); /* Draw paint where stencil odd */ glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); shDrawPaintMesh(context, &p->min, &p->max, VG_FILL_PATH, GL_TEXTURE0); /* Reset state */ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDisable(GL_STENCIL_TEST); glDisable(GL_BLEND); } /* TODO: Turn antialiasing on/off */ glDisable(GL_LINE_SMOOTH); glDisable(GL_POLYGON_SMOOTH); glEnable(GL_MULTISAMPLE); if ((paintModes & VG_STROKE_PATH) && context->strokeLineWidth > 0.0f) { if (1) {/*context->strokeLineWidth > 1.0f) {*/ if (shIsStrokeCacheValid( context, p ) == VG_FALSE) { /* Generate stroke triangles in user space */ shVector2ArrayClear(&p->stroke); shStrokePath(context, p); } /* Stroke into stencil */ glEnable(GL_STENCIL_TEST); /* Clear the stencil buffer first */ glStencilFunc(GL_ALWAYS, 0, 0); glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); shDrawBoundBox(context, p, VG_STROKE_PATH); glStencilFunc(GL_NOTEQUAL, 1, 1); glStencilOp(GL_KEEP, GL_INCR, GL_INCR); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); shDrawStroke(p); /* Setup blending */ updateBlendingStateGL(context, stroke->type == VG_PAINT_TYPE_COLOR && stroke->color.a == 1.0f); /* Draw paint where stencil odd */ glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); shDrawPaintMesh(context, &p->min, &p->max, VG_STROKE_PATH, GL_TEXTURE0); /* Reset state */ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDisable(GL_STENCIL_TEST); glDisable(GL_BLEND); }else{ /* Simulate thin stroke by alpha */ SHColor c = stroke->color; if (context->strokeLineWidth < 1.0f) c.a *= context->strokeLineWidth; /* Draw contour as a line */ glDisable(GL_MULTISAMPLE); glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4fv((GLfloat*)&c); shDrawVertices(p, GL_LINE_STRIP); glDisable(GL_BLEND); glDisable(GL_LINE_SMOOTH); } } glDisable(GL_MULTISAMPLE); glPopMatrix(); if (context->scissoring == VG_TRUE) glDisable( GL_SCISSOR_TEST ); VG_RETURN(VG_NO_RETVAL); }