Home | History | Annotate | Download | only in skia
      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