static void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice, const SkMatrix* toSrc, BezierVertex verts[kQuadNumVertices]) { SkASSERT(!toDevice == !toSrc); // original quad is specified by tri a,b,c SkPoint a = qpts[0]; SkPoint b = qpts[1]; SkPoint c = qpts[2]; if (toDevice) { toDevice->mapPoints(&a, 1); toDevice->mapPoints(&b, 1); toDevice->mapPoints(&c, 1); } // make a new poly where we replace a and c by a 1-pixel wide edges orthog // to edges ab and bc: // // before | after // | b0 // b | // | // | a0 c0 // a c | a1 c1 // // edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c, // respectively. BezierVertex& a0 = verts[0]; BezierVertex& a1 = verts[1]; BezierVertex& b0 = verts[2]; BezierVertex& c0 = verts[3]; BezierVertex& c1 = verts[4]; SkVector ab = b; ab -= a; SkVector ac = c; ac -= a; SkVector cb = b; cb -= c; // We should have already handled degenerates SkASSERT(ab.length() > 0 && cb.length() > 0); ab.normalize(); SkVector abN; abN.setOrthog(ab, SkVector::kLeft_Side); if (abN.dot(ac) > 0) { abN.negate(); } cb.normalize(); SkVector cbN; cbN.setOrthog(cb, SkVector::kLeft_Side); if (cbN.dot(ac) < 0) { cbN.negate(); } a0.fPos = a; a0.fPos += abN; a1.fPos = a; a1.fPos -= abN; c0.fPos = c; c0.fPos += cbN; c1.fPos = c; c1.fPos -= cbN; intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos); if (toSrc) { toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(BezierVertex), kQuadNumVertices); } }
void GrPathUtils::QuadUVMatrix::set(const SkPoint qPts[3]) { SkMatrix m; // We want M such that M * xy_pt = uv_pt // We know M * control_pts = [0 1/2 1] // [0 0 1] // [1 1 1] // And control_pts = [x0 x1 x2] // [y0 y1 y2] // [1 1 1 ] // We invert the control pt matrix and post concat to both sides to get M. // Using the known form of the control point matrix and the result, we can // optimize and improve precision. double x0 = qPts[0].fX; double y0 = qPts[0].fY; double x1 = qPts[1].fX; double y1 = qPts[1].fY; double x2 = qPts[2].fX; double y2 = qPts[2].fY; double det = x0*y1 - y0*x1 + x2*y0 - y2*x0 + x1*y2 - y1*x2; if (!sk_float_isfinite(det) || SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero)) { // The quad is degenerate. Hopefully this is rare. Find the pts that are // farthest apart to compute a line (unless it is really a pt). SkScalar maxD = qPts[0].distanceToSqd(qPts[1]); int maxEdge = 0; SkScalar d = qPts[1].distanceToSqd(qPts[2]); if (d > maxD) { maxD = d; maxEdge = 1; } d = qPts[2].distanceToSqd(qPts[0]); if (d > maxD) { maxD = d; maxEdge = 2; } // We could have a tolerance here, not sure if it would improve anything if (maxD > 0) { // Set the matrix to give (u = 0, v = distance_to_line) SkVector lineVec = qPts[(maxEdge + 1)%3] - qPts[maxEdge]; // when looking from the point 0 down the line we want positive // distances to be to the left. This matches the non-degenerate // case. lineVec.setOrthog(lineVec, SkPoint::kLeft_Side); // first row fM[0] = 0; fM[1] = 0; fM[2] = 0; // second row fM[3] = lineVec.fX; fM[4] = lineVec.fY; fM[5] = -lineVec.dot(qPts[maxEdge]); } else { // It's a point. It should cover zero area. Just set the matrix such // that (u, v) will always be far away from the quad. fM[0] = 0; fM[1] = 0; fM[2] = 100.f; fM[3] = 0; fM[4] = 0; fM[5] = 100.f; } } else { double scale = 1.0/det; // compute adjugate matrix double a2, a3, a4, a5, a6, a7, a8; a2 = x1*y2-x2*y1; a3 = y2-y0; a4 = x0-x2; a5 = x2*y0-x0*y2; a6 = y0-y1; a7 = x1-x0; a8 = x0*y1-x1*y0; // this performs the uv_pts*adjugate(control_pts) multiply, // then does the scale by 1/det afterwards to improve precision m[SkMatrix::kMScaleX] = (float)((0.5*a3 + a6)*scale); m[SkMatrix::kMSkewX] = (float)((0.5*a4 + a7)*scale); m[SkMatrix::kMTransX] = (float)((0.5*a5 + a8)*scale); m[SkMatrix::kMSkewY] = (float)(a6*scale); m[SkMatrix::kMScaleY] = (float)(a7*scale); m[SkMatrix::kMTransY] = (float)(a8*scale); // kMPersp0 & kMPersp1 should algebraically be zero m[SkMatrix::kMPersp0] = 0.0f; m[SkMatrix::kMPersp1] = 0.0f; m[SkMatrix::kMPersp2] = (float)((a2 + a5 + a8)*scale); // It may not be normalized to have 1.0 in the bottom right float m33 = m.get(SkMatrix::kMPersp2); if (1.f != m33) { m33 = 1.f / m33; fM[0] = m33 * m.get(SkMatrix::kMScaleX); fM[1] = m33 * m.get(SkMatrix::kMSkewX); fM[2] = m33 * m.get(SkMatrix::kMTransX); fM[3] = m33 * m.get(SkMatrix::kMSkewY); fM[4] = m33 * m.get(SkMatrix::kMScaleY); fM[5] = m33 * m.get(SkMatrix::kMTransY); } else { fM[0] = m.get(SkMatrix::kMScaleX); fM[1] = m.get(SkMatrix::kMSkewX); fM[2] = m.get(SkMatrix::kMTransX); fM[3] = m.get(SkMatrix::kMSkewY); fM[4] = m.get(SkMatrix::kMScaleY); fM[5] = m.get(SkMatrix::kMTransY); } } }