69e5adffb1
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
231 lines
6.8 KiB
C++
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 */
|