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);
}
}
}