/** * Draw a single path element of the clip stack into the accumulation bitmap */ void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op, bool antiAlias, uint8_t alpha) { SkPaint paint; if (stroke.isHairlineStyle()) { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(SK_Scalar1); } else { if (stroke.isFillStyle()) { paint.setStyle(SkPaint::kFill_Style); } else { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeJoin(stroke.getJoin()); paint.setStrokeCap(stroke.getCap()); paint.setStrokeWidth(stroke.getWidth()); } } paint.setAntiAlias(antiAlias); if (SkRegion::kReplace_Op == op && 0xFF == alpha) { SkASSERT(0xFF == paint.getAlpha()); fDraw.drawPathCoverage(path, paint); } else { paint.setXfermodeMode(op_to_mode(op)); paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); fDraw.drawPath(path, paint); } }
uint64_t GrPath::ComputeStrokeKey(const SkStrokeRec& stroke) { enum { kStyleBits = 2, kJoinBits = 2, kCapBits = 2, kWidthBits = 29, kMiterBits = 29, kJoinShift = kStyleBits, kCapShift = kJoinShift + kJoinBits, kWidthShift = kCapShift + kCapBits, kMiterShift = kWidthShift + kWidthBits, kBitCount = kMiterShift + kMiterBits }; SK_COMPILE_ASSERT(SkStrokeRec::kStyleCount <= (1 << kStyleBits), style_shift_will_be_wrong); SK_COMPILE_ASSERT(SkPaint::kJoinCount <= (1 << kJoinBits), cap_shift_will_be_wrong); SK_COMPILE_ASSERT(SkPaint::kCapCount <= (1 << kCapBits), miter_shift_will_be_wrong); SK_COMPILE_ASSERT(kBitCount == 64, wrong_stroke_key_size); if (!stroke.needToApply()) { return SkStrokeRec::kFill_Style; } uint64_t key = stroke.getStyle(); key |= stroke.getJoin() << kJoinShift; key |= stroke.getCap() << kCapShift; key |= get_top_n_float_bits<kWidthBits>(stroke.getWidth()) << kWidthShift; key |= get_top_n_float_bits<kMiterBits>(stroke.getMiter()) << kMiterShift; return key; }
/** * Draw a single path element of the clip stack into the accumulation bitmap */ void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op, bool antiAlias, uint8_t alpha) { SkPaint paint; if (stroke.isHairlineStyle()) { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(SK_Scalar1); } else { if (stroke.isFillStyle()) { paint.setStyle(SkPaint::kFill_Style); } else { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeJoin(stroke.getJoin()); paint.setStrokeCap(stroke.getCap()); paint.setStrokeWidth(stroke.getWidth()); } } SkXfermode* mode = SkXfermode::Create(op_to_mode(op)); paint.setXfermode(mode); paint.setAntiAlias(antiAlias); paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); fDraw.drawPath(path, paint); SkSafeUnref(mode); }
bool GrStrokePathRenderer::canDrawPath(const SkPath& path, const SkStrokeRec& stroke, const GrDrawTarget* target, bool antiAlias) const { // FIXME : put the proper condition once GrDrawTarget::isOpaque is implemented const bool isOpaque = true; // target->isOpaque(); // FIXME : remove this requirement once we have AA circles and implement the // circle joins/caps appropriately in the ::onDrawPath() function. const bool requiresAACircle = (stroke.getCap() == SkPaint::kRound_Cap) || (stroke.getJoin() == SkPaint::kRound_Join); // Indices being stored in uint16, we don't want to overflow the indices capacity static const int maxVBSize = 1 << 16; const int maxNbVerts = (path.countPoints() + 1) * 5; // Check that the path contains no curved lines, only straight lines static const uint32_t unsupportedMask = SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask; // Must not be filled nor hairline nor semi-transparent // Note : May require a check to path.isConvex() if AA is supported return ((stroke.getStyle() == SkStrokeRec::kStroke_Style) && (maxNbVerts < maxVBSize) && !path.isInverseFillType() && isOpaque && !requiresAACircle && !antiAlias && ((path.getSegmentMasks() & unsupportedMask) == 0)); }
inline static bool is_miter(const SkStrokeRec& stroke) { // For hairlines, make bevel and round joins appear the same as mitered ones. // small miter limit means right angles show bevel... if ((stroke.getWidth() > 0) && (stroke.getJoin() != SkPaint::kMiter_Join || stroke.getMiter() < SK_ScalarSqrt2)) { return false; } return true; }
static void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) { SkScalar radius = SkScalarHalf(rec.getWidth()); if (0 == radius) { radius = SK_Scalar1; // hairlines } if (SkPaint::kMiter_Join == rec.getJoin()) { radius = SkScalarMul(radius, rec.getMiter()); } rect->outset(radius, radius); }
/** * Draw a single path element of the clip stack into the accumulation bitmap */ void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op, bool antiAlias, uint8_t alpha) { SkPaint paint; if (stroke.isHairlineStyle()) { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(SK_Scalar1); } else { if (stroke.isFillStyle()) { paint.setStyle(SkPaint::kFill_Style); } else { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeJoin(stroke.getJoin()); paint.setStrokeCap(stroke.getCap()); paint.setStrokeWidth(stroke.getWidth()); } } paint.setAntiAlias(antiAlias); SkTBlitterAllocator allocator; SkBlitter* blitter = nullptr; if (kBlitter_CompressionMode == fCompressionMode) { SkASSERT(fCompressedBuffer.get()); blitter = SkTextureCompressor::CreateBlitterForFormat( fPixels.width(), fPixels.height(), fCompressedBuffer.get(), &allocator, fCompressedFormat); } if (SkRegion::kReplace_Op == op && 0xFF == alpha) { SkASSERT(0xFF == paint.getAlpha()); fDraw.drawPathCoverage(path, paint, blitter); } else { paint.setXfermodeMode(op_to_mode(op)); paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); fDraw.drawPath(path, paint, blitter); } }
// Allow all hairlines and all miters, so long as the miter limit doesn't produce beveled corners. inline static bool allowed_stroke(const SkStrokeRec& stroke) { SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style || stroke.getStyle() == SkStrokeRec::kHairline_Style); return !stroke.getWidth() || (stroke.getJoin() == SkPaint::kMiter_Join && stroke.getMiter() > SK_ScalarSqrt2); }
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 GrStrokePathRenderer::onDrawPath(const SkPath& origPath, const SkStrokeRec& stroke, GrDrawTarget* target, bool antiAlias) { if (origPath.isEmpty()) { return true; } SkScalar width = stroke.getWidth(); if (width <= 0) { return false; } // Get the join type SkPaint::Join join = stroke.getJoin(); SkScalar miterLimit = stroke.getMiter(); SkScalar sqMiterLimit = SkScalarMul(miterLimit, miterLimit); if ((join == SkPaint::kMiter_Join) && (miterLimit <= SK_Scalar1)) { // If the miter limit is small, treat it as a bevel join join = SkPaint::kBevel_Join; } const bool isMiter = (join == SkPaint::kMiter_Join); const bool isBevel = (join == SkPaint::kBevel_Join); SkScalar invMiterLimit = isMiter ? SK_Scalar1 / miterLimit : 0; SkScalar invMiterLimitSq = SkScalarMul(invMiterLimit, invMiterLimit); // Allocate vertices const int nbQuads = origPath.countPoints() + 1; // Could be "-1" if path is not closed const int extraVerts = isMiter || isBevel ? 1 : 0; const int maxVertexCount = nbQuads * (4 + extraVerts); const int maxIndexCount = nbQuads * (6 + extraVerts * 3); // Each extra vert adds a triangle target->drawState()->setDefaultVertexAttribs(); GrDrawTarget::AutoReleaseGeometry arg(target, maxVertexCount, maxIndexCount); if (!arg.succeeded()) { return false; } SkPoint* verts = reinterpret_cast<SkPoint*>(arg.vertices()); uint16_t* idxs = reinterpret_cast<uint16_t*>(arg.indices()); int vCount = 0, iCount = 0; // Transform the path into a list of triangles SkPath::Iter iter(origPath, false); SkPoint pts[4]; const SkScalar radius = SkScalarMul(width, 0.5f); SkPoint *firstPt = verts, *lastPt = NULL; SkVector firstDir, dir; firstDir.set(0, 0); dir.set(0, 0); bool isOpen = true; for(SkPath::Verb v = iter.next(pts); v != SkPath::kDone_Verb; v = iter.next(pts)) { switch(v) { case SkPath::kMove_Verb: // This will already be handled as pts[0] of the 1st line break; case SkPath::kClose_Verb: isOpen = (lastPt == NULL); break; case SkPath::kLine_Verb: { SkVector v0 = dir; dir = pts[1] - pts[0]; if (dir.setLength(radius)) { SkVector dirT; dirT.set(dir.fY, -dir.fX); // Get perpendicular direction SkPoint l1a = pts[0]+dirT, l1b = pts[1]+dirT, l2a = pts[0]-dirT, l2b = pts[1]-dirT; SkPoint miterPt[2]; bool useMiterPoint = false; int idx0(-1), idx1(-1); if (NULL == lastPt) { firstDir = dir; } else { SkVector v1 = dir; if (v0.normalize() && v1.normalize()) { SkScalar dotProd = v0.dot(v1); // No need for bevel or miter join if the angle // is either 0 or 180 degrees if (!SkScalarNearlyZero(dotProd + SK_Scalar1) && !SkScalarNearlyZero(dotProd - SK_Scalar1)) { bool ccw = !is_clockwise(v0, v1); int offset = ccw ? 1 : 0; idx0 = vCount-2+offset; idx1 = vCount+offset; const SkPoint* pt0 = &(lastPt[offset]); const SkPoint* pt1 = ccw ? &l2a : &l1a; switch(join) { case SkPaint::kMiter_Join: { // *Note : Logic is from MiterJoiner // FIXME : Special case if we have a right angle ? // if (SkScalarNearlyZero(dotProd)) {...} SkScalar sinHalfAngleSq = SkScalarHalf(SK_Scalar1 + dotProd); if (sinHalfAngleSq >= invMiterLimitSq) { // Find the miter point (or points if it is further // than the miter limit) const SkPoint pt2 = *pt0+v0, pt3 = *pt1+v1; if (intersection(*pt0, pt2, *pt1, pt3, miterPt[0]) != kNone_IntersectionType) { SkPoint miterPt0 = miterPt[0] - *pt0; SkPoint miterPt1 = miterPt[0] - *pt1; SkScalar sqDist0 = miterPt0.dot(miterPt0); SkScalar sqDist1 = miterPt1.dot(miterPt1); const SkScalar rSq = SkScalarDiv(SkScalarMul(radius, radius), sinHalfAngleSq); const SkScalar sqRLimit = SkScalarMul(sqMiterLimit, rSq); if (sqDist0 > sqRLimit || sqDist1 > sqRLimit) { if (sqDist1 > sqRLimit) { v1.setLength(SkScalarSqrt(sqRLimit)); miterPt[1] = *pt1+v1; } else { miterPt[1] = miterPt[0]; } if (sqDist0 > sqRLimit) { v0.setLength(SkScalarSqrt(sqRLimit)); miterPt[0] = *pt0+v0; } } else { miterPt[1] = miterPt[0]; } useMiterPoint = true; } } if (useMiterPoint && (miterPt[1] == miterPt[0])) { break; } } default: case SkPaint::kBevel_Join: { // Note : This currently causes some overdraw where both // lines initially intersect. We'd need to add // another line intersection check here if the // overdraw becomes an issue instead of using the // current point directly. // Add center point *verts++ = pts[0]; // Use current point directly // This idx is passed the current point so increment it ++idx1; // Add center triangle *idxs++ = idx0; *idxs++ = vCount; *idxs++ = idx1; vCount++; iCount += 3; } break; } } } } *verts++ = l1a; *verts++ = l2a; lastPt = verts; *verts++ = l1b; *verts++ = l2b; if (useMiterPoint && (idx0 >= 0) && (idx1 >= 0)) { firstPt[idx0] = miterPt[0]; firstPt[idx1] = miterPt[1]; } // 1st triangle *idxs++ = vCount+0; *idxs++ = vCount+2; *idxs++ = vCount+1; // 2nd triangle *idxs++ = vCount+1; *idxs++ = vCount+2; *idxs++ = vCount+3; vCount += 4; iCount += 6; } } break; case SkPath::kQuad_Verb: case SkPath::kCubic_Verb: SkDEBUGFAIL("Curves not supported!"); default: // Unhandled cases SkASSERT(false); } } if (isOpen) { // Add caps switch (stroke.getCap()) { case SkPaint::kSquare_Cap: firstPt[0] -= firstDir; firstPt[1] -= firstDir; lastPt [0] += dir; lastPt [1] += dir; break; case SkPaint::kRound_Cap: SkDEBUGFAIL("Round caps not supported!"); default: // No cap break; } } SkASSERT(vCount <= maxVertexCount); SkASSERT(iCount <= maxIndexCount); if (vCount > 0) { target->drawIndexed(kTriangles_GrPrimitiveType, 0, // start vertex 0, // start index vCount, iCount); } return true; }
void GrAARectRenderer::StrokeAARect(GrDrawTarget* target, const GrPipelineBuilder& pipelineBuilder, GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, const SkRect& devRect, const SkStrokeRec& stroke) { SkVector devStrokeSize; SkScalar width = stroke.getWidth(); if (width > 0) { devStrokeSize.set(width, width); viewMatrix.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); 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) { FillAARect(target, pipelineBuilder, color, viewMatrix, devOutside, 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); } GeometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside, devOutsideAssist, devInside, miterStroke); }