static bool d_not_equal_ulps(float a, float b, int epsilon) { if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) { return false; } int aBits = SkFloatAs2sCompliment(a); int bBits = SkFloatAs2sCompliment(b); // Find the difference in ULPs. return aBits >= bBits + epsilon || bBits >= aBits + epsilon; }
sk_sp<SkImageFilter> SkOffsetImageFilter::Make(SkScalar dx, SkScalar dy, sk_sp<SkImageFilter> input, const CropRect* cropRect) { if (!SkScalarIsFinite(dx) || !SkScalarIsFinite(dy)) { return nullptr; } return sk_sp<SkImageFilter>(new SkOffsetImageFilter(dx, dy, std::move(input), cropRect)); }
SkBlurImageFilter::SkBlurImageFilter(SkReadBuffer& buffer) : INHERITED(1, buffer) { fSigma.fWidth = buffer.readScalar(); fSigma.fHeight = buffer.readScalar(); buffer.validate(SkScalarIsFinite(fSigma.fWidth) && SkScalarIsFinite(fSigma.fHeight) && (fSigma.fWidth >= 0) && (fSigma.fHeight >= 0)); }
// From Barten SPIE 1989 static float contrast_sensitivity(float cyclesPerDegree, float luminance) { float a = 440.0f * powf(1.0f + 0.7f / luminance, -0.2f); float b = 0.3f * powf(1.0f + 100.0f / luminance, 0.15f); float exp = expf(-b * cyclesPerDegree); float root = sqrtf(1.0f + 0.06f * expf(b * cyclesPerDegree)); if (!SkScalarIsFinite(exp) || !SkScalarIsFinite(root)) { return 0; } return a * cyclesPerDegree * exp * root; }
sk_sp<SkPathEffect> SkDiscretePathEffect::Make(SkScalar segLength, SkScalar deviation, uint32_t seedAssist) { if (!SkScalarIsFinite(segLength) || !SkScalarIsFinite(deviation)) { return nullptr; } if (segLength <= SK_ScalarNearlyZero) { return nullptr; } return sk_sp<SkPathEffect>(new SkDiscretePathEffect(segLength, deviation, seedAssist)); }
SkBicubicImageFilter::SkBicubicImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) { SkDEBUGCODE(bool success =) buffer.readScalarArray(fCoefficients, 16); SkASSERT(success); fScale.fWidth = buffer.readScalar(); fScale.fHeight = buffer.readScalar(); buffer.validate(SkScalarIsFinite(fScale.fWidth) && SkScalarIsFinite(fScale.fHeight) && (fScale.fWidth >= 0) && (fScale.fHeight >= 0)); }
static bool less_or_equal_ulps(float a, float b, int epsilon) { if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) { return false; } if (arguments_denormalized(a, b, epsilon)) { return a < b + FLT_EPSILON * epsilon; } int aBits = SkFloatAs2sCompliment(a); int bBits = SkFloatAs2sCompliment(b); // Find the difference in ULPs. return aBits < bBits + epsilon; }
SkDropShadowImageFilter::SkDropShadowImageFilter(SkReadBuffer& buffer) : INHERITED(1, buffer) { fDx = buffer.readScalar(); fDy = buffer.readScalar(); fSigmaX = buffer.readScalar(); fSigmaY = buffer.readScalar(); fColor = buffer.readColor(); buffer.validate(SkScalarIsFinite(fDx) && SkScalarIsFinite(fDy) && SkScalarIsFinite(fSigmaX) && SkScalarIsFinite(fSigmaY)); }
// from http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ // FIXME: move to SkFloatBits.h static bool equal_ulps(float a, float b, int epsilon, int depsilon) { if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) { return false; } if (arguments_denormalized(a, b, depsilon)) { return true; } int aBits = SkFloatAs2sCompliment(a); int bBits = SkFloatAs2sCompliment(b); // Find the difference in ULPs. return aBits < bBits + epsilon && bBits < aBits + epsilon; }
sk_sp<SkFont> SkFont::Make(sk_sp<SkTypeface> face, SkScalar size, SkScalar scaleX, SkScalar skewX, MaskType mt, uint32_t flags) { if (size <= 0 || !SkScalarIsFinite(size)) { return nullptr; } if (scaleX <= 0 || !SkScalarIsFinite(scaleX)) { return nullptr; } if (!SkScalarIsFinite(skewX)) { return nullptr; } flags &= kAllFlags; return sk_sp<SkFont>(new SkFont(std::move(face), size, scaleX, skewX, mt, flags)); }
SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase, bool scaleToFit) : fScaleToFit(scaleToFit) { SkASSERT(intervals); SkASSERT(count > 1 && SkAlign2(count) == count); fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count); fCount = count; SkScalar len = 0; for (int i = 0; i < count; i++) { SkASSERT(intervals[i] >= 0); fIntervals[i] = intervals[i]; len += intervals[i]; } fIntervalLength = len; // watch out for values that might make us go out of bounds if ((len > 0) && SkScalarIsFinite(phase) && SkScalarIsFinite(len)) { // Adjust phase to be between 0 and len, "flipping" phase if negative. // e.g., if len is 100, then phase of -20 (or -120) is equivalent to 80 if (phase < 0) { phase = -phase; if (phase > len) { phase = SkScalarMod(phase, len); } phase = len - phase; // Due to finite precision, it's possible that phase == len, // even after the subtract (if len >>> phase), so fix that here. // This fixes http://crbug.com/124652 . SkASSERT(phase <= len); if (phase == len) { phase = 0; } } else if (phase >= len) { phase = SkScalarMod(phase, len); } SkASSERT(phase >= 0 && phase < len); fInitialDashLength = FindFirstInterval(intervals, phase, &fInitialDashIndex, count); SkASSERT(fInitialDashLength >= 0); SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount); } else { fInitialDashLength = -1; // signal bad dash intervals } }
bool SkDashPath::ValidDashPath(SkScalar phase, const SkScalar intervals[], int32_t count) { if (count < 2 || !SkIsAlign2(count)) { return false; } SkScalar length = 0; for (int i = 0; i < count; i++) { if (intervals[i] < 0) { return false; } length += intervals[i]; } // watch out for values that might make us go out of bounds return length > 0 && SkScalarIsFinite(phase) && SkScalarIsFinite(length); }
int UlpsDistance(float a, float b) { if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) { return SK_MaxS32; } SkFloatIntUnion floatIntA, floatIntB; floatIntA.fFloat = a; floatIntB.fFloat = b; // Different signs means they do not match. if ((floatIntA.fSignBitInt < 0) != (floatIntB.fSignBitInt < 0)) { // Check for equality to make sure +0 == -0 return a == b ? 0 : SK_MaxS32; } // Find the difference in ULPs. return abs(floatIntA.fSignBitInt - floatIntB.fSignBitInt); }
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::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); } } }
/* 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; }
/* * We have to worry about 2 tricky conditions: * 1. underflow of mag2 (compared against nearlyzero^2) * 2. overflow of mag2 (compared w/ isfinite) * * If we underflow, we return false. If we overflow, we compute again using * doubles, which is much slower (3x in a desktop test) but will not overflow. */ bool SkPoint::setLength(float x, float y, float length) { float mag2; if (isLengthNearlyZero(x, y, &mag2)) { return false; } float scale; if (SkScalarIsFinite(mag2)) { scale = length / sk_float_sqrt(mag2); } else { // our mag2 step overflowed to infinity, so use doubles instead. // much slower, but needed when x or y are very large, other wise we // divide by inf. and return (0,0) vector. double xx = x; double yy = y; #ifdef SK_DISCARD_DENORMALIZED_FOR_SPEED // The iOS ARM processor discards small denormalized numbers to go faster. // Casting this to a float would cause the scale to go to zero. Keeping it // as a double for the multiply keeps the scale non-zero. double dscale = length / sqrt(xx * xx + yy * yy); fX = x * dscale; fY = y * dscale; return true; #else scale = (float)(length / sqrt(xx * xx + yy * yy)); #endif } fX = x * scale; fY = y * scale; return true; }
SkScalar SkPoint::Normalize(SkPoint* pt) { float x = pt->fX; float y = pt->fY; float mag2; if (isLengthNearlyZero(x, y, &mag2)) { return 0; } float mag, scale; if (SkScalarIsFinite(mag2)) { mag = sk_float_sqrt(mag2); scale = 1 / mag; } else { // our mag2 step overflowed to infinity, so use doubles instead. // much slower, but needed when x or y are very large, other wise we // divide by inf. and return (0,0) vector. double xx = x; double yy = y; double magmag = sqrt(xx * xx + yy * yy); mag = (float)magmag; // we perform the divide with the double magmag, to stay exactly the // same as setLength. It would be faster to perform the divide with // mag, but it is possible that mag has overflowed to inf. but still // have a non-zero value for scale (thanks to denormalized numbers). scale = (float)(1 / magmag); } pt->set(x * scale, y * scale); return mag; }
/** 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); }
// Conic weights must be 0 < weight <= finite static bool validate_conic_weights(const SkScalar weights[], int count) { for (int i = 0; i < count; ++i) { if (weights[i] <= 0 || !SkScalarIsFinite(weights[i])) { return false; } } return true; }
void SkDashPath::CalcDashParameters(SkScalar phase, const SkScalar intervals[], int32_t count, SkScalar* initialDashLength, int32_t* initialDashIndex, SkScalar* intervalLength, SkScalar* adjustedPhase) { SkScalar len = 0; for (int i = 0; i < count; i++) { len += intervals[i]; } *intervalLength = len; // watch out for values that might make us go out of bounds if ((len > 0) && SkScalarIsFinite(phase) && SkScalarIsFinite(len)) { // Adjust phase to be between 0 and len, "flipping" phase if negative. // e.g., if len is 100, then phase of -20 (or -120) is equivalent to 80 if (adjustedPhase) { if (phase < 0) { phase = -phase; if (phase > len) { phase = SkScalarMod(phase, len); } phase = len - phase; // Due to finite precision, it's possible that phase == len, // even after the subtract (if len >>> phase), so fix that here. // This fixes http://crbug.com/124652 . SkASSERT(phase <= len); if (phase == len) { phase = 0; } } else if (phase >= len) { phase = SkScalarMod(phase, len); } *adjustedPhase = phase; } SkASSERT(phase >= 0 && phase < len); *initialDashLength = find_first_interval(intervals, phase, initialDashIndex, count); SkASSERT(*initialDashLength >= 0); SkASSERT(*initialDashIndex >= 0 && *initialDashIndex < count); } else { *initialDashLength = -1; // signal bad dash intervals } }
sk_sp<SkImageFilter> SkDownSampleImageFilter::Make(SkScalar scale, sk_sp<SkImageFilter> input) { if (!SkScalarIsFinite(scale)) { return nullptr; } // we don't support scale in this range if (scale > SK_Scalar1 || scale <= 0) { return nullptr; } return sk_sp<SkImageFilter>(new SkDownSampleImageFilter(scale, std::move(input))); }
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; }
SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) { float mag2 = dx * dx + dy * dy; if (SkScalarIsFinite(mag2)) { return sk_float_sqrt(mag2); } else { double xx = dx; double yy = dy; return (float)sqrt(xx * xx + yy * yy); } }
SkDisplacementMapEffect::SkDisplacementMapEffect(SkReadBuffer& buffer) : INHERITED(2, buffer) { fXChannelSelector = (SkDisplacementMapEffect::ChannelSelectorType) buffer.readInt(); fYChannelSelector = (SkDisplacementMapEffect::ChannelSelectorType) buffer.readInt(); fScale = buffer.readScalar(); buffer.validate(channel_selector_type_is_valid(fXChannelSelector) && channel_selector_type_is_valid(fYChannelSelector) && SkScalarIsFinite(fScale)); }
void SkDashPathEffect::setInternalMembers(SkScalar phase) { SkScalar len = 0; for (int i = 0; i < fCount; i++) { len += fIntervals[i]; } fIntervalLength = len; // watch out for values that might make us go out of bounds if ((len > 0) && SkScalarIsFinite(phase) && SkScalarIsFinite(len)) { // Adjust phase to be between 0 and len, "flipping" phase if negative. // e.g., if len is 100, then phase of -20 (or -120) is equivalent to 80 if (phase < 0) { phase = -phase; if (phase > len) { phase = SkScalarMod(phase, len); } phase = len - phase; // Due to finite precision, it's possible that phase == len, // even after the subtract (if len >>> phase), so fix that here. // This fixes http://crbug.com/124652 . SkASSERT(phase <= len); if (phase == len) { phase = 0; } } else if (phase >= len) { phase = SkScalarMod(phase, len); } SkASSERT(phase >= 0 && phase < len); fPhase = phase; fInitialDashLength = FindFirstInterval(fIntervals, fPhase, &fInitialDashIndex, fCount); SkASSERT(fInitialDashLength >= 0); SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount); } else { fInitialDashLength = -1; // signal bad dash intervals } }
static SkScalar quad_folded_len(const SkPoint pts[3]) { SkScalar t = SkFindQuadMaxCurvature(pts); SkPoint pt = SkEvalQuadAt(pts, t); SkVector a = pts[2] - pt; SkScalar result = a.length(); if (0 != t) { SkVector b = pts[0] - pt; result += b.length(); } SkASSERT(SkScalarIsFinite(result)); return result; }
sk_sp<SkImageFilter> SkMagnifierImageFilter::Make(const SkRect& srcRect, SkScalar inset, sk_sp<SkImageFilter> input) { if (!SkScalarIsFinite(inset) || !SkIsValidRect(srcRect)) { return nullptr; } // Negative numbers in src rect are not supported if (srcRect.fLeft < 0 || srcRect.fTop < 0) { return nullptr; } return sk_sp<SkImageFilter>(new SkMagnifierImageFilter(srcRect, inset, std::move(input))); }
SkImageFilter* SkMagnifierImageFilter::Create(const SkRect& srcRect, SkScalar inset, SkImageFilter* input) { if (!SkScalarIsFinite(inset) || !SkIsValidRect(srcRect)) { return NULL; } // Negative numbers in src rect are not supported if (srcRect.fLeft < 0 || srcRect.fTop < 0) { return NULL; } return SkNEW_ARGS(SkMagnifierImageFilter, (srcRect, inset, input)); }
SkFont::SkFont(sk_sp<SkTypeface> face, SkScalar size, SkScalar scaleX, SkScalar skewX, MaskType mt, uint32_t flags) : fTypeface(face ? std::move(face) : SkTypeface::MakeDefault()) , fSize(size) , fScaleX(scaleX) , fSkewX(skewX) , fFlags(flags) , fMaskType(SkToU8(mt)) { SkASSERT(size > 0); SkASSERT(scaleX > 0); SkASSERT(SkScalarIsFinite(skewX)); SkASSERT(0 == (flags & ~kAllFlags)); }