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