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 <SkPathOps.h> 23 #include <SkShadowUtils.h> 24 25 namespace android { 26 namespace uirenderer { 27 namespace skiapipeline { 28 29 StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data) 30 : mEndChildIndex(0), mBeginChildIndex(data->mChildNodes.size()), mDisplayList(data) {} 31 32 void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) { 33 if (mChildren.empty()) { 34 // mChildren is allocated and initialized only the first time onDraw is called and cached 35 // for 36 // subsequent calls 37 mChildren.reserve(mEndChildIndex - mBeginChildIndex + 1); 38 for (int i = mBeginChildIndex; i <= mEndChildIndex; i++) { 39 mChildren.push_back(const_cast<RenderNodeDrawable*>(&mDisplayList->mChildNodes[i])); 40 } 41 } 42 std::stable_sort(mChildren.begin(), mChildren.end(), 43 [](RenderNodeDrawable* a, RenderNodeDrawable* b) { 44 const float aZValue = a->getNodeProperties().getZ(); 45 const float bZValue = b->getNodeProperties().getZ(); 46 return aZValue < bZValue; 47 }); 48 49 size_t drawIndex = 0; 50 const size_t endIndex = mChildren.size(); 51 while (drawIndex < endIndex) { 52 RenderNodeDrawable* childNode = mChildren[drawIndex]; 53 SkASSERT(childNode); 54 const float casterZ = childNode->getNodeProperties().getZ(); 55 if (casterZ >= -NON_ZERO_EPSILON) { // draw only children with negative Z 56 return; 57 } 58 SkAutoCanvasRestore acr(canvas, true); 59 // Since we're drawing out of recording order, the child's matrix needs to be applied to the 60 // canvas. In in-order drawing, the canvas already has the child's matrix applied. 61 canvas->setMatrix(mDisplayList->mParentMatrix); 62 canvas->concat(childNode->getRecordedMatrix()); 63 childNode->forceDraw(canvas); 64 drawIndex++; 65 } 66 } 67 68 EndReorderBarrierDrawable::EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier) 69 : mStartBarrier(startBarrier) { 70 mStartBarrier->mEndChildIndex = mStartBarrier->mDisplayList->mChildNodes.size() - 1; 71 } 72 73 #define SHADOW_DELTA 0.1f 74 75 void 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) 90 drawIndex++; 91 size_t shadowIndex = drawIndex; 92 93 float lastCasterZ = 0.0f; 94 while (shadowIndex < endIndex || drawIndex < endIndex) { 95 if (shadowIndex < endIndex) { 96 const float casterZ = zChildren[shadowIndex]->getNodeProperties().getZ(); 97 98 // attempt to render the shadow if the caster about to be drawn is its caster, 99 // OR if its caster's Z value is similar to the previous potential caster 100 if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) { 101 this->drawShadow(canvas, zChildren[shadowIndex]); 102 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow 103 shadowIndex++; 104 continue; 105 } 106 } 107 108 RenderNodeDrawable* childNode = zChildren[drawIndex]; 109 SkASSERT(childNode); 110 SkAutoCanvasRestore acr(canvas, true); 111 // Since we're drawing out of recording order, the child's matrix needs to be applied to the 112 // canvas. In in-order drawing, the canvas already has the child's matrix applied. 113 canvas->setMatrix(mStartBarrier->mDisplayList->mParentMatrix); 114 canvas->concat(childNode->getRecordedMatrix()); 115 childNode->forceDraw(canvas); 116 117 drawIndex++; 118 } 119 } 120 121 static SkColor multiplyAlpha(SkColor color, float alpha) { 122 return SkColorSetA(color, alpha * SkColorGetA(color)); 123 } 124 125 // copied from FrameBuilder::deferShadow 126 void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) { 127 const RenderProperties& casterProperties = caster->getNodeProperties(); 128 129 if (casterProperties.getAlpha() <= 0.0f || casterProperties.getOutline().getAlpha() <= 0.0f || 130 !casterProperties.getOutline().getPath() || casterProperties.getScaleX() == 0 || 131 casterProperties.getScaleY() == 0) { 132 // no shadow to draw 133 return; 134 } 135 136 const SkScalar casterAlpha = 137 casterProperties.getAlpha() * casterProperties.getOutline().getAlpha(); 138 if (casterAlpha <= 0.0f) { 139 return; 140 } 141 142 float ambientAlpha = (SkiaPipeline::getAmbientShadowAlpha() / 255.f) * casterAlpha; 143 float spotAlpha = (SkiaPipeline::getSpotShadowAlpha() / 255.f) * casterAlpha; 144 145 const RevealClip& revealClip = casterProperties.getRevealClip(); 146 const SkPath* revealClipPath = revealClip.getPath(); 147 if (revealClipPath && revealClipPath->isEmpty()) { 148 // An empty reveal clip means nothing is drawn 149 return; 150 } 151 152 bool clippedToBounds = casterProperties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS; 153 154 SkRect casterClipRect = SkRect::MakeEmpty(); 155 if (clippedToBounds) { 156 Rect clipBounds; 157 casterProperties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds); 158 casterClipRect = clipBounds.toSkRect(); 159 if (casterClipRect.isEmpty()) { 160 // An empty clip rect means nothing is drawn 161 return; 162 } 163 } 164 165 SkAutoCanvasRestore acr(canvas, true); 166 // Since we're drawing out of recording order, the child's matrix needs to be applied to the 167 // canvas. In in-order drawing, the canvas already has the child's matrix applied. 168 canvas->setMatrix(mStartBarrier->mDisplayList->mParentMatrix); 169 170 SkMatrix shadowMatrix; 171 mat4 hwuiMatrix(caster->getRecordedMatrix()); 172 // TODO we don't pass the optional boolean to treat it as a 4x4 matrix 173 // applyViewPropertyTransforms gets the same matrix, which render nodes apply with 174 // RenderNodeDrawable::setViewProperties as a part if their draw. 175 caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix); 176 hwuiMatrix.copyTo(shadowMatrix); 177 canvas->concat(shadowMatrix); 178 179 // default the shadow-casting path to the outline of the caster 180 const SkPath* casterPath = casterProperties.getOutline().getPath(); 181 182 // intersect the shadow-casting path with the clipBounds, if present 183 if (clippedToBounds && !casterClipRect.contains(casterPath->getBounds())) { 184 casterPath = caster->getRenderNode()->getClippedOutline(casterClipRect); 185 } 186 187 // intersect the shadow-casting path with the reveal, if present 188 SkPath tmpPath; // holds temporary SkPath to store the result of intersections 189 if (revealClipPath) { 190 Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath); 191 tmpPath.setIsVolatile(true); 192 casterPath = &tmpPath; 193 } 194 195 const Vector3 lightPos = SkiaPipeline::getLightCenter(); 196 SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z); 197 SkPoint3 zParams; 198 if (shadowMatrix.hasPerspective()) { 199 // get the matrix with the full 3D transform 200 mat4 zMatrix; 201 caster->getRenderNode()->applyViewPropertyTransforms(zMatrix, true); 202 zParams = SkPoint3::Make(zMatrix[2], zMatrix[6], zMatrix[mat4::kTranslateZ]); 203 } else { 204 zParams = SkPoint3::Make(0, 0, casterProperties.getZ()); 205 } 206 SkColor ambientColor = multiplyAlpha(casterProperties.getAmbientShadowColor(), ambientAlpha); 207 SkColor spotColor = multiplyAlpha(casterProperties.getSpotShadowColor(), spotAlpha); 208 SkShadowUtils::DrawShadow( 209 canvas, *casterPath, zParams, skiaLightPos, SkiaPipeline::getLightRadius(), 210 ambientColor, spotColor, 211 casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0); 212 } 213 214 } // namespace skiapipeline 215 } // namespace uirenderer 216 } // namespace android 217