RenderNodeDrawable.cpp revision 40800725d2dec2915607af0231f04f40c5cd221b
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "RenderNodeDrawable.h" 18#include "RenderNode.h" 19#include "SkiaDisplayList.h" 20#include "SkiaPipeline.h" 21#include "utils/TraceUtils.h" 22 23namespace android { 24namespace uirenderer { 25namespace skiapipeline { 26 27static void clipOutline(const Outline& outline, SkCanvas* canvas, const SkRect* pendingClip) { 28 SkASSERT(outline.willClip()); 29 Rect possibleRect; 30 float radius; 31 LOG_ALWAYS_FATAL_IF(!outline.getAsRoundRect(&possibleRect, &radius), 32 "clipping outlines should be at most roundedRects"); 33 SkRect rect = possibleRect.toSkRect(); 34 if (radius != 0.0f) { 35 if (pendingClip && !pendingClip->contains(rect)) { 36 canvas->clipRect(*pendingClip); 37 } 38 canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), SkRegion::kIntersect_Op, true); 39 } else { 40 if (pendingClip) { 41 (void)rect.intersect(*pendingClip); 42 } 43 canvas->clipRect(rect); 44 } 45} 46 47const RenderProperties& RenderNodeDrawable::getNodeProperties() const { 48 return mRenderNode->properties(); 49} 50 51void RenderNodeDrawable::onDraw(SkCanvas* canvas) { 52 //negative and positive Z order are drawn out of order 53 if (MathUtils::isZero(mRenderNode->properties().getZ())) { 54 this->forceDraw(canvas); 55 } 56} 57 58void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { 59 RenderNode* renderNode = mRenderNode.get(); 60 if (SkiaPipeline::skpCaptureEnabled()) { 61 SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight()); 62 canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr); 63 } 64 65 // We only respect the nothingToDraw check when we are composing a layer. This 66 // ensures that we paint the layer even if it is not currently visible in the 67 // event that the properties change and it becomes visible. 68 if (!renderNode->isRenderable() || (renderNode->nothingToDraw() && mComposeLayer)) { 69 return; 70 } 71 72 SkASSERT(renderNode->getDisplayList()->isSkiaDL()); 73 SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList(); 74 75 SkAutoCanvasRestore acr(canvas, true); 76 77 const RenderProperties& properties = this->getNodeProperties(); 78 if (displayList->mIsProjectionReceiver) { 79 // this node is a projection receiver. We will gather the projected nodes as we draw our 80 // children, and then draw them on top of this node's content. 81 std::vector<ProjectedChild> newList; 82 for (auto& child : displayList->mChildNodes) { 83 // our direct children are not supposed to project into us (nodes project to, at the 84 // nearest, their grandparents). So we "delay" the list's activation one level by 85 // passing it into mNextProjectedChildrenTarget rather than mProjectedChildrenTarget. 86 child.mProjectedChildrenTarget = mNextProjectedChildrenTarget; 87 child.mNextProjectedChildrenTarget = &newList; 88 } 89 // draw ourselves and our children. As a side effect, this will add projected nodes to 90 // newList. 91 this->drawContent(canvas); 92 bool willClip = properties.getOutline().willClip(); 93 if (willClip) { 94 canvas->save(); 95 clipOutline(properties.getOutline(), canvas, nullptr); 96 } 97 // draw the collected projected nodes 98 for (auto& projectedChild : newList) { 99 canvas->setMatrix(projectedChild.matrix); 100 projectedChild.node->drawContent(canvas); 101 } 102 if (willClip) { 103 canvas->restore(); 104 } 105 } else { 106 if (properties.getProjectBackwards() && mProjectedChildrenTarget) { 107 // We are supposed to project this node, so add it to the list and do not actually draw 108 // yet. It will be drawn by its projection receiver. 109 mProjectedChildrenTarget->push_back({ this, canvas->getTotalMatrix() }); 110 return; 111 } 112 for (auto& child : displayList->mChildNodes) { 113 // storing these values in the nodes themselves is a bit ugly; they should "really" be 114 // function parameters, but we have to go through the preexisting draw() method and 115 // therefore cannot add additional parameters to it 116 child.mProjectedChildrenTarget = mNextProjectedChildrenTarget; 117 child.mNextProjectedChildrenTarget = mNextProjectedChildrenTarget; 118 } 119 this->drawContent(canvas); 120 } 121 mProjectedChildrenTarget = nullptr; 122 mNextProjectedChildrenTarget = nullptr; 123} 124 125static bool layerNeedsPaint(const LayerProperties& properties, 126 float alphaMultiplier, SkPaint* paint) { 127 if (alphaMultiplier < 1.0f 128 || properties.alpha() < 255 129 || properties.xferMode() != SkBlendMode::kSrcOver 130 || properties.colorFilter() != nullptr) { 131 paint->setAlpha(properties.alpha() * alphaMultiplier); 132 paint->setBlendMode(properties.xferMode()); 133 paint->setColorFilter(sk_ref_sp(properties.colorFilter())); 134 return true; 135 } 136 return false; 137} 138 139void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { 140 RenderNode* renderNode = mRenderNode.get(); 141 float alphaMultiplier = 1.0f; 142 const RenderProperties& properties = renderNode->properties(); 143 144 // If we are drawing the contents of layer, we don't want to apply any of 145 // the RenderNode's properties during this pass. Those will all be applied 146 // when the layer is composited. 147 if (mComposeLayer) { 148 setViewProperties(properties, canvas, &alphaMultiplier); 149 } 150 151 //TODO should we let the bound of the drawable do this for us? 152 const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight()); 153 bool quickRejected = properties.getClipToBounds() && canvas->quickReject(bounds); 154 if (!quickRejected) { 155 SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList(); 156 const LayerProperties& layerProperties = properties.layerProperties(); 157 // composing a hardware layer 158 if (renderNode->getLayerSurface() && mComposeLayer) { 159 SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer); 160 SkPaint* paint = nullptr; 161 SkPaint tmpPaint; 162 if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) { 163 paint = &tmpPaint; 164 } 165 renderNode->getLayerSurface()->draw(canvas, 0, 0, paint); 166 // composing a software layer with alpha 167 } else if (properties.effectiveLayerType() == LayerType::Software) { 168 SkPaint paint; 169 bool needsLayer = layerNeedsPaint(layerProperties, alphaMultiplier, &paint); 170 if (needsLayer) { 171 canvas->saveLayer(bounds, &paint); 172 } 173 canvas->drawDrawable(displayList->mDrawable.get()); 174 if (needsLayer) { 175 canvas->restore(); 176 } 177 } else { 178 canvas->drawDrawable(displayList->mDrawable.get()); 179 } 180 } 181} 182 183void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas, 184 float* alphaMultiplier) { 185 if (properties.getLeft() != 0 || properties.getTop() != 0) { 186 canvas->translate(properties.getLeft(), properties.getTop()); 187 } 188 if (properties.getStaticMatrix()) { 189 canvas->concat(*properties.getStaticMatrix()); 190 } else if (properties.getAnimationMatrix()) { 191 canvas->concat(*properties.getAnimationMatrix()); 192 } 193 if (properties.hasTransformMatrix()) { 194 if (properties.isTransformTranslateOnly()) { 195 canvas->translate(properties.getTranslationX(), properties.getTranslationY()); 196 } else { 197 canvas->concat(*properties.getTransformMatrix()); 198 } 199 } 200 const bool isLayer = properties.effectiveLayerType() != LayerType::None; 201 int clipFlags = properties.getClippingFlags(); 202 if (properties.getAlpha() < 1) { 203 if (isLayer) { 204 clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer 205 } 206 if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) { 207 *alphaMultiplier = properties.getAlpha(); 208 } else { 209 // savelayer needed to create an offscreen buffer 210 Rect layerBounds(0, 0, properties.getWidth(), properties.getHeight()); 211 if (clipFlags) { 212 properties.getClippingRectForFlags(clipFlags, &layerBounds); 213 clipFlags = 0; // all clipping done by savelayer 214 } 215 SkRect bounds = SkRect::MakeLTRB(layerBounds.left, layerBounds.top, 216 layerBounds.right, layerBounds.bottom); 217 canvas->saveLayerAlpha(&bounds, (int) (properties.getAlpha() * 255)); 218 } 219 220 if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) { 221 // pretend alpha always causes savelayer to warn about 222 // performance problem affecting old versions 223 ATRACE_FORMAT("alpha caused saveLayer %dx%d", properties.getWidth(), 224 properties.getHeight()); 225 } 226 } 227 228 const SkRect* pendingClip = nullptr; 229 SkRect clipRect; 230 231 if (clipFlags) { 232 Rect tmpRect; 233 properties.getClippingRectForFlags(clipFlags, &tmpRect); 234 clipRect = tmpRect.toSkRect(); 235 pendingClip = &clipRect; 236 } 237 238 if (properties.getRevealClip().willClip()) { 239 canvas->clipPath(*properties.getRevealClip().getPath(), SkRegion::kIntersect_Op, true); 240 } else if (properties.getOutline().willClip()) { 241 clipOutline(properties.getOutline(), canvas, pendingClip); 242 pendingClip = nullptr; 243 } 244 245 if (pendingClip) { 246 canvas->clipRect(*pendingClip); 247 } 248} 249 250}; // namespace skiapipeline 251}; // namespace uirenderer 252}; // namespace android 253