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 "ReorderBarrierDrawables.h" 18#include "RenderNode.h" 19#include "SkiaDisplayList.h" 20#include "SkiaPipeline.h" 21 22#include <SkBlurMask.h> 23#include <SkBlurMaskFilter.h> 24#include <SkPathOps.h> 25#include <SkRRectsGaussianEdgeMaskFilter.h> 26#include <SkShadowUtils.h> 27 28namespace android { 29namespace uirenderer { 30namespace skiapipeline { 31 32StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data) 33 : mEndChildIndex(0) 34 , mBeginChildIndex(data->mChildNodes.size()) 35 , mDisplayList(data) { 36} 37 38void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) { 39 if (mChildren.empty()) { 40 //mChildren is allocated and initialized only the first time onDraw is called and cached for 41 //subsequent calls 42 mChildren.reserve(mEndChildIndex - mBeginChildIndex + 1); 43 for (int i = mBeginChildIndex; i <= mEndChildIndex; i++) { 44 mChildren.push_back(const_cast<RenderNodeDrawable*>(&mDisplayList->mChildNodes[i])); 45 } 46 } 47 std::stable_sort(mChildren.begin(), mChildren.end(), 48 [](RenderNodeDrawable* a, RenderNodeDrawable* b) { 49 const float aZValue = a->getNodeProperties().getZ(); 50 const float bZValue = b->getNodeProperties().getZ(); 51 return aZValue < bZValue; 52 }); 53 54 size_t drawIndex = 0; 55 const size_t endIndex = mChildren.size(); 56 while (drawIndex < endIndex) { 57 RenderNodeDrawable* childNode = mChildren[drawIndex]; 58 SkASSERT(childNode); 59 const float casterZ = childNode->getNodeProperties().getZ(); 60 if (casterZ >= -NON_ZERO_EPSILON) { //draw only children with negative Z 61 return; 62 } 63 childNode->forceDraw(canvas); 64 drawIndex++; 65 } 66} 67 68EndReorderBarrierDrawable::EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier) 69 : mStartBarrier(startBarrier) { 70 mStartBarrier->mEndChildIndex = mStartBarrier->mDisplayList->mChildNodes.size() - 1; 71} 72 73#define SHADOW_DELTA 0.1f 74 75void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) { 76 auto& zChildren = mStartBarrier->mChildren; 77 78 /** 79 * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters 80 * with very similar Z heights to draw together. 81 * 82 * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are 83 * underneath both, and neither's shadow is drawn on top of the other. 84 */ 85 size_t drawIndex = 0; 86 87 const size_t endIndex = zChildren.size(); 88 while (drawIndex < endIndex //draw only children with positive Z 89 && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON) drawIndex++; 90 size_t shadowIndex = drawIndex; 91 92 float lastCasterZ = 0.0f; 93 while (shadowIndex < endIndex || drawIndex < endIndex) { 94 if (shadowIndex < endIndex) { 95 const float casterZ = zChildren[shadowIndex]->getNodeProperties().getZ(); 96 97 // attempt to render the shadow if the caster about to be drawn is its caster, 98 // OR if its caster's Z value is similar to the previous potential caster 99 if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) { 100 this->drawShadow(canvas, zChildren[shadowIndex]); 101 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow 102 shadowIndex++; 103 continue; 104 } 105 } 106 107 RenderNodeDrawable* childNode = zChildren[drawIndex]; 108 SkASSERT(childNode); 109 childNode->forceDraw(canvas); 110 111 drawIndex++; 112 } 113} 114 115// copied from FrameBuilder::deferShadow 116void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) { 117 const RenderProperties& casterProperties = caster->getNodeProperties(); 118 119 if (casterProperties.getAlpha() <= 0.0f 120 || casterProperties.getOutline().getAlpha() <= 0.0f 121 || !casterProperties.getOutline().getPath() 122 || casterProperties.getScaleX() == 0 123 || casterProperties.getScaleY() == 0) { 124 // no shadow to draw 125 return; 126 } 127 128 const SkScalar casterAlpha = casterProperties.getAlpha() 129 * casterProperties.getOutline().getAlpha(); 130 if (casterAlpha <= 0.0f) { 131 return; 132 } 133 134 float ambientAlpha = (SkiaPipeline::getAmbientShadowAlpha()/255.f)*casterAlpha; 135 float spotAlpha = (SkiaPipeline::getSpotShadowAlpha()/255.f)*casterAlpha; 136 137 const RevealClip& revealClip = casterProperties.getRevealClip(); 138 const SkPath* revealClipPath = revealClip.getPath(); 139 if (revealClipPath && revealClipPath->isEmpty()) { 140 // An empty reveal clip means nothing is drawn 141 return; 142 } 143 144 bool clippedToBounds = casterProperties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS; 145 146 SkRect casterClipRect = SkRect::MakeEmpty(); 147 if (clippedToBounds) { 148 Rect clipBounds; 149 casterProperties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds); 150 casterClipRect = clipBounds.toSkRect(); 151 if (casterClipRect.isEmpty()) { 152 // An empty clip rect means nothing is drawn 153 return; 154 } 155 } 156 157 SkAutoCanvasRestore acr(canvas, true); 158 159 SkMatrix shadowMatrix; 160 mat4 hwuiMatrix; 161 // TODO we don't pass the optional boolean to treat it as a 4x4 matrix 162 caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix); 163 hwuiMatrix.copyTo(shadowMatrix); 164 canvas->concat(shadowMatrix); 165 166 const SkPath* casterOutlinePath = casterProperties.getOutline().getPath(); 167 // holds temporary SkPath to store the result of intersections 168 SkPath tmpPath; 169 const SkPath* casterPath = casterOutlinePath; 170 171 // TODO: In to following course of code that calculates the final shape, is there an optimal 172 // of doing the Op calculations? 173 // intersect the shadow-casting path with the reveal, if present 174 if (revealClipPath) { 175 Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath); 176 tmpPath.setIsVolatile(true); 177 casterPath = &tmpPath; 178 } 179 180 // intersect the shadow-casting path with the clipBounds, if present 181 if (clippedToBounds) { 182 SkPath clipBoundsPath; 183 clipBoundsPath.addRect(casterClipRect); 184 Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, &tmpPath); 185 tmpPath.setIsVolatile(true); 186 casterPath = &tmpPath; 187 } 188 const Vector3 lightPos = SkiaPipeline::getLightCenter(); 189 SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z); 190 SkPoint3 zParams; 191 if (shadowMatrix.hasPerspective()) { 192 // get the matrix with the full 3D transform 193 mat4 zMatrix; 194 caster->getRenderNode()->applyViewPropertyTransforms(zMatrix, true); 195 zParams = SkPoint3::Make(zMatrix[2], zMatrix[6], zMatrix[mat4::kTranslateZ]); 196 } else { 197 zParams = SkPoint3::Make(0, 0, casterProperties.getZ()); 198 } 199 SkShadowUtils::DrawShadow(canvas, *casterPath, zParams, skiaLightPos, 200 SkiaPipeline::getLightRadius(), ambientAlpha, spotAlpha, SK_ColorBLACK, 201 casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0); 202} 203 204}; // namespace skiapipeline 205}; // namespace uirenderer 206}; // namespace android 207