ReorderBarrierDrawables.cpp revision e547dd0b80b819bbd377bd7de228737b10570aa0
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 <SkGaussianEdgeShader.h>
25#include <SkPathOps.h>
26#include <SkRRectsGaussianEdgeMaskFilter.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 (unsigned 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    SkASSERT(!mChildren.empty());
55
56    size_t drawIndex = 0;
57    const size_t endIndex = mChildren.size();
58    while (drawIndex < endIndex) {
59        RenderNodeDrawable* childNode = mChildren[drawIndex];
60        SkASSERT(childNode);
61        const float casterZ = childNode->getNodeProperties().getZ();
62        if (casterZ >= -NON_ZERO_EPSILON) { //draw only children with negative Z
63            return;
64        }
65        childNode->forceDraw(canvas);
66        drawIndex++;
67    }
68}
69
70EndReorderBarrierDrawable::EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier)
71        : mStartBarrier(startBarrier) {
72    mStartBarrier->mEndChildIndex = mStartBarrier->mDisplayList->mChildNodes.size() - 1;
73}
74
75#define SHADOW_DELTA 0.1f
76
77void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
78    auto& zChildren = mStartBarrier->mChildren;
79    SkASSERT(!zChildren.empty());
80
81    /**
82     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
83     * with very similar Z heights to draw together.
84     *
85     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
86     * underneath both, and neither's shadow is drawn on top of the other.
87     */
88    size_t drawIndex = 0;
89
90    const size_t endIndex = zChildren.size();
91    while (drawIndex < endIndex     //draw only children with positive Z
92            && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON) drawIndex++;
93    size_t shadowIndex = drawIndex;
94
95    float lastCasterZ = 0.0f;
96    while (shadowIndex < endIndex || drawIndex < endIndex) {
97        if (shadowIndex < endIndex) {
98            const float casterZ = zChildren[shadowIndex]->getNodeProperties().getZ();
99
100            // attempt to render the shadow if the caster about to be drawn is its caster,
101            // OR if its caster's Z value is similar to the previous potential caster
102            if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
103                this->drawShadow(canvas, zChildren[shadowIndex]);
104                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
105                shadowIndex++;
106                continue;
107            }
108        }
109
110        RenderNodeDrawable* childNode = zChildren[drawIndex];
111        SkASSERT(childNode);
112        childNode->forceDraw(canvas);
113
114        drawIndex++;
115    }
116}
117
118/**
119 * @param canvas             the destination for the shadow draws
120 * @param shape              the shape casting the shadow
121 * @param casterZValue       the Z value of the caster RRect
122 * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
123 * @param draw               the function used to draw 'shape'
124 */
125template <typename Shape, typename F>
126static void DrawAmbientShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue,
127        float ambientAlpha, F&& draw) {
128    if (ambientAlpha <= 0) {
129        return;
130    }
131
132    const float kHeightFactor = 1.f/128.f;
133    const float kGeomFactor = 64;
134
135    float umbraAlpha = 1 / (1 + SkMaxScalar(casterZValue*kHeightFactor, 0));
136    float radius = casterZValue*kHeightFactor*kGeomFactor;
137
138    sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
139            SkBlurMask::ConvertRadiusToSigma(radius), SkBlurMaskFilter::kNone_BlurFlag);
140    SkPaint paint;
141    paint.setAntiAlias(true);
142    paint.setMaskFilter(std::move(mf));
143    paint.setARGB(ambientAlpha*umbraAlpha, 0, 0, 0);
144
145    draw(shape, paint);
146}
147
148/**
149 * @param canvas             the destination for the shadow draws
150 * @param shape              the shape casting the shadow
151 * @param casterZValue       the Z value of the caster RRect
152 * @param lightPos           the position of the light casting the shadow
153 * @param lightWidth
154 * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
155 * @param draw               the function used to draw 'shape'
156 */
157template <typename Shape, typename F>
158static void DrawSpotShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue,
159        float spotAlpha, F&& draw) {
160    if (spotAlpha <= 0) {
161        return;
162    }
163
164    const Vector3 lightPos = SkiaPipeline::getLightCenter();
165    float zRatio = casterZValue / (lightPos.z - casterZValue);
166    // clamp
167    if (zRatio < 0.0f) {
168        zRatio = 0.0f;
169    } else if (zRatio > 0.95f) {
170        zRatio = 0.95f;
171    }
172
173    float blurRadius = SkiaPipeline::getLightRadius()*zRatio;
174
175    SkAutoCanvasRestore acr(canvas, true);
176
177    sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
178            SkBlurMask::ConvertRadiusToSigma(blurRadius), SkBlurMaskFilter::kNone_BlurFlag);
179
180    SkPaint paint;
181    paint.setAntiAlias(true);
182    paint.setMaskFilter(std::move(mf));
183    paint.setARGB(spotAlpha, 0, 0, 0);
184
185    // approximate projection by translating and scaling projected offset of bounds center
186    // TODO: compute the actual 2D projection
187    SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
188    canvas->scale(scale, scale);
189    SkPoint center = SkPoint::Make(shape.getBounds().centerX(), shape.getBounds().centerY());
190    SkMatrix ctmInverse;
191    if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
192        ALOGW("Matrix is degenerate. Will not render shadow!");
193        return;
194    }
195    SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
196    ctmInverse.mapPoints(&lightPos2D, 1);
197    canvas->translate(zRatio*(center.fX - lightPos2D.fX), zRatio*(center.fY - lightPos2D.fY));
198
199    draw(shape, paint);
200}
201
202#define MAX_BLUR_RADIUS 16383.75f
203#define MAX_PAD         64
204
205/**
206 * @param casterRect         the rectangle bounds of the RRect casting the shadow
207 * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
208 * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
209 * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
210 * @param casterAlpha        the alpha value of the RRect casting the shadow (0.0-1.0 range)
211 * @param casterZValue       the Z value of the caster RRect
212 * @param scaleFactor        the scale needed to map from src-space to device-space
213 * @param canvas             the destination for the shadow draws
214 */
215static void DrawRRectShadows(const SkRect& casterRect, SkScalar casterCornerRadius,
216        SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue,
217        SkScalar scaleFactor, SkCanvas* canvas) {
218    SkASSERT(casterCornerRadius >= 0.0f);
219
220    // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
221    const SkScalar minRadius = 0.5f / scaleFactor;
222
223    const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()),
224            SkScalarHalf(casterRect.height()));
225    const bool isRect = casterCornerRadius <= minRadius;
226
227    sk_sp<SkShader> edgeShader(SkGaussianEdgeShader::Make());
228
229    if (ambientAlpha > 0.0f) {
230        static const float kHeightFactor = 1.0f / 128.0f;
231        static const float kGeomFactor = 64.0f;
232
233        SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor;
234        // the device-space radius sent to the blur shader must fit in 14.2 fixed point
235        if (srcSpaceAmbientRadius*scaleFactor > MAX_BLUR_RADIUS) {
236            srcSpaceAmbientRadius = MAX_BLUR_RADIUS/scaleFactor;
237        }
238        const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f));
239        const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
240
241        // For the ambient rrect, we inset the offset rect by half the srcSpaceAmbientRadius
242        // to get our stroke shape.
243        SkScalar ambientPathOutset = std::max(ambientOffset - srcSpaceAmbientRadius * 0.5f,
244                minRadius);
245
246        SkRRect ambientRRect;
247        const SkRect temp = casterRect.makeOutset(ambientPathOutset, ambientPathOutset);
248        if (isOval) {
249            ambientRRect = SkRRect::MakeOval(temp);
250        } else if (isRect) {
251            ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
252        } else {
253            ambientRRect = SkRRect::MakeRectXY(temp, casterCornerRadius + ambientPathOutset,
254                    casterCornerRadius + ambientPathOutset);
255        }
256
257        SkPaint paint;
258        paint.setAntiAlias(true);
259        paint.setStyle(SkPaint::kStroke_Style);
260        // we outset the stroke a little to cover up AA on the interior edge
261        float pad = 0.5f;
262        paint.setStrokeWidth(srcSpaceAmbientRadius + 2.0f * pad);
263        // handle scale of radius and pad due to CTM
264        pad *= scaleFactor;
265        const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
266        SkASSERT(devSpaceAmbientRadius <= MAX_BLUR_RADIUS);
267        SkASSERT(pad < MAX_PAD);
268        // convert devSpaceAmbientRadius to 14.2 fixed point and place in the R & G components
269        // convert pad to 6.2 fixed point and place in the B component
270        uint16_t iDevSpaceAmbientRadius = (uint16_t)(4.0f * devSpaceAmbientRadius);
271        paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, iDevSpaceAmbientRadius >> 8,
272                iDevSpaceAmbientRadius & 0xff, (unsigned char)(4.0f * pad)));
273
274        paint.setShader(edgeShader);
275        canvas->drawRRect(ambientRRect, paint);
276    }
277
278    if (spotAlpha > 0.0f) {
279        const Vector3 lightPos = SkiaPipeline::getLightCenter();
280        float zRatio = casterZValue / (lightPos.z - casterZValue);
281        // clamp
282        if (zRatio < 0.0f) {
283            zRatio = 0.0f;
284        } else if (zRatio > 0.95f) {
285            zRatio = 0.95f;
286        }
287
288        const SkScalar lightWidth = SkiaPipeline::getLightRadius();
289        SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
290        // the device-space radius sent to the blur shader must fit in 14.2 fixed point
291        if (srcSpaceSpotRadius*scaleFactor > MAX_BLUR_RADIUS) {
292            srcSpaceSpotRadius = MAX_BLUR_RADIUS/scaleFactor;
293        }
294
295        SkRRect spotRRect;
296        if (isOval) {
297            spotRRect = SkRRect::MakeOval(casterRect);
298        } else if (isRect) {
299            spotRRect = SkRRect::MakeRectXY(casterRect, minRadius, minRadius);
300        } else {
301            spotRRect = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius);
302        }
303
304        SkRRect spotShadowRRect;
305        // Compute the scale and translation for the spot shadow.
306        const SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
307        spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
308
309        SkPoint center = SkPoint::Make(spotShadowRRect.rect().centerX(),
310                                       spotShadowRRect.rect().centerY());
311        SkMatrix ctmInverse;
312        if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
313            ALOGW("Matrix is degenerate. Will not render spot shadow!");
314            return;
315        }
316        SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
317        ctmInverse.mapPoints(&lightPos2D, 1);
318        const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
319                zRatio*(center.fY - lightPos2D.fY));
320
321        SkAutoCanvasRestore acr(canvas, true);
322
323        // We want to extend the stroked area in so that it meets up with the caster
324        // geometry. The stroked geometry will, by definition already be inset half the
325        // stroke width but we also have to account for the scaling.
326        // We also add 1/2 to cover up AA on the interior edge.
327        SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(casterRect.fLeft),
328                SkTAbs(casterRect.fRight)), SkTMax(SkTAbs(casterRect.fTop),
329                SkTAbs(casterRect.fBottom)));
330        SkScalar insetAmount = spotOffset.length() - (0.5f * srcSpaceSpotRadius) +
331                scaleOffset + 0.5f;
332
333        // Compute area
334        SkScalar strokeWidth = srcSpaceSpotRadius + insetAmount;
335        SkScalar strokedArea = 2.0f*strokeWidth * (spotShadowRRect.width()
336                + spotShadowRRect.height());
337        SkScalar filledArea = (spotShadowRRect.height() + srcSpaceSpotRadius)
338                * (spotShadowRRect.width() + srcSpaceSpotRadius);
339
340        SkPaint paint;
341        paint.setAntiAlias(true);
342
343        // If the area of the stroked geometry is larger than the fill geometry, just fill it.
344        if (strokedArea > filledArea || casterAlpha < 1.0f || insetAmount < 0.0f) {
345            paint.setStyle(SkPaint::kStrokeAndFill_Style);
346            paint.setStrokeWidth(srcSpaceSpotRadius);
347        } else {
348            // Since we can't have unequal strokes, inset the shadow rect so the inner
349            // and outer edges of the stroke will land where we want.
350            SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount/2.0f, insetAmount/2.0f);
351            SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount/2.0f,
352                    minRadius);
353            spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
354            paint.setStyle(SkPaint::kStroke_Style);
355            paint.setStrokeWidth(strokeWidth);
356        }
357
358        // handle scale of radius and pad due to CTM
359        const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
360        SkASSERT(devSpaceSpotRadius <= MAX_BLUR_RADIUS);
361
362        const SkScalar devSpaceSpotPad = 0;
363        SkASSERT(devSpaceSpotPad < MAX_PAD);
364
365        // convert devSpaceSpotRadius to 14.2 fixed point and place in the R & G
366        // components convert devSpaceSpotPad to 6.2 fixed point and place in the B component
367        uint16_t iDevSpaceSpotRadius = (uint16_t)(4.0f * devSpaceSpotRadius);
368        paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, iDevSpaceSpotRadius >> 8,
369                iDevSpaceSpotRadius & 0xff, (unsigned char)(4.0f * devSpaceSpotPad)));
370        paint.setShader(edgeShader);
371
372        canvas->translate(spotOffset.fX, spotOffset.fY);
373        canvas->drawRRect(spotShadowRRect, paint);
374    }
375}
376
377/**
378 * @param casterRect         the rectangle bounds of the RRect casting the shadow
379 * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
380 * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
381 * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
382 * @param casterZValue       the Z value of the caster RRect
383 * @param scaleFactor        the scale needed to map from src-space to device-space
384 * @param clipRR             the oval or rect with which the drawn roundrect must be intersected
385 * @param canvas             the destination for the shadow draws
386 */
387static void DrawRRectShadowsWithClip(const SkRect& casterRect, SkScalar casterCornerRadius,
388        SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterZValue, SkScalar scaleFactor,
389        const SkRRect& clipRR, SkCanvas* canvas) {
390    SkASSERT(casterCornerRadius >= 0.0f);
391
392    const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()),
393            SkScalarHalf(casterRect.height()));
394
395    if (ambientAlpha > 0.0f) {
396        static const float kHeightFactor = 1.0f / 128.0f;
397        static const float kGeomFactor = 64.0f;
398
399        const SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor;
400        const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
401
402        const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f));
403        const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
404
405        const SkRect srcSpaceAmbientRect = casterRect.makeOutset(ambientOffset, ambientOffset);
406        SkRect devSpaceAmbientRect;
407        canvas->getTotalMatrix().mapRect(&devSpaceAmbientRect, srcSpaceAmbientRect);
408
409        SkRRect devSpaceAmbientRRect;
410        if (isOval) {
411            devSpaceAmbientRRect = SkRRect::MakeOval(devSpaceAmbientRect);
412        } else {
413            const SkScalar devSpaceCornerRadius = scaleFactor * (casterCornerRadius + ambientOffset);
414            devSpaceAmbientRRect = SkRRect::MakeRectXY(devSpaceAmbientRect, devSpaceCornerRadius,
415                    devSpaceCornerRadius);
416        }
417
418        const SkRect srcSpaceAmbClipRect = clipRR.rect().makeOutset(ambientOffset, ambientOffset);
419        SkRect devSpaceAmbClipRect;
420        canvas->getTotalMatrix().mapRect(&devSpaceAmbClipRect, srcSpaceAmbClipRect);
421        SkRRect devSpaceAmbientClipRR;
422        if (clipRR.isOval()) {
423            devSpaceAmbientClipRR = SkRRect::MakeOval(devSpaceAmbClipRect);
424        } else {
425            SkASSERT(clipRR.isRect());
426            devSpaceAmbientClipRR = SkRRect::MakeRect(devSpaceAmbClipRect);
427        }
428
429        SkRect cover = srcSpaceAmbClipRect;
430        if (!cover.intersect(srcSpaceAmbientRect)) {
431            return;
432        }
433
434        SkPaint paint;
435        paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, 0, 0, 0));
436        paint.setMaskFilter(SkRRectsGaussianEdgeMaskFilter::Make(devSpaceAmbientRRect,
437            devSpaceAmbientClipRR, devSpaceAmbientRadius));
438        canvas->drawRect(cover, paint);
439    }
440
441    if (spotAlpha > 0.0f) {
442        const Vector3 lightPos = SkiaPipeline::getLightCenter();
443        float zRatio = casterZValue / (lightPos.z - casterZValue);
444        // clamp
445        if (zRatio < 0.0f) {
446            zRatio = 0.0f;
447        } else if (zRatio > 0.95f) {
448            zRatio = 0.95f;
449        }
450
451        const SkScalar lightWidth = SkiaPipeline::getLightRadius();
452        const SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
453        const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
454
455        // Compute the scale and translation for the spot shadow.
456        const SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
457        const SkMatrix spotMatrix = SkMatrix::MakeScale(scale, scale);
458
459        SkRect srcSpaceScaledRect = casterRect;
460        spotMatrix.mapRect(&srcSpaceScaledRect);
461        srcSpaceScaledRect.outset(SkScalarHalf(srcSpaceSpotRadius),
462                SkScalarHalf(srcSpaceSpotRadius));
463
464        SkRRect srcSpaceSpotRRect;
465        if (isOval) {
466            srcSpaceSpotRRect = SkRRect::MakeOval(srcSpaceScaledRect);
467        } else {
468            srcSpaceSpotRRect = SkRRect::MakeRectXY(srcSpaceScaledRect, casterCornerRadius * scale,
469                    casterCornerRadius * scale);
470        }
471
472        SkPoint center = SkPoint::Make(srcSpaceSpotRRect.rect().centerX(),
473                srcSpaceSpotRRect.rect().centerY());
474        SkMatrix ctmInverse;
475        if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
476            ALOGW("Matrix is degenerate. Will not render spot shadow!");
477            return;
478        }
479        SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
480        ctmInverse.mapPoints(&lightPos2D, 1);
481        const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
482                zRatio*(center.fY - lightPos2D.fY));
483
484        SkAutoCanvasRestore acr(canvas, true);
485        canvas->translate(spotOffset.fX, spotOffset.fY);
486
487        SkRect devSpaceScaledRect;
488        canvas->getTotalMatrix().mapRect(&devSpaceScaledRect, srcSpaceScaledRect);
489
490        SkRRect devSpaceSpotRRect;
491        if (isOval) {
492            devSpaceSpotRRect = SkRRect::MakeOval(devSpaceScaledRect);
493        } else {
494            const SkScalar devSpaceScaledCornerRadius = casterCornerRadius * scale * scaleFactor;
495            devSpaceSpotRRect = SkRRect::MakeRectXY(devSpaceScaledRect, devSpaceScaledCornerRadius,
496                    devSpaceScaledCornerRadius);
497        }
498
499        SkPaint paint;
500        paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, 0, 0, 0));
501
502        SkRect srcSpaceScaledClipRect = clipRR.rect();
503        spotMatrix.mapRect(&srcSpaceScaledClipRect);
504        srcSpaceScaledClipRect.outset(SkScalarHalf(srcSpaceSpotRadius),
505                SkScalarHalf(srcSpaceSpotRadius));
506
507        SkRect devSpaceScaledClipRect;
508        canvas->getTotalMatrix().mapRect(&devSpaceScaledClipRect, srcSpaceScaledClipRect);
509        SkRRect devSpaceSpotClipRR;
510        if (clipRR.isOval()) {
511            devSpaceSpotClipRR = SkRRect::MakeOval(devSpaceScaledClipRect);
512        } else {
513            SkASSERT(clipRR.isRect());
514            devSpaceSpotClipRR = SkRRect::MakeRect(devSpaceScaledClipRect);
515        }
516
517        paint.setMaskFilter(SkRRectsGaussianEdgeMaskFilter::Make(devSpaceSpotRRect,
518            devSpaceSpotClipRR, devSpaceSpotRadius));
519
520        SkRect cover = srcSpaceScaledClipRect;
521        if (!cover.intersect(srcSpaceSpotRRect.rect())) {
522            return;
523        }
524
525        canvas->drawRect(cover, paint);
526    }
527}
528
529/**
530 * @param casterRect         the rectangle bounds of the RRect casting the shadow
531 * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
532 * @param casterClipRect     a rectangular clip that must be intersected with the
533 *                           shadow-casting RRect prior to casting the shadow
534 * @param revealClip         a circular clip that must be interested with the castClipRect
535 *                           and the shadow-casting rect prior to casting the shadow
536 * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
537 * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
538 * @param casterAlpha        the alpha value of the RRect casting the shadow (0.0-1.0 range)
539 * @param casterZValue       the Z value of the caster RRect
540 * @param canvas             the destination for the shadow draws
541 *
542 * We have special cases for 4 round rect shadow draws:
543 *    1) a RRect clipped by a reveal animation
544 *    2) a RRect clipped by a rectangle
545 *    3) an unclipped RRect with non-uniform scale
546 *    4) an unclipped RRect with uniform scale
547 * 1,2 and 4 require that the scale is uniform.
548 * 1 and 2 require that rects stay rects.
549 */
550static bool DrawShadowsAsRRects(const SkRect& casterRect, SkScalar casterCornerRadius,
551        const SkRect& casterClipRect, const RevealClip& revealClip, SkScalar ambientAlpha,
552        SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue, SkCanvas* canvas) {
553    SkScalar scaleFactors[2];
554    if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
555        ALOGW("Matrix is degenerate. Will not render shadow!");
556        return false;
557    }
558
559    // The casterClipRect will be empty when bounds clipping is disabled
560    bool casterIsClippedByRect = !casterClipRect.isEmpty();
561    bool uniformScale = scaleFactors[0] == scaleFactors[1];
562
563    if (revealClip.willClip()) {
564        if (casterIsClippedByRect || !uniformScale || !canvas->getTotalMatrix().rectStaysRect()) {
565            return false;  // Fall back to the slow path since PathOps are required
566        }
567
568        const float revealRadius = revealClip.getRadius();
569        SkRect revealClipRect = SkRect::MakeLTRB(revealClip.getX()-revealRadius,
570                revealClip.getY()-revealRadius, revealClip.getX()+revealRadius,
571                revealClip.getY()+revealRadius);
572        SkRRect revealClipRR = SkRRect::MakeOval(revealClipRect);
573
574        DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha,
575                casterZValue, scaleFactors[0], revealClipRR, canvas);
576        return true;
577    }
578
579    if (casterIsClippedByRect) {
580        if (!uniformScale || !canvas->getTotalMatrix().rectStaysRect()) {
581            return false;  // Fall back to the slow path since PathOps are required
582        }
583
584        SkRRect casterClipRR = SkRRect::MakeRect(casterClipRect);
585
586        DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha,
587                casterZValue, scaleFactors[0], casterClipRR, canvas);
588        return true;
589    }
590
591    // The fast path needs uniform scale
592    if (!uniformScale) {
593        SkRRect casterRR = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius);
594        DrawAmbientShadowGeneral(canvas, casterRR, casterZValue, ambientAlpha,
595                [&](const SkRRect& rrect, const SkPaint& paint) {
596                    canvas->drawRRect(rrect, paint);
597                });
598        DrawSpotShadowGeneral(canvas, casterRR, casterZValue, spotAlpha,
599                [&](const SkRRect& rrect, const SkPaint& paint) {
600                canvas->drawRRect(rrect, paint);
601                });
602        return true;
603    }
604
605    DrawRRectShadows(casterRect, casterCornerRadius, ambientAlpha, spotAlpha, casterAlpha,
606            casterZValue, scaleFactors[0], canvas);
607    return true;
608}
609
610// copied from FrameBuilder::deferShadow
611void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) {
612    const RenderProperties& casterProperties = caster->getNodeProperties();
613
614    if (casterProperties.getAlpha() <= 0.0f
615            || casterProperties.getOutline().getAlpha() <= 0.0f
616            || !casterProperties.getOutline().getPath()
617            || casterProperties.getScaleX() == 0
618            || casterProperties.getScaleY() == 0) {
619        // no shadow to draw
620        return;
621    }
622
623    const SkScalar casterAlpha = casterProperties.getAlpha()
624            * casterProperties.getOutline().getAlpha();
625    if (casterAlpha <= 0.0f) {
626        return;
627    }
628
629    float ambientAlpha = SkiaPipeline::getAmbientShadowAlpha()*casterAlpha;
630    float spotAlpha = SkiaPipeline::getSpotShadowAlpha()*casterAlpha;
631    const float casterZValue = casterProperties.getZ();
632
633    const RevealClip& revealClip = casterProperties.getRevealClip();
634    const SkPath* revealClipPath = revealClip.getPath();
635    if (revealClipPath && revealClipPath->isEmpty()) {
636        // An empty reveal clip means nothing is drawn
637        return;
638    }
639
640    bool clippedToBounds = casterProperties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS;
641
642    SkRect casterClipRect = SkRect::MakeEmpty();
643    if (clippedToBounds) {
644        Rect clipBounds;
645        casterProperties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
646        casterClipRect = clipBounds.toSkRect();
647        if (casterClipRect.isEmpty()) {
648            // An empty clip rect means nothing is drawn
649            return;
650        }
651    }
652
653    SkAutoCanvasRestore acr(canvas, true);
654
655    SkMatrix shadowMatrix;
656    mat4 hwuiMatrix(caster->getRecordedMatrix());
657    // TODO we don't pass the optional boolean to treat it as a 4x4 matrix
658    caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix);
659    hwuiMatrix.copyTo(shadowMatrix);
660    canvas->concat(shadowMatrix);
661
662    const Outline& casterOutline = casterProperties.getOutline();
663    Rect possibleRect;
664    float radius;
665    if (casterOutline.getAsRoundRect(&possibleRect, &radius)) {
666        if (DrawShadowsAsRRects(possibleRect.toSkRect(), radius, casterClipRect, revealClip,
667                ambientAlpha, spotAlpha, casterAlpha, casterZValue, canvas)) {
668            return;
669        }
670    }
671
672    // Hard cases and calls to general shadow code
673    const SkPath* casterOutlinePath = casterProperties.getOutline().getPath();
674
675    // holds temporary SkPath to store the result of intersections
676    SkPath tmpPath;
677    const SkPath* casterPath = casterOutlinePath;
678
679    // TODO: In to following course of code that calculates the final shape, is there an optimal
680    //       of doing the Op calculations?
681    // intersect the shadow-casting path with the reveal, if present
682    if (revealClipPath) {
683        Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath);
684        casterPath = &tmpPath;
685    }
686
687    // intersect the shadow-casting path with the clipBounds, if present
688    if (clippedToBounds) {
689        SkPath clipBoundsPath;
690        clipBoundsPath.addRect(casterClipRect);
691        Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, &tmpPath);
692        casterPath = &tmpPath;
693    }
694
695    DrawAmbientShadowGeneral(canvas, *casterPath, casterZValue, ambientAlpha,
696            [&](const SkPath& path, const SkPaint& paint) {
697                canvas->drawPath(path, paint);
698            });
699
700    DrawSpotShadowGeneral(canvas, *casterPath, casterZValue, spotAlpha,
701            [&](const SkPath& path, const SkPaint& paint) {
702                canvas->drawPath(path, paint);
703            });
704}
705
706}; // namespace skiapipeline
707}; // namespace uirenderer
708}; // namespace android
709