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 "RenderNodeDrawable.h"
     18 #include <SkPaintFilterCanvas.h>
     19 #include "RenderNode.h"
     20 #include "SkiaDisplayList.h"
     21 #include "SkiaPipeline.h"
     22 #include "utils/TraceUtils.h"
     23 
     24 namespace android {
     25 namespace uirenderer {
     26 namespace skiapipeline {
     27 
     28 void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas,
     29                                                      const SkiaDisplayList& displayList,
     30                                                      int nestLevel) {
     31     LOG_ALWAYS_FATAL_IF(0 == nestLevel && !displayList.mProjectionReceiver);
     32     for (auto& child : displayList.mChildNodes) {
     33         const RenderProperties& childProperties = child.getNodeProperties();
     34 
     35         // immediate children cannot be projected on their parent
     36         if (childProperties.getProjectBackwards() && nestLevel > 0) {
     37             SkAutoCanvasRestore acr2(canvas, true);
     38             // Apply recorded matrix, which is a total matrix saved at recording time to avoid
     39             // replaying all DL commands.
     40             canvas->concat(child.getRecordedMatrix());
     41             child.drawContent(canvas);
     42         }
     43 
     44         // skip walking sub-nodes if current display list contains a receiver with exception of
     45         // level 0, which is a known receiver
     46         if (0 == nestLevel || !displayList.containsProjectionReceiver()) {
     47             SkAutoCanvasRestore acr(canvas, true);
     48             SkMatrix nodeMatrix;
     49             mat4 hwuiMatrix(child.getRecordedMatrix());
     50             auto childNode = child.getRenderNode();
     51             childNode->applyViewPropertyTransforms(hwuiMatrix);
     52             hwuiMatrix.copyTo(nodeMatrix);
     53             canvas->concat(nodeMatrix);
     54             SkiaDisplayList* childDisplayList = static_cast<SkiaDisplayList*>(
     55                     (const_cast<DisplayList*>(childNode->getDisplayList())));
     56             if (childDisplayList) {
     57                 drawBackwardsProjectedNodes(canvas, *childDisplayList, nestLevel + 1);
     58             }
     59         }
     60     }
     61 }
     62 
     63 static void clipOutline(const Outline& outline, SkCanvas* canvas, const SkRect* pendingClip) {
     64     Rect possibleRect;
     65     float radius;
     66 
     67     /* To match the existing HWUI behavior we only supports rectangles or
     68      * rounded rectangles; passing in a more complicated outline fails silently.
     69      */
     70     if (!outline.getAsRoundRect(&possibleRect, &radius)) {
     71         if (pendingClip) {
     72             canvas->clipRect(*pendingClip);
     73         }
     74         return;
     75     }
     76 
     77     SkRect rect = possibleRect.toSkRect();
     78     if (radius != 0.0f) {
     79         if (pendingClip && !pendingClip->contains(rect)) {
     80             canvas->clipRect(*pendingClip);
     81         }
     82         canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), SkClipOp::kIntersect, true);
     83     } else {
     84         if (pendingClip) {
     85             (void)rect.intersect(*pendingClip);
     86         }
     87         canvas->clipRect(rect);
     88     }
     89 }
     90 
     91 const RenderProperties& RenderNodeDrawable::getNodeProperties() const {
     92     return mRenderNode->properties();
     93 }
     94 
     95 void RenderNodeDrawable::onDraw(SkCanvas* canvas) {
     96     // negative and positive Z order are drawn out of order, if this render node drawable is in
     97     // a reordering section
     98     if ((!mInReorderingSection) || MathUtils::isZero(mRenderNode->properties().getZ())) {
     99         this->forceDraw(canvas);
    100     }
    101 }
    102 
    103 void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
    104     RenderNode* renderNode = mRenderNode.get();
    105     if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
    106         SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
    107         canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
    108     }
    109 
    110     // We only respect the nothingToDraw check when we are composing a layer. This
    111     // ensures that we paint the layer even if it is not currently visible in the
    112     // event that the properties change and it becomes visible.
    113     if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) ||
    114             (renderNode->nothingToDraw() && mComposeLayer)) {
    115         return;
    116     }
    117 
    118     SkASSERT(renderNode->getDisplayList()->isSkiaDL());
    119     SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
    120 
    121     SkAutoCanvasRestore acr(canvas, true);
    122     const RenderProperties& properties = this->getNodeProperties();
    123     // pass this outline to the children that may clip backward projected nodes
    124     displayList->mProjectedOutline =
    125             displayList->containsProjectionReceiver() ? &properties.getOutline() : nullptr;
    126     if (!properties.getProjectBackwards()) {
    127         drawContent(canvas);
    128         if (mProjectedDisplayList) {
    129             acr.restore();  // draw projected children using parent matrix
    130             LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline);
    131             const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath();
    132             SkAutoCanvasRestore acr2(canvas, shouldClip);
    133             canvas->setMatrix(mProjectedDisplayList->mProjectedReceiverParentMatrix);
    134             if (shouldClip) {
    135                 clipOutline(*mProjectedDisplayList->mProjectedOutline, canvas, nullptr);
    136             }
    137             drawBackwardsProjectedNodes(canvas, *mProjectedDisplayList);
    138         }
    139     }
    140     displayList->mProjectedOutline = nullptr;
    141 }
    142 
    143 static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier,
    144                             SkPaint* paint) {
    145     paint->setFilterQuality(kLow_SkFilterQuality);
    146     if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
    147         properties.xferMode() != SkBlendMode::kSrcOver || properties.colorFilter() != nullptr) {
    148         paint->setAlpha(properties.alpha() * alphaMultiplier);
    149         paint->setBlendMode(properties.xferMode());
    150         paint->setColorFilter(sk_ref_sp(properties.colorFilter()));
    151         return true;
    152     }
    153     return false;
    154 }
    155 
    156 class AlphaFilterCanvas : public SkPaintFilterCanvas {
    157 public:
    158     AlphaFilterCanvas(SkCanvas* canvas, float alpha) : SkPaintFilterCanvas(canvas), mAlpha(alpha) {}
    159 
    160 protected:
    161     bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type t) const override {
    162         SkTLazy<SkPaint> defaultPaint;
    163         if (!*paint) {
    164             paint->init(*defaultPaint.init());
    165         }
    166         paint->writable()->setAlpha((uint8_t)(*paint)->getAlpha() * mAlpha);
    167         return true;
    168     }
    169     void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
    170         // We unroll the drawable using "this" canvas, so that draw calls contained inside will
    171         // get their alpha applied. THe default SkPaintFilterCanvas::onDrawDrawable does not unroll.
    172         drawable->draw(this, matrix);
    173     }
    174 
    175 private:
    176     float mAlpha;
    177 };
    178 
    179 void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
    180     RenderNode* renderNode = mRenderNode.get();
    181     float alphaMultiplier = 1.0f;
    182     const RenderProperties& properties = renderNode->properties();
    183 
    184     // If we are drawing the contents of layer, we don't want to apply any of
    185     // the RenderNode's properties during this pass. Those will all be applied
    186     // when the layer is composited.
    187     if (mComposeLayer) {
    188         setViewProperties(properties, canvas, &alphaMultiplier);
    189     }
    190     SkiaDisplayList* displayList = (SkiaDisplayList*)mRenderNode->getDisplayList();
    191     if (displayList->containsProjectionReceiver()) {
    192         displayList->mProjectedReceiverParentMatrix = canvas->getTotalMatrix();
    193     }
    194 
    195     // TODO should we let the bound of the drawable do this for us?
    196     const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
    197     bool quickRejected = properties.getClipToBounds() && canvas->quickReject(bounds);
    198     if (!quickRejected) {
    199         SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
    200         const LayerProperties& layerProperties = properties.layerProperties();
    201         // composing a hardware layer
    202         if (renderNode->getLayerSurface() && mComposeLayer) {
    203             SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
    204             SkPaint paint;
    205             layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
    206 
    207             // surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so
    208             // we need to restrict the portion of the surface drawn to the size of the renderNode.
    209             SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
    210             SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
    211             canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(),
    212                     bounds, bounds, &paint);
    213 
    214             if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
    215                 renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
    216                 if (CC_UNLIKELY(Properties::debugLayersUpdates)) {
    217                     SkPaint layerPaint;
    218                     layerPaint.setColor(0x7f00ff00);
    219                     canvas->drawRect(bounds, layerPaint);
    220                 } else if (CC_UNLIKELY(Properties::debugOverdraw)) {
    221                     // Render transparent rect to increment overdraw for repaint area.
    222                     // This can be "else if" because flashing green on layer updates
    223                     // will also increment the overdraw if it happens to be turned on.
    224                     SkPaint transparentPaint;
    225                     transparentPaint.setColor(SK_ColorTRANSPARENT);
    226                     canvas->drawRect(bounds, transparentPaint);
    227                 }
    228             }
    229         } else {
    230             if (alphaMultiplier < 1.0f) {
    231                 // Non-layer draw for a view with getHasOverlappingRendering=false, will apply
    232                 // the alpha to the paint of each nested draw.
    233                 AlphaFilterCanvas alphaCanvas(canvas, alphaMultiplier);
    234                 displayList->draw(&alphaCanvas);
    235             } else {
    236                 displayList->draw(canvas);
    237             }
    238         }
    239     }
    240 }
    241 
    242 void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
    243                                            float* alphaMultiplier) {
    244     if (properties.getLeft() != 0 || properties.getTop() != 0) {
    245         canvas->translate(properties.getLeft(), properties.getTop());
    246     }
    247     if (properties.getStaticMatrix()) {
    248         canvas->concat(*properties.getStaticMatrix());
    249     } else if (properties.getAnimationMatrix()) {
    250         canvas->concat(*properties.getAnimationMatrix());
    251     }
    252     if (properties.hasTransformMatrix()) {
    253         if (properties.isTransformTranslateOnly()) {
    254             canvas->translate(properties.getTranslationX(), properties.getTranslationY());
    255         } else {
    256             canvas->concat(*properties.getTransformMatrix());
    257         }
    258     }
    259     const bool isLayer = properties.effectiveLayerType() != LayerType::None;
    260     int clipFlags = properties.getClippingFlags();
    261     if (properties.getAlpha() < 1) {
    262         if (isLayer) {
    263             clipFlags &= ~CLIP_TO_BOUNDS;  // bounds clipping done by layer
    264         }
    265         if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
    266             *alphaMultiplier = properties.getAlpha();
    267         } else {
    268             // savelayer needed to create an offscreen buffer
    269             Rect layerBounds(0, 0, properties.getWidth(), properties.getHeight());
    270             if (clipFlags) {
    271                 properties.getClippingRectForFlags(clipFlags, &layerBounds);
    272                 clipFlags = 0;  // all clipping done by savelayer
    273             }
    274             SkRect bounds = SkRect::MakeLTRB(layerBounds.left, layerBounds.top, layerBounds.right,
    275                                              layerBounds.bottom);
    276             canvas->saveLayerAlpha(&bounds, (int)(properties.getAlpha() * 255));
    277         }
    278 
    279         if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) {
    280             // pretend alpha always causes savelayer to warn about
    281             // performance problem affecting old versions
    282             ATRACE_FORMAT("alpha caused saveLayer %dx%d", properties.getWidth(),
    283                           properties.getHeight());
    284         }
    285     }
    286 
    287     const SkRect* pendingClip = nullptr;
    288     SkRect clipRect;
    289 
    290     if (clipFlags) {
    291         Rect tmpRect;
    292         properties.getClippingRectForFlags(clipFlags, &tmpRect);
    293         clipRect = tmpRect.toSkRect();
    294         pendingClip = &clipRect;
    295     }
    296 
    297     if (properties.getRevealClip().willClip()) {
    298         canvas->clipPath(*properties.getRevealClip().getPath(), SkClipOp::kIntersect, true);
    299     } else if (properties.getOutline().willClip()) {
    300         clipOutline(properties.getOutline(), canvas, pendingClip);
    301         pendingClip = nullptr;
    302     }
    303 
    304     if (pendingClip) {
    305         canvas->clipRect(*pendingClip);
    306     }
    307 }
    308 
    309 };  // namespace skiapipeline
    310 };  // namespace uirenderer
    311 };  // namespace android
    312