void SkPathStroker::quad_to(const SkPoint pts[3], const SkVector& normalAB, const SkVector& unitNormalAB, SkVector* normalBC, SkVector* unitNormalBC, int subDivide) { if (!set_normal_unitnormal(pts[1], pts[2], fRadius, normalBC, unitNormalBC)) { // pts[1] nearly equals pts[2], so just draw a line to pts[2] this->line_to(pts[2], normalAB); *normalBC = normalAB; *unitNormalBC = unitNormalAB; return; } if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) { SkPoint tmp[5]; SkVector norm, unit; SkChopQuadAtHalf(pts, tmp); this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide); this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide); } else { SkVector normalB; normalB = pts[2] - pts[0]; normalB.rotateCCW(); SkScalar dot = SkPoint::DotProduct(unitNormalAB, *unitNormalBC); SkAssertResult(normalB.setLength(SkScalarDiv(fRadius, SkScalarSqrt((SK_Scalar1 + dot)/2)))); fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY); fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY); } }
static void addbump(SkPath* path, const SkPoint pts[2], SkScalar bump) { SkVector tang; tang.setLength(pts[1].fX - pts[0].fX, pts[1].fY - pts[0].fY, bump); path->lineTo(SkScalarHalf(pts[0].fX + pts[1].fX) - tang.fY, SkScalarHalf(pts[0].fY + pts[1].fY) + tang.fX); path->lineTo(pts[1]); }
bool SkEmbossMaskFilter::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin) const { SkScalar sigma = matrix.mapRadius(fBlurSigma); if (!SkBlurMask::BoxBlur(dst, src, sigma, SkBlurMask::kInner_Style, SkBlurMask::kLow_Quality)) { return false; } dst->fFormat = SkMask::k3D_Format; if (margin) { margin->set(SkScalarCeilToInt(3*sigma), SkScalarCeilToInt(3*sigma)); } if (src.fImage == NULL) { return true; } // create a larger buffer for the other two channels (should force fBlur to do this for us) { uint8_t* alphaPlane = dst->fImage; size_t planeSize = dst->computeImageSize(); if (0 == planeSize) { return false; // too big to allocate, abort } dst->fImage = SkMask::AllocImage(planeSize * 3); memcpy(dst->fImage, alphaPlane, planeSize); SkMask::FreeImage(alphaPlane); } // run the light direction through the matrix... Light light = fLight; matrix.mapVectors((SkVector*)(void*)light.fDirection, (SkVector*)(void*)fLight.fDirection, 1); // now restore the length of the XY component // cast to SkVector so we can call setLength (this double cast silences alias warnings) SkVector* vec = (SkVector*)(void*)light.fDirection; vec->setLength(light.fDirection[0], light.fDirection[1], SkPoint::Length(fLight.fDirection[0], fLight.fDirection[1])); SkEmbossMask::Emboss(dst, light); // restore original alpha memcpy(dst->fImage, src.fImage, src.computeImageSize()); return true; }
// Offset line segment p0-p1 'd0' and 'd1' units in the direction specified by 'side' bool SkOffsetSegment(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1, int side, SkPoint* offset0, SkPoint* offset1) { SkASSERT(side == -1 || side == 1); SkVector perp = SkVector::Make(p0.fY - p1.fY, p1.fX - p0.fX); if (SkScalarNearlyEqual(d0, d1)) { // if distances are equal, can just outset by the perpendicular perp.setLength(d0*side); *offset0 = p0 + perp; *offset1 = p1 + perp; } else { // Otherwise we need to compute the outer tangent. // See: http://www.ambrsoft.com/TrigoCalc/Circles2/Circles2Tangent_.htm if (d0 < d1) { side = -side; } SkScalar dD = d0 - d1; // if one circle is inside another, we can't compute an offset if (dD*dD >= p0.distanceToSqd(p1)) { return false; } SkPoint outerTangentIntersect = SkPoint::Make((p1.fX*d0 - p0.fX*d1) / dD, (p1.fY*d0 - p0.fY*d1) / dD); SkScalar d0sq = d0*d0; SkVector dP = outerTangentIntersect - p0; SkScalar dPlenSq = dP.lengthSqd(); SkScalar discrim = SkScalarSqrt(dPlenSq - d0sq); offset0->fX = p0.fX + (d0sq*dP.fX - side*d0*dP.fY*discrim) / dPlenSq; offset0->fY = p0.fY + (d0sq*dP.fY + side*d0*dP.fX*discrim) / dPlenSq; SkScalar d1sq = d1*d1; dP = outerTangentIntersect - p1; dPlenSq = dP.lengthSqd(); discrim = SkScalarSqrt(dPlenSq - d1sq); offset1->fX = p1.fX + (d1sq*dP.fX - side*d1*dP.fY*discrim) / dPlenSq; offset1->fY = p1.fY + (d1sq*dP.fY + side*d1*dP.fX*discrim) / dPlenSq; } return true; }
static void Perterb(SkPoint* p, const SkVector& tangent, SkScalar scale) { SkVector normal = tangent; SkPointPriv::RotateCCW(&normal); normal.setLength(scale); *p += normal; }
static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, const SkPoint& pivot, const SkVector& afterUnitNormal, SkScalar radius, SkScalar invMiterLimit, bool prevIsLine, bool currIsLine) { // negate the dot since we're using normals instead of tangents SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal); AngleType angleType = Dot2AngleType(dotProd); SkVector before = beforeUnitNormal; SkVector after = afterUnitNormal; SkVector mid; SkScalar sinHalfAngle; bool ccw; if (angleType == kNearlyLine_AngleType) return; if (angleType == kNearly180_AngleType) { currIsLine = false; goto DO_BLUNT; } ccw = !is_clockwise(before, after); if (ccw) { SkTSwap<SkPath*>(outer, inner); before.negate(); after.negate(); } /* Before we enter the world of square-roots and divides, check if we're trying to join an upright right angle (common case for stroking rectangles). If so, special case that (for speed an accuracy). Note: we only need to check one normal if dot==0 */ if (0 == dotProd && invMiterLimit <= kOneOverSqrt2) { mid.set(SkScalarMul(before.fX + after.fX, radius), SkScalarMul(before.fY + after.fY, radius)); goto DO_MITER; } /* midLength = radius / sinHalfAngle if (midLength > miterLimit * radius) abort if (radius / sinHalf > miterLimit * radius) abort if (1 / sinHalf > miterLimit) abort if (1 / miterLimit > sinHalf) abort My dotProd is opposite sign, since it is built from normals and not tangents hence 1 + dot instead of 1 - dot in the formula */ sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd)); if (sinHalfAngle < invMiterLimit) { currIsLine = false; goto DO_BLUNT; } // choose the most accurate way to form the initial mid-vector if (angleType == kSharp_AngleType) { mid.set(after.fY - before.fY, before.fX - after.fX); if (ccw) mid.negate(); } else mid.set(before.fX + after.fX, before.fY + after.fY); mid.setLength(SkScalarDiv(radius, sinHalfAngle)); DO_MITER: if (prevIsLine) outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY); else outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY); DO_BLUNT: after.scale(radius); if (!currIsLine) outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY); HandleInnerJoin(inner, pivot, after); }