DamageAccumulator.cpp revision 69e5adffb19135d51bde8e458f4907d7265f3e23
11a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien/* 21a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * Copyright (C) 2014 The Android Open Source Project 31a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * 41a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * Licensed under the Apache License, Version 2.0 (the "License"); 51a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * you may not use this file except in compliance with the License. 61a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * You may obtain a copy of the License at 71a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * 81a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * http://www.apache.org/licenses/LICENSE-2.0 91a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * 101a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * Unless required by applicable law or agreed to in writing, software 111a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * distributed under the License is distributed on an "AS IS" BASIS, 121a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * See the License for the specific language governing permissions and 141a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * limitations under the License. 151a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien */ 161a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien 171a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien#include "DamageAccumulator.h" 181a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien 191a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien#include <cutils/log.h> 201a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien 211a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien#include "RenderNode.h" 221a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien#include "utils/MathUtils.h" 231a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien 241a73f732f91e97c9c66b808c245ddda36a10e987Raph Leviennamespace android { 251a73f732f91e97c9c66b808c245ddda36a10e987Raph Leviennamespace uirenderer { 261a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien 271a73f732f91e97c9c66b808c245ddda36a10e987Raph Levienenum TransformType { 281a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien TransformInvalid = 0, 29e95b5850ac2e56330abf68362451e7614b3dfe16Raph Levien TransformRenderNode, 30e95b5850ac2e56330abf68362451e7614b3dfe16Raph Levien TransformMatrix4, 31e95b5850ac2e56330abf68362451e7614b3dfe16Raph Levien TransformNone, 321a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien}; 331a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien 346bfd7e7df3a9f9ea2f9e57a3552eed7f518be711Raph Levienstruct DirtyStack { 356bfd7e7df3a9f9ea2f9e57a3552eed7f518be711Raph Levien TransformType type; 36e95b5850ac2e56330abf68362451e7614b3dfe16Raph Levien union { 37f2114d5646194100242206b267ddd6e7194b7da9Raph Levien const RenderNode* renderNode; 38f2114d5646194100242206b267ddd6e7194b7da9Raph Levien const Matrix4* matrix4; 391fc0fa87d42ce9268ece76b85b9edc834593e53aRaph Levien }; 40f2114d5646194100242206b267ddd6e7194b7da9Raph Levien // When this frame is pop'd, this rect is mapped through the above transform 411fc0fa87d42ce9268ece76b85b9edc834593e53aRaph Levien // and applied to the previous (aka parent) frame 421fc0fa87d42ce9268ece76b85b9edc834593e53aRaph Levien SkRect pendingDirty; 43f2114d5646194100242206b267ddd6e7194b7da9Raph Levien DirtyStack* prev; 44f2114d5646194100242206b267ddd6e7194b7da9Raph Levien DirtyStack* next; 45f2114d5646194100242206b267ddd6e7194b7da9Raph Levien}; 461fc0fa87d42ce9268ece76b85b9edc834593e53aRaph Levien 471fc0fa87d42ce9268ece76b85b9edc834593e53aRaph LevienDamageAccumulator::DamageAccumulator() { 481fc0fa87d42ce9268ece76b85b9edc834593e53aRaph Levien mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack)); 491fc0fa87d42ce9268ece76b85b9edc834593e53aRaph Levien memset(mHead, 0, sizeof(DirtyStack)); 50f2114d5646194100242206b267ddd6e7194b7da9Raph Levien // Create a root that we will not pop off 51f2114d5646194100242206b267ddd6e7194b7da9Raph Levien mHead->prev = mHead; 521fc0fa87d42ce9268ece76b85b9edc834593e53aRaph Levien mHead->type = TransformNone; 53f2114d5646194100242206b267ddd6e7194b7da9Raph Levien} 54f2114d5646194100242206b267ddd6e7194b7da9Raph Levien 551fc0fa87d42ce9268ece76b85b9edc834593e53aRaph Levienstatic void computeTransformImpl(const DirtyStack* currentFrame, Matrix4* outMatrix) { 561fc0fa87d42ce9268ece76b85b9edc834593e53aRaph Levien if (currentFrame->prev != currentFrame) { 57f2114d5646194100242206b267ddd6e7194b7da9Raph Levien computeTransformImpl(currentFrame->prev, outMatrix); 58f2114d5646194100242206b267ddd6e7194b7da9Raph Levien } 591a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien switch (currentFrame->type) { 601a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien case TransformRenderNode: 611a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix); 621a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien break; 631a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien 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 74void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const { 75 outMatrix->loadIdentity(); 76 computeTransformImpl(mHead, outMatrix); 77} 78 79void 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 90void DamageAccumulator::pushTransform(const RenderNode* transform) { 91 pushCommon(); 92 mHead->type = TransformRenderNode; 93 mHead->renderNode = transform; 94} 95 96void DamageAccumulator::pushTransform(const Matrix4* transform) { 97 pushCommon(); 98 mHead->type = TransformMatrix4; 99 mHead->matrix4 = transform; 100} 101 102void 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 121static 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 128void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) { 129 mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty); 130} 131 132static 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 143static 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 153static 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 166static 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 178void 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 213void DamageAccumulator::dirty(float left, float top, float right, float bottom) { 214 mHead->pendingDirty.join(left, top, right, bottom); 215} 216 217void DamageAccumulator::peekAtDirty(SkRect* dest) { 218 *dest = mHead->pendingDirty; 219} 220 221void 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