Canvas的drawPath方法用于绘制一个路径,参数一为Path路径对象,代码如下:
//frameworks/base/graphics/java/android/graphic/Canvas.java
public class Canvas extends BaseCanvas {
public void drawPath(@NonNull Path path, @NonNull Paint paint) {
super.drawPath(path, paint);
}
}
调用Canvas的父类BaseCanvas的drawPath方法:
//frameworks/base/graphics/java/android/graphic/BaseCanvas.java
public abstract class BaseCanvas {}
public void drawPath(@NonNull Path path, @NonNull Paint paint) {
throwIfHasHwFeaturesInSwMode(paint);
if (path.isSimplePath && path.rects != null) {
nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
} else {
nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
}
}
}
上面方法根据情况调用如下方法:
1、nDrawRegion方法。
2、nDrawPath方法。
这个两个方法都是Native方法,在android_graphics_Canvas.cpp中实现,下面分别进行分析:
drawRegion
调用nDrawRegion方法,根据查表调用的是android_graphics_Canvas.cpp中的drawRegion方法:
//framework/base/libs/hwui/jni/android_graphics_Canvas.cpp
static void drawRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong regionHandle,
jlong paintHandle) {
const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
get_canvas(canvasHandle)->drawRegion(*region, *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 drawRegion
SkiaCanvas继承于Canvas,因此调用SkiaCanvas的drawRegion方法,SkiaCanvas的drawRegion方法用于在画布上绘制一个区域。具体来说,它会在指定的区域内绘制一个矩形,并使用给定的画笔进行填充或描边
//frameworks/base/libs/hwui/SkiaCanvas.cpp
class SkiaCanvas : public Canvas {
SkCanvas* mCanvas;
void SkiaCanvas::drawRegion(const SkRegion& region, const Paint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawRegion(region, p); });
}
}
SkCanvas drawRegion
调用mCanvas(SkCanvas)的drawRegion方法:
//external/skia/include/core/SkCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
TRACE_EVENT0("skia", TRACE_FUNC);
if (region.isEmpty()) {
return;
}
if (region.isRect()) {
return this->drawIRect(region.getBounds(), paint);
}
this->onDrawRegion(region, paint);
}
}
调用SkCanvas的onDrawRegion方法:
//external/skia/include/core/SkCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
const SkRect bounds = SkRect::Make(region.getBounds());
if (this->internalQuickReject(bounds, paint)) {
return;
}
auto layer = this->aboutToDraw(this, paint, &bounds);
if (layer) {
this->topDevice()->drawRegion(region, layer->paint());
}
}
}
调用SkCanvas的topDevice方法,返回SkBaseDevice对象:
//external/skia/include/core/SkCanvas.cpp
SkBaseDevice* SkCanvas::topDevice() const {
SkASSERT(fMCRec->fDevice);
return fMCRec->fDevice;
}
SkBaseDevice drawRegion
调用SkBaseDevice的drawRegion方法:
//external/skia/src/core/SkDevice.cpp
class SkBaseDevice : public SkRefCnt, public SkMatrixProvider {
void SkBaseDevice::drawRegion(const SkRegion& region, const SkPaint& paint) {
const SkMatrix& localToDevice = this->localToDevice();
bool isNonTranslate = localToDevice.getType() & ~(SkMatrix::kTranslate_Mask);
bool complexPaint = paint.getStyle() != SkPaint::kFill_Style || paint.getMaskFilter() ||
paint.getPathEffect();
bool antiAlias = paint.isAntiAlias() && (!is_int(localToDevice.getTranslateX()) ||
!is_int(localToDevice.getTranslateY()));
if (isNonTranslate || complexPaint || antiAlias) {
SkPath path;
region.getBoundaryPath(&path);
path.setIsVolatile(true);
return this->drawPath(path, paint, true);
}
SkRegion::Iterator it(region);
while (!it.done()) {
this->drawRect(SkRect::Make(it.rect()), paint);
it.next();
}
}
}
调用SkBaseDevice的drawPath方法,由SkBaseDevice的子类SkBitmapDevice实现这个方法:
//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);
}
}
SkScan FillPath
根据不同的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);
}
}
}
drawPath
调用nDrawRegion方法,根据查表调用的是android_graphics_Canvas.cpp中的drawRegion方法:
//framework/base/libs/hwui/jni/android_graphics_Canvas.cpp
static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
jlong paintHandle) {
const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
get_canvas(canvasHandle)->drawPath(*path, *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 drawPath
SkiaCanvas继承于Canvas,因此调用SkiaCanvas的drawPath方法,SkiaCanvas的drawPath方法用于绘制自定义路径,整体流程如下:
代码如下:
//frameworks/base/libs/hwui/SkiaCanvas.cpp
class SkiaCanvas : public Canvas {
SkCanvas* mCanvas;
void SkiaCanvas::drawPath(const SkPath& path, const Paint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) {
return;
}
applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawPath(path, p); });
}
}
SkCanvas drawPath
调用mCanvas(SkCanvas)的drawPath方法:
//external/skia/include/core/SkCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
TRACE_EVENT0("skia", TRACE_FUNC);
this->onDrawPath(path, paint);
}
}
调用SkCanvas的onDrawPath方法:
//external/skia/include/core/SkCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
if (!path.isFinite()) {
return;
}
const SkRect& pathBounds = path.getBounds();
if (!path.isInverseFillType() && this->internalQuickReject(pathBounds, paint)) {
return;
}
if (path.isInverseFillType() && pathBounds.width() <= 0 && pathBounds.height() <= 0) {
this->internalDrawPaint(paint);
return;
}
auto layer = this->aboutToDraw(this, paint, path.isInverseFillType() ? nullptr : &pathBounds);
if (layer) {
this->topDevice()->drawPath(path, layer->paint());
}
}
}
调用SkCanvas的topDevice方法,返回SkBaseDevice对象:
//external/skia/include/core/SkCanvas.cpp
SkBaseDevice* SkCanvas::topDevice() const {
SkASSERT(fMCRec->fDevice);
return fMCRec->fDevice;
}
SkBitmapDevice drawPath
调用SkBaseDevice的drawPath,由SkBaseDevice的子类SkBitmapDevice实现drawPath方法:
//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);
}
}
SkScan FillPath
根据不同的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);
}
}
}