上一篇文章介绍了在Android结构中的各种Canvas,其间C层的RecordingCanas承上启下,在SkiaRecordingCanvas的制作办法会经过调用它的mRecorder来记载,而这个mRecorder的类型正好便是SkCanvas,准确的说是它的子类RecordingCanas。而各种制作办法会对应生成一个Op方针来描述这个制作操作,RecordingCanvas将这个op方针分配到它持有的DisplayListData的fBytes上,从而完成记载。 到这儿也仅仅是完成记载,离真实运用GPU依照Op描述数据烘托素还很远。这篇我们进入skia库来剖析SKCanvas对制作操作的处理。
SKCanvas有好几个结构办法,依据不同的场景能够生成根据不同制作方针的方针,制作的方针被笼统为SkBaseDevice,它有许多的子类,代表不同的制作方针,比方SkBitmapDevice,制作到一个SKBitmap上; SkNoPixelsDevice是一个虚拟的不会制作成像素点的设备,比方前面介绍的SKCanvas的子类RecordingCanvas便是运用的SkNoPixelsDevice,它就仅仅将制作命令记载到一个二进制数组,并不烘托像素;还有SkGpuDevice,这才是代表运用GPU进行像素烘托的方针设备。所以要完成像素烘托,必须要要在某个当地生成一个运用SkGpuDevice的SkCanvas的方针。
1 SkCanvas
下面是几个结构函数的界说:
这是外部传入Device的结构办法,会将device传入init办法进行初始化
SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
, fProps(device->surfaceProps())
{
inc_canvas();
this->init(device);
}
这是RecordingCanvas继承的结构办法,init传入null进行初始化,内部会生成一个SkNoPixelsDevice
SkCanvas::SkCanvas()
: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
, fProps()
{
inc_canvas();
this->init(nullptr);
}
这是运用SkBitmap作为制作方针的结构办法,它会生成一个SkBitmapDevice,用于制作
SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
, fProps(props)
{
inc_canvas();
sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
this->init(device);
}
所以看到,结构函数内部都会调用init办法来初始化,下面剖析一下这个init办法
void SkCanvas::init(sk_sp<SkBaseDevice> device) {
...
if (!device) {
device = sk_make_sp<SkNoPixelsDevice>(SkIRect::MakeEmpty(), fProps);
}
// From this point on, SkCanvas will always have a device
SkASSERT(device);
fSaveCount = 1;
fMCRec = new (fMCStack.push_back()) MCRec(device.get());
fMarkerStack = sk_make_sp<SkMarkerStack>();
// The root device and the canvas should always have the same pixel geometry
SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
device->setMarkerStack(fMarkerStack.get());
fSurfaceBase = nullptr;
fBaseDevice = std::move(device);
fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
fQuickRejectBounds = this->computeDeviceClipBounds();
}
假如device为空,则生成一个SkNoPixelsDevice方针以确保每个SKCanvas都有制作方针设备,然后将device保存到fBaseDevice
然后以device初始化一个MCRec,然后保存到fMCStack. MCRec的界说如下:看起来时记载一个Layer的制作,每个layer将对应一个fBackImage。而SkCanvas中fMCStack是一个栈,所以layer将以栈的方式来保存。每个layer都持有一个SkBaseDevice 和 一个BackImage。初始化时即默许包括一个layer,即便没有调用过saveLayer办法。所以fSaveCount也被设置为1。
class SkCanvas::MCRec {
public:
// If not null, this MCRec corresponds with the saveLayer() record that made the layer.
// The base "layer" is not stored here, since it is stored inline in SkCanvas and has no
// restoration behavior.
std::unique_ptr<Layer> fLayer;
// This points to the device of the top-most layer (which may be lower in the stack), or
// to the canvas's fBaseDevice. The MCRec does not own the device.
SkBaseDevice* fDevice;
std::unique_ptr<BackImage> fBackImage;
SkM44 fMatrix;
int fDeferredSaveCount;
MCRec(SkBaseDevice* device)
: fLayer(nullptr)
, fDevice(device)
, fBackImage(nullptr)
, fDeferredSaveCount(0) {
SkASSERT(fDevice);
fMatrix.setIdentity();
inc_rec();
...
}
2 drawRect
SkCanvas也有许多的对应制作办法,流程也差不多,最终,我们也来看看SkCanvas的制作矩形的办法drawRect
void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
...
this->onDrawRect(r.makeSorted(), paint);
}
持续调用onDrawRect办法
void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
SkASSERT(r.isSorted());
if (this->internalQuickReject(r, paint)) {
return;
}
AutoLayerForImageFilter layer(this, paint, &r, CheckForOverwrite::kYes);
this->topDevice()->drawRect(r, layer.paint());
}
SkBaseDevice* SkCanvas::topDevice() const {
SkASSERT(fMCRec->fDevice);
return fMCRec->fDevice;
}
它运用的时topDevice,便是最上面一个layer对应的device。由于我们的想要看一下如何进行像素烘托的,因而看一下SkGpuDevice的状况
external/skia/src/gpu/SkGpuDevice.cpp
void SkGpuDevice::drawRect(const SkRect& rect, const SkPaint& paint) {
...
fSurfaceDrawContext->drawRect(this->clip(), std::move(grPaint),
fSurfaceDrawContext->chooseAA(paint), this->localToDevice(), rect,
&style);
}
在SkGpuDevice中由会去调用fSurfaceDrawContext的drawRect办法。它的类型是GrSurfaceDrawContext
external/skia/src/gpu/SkGpuDevice.h
private:
std::unique_ptr<GrSurfaceDrawContext> fSurfaceDrawContext;
它是在结构的时分从外部传入的
external/skia/src/gpu/SkGpuDevice.cpp
SkGpuDevice::SkGpuDevice(std::unique_ptr<GrSurfaceDrawContext> surfaceDrawContext, unsigned flags)
: INHERITED(make_info(surfaceDrawContext.get(), SkToBool(flags & kIsOpaque_Flag)), surfaceDrawContext->surfaceProps())
, fContext(sk_ref_sp(surfaceDrawContext->recordingContext()))
, fSurfaceDrawContext(std::move(surfaceDrawContext))
...
}
我们直接去看一下GrSurfaceDrawContext的drawRect办法, 且仅仅看看Fill的状况
void GrSurfaceDrawContext::drawRect(const GrClip* clip,
GrPaint&& paint,
GrAA aa,
const SkMatrix& viewMatrix,
const SkRect& rect,
const GrStyle* style) {
...
AutoCheckFlush acf(this->drawingManager());
const SkStrokeRec& stroke = style->strokeRec();
if (stroke.getStyle() == SkStrokeRec::kFill_Style) {
// Fills the rect, using rect as its own local coordinates
this->fillRectToRect(clip, std::move(paint), aa, viewMatrix, rect, rect);
return;
}
...
}
持续调用fillRectToRect
void GrSurfaceDrawContext::fillRectToRect(const GrClip* clip,
GrPaint&& paint,
GrAA aa,
const SkMatrix& viewMatrix,
const SkRect& rectToDraw,
const SkRect& localRect) {
DrawQuad quad{GrQuad::MakeFromRect(rectToDraw, viewMatrix), GrQuad(localRect),
aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone};
...
this->drawFilledQuad(clip, std::move(paint), aa, &quad);
}
持续调用drawFilledQuad
void GrSurfaceDrawContext::drawFilledQuad(const GrClip* clip,
GrPaint&& paint,
GrAA aa,
DrawQuad* quad,
const GrUserStencilSettings* ss) {
...
this->addDrawOp(finalClip, GrFillRectOp::Make(fContext, std::move(paint), aaType,
quad, ss));
}
}
持续调用addDrawOp
void GrSurfaceDrawContext::addDrawOp(const GrClip* clip,
GrOp::Owner op,
const std::function<WillAddOpFn>& willAddFn) {
GrDrawOp* drawOp = (GrDrawOp*)op.get();
...
auto opsTask = this->getOpsTask();
...
opsTask->addDrawOp(this->drawingManager(), std::move(op), fixedFunctionFlags, analysis,
std::move(appliedClip), dstProxyView,
GrTextureResolveManager(this->drawingManager()), *this->caps());
...
}
这儿的GrDrawOp 是一个GrFillRectOp方针,表明制作填充矩形,这和Android中的Op是差不多的概念,仅仅是描述方针。最终将这个描述方针添加到了opsTask。 getOpsTask是界说在GrSurfaceDrawContext的父类GrSurfaceFillContext中的办法
GrOpsTask* GrSurfaceFillContext::getOpsTask() {
if (!fOpsTask || fOpsTask->isClosed()) {
sk_sp<GrOpsTask> newOpsTask = this->drawingManager()->newOpsTask(
this->writeSurfaceView(), this->arenas(), fFlushTimeOpsTask);
this->willReplaceOpsTask(fOpsTask.get(), newOpsTask.get());
fOpsTask = std::move(newOpsTask);
}
SkASSERT(!fOpsTask->isClosed());
return fOpsTask.get();
}
获得了一个GrOpsTask之后,调用addDrawOp办法
external/skia/src/gpu/GrOpsTask.cpp
void GrOpsTask::addDrawOp(GrDrawingManager* drawingMgr, GrOp::Owner op,
GrDrawOp::FixedFunctionFlags fixedFunctionFlags,
const GrProcessorSet::Analysis& processorAnalysis, GrAppliedClip&& clip,
const DstProxyView& dstProxyView,
GrTextureResolveManager textureResolveManager, const GrCaps& caps) {
...
this->recordOp(std::move(op), processorAnalysis, clip.doesClip() ? &clip : nullptr,
&dstProxyView, caps);
}
持续调用recordOp办法
void GrOpsTask::recordOp(
GrOp::Owner op, GrProcessorSet::Analysis processorAnalysis, GrAppliedClip* clip,
const DstProxyView* dstProxyView, const GrCaps& caps) {
...
GrSurfaceProxy* proxy = this->target(0);
...
fOpChains.emplace_back(std::move(op), processorAnalysis, clip, dstProxyView);
}
fOpChains是一个OpChain的调集,因而最终recordOp是生成了一个OpChain方针,并放入到fOpChains中。
external/skia/src/gpu/GrOpsTask.h
SkSTArray<25, OpChain> fOpChains;
3 总结
本文接着上一篇文章,持续剖析了skia层的SkCanvas, 它能够接受多种制作方针设备,比方它的子类RecordingCanvas运用的是SkNoPixelsDevice,因而只能记载而不能烘托成像素;需要烘托成像素需要运用比方SkGpuDevice。SkCanvas除了device这个重要特点外,还有一个fMCStack用于保存制作Layer,并且默许会创建一个Layer,制作时,制作办法都是作用于栈顶的Layer。接着剖析了典型的制作办法drawRect,它穿越了多个类,最终生成一个OpChain方针保存GrOpsTask的fOpChains调集。因而到现在方位,SkCanvas仍然仅仅起到一个记载的作用,并未产生像素烘托。
关注大众号:Android老皮!!!欢迎大家来找我讨论交流