blob: d6ba0a1d5e0d6386dd7ac3b196984cd37f2fdcfa [file] [log] [blame]
#include "config.h"
#include "LayerAndroid.h"
#if USE(ACCELERATED_COMPOSITING)
#include "AndroidAnimation.h"
#include "DrawExtra.h"
#include "SkCanvas.h"
#include "SkDrawFilter.h"
#include "SkPaint.h"
#include "SkPicture.h"
#include <wtf/CurrentTime.h>
#define LAYER_DEBUG // Add diagonals for debugging
#undef LAYER_DEBUG
namespace WebCore {
static int gDebugLayerAndroidInstances;
static int gUniqueId;
inline int LayerAndroid::instancesCount()
{
return gDebugLayerAndroidInstances;
}
class OpacityDrawFilter : public SkDrawFilter {
public:
OpacityDrawFilter(int opacity) : m_opacity(opacity) { }
virtual bool filter(SkCanvas* canvas, SkPaint* paint, Type)
{
m_previousOpacity = paint->getAlpha();
paint->setAlpha(m_opacity);
return true;
}
virtual void restore(SkCanvas* canvas, SkPaint* paint, Type)
{
paint->setAlpha(m_previousOpacity);
}
private:
int m_opacity;
int m_previousOpacity;
};
///////////////////////////////////////////////////////////////////////////////
LayerAndroid::LayerAndroid(bool isRootLayer) : SkLayer(),
m_isRootLayer(isRootLayer),
m_haveClip(false),
m_doRotation(false),
m_isFixed(false),
m_recordingPicture(0),
m_extra(0),
m_uniqueId(++gUniqueId)
{
m_angleTransform = 0;
m_translation.set(0, 0);
m_scale.set(1, 1);
m_backgroundColor = 0;
gDebugLayerAndroidInstances++;
}
LayerAndroid::LayerAndroid(const LayerAndroid& layer) : SkLayer(layer),
m_isRootLayer(layer.m_isRootLayer),
m_haveClip(layer.m_haveClip),
m_extra(0), // deliberately not copied
m_uniqueId(layer.m_uniqueId)
{
m_doRotation = layer.m_doRotation;
m_isFixed = layer.m_isFixed;
m_angleTransform = layer.m_angleTransform;
m_translation = layer.m_translation;
m_scale = layer.m_scale;
m_backgroundColor = layer.m_backgroundColor;
m_fixedLeft = layer.m_fixedLeft;
m_fixedTop = layer.m_fixedTop;
m_fixedRight = layer.m_fixedRight;
m_fixedBottom = layer.m_fixedBottom;
m_fixedMarginLeft = layer.m_fixedMarginLeft;
m_fixedMarginTop = layer.m_fixedMarginTop;
m_fixedMarginRight = layer.m_fixedMarginRight;
m_fixedMarginBottom = layer.m_fixedMarginBottom;
m_fixedOffset = layer.m_fixedOffset;
m_fixedWidth = layer.m_fixedWidth;
m_fixedHeight = layer.m_fixedHeight;
m_recordingPicture = layer.m_recordingPicture;
SkSafeRef(m_recordingPicture);
for (int i = 0; i < layer.countChildren(); i++)
addChild(new LayerAndroid(*layer.getChild(i)))->unref();
KeyframesMap::const_iterator end = layer.m_animations.end();
for (KeyframesMap::const_iterator it = layer.m_animations.begin(); it != end; ++it)
m_animations.add((it->second)->name(), (it->second)->copy());
gDebugLayerAndroidInstances++;
}
LayerAndroid::LayerAndroid(SkPicture* picture) : SkLayer(),
m_isRootLayer(true),
m_haveClip(false),
m_doRotation(false),
m_isFixed(false),
m_recordingPicture(picture),
m_extra(0),
m_uniqueId(-1)
{
m_angleTransform = 0;
m_translation.set(0, 0);
m_scale.set(1, 1);
m_backgroundColor = 0;
SkSafeRef(m_recordingPicture);
gDebugLayerAndroidInstances++;
}
LayerAndroid::~LayerAndroid()
{
removeChildren();
m_recordingPicture->safeUnref();
m_animations.clear();
gDebugLayerAndroidInstances--;
}
static int gDebugNbAnims = 0;
bool LayerAndroid::evaluateAnimations() const
{
double time = WTF::currentTime();
gDebugNbAnims = 0;
return evaluateAnimations(time);
}
bool LayerAndroid::hasAnimations() const
{
for (int i = 0; i < countChildren(); i++) {
if (getChild(i)->hasAnimations())
return true;
}
return !!m_animations.size();
}
bool LayerAndroid::evaluateAnimations(double time) const
{
bool hasRunningAnimations = false;
for (int i = 0; i < countChildren(); i++) {
if (getChild(i)->evaluateAnimations(time))
hasRunningAnimations = true;
}
KeyframesMap::const_iterator end = m_animations.end();
for (KeyframesMap::const_iterator it = m_animations.begin(); it != end; ++it) {
gDebugNbAnims++;
LayerAndroid* currentLayer = const_cast<LayerAndroid*>(this);
if ((it->second)->evaluate(currentLayer, time)) {
hasRunningAnimations = true;
}
}
return hasRunningAnimations;
}
void LayerAndroid::addAnimation(PassRefPtr<AndroidAnimation> anim)
{
m_animations.add(anim->name(), anim);
}
void LayerAndroid::removeAnimation(const String& name)
{
m_animations.remove(name);
}
// We only use the bounding rect of the layer as mask...
// TODO: use a real mask?
void LayerAndroid::setMaskLayer(LayerAndroid* layer)
{
if (layer)
m_haveClip = true;
}
void LayerAndroid::setMasksToBounds(bool masksToBounds)
{
m_haveClip = masksToBounds;
}
void LayerAndroid::setBackgroundColor(SkColor color)
{
m_backgroundColor = color;
}
static int gDebugChildLevel;
void LayerAndroid::bounds(SkRect* rect) const
{
const SkPoint& pos = this->getPosition();
const SkSize& size = this->getSize();
rect->fLeft = pos.fX + m_translation.fX;
rect->fTop = pos.fY + m_translation.fY;
rect->fRight = rect->fLeft + size.width();
rect->fBottom = rect->fTop + size.height();
}
static bool boundsIsUnique(const SkTDArray<SkRect>& region,
const SkRect& local)
{
for (int i = 0; i < region.count(); i++) {
if (region[i].contains(local))
return false;
}
return true;
}
void LayerAndroid::clipArea(SkTDArray<SkRect>* region) const
{
SkRect local;
local.set(0, 0, std::numeric_limits<float>::max(),
std::numeric_limits<float>::max());
clipInner(region, local);
}
void LayerAndroid::clipInner(SkTDArray<SkRect>* region,
const SkRect& local) const
{
SkRect localBounds;
bounds(&localBounds);
localBounds.intersect(local);
if (localBounds.isEmpty())
return;
if (m_recordingPicture && boundsIsUnique(*region, localBounds))
*region->append() = localBounds;
for (int i = 0; i < countChildren(); i++)
getChild(i)->clipInner(region, m_haveClip ? localBounds : local);
}
const LayerAndroid* LayerAndroid::find(int x, int y) const
{
for (int i = 0; i < countChildren(); i++) {
const LayerAndroid* found = getChild(i)->find(x, y);
if (found)
return found;
}
SkRect localBounds;
bounds(&localBounds);
if (localBounds.contains(x, y))
return this;
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// The Layer bounds and the renderview bounds are not always indentical.
// We need to compute the intersection to correctly compute the
// positiong...
static SkRect computeLayerRect(LayerAndroid* layer) {
SkRect layerRect, viewRect;
SkScalar fX, fY;
fX = layer->getOffset().fX;
fY = layer->getOffset().fY;
layerRect.set(0, 0, layer->getSize().width(), layer->getSize().height());
viewRect.set(-fX, -fY, -fX + layer->getFixedWidth(), -fY + layer->getFixedHeight());
layerRect.intersect(viewRect);
return layerRect;
}
void LayerAndroid::updateFixedLayersPositions(const SkRect& viewport)
{
if (m_isFixed) {
float w = viewport.width();
float h = viewport.height();
float dx = viewport.fLeft;
float dy = viewport.fTop;
float x = dx;
float y = dy;
SkRect layerRect = computeLayerRect(this);
if (m_fixedLeft.defined())
x += m_fixedMarginLeft.calcFloatValue(w) + m_fixedLeft.calcFloatValue(w) - layerRect.fLeft;
else if (m_fixedRight.defined())
x += w - m_fixedMarginRight.calcFloatValue(w) - m_fixedRight.calcFloatValue(w) - layerRect.width();
if (m_fixedTop.defined())
y += m_fixedMarginTop.calcFloatValue(h) + m_fixedTop.calcFloatValue(h) - layerRect.fTop;
else if (m_fixedBottom.defined())
y += h - m_fixedMarginBottom.calcFloatValue(h) - m_fixedBottom.calcFloatValue(h) - layerRect.fTop - layerRect.height();
this->setPosition(x, y);
}
int count = this->countChildren();
for (int i = 0; i < count; i++) {
this->getChild(i)->updateFixedLayersPositions(viewport);
}
}
void LayerAndroid::updatePositions() {
// apply the viewport to us
SkMatrix matrix;
if (!m_isFixed) {
// turn our fields into a matrix.
//
// TODO: this should happen in the caller, and we should remove these
// fields from our subclass
matrix.setTranslate(m_translation.fX, m_translation.fY);
if (m_doRotation) {
matrix.preRotate(m_angleTransform);
}
matrix.preScale(m_scale.fX, m_scale.fY);
this->setMatrix(matrix);
}
// now apply it to our children
int count = this->countChildren();
for (int i = 0; i < count; i++) {
this->getChild(i)->updatePositions();
}
}
void LayerAndroid::onDraw(SkCanvas* canvas, SkScalar opacity) {
if (m_haveClip) {
SkRect r;
r.set(0, 0, getSize().width(), getSize().height());
canvas->clipRect(r);
}
if (!prepareContext())
return;
// we just have this save/restore for opacity...
SkAutoCanvasRestore restore(canvas, true);
int canvasOpacity = SkScalarRound(opacity * 255);
if (canvasOpacity < 255)
canvas->setDrawFilter(new OpacityDrawFilter(canvasOpacity));
canvas->drawPicture(*m_recordingPicture);
if (m_extra)
m_extra->draw(canvas, this);
#ifdef LAYER_DEBUG
float w = getSize().width();
float h = getSize().height();
SkPaint paint;
paint.setARGB(128, 255, 0, 0);
canvas->drawLine(0, 0, w, h, paint);
canvas->drawLine(0, h, w, 0, paint);
paint.setARGB(128, 0, 255, 0);
canvas->drawLine(0, 0, 0, h, paint);
canvas->drawLine(0, h, w, h, paint);
canvas->drawLine(w, h, w, 0, paint);
canvas->drawLine(w, 0, 0, 0, paint);
if (m_isFixed) {
SkRect layerRect = computeLayerRect(this);
SkPaint paint;
paint.setARGB(128, 0, 0, 255);
canvas->drawRect(layerRect, paint);
}
#endif
}
SkPicture* LayerAndroid::recordContext()
{
if (prepareContext(true))
return m_recordingPicture;
return 0;
}
bool LayerAndroid::prepareContext(bool force)
{
if (!m_isRootLayer) {
if (force || !m_recordingPicture
|| (m_recordingPicture
&& ((m_recordingPicture->width() != (int) getSize().width())
|| (m_recordingPicture->height() != (int) getSize().height())))) {
m_recordingPicture->safeUnref();
m_recordingPicture = new SkPicture();
}
} else if (m_recordingPicture) {
m_recordingPicture->safeUnref();
m_recordingPicture = 0;
}
return m_recordingPicture;
}
SkRect LayerAndroid::subtractLayers(const SkRect& visibleRect) const
{
SkRect result;
if (m_recordingPicture) {
SkRect globalRect = bounds();
globalRect.offset(-getPosition()); // localToGlobal adds in position
SkMatrix globalMatrix;
localToGlobal(&globalMatrix);
globalMatrix.mapRect(&globalRect);
SkIRect roundedGlobal;
globalRect.round(&roundedGlobal);
SkIRect iVisibleRect;
visibleRect.round(&iVisibleRect);
SkRegion visRegion(iVisibleRect);
visRegion.op(roundedGlobal, SkRegion::kDifference_Op);
result.set(visRegion.getBounds());
#if DEBUG_NAV_UI
SkDebugf("%s visibleRect=(%g,%g,r=%g,b=%g) globalRect=(%g,%g,r=%g,b=%g)"
"result=(%g,%g,r=%g,b=%g)", __FUNCTION__,
visibleRect.fLeft, visibleRect.fTop,
visibleRect.fRight, visibleRect.fBottom,
globalRect.fLeft, globalRect.fTop,
globalRect.fRight, globalRect.fBottom,
result.fLeft, result.fTop, result.fRight, result.fBottom);
#endif
} else
result = visibleRect;
for (int i = 0; i < countChildren(); i++)
result = getChild(i)->subtractLayers(result);
return result;
}
// Debug tools : dump the layers tree in a file.
// The format is simple:
// properties have the form: key = value;
// all statements are finished with a semi-colon.
// value can be:
// - int
// - float
// - array of elements
// - composed type
// a composed type enclose properties in { and }
// an array enclose composed types in { }, separated with a comma.
// exemple:
// {
// x = 3;
// y = 4;
// value = {
// x = 3;
// y = 4;
// };
// anarray = [
// { x = 3; },
// { y = 4; }
// ];
// }
void lwrite(FILE* file, const char* str)
{
fwrite(str, sizeof(char), strlen(str), file);
}
void writeIndent(FILE* file, int indentLevel)
{
if (indentLevel)
fprintf(file, "%*s", indentLevel*2, " ");
}
void writeln(FILE* file, int indentLevel, const char* str)
{
writeIndent(file, indentLevel);
lwrite(file, str);
lwrite(file, "\n");
}
void writeIntVal(FILE* file, int indentLevel, const char* str, int value)
{
writeIndent(file, indentLevel);
fprintf(file, "%s = %d;\n", str, value);
}
void writeHexVal(FILE* file, int indentLevel, const char* str, int value)
{
writeIndent(file, indentLevel);
fprintf(file, "%s = %x;\n", str, value);
}
void writeFloatVal(FILE* file, int indentLevel, const char* str, float value)
{
writeIndent(file, indentLevel);
fprintf(file, "%s = %.3f;\n", str, value);
}
void writePoint(FILE* file, int indentLevel, const char* str, SkPoint point)
{
writeIndent(file, indentLevel);
fprintf(file, "%s = { x = %.3f; y = %.3f; };\n", str, point.fX, point.fY);
}
void writeSize(FILE* file, int indentLevel, const char* str, SkSize size)
{
writeIndent(file, indentLevel);
fprintf(file, "%s = { w = %.3f; h = %.3f; };\n", str, size.width(), size.height());
}
void writeLength(FILE* file, int indentLevel, const char* str, SkLength length)
{
if (!length.defined()) return;
writeIndent(file, indentLevel);
fprintf(file, "%s = { type = %d; value = %.2f; };\n", str, length.type, length.value);
}
void LayerAndroid::dumpLayers(FILE* file, int indentLevel) const
{
writeln(file, indentLevel, "{");
writeHexVal(file, indentLevel + 1, "layer", (int)this);
writeIntVal(file, indentLevel + 1, "layerId", m_uniqueId);
writeIntVal(file, indentLevel + 1, "haveClip", m_haveClip);
writeIntVal(file, indentLevel + 1, "isRootLayer", m_isRootLayer);
writeIntVal(file, indentLevel + 1, "isFixed", m_isFixed);
writeFloatVal(file, indentLevel + 1, "opacity", getOpacity());
writeSize(file, indentLevel + 1, "size", getSize());
writePoint(file, indentLevel + 1, "position", getPosition());
writePoint(file, indentLevel + 1, "translation", m_translation);
writePoint(file, indentLevel + 1, "anchor", getAnchorPoint());
writePoint(file, indentLevel + 1, "scale", m_scale);
if (m_doRotation)
writeFloatVal(file, indentLevel + 1, "angle", m_angleTransform);
if (m_isFixed) {
writeLength(file, indentLevel + 1, "fixedLeft", m_fixedLeft);
writeLength(file, indentLevel + 1, "fixedTop", m_fixedTop);
writeLength(file, indentLevel + 1, "fixedRight", m_fixedRight);
writeLength(file, indentLevel + 1, "fixedBottom", m_fixedBottom);
writeLength(file, indentLevel + 1, "fixedMarginLeft", m_fixedMarginLeft);
writeLength(file, indentLevel + 1, "fixedMarginTop", m_fixedMarginTop);
writeLength(file, indentLevel + 1, "fixedMarginRight", m_fixedMarginRight);
writeLength(file, indentLevel + 1, "fixedMarginBottom", m_fixedMarginBottom);
writePoint(file, indentLevel + 1, "fixedOffset", m_fixedOffset);
writeIntVal(file, indentLevel + 1, "fixedWidth", m_fixedWidth);
writeIntVal(file, indentLevel + 1, "fixedHeight", m_fixedHeight);
}
if (m_recordingPicture) {
writeIntVal(file, indentLevel + 1, "picture width", m_recordingPicture->width());
writeIntVal(file, indentLevel + 1, "picture height", m_recordingPicture->height());
}
if (countChildren()) {
writeln(file, indentLevel + 1, "children = [");
for (int i = 0; i < countChildren(); i++) {
if (i > 0)
writeln(file, indentLevel + 1, ", ");
getChild(i)->dumpLayers(file, indentLevel + 1);
}
writeln(file, indentLevel + 1, "];");
}
writeln(file, indentLevel, "}");
}
void LayerAndroid::dumpToLog() const
{
FILE* file = fopen("/data/data/com.android.browser/layertmp", "w");
dumpLayers(file, 0);
fclose(file);
file = fopen("/data/data/com.android.browser/layertmp", "r");
char buffer[512];
bzero(buffer, sizeof(buffer));
while (fgets(buffer, sizeof(buffer), file))
SkDebugf("%s", buffer);
fclose(file);
}
const LayerAndroid* LayerAndroid::findById(int match) const
{
if (m_uniqueId == match)
return this;
for (int i = 0; i < countChildren(); i++) {
const LayerAndroid* result = getChild(i)->findById(match);
if (result)
return result;
}
return 0;
}
void LayerAndroid::setExtra(DrawExtra* extra)
{
m_extra = extra;
for (int i = 0; i < countChildren(); i++)
getChild(i)->setExtra(extra);
}
} // namespace WebCore
#endif // USE(ACCELERATED_COMPOSITING)