android_frameworks_base/libs/hwui/DamageAccumulator.cpp
Chris Craik 69e5adffb1 Define shadow casting behavior within layers
bug:15860114

Savelayers and HW layers both now support shadow casting.

For save layers, the light source should always be correct, for HW
layers, the light source position is set when the layer is created,
and updated when it is resized.

Change-Id: Ie85567dd43c2bb0a0b08fd0bd4db41efa793ac2b
2014-08-15 00:59:44 +00:00

231 lines
6.8 KiB
C++

/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "DamageAccumulator.h"
#include <cutils/log.h>
#include "RenderNode.h"
#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
enum TransformType {
TransformInvalid = 0,
TransformRenderNode,
TransformMatrix4,
TransformNone,
};
struct DirtyStack {
TransformType type;
union {
const RenderNode* renderNode;
const Matrix4* matrix4;
};
// When this frame is pop'd, this rect is mapped through the above transform
// and applied to the previous (aka parent) frame
SkRect pendingDirty;
DirtyStack* prev;
DirtyStack* next;
};
DamageAccumulator::DamageAccumulator() {
mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
memset(mHead, 0, sizeof(DirtyStack));
// Create a root that we will not pop off
mHead->prev = mHead;
mHead->type = TransformNone;
}
static void computeTransformImpl(const DirtyStack* currentFrame, Matrix4* outMatrix) {
if (currentFrame->prev != currentFrame) {
computeTransformImpl(currentFrame->prev, outMatrix);
}
switch (currentFrame->type) {
case TransformRenderNode:
currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix);
break;
case TransformMatrix4:
outMatrix->multiply(*currentFrame->matrix4);
break;
case TransformNone:
// nothing to be done
break;
default:
LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d", currentFrame->type);
}
}
void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const {
outMatrix->loadIdentity();
computeTransformImpl(mHead, outMatrix);
}
void DamageAccumulator::pushCommon() {
if (!mHead->next) {
DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
nextFrame->next = 0;
nextFrame->prev = mHead;
mHead->next = nextFrame;
}
mHead = mHead->next;
mHead->pendingDirty.setEmpty();
}
void DamageAccumulator::pushTransform(const RenderNode* transform) {
pushCommon();
mHead->type = TransformRenderNode;
mHead->renderNode = transform;
}
void DamageAccumulator::pushTransform(const Matrix4* transform) {
pushCommon();
mHead->type = TransformMatrix4;
mHead->matrix4 = transform;
}
void DamageAccumulator::popTransform() {
LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
DirtyStack* dirtyFrame = mHead;
mHead = mHead->prev;
switch (dirtyFrame->type) {
case TransformRenderNode:
applyRenderNodeTransform(dirtyFrame);
break;
case TransformMatrix4:
applyMatrix4Transform(dirtyFrame);
break;
case TransformNone:
mHead->pendingDirty.join(dirtyFrame->pendingDirty);
break;
default:
LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
}
}
static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
if (in.isEmpty()) return;
Rect temp(in);
matrix->mapRect(temp);
out->join(RECT_ARGS(temp));
}
void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
}
static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
if (in.isEmpty()) return;
const SkMatrix* transform = props.getTransformMatrix();
SkRect temp(in);
if (transform && !transform->isIdentity()) {
transform->mapRect(&temp);
}
temp.offset(props.getLeft(), props.getTop());
out->join(temp);
}
static DirtyStack* findParentRenderNode(DirtyStack* frame) {
while (frame->prev != frame) {
frame = frame->prev;
if (frame->type == TransformRenderNode) {
return frame;
}
}
return NULL;
}
static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
if (frame) {
while (frame->prev != frame) {
frame = frame->prev;
if (frame->type == TransformRenderNode
&& frame->renderNode->hasProjectionReceiver()) {
return frame;
}
}
}
return NULL;
}
static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
SkRect* rect = &frame->pendingDirty;
while (frame != end) {
if (frame->type == TransformRenderNode) {
mapRect(frame->renderNode->properties(), *rect, rect);
} else {
mapRect(frame->matrix4, *rect, rect);
}
frame = frame->prev;
}
}
void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
if (frame->pendingDirty.isEmpty()) {
return;
}
const RenderProperties& props = frame->renderNode->properties();
if (props.getAlpha() <= 0) {
return;
}
// Perform clipping
if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
frame->pendingDirty.setEmpty();
}
}
// apply all transforms
mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
// project backwards if necessary
if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
// First, find our parent RenderNode:
DirtyStack* parentNode = findParentRenderNode(frame);
// Find our parent's projection receiver, which is what we project onto
DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
if (projectionReceiver) {
applyTransforms(frame, projectionReceiver);
projectionReceiver->pendingDirty.join(frame->pendingDirty);
}
frame->pendingDirty.setEmpty();
}
}
void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
mHead->pendingDirty.join(left, top, right, bottom);
}
void DamageAccumulator::peekAtDirty(SkRect* dest) {
*dest = mHead->pendingDirty;
}
void DamageAccumulator::finish(SkRect* totalDirty) {
LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead);
// Root node never has a transform, so this is the fully mapped dirty rect
*totalDirty = mHead->pendingDirty;
totalDirty->roundOut();
mHead->pendingDirty.setEmpty();
}
} /* namespace uirenderer */
} /* namespace android */