static void intersect_lines(const SkPoint& ptA, const SkVector& normA, const SkPoint& ptB, const SkVector& normB, SkPoint* result) { SkScalar lineAW = -normA.dot(ptA); SkScalar lineBW = -normB.dot(ptB); SkScalar wInv = SkScalarMul(normA.fX, normB.fY) - SkScalarMul(normA.fY, normB.fX); wInv = SkScalarInvert(wInv); result->fX = SkScalarMul(normA.fY, lineBW) - SkScalarMul(lineAW, normB.fY); result->fX = SkScalarMul(result->fX, wInv); result->fY = SkScalarMul(lineAW, normB.fX) - SkScalarMul(normA.fX, lineBW); result->fY = SkScalarMul(result->fY, wInv); }
SkScalar SkPoint::distanceToLineBetweenSqd(const SkPoint& a, const SkPoint& b, Side* side) const { SkVector u = b - a; SkVector v = *this - a; SkScalar uLengthSqd = u.lengthSqd(); SkScalar det = u.cross(v); if (side) { SkASSERT(-1 == SkPoint::kLeft_Side && 0 == SkPoint::kOn_Side && 1 == kRight_Side); *side = (Side) SkScalarSignAsInt(det); } return SkScalarMulDiv(det, det, uLengthSqd); }
bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkBitmap src = source; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (!cropRectIsSet()) { if (!this->filterInput(0, proxy, source, ctx, &src, &srcOffset)) { return false; } SkVector vec; ctx.ctm().mapVectors(&vec, &fOffset, 1); offset->fX = srcOffset.fX + SkScalarRoundToInt(vec.fX); offset->fY = srcOffset.fY + SkScalarRoundToInt(vec.fY); *result = src; } else { if (!this->filterInput(0, proxy, source, ctx, &src, &srcOffset)) { return false; } SkIRect bounds; if (!this->applyCropRect(ctx, src, srcOffset, &bounds)) { return false; } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height())); if (nullptr == device.get()) { return false; } SkCanvas canvas(device); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); canvas.translate(SkIntToScalar(srcOffset.fX - bounds.fLeft), SkIntToScalar(srcOffset.fY - bounds.fTop)); SkVector vec; ctx.ctm().mapVectors(&vec, &fOffset, 1); canvas.drawBitmap(src, vec.x(), vec.y(), &paint); *result = device->accessBitmap(false); offset->fX = bounds.fLeft; offset->fY = bounds.fTop; } return true; }
void draw(SkCanvas* canvas) { SkPaint paint; paint.setAntiAlias(true); const SkPoint points[] = { { 60, -110 }, { 90, 10 }, { 120, -110 }, { 180, -50 } }; const SkPoint origin = {0, 0}; canvas->translate(30, 140); for (auto point : points) { paint.setStrokeWidth(1); paint.setColor(SK_ColorBLACK); canvas->drawLine(origin, point, paint); SkVector normal; normal.setNormalize(point.fX, point.fY); normal *= 100; paint.setStrokeWidth(10); paint.setColor(0x3f4512bf); canvas->drawLine(origin, normal, paint); } }
bool SkEmbossMaskFilter::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin) { SkScalar radius = matrix.mapRadius(fBlurRadius); if (!SkBlurMask::Blur(dst, src, radius, SkBlurMask::kInner_Style)) return false; dst->fFormat = SkMask::k3D_Format; if (margin) margin->set(SkScalarCeil(radius), SkScalarCeil(radius)); if (src.fImage == NULL) return true; // create a larger buffer for the other two channels (should force fBlur to do this for us) { uint8_t* alphaPlane = dst->fImage; size_t planeSize = dst->computeImageSize(); dst->fImage = SkMask::AllocImage(planeSize * 3); memcpy(dst->fImage, alphaPlane, planeSize); SkMask::FreeImage(alphaPlane); } // run the light direction through the matrix... Light light = fLight; matrix.mapVectors((SkVector*)(void*)light.fDirection, (SkVector*)(void*)fLight.fDirection, 1); // now restore the length of the XY component // cast to SkVector so we can call setLength (this double cast silences alias warnings) SkVector* vec = (SkVector*)(void*)light.fDirection; vec->setLength(light.fDirection[0], light.fDirection[1], SkPoint::Length(fLight.fDirection[0], fLight.fDirection[1])); SkEmbossMask::Emboss(dst, light); // restore original alpha memcpy(dst->fImage, src.fImage, src.computeImageSize()); return true; }
static void test_conic_tangents(skiatest::Reporter* reporter) { SkPoint pts[] = { { 10, 20}, {10, 20}, {20, 30}, { 10, 20}, {15, 25}, {20, 30}, { 10, 20}, {20, 30}, {20, 30} }; int count = (int) SK_ARRAY_COUNT(pts) / 3; for (int index = 0; index < count; ++index) { SkConic conic(&pts[index * 3], 0.707f); SkVector start = conic.evalTangentAt(0); SkVector mid = conic.evalTangentAt(.5f); SkVector end = conic.evalTangentAt(1); REPORTER_ASSERT(reporter, start.fX && start.fY); REPORTER_ASSERT(reporter, mid.fX && mid.fY); REPORTER_ASSERT(reporter, end.fX && end.fY); REPORTER_ASSERT(reporter, SkScalarNearlyZero(start.cross(mid))); REPORTER_ASSERT(reporter, SkScalarNearlyZero(mid.cross(end))); } }
// Compute the intersection 'p' between segments s0 and s1, if any. // 's' is the parametric value for the intersection along 's0' & 't' is the same for 's1'. // Returns false if there is no intersection. static bool compute_intersection(const InsetSegment& s0, const InsetSegment& s1, SkPoint* p, SkScalar* s, SkScalar* t) { SkVector v0 = s0.fP1 - s0.fP0; SkVector v1 = s1.fP1 - s1.fP0; SkScalar perpDot = v0.cross(v1); if (SkScalarNearlyZero(perpDot)) { // segments are parallel // check if endpoints are touching if (s0.fP1.equalsWithinTolerance(s1.fP0)) { *p = s0.fP1; *s = SK_Scalar1; *t = 0; return true; } if (s1.fP1.equalsWithinTolerance(s0.fP0)) { *p = s1.fP1; *s = 0; *t = SK_Scalar1; return true; } return false; } SkVector d = s1.fP0 - s0.fP0; SkScalar localS = d.cross(v1) / perpDot; if (localS < 0 || localS > SK_Scalar1) { return false; } SkScalar localT = d.cross(v0) / perpDot; if (localT < 0 || localT > SK_Scalar1) { return false; } v0 *= localS; *p = s0.fP0 + v0; *s = localS; *t = localT; return true; }
void GrAAConvexTessellator::computeBisectors() { fBisectors.setCount(fNorms.count()); int prev = fBisectors.count() - 1; for (int cur = 0; cur < fBisectors.count(); prev = cur, ++cur) { fBisectors[cur] = fNorms[cur] + fNorms[prev]; if (!fBisectors[cur].normalize()) { SkASSERT(SkPoint::kLeft_Side == fSide || SkPoint::kRight_Side == fSide); fBisectors[cur].setOrthog(fNorms[cur], (SkPoint::Side)-fSide); SkVector other; other.setOrthog(fNorms[prev], fSide); fBisectors[cur] += other; SkAssertResult(fBisectors[cur].normalize()); } else { fBisectors[cur].negate(); // make the bisector face in } SkASSERT(SkScalarNearlyEqual(1.0f, fBisectors[cur].length())); } }
bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { #if SK_SUPPORT_GPU SkBitmap input = src; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPU(0, proxy, src, ctx, &input, &srcOffset)) { return false; } SkIRect srcBounds, dstBounds; if (!this->applyCropRect(this->mapContext(ctx), input, srcOffset, &dstBounds, &srcBounds)) { return false; } if (!srcBounds.intersect(dstBounds)) { return false; } GrTexture* source = input.getTexture(); SkVector sigma = mapSigma(fSigma, ctx.ctm()); offset->fX = dstBounds.fLeft; offset->fY = dstBounds.fTop; srcBounds.offset(-srcOffset); dstBounds.offset(-srcOffset); SkRect srcBoundsF(SkRect::Make(srcBounds)); auto constraint = GrTextureProvider::FromImageFilter(ctx.sizeConstraint()); SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(), source, false, SkRect::Make(dstBounds), &srcBoundsF, sigma.x(), sigma.y(), constraint)); if (!tex) { return false; } WrapTexture(tex, dstBounds.width(), dstBounds.height(), result); return true; #else SkDEBUGFAIL("Should not call in GPU-less build"); return false; #endif }
static void intersect_lines(const SkPoint& ptA, const SkVector& normA, const SkPoint& ptB, const SkVector& normB, SkPoint* result) { SkScalar lineAW = -normA.dot(ptA); SkScalar lineBW = -normB.dot(ptB); SkScalar wInv = normA.fX * normB.fY - normA.fY * normB.fX; wInv = SkScalarInvert(wInv); if (!SkScalarIsFinite(wInv)) { // lines are parallel, pick the point in between *result = (ptA + ptB)*SK_ScalarHalf; *result += normA; } else { result->fX = normA.fY * lineBW - lineAW * normB.fY; result->fX *= wInv; result->fY = lineAW * normB.fX - normA.fX * lineBW; result->fY *= wInv; } }
// Offset line segment p0-p1 'd0' and 'd1' units in the direction specified by 'side' bool SkOffsetSegment(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1, int side, SkPoint* offset0, SkPoint* offset1) { SkASSERT(side == -1 || side == 1); SkVector perp = SkVector::Make(p0.fY - p1.fY, p1.fX - p0.fX); if (SkScalarNearlyEqual(d0, d1)) { // if distances are equal, can just outset by the perpendicular perp.setLength(d0*side); *offset0 = p0 + perp; *offset1 = p1 + perp; } else { // Otherwise we need to compute the outer tangent. // See: http://www.ambrsoft.com/TrigoCalc/Circles2/Circles2Tangent_.htm if (d0 < d1) { side = -side; } SkScalar dD = d0 - d1; // if one circle is inside another, we can't compute an offset if (dD*dD >= p0.distanceToSqd(p1)) { return false; } SkPoint outerTangentIntersect = SkPoint::Make((p1.fX*d0 - p0.fX*d1) / dD, (p1.fY*d0 - p0.fY*d1) / dD); SkScalar d0sq = d0*d0; SkVector dP = outerTangentIntersect - p0; SkScalar dPlenSq = dP.lengthSqd(); SkScalar discrim = SkScalarSqrt(dPlenSq - d0sq); offset0->fX = p0.fX + (d0sq*dP.fX - side*d0*dP.fY*discrim) / dPlenSq; offset0->fY = p0.fY + (d0sq*dP.fY + side*d0*dP.fX*discrim) / dPlenSq; SkScalar d1sq = d1*d1; dP = outerTangentIntersect - p1; dPlenSq = dP.lengthSqd(); discrim = SkScalarSqrt(dPlenSq - d1sq); offset1->fX = p1.fX + (d1sq*dP.fX - side*d1*dP.fY*discrim) / dPlenSq; offset1->fY = p1.fY + (d1sq*dP.fY + side*d1*dP.fX*discrim) / dPlenSq; } return true; }
static sk_sp<SkImage> MakeImage(const SkVector& vec, SkColor color) { const SkPoint start = SkPoint::Make(vec.y() * kSegLen / 2, vec.x() * kSegLen / 2); const SkPoint end = SkPoint::Make(start.x() + vec.x() * (kSegLen - 1), start.y() + vec.y() * (kSegLen - 1)); SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(kSegLen, kSegLen)); surface->getCanvas()->clear(SK_ColorTRANSPARENT); SkPaint paint; paint.setAntiAlias(true); const SkRect border = SkRect::MakeIWH(kSegLen, kSegLen).makeInset(.5f, .5f); paint.setColor(SK_ColorBLUE); paint.setStyle(SkPaint::kStroke_Style); surface->getCanvas()->drawRect(border, paint); paint.setColor(SK_ColorBLACK); surface->getCanvas()->drawLine(start.x(), start.y(), end.x(), end.y(), paint); surface->getCanvas()->drawPoint(start.x(), start.y(), color); surface->getCanvas()->drawPoint(end.x(), end.y(), color); return surface->makeImageSnapshot(); }
// Use the intersection of the corners' diagonals with their ellipses to shrink // the bounding rect SkRect compute_central_occluder(const SkRRect& rr) { const SkRect r = rr.getBounds(); SkScalar newL = r.fLeft, newT = r.fTop, newR = r.fRight, newB = r.fBottom; SkVector radii = rr.radii(SkRRect::kUpperLeft_Corner); if (!radii.isZero()) { SkPoint p = intersection(radii.fX, radii.fY); newL = SkTMax(newL, r.fLeft + radii.fX - p.fX); newT = SkTMax(newT, r.fTop + radii.fY - p.fY); } radii = rr.radii(SkRRect::kUpperRight_Corner); if (!radii.isZero()) { SkPoint p = intersection(radii.fX, radii.fY); newR = SkTMin(newR, r.fRight + p.fX - radii.fX); newT = SkTMax(newT, r.fTop + radii.fY - p.fY); } radii = rr.radii(SkRRect::kLowerRight_Corner); if (!radii.isZero()) { SkPoint p = intersection(radii.fX, radii.fY); newR = SkTMin(newR, r.fRight + p.fX - radii.fX); newB = SkTMin(newB, r.fBottom - radii.fY + p.fY); } radii = rr.radii(SkRRect::kLowerLeft_Corner); if (!radii.isZero()) { SkPoint p = intersection(radii.fX, radii.fY); newL = SkTMax(newL, r.fLeft + radii.fX - p.fX); newB = SkTMin(newB, r.fBottom - radii.fY + p.fY); } return SkRect::MakeLTRB(newL, newT, newR, newB); }
static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, const SkPoint& pivot, const SkVector& afterUnitNormal, SkScalar radius, SkScalar invMiterLimit, bool, bool) { SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal); AngleType angleType = Dot2AngleType(dotProd); if (angleType == kNearlyLine_AngleType) return; SkVector before = beforeUnitNormal; SkVector after = afterUnitNormal; SkRotationDirection dir = kCW_SkRotationDirection; if (!is_clockwise(before, after)) { SkTSwap<SkPath*>(outer, inner); before.negate(); after.negate(); dir = kCCW_SkRotationDirection; } SkPoint pts[kSkBuildQuadArcStorage]; SkMatrix matrix; matrix.setScale(radius, radius); matrix.postTranslate(pivot.fX, pivot.fY); int count = SkBuildQuadArc(before, after, dir, &matrix, pts); SkASSERT((count & 1) == 1); if (count > 1) { for (int i = 1; i < count; i += 2) outer->quadTo(pts[i].fX, pts[i].fY, pts[i+1].fX, pts[i+1].fY); after.scale(radius); HandleInnerJoin(inner, pivot, after); } }
static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale, const SkMatrix& viewMatrix, const SkPoint pts[2]) { SkVector vecSrc = pts[1] - pts[0]; SkScalar magSrc = vecSrc.length(); SkScalar invSrc = magSrc ? SkScalarInvert(magSrc) : 0; vecSrc.scale(invSrc); SkVector vecSrcPerp; vecSrc.rotateCW(&vecSrcPerp); viewMatrix.mapVectors(&vecSrc, 1); viewMatrix.mapVectors(&vecSrcPerp, 1); // parallelScale tells how much to scale along the line parallel to the dash line // perpScale tells how much to scale in the direction perpendicular to the dash line *parallelScale = vecSrc.length(); *perpScale = vecSrcPerp.length(); }
static void SquareCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal, const SkPoint& stop, SkPath* otherPath) { SkVector parallel; normal.rotateCW(¶llel); if (otherPath) { path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); } else { path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); path->lineTo(stop.fX, stop.fY); } }
void SkDropShadowImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst, MapDirection direction) const { *dst = src; SkVector offsetVec = SkVector::Make(fDx, fDy); if (kReverse_MapDirection == direction) { offsetVec.negate(); } ctm.mapVectors(&offsetVec, 1); dst->offset(SkScalarCeilToInt(offsetVec.x()), SkScalarCeilToInt(offsetVec.y())); SkVector sigma = SkVector::Make(fSigmaX, fSigmaY); ctm.mapVectors(&sigma, 1); dst->outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); if (fShadowMode == kDrawShadowAndForeground_ShadowMode) { dst->join(src); } }
SkIRect SkDropShadowImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, MapDirection direction) const { SkVector offsetVec = SkVector::Make(fDx, fDy); if (kReverse_MapDirection == direction) { offsetVec.negate(); } ctm.mapVectors(&offsetVec, 1); SkIRect dst = src.makeOffset(SkScalarCeilToInt(offsetVec.x()), SkScalarCeilToInt(offsetVec.y())); SkVector sigma = SkVector::Make(fSigmaX, fSigmaY); ctm.mapVectors(&sigma, 1); dst.outset( SkScalarCeilToInt(SkScalarAbs(sigma.x() * 3)), SkScalarCeilToInt(SkScalarAbs(sigma.y() * 3))); if (fShadowMode == kDrawShadowAndForeground_ShadowMode) { dst.join(src); } return dst; }
bool SkDropShadowImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { SkIRect bounds = src; if (getInput(0) && !getInput(0)->filterBounds(src, ctm, &bounds)) { return false; } SkVector offsetVec = SkVector::Make(fDx, fDy); ctm.mapVectors(&offsetVec, 1); bounds.offset(-SkScalarCeilToInt(offsetVec.x()), -SkScalarCeilToInt(offsetVec.y())); SkVector sigma = SkVector::Make(fSigmaX, fSigmaY); ctm.mapVectors(&sigma, 1); bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); bounds.join(src); *dst = bounds; return true; }
static void create_vertices(const SegmentArray& segments, const SkPoint& fanPt, DrawArray* draws, QuadVertex* verts, uint16_t* idxs) { Draw* draw = &draws->push_back(); // alias just to make vert/index assignments easier to read. int* v = &draw->fVertexCnt; int* i = &draw->fIndexCnt; int count = segments.count(); for (int a = 0; a < count; ++a) { const Segment& sega = segments[a]; int b = (a + 1) % count; const Segment& segb = segments[b]; // Check whether adding the verts for this segment to the current draw would cause index // values to overflow. int vCount = 4; if (Segment::kLine == segb.fType) { vCount += 5; } else { vCount += 6; } if (draw->fVertexCnt + vCount > (1 << 16)) { verts += *v; idxs += *i; draw = &draws->push_back(); v = &draw->fVertexCnt; i = &draw->fIndexCnt; } // FIXME: These tris are inset in the 1 unit arc around the corner verts[*v + 0].fPos = sega.endPt(); verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm(); verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid; verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0]; verts[*v + 0].fUV.set(0,0); verts[*v + 1].fUV.set(0,-SK_Scalar1); verts[*v + 2].fUV.set(0,-SK_Scalar1); verts[*v + 3].fUV.set(0,-SK_Scalar1); verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1; verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1; verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1; verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1; idxs[*i + 0] = *v + 0; idxs[*i + 1] = *v + 2; idxs[*i + 2] = *v + 1; idxs[*i + 3] = *v + 0; idxs[*i + 4] = *v + 3; idxs[*i + 5] = *v + 2; *v += 4; *i += 6; if (Segment::kLine == segb.fType) { verts[*v + 0].fPos = fanPt; verts[*v + 1].fPos = sega.endPt(); verts[*v + 2].fPos = segb.fPts[0]; verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0]; verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0]; // we draw the line edge as a degenerate quad (u is 0, v is the // signed distance to the edge) SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos, verts[*v + 2].fPos); verts[*v + 0].fUV.set(0, dist); verts[*v + 1].fUV.set(0, 0); verts[*v + 2].fUV.set(0, 0); verts[*v + 3].fUV.set(0, -SK_Scalar1); verts[*v + 4].fUV.set(0, -SK_Scalar1); verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1; verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1; verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1; verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1; verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1; idxs[*i + 0] = *v + 0; idxs[*i + 1] = *v + 2; idxs[*i + 2] = *v + 1; idxs[*i + 3] = *v + 3; idxs[*i + 4] = *v + 1; idxs[*i + 5] = *v + 2; idxs[*i + 6] = *v + 4; idxs[*i + 7] = *v + 3; idxs[*i + 8] = *v + 2; *v += 5; *i += 9; } else { SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]}; SkVector midVec = segb.fNorms[0] + segb.fNorms[1]; midVec.normalize(); verts[*v + 0].fPos = fanPt; verts[*v + 1].fPos = qpts[0]; verts[*v + 2].fPos = qpts[2]; verts[*v + 3].fPos = qpts[0] + segb.fNorms[0]; verts[*v + 4].fPos = qpts[2] + segb.fNorms[1]; verts[*v + 5].fPos = qpts[1] + midVec; SkScalar c = segb.fNorms[0].dot(qpts[0]); verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c; verts[*v + 1].fD0 = 0.f; verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c; verts[*v + 3].fD0 = -SK_ScalarMax/100; verts[*v + 4].fD0 = -SK_ScalarMax/100; verts[*v + 5].fD0 = -SK_ScalarMax/100; c = segb.fNorms[1].dot(qpts[2]); verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c; verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c; verts[*v + 2].fD1 = 0.f; verts[*v + 3].fD1 = -SK_ScalarMax/100; verts[*v + 4].fD1 = -SK_ScalarMax/100; verts[*v + 5].fD1 = -SK_ScalarMax/100; GrPathUtils::QuadUVMatrix toUV(qpts); toUV.apply<6, sizeof(QuadVertex), sizeof(SkPoint)>(verts + *v); idxs[*i + 0] = *v + 3; idxs[*i + 1] = *v + 1; idxs[*i + 2] = *v + 2; idxs[*i + 3] = *v + 4; idxs[*i + 4] = *v + 3; idxs[*i + 5] = *v + 2; idxs[*i + 6] = *v + 5; idxs[*i + 7] = *v + 3; idxs[*i + 8] = *v + 4; idxs[*i + 9] = *v + 0; idxs[*i + 10] = *v + 2; idxs[*i + 11] = *v + 1; *v += 6; *i += 12; } } }
static void Perterb(SkPoint* p, const SkVector& tangent, SkScalar scale) { SkVector normal = tangent; SkPointPriv::RotateCCW(&normal); normal.setLength(scale); *p += normal; }
void GrAARectRenderer::strokeAARect(GrDrawTarget* target, GrDrawState* drawState, GrColor color, const SkRect& rect, const SkMatrix& combinedMatrix, const SkRect& devRect, const SkStrokeRec& stroke) { SkVector devStrokeSize; SkScalar width = stroke.getWidth(); if (width > 0) { devStrokeSize.set(width, width); combinedMatrix.mapVectors(&devStrokeSize, 1); devStrokeSize.setAbs(devStrokeSize); } else { devStrokeSize.set(SK_Scalar1, SK_Scalar1); } const SkScalar dx = devStrokeSize.fX; const SkScalar dy = devStrokeSize.fY; const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf); const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf); // Temporarily #if'ed out. We don't want to pass in the devRect but // right now it is computed in GrContext::apply_aa_to_rect and we don't // want to throw away the work #if 0 SkRect devRect; combinedMatrix.mapRect(&devRect, rect); #endif SkScalar spare; { SkScalar w = devRect.width() - dx; SkScalar h = devRect.height() - dy; spare = SkTMin(w, h); } SkRect devOutside(devRect); devOutside.outset(rx, ry); bool miterStroke = true; // For hairlines, make bevel and round joins appear the same as mitered ones. // small miter limit means right angles show bevel... if ((width > 0) && (stroke.getJoin() != SkPaint::kMiter_Join || stroke.getMiter() < SK_ScalarSqrt2)) { miterStroke = false; } if (spare <= 0 && miterStroke) { this->fillAARect(target, drawState, color, devOutside, SkMatrix::I(), devOutside); return; } SkRect devInside(devRect); devInside.inset(rx, ry); SkRect devOutsideAssist(devRect); // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist) // to draw the outer of the rect. Because there are 8 vertices on the outer // edge, while vertex number of inner edge is 4, the same as miter-stroke. if (!miterStroke) { devOutside.inset(0, ry); devOutsideAssist.outset(0, ry); } this->geometryStrokeAARect(target, drawState, color, devOutside, devOutsideAssist, devInside, miterStroke); }
bool SkBlurImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* dst, SkIPoint* offset) const { SkBitmap src = source; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (!this->filterInput(0, proxy, source, ctx, &src, &srcOffset)) { return false; } if (src.colorType() != kN32_SkColorType) { return false; } SkIRect srcBounds, dstBounds; if (!this->applyCropRect(this->mapContext(ctx), src, srcOffset, &dstBounds, &srcBounds)) { return false; } if (!srcBounds.intersect(dstBounds)) { return false; } SkAutoLockPixels alp(src); if (!src.getPixels()) { return false; } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(dstBounds.width(), dstBounds.height())); if (!device) { return false; } *dst = device->accessBitmap(false); SkAutoLockPixels alp_dst(*dst); SkVector sigma = mapSigma(fSigma, ctx.ctm()); int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; getBox3Params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); getBox3Params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); if (kernelSizeX < 0 || kernelSizeY < 0) { return false; } if (kernelSizeX == 0 && kernelSizeY == 0) { src.copyTo(dst, dst->colorType()); offset->fX = dstBounds.x() + srcOffset.x(); offset->fY = dstBounds.y() + srcOffset.y(); return true; } SkAutoTUnref<SkBaseDevice> tempDevice(proxy->createDevice(dst->width(), dst->height())); if (!tempDevice) { return false; } SkBitmap temp = tempDevice->accessBitmap(false); SkAutoLockPixels alpTemp(temp); offset->fX = dstBounds.fLeft; offset->fY = dstBounds.fTop; SkPMColor* t = temp.getAddr32(0, 0); SkPMColor* d = dst->getAddr32(0, 0); int w = dstBounds.width(), h = dstBounds.height(); const SkPMColor* s = src.getAddr32(srcBounds.x() - srcOffset.x(), srcBounds.y() - srcOffset.y()); srcBounds.offset(-dstBounds.x(), -dstBounds.y()); dstBounds.offset(-dstBounds.x(), -dstBounds.y()); SkIRect srcBoundsT = SkIRect::MakeLTRB(srcBounds.top(), srcBounds.left(), srcBounds.bottom(), srcBounds.right()); SkIRect dstBoundsT = SkIRect::MakeWH(dstBounds.height(), dstBounds.width()); int sw = src.rowBytesAsPixels(); /** * * In order to make memory accesses cache-friendly, we reorder the passes to * use contiguous memory reads wherever possible. * * For example, the 6 passes of the X-and-Y blur case are rewritten as * follows. Instead of 3 passes in X and 3 passes in Y, we perform * 2 passes in X, 1 pass in X transposed to Y on write, 2 passes in X, * then 1 pass in X transposed to Y on write. * * +----+ +----+ +----+ +---+ +---+ +---+ +----+ * + AB + ----> | AB | ----> | AB | -----> | A | ----> | A | ----> | A | -----> | AB | * +----+ blurX +----+ blurX +----+ blurXY | B | blurX | B | blurX | B | blurXY +----+ * +---+ +---+ +---+ * * In this way, two of the y-blurs become x-blurs applied to transposed * images, and all memory reads are contiguous. */ if (kernelSizeX > 0 && kernelSizeY > 0) { SkOpts::box_blur_xx(s, sw, srcBounds, t, kernelSizeX, lowOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(t, w, dstBounds, d, kernelSizeX, highOffsetX, lowOffsetX, w, h); SkOpts::box_blur_xy(d, w, dstBounds, t, kernelSizeX3, highOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(t, h, dstBoundsT, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); SkOpts::box_blur_xx(d, h, dstBoundsT, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); SkOpts::box_blur_xy(t, h, dstBoundsT, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } else if (kernelSizeX > 0) { SkOpts::box_blur_xx(s, sw, srcBounds, d, kernelSizeX, lowOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(d, w, dstBounds, t, kernelSizeX, highOffsetX, lowOffsetX, w, h); SkOpts::box_blur_xx(t, w, dstBounds, d, kernelSizeX3, highOffsetX, highOffsetX, w, h); } else if (kernelSizeY > 0) { SkOpts::box_blur_yx(s, sw, srcBoundsT, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); SkOpts::box_blur_xx(d, h, dstBoundsT, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); SkOpts::box_blur_xy(t, h, dstBoundsT, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } return true; }
bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkImageFilter* input = getInput(0); SkBitmap src = source; SkIPoint srcOffset = SkIPoint::Make(0, 0); #ifdef SK_DISABLE_OFFSETIMAGEFILTER_OPTIMIZATION if (false) { #else if (!cropRectIsSet()) { #endif if (input && !input->filterImage(proxy, source, ctx, &src, &srcOffset)) { return false; } SkVector vec; ctx.ctm().mapVectors(&vec, &fOffset, 1); offset->fX = srcOffset.fX + SkScalarRoundToInt(vec.fX); offset->fY = srcOffset.fY + SkScalarRoundToInt(vec.fY); *result = src; } else { if (input && !input->filterImage(proxy, source, ctx, &src, &srcOffset)) { return false; } SkIRect bounds; if (!this->applyCropRect(ctx, src, srcOffset, &bounds)) { return false; } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height())); if (NULL == device.get()) { return false; } SkCanvas canvas(device); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); canvas.translate(SkIntToScalar(srcOffset.fX - bounds.fLeft), SkIntToScalar(srcOffset.fY - bounds.fTop)); SkVector vec; ctx.ctm().mapVectors(&vec, &fOffset, 1); canvas.drawBitmap(src, vec.x(), vec.y(), &paint); *result = device->accessBitmap(false); offset->fX = bounds.fLeft; offset->fY = bounds.fTop; } return true; } void SkOffsetImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const { if (getInput(0)) { getInput(0)->computeFastBounds(src, dst); } else { *dst = src; } SkRect copy = *dst; dst->offset(fOffset.fX, fOffset.fY); dst->join(copy); } bool SkOffsetImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { SkVector vec; ctm.mapVectors(&vec, &fOffset, 1); SkIRect bounds = src; bounds.offset(-SkScalarCeilToInt(vec.fX), -SkScalarCeilToInt(vec.fY)); bounds.join(src); if (getInput(0)) { return getInput(0)->filterBounds(bounds, ctm, dst); } *dst = bounds; return true; }
bool SkBlurImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* dst, SkIPoint* offset) const { SkBitmap src = source; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) { return false; } if (src.colorType() != kN32_SkColorType) { return false; } SkIRect srcBounds, dstBounds; if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &srcBounds, &src)) { return false; } SkAutoLockPixels alp(src); if (!src.getPixels()) { return false; } if (!dst->allocPixels(src.info().makeWH(srcBounds.width(), srcBounds.height()))) { return false; } dst->getBounds(&dstBounds); SkVector sigma = SkVector::Make(fSigma.width(), fSigma.height()); ctx.ctm().mapVectors(&sigma, 1); sigma.fX = SkMinScalar(sigma.fX, MAX_SIGMA); sigma.fY = SkMinScalar(sigma.fY, MAX_SIGMA); int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; getBox3Params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); getBox3Params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); if (kernelSizeX < 0 || kernelSizeY < 0) { return false; } if (kernelSizeX == 0 && kernelSizeY == 0) { src.copyTo(dst, dst->colorType()); offset->fX = srcBounds.fLeft; offset->fY = srcBounds.fTop; return true; } SkBitmap temp; if (!temp.allocPixels(dst->info())) { return false; } offset->fX = srcBounds.fLeft; offset->fY = srcBounds.fTop; srcBounds.offset(-srcOffset); const SkPMColor* s = src.getAddr32(srcBounds.left(), srcBounds.top()); SkPMColor* t = temp.getAddr32(0, 0); SkPMColor* d = dst->getAddr32(0, 0); int w = dstBounds.width(), h = dstBounds.height(); int sw = src.rowBytesAsPixels(); SkBoxBlurProc boxBlurX, boxBlurY, boxBlurXY, boxBlurYX; if (!SkBoxBlurGetPlatformProcs(&boxBlurX, &boxBlurY, &boxBlurXY, &boxBlurYX)) { boxBlurX = boxBlur<kX, kX>; boxBlurY = boxBlur<kY, kY>; boxBlurXY = boxBlur<kX, kY>; boxBlurYX = boxBlur<kY, kX>; } if (kernelSizeX > 0 && kernelSizeY > 0) { boxBlurX(s, sw, t, kernelSizeX, lowOffsetX, highOffsetX, w, h); boxBlurX(t, w, d, kernelSizeX, highOffsetX, lowOffsetX, w, h); boxBlurXY(d, w, t, kernelSizeX3, highOffsetX, highOffsetX, w, h); boxBlurX(t, h, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); boxBlurX(d, h, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); boxBlurXY(t, h, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } else if (kernelSizeX > 0) { boxBlurX(s, sw, d, kernelSizeX, lowOffsetX, highOffsetX, w, h); boxBlurX(d, w, t, kernelSizeX, highOffsetX, lowOffsetX, w, h); boxBlurX(t, w, d, kernelSizeX3, highOffsetX, highOffsetX, w, h); } else if (kernelSizeY > 0) { boxBlurYX(s, sw, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); boxBlurX(d, h, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); boxBlurXY(t, h, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } return true; }
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); } }
static void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice, const SkMatrix* toSrc, BezierVertex verts[kQuadNumVertices]) { SkASSERT(!toDevice == !toSrc); // original quad is specified by tri a,b,c SkPoint a = qpts[0]; SkPoint b = qpts[1]; SkPoint c = qpts[2]; if (toDevice) { toDevice->mapPoints(&a, 1); toDevice->mapPoints(&b, 1); toDevice->mapPoints(&c, 1); } // make a new poly where we replace a and c by a 1-pixel wide edges orthog // to edges ab and bc: // // before | after // | b0 // b | // | // | a0 c0 // a c | a1 c1 // // edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c, // respectively. BezierVertex& a0 = verts[0]; BezierVertex& a1 = verts[1]; BezierVertex& b0 = verts[2]; BezierVertex& c0 = verts[3]; BezierVertex& c1 = verts[4]; SkVector ab = b; ab -= a; SkVector ac = c; ac -= a; SkVector cb = b; cb -= c; // We should have already handled degenerates SkASSERT(ab.length() > 0 && cb.length() > 0); ab.normalize(); SkVector abN; abN.setOrthog(ab, SkVector::kLeft_Side); if (abN.dot(ac) > 0) { abN.negate(); } cb.normalize(); SkVector cbN; cbN.setOrthog(cb, SkVector::kLeft_Side); if (cbN.dot(ac) < 0) { cbN.negate(); } a0.fPos = a; a0.fPos += abN; a1.fPos = a; a1.fPos -= abN; c0.fPos = c; c0.fPos += cbN; c1.fPos = c; c1.fPos -= cbN; intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos); if (toSrc) { toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(BezierVertex), kQuadNumVertices); } }
SkIRect SkBlurImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, MapDirection) const { SkVector sigma = map_sigma(fSigma, ctm); return src.makeOutset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); }
// Currently asPoints is more restrictive then it needs to be. In the future // we need to: // allow kRound_Cap capping (could allow rotations in the matrix with this) // allow paths to be returned bool SkDashPathEffect::asPoints(PointData* results, const SkPath& src, const SkStrokeRec& rec, const SkMatrix& matrix, const SkRect* cullRect) const { // width < 0 -> fill && width == 0 -> hairline so requiring width > 0 rules both out if (fInitialDashLength < 0 || 0 >= rec.getWidth()) { return false; } // TODO: this next test could be eased up. We could allow any number of // intervals as long as all the ons match and all the offs match. // Additionally, they do not necessarily need to be integers. // We cannot allow arbitrary intervals since we want the returned points // to be uniformly sized. if (fCount != 2 || !SkScalarNearlyEqual(fIntervals[0], fIntervals[1]) || !SkScalarIsInt(fIntervals[0]) || !SkScalarIsInt(fIntervals[1])) { return false; } SkPoint pts[2]; if (!src.isLine(pts)) { return false; } // TODO: this test could be eased up to allow circles if (SkPaint::kButt_Cap != rec.getCap()) { return false; } // TODO: this test could be eased up for circles. Rotations could be allowed. if (!matrix.rectStaysRect()) { return false; } // See if the line can be limited to something plausible. if (!cull_line(pts, rec, matrix, cullRect, fIntervalLength)) { return false; } SkScalar length = SkPoint::Distance(pts[1], pts[0]); SkVector tangent = pts[1] - pts[0]; if (tangent.isZero()) { return false; } tangent.scale(SkScalarInvert(length)); // TODO: make this test for horizontal & vertical lines more robust bool isXAxis = true; if (SkScalarNearlyEqual(SK_Scalar1, tangent.fX) || SkScalarNearlyEqual(-SK_Scalar1, tangent.fX)) { results->fSize.set(SkScalarHalf(fIntervals[0]), SkScalarHalf(rec.getWidth())); } else if (SkScalarNearlyEqual(SK_Scalar1, tangent.fY) || SkScalarNearlyEqual(-SK_Scalar1, tangent.fY)) { results->fSize.set(SkScalarHalf(rec.getWidth()), SkScalarHalf(fIntervals[0])); isXAxis = false; } else if (SkPaint::kRound_Cap != rec.getCap()) { // Angled lines don't have axis-aligned boxes. return false; } if (results) { results->fFlags = 0; SkScalar clampedInitialDashLength = SkMinScalar(length, fInitialDashLength); if (SkPaint::kRound_Cap == rec.getCap()) { results->fFlags |= PointData::kCircles_PointFlag; } results->fNumPoints = 0; SkScalar len2 = length; if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) { SkASSERT(len2 >= clampedInitialDashLength); if (0 == fInitialDashIndex) { if (clampedInitialDashLength > 0) { if (clampedInitialDashLength >= fIntervals[0]) { ++results->fNumPoints; // partial first dash } len2 -= clampedInitialDashLength; } len2 -= fIntervals[1]; // also skip first space if (len2 < 0) { len2 = 0; } } else { len2 -= clampedInitialDashLength; // skip initial partial empty } } int numMidPoints = SkScalarFloorToInt(len2 / fIntervalLength); results->fNumPoints += numMidPoints; len2 -= numMidPoints * fIntervalLength; bool partialLast = false; if (len2 > 0) { if (len2 < fIntervals[0]) { partialLast = true; } else { ++numMidPoints; ++results->fNumPoints; } } results->fPoints = new SkPoint[results->fNumPoints]; SkScalar distance = 0; int curPt = 0; if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) { SkASSERT(clampedInitialDashLength <= length); if (0 == fInitialDashIndex) { if (clampedInitialDashLength > 0) { // partial first block SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, SkScalarHalf(clampedInitialDashLength)); SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, SkScalarHalf(clampedInitialDashLength)); SkScalar halfWidth, halfHeight; if (isXAxis) { halfWidth = SkScalarHalf(clampedInitialDashLength); halfHeight = SkScalarHalf(rec.getWidth()); } else { halfWidth = SkScalarHalf(rec.getWidth()); halfHeight = SkScalarHalf(clampedInitialDashLength); } if (clampedInitialDashLength < fIntervals[0]) { // This one will not be like the others results->fFirst.addRect(x - halfWidth, y - halfHeight, x + halfWidth, y + halfHeight); } else { SkASSERT(curPt < results->fNumPoints); results->fPoints[curPt].set(x, y); ++curPt; } distance += clampedInitialDashLength; } distance += fIntervals[1]; // skip over the next blank block too } else { distance += clampedInitialDashLength; } } if (0 != numMidPoints) { distance += SkScalarHalf(fIntervals[0]); for (int i = 0; i < numMidPoints; ++i) { SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance); SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance); SkASSERT(curPt < results->fNumPoints); results->fPoints[curPt].set(x, y); ++curPt; distance += fIntervalLength; } distance -= SkScalarHalf(fIntervals[0]); } if (partialLast) { // partial final block SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles SkScalar temp = length - distance; SkASSERT(temp < fIntervals[0]); SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance + SkScalarHalf(temp)); SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance + SkScalarHalf(temp)); SkScalar halfWidth, halfHeight; if (isXAxis) { halfWidth = SkScalarHalf(temp); halfHeight = SkScalarHalf(rec.getWidth()); } else { halfWidth = SkScalarHalf(rec.getWidth()); halfHeight = SkScalarHalf(temp); } results->fLast.addRect(x - halfWidth, y - halfHeight, x + halfWidth, y + halfHeight); } SkASSERT(curPt == results->fNumPoints); } return true; }
sk_sp<SkSpecialImage> SkBlurImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint inputOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset)); if (!input) { return nullptr; } SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.fX, inputOffset.fY, input->width(), input->height()); SkIRect dstBounds; if (!this->applyCropRect(this->mapContext(ctx), inputBounds, &dstBounds)) { return nullptr; } if (!inputBounds.intersect(dstBounds)) { return nullptr; } const SkVector sigma = map_sigma(fSigma, ctx.ctm()); #if SK_SUPPORT_GPU if (input->peekTexture() && input->peekTexture()->getContext()) { if (0 == sigma.x() && 0 == sigma.y()) { offset->fX = inputBounds.x(); offset->fY = inputBounds.y(); return input->makeSubset(inputBounds.makeOffset(-inputOffset.x(), -inputOffset.y())); } GrTexture* inputTexture = input->peekTexture(); offset->fX = dstBounds.fLeft; offset->fY = dstBounds.fTop; inputBounds.offset(-inputOffset); dstBounds.offset(-inputOffset); SkRect inputBoundsF(SkRect::Make(inputBounds)); SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(inputTexture->getContext(), inputTexture, false, source->props().allowSRGBInputs(), SkRect::Make(dstBounds), &inputBoundsF, sigma.x(), sigma.y())); if (!tex) { return nullptr; } return SkSpecialImage::MakeFromGpu(source->internal_getProxy(), SkIRect::MakeWH(dstBounds.width(), dstBounds.height()), kNeedNewImageUniqueID_SpecialImage, tex, &source->props()); } #endif int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; get_box3_params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); get_box3_params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); if (kernelSizeX < 0 || kernelSizeY < 0) { return nullptr; } if (kernelSizeX == 0 && kernelSizeY == 0) { offset->fX = inputBounds.x(); offset->fY = inputBounds.y(); return input->makeSubset(inputBounds.makeOffset(-inputOffset.x(), -inputOffset.y())); } SkPixmap inputPixmap; if (!input->peekPixels(&inputPixmap)) { return nullptr; } if (inputPixmap.colorType() != kN32_SkColorType) { return nullptr; } SkImageInfo info = SkImageInfo::Make(dstBounds.width(), dstBounds.height(), inputPixmap.colorType(), inputPixmap.alphaType()); SkBitmap tmp, dst; if (!tmp.tryAllocPixels(info) || !dst.tryAllocPixels(info)) { return nullptr; } SkAutoLockPixels tmpLock(tmp), dstLock(dst); offset->fX = dstBounds.fLeft; offset->fY = dstBounds.fTop; SkPMColor* t = tmp.getAddr32(0, 0); SkPMColor* d = dst.getAddr32(0, 0); int w = dstBounds.width(), h = dstBounds.height(); const SkPMColor* s = inputPixmap.addr32(inputBounds.x() - inputOffset.x(), inputBounds.y() - inputOffset.y()); inputBounds.offset(-dstBounds.x(), -dstBounds.y()); dstBounds.offset(-dstBounds.x(), -dstBounds.y()); SkIRect inputBoundsT = SkIRect::MakeLTRB(inputBounds.top(), inputBounds.left(), inputBounds.bottom(), inputBounds.right()); SkIRect dstBoundsT = SkIRect::MakeWH(dstBounds.height(), dstBounds.width()); int sw = int(inputPixmap.rowBytes() >> 2); /** * * In order to make memory accesses cache-friendly, we reorder the passes to * use contiguous memory reads wherever possible. * * For example, the 6 passes of the X-and-Y blur case are rewritten as * follows. Instead of 3 passes in X and 3 passes in Y, we perform * 2 passes in X, 1 pass in X transposed to Y on write, 2 passes in X, * then 1 pass in X transposed to Y on write. * * +----+ +----+ +----+ +---+ +---+ +---+ +----+ * + AB + ----> | AB | ----> | AB | -----> | A | ----> | A | ----> | A | -----> | AB | * +----+ blurX +----+ blurX +----+ blurXY | B | blurX | B | blurX | B | blurXY +----+ * +---+ +---+ +---+ * * In this way, two of the y-blurs become x-blurs applied to transposed * images, and all memory reads are contiguous. */ if (kernelSizeX > 0 && kernelSizeY > 0) { SkOpts::box_blur_xx(s, sw, inputBounds, t, kernelSizeX, lowOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(t, w, dstBounds, d, kernelSizeX, highOffsetX, lowOffsetX, w, h); SkOpts::box_blur_xy(d, w, dstBounds, t, kernelSizeX3, highOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(t, h, dstBoundsT, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); SkOpts::box_blur_xx(d, h, dstBoundsT, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); SkOpts::box_blur_xy(t, h, dstBoundsT, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } else if (kernelSizeX > 0) { SkOpts::box_blur_xx(s, sw, inputBounds, d, kernelSizeX, lowOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(d, w, dstBounds, t, kernelSizeX, highOffsetX, lowOffsetX, w, h); SkOpts::box_blur_xx(t, w, dstBounds, d, kernelSizeX3, highOffsetX, highOffsetX, w, h); } else if (kernelSizeY > 0) { SkOpts::box_blur_yx(s, sw, inputBoundsT, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); SkOpts::box_blur_xx(d, h, dstBoundsT, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); SkOpts::box_blur_xy(t, h, dstBoundsT, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } return SkSpecialImage::MakeFromRaster(source->internal_getProxy(), SkIRect::MakeWH(dstBounds.width(), dstBounds.height()), dst, &source->props()); }