uint32_t GrPathUtils::cubicPointCount(const SkPoint points[], SkScalar tol) { if (tol < gMinCurveTol) { tol = gMinCurveTol; } SkASSERT(tol > 0); SkScalar d = SkTMax( points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]), points[2].distanceToLineSegmentBetweenSqd(points[0], points[3])); d = SkScalarSqrt(d); if (!SkScalarIsFinite(d)) { return MAX_POINTS_PER_CURVE; } else if (d <= tol) { return 1; } else { SkScalar divSqrt = SkScalarSqrt(d / tol); if (((SkScalar)SK_MaxS32) <= divSqrt) { return MAX_POINTS_PER_CURVE; } else { int temp = SkScalarCeilToInt(SkScalarSqrt(d / tol)); int pow2 = GrNextPow2(temp); // Because of NaNs & INFs we can wind up with a degenerate temp // such that pow2 comes out negative. Also, our point generator // will always output at least one pt. if (pow2 < 1) { pow2 = 1; } return SkTMin(pow2, MAX_POINTS_PER_CURVE); } } }
uint32_t GrPathUtils::cubicPointCount(const GrPoint points[], GrScalar tol) { if (tol < gMinCurveTol) { tol == gMinCurveTol; } GrAssert(tol > 0); GrScalar d = GrMax( points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]), points[2].distanceToLineSegmentBetweenSqd(points[0], points[3])); d = SkScalarSqrt(d); if (d <= tol) { return 1; } else { int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol))); int pow2 = GrNextPow2(temp); // Because of NaNs & INFs we can wind up with a degenerate temp // such that pow2 comes out negative. Also, our point generator // will always output at least one pt. if (pow2 < 1) { pow2 = 1; } return GrMin(pow2, MAX_POINTS_PER_CURVE); } }
void SkActive::pickUp(SkActive* existing) { SkTDOperandArray existingValues; for (int index = 0; index < fAnimators.count(); index++) { SkAnimateBase* animate = fAnimators[index]; SkASSERT(animate->getValuesType() == SkType_Float); int components = animate->components(); SkOperand* from = animate->getValues(); SkOperand* to = &from[animate->components()]; existingValues.setCount(components); existing->fInterpolators[index]->timeToValues( existing->fState[index].fTicks - existing->fState[index].fStartTime, existingValues.begin()); SkScalar originalSum = 0; SkScalar workingSum = 0; for (int cIndex = 0; cIndex < components; cIndex++) { SkScalar delta = to[cIndex].fScalar - from[cIndex].fScalar; originalSum += SkScalarMul(delta, delta); delta = to[cIndex].fScalar - existingValues[cIndex].fScalar; workingSum += SkScalarMul(delta, delta); } if (workingSum < originalSum) { SkScalar originalDistance = SkScalarSqrt(originalSum); SkScalar workingDistance = SkScalarSqrt(workingSum); existing->fState[index].fDuration = (SkMSec) SkScalarMulDiv(fState[index].fDuration, workingDistance, originalDistance); } fInterpolators[index]->reset(components, 2, SkType_Float); fInterpolators[index]->setKeyFrame(0, 0, existingValues.begin(), animate->blend[0]); fInterpolators[index]->setKeyFrame(1, fState[index].fDuration, to, animate->blend[0]); } }
/* This works -- more needs to be done to see if it is performant on all platforms. To use this to measure parts of quads requires recomputing everything -- perhaps a chop-like interface can start from a larger measurement and get two new measurements with one call here. */ static SkScalar compute_quad_len(const SkPoint pts[3]) { SkPoint a,b; a.fX = pts[0].fX - 2 * pts[1].fX + pts[2].fX; a.fY = pts[0].fY - 2 * pts[1].fY + pts[2].fY; SkScalar A = 4 * (a.fX * a.fX + a.fY * a.fY); if (0 == A) { a = pts[2] - pts[0]; return a.length(); } b.fX = 2 * (pts[1].fX - pts[0].fX); b.fY = 2 * (pts[1].fY - pts[0].fY); SkScalar B = 4 * (a.fX * b.fX + a.fY * b.fY); SkScalar C = b.fX * b.fX + b.fY * b.fY; SkScalar Sabc = 2 * SkScalarSqrt(A + B + C); SkScalar A_2 = SkScalarSqrt(A); SkScalar A_32 = 2 * A * A_2; SkScalar C_2 = 2 * SkScalarSqrt(C); SkScalar BA = B / A_2; if (0 == BA + C_2) { return quad_folded_len(pts); } SkScalar J = A_32 * Sabc + A_2 * B * (Sabc - C_2); SkScalar K = 4 * C * A - B * B; SkScalar L = (2 * A_2 + BA + Sabc) / (BA + C_2); if (L <= 0) { return quad_folded_len(pts); } SkScalar M = SkScalarLog(L); SkScalar result = (J + K * M) / (4 * A_32); SkASSERT(SkScalarIsFinite(result)); return result; }
SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatrix* localM) const { SkASSERT(fPicture && fPicture->width() > 0 && fPicture->height() > 0); SkMatrix m; m.setConcat(matrix, this->getLocalMatrix()); if (localM) { m.preConcat(*localM); } // Use a rotation-invariant scale SkPoint scale; if (!SkDecomposeUpper2x2(m, NULL, &scale, NULL)) { // Decomposition failed, use an approximation. scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()), SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY())); } SkSize scaledSize = SkSize::Make(scale.x() * fPicture->width(), scale.y() * fPicture->height()); SkISize tileSize = scaledSize.toRound(); if (tileSize.isEmpty()) { return NULL; } // The actual scale, compensating for rounding. SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fPicture->width(), SkIntToScalar(tileSize.height()) / fPicture->height()); SkAutoMutexAcquire ama(fCachedBitmapShaderMutex); if (!fCachedBitmapShader || tileScale != fCachedTileScale) { SkBitmap bm; if (!bm.allocN32Pixels(tileSize.width(), tileSize.height())) { return NULL; } bm.eraseColor(SK_ColorTRANSPARENT); SkCanvas canvas(bm); canvas.scale(tileScale.width(), tileScale.height()); canvas.drawPicture(fPicture); fCachedTileScale = tileScale; SkMatrix shaderMatrix = this->getLocalMatrix(); shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height()); fCachedBitmapShader.reset(CreateBitmapShader(bm, fTmx, fTmy, &shaderMatrix)); } // Increment the ref counter inside the mutex to ensure the returned pointer is still valid. // Otherwise, the pointer may have been overwritten on a different thread before the object's // ref count was incremented. fCachedBitmapShader.get()->ref(); return fCachedBitmapShader; }
uint32_t GrPathUtils::quadraticPointCount(const GrPoint points[], GrScalar tol) { if (tol < gMinCurveTol) { tol == gMinCurveTol; } GrAssert(tol > 0); GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]); if (d <= tol) { return 1; } else { // Each time we subdivide, d should be cut in 4. So we need to // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x) // points. // 2^(log4(x)) = sqrt(x); int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol))); int pow2 = GrNextPow2(temp); // Because of NaNs & INFs we can wind up with a degenerate temp // such that pow2 comes out negative. Also, our point generator // will always output at least one pt. if (pow2 < 1) { pow2 = 1; } return GrMin(pow2, MAX_POINTS_PER_CURVE); } }
SkRTree::Branch SkRTree::bulkLoad(SkTDArray<Branch>* branches, int level) { if (branches->count() == 1) { // Only one branch. It will be the root. return (*branches)[0]; } // We might sort our branches here, but we expect Blink gives us a reasonable x,y order. // Skipping a call to sort (in Y) here resulted in a 17% win for recording with negligible // difference in playback speed. int numBranches = branches->count() / kMaxChildren; int remainder = branches->count() % kMaxChildren; int newBranches = 0; if (remainder > 0) { ++numBranches; // If the remainder isn't enough to fill a node, we'll add fewer nodes to other branches. if (remainder >= kMinChildren) { remainder = 0; } else { remainder = kMinChildren - remainder; } } int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches) / fAspectRatio)); int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) / SkIntToScalar(numStrips)); int currentBranch = 0; for (int i = 0; i < numStrips; ++i) { // Might be worth sorting by X here too. for (int j = 0; j < numTiles && currentBranch < branches->count(); ++j) { int incrementBy = kMaxChildren; if (remainder != 0) { // if need be, omit some nodes to make up for remainder if (remainder <= kMaxChildren - kMinChildren) { incrementBy -= remainder; remainder = 0; } else { incrementBy = kMinChildren; remainder -= kMaxChildren - kMinChildren; } } Node* n = allocateNodeAtLevel(level); n->fNumChildren = 1; n->fChildren[0] = (*branches)[currentBranch]; Branch b; b.fBounds = (*branches)[currentBranch].fBounds; b.fSubtree = n; ++currentBranch; for (int k = 1; k < incrementBy && currentBranch < branches->count(); ++k) { b.fBounds.join((*branches)[currentBranch].fBounds); n->fChildren[k] = (*branches)[currentBranch]; ++n->fNumChildren; ++currentBranch; } (*branches)[newBranches] = b; ++newBranches; } } branches->setCount(newBranches); return this->bulkLoad(branches, level + 1); }
void onDraw(SkCanvas* canvas) override { canvas->translate(20, 20); SkRect r = SkRect::MakeWH(1000, 1000); SkPaint paint; paint.setAntiAlias(true); paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(15); const SkScalar inset = paint.getStrokeWidth() + 4; const SkScalar sweepAngle = 345; SkRandom rand; SkScalar sign = 1; while (r.width() > paint.getStrokeWidth() * 3) { paint.setColor(sk_tool_utils::color_to_565(rand.nextU() | (0xFF << 24))); SkScalar startAngle = rand.nextUScalar1() * 360; SkScalar speed = SkScalarSqrt(16 / r.width()) * 0.5f; startAngle += fRotate * 360 * speed * sign; SkPath path; path.addArc(r, startAngle, sweepAngle); canvas->drawPath(path, paint); r.inset(inset, inset); sign = -sign; } }
uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol) { if (tol < gMinCurveTol) { tol = gMinCurveTol; } SkASSERT(tol > 0); SkScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]); if (!SkScalarIsFinite(d)) { return MAX_POINTS_PER_CURVE; } else if (d <= tol) { return 1; } else { // Each time we subdivide, d should be cut in 4. So we need to // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x) // points. // 2^(log4(x)) = sqrt(x); SkScalar divSqrt = SkScalarSqrt(d / tol); if (((SkScalar)SK_MaxS32) <= divSqrt) { return MAX_POINTS_PER_CURVE; } else { int temp = SkScalarCeilToInt(divSqrt); int pow2 = GrNextPow2(temp); // Because of NaNs & INFs we can wind up with a degenerate temp // such that pow2 comes out negative. Also, our point generator // will always output at least one pt. if (pow2 < 1) { pow2 = 1; } return SkTMin(pow2, MAX_POINTS_PER_CURVE); } } }
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); } }
/** From Numerical Recipes in C. Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C]) x1 = Q / A x2 = C / Q */ int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]) { SkASSERT(roots); if (A == 0) { return valid_unit_divide(-C, B, roots); } SkScalar* r = roots; SkScalar R = B*B - 4*A*C; if (R < 0 || SkScalarIsNaN(R)) { // complex roots return 0; } R = SkScalarSqrt(R); SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2; r += valid_unit_divide(Q, A, r); r += valid_unit_divide(C, Q, r); if (r - roots == 2) { if (roots[0] > roots[1]) SkTSwap<SkScalar>(roots[0], roots[1]); else if (roots[0] == roots[1]) // nearly-equal? r -= 1; // skip the double root } return (int)(r - roots); }
/** From Numerical Recipes in C. Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C]) x1 = Q / A x2 = C / Q */ int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]) { SkASSERT(roots); if (A == 0) { return valid_unit_divide(-C, B, roots); } SkScalar* r = roots; SkScalar R = B*B - 4*A*C; if (R < 0 || !SkScalarIsFinite(R)) { // complex roots // if R is infinite, it's possible that it may still produce // useful results if the operation was repeated in doubles // the flipside is determining if the more precise answer // isn't useful because surrounding machinery (e.g., subtracting // the axis offset from C) already discards the extra precision // more investigation and unit tests required... return 0; } R = SkScalarSqrt(R); SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2; r += valid_unit_divide(Q, A, r); r += valid_unit_divide(C, Q, r); if (r - roots == 2) { if (roots[0] > roots[1]) SkTSwap<SkScalar>(roots[0], roots[1]); else if (roots[0] == roots[1]) // nearly-equal? r -= 1; // skip the double root } return (int)(r - roots); }
static void set_loop_klm(const SkScalar d[3], SkScalar k[4], SkScalar l[4], SkScalar m[4]) { SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]); SkScalar ls = d[1] - tempSqrt; SkScalar lt = 2.f * d[0]; SkScalar ms = d[1] + tempSqrt; SkScalar mt = 2.f * d[0]; k[0] = ls * ms; k[1] = (3.f * ls*ms - ls * mt - lt * ms) / 3.f; k[2] = (lt * (mt - 2.f * ms) + ls * (3.f * ms - 2.f * mt)) / 3.f; k[3] = (lt - ls) * (mt - ms); l[0] = ls * ls * ms; l[1] = (ls * (ls * (mt - 3.f * ms) + 2.f * lt * ms))/-3.f; l[2] = ((lt - ls) * (ls * (2.f * mt - 3.f * ms) + lt * ms))/3.f; l[3] = -1.f * (lt - ls) * (lt - ls) * (mt - ms); m[0] = ls * ms * ms; m[1] = (ms * (ls * (2.f * mt - 3.f * ms) + lt * ms))/-3.f; m[2] = ((mt - ms) * (ls * (mt - 3.f * ms) + 2.f * lt * ms))/3.f; m[3] = -1.f * (lt - ls) * (mt - ms) * (mt - ms); // If (d0 < 0 && sign(k1) > 0) || (d0 > 0 && sign(k1) < 0), // we need to flip the orientation of our curve. // This is done by negating the k and l values if ( (d[0] < 0 && k[1] > 0) || (d[0] > 0 && k[1] < 0)) { for (int i = 0; i < 4; ++i) { k[i] = -k[i]; l[i] = -l[i]; } } }
static void set_serp_klm(const SkScalar d[3], SkScalar k[4], SkScalar l[4], SkScalar m[4]) { SkScalar tempSqrt = SkScalarSqrt(9.f * d[1] * d[1] - 12.f * d[0] * d[2]); SkScalar ls = 3.f * d[1] - tempSqrt; SkScalar lt = 6.f * d[0]; SkScalar ms = 3.f * d[1] + tempSqrt; SkScalar mt = 6.f * d[0]; k[0] = ls * ms; k[1] = (3.f * ls * ms - ls * mt - lt * ms) / 3.f; k[2] = (lt * (mt - 2.f * ms) + ls * (3.f * ms - 2.f * mt)) / 3.f; k[3] = (lt - ls) * (mt - ms); l[0] = ls * ls * ls; const SkScalar lt_ls = lt - ls; l[1] = ls * ls * lt_ls * -1.f; l[2] = lt_ls * lt_ls * ls; l[3] = -1.f * lt_ls * lt_ls * lt_ls; m[0] = ms * ms * ms; const SkScalar mt_ms = mt - ms; m[1] = ms * ms * mt_ms * -1.f; m[2] = mt_ms * mt_ms * ms; m[3] = -1.f * mt_ms * mt_ms * mt_ms; // If d0 < 0 we need to flip the orientation of our curve // This is done by negating the k and l values // We want negative distance values to be on the inside if ( d[0] > 0) { for (int i = 0; i < 4; ++i) { k[i] = -k[i]; l[i] = -l[i]; } } }
static void normalize(SkScalar v[3]) { SkScalar mag = SkScalarSquare(v[0]) + SkScalarSquare(v[1]) + SkScalarSquare(v[2]); mag = SkScalarSqrt(mag); for (int i = 0; i < 3; i++) { v[i] = SkScalarDiv(v[i], mag); } }
bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) { SkScalar d[3]; SkCubicType cubicType = SkClassifyCubic(pointsPtr, d); if (cubicType == kLoop_SkCubicType) { // crib code from gpu path utils that finds t values where loop self-intersects // use it to find mid of t values which should be a friendly place to chop SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]); SkScalar ls = d[1] - tempSqrt; SkScalar lt = 2.f * d[0]; SkScalar ms = d[1] + tempSqrt; SkScalar mt = 2.f * d[0]; if (roughly_between(0, ls, lt) && roughly_between(0, ms, mt)) { ls = ls / lt; ms = ms / mt; SkASSERT(roughly_between(0, ls, 1) && roughly_between(0, ms, 1)); *t = (ls + ms) / 2; SkASSERT(roughly_between(0, *t, 1)); return *t > 0 && *t < 1; } } else if (kSerpentine_SkCubicType == cubicType || kCusp_SkCubicType == cubicType) { SkDCubic cubic; cubic.set(pointsPtr); double inflectionTs[2]; int infTCount = cubic.findInflections(inflectionTs); if (infTCount == 2) { double maxCurvature[3]; int roots = cubic.findMaxCurvature(maxCurvature); #if DEBUG_CUBIC_SPLIT SkDebugf("%s\n", __FUNCTION__); cubic.dump(); for (int index = 0; index < infTCount; ++index) { SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]); SkDPoint pt = cubic.ptAtT(inflectionTs[index]); SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]); SkDLine perp = {{pt - dPt, pt + dPt}}; perp.dump(); } for (int index = 0; index < roots; ++index) { SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]); SkDPoint pt = cubic.ptAtT(maxCurvature[index]); SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]); SkDLine perp = {{pt - dPt, pt + dPt}}; perp.dump(); } #endif for (int index = 0; index < roots; ++index) { if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) { *t = maxCurvature[index]; return *t > 0 && *t < 1; } } } else if (infTCount == 1) { *t = inflectionTs[0]; return *t > 0 && *t < 1; } } return false; }
/* Solve coeff(t) == 0, returning the number of roots that lie withing 0 < t < 1. coeff[0]t^3 + coeff[1]t^2 + coeff[2]t + coeff[3] Eliminates repeated roots (so that all tValues are distinct, and are always in increasing order. */ static int solve_cubic_poly(const SkScalar coeff[4], SkScalar tValues[3]) { if (SkScalarNearlyZero(coeff[0])) { // we're just a quadratic return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues); } SkScalar a, b, c, Q, R; { SkASSERT(coeff[0] != 0); SkScalar inva = SkScalarInvert(coeff[0]); a = coeff[1] * inva; b = coeff[2] * inva; c = coeff[3] * inva; } Q = (a*a - b*3) / 9; R = (2*a*a*a - 9*a*b + 27*c) / 54; SkScalar Q3 = Q * Q * Q; SkScalar R2MinusQ3 = R * R - Q3; SkScalar adiv3 = a / 3; SkScalar* roots = tValues; SkScalar r; if (R2MinusQ3 < 0) { // we have 3 real roots SkScalar theta = SkScalarACos(R / SkScalarSqrt(Q3)); SkScalar neg2RootQ = -2 * SkScalarSqrt(Q); r = neg2RootQ * SkScalarCos(theta/3) - adiv3; if (is_unit_interval(r)) { *roots++ = r; } r = neg2RootQ * SkScalarCos((theta + 2*SK_ScalarPI)/3) - adiv3; if (is_unit_interval(r)) { *roots++ = r; } r = neg2RootQ * SkScalarCos((theta - 2*SK_ScalarPI)/3) - adiv3; if (is_unit_interval(r)) { *roots++ = r; } SkDEBUGCODE(test_collaps_duplicates();)
// 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; }
/* Used by bloat_tri; outsets a single point. */ static bool outset(SkPoint* p1, SkPoint line1, SkPoint line2) { // rotate the two line vectors 90 degrees to form the normals, and compute // the dot product of the normals SkScalar dotProd = line1.fY * line2.fY + line1.fX * line2.fX; SkScalar lengthSq = 1.0f / ((1.0f - dotProd) / 2.0f); if (lengthSq > kBloatLimit) { return false; } SkPoint bisector = line1 + line2; bisector.setLength(SkScalarSqrt(lengthSq) * kBloatSize); *p1 += bisector; return true; }
/* * Modulo internal errors, this should always succeed *if* the matrix is downscaling * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling) */ bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) { SkASSERT(fQuality <= kMedium_SkFilterQuality); if (fQuality != kMedium_SkFilterQuality) { return false; } // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap // to a valid bitmap. fQuality = kLow_SkFilterQuality; SkSize invScaleSize; if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) { return false; } SkScalar invScale = SkScalarSqrt(invScaleSize.width() * invScaleSize.height()); if (invScale > SK_Scalar1) { fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc())); if (nullptr == fCurrMip.get()) { SkBitmap orig; if (!provider.asBitmap(&orig)) { return false; } fCurrMip.reset(SkMipMapCache::AddAndRef(orig)); if (nullptr == fCurrMip.get()) { return false; } } // diagnostic for a crasher... if (nullptr == fCurrMip->data()) { sk_throw(); } SkScalar levelScale = SkScalarInvert(invScale); SkMipMap::Level level; if (fCurrMip->extractLevel(levelScale, &level)) { SkScalar invScaleFixup = level.fScale; fInvMatrix.postScale(invScaleFixup, invScaleFixup); const SkImageInfo info = provider.info().makeWH(level.fWidth, level.fHeight); // todo: if we could wrap the fCurrMip in a pixelref, then we could just install // that here, and not need to explicitly track it ourselves. return fResultBitmap.installPixels(info, level.fPixels, level.fRowBytes); } else { // failed to extract, so release the mipmap fCurrMip.reset(nullptr); } } return false; }
void SkNormalMapSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[], int count) const { SkPMColor tmpNormalColors[BUFFER_MAX]; do { int n = SkTMin(count, BUFFER_MAX); fMapContext->shadeSpan(x, y, tmpNormalColors, n); for (int i = 0; i < n; i++) { SkPoint3 tempNorm; tempNorm.set(SkIntToScalar(SkGetPackedR32(tmpNormalColors[i])) - 127.0f, SkIntToScalar(SkGetPackedG32(tmpNormalColors[i])) - 127.0f, SkIntToScalar(SkGetPackedB32(tmpNormalColors[i])) - 127.0f); tempNorm.normalize(); if (!SkScalarNearlyEqual(SkScalarAbs(tempNorm.fZ), 1.0f)) { SkVector transformed = fSource.fInvCTM.mapVector(tempNorm.fX, tempNorm.fY); // Normalizing the transformed X and Y, while keeping constant both Z and the // vector's angle in the XY plane. This maintains the "slope" for the surface while // appropriately rotating the normal for any anisotropic scaling that occurs. // Here, we call scaling factor the number that must divide the transformed X and Y // so that the normal's length remains equal to 1. SkScalar scalingFactorSquared = (SkScalarSquare(transformed.fX) + SkScalarSquare(transformed.fY)) / (1.0f - SkScalarSquare(tempNorm.fZ)); SkScalar invScalingFactor = SkScalarInvert(SkScalarSqrt(scalingFactorSquared)); output[i].fX = transformed.fX * invScalingFactor; output[i].fY = transformed.fY * invScalingFactor; output[i].fZ = tempNorm.fZ; } else { output[i] = {0.0f, 0.0f, tempNorm.fZ}; output[i].normalize(); } SkASSERT(SkScalarNearlyEqual(output[i].length(), 1.0f)); } output += n; x += n; count -= n; } while (count > 0); }
bool SkMipMap::extractLevel(const SkSize& scaleSize, Level* levelPtr) const { if (nullptr == fLevels) { return false; } SkASSERT(scaleSize.width() >= 0 && scaleSize.height() >= 0); #ifndef SK_SUPPORT_LEGACY_ANISOTROPIC_MIPMAP_SCALE // Use the smallest scale to match the GPU impl. const SkScalar scale = SkTMin(scaleSize.width(), scaleSize.height()); #else // Ideally we'd pick the smaller scale, to match Ganesh. But ignoring one of the // scales can produce some atrocious results, so for now we use the geometric mean. // (https://bugs.chromium.org/p/skia/issues/detail?id=4863) const SkScalar scale = SkScalarSqrt(scaleSize.width() * scaleSize.height()); #endif if (scale >= SK_Scalar1 || scale <= 0 || !SkScalarIsFinite(scale)) { return false; } SkScalar L = -SkScalarLog2(scale); if (!SkScalarIsFinite(L)) { return false; } SkASSERT(L >= 0); int level = SkScalarFloorToInt(L); SkASSERT(level >= 0); if (level <= 0) { return false; } if (level > fCount) { level = fCount; } if (levelPtr) { *levelPtr = fLevels[level - 1]; // need to augment with our colorspace levelPtr->fPixmap.setColorSpace(fCS); } return true; }
// This function parallels bulkLoad, but just counts how many nodes bulkLoad would allocate. int SkRTree::CountNodes(int branches, SkScalar aspectRatio) { if (branches == 1) { return 1; } int numBranches = branches / kMaxChildren; int remainder = branches % kMaxChildren; if (remainder > 0) { numBranches++; if (remainder >= kMinChildren) { remainder = 0; } else { remainder = kMinChildren - remainder; } } int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches) / aspectRatio)); int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) / SkIntToScalar(numStrips)); int currentBranch = 0; int nodes = 0; for (int i = 0; i < numStrips; ++i) { for (int j = 0; j < numTiles && currentBranch < branches; ++j) { int incrementBy = kMaxChildren; if (remainder != 0) { if (remainder <= kMaxChildren - kMinChildren) { incrementBy -= remainder; remainder = 0; } else { incrementBy = kMinChildren; remainder -= kMaxChildren - kMinChildren; } } nodes++; currentBranch++; for (int k = 1; k < incrementBy && currentBranch < branches; ++k) { currentBranch++; } } } return nodes + CountNodes(nodes, aspectRatio); }
bool SkBitmapProcState::possiblyScaleImage() { SkASSERT(NULL == fBitmap); fAdjustedMatrix = false; if (fFilterLevel <= SkPaint::kLow_FilterLevel) { return false; } // Check to see if the transformation matrix is simple, and if we're // doing high quality scaling. If so, do the bitmap scale here and // remove the (non-fractional) scaling component from the matrix. SkScalar invScaleX = fInvMatrix.getScaleX(); SkScalar invScaleY = fInvMatrix.getScaleY(); float trueDestWidth = fOrigBitmap.width() / invScaleX; float trueDestHeight = fOrigBitmap.height() / invScaleY; #ifndef SK_IGNORE_PROPER_FRACTIONAL_SCALING float roundedDestWidth = SkScalarRoundToScalar(trueDestWidth); float roundedDestHeight = SkScalarRoundToScalar(trueDestHeight); #else float roundedDestWidth = trueDestWidth; float roundedDestHeight = trueDestHeight; #endif if (SkPaint::kHigh_FilterLevel == fFilterLevel && fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) && kN32_SkColorType == fOrigBitmap.colorType() && cache_size_okay(fOrigBitmap, fInvMatrix)) { if (SkScalarNearlyEqual(invScaleX,1.0f) && SkScalarNearlyEqual(invScaleY,1.0f)) { // short-circuit identity scaling; the output is supposed to // be the same as the input, so we might as well go fast. // Note(humper): We could also probably do this if the scales // are close to -1 as well, since the flip doesn't require // any fancy re-sampling... // Set our filter level to low -- the only post-filtering this // image might require is some interpolation if the translation // is fractional. fFilterLevel = SkPaint::kLow_FilterLevel; return false; } if (!SkBitmapCache::Find(fOrigBitmap, roundedDestWidth, roundedDestHeight, &fScaledBitmap)) { // All the criteria are met; let's make a new bitmap. if (!SkBitmapScaler::Resize(&fScaledBitmap, fOrigBitmap, SkBitmapScaler::RESIZE_BEST, roundedDestWidth, roundedDestHeight, SkResourceCache::GetAllocator())) { // we failed to create fScaledBitmap, so just return and let // the scanline proc handle it. return false; } SkASSERT(fScaledBitmap.getPixels()); fScaledBitmap.setImmutable(); SkBitmapCache::Add(fOrigBitmap, roundedDestWidth, roundedDestHeight, fScaledBitmap); } SkASSERT(fScaledBitmap.getPixels()); fBitmap = &fScaledBitmap; // set the inv matrix type to translate-only; fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScaleX(), fInvMatrix.getTranslateY() / fInvMatrix.getScaleY()); #ifndef SK_IGNORE_PROPER_FRACTIONAL_SCALING // reintroduce any fractional scaling missed by our integral scale done above. float fractionalScaleX = roundedDestWidth/trueDestWidth; float fractionalScaleY = roundedDestHeight/trueDestHeight; fInvMatrix.postScale(fractionalScaleX, fractionalScaleY); #endif fAdjustedMatrix = true; // Set our filter level to low -- the only post-filtering this // image might require is some interpolation if the translation // is fractional or if there's any remaining scaling to be done. fFilterLevel = SkPaint::kLow_FilterLevel; return true; } /* * If High, then our special-case for scale-only did not take, and so we * have to make a choice: * 1. fall back on mipmaps + bilerp * 2. fall back on scanline bicubic filter * For now, we compute the "scale" value from the matrix, and have a * threshold to decide when bicubic is better, and when mips are better. * No doubt a fancier decision tree could be used uere. * * If Medium, then we just try to build a mipmap and select a level, * setting the filter-level to kLow to signal that we just need bilerp * to process the selected level. */ SkScalar scaleSqd = effective_matrix_scale_sqrd(fInvMatrix); if (SkPaint::kHigh_FilterLevel == fFilterLevel) { // Set the limit at 0.25 for the CTM... if the CTM is scaling smaller // than this, then the mipmaps quality may be greater (certainly faster) // so we only keep High quality if the scale is greater than this. // // Since we're dealing with the inverse, we compare against its inverse. const SkScalar bicubicLimit = 4.0f; const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit; if (scaleSqd < bicubicLimitSqd) { // use bicubic scanline return false; } // else set the filter-level to Medium, since we're scaling down and // want to reqeust mipmaps fFilterLevel = SkPaint::kMedium_FilterLevel; } SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel); /** * Medium quality means use a mipmap for down-scaling, and just bilper * for upscaling. Since we're examining the inverse matrix, we look for * a scale > 1 to indicate down scaling by the CTM. */ if (scaleSqd > SK_Scalar1) { fCurrMip.reset(SkMipMapCache::FindAndRef(fOrigBitmap)); if (NULL == fCurrMip.get()) { fCurrMip.reset(SkMipMap::Build(fOrigBitmap)); if (NULL == fCurrMip.get()) { return false; } SkMipMapCache::Add(fOrigBitmap, fCurrMip); } SkScalar levelScale = SkScalarInvert(SkScalarSqrt(scaleSqd)); SkMipMap::Level level; if (fCurrMip->extractLevel(levelScale, &level)) { SkScalar invScaleFixup = level.fScale; fInvMatrix.postScale(invScaleFixup, invScaleFixup); const SkImageInfo info = fOrigBitmap.info().makeWH(level.fWidth, level.fHeight); // todo: if we could wrap the fCurrMip in a pixelref, then we could just install // that here, and not need to explicitly track it ourselves. fScaledBitmap.installPixels(info, level.fPixels, level.fRowBytes); fBitmap = &fScaledBitmap; fFilterLevel = SkPaint::kLow_FilterLevel; return true; } } return false; }
static void draw_vector_logo(SkCanvas* canvas, const SkRect& viewBox) { constexpr char kSkiaStr[] = "SKIA"; constexpr SkScalar kGradientPad = .1f; constexpr SkScalar kVerticalSpacing = 0.25f; constexpr SkScalar kAccentScale = 1.20f; SkPaint paint; paint.setAntiAlias(true); paint.setSubpixelText(true); paint.setFakeBoldText(true); sk_tool_utils::set_portable_typeface(&paint); SkPath path; SkRect iBox, skiBox, skiaBox; paint.getTextPath("SKI", 3, 0, 0, &path); TightBounds(path, &skiBox); paint.getTextPath("I", 1, 0, 0, &path); TightBounds(path, &iBox); iBox.offsetTo(skiBox.fRight - iBox.width(), iBox.fTop); const size_t textLen = strlen(kSkiaStr); paint.getTextPath(kSkiaStr, textLen, 0, 0, &path); TightBounds(path, &skiaBox); skiaBox.outset(0, 2 * iBox.width() * (kVerticalSpacing + 1)); const SkScalar accentSize = iBox.width() * kAccentScale; const SkScalar underlineY = iBox.bottom() + (kVerticalSpacing + SkScalarSqrt(3) / 2) * accentSize; SkMatrix m; m.setRectToRect(skiaBox, viewBox, SkMatrix::kFill_ScaleToFit); SkAutoCanvasRestore acr(canvas, true); canvas->concat(m); canvas->drawCircle(iBox.centerX(), iBox.y() - (0.5f + kVerticalSpacing) * accentSize, accentSize / 2, paint); path.reset(); path.moveTo(iBox.centerX() - accentSize / 2, iBox.bottom() + kVerticalSpacing * accentSize); path.rLineTo(accentSize, 0); path.lineTo(iBox.centerX(), underlineY); canvas->drawPath(path, paint); SkRect underlineRect = SkRect::MakeLTRB(iBox.centerX() - iBox.width() * accentSize * 3, underlineY, iBox.centerX(), underlineY + accentSize / 10); const SkPoint pts1[] = { SkPoint::Make(underlineRect.x(), 0), SkPoint::Make(iBox.centerX(), 0) }; const SkScalar pos1[] = { 0, 0.75f }; const SkColor colors1[] = { SK_ColorTRANSPARENT, SK_ColorBLACK }; SkASSERT(SK_ARRAY_COUNT(pos1) == SK_ARRAY_COUNT(colors1)); paint.setShader(SkGradientShader::MakeLinear(pts1, colors1, pos1, SK_ARRAY_COUNT(pos1), SkShader::kClamp_TileMode)); canvas->drawRect(underlineRect, paint); const SkPoint pts2[] = { SkPoint::Make(iBox.x() - iBox.width() * kGradientPad, 0), SkPoint::Make(iBox.right() + iBox.width() * kGradientPad, 0) }; const SkScalar pos2[] = { 0, .01f, 1.0f/3, 1.0f/3, 2.0f/3, 2.0f/3, .99f, 1 }; const SkColor colors2[] = { SK_ColorBLACK, 0xffca5139, 0xffca5139, 0xff8dbd53, 0xff8dbd53, 0xff5460a5, 0xff5460a5, SK_ColorBLACK }; SkASSERT(SK_ARRAY_COUNT(pos2) == SK_ARRAY_COUNT(colors2)); paint.setShader(SkGradientShader::MakeLinear(pts2, colors2, pos2, SK_ARRAY_COUNT(pos2), SkShader::kClamp_TileMode)); canvas->drawText(kSkiaStr, textLen, 0, 0, paint); }
SkPDFShader::State::State(const SkShader& shader, const SkMatrix& canvasTransform, const SkIRect& bbox, SkScalar rasterScale) : fCanvasTransform(canvasTransform), fBBox(bbox), fPixelGeneration(0) { fInfo.fColorCount = 0; fInfo.fColors = NULL; fInfo.fColorOffsets = NULL; fShaderTransform = shader.getLocalMatrix(); fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode; fType = shader.asAGradient(&fInfo); if (fType == SkShader::kNone_GradientType) { SkShader::BitmapType bitmapType; SkMatrix matrix; bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes); if (bitmapType != SkShader::kDefault_BitmapType) { // Generic fallback for unsupported shaders: // * allocate a bbox-sized bitmap // * shade the whole area // * use the result as a bitmap shader // bbox is in device space. While that's exactly what we want for sizing our bitmap, // we need to map it into shader space for adjustments (to match // SkPDFImageShader::Create's behavior). SkRect shaderRect = SkRect::Make(bbox); if (!inverse_transform_bbox(canvasTransform, &shaderRect)) { fImage.reset(); return; } // Clamp the bitmap size to about 1M pixels static const SkScalar kMaxBitmapArea = 1024 * 1024; SkScalar bitmapArea = rasterScale * bbox.width() * rasterScale * bbox.height(); if (bitmapArea > kMaxBitmapArea) { rasterScale *= SkScalarSqrt(SkScalarDiv(kMaxBitmapArea, bitmapArea)); } SkISize size = SkISize::Make(SkScalarRoundToInt(rasterScale * bbox.width()), SkScalarRoundToInt(rasterScale * bbox.height())); SkSize scale = SkSize::Make(SkIntToScalar(size.width()) / shaderRect.width(), SkIntToScalar(size.height()) / shaderRect.height()); fImage.allocN32Pixels(size.width(), size.height()); fImage.eraseColor(SK_ColorTRANSPARENT); SkPaint p; p.setShader(const_cast<SkShader*>(&shader)); SkCanvas canvas(fImage); canvas.scale(scale.width(), scale.height()); canvas.translate(-shaderRect.x(), -shaderRect.y()); canvas.drawPaint(p); fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y()); fShaderTransform.preScale(1 / scale.width(), 1 / scale.height()); } else { SkASSERT(matrix.isIdentity()); } fPixelGeneration = fImage.getGenerationID(); } else { AllocateGradientInfoStorage(); shader.asAGradient(&fInfo); } }
void SkPathStroker::cubic_to(const SkPoint pts[4], const SkVector& normalAB, const SkVector& unitNormalAB, SkVector* normalCD, SkVector* unitNormalCD, int subDivide) { SkVector ab = pts[1] - pts[0]; SkVector cd = pts[3] - pts[2]; SkVector normalBC, unitNormalBC; bool degenerateAB = degenerate_vector(ab); bool degenerateCD = degenerate_vector(cd); if (degenerateAB && degenerateCD) { DRAW_LINE: this->line_to(pts[3], normalAB); *normalCD = normalAB; *unitNormalCD = unitNormalAB; return; } if (degenerateAB) { ab = pts[2] - pts[0]; degenerateAB = degenerate_vector(ab); } if (degenerateCD) { cd = pts[3] - pts[1]; degenerateCD = degenerate_vector(cd); } if (degenerateAB || degenerateCD) { goto DRAW_LINE; } SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD)); bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius, &normalBC, &unitNormalBC); if (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) || normals_too_curvy(unitNormalBC, *unitNormalCD)) { // subdivide if we can if (--subDivide < 0) { goto DRAW_LINE; } SkPoint tmp[7]; SkVector norm, unit, dummy, unitDummy; SkChopCubicAtHalf(pts, tmp); this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide); // we use dummys since we already have a valid (and more accurate) // normals for CD this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide); } else { SkVector normalB, normalC; // need normals to inset/outset the off-curve pts B and C if (0) { // this is normal to the line between our adjacent pts normalB = pts[2] - pts[0]; normalB.rotateCCW(); SkAssertResult(normalB.setLength(fRadius)); normalC = pts[3] - pts[1]; normalC.rotateCCW(); SkAssertResult(normalC.setLength(fRadius)); } else { // miter-join SkVector unitBC = pts[2] - pts[1]; unitBC.normalize(); unitBC.rotateCCW(); normalB = unitNormalAB + unitBC; normalC = *unitNormalCD + unitBC; SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC); SkAssertResult(normalB.setLength(SkScalarDiv(fRadius, SkScalarSqrt((SK_Scalar1 + dot)/2)))); dot = SkPoint::DotProduct(*unitNormalCD, unitBC); SkAssertResult(normalC.setLength(SkScalarDiv(fRadius, SkScalarSqrt((SK_Scalar1 + dot)/2)))); } fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, pts[2].fX + normalC.fX, pts[2].fY + normalC.fY, pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY); fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, pts[2].fX - normalC.fX, pts[2].fY - normalC.fY, pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY); } }
SkRTree::Branch SkRTree::bulkLoad(SkTDArray<Branch>* branches, int level) { if (branches->count() == 1) { // Only one branch: it will be the root Branch out = (*branches)[0]; branches->rewind(); return out; } else { // We sort the whole list by y coordinates, if we are told to do so. // // We expect Webkit / Blink to give us a reasonable x,y order. // Avoiding this call resulted in a 17% win for recording with // negligible difference in playback speed. if (fSortWhenBulkLoading) { SkTQSort(branches->begin(), branches->end() - 1, RectLessY()); } int numBranches = branches->count() / fMaxChildren; int remainder = branches->count() % fMaxChildren; int newBranches = 0; if (0 != remainder) { ++numBranches; // If the remainder isn't enough to fill a node, we'll need to add fewer nodes to // some other branches to make up for it if (remainder >= fMinChildren) { remainder = 0; } else { remainder = fMinChildren - remainder; } } int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches) * SkScalarInvert(fAspectRatio))); int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) / SkIntToScalar(numStrips)); int currentBranch = 0; for (int i = 0; i < numStrips; ++i) { // Once again, if we are told to do so, we sort by x. if (fSortWhenBulkLoading) { int begin = currentBranch; int end = currentBranch + numTiles * fMaxChildren - SkMin32(remainder, (fMaxChildren - fMinChildren) * numTiles); if (end > branches->count()) { end = branches->count(); } // Now we sort horizontal strips of rectangles by their x coords SkTQSort(branches->begin() + begin, branches->begin() + end - 1, RectLessX()); } for (int j = 0; j < numTiles && currentBranch < branches->count(); ++j) { int incrementBy = fMaxChildren; if (remainder != 0) { // if need be, omit some nodes to make up for remainder if (remainder <= fMaxChildren - fMinChildren) { incrementBy -= remainder; remainder = 0; } else { incrementBy = fMinChildren; remainder -= fMaxChildren - fMinChildren; } } Node* n = allocateNode(level); n->fNumChildren = 1; *n->child(0) = (*branches)[currentBranch]; Branch b; b.fBounds = (*branches)[currentBranch].fBounds; b.fChild.subtree = n; ++currentBranch; for (int k = 1; k < incrementBy && currentBranch < branches->count(); ++k) { b.fBounds.join((*branches)[currentBranch].fBounds); *n->child(k) = (*branches)[currentBranch]; ++n->fNumChildren; ++currentBranch; } (*branches)[newBranches] = b; ++newBranches; } } branches->setCount(newBranches); return this->bulkLoad(branches, level + 1); } }
bool SkMagnifierImageFilter::onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* dst, SkIPoint* offset) const { if ((src.colorType() != kN32_SkColorType) || (fSrcRect.width() >= src.width()) || (fSrcRect.height() >= src.height())) { return false; } SkAutoLockPixels alp(src); SkASSERT(src.getPixels()); if (!src.getPixels() || src.width() <= 0 || src.height() <= 0) { return false; } if (!dst->tryAllocPixels(src.info())) { return false; } SkScalar inv_inset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1; SkScalar inv_x_zoom = fSrcRect.width() / src.width(); SkScalar inv_y_zoom = fSrcRect.height() / src.height(); SkColor* sptr = src.getAddr32(0, 0); SkColor* dptr = dst->getAddr32(0, 0); int width = src.width(), height = src.height(); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { SkScalar x_dist = SkMin32(x, width - x - 1) * inv_inset; SkScalar y_dist = SkMin32(y, height - y - 1) * inv_inset; SkScalar weight = 0; static const SkScalar kScalar2 = SkScalar(2); // To create a smooth curve at the corners, we need to work on // a square twice the size of the inset. if (x_dist < kScalar2 && y_dist < kScalar2) { x_dist = kScalar2 - x_dist; y_dist = kScalar2 - y_dist; SkScalar dist = SkScalarSqrt(SkScalarSquare(x_dist) + SkScalarSquare(y_dist)); dist = SkMaxScalar(kScalar2 - dist, 0); weight = SkMinScalar(SkScalarSquare(dist), SK_Scalar1); } else { SkScalar sqDist = SkMinScalar(SkScalarSquare(x_dist), SkScalarSquare(y_dist)); weight = SkMinScalar(sqDist, SK_Scalar1); } SkScalar x_interp = SkScalarMul(weight, (fSrcRect.x() + x * inv_x_zoom)) + (SK_Scalar1 - weight) * x; SkScalar y_interp = SkScalarMul(weight, (fSrcRect.y() + y * inv_y_zoom)) + (SK_Scalar1 - weight) * y; int x_val = SkPin32(SkScalarFloorToInt(x_interp), 0, width - 1); int y_val = SkPin32(SkScalarFloorToInt(y_interp), 0, height - 1); *dptr = sptr[y_val * width + x_val]; dptr++; } } return true; }
void SkDisplayMath::executeFunction(SkDisplayable* target, int index, SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type, SkScriptValue* scriptValue) { if (scriptValue == NULL) return; SkASSERT(target == this); SkScriptValue* array = parameters.begin(); SkScriptValue* end = parameters.end(); SkScalar input = parameters[0].fOperand.fScalar; SkScalar scalarResult; switch (index) { case SK_FUNCTION(abs): scalarResult = SkScalarAbs(input); break; case SK_FUNCTION(acos): scalarResult = SkScalarACos(input); break; case SK_FUNCTION(asin): scalarResult = SkScalarASin(input); break; case SK_FUNCTION(atan): scalarResult = SkScalarATan2(input, SK_Scalar1); break; case SK_FUNCTION(atan2): scalarResult = SkScalarATan2(input, parameters[1].fOperand.fScalar); break; case SK_FUNCTION(ceil): scalarResult = SkIntToScalar(SkScalarCeil(input)); break; case SK_FUNCTION(cos): scalarResult = SkScalarCos(input); break; case SK_FUNCTION(exp): scalarResult = SkScalarExp(input); break; case SK_FUNCTION(floor): scalarResult = SkIntToScalar(SkScalarFloor(input)); break; case SK_FUNCTION(log): scalarResult = SkScalarLog(input); break; case SK_FUNCTION(max): scalarResult = -SK_ScalarMax; while (array < end) { scalarResult = SkMaxScalar(scalarResult, array->fOperand.fScalar); array++; } break; case SK_FUNCTION(min): scalarResult = SK_ScalarMax; while (array < end) { scalarResult = SkMinScalar(scalarResult, array->fOperand.fScalar); array++; } break; case SK_FUNCTION(pow): // not the greatest -- but use x^y = e^(y * ln(x)) scalarResult = SkScalarLog(input); scalarResult = SkScalarMul(parameters[1].fOperand.fScalar, scalarResult); scalarResult = SkScalarExp(scalarResult); break; case SK_FUNCTION(random): scalarResult = fRandom.nextUScalar1(); break; case SK_FUNCTION(round): scalarResult = SkIntToScalar(SkScalarRound(input)); break; case SK_FUNCTION(sin): scalarResult = SkScalarSin(input); break; case SK_FUNCTION(sqrt): { SkASSERT(parameters.count() == 1); SkASSERT(type == SkType_Float); scalarResult = SkScalarSqrt(input); } break; case SK_FUNCTION(tan): scalarResult = SkScalarTan(input); break; default: SkASSERT(0); scalarResult = SK_ScalarNaN; } scriptValue->fOperand.fScalar = scalarResult; scriptValue->fType = SkType_Float; }