安卓的RenderEffect可以给View添加一些渲染特效,例如模糊效果等。
RenderEffect定义:(frameworks/base/graphics/java/android/graphics/RenderEffect.java)
/**
* Intermediate rendering step used to render drawing commands with a corresponding
* visual effect. A {@link RenderEffect} can be configured on a {@link RenderNode} through
* {@link RenderNode#setRenderEffect(RenderEffect)} and will be applied when drawn through
* {@link Canvas#drawRenderNode(RenderNode)}.
* Additionally a {@link RenderEffect} can be applied to a View's backing RenderNode through
* {@link android.view.View#setRenderEffect(RenderEffect)}
*/
public final class RenderEffect {
private static class RenderEffectHolder {
public static final NativeAllocationRegistry RENDER_EFFECT_REGISTRY =
NativeAllocationRegistry.createMalloced(
RenderEffect.class.getClassLoader(), nativeGetFinalizer());
}
private final long mNativeRenderEffect;
/* only constructed from static factory methods */
private RenderEffect(long nativeRenderEffect) {
mNativeRenderEffect = nativeRenderEffect;
RenderEffectHolder.RENDER_EFFECT_REGISTRY.registerNativeAllocation(
this, mNativeRenderEffect);
}
/**
* Obtain the pointer to the underlying RenderEffect to be configured
* on a RenderNode object via {@link RenderNode#setRenderEffect(RenderEffect)}
*/
/* package */ long getNativeInstance() {
return mNativeRenderEffect;
}
}
RenderEffect只有一个成员变量mNativeRenderEffect,保存了一个C++层的指针。所以RenderEffect的实现原理基本在C++层。
接下来本文以给View添加模糊效果为例分析RenderEffect的原理。
给View添加模糊效果
安卓的RenderEffect可以给View添加模糊效果,添加方法:
RenderEffect renderEffect = RenderEffect.createBlurEffect(
radiusX, radiusY, Shader.TileMode.CLAMP);
view.setRenderEffect(renderEffect);
首先创建了RenderEffect对象,然后调用View.setRenderEffect传入刚才创建的RenderEffect对象,就完成了给View添加模糊效果。
创建RenderEffect
RenderEffect.createBlurEffect方法:(frameworks/base/graphics/java/android/graphics/RenderEffect.java)
/**
* Create a {@link RenderEffect} that blurs the contents of the
* {@link android.graphics.RenderNode} that this RenderEffect is installed on with the
* specified radius along the x and y axis.
* @param radiusX Radius of blur along the X axis
* @param radiusY Radius of blur along the Y axis
* @param edgeTreatment Policy for how to blur content near edges of the blur kernel
*/
@NonNull
public static RenderEffect createBlurEffect(
float radiusX,
float radiusY,
@NonNull TileMode edgeTreatment
) {
return new RenderEffect(
nativeCreateBlurEffect(
radiusX,
radiusY,
0,
edgeTreatment.nativeInt
)
);
}
主要是使用了nativeCreateBlurEffect方法创建C++对象:(frameworks/base/libs/hwui/jni/RenderEffect.cpp)
static jlong createBlurEffect(JNIEnv* env , jobject, jfloat radiusX,
jfloat radiusY, jlong inputFilterHandle, jint edgeTreatment) {
auto* inputImageFilter = reinterpret_cast<SkImageFilter*>(inputFilterHandle);
sk_sp<SkImageFilter> blurFilter =
SkImageFilters::Blur(
Blur::convertRadiusToSigma(radiusX),
Blur::convertRadiusToSigma(radiusY),
static_cast<SkTileMode>(edgeTreatment),
sk_ref_sp(inputImageFilter),
nullptr);
return reinterpret_cast<jlong>(blurFilter.release());
}
这里主要是调用SkImageFilters::Blur创建了一个SkBlurImageFilter对象:(external/skia/src/effects/imagefilters/SkBlurImageFilter.cpp)
sk_sp<SkImageFilter> SkImageFilters::Blur(
SkScalar sigmaX, SkScalar sigmaY, SkTileMode tileMode, sk_sp<SkImageFilter> input,
const CropRect& cropRect) {
if (!SkScalarsAreFinite(sigmaX, sigmaY) || sigmaX < 0.f || sigmaY < 0.f) {
// Non-finite or negative sigmas are error conditions. We allow 0 sigma for X and/or Y
// for 1D blurs; onFilterImage() will detect when no visible blurring would occur based on
// the Context mapping.
return nullptr;
}
// Temporarily allow tiling with no crop rect
if (tileMode != SkTileMode::kDecal && !cropRect) {
return sk_make_sp<SkBlurImageFilter>(SkSize{sigmaX, sigmaY}, tileMode, std::move(input));
}
// The 'tileMode' behavior is not well-defined if there is no crop. We only apply it if
// there is a provided 'cropRect'.
sk_sp<SkImageFilter> filter = std::move(input);
if (tileMode != SkTileMode::kDecal && cropRect) {
// Historically the input image was restricted to the cropRect when tiling was not
// kDecal, so that the kernel evaluated the tiled edge conditions, while a kDecal crop
// only affected the output.
filter = SkImageFilters::Crop(*cropRect, tileMode, std::move(filter));
}
filter = sk_make_sp<SkBlurImageFilter>(SkSize{sigmaX, sigmaY}, std::move(filter));
if (cropRect) {
// But regardless of the tileMode, the output is always decal cropped
filter = SkImageFilters::Crop(*cropRect, SkTileMode::kDecal, std::move(filter));
}
return filter;
}
小结
创建java的RenderEffect实际上是创建了一个C++的SkImageFilter对象。
给View设置RenderEffect
View.setRenderEffect方法:(frameworks/base/core/java/android/view/View.java)
/**
* Configure the {@link android.graphics.RenderEffect} to apply to this View.
* This will apply a visual effect to the results of the View before it is drawn. For example if
* {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)}
* is provided, the contents will be drawn in a separate layer, then this layer will be blurred
* when this View is drawn.
* @param renderEffect to be applied to the View. Passing null clears the previously configured
* {@link RenderEffect}
*/
public void setRenderEffect(@Nullable RenderEffect renderEffect) {
if (mRenderNode.setRenderEffect(renderEffect)) {
invalidateViewProperty(true, true);
}
}
主要是调用了RenderNode.setRenderEffect方法:(frameworks/base/graphics/java/android/graphics/RenderNode.java)
/**
* Configure the {@link android.graphics.RenderEffect} to apply to this RenderNode. This
* will apply a visual effect to the end result of the contents of this RenderNode before
* it is drawn into the destination. For example if
* {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)}
* is provided, the contents will be drawn in a separate layer, then this layer will
* be blurred when this RenderNode is drawn into the destination.
* @param renderEffect to be applied to the RenderNode. Passing null clears all previously
* configured RenderEffects
* @return True if the value changed, false if the new value was the same as the previous value.
*/
public boolean setRenderEffect(@Nullable RenderEffect renderEffect) {
return nSetRenderEffect(mNativeRenderNode,
renderEffect != null ? renderEffect.getNativeInstance() : 0);
}
这里调用了C++层的setRenderEffect方法:
static jboolean android_view_RenderNode_setRenderEffect(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
jlong renderEffectPtr) {
SkImageFilter* imageFilter = reinterpret_cast<SkImageFilter*>(renderEffectPtr);
return SET_AND_DIRTY(mutateLayerProperties().setImageFilter, imageFilter, RenderNode::GENERIC);
}
RenderNode有一个类型为RenderProperties的成员变量mStagingProperties,
RenderProperties有一个类型为LayerProperties的成员变量mLayerProperties,
LayerProperties有一个类型为sk_sp的成员变量mImageFilter,
这里将imageFilter设置到了mImageFilter上面:(frameworks/base/libs/hwui/RenderProperties.cpp)
bool LayerProperties::setImageFilter(SkImageFilter* imageFilter) {
if (mImageFilter.get() == imageFilter) return false;
mImageFilter = sk_ref_sp(imageFilter);
return true;
}
小结
给View设置RenderEffect实际上是将SkImageFilter设置到了View.mRenderNode.mStagingProperties.mLayerProperties.mImageFilter上面。
录制RenderNode
在UI线程录制绘制操作的时候,会遍历整个View树。父View通过drawRenderNode方法录制一个子View的renderNode为子节点:(frameworks/base/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp)
void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
// Record the child node. Drawable dtor will be invoked when mChildNodes deque is cleared.
mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier);
auto& renderNodeDrawable = mDisplayList->mChildNodes.back();
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
// Put Vulkan WebViews with non-rectangular clips in a HW layer
renderNode->mutateStagingProperties().setClipMayBeComplex(mRecorder.isClipMayBeComplex());
}
// draw backdrop filter drawable if needed.
if (renderNode->stagingProperties().layerProperties().getBackdropImageFilter()) {
auto* backdropFilterDrawable =
mDisplayList->allocateDrawable<BackdropFilterDrawable>(renderNode, asSkCanvas());
drawDrawable(backdropFilterDrawable);
}
drawDrawable(&renderNodeDrawable);
// use staging property, since recording on UI thread
if (renderNode->stagingProperties().isProjectionReceiver()) {
mDisplayList->mProjectionReceiver = &renderNodeDrawable;
}
}
这里mDisplayList->mChildNodes的类型为std::deque,通过emplace_back方法调用了RenderNodeDrawable的构造器,构建了mComposeLayer为true的子RenderNodeDrawable。
再通过drawDrawable方法去调用RecordingCanvas的onDrawDrawable方法,添加这个RenderNodeDrawable到DisplayListData中。
RenderNodeDrawable
RenderNodeDrawable的mComposeLayer成员变量定义如下:(frameworks/base/libs/hwui/pipeline/skia/RenderNodeDrawable.h)
/**
* If mRenderNode's layer type is RenderLayer this flag determines whether we
* should draw into the contents of the layer or compose the existing contents
* of the layer into the canvas.
*/
const bool mComposeLayer;
mComposeLayer如果为false,则绘制这个layer的内容,如果为true,则合成已存在的内容到canvas上。
mComposeLayer在RenderNodeDrawable的构造器中赋值,并且默认值为true:(frameworks/base/libs/hwui/pipeline/skia/RenderNodeDrawable.h)
/**
* Creates a new RenderNodeDrawable backed by a render node.
*
* @param node that has to be drawn
* @param canvas is a recording canvas used to extract its matrix
* @param composeLayer if the node's layer type is RenderLayer this flag determines whether
* we should draw into the contents of the layer or compose the existing contents of the
* layer into the canvas.
*/
explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer = true,
bool inReorderingSection = false);
RenderNodeDrawable会随着mComposeLayer的值不同而在绘制时有不同的行为,所以每次构建RenderNodeDrawable的时候可以关注下其mComposeLayer的值是什么。
小结
父View录制绘制子View时,使用SkiaRecordingCanvas::drawRenderNode方法,将子View的renderNode装在了一个mComposeLayer为true的RenderNodeDrawable中,存到了SkiaRecordingCanvas.mDisplayList.mChildNodes里面。
创建layer的SkSurface
View的绘制操作在UI线程录制完成后,再切换到RenderThread线程,首先执行prepareTree操作,在prepareTree中将有RenderEffect的RenderNode视为layer,创建独立的SkSurface。
prepareTree调用栈
RenderNode::prepareTreeImpl
RenderNode::prepareTree
CanvasContext::prepareTree
DrawFrameTask::syncFrameState
DrawFrameTask::run
创建SkSurface
绘制前hwui会调用RenderNode的prepareTreeImpl方法:(frameworks/base/libs/hwui/RenderNode.cpp)
/**
* Traverse down the the draw tree to prepare for a frame.
*
* MODE_FULL = UI Thread-driven (thus properties must be synced), otherwise RT driven
*
* While traversing down the tree, functorsNeedLayer flag is set to true if anything that uses the
* stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer.
*/
void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) {
...
pushLayerUpdate(info);
...
}
调用了RenderNode的pushLayerUpdate方法:(frameworks/base/libs/hwui/RenderNode.cpp)
void RenderNode::pushLayerUpdate(TreeInfo& info) {
#ifdef __ANDROID__ // Layoutlib does not support CanvasContext and Layers
LayerType layerType = properties().effectiveLayerType();
// If we are not a layer OR we cannot be rendered (eg, view was detached)
// we need to destroy any Layers we may have had previously
if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable()) ||
CC_UNLIKELY(properties().getWidth() == 0) || CC_UNLIKELY(properties().getHeight() == 0) ||
CC_UNLIKELY(!properties().fitsOnLayer())) {
if (CC_UNLIKELY(hasLayer())) {
this->setLayerSurface(nullptr);
}
return;
}
if (info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator, info.errorHandler)) {
damageSelf(info);
}
if (!hasLayer()) {
return;
}
SkRect dirty;
info.damageAccumulator->peekAtDirty(&dirty);
info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
if (!dirty.isEmpty()) {
mStretchMask.markDirty();
}
// There might be prefetched layers that need to be accounted for.
// That might be us, so tell CanvasContext that this layer is in the
// tree and should not be destroyed.
info.canvasContext.markLayerInUse(this);
#endif
}
这里首先调用RenderProperties的effectiveLayerType方法判断这个RenderNode是否是一个layer:(frameworks/base/libs/hwui/RenderProperties.h)
bool fitsOnLayer() const {
const DeviceInfo* deviceInfo = DeviceInfo::get();
return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() &&
mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize();
}
bool promotedToLayer() const {
return mLayerProperties.mType == LayerType::None && fitsOnLayer() &&
(mComputedFields.mNeedLayerForFunctors || mLayerProperties.mImageFilter != nullptr ||
mLayerProperties.getStretchEffect().requiresLayer() ||
(!MathUtils::isZero(mPrimitiveFields.mAlpha) && mPrimitiveFields.mAlpha < 1 &&
mPrimitiveFields.mHasOverlappingRendering));
}
LayerType effectiveLayerType() const {
return CC_UNLIKELY(promotedToLayer()) ? LayerType::RenderLayer : mLayerProperties.mType;
}
由于我们之前设置了mImageFilter,mLayerProperties.mImageFilter != nullptr是true,导致promotedToLayer是true,从而导致effectiveLayerType方法返回LayerType::RenderLayer。
因此这个RenderNode被认为是layer,继续去调用CanvasContext的createOrUpdateLayer方法:(frameworks/base/libs/hwui/renderthread/CanvasContext.h)
/**
* Update or create a layer specific for the provided RenderNode. The layer
* attached to the node will be specific to the RenderPipeline used by this
* context
*
* @return true if the layer has been created or updated
*/
bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator,
ErrorHandler* errorHandler) {
return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, errorHandler);
}
SkiaPipeline的createOrUpdateLayer方法:(frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp)
bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
ErrorHandler* errorHandler) {
// compute the size of the surface (i.e. texture) to be allocated for this layer
const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE;
const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE;
SkSurface* layer = node->getLayerSurface();
if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
SkImageInfo info;
info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(),
kPremul_SkAlphaType, getSurfaceColorSpace());
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
SkASSERT(mRenderThread.getGrContext() != nullptr);
node->setLayerSurface(SkSurfaces::RenderTarget(mRenderThread.getGrContext(),
skgpu::Budgeted::kYes, info, 0,
this->getSurfaceOrigin(), &props));
if (node->getLayerSurface()) {
// update the transform in window of the layer to reset its origin wrt light source
// position
Matrix4 windowTransform;
damageAccumulator.computeCurrentTransform(&windowTransform);
node->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
} else {
String8 cachesOutput;
mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput,
&mRenderThread.renderState());
ALOGE("%s", cachesOutput.c_str());
if (errorHandler) {
std::ostringstream err;
err << "Unable to create layer for " << node->getName();
const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
err << ", size " << info.width() << "x" << info.height() << " max size "
<< maxTextureSize << " color type " << (int)info.colorType() << " has context "
<< (int)(mRenderThread.getGrContext() != nullptr);
errorHandler->onError(err.str());
}
}
return true;
}
return false;
}
这里最终创建了这个RenderNode的LayerSurface。
如果一个View有是layer的RenderNode,这个View又被称为离屏View。
LayerSurface创建完成后,在RenderNode的pushLayerUpdate方法后面还通过info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty)将这个RenderNode添加到了layerUpdateQueue里面。
小结
RenderNode有ImageFilter之后,可以被看成是一个单独的layer,会在prepareTree阶段创建自己的SkSurface,并保存在layerUpdateQueue中。
绘制RenderNode
在RenderThread线程执行完prepareTree之后,就要开始绘制RenderNode了。
绘制调用栈
SkiaPipeline::renderFrame
SkiaVulkanPipeline::draw
CanvasContext::draw
DrawFrameTask::run
绘制layer
在SkiaPipeline的renderFrame方法:(frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp)
void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
const std::vector<sp<RenderNode>>& nodes, bool opaque,
const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
const SkMatrix& preTransform) {
bool previousSkpEnabled = Properties::skpCaptureEnabled;
if (mPictureCapturedCallback) {
Properties::skpCaptureEnabled = true;
}
// Initialize the canvas for the current frame, that might be a recording canvas if SKP
// capture is enabled.
SkCanvas* canvas = tryCapture(surface.get(), nodes[0].get(), layers);
// draw all layers up front
renderLayersImpl(layers, opaque);
renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform);
endCapture(surface.get());
if (CC_UNLIKELY(Properties::debugOverdraw)) {
renderOverdraw(clip, nodes, contentDrawBounds, surface, preTransform);
}
Properties::skpCaptureEnabled = previousSkpEnabled;
}
首先调用了SkiaPipeline的renderLayersImpl方法:(frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp)
void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
sk_sp<GrDirectContext> cachedContext;
// Render all layers that need to be updated, in order.
for (size_t i = 0; i < layers.entries().size(); i++) {
RenderNode* layerNode = layers.entries()[i].renderNode.get();
// only schedule repaint if node still on layer - possible it may have been
// removed during a dropped frame, but layers may still remain scheduled so
// as not to lose info on what portion is damaged
if (CC_UNLIKELY(layerNode->getLayerSurface() == nullptr)) {
continue;
}
SkASSERT(layerNode->getLayerSurface());
SkiaDisplayList* displayList = layerNode->getDisplayList().asSkiaDl();
if (!displayList || displayList->isEmpty()) {
ALOGE("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName());
return;
}
const Rect& layerDamage = layers.entries()[i].damage;
SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
int saveCount = layerCanvas->save();
SkASSERT(saveCount == 1);
layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect());
// TODO: put localized light center calculation and storage to a drawable related code.
// It does not seem right to store something localized in a global state
// fix here and in recordLayers
const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw());
Vector3 transformedLightCenter(savedLightCenter);
// map current light center into RenderNode's coordinate space
layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter);
LightingInfo::setLightCenterRaw(transformedLightCenter);
const RenderProperties& properties = layerNode->properties();
const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) {
return;
}
ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(),
bounds.height());
layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false;
layerCanvas->clear(SK_ColorTRANSPARENT);
RenderNodeDrawable root(layerNode, layerCanvas, false);
root.forceDraw(layerCanvas);
layerCanvas->restoreToCount(saveCount);
LightingInfo::setLightCenterRaw(savedLightCenter);
// cache the current context so that we can defer flushing it until
// either all the layers have been rendered or the context changes
GrDirectContext* currentContext =
GrAsDirectContext(layerNode->getLayerSurface()->getCanvas()->recordingContext());
if (cachedContext.get() != currentContext) {
if (cachedContext.get()) {
ATRACE_NAME("flush layers (context changed)");
cachedContext->flushAndSubmit();
}
cachedContext.reset(SkSafeRef(currentContext));
}
}
if (cachedContext.get()) {
ATRACE_NAME("flush layers");
cachedContext->flushAndSubmit();
}
}
这里遍历了所有的layer,使用mComposeLayer为false的RenderNodeDrawable的forceDraw方法,将这个layer的内容绘制到了之前创建的SkSurface上。
合成layer
SkiaPipeline::renderFrame后面还调用了SkiaPipeline::renderFrameImpl方法:(frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp)
void SkiaPipeline::renderFrameImpl(const SkRect& clip,
const std::vector<sp<RenderNode>>& nodes, bool opaque,
const Rect& contentDrawBounds, SkCanvas* canvas,
const SkMatrix& preTransform) {
SkAutoCanvasRestore saver(canvas, true);
auto clipRestriction = preTransform.mapRect(clip).roundOut();
if (CC_UNLIKELY(isCapturingSkp())) {
canvas->drawAnnotation(SkRect::Make(clipRestriction), "AndroidDeviceClipRestriction",
nullptr);
} else {
// clip drawing to dirty region only when not recording SKP files (which should contain all
// draw ops on every frame)
canvas->androidFramework_setDeviceClipRestriction(clipRestriction);
}
canvas->concat(preTransform);
if (!opaque) {
canvas->clear(SK_ColorTRANSPARENT);
}
if (1 == nodes.size()) {
if (!nodes[0]->nothingToDraw()) {
RenderNodeDrawable root(nodes[0].get(), canvas);
root.draw(canvas);
}
} else if (0 == nodes.size()) {
// nothing to draw
} else {
......
}
}
这里对RootRenderNode,使用mComposeLayer为true的RenderNodeDrawable的forceDraw方法(RenderNodeDrawable::draw还是调用了RenderNodeDrawable::forceDraw),将所有内容合成绘制到了这个窗口的Surface上。
小结
hwui绘制时,先把每个layer的所有displayList绘制到之前创建的SkCanvas上,最后再一起合成绘制到最终的Surface上面。
绘制模糊效果
在合成layer的时候调用了RenderNodeDrawable的forceDraw方法:(frameworks/base/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp)
void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const {
RenderNode* renderNode = mRenderNode.get();
MarkDraw _marker{*canvas, *renderNode};
// We only respect the nothingToDraw check when we are composing a layer. This
// ensures that we paint the layer even if it is not currently visible in the
// event that the properties change and it becomes visible.
if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) ||
(renderNode->nothingToDraw() && mComposeLayer)) {
return;
}
SkiaDisplayList* displayList = renderNode->getDisplayList().asSkiaDl();
SkAutoCanvasRestore acr(canvas, true);
const RenderProperties& properties = this->getNodeProperties();
// pass this outline to the children that may clip backward projected nodes
displayList->mProjectedOutline =
displayList->containsProjectionReceiver() ? &properties.getOutline() : nullptr;
if (!properties.getProjectBackwards()) {
drawContent(canvas);
if (mProjectedDisplayList) {
acr.restore(); // draw projected children using parent matrix
LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline);
const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath();
SkAutoCanvasRestore acr2(canvas, shouldClip);
canvas->setMatrix(mProjectedDisplayList->mParentMatrix);
if (shouldClip) {
canvas->clipPath(*mProjectedDisplayList->mProjectedOutline->getPath());
}
drawBackwardsProjectedNodes(canvas, *mProjectedDisplayList);
}
}
displayList->mProjectedOutline = nullptr;
}
调用了RenderNodeDrawable的drawContent方法:(frameworks/base/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp)
void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
RenderNode* renderNode = mRenderNode.get();
float alphaMultiplier = 1.0f;
const RenderProperties& properties = renderNode->properties();
// If we are drawing the contents of layer, we don't want to apply any of
// the RenderNode's properties during this pass. Those will all be applied
// when the layer is composited.
if (mComposeLayer) {
setViewProperties(properties, canvas, &alphaMultiplier);
}
SkiaDisplayList* displayList = mRenderNode->getDisplayList().asSkiaDl();
displayList->mParentMatrix = canvas->getTotalMatrix();
// TODO should we let the bound of the drawable do this for us?
const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
bool quickRejected = properties.getClipToBounds() && canvas->quickReject(bounds);
if (!quickRejected) {
auto clipBounds = canvas->getLocalClipBounds();
SkIRect srcBounds = SkIRect::MakeWH(bounds.width(), bounds.height());
SkIPoint offset = SkIPoint::Make(0.0f, 0.0f);
SkiaDisplayList* displayList = renderNode->getDisplayList().asSkiaDl();
const LayerProperties& layerProperties = properties.layerProperties();
// composing a hardware layer
if (renderNode->getLayerSurface() && mComposeLayer) { // 如果这个RenderNode是layer
...
if (!Properties::enableRenderEffectCache) {
...
} else { // 使用imageFilter生成snapshotImage
const auto snapshotResult = renderNode->updateSnapshotIfRequired(
recordingContext, layerProperties.getImageFilter(), clipBounds.roundOut());
snapshotImage = snapshotResult->snapshot;
srcBounds = snapshotResult->outSubset;
offset = snapshotResult->outOffset;
}
...
const StretchEffect& stretch = properties.layerProperties().getStretchEffect();
if (stretch.isEmpty() ||
Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale) {
...
// 将snapshotImage绘制到canvas上
canvas->drawImageRect(snapshotImage, SkRect::Make(srcBounds),
SkRect::Make(dstBounds), sampling, &paint,
SkCanvas::kStrict_SrcRectConstraint);
} else {
...
}
...
} else { // 如果这个RenderNode不是layer,直接将displayList绘制到canvas上
if (alphaMultiplier < 1.0f) {
// Non-layer draw for a view with getHasOverlappingRendering=false, will apply
// the alpha to the paint of each nested draw.
AlphaFilterCanvas alphaCanvas(canvas, alphaMultiplier);
displayList->draw(&alphaCanvas);
} else {
displayList->draw(canvas);
}
}
}
}
最终合成时,作为layer的RenderNode的内容首先经过imageFilter绘制到snapshotImage上,然后通过canvas->drawImageRect(snapshotImage, …)方法绘制到最终的Surface上面。
非layer的RenderNode则直接将displayList绘制到canvas上,displayList中可能还会有RenderNodeDrawable,则会递归调用这个RenderNodeDrawable::drawContent方法。