Ejemplo n.º 1
0
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);
        }
    }
}
Ejemplo n.º 2
0
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);
    }
}
Ejemplo n.º 3
0
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]);
    }
}
Ejemplo n.º 4
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;
}
Ejemplo n.º 5
0
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;
}
Ejemplo n.º 6
0
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);
    }
}
Ejemplo n.º 7
0
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);
}
Ejemplo n.º 8
0
    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;
        }
    }
Ejemplo n.º 9
0
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);
    }
}
Ejemplo n.º 11
0
/** 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);
}
Ejemplo n.º 12
0
/** 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);
}
Ejemplo n.º 13
0
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];
        }
    }
}
Ejemplo n.º 14
0
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];
        }
    }
}
Ejemplo n.º 15
0
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);
    }
}
Ejemplo n.º 16
0
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;
}
Ejemplo n.º 17
0
/*  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;
}
Ejemplo n.º 19
0
/* 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;
}
Ejemplo n.º 20
0
/*
 *  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;
}
Ejemplo n.º 21
0
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);
}
Ejemplo n.º 22
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;
}
Ejemplo n.º 23
0
// 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);
}
Ejemplo n.º 24
0
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;
}
Ejemplo n.º 25
0
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);
}
Ejemplo n.º 26
0
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);
    }
}
Ejemplo n.º 27
0
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);
    }
}
Ejemplo n.º 28
0
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;
}
Ejemplo n.º 30
0
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;
}