Android13 Canvas drawRoundRect流程分析

Canvas的drawRoundRect用于绘制指定的圆角矩形,代码如下:

//frameworks/base/graphics/java/android/graphic/Canvas.java
public class Canvas extends BaseCanvas {
    public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
        super.drawRoundRect(rect, rx, ry, paint);
    }
}

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

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

drawRoundRect

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

//framework/base/libs/hwui/jni/android_graphics_Canvas.cpp
static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
                          jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *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 drawRoundRect

SkiaCanvas继承于Canvas,因此调用SkiaCanvas的drawRoundRect方法:

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

SkCanvas drawRoundRect

调用mCanvas(SkCanvas)的drawRoundRect方法:

//external/skia/include/core/SkCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
                             const SkPaint& paint) {
    if (rx > 0 && ry > 0) {
        SkRRect rrect;
        rrect.setRectXY(r, rx, ry);
        this->drawRRect(rrect, paint);
    } else {
        this->drawRect(r, paint);
    }
}
}

根据条件调用SkCanvas的drawRRect或drawRect方法,下面分别进行分析:

SkCanvas drawRRect

调用SkCanvas的drawRRect方法:

//external/skia/include/core/SkCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    this->onDrawRRect(rrect, paint);
}
}

调用SkCanvas的onDrawRRect方法:

//external/skia/include/core/SkCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
    const SkRect& bounds = rrect.getBounds();


    // Delegating to simpler draw operations
    if (rrect.isRect()) {
        // call the non-virtual version
        this->SkCanvas::drawRect(bounds, paint);
        return;
    } else if (rrect.isOval()) {
        // call the non-virtual version
        this->SkCanvas::drawOval(bounds, paint);
        return;
    }


    if (this->internalQuickReject(bounds, paint)) {
        return;
    }


    auto layer = this->aboutToDraw(this, paint, &bounds);
    if (layer) {
        this->topDevice()->drawRRect(rrect, layer->paint());
    }
}
}

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

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

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

//external/skia/src/core/SkBitmapDevice.cpp
class SkBitmapDevice : public SkBaseDevice {
void SkBitmapDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
#ifdef SK_IGNORE_BLURRED_RRECT_OPT
    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
    // required to override drawRRect.
    this->drawPath(SkPath::RRect(rrect), paint, true);
#else
    LOOP_TILER( drawRRect(rrect, paint), Bounder(rrect.getBounds(), paint))
#endif
}
}
SkDraw drawRRect

调用SkDraw的drawRRect方法:

//external/skia/src/core/SkDraw.cpp
class SkDraw : public SkGlyphRunListPainter::BitmapDevicePainter {
void SkDraw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const {
    SkDEBUGCODE(this->validate());


    if (fRC->isEmpty()) {
        return;
    }


    SkMatrix ctm = fMatrixProvider->localToDevice();
    {
        // TODO: Investigate optimizing these options. They are in the same
        // order as SkDraw::drawPath, which handles each case. It may be
        // that there is no way to optimize for these using the SkRRect path.
        SkScalar coverage;
        if (SkDrawTreatAsHairline(paint, ctm, &coverage)) {
            goto DRAW_PATH;
        }


        if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
            goto DRAW_PATH;
        }
    }


    if (paint.getMaskFilter()) {
        // Transform the rrect into device space.
        SkRRect devRRect;
        if (rrect.transform(ctm, &devRRect)) {
            SkAutoBlitterChoose blitter(*this, nullptr, paint);
            if (as_MFB(paint.getMaskFilter())->filterRRect(devRRect, ctm, *fRC, blitter.get())) {
                return;  // filterRRect() called the blitter, so we're done
            }
        }
    }


DRAW_PATH:
    // Now fall back to the default case of using a path.
    SkPath path;
    path.addRRect(rrect);
    this->drawPath(path, paint, nullptr, true);
}
}
SkCanvas drawRect

调用SkCanvas的drawRect方法:

//external/skia/include/core/SkCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::drawRect(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->onDrawRect(r.makeSorted(), paint);
}
}

调用SkCanvas的onDrawRect方法:

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


    auto layer = this->aboutToDraw(this, paint, &r, CheckForOverwrite::kYes);
    if (layer) {
        this->topDevice()->drawRect(r, layer->paint());
    }
}
}

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

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

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

//external/skia/src/core/SkBitmapDevice.cpp
class SkBitmapDevice : public SkBaseDevice {
void SkBitmapDevice::drawRect(const SkRect& r, const SkPaint& paint) {
    LOOP_TILER( drawRect(r, paint), Bounder(r, paint))
}
}
SkDraw drawRect

调用SkDraw的drawRect方法:

//external/skia/src/core/SkDraw.cpp
class SkDraw : public SkGlyphRunListPainter::BitmapDevicePainter {
void SkDraw::drawRect(const SkRect& prePaintRect, const SkPaint& paint,
                      const SkMatrix* paintMatrix, const SkRect* postPaintRect) const {
    SkDEBUGCODE(this->validate();)


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


    const SkMatrixProvider* matrixProvider = fMatrixProvider;
    SkTLazy<SkPreConcatMatrixProvider> preConcatMatrixProvider;
    if (paintMatrix) {
        SkASSERT(postPaintRect);
        matrixProvider = preConcatMatrixProvider.init(*matrixProvider, *paintMatrix);
    } else {
        SkASSERT(!postPaintRect);
    }


    SkMatrix ctm = fMatrixProvider->localToDevice();
    SkPoint strokeSize;
    RectType rtype = ComputeRectType(prePaintRect, paint, ctm, &strokeSize);


    if (kPath_RectType == rtype) {
        draw_rect_as_path(*this, prePaintRect, paint, matrixProvider);
        return;
    }


    SkRect devRect;
    const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect;
    // skip the paintMatrix when transforming the rect by the CTM
    ctm.mapPoints(rect_points(devRect), rect_points(paintRect), 2);
    devRect.sort();


    // look for the quick exit, before we build a blitter
    SkRect bbox = devRect;
    if (paint.getStyle() != SkPaint::kFill_Style) {
        // extra space for hairlines
        if (paint.getStrokeWidth() == 0) {
            bbox.outset(1, 1);
        } else {
            // For kStroke_RectType, strokeSize is already computed.
            const SkPoint& ssize = (kStroke_RectType == rtype)
                ? strokeSize
                : compute_stroke_size(paint, ctm);
            bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
        }
    }
    if (SkPathPriv::TooBigForMath(bbox)) {
        return;
    }


    if (!SkRectPriv::FitsInFixed(bbox) && rtype != kHair_RectType) {
        draw_rect_as_path(*this, prePaintRect, paint, matrixProvider);
        return;
    }


    SkIRect ir = bbox.roundOut();
    if (fRC->quickReject(ir)) {
        return;
    }


    SkAutoBlitterChoose blitterStorage(*this, matrixProvider, paint);
    const SkRasterClip& clip = *fRC;
    SkBlitter*          blitter = blitterStorage.get();


    // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
    // case we are also hairline (if we've gotten to here), which devolves to
    // effectively just kFill
    switch (rtype) {
        case kFill_RectType:
            if (paint.isAntiAlias()) {
                SkScan::AntiFillRect(devRect, clip, blitter);
            } else {
                SkScan::FillRect(devRect, clip, blitter);
            }
            break;
        case kStroke_RectType:
            if (paint.isAntiAlias()) {
                SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter);
            } else {
                SkScan::FrameRect(devRect, strokeSize, clip, blitter);
            }
            break;
        case kHair_RectType:
            if (paint.isAntiAlias()) {
                SkScan::AntiHairRect(devRect, clip, blitter);
            } else {
                SkScan::HairRect(devRect, clip, blitter);
            }
            break;
        default:
            SkDEBUGFAIL("bad rtype");
    }
}
}

调用SkScan的FillIRect方法:

//external/skia/src/core/SkScan.cpp
class SkScan {
void SkScan::FillIRect(const SkIRect& r, const SkRegion* clip,
                       SkBlitter* blitter) {
    if (!r.isEmpty()) {
        if (clip) {
            if (clip->isRect()) {
                const SkIRect& clipBounds = clip->getBounds();


                if (clipBounds.contains(r)) {
                    blitrect(blitter, r);
                } else {
                    SkIRect rr = r;
                    if (rr.intersect(clipBounds)) {
                        blitrect(blitter, rr);
                    }
                }
            } else {
                SkRegion::Cliperator    cliper(*clip, r);
                const SkIRect&          rr = cliper.rect();


                while (!cliper.done()) {
                    blitrect(blitter, rr);
                    cliper.next();
                }
            }
        } else {
            blitrect(blitter, r);
        }
    }
}
}

调用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);
    }
}
}
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值