Home | History | Annotate | Download | only in hwui
      1 /*
      2  * Copyright (C) 2014 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 "DamageAccumulator.h"
     18 
     19 #include <cutils/log.h>
     20 
     21 #include "RenderNode.h"
     22 #include "utils/MathUtils.h"
     23 
     24 namespace android {
     25 namespace uirenderer {
     26 
     27 enum TransformType {
     28     TransformInvalid = 0,
     29     TransformRenderNode,
     30     TransformMatrix4,
     31     TransformNone,
     32 };
     33 
     34 struct DirtyStack {
     35     TransformType type;
     36     union {
     37         const RenderNode* renderNode;
     38         const Matrix4* matrix4;
     39     };
     40     // When this frame is pop'd, this rect is mapped through the above transform
     41     // and applied to the previous (aka parent) frame
     42     SkRect pendingDirty;
     43     DirtyStack* prev;
     44     DirtyStack* next;
     45 };
     46 
     47 DamageAccumulator::DamageAccumulator() {
     48     mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
     49     memset(mHead, 0, sizeof(DirtyStack));
     50     // Create a root that we will not pop off
     51     mHead->prev = mHead;
     52     mHead->type = TransformNone;
     53 }
     54 
     55 static void computeTransformImpl(const DirtyStack* currentFrame, Matrix4* outMatrix) {
     56     if (currentFrame->prev != currentFrame) {
     57         computeTransformImpl(currentFrame->prev, outMatrix);
     58     }
     59     switch (currentFrame->type) {
     60     case TransformRenderNode:
     61         currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix);
     62         break;
     63     case TransformMatrix4:
     64         outMatrix->multiply(*currentFrame->matrix4);
     65         break;
     66     case TransformNone:
     67         // nothing to be done
     68         break;
     69     default:
     70         LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d", currentFrame->type);
     71     }
     72 }
     73 
     74 void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const {
     75     outMatrix->loadIdentity();
     76     computeTransformImpl(mHead, outMatrix);
     77 }
     78 
     79 void DamageAccumulator::pushCommon() {
     80     if (!mHead->next) {
     81         DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
     82         nextFrame->next = 0;
     83         nextFrame->prev = mHead;
     84         mHead->next = nextFrame;
     85     }
     86     mHead = mHead->next;
     87     mHead->pendingDirty.setEmpty();
     88 }
     89 
     90 void DamageAccumulator::pushTransform(const RenderNode* transform) {
     91     pushCommon();
     92     mHead->type = TransformRenderNode;
     93     mHead->renderNode = transform;
     94 }
     95 
     96 void DamageAccumulator::pushTransform(const Matrix4* transform) {
     97     pushCommon();
     98     mHead->type = TransformMatrix4;
     99     mHead->matrix4 = transform;
    100 }
    101 
    102 void DamageAccumulator::popTransform() {
    103     LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
    104     DirtyStack* dirtyFrame = mHead;
    105     mHead = mHead->prev;
    106     switch (dirtyFrame->type) {
    107     case TransformRenderNode:
    108         applyRenderNodeTransform(dirtyFrame);
    109         break;
    110     case TransformMatrix4:
    111         applyMatrix4Transform(dirtyFrame);
    112         break;
    113     case TransformNone:
    114         mHead->pendingDirty.join(dirtyFrame->pendingDirty);
    115         break;
    116     default:
    117         LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
    118     }
    119 }
    120 
    121 static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
    122     if (in.isEmpty()) return;
    123     Rect temp(in);
    124     matrix->mapRect(temp);
    125     out->join(RECT_ARGS(temp));
    126 }
    127 
    128 void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
    129     mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
    130 }
    131 
    132 static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
    133     if (in.isEmpty()) return;
    134     const SkMatrix* transform = props.getTransformMatrix();
    135     SkRect temp(in);
    136     if (transform && !transform->isIdentity()) {
    137         transform->mapRect(&temp);
    138     }
    139     temp.offset(props.getLeft(), props.getTop());
    140     out->join(temp);
    141 }
    142 
    143 static DirtyStack* findParentRenderNode(DirtyStack* frame) {
    144     while (frame->prev != frame) {
    145         frame = frame->prev;
    146         if (frame->type == TransformRenderNode) {
    147             return frame;
    148         }
    149     }
    150     return NULL;
    151 }
    152 
    153 static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
    154     if (frame) {
    155         while (frame->prev != frame) {
    156             frame = frame->prev;
    157             if (frame->type == TransformRenderNode
    158                     && frame->renderNode->hasProjectionReceiver()) {
    159                 return frame;
    160             }
    161         }
    162     }
    163     return NULL;
    164 }
    165 
    166 static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
    167     SkRect* rect = &frame->pendingDirty;
    168     while (frame != end) {
    169         if (frame->type == TransformRenderNode) {
    170             mapRect(frame->renderNode->properties(), *rect, rect);
    171         } else {
    172             mapRect(frame->matrix4, *rect, rect);
    173         }
    174         frame = frame->prev;
    175     }
    176 }
    177 
    178 void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
    179     if (frame->pendingDirty.isEmpty()) {
    180         return;
    181     }
    182 
    183     const RenderProperties& props = frame->renderNode->properties();
    184     if (props.getAlpha() <= 0) {
    185         return;
    186     }
    187 
    188     // Perform clipping
    189     if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
    190         if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
    191             frame->pendingDirty.setEmpty();
    192         }
    193     }
    194 
    195     // apply all transforms
    196     mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
    197 
    198     // project backwards if necessary
    199     if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
    200         // First, find our parent RenderNode:
    201         DirtyStack* parentNode = findParentRenderNode(frame);
    202         // Find our parent's projection receiver, which is what we project onto
    203         DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
    204         if (projectionReceiver) {
    205             applyTransforms(frame, projectionReceiver);
    206             projectionReceiver->pendingDirty.join(frame->pendingDirty);
    207         }
    208 
    209         frame->pendingDirty.setEmpty();
    210     }
    211 }
    212 
    213 void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
    214     mHead->pendingDirty.join(left, top, right, bottom);
    215 }
    216 
    217 void DamageAccumulator::peekAtDirty(SkRect* dest) const {
    218     *dest = mHead->pendingDirty;
    219 }
    220 
    221 void DamageAccumulator::finish(SkRect* totalDirty) {
    222     LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead);
    223     // Root node never has a transform, so this is the fully mapped dirty rect
    224     *totalDirty = mHead->pendingDirty;
    225     totalDirty->roundOut();
    226     mHead->pendingDirty.setEmpty();
    227 }
    228 
    229 } /* namespace uirenderer */
    230 } /* namespace android */
    231