Android中Canvas的clipRect方法用于裁剪画布,将画布限制在指定的矩形区域内。裁剪后,只有在该区域内的绘制内容才会被显示出来,超出该区域的内容将被隐藏。裁剪画布的作用是可以控制绘制的范围,只绘制在指定区域内的内容,可以实现一些特殊的绘制效果,比如局部放大、镂空等,代码如下:
//frameworks/base/graphics/java/android/graphic/Canvas.java
public class Canvas extends BaseCanvas {
public boolean clipRect(@NonNull RectF rect, @NonNull Region.Op op) {
checkValidClipOp(op);
return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
op.nativeInt);
}
}
clipRect
调用nClipRect方法,nClipRect方法是Native方法,在android_graphics_Canvas.cpp中实现,下面分别进行分析
//framework/base/libs/hwui/jni/android_graphics_Canvas.cpp
static jboolean clipRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
jfloat r, jfloat b, jint opHandle) {
// The opHandle is defined in Canvas.java to be Region::Op
SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
bool nonEmptyClip;
switch (rgnOp) {
case SkRegion::Op::kIntersect_Op:
case SkRegion::Op::kDifference_Op:
// Intersect and difference are supported clip operations
nonEmptyClip =
get_canvas(canvasHandle)->clipRect(l, t, r, b, static_cast<SkClipOp>(rgnOp));
break;
case SkRegion::Op::kReplace_Op:
// Replace is emulated to support legacy apps older than P
nonEmptyClip = get_canvas(canvasHandle)->replaceClipRect_deprecated(l, t, r, b);
break;
default:
// All other operations would expand the clip and are no longer supported,
// so log and skip (to avoid breaking legacy apps).
ALOGW("Ignoring unsupported clip operation %d", opHandle);
SkRect clipBounds; // ignored
nonEmptyClip = get_canvas(canvasHandle)->getClipBounds(&clipBounds);
break;
}
return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
}
调用get_canvas取得Canvas:
//framework/base/libs/hwui/jni/android_graphics_Canvas.cpp
static Canvas* get_canvas(jlong canvasHandle) {
return reinterpret_cast<Canvas*>(canvasHandle);
}
SkiaCanvas clipRect
SkiaCanvas继承于Canvas,因此调用SkiaCanvas的clipRect方法,SkiaCanvas的clipRect方法用于在画布上创建一个矩形剪裁区域,只有在该区域内的内容才会被绘制出来
//frameworks/base/libs/hwui/SkiaCanvas.cpp
class SkiaCanvas : public Canvas {
SkCanvas* mCanvas;
bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkClipOp op) {
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
this->recordClip(rect, op);
mCanvas->clipRect(rect, op);
return !mCanvas->isClipEmpty();
}
}
SkCanvas clipRect
调用SkCanvas的clipRect方法:
//external/skia/include/core/SkCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
if (!rect.isFinite()) {
return;
}
this->checkForDeferredSave();
ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
this->onClipRect(rect.makeSorted(), op, edgeStyle);
}
}
调用SkCanvas的onClipRect方法:
//external/skia/include/core/SkCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
SkASSERT(rect.isSorted());
const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
AutoUpdateQRBounds aqr(this);
this->topDevice()->clipRect(rect, op, isAA);
}
}
调用SkCanvas的topDevice方法,返回SkBaseDevice对象:
//external/skia/include/core/SkCanvas.cpp
SkBaseDevice* SkCanvas::topDevice() const {
SkASSERT(fMCRec->fDevice);
return fMCRec->fDevice;
}
SkBitmapDevice clipRect
调用SkBaseDevice的drawPath,由SkBaseDevice的子类SkBitmapDevice实现drawRect方法:
//external/skia/src/core/SkBitmapDevice.cpp
class SkBitmapDevice : public SkBaseDevice {
void SkBitmapDevice::onClipRect(const SkRect& rect, SkClipOp op, bool aa) {
fRCStack.clipRect(this->localToDevice(), rect, op, aa);
}
}