static void shDrawBoundBox(VGContext *c, SHPath *p, VGPaintMode mode) { SHfloat K = 1.0f; if (mode == VG_STROKE_PATH) { K = SH_CEIL(c->strokeMiterLimit * c->strokeLineWidth) + 1.0f; } #ifdef ANDROIDVG GLfloat v[6][2]; v[0][0] = p->min.x-K; v[0][1] = p->min.y-K; v[1][0] = p->max.x+K; v[1][1] = p->min.y-K; v[2][0] = p->max.x+K; v[2][1] = p->max.y+K; v[3][0] = p->min.x-K; v[3][1] = p->min.y-K; v[4][0] = p->max.x+K; v[4][1] = p->max.y+K; v[5][0] = p->min.x-K; v[5][1] = p->max.y+K; glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, v); glDrawArrays(GL_TRIANGLES, 0, 6); glDisableClientState(GL_VERTEX_ARRAY); #else glBegin(GL_QUADS); glVertex2f(p->min.x-K, p->min.y-K); glVertex2f(p->max.x+K, p->min.y-K); glVertex2f(p->max.x+K, p->max.y+K); glVertex2f(p->min.x-K, p->max.y+K); glEnd(); #endif }
static void shDrawBoundBox(VGContext *c, SHPath *p, VGPaintMode mode) { SHfloat K = 1.0f; if (mode == VG_STROKE_PATH) K = SH_CEIL(c->strokeMiterLimit * c->strokeLineWidth) + 1.0f; glBegin(GL_QUADS); glVertex2f(p->min.x-K, p->min.y-K); glVertex2f(p->max.x+K, p->min.y-K); glVertex2f(p->max.x+K, p->max.y+K); glVertex2f(p->min.x-K, p->max.y+K); glEnd(); }
static void shDrawPaintMesh(VGContext *c, SHVector2 *min, SHVector2 *max, VGPaintMode mode, GLenum texUnit) { SHPaint *p; SHVector2 pmin, pmax; SHfloat K = 1.0f; /* Pick the right paint */ if (mode == VG_FILL_PATH) { p = (c->fillPaint ? c->fillPaint : &c->defaultPaint); }else if (mode == VG_STROKE_PATH) { p = (c->strokePaint ? c->strokePaint : &c->defaultPaint); K = SH_CEIL(c->strokeMiterLimit * c->strokeLineWidth) + 1.0f; } /* We want to be sure to cover every pixel of this path so better take a pixel more than leave some out (multisampling is tricky). */ SET2V(pmin, (*min)); SUB2(pmin, K,K); SET2V(pmax, (*max)); ADD2(pmax, K,K); /* Construct appropriate OpenGL primitives so as to fill the stencil mask with select paint */ switch (p->type) { case VG_PAINT_TYPE_LINEAR_GRADIENT: shDrawLinearGradientMesh(p, min, max, mode, texUnit); break; case VG_PAINT_TYPE_RADIAL_GRADIENT: shDrawRadialGradientMesh(p, min, max, mode, texUnit); break; case VG_PAINT_TYPE_PATTERN: if (p->pattern != VG_INVALID_HANDLE) { shDrawPatternMesh(p, min, max, mode, texUnit); break; }/* else behave as a color paint */ case VG_PAINT_TYPE_COLOR: glColor4fv((GLfloat*)&p->color); glBegin(GL_QUADS); glVertex2f(pmin.x, pmin.y); glVertex2f(pmax.x, pmin.y); glVertex2f(pmax.x, pmax.y); glVertex2f(pmin.x, pmax.y); glEnd(); break; } }
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; }
static void shDrawPaintMesh(VGContext *c, SHVector2 *min, SHVector2 *max, VGPaintMode mode, GLenum texUnit) { SHPaint *p; SHVector2 pmin, pmax; SHfloat K = 1.0f; #ifdef ANDROIDVG SHColor *color; GLfloat v[6][2]; #endif /* Pick the right paint */ if (mode == VG_FILL_PATH) { p = (c->fillPaint ? c->fillPaint : &c->defaultPaint); }else if (mode == VG_STROKE_PATH) { p = (c->strokePaint ? c->strokePaint : &c->defaultPaint); K = SH_CEIL(c->strokeMiterLimit * c->strokeLineWidth) + 1.0f; } /* We want to be sure to cover every pixel of this path so better take a pixel more than leave some out (multisampling is tricky). */ SET2V(pmin, (*min)); SUB2(pmin, K,K); SET2V(pmax, (*max)); ADD2(pmax, K,K); /* Construct appropriate OpenGL primitives so as to fill the stencil mask with select paint */ switch (p->type) { case VG_PAINT_TYPE_LINEAR_GRADIENT: shDrawLinearGradientMesh(p, min, max, mode, texUnit); break; case VG_PAINT_TYPE_RADIAL_GRADIENT: shDrawRadialGradientMesh(p, min, max, mode, texUnit); break; case VG_PAINT_TYPE_PATTERN: if (p->pattern != VG_INVALID_HANDLE) { shDrawPatternMesh(p, min, max, mode, texUnit); break; }/* else behave as a color paint */ case VG_PAINT_TYPE_COLOR: #ifdef ANDROIDVG v[0][0] = pmin.x; v[0][1] = pmin.y; v[1][0] = pmax.x; v[1][1] = pmin.y; v[2][0] = pmax.x; v[2][1] = pmax.y; v[3][0] = pmin.x; v[3][1] = pmin.y; v[4][0] = pmax.x; v[4][1] = pmax.y; v[5][0] = pmin.x; v[5][1] = pmax.y; color = &p->color; glColor4f(color->r, color->g, color->b, color->a); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, v); glDrawArrays(GL_TRIANGLES, 0, 6); glDisableClientState(GL_VERTEX_ARRAY); #else glColor4fv((GLfloat*)&p->color); glBegin(GL_QUADS); glVertex2f(pmin.x, pmin.y); glVertex2f(pmax.x, pmin.y); glVertex2f(pmax.x, pmax.y); glVertex2f(pmin.x, pmax.y); glEnd(); #endif break; } }