Android Canvas drawOval流程分析

Canvas的drawOval方法,画椭圆,参数一是扫描区域,参数二为paint对象:

//frameworks/base/graphics/java/android/graphic/Canvas.java
public class Canvas extends BaseCanvas {
    public void drawOval(@NonNull RectF oval, @NonNull Paint paint) {
        super.drawOval(oval, paint);
    }
}

调用Canvas的父类BaseCanvas的drawOval方法:

//frameworks/base/graphics/java/android/graphic/BaseCanvas.java
public abstract class BaseCanvas {}
    public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
    }
}

drawOval

调用nDrawOval方法,nDrawOval方法是Native方法,在android_graphics_Canvas.cpp中实现,下面分别进行分析:

//framework/base/libs/hwui/jni/android_graphics_Canvas.cpp
static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
                     jfloat right, jfloat bottom, jlong paintHandle) {
    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
}

调用get_canvas取得Canvas:

//framework/base/libs/hwui/jni/android_graphics_Canvas.cpp
static Canvas* get_canvas(jlong canvasHandle) {
    return reinterpret_cast<Canvas*>(canvasHandle);
}

SkiaCanvas drawOval

SkiaCanvas继承于Canvas,因此调用SkiaCanvas的drawOval方法,SkiaCanvas的drawOval方法可以用于在画布上绘制椭圆形

//frameworks/base/libs/hwui/SkiaCanvas.cpp
class SkiaCanvas : public Canvas {
SkCanvas* mCanvas; 
void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const Paint& paint) {
    if (CC_UNLIKELY(paint.nothingToDraw())) return;
    SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
    applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawOval(oval, p); });
}

SkCanvas drawOval

调用mCanvas(SkCanvas)的drawOval方法:

//external/skia/include/core/SkCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    // To avoid redundant logic in our culling code and various backends, we always sort rects
    // before passing them along.
    this->onDrawOval(r.makeSorted(), paint);
}
}

调用SkCanvas的onDrawOval方法:

//external/skia/include/core/SkCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
    SkASSERT(oval.isSorted());
    if (this->internalQuickReject(oval, paint)) {
        return;
    }


    auto layer = this->aboutToDraw(this, paint, &oval);
    if (layer) {
        this->topDevice()->drawOval(oval, layer->paint());
    }
}
}

调用SkCanvas的topDevice方法,返回SkBaseDevice对象:

//external/skia/include/core/SkCanvas.cpp
SkBaseDevice* SkCanvas::topDevice() const {
    SkASSERT(fMCRec->fDevice);
    return fMCRec->fDevice;
}
SkBitmapDevice drawOval

调用SkBaseDevice的drawOval,由SkBaseDevice的子类SkBitmapDevice实现drawOval方法:

//external/skia/src/core/SkBitmapDevice.cpp
class SkBitmapDevice : public SkBaseDevice {
void SkBitmapDevice::drawOval(const SkRect& oval, const SkPaint& paint) {
    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
    // required to override drawOval.
    this->drawPath(SkPath::Oval(oval), paint, true);
}
}
SkPath Oval

调用SkPath的Oval方法返回一个SkPath:

//external/skia/src/core/SkPath.cpp
class SK_API SkPath {
SkPath SkPath::Oval(const SkRect& r, SkPathDirection dir, unsigned startIndex) {
    return SkPathBuilder().addOval(r, dir, startIndex).detach();
}

调用SkPathBuilder的addOval方法:

//external/skia/src/core/SkPathBuilder.cpp
class SK_API SkPathBuilder {
SkPathBuilder& SkPathBuilder::addOval(const SkRect& oval, SkPathDirection dir, unsigned index) {
    const IsA prevIsA = fIsA;


    const int kPts   = 9;   // moveTo + 4 conics(2 pts each)
    const int kVerbs = 6;   // moveTo + 4 conics + close
    this->incReserve(kPts, kVerbs);


    OvalPointIterator ovalIter(oval, dir, index);
    RectPointIterator rectIter(oval, dir, index + (dir == SkPathDirection::kCW ? 0 : 1));


    // The corner iterator pts are tracking "behind" the oval/radii pts.


    this->moveTo(ovalIter.current());
    for (unsigned i = 0; i < 4; ++i) {
        this->conicTo(rectIter.next(), ovalIter.next(), SK_ScalarRoot2Over2);
    }
    this->close();


    if (prevIsA == kIsA_JustMoves) {
        fIsA      = kIsA_Oval;
        fIsACCW   = (dir == SkPathDirection::kCCW);
        fIsAStart = index % 4;
    }
    return *this;
}
}
SkBitmapDevice drawPath

然后调用SkBitmapDevice的drawPath方法,描绘这个Path:

//external/skia/src/core/SkBitmapDevice.cpp
class SkBitmapDevice : public SkBaseDevice {
void SkBitmapDevice::drawPath(const SkPath& path,
                              const SkPaint& paint,
                              bool pathIsMutable) {
    const SkRect* bounds = nullptr;
    if (SkDrawTiler::NeedsTiling(this) && !path.isInverseFillType()) {
        bounds = &path.getBounds();
    }
    SkDrawTiler tiler(this, bounds ? Bounder(*bounds, paint).bounds() : nullptr);
    if (tiler.needsTiling()) {
        pathIsMutable = false;
    }
    while (const SkDraw* draw = tiler.next()) {
        draw->drawPath(path, paint, nullptr, pathIsMutable);
    }
}
}
SkDraw drawPath

调用SkDraw的drawPath方法:

//external/skia/src/core/SkDraw.cpp
class SkDraw : public SkGlyphRunListPainter::BitmapDevicePainter {
void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
                      const SkMatrix* prePathMatrix, bool pathIsMutable,
                      bool drawCoverage, SkBlitter* customBlitter) const {
    SkDEBUGCODE(this->validate();)


    // nothing to draw
    if (fRC->isEmpty()) {
        return;
    }


    SkPath*         pathPtr = (SkPath*)&origSrcPath;
    bool            doFill = true;
    SkPath          tmpPathStorage;
    SkPath*         tmpPath = &tmpPathStorage;
    const SkMatrixProvider*            matrixProvider = fMatrixProvider;
    SkTLazy<SkPreConcatMatrixProvider> preConcatMatrixProvider;
    tmpPath->setIsVolatile(true);


    if (prePathMatrix) {
        if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style) {
            SkPath* result = pathPtr;


            if (!pathIsMutable) {
                result = tmpPath;
                pathIsMutable = true;
            }
            pathPtr->transform(*prePathMatrix, result);
            pathPtr = result;
        } else {
            matrixProvider = preConcatMatrixProvider.init(*matrixProvider, *prePathMatrix);
        }
    }


    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);


    {
        SkScalar coverage;
        if (SkDrawTreatAsHairline(origPaint, matrixProvider->localToDevice(), &coverage)) {
            const auto bm = origPaint.asBlendMode();
            if (SK_Scalar1 == coverage) {
                paint.writable()->setStrokeWidth(0);
            } else if (bm && SkBlendMode_SupportsCoverageAsAlpha(bm.value())) {
                U8CPU newAlpha;
#if 0
                newAlpha = SkToU8(SkScalarRoundToInt(coverage *
                                                     origPaint.getAlpha()));
#else
                // this is the old technique, which we preserve for now so
                // we don't change previous results (testing)
                // the new way seems fine, its just (a tiny bit) different
                int scale = (int)(coverage * 256);
                newAlpha = origPaint.getAlpha() * scale >> 8;
#endif
                SkPaint* writablePaint = paint.writable();
                writablePaint->setStrokeWidth(0);
                writablePaint->setAlpha(newAlpha);
            }
        }
    }


    if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
        SkRect cullRect;
        const SkRect* cullRectPtr = nullptr;
        if (this->computeConservativeLocalClipBounds(&cullRect)) {
            cullRectPtr = &cullRect;
        }
        doFill = paint->getFillPath(*pathPtr, tmpPath, cullRectPtr,
                                    fMatrixProvider->localToDevice());
        pathPtr = tmpPath;
    }


    // avoid possibly allocating a new path in transform if we can
    SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath;


    // transform the path into device space
    pathPtr->transform(matrixProvider->localToDevice(), devPathPtr);


#if defined(SK_BUILD_FOR_FUZZER)
    if (devPathPtr->countPoints() > 1000) {
        return;
    }
#endif


    this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
}
}

调用SkDraw的drawDevPath方法:

//external/skia/src/core/SkDraw.cpp
class SkDraw : public SkGlyphRunListPainter::BitmapDevicePainter {
void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage,
                         SkBlitter* customBlitter, bool doFill) const {
    if (SkPathPriv::TooBigForMath(devPath)) {
        return;
    }
    SkBlitter* blitter = nullptr;
    SkAutoBlitterChoose blitterStorage;
    if (nullptr == customBlitter) {
        blitter = blitterStorage.choose(*this, nullptr, paint, drawCoverage);
    } else {
        blitter = customBlitter;
    }


    if (paint.getMaskFilter()) {
        SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle
                                              : SkStrokeRec::kHairline_InitStyle;
        if (as_MFB(paint.getMaskFilter())
                    ->filterPath(devPath, fMatrixProvider->localToDevice(), *fRC, blitter, style)) {
            return;  // filterPath() called the blitter, so we're done
        }
    }


    void (*proc)(const SkPath&, const SkRasterClip&, SkBlitter*);
    if (doFill) {
        if (paint.isAntiAlias()) {
            proc = SkScan::AntiFillPath;
        } else {
            proc = SkScan::FillPath;
        }
    } else {    // hairline
        if (paint.isAntiAlias()) {
            switch (paint.getStrokeCap()) {
                case SkPaint::kButt_Cap:
                    proc = SkScan::AntiHairPath;
                    break;
                case SkPaint::kSquare_Cap:
                    proc = SkScan::AntiHairSquarePath;
                    break;
                case SkPaint::kRound_Cap:
                    proc = SkScan::AntiHairRoundPath;
                    break;
            }
        } else {
            switch (paint.getStrokeCap()) {
                case SkPaint::kButt_Cap:
                    proc = SkScan::HairPath;
                    break;
                case SkPaint::kSquare_Cap:
                    proc = SkScan::HairSquarePath;
                    break;
                case SkPaint::kRound_Cap:
                    proc = SkScan::HairRoundPath;
                    break;
            }
        }
    }


    proc(devPath, *fRC, blitter);
}
}

根据不同的case设置不同的proc,然后运行这个proc,我们继续分析SkScan::FillPath处理:

//external/skia/src/core/SkScan.cpp
class SkScan {
void SkScan::FillPath(const SkPath& path, const SkRegion& origClip,
                      SkBlitter* blitter) {
    if (origClip.isEmpty()) {
        return;
    }


    // Our edges are fixed-point, and don't like the bounds of the clip to
    // exceed that. Here we trim the clip just so we don't overflow later on
    const SkRegion* clipPtr = &origClip;
    SkRegion finiteClip;
    if (clip_to_limit(origClip, &finiteClip)) {
        if (finiteClip.isEmpty()) {
            return;
        }
        clipPtr = &finiteClip;
    }
    // don't reference "origClip" any more, just use clipPtr




    SkRect bounds = path.getBounds();
    bool irPreClipped = false;
    if (!SkRectPriv::MakeLargeS32().contains(bounds)) {
        if (!bounds.intersect(SkRectPriv::MakeLargeS32())) {
            bounds.setEmpty();
        }
        irPreClipped = true;
    }


    SkIRect ir = conservative_round_to_int(bounds);
    if (ir.isEmpty()) {
        if (path.isInverseFillType()) {
            blitter->blitRegion(*clipPtr);
        }
        return;
    }


    SkScanClipper clipper(blitter, clipPtr, ir, path.isInverseFillType(), irPreClipped);


    blitter = clipper.getBlitter();
    if (blitter) {
        // we have to keep our calls to blitter in sorted order, so we
        // must blit the above section first, then the middle, then the bottom.
        if (path.isInverseFillType()) {
            sk_blit_above(blitter, ir, *clipPtr);
        }
        SkASSERT(clipper.getClipRect() == nullptr ||
                *clipper.getClipRect() == clipPtr->getBounds());
        sk_fill_path(path, clipPtr->getBounds(), blitter, ir.fTop, ir.fBottom,
                     0, clipper.getClipRect() == nullptr);
        if (path.isInverseFillType()) {
            sk_blit_below(blitter, ir, *clipPtr);
        }
    } else {
        // what does it mean to not have a blitter if path.isInverseFillType???
    }
}
}

调用sk_fill_path方法:

//external/skia/src/core/SkScan_Path.cpp
void sk_fill_path(const SkPath& path, const SkIRect& clipRect, SkBlitter* blitter,
                  int start_y, int stop_y, int shiftEdgesUp, bool pathContainedInClip) {
    SkASSERT(blitter);


    SkIRect shiftedClip = clipRect;
    shiftedClip.fLeft = SkLeftShift(shiftedClip.fLeft, shiftEdgesUp);
    shiftedClip.fRight = SkLeftShift(shiftedClip.fRight, shiftEdgesUp);
    shiftedClip.fTop = SkLeftShift(shiftedClip.fTop, shiftEdgesUp);
    shiftedClip.fBottom = SkLeftShift(shiftedClip.fBottom, shiftEdgesUp);


    SkBasicEdgeBuilder builder(shiftEdgesUp);
    int count = builder.buildEdges(path, pathContainedInClip ? nullptr : &shiftedClip);
    SkEdge** list = builder.edgeList();


    if (0 == count) {
        if (path.isInverseFillType()) {
            /*
             *  Since we are in inverse-fill, our caller has already drawn above
             *  our top (start_y) and will draw below our bottom (stop_y). Thus
             *  we need to restrict our drawing to the intersection of the clip
             *  and those two limits.
             */
            SkIRect rect = clipRect;
            if (rect.fTop < start_y) {
                rect.fTop = start_y;
            }
            if (rect.fBottom > stop_y) {
                rect.fBottom = stop_y;
            }
            if (!rect.isEmpty()) {
                blitter->blitRect(rect.fLeft << shiftEdgesUp,
                                  rect.fTop << shiftEdgesUp,
                                  rect.width() << shiftEdgesUp,
                                  rect.height() << shiftEdgesUp);
            }
        }
        return;
    }


    SkEdge headEdge, tailEdge, *last;
    // this returns the first and last edge after they're sorted into a dlink list
    SkEdge* edge = sort_edges(list, count, &last);


    headEdge.fPrev = nullptr;
    headEdge.fNext = edge;
    headEdge.fFirstY = kEDGE_HEAD_Y;
    headEdge.fX = SK_MinS32;
    edge->fPrev = &headEdge;


    tailEdge.fPrev = last;
    tailEdge.fNext = nullptr;
    tailEdge.fFirstY = kEDGE_TAIL_Y;
    last->fNext = &tailEdge;


    // now edge is the head of the sorted linklist


    start_y = SkLeftShift(start_y, shiftEdgesUp);
    stop_y = SkLeftShift(stop_y, shiftEdgesUp);
    if (!pathContainedInClip && start_y < shiftedClip.fTop) {
        start_y = shiftedClip.fTop;
    }
    if (!pathContainedInClip && stop_y > shiftedClip.fBottom) {
        stop_y = shiftedClip.fBottom;
    }


    InverseBlitter  ib;
    PrePostProc     proc = nullptr;


    if (path.isInverseFillType()) {
        ib.setBlitter(blitter, clipRect, shiftEdgesUp);
        blitter = &ib;
        proc = PrePostInverseBlitterProc;
    }


    // count >= 2 is required as the convex walker does not handle missing right edges
    if (path.isConvex() && (nullptr == proc) && count >= 2) {
        walk_simple_edges(&headEdge, blitter, start_y, stop_y);
    } else {
        walk_edges(&headEdge, path.getFillType(), blitter, start_y, stop_y, proc,
                   shiftedClip.right());
    }
}

调用SkBlitter的blitRect方法:

//external/skia/src/core/SkBlitter.cpp
class SkBlitter {
void SkBlitter::blitRect(int x, int y, int width, int height) {
    SkASSERT(width > 0);
    while (--height >= 0) {
        this->blitH(x, y++, width);
    }
}
}
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值