blob: 8fb335e92667edbb4a2a1d35cffcd78a0a5747e2 [file] [log] [blame]
#include "config.h"
#include "LayerAndroid.h"
#if USE(ACCELERATED_COMPOSITING)
#include "AndroidAnimation.h"
#include "FindCanvas.h"
#include "SkDrawFilter.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_haveContents(false),
m_drawsContent(true),
m_haveImage(false),
m_haveClip(false),
m_recordingPicture(0),
m_findOnPage(0),
m_uniqueId(++gUniqueId)
{
gDebugLayerAndroidInstances++;
}
LayerAndroid::LayerAndroid(const LayerAndroid& layer) : SkLayer(layer),
m_isRootLayer(layer.m_isRootLayer),
m_haveContents(layer.m_haveContents),
m_drawsContent(layer.m_drawsContent),
m_haveImage(layer.m_haveImage),
m_haveClip(layer.m_haveClip),
m_findOnPage(0),
m_uniqueId(layer.m_uniqueId)
{
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()
{
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);
}
void LayerAndroid::setDrawsContent(bool drawsContent)
{
m_drawsContent = drawsContent;
for (int i = 0; i < countChildren(); i++)
getChild(i)->setDrawsContent(drawsContent);
}
// 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;
m_backgroundColorSet = true;
setHaveContents(true);
setDrawsContent(true);
}
static int gDebugChildLevel;
void LayerAndroid::draw(SkCanvas* canvas, const SkRect* viewPort)
{
gDebugChildLevel = 0;
paintChildren(viewPort, canvas, 1);
}
void LayerAndroid::bounds(SkRect* rect) const
{
rect->fLeft = m_position.fX + m_translation.fX;
rect->fTop = m_position.fY + m_translation.fY;
rect->fRight = rect->fLeft + m_size.width();
rect->fBottom = rect->fTop + m_size.height();
}
bool LayerAndroid::boundsIsUnique(SkTDArray<SkRect>* region,
const SkRect& local) const
{
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(FloatPoint pos) const
{
for (int i = 0; i < countChildren(); i++) {
const LayerAndroid* found = getChild(i)->find(pos);
if (found)
return found;
}
SkRect localBounds;
bounds(&localBounds);
if (localBounds.contains(pos))
return this;
return 0;
}
void LayerAndroid::setClip(SkCanvas* canvas)
{
SkRect clip;
bounds(&clip);
canvas->clipRect(clip);
}
void LayerAndroid::paintChildren(const SkRect* viewPort, SkCanvas* canvas,
float opacity)
{
int count = canvas->save();
if (m_haveClip)
setClip(canvas);
paintMe(viewPort, canvas, opacity);
canvas->translate(m_position.fX + m_translation.fX,
m_position.fY + m_translation.fY);
for (int i = 0; i < countChildren(); i++) {
gDebugChildLevel++;
getChild(i)->paintChildren(viewPort, canvas, opacity * m_opacity);
gDebugChildLevel--;
}
canvas->restoreToCount(count);
}
bool LayerAndroid::calcPosition(const SkRect* viewPort,
SkMatrix* matrix) {
if (viewPort && m_isFixed) {
float x = 0;
float y = 0;
float w = viewPort->width();
float h = viewPort->height();
float dx = viewPort->fLeft;
float dy = viewPort->fTop;
if (m_fixedLeft.defined())
x = dx + m_fixedLeft.calcFloatValue(w);
else if (m_fixedRight.defined())
x = dx + w - m_fixedRight.calcFloatValue(w) - m_size.width();
if (m_fixedTop.defined())
y = dy + m_fixedTop.calcFloatValue(h);
else if (m_fixedBottom.defined())
y = dy + h - m_fixedBottom.calcFloatValue(h) - m_size.height();
if (matrix)
matrix->setTranslate(x, y);
setPosition(x, y);
return true;
}
return false;
}
void LayerAndroid::paintMe(const SkRect* viewPort,
SkCanvas* canvas,
float opacity)
{
if (!prepareContext())
return;
if (!m_haveImage && !m_drawsContent && !m_isRootLayer)
return;
SkAutoCanvasRestore restore(canvas, true);
int canvasOpacity = opacity * m_opacity * 255;
if (canvasOpacity != 255)
canvas->setDrawFilter(new OpacityDrawFilter(canvasOpacity));
/* FIXME
SkPaint paintMode;
if (m_backgroundColorSet) {
paintMode.setColor(m_backgroundColor);
} else
paintMode.setARGB(0, 0, 0, 0);
paintMode.setXfermodeMode(SkXfermode::kSrc_Mode);
*/
float x, y;
SkMatrix matrix;
if (!calcPosition(viewPort,
&matrix)) {
matrix.reset();
if (m_doRotation) {
float anchorX = m_anchorPoint.fX * m_size.width();
float anchorY = m_anchorPoint.fY * m_size.height();
matrix.preTranslate(anchorX, anchorY);
matrix.preRotate(m_angleTransform);
matrix.preTranslate(-anchorX, -anchorY);
}
float sx = m_scale.fX;
float sy = m_scale.fY;
if (sx > 1.0f || sy > 1.0f) {
float dx = (sx * m_size.width()) - m_size.width();
float dy = (sy * m_size.height()) - m_size.height();
matrix.preTranslate(-dx / 2.0f, -dy / 2.0f);
matrix.preScale(sx, sy);
}
matrix.postTranslate(m_translation.fX + m_position.fX,
m_translation.fY + m_position.fY);
}
canvas->concat(matrix);
m_recordingPicture->draw(canvas);
if (m_findOnPage)
m_findOnPage->drawLayer(canvas, 0, m_uniqueId);
#ifdef LAYER_DEBUG
float w = m_size.width();
float h = m_size.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);
#endif
}
SkPicture* LayerAndroid::recordContext()
{
if (prepareContext(true))
return m_recordingPicture;
return 0;
}
bool LayerAndroid::prepareContext(bool force)
{
if (!m_haveContents)
return false;
if (!m_isRootLayer) {
if (force || !m_recordingPicture
|| (m_recordingPicture
&& ((m_recordingPicture->width() != (int) m_size.width())
|| (m_recordingPicture->height() != (int) m_size.height())))) {
m_recordingPicture->safeUnref();
m_recordingPicture = new SkPicture();
}
} else if (m_recordingPicture) {
m_recordingPicture->safeUnref();
m_recordingPicture = 0;
}
return m_recordingPicture;
}
// 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 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, "{");
writeIntVal(file, indentLevel + 1, "haveContents", m_haveContents);
writeIntVal(file, indentLevel + 1, "drawsContent", m_drawsContent);
writeIntVal(file, indentLevel + 1, "haveImage", m_haveImage);
writeIntVal(file, indentLevel + 1, "clipRect", m_haveClip);
writeFloatVal(file, indentLevel + 1, "opacity", m_opacity);
writeSize(file, indentLevel + 1, "size", m_size);
writePoint(file, indentLevel + 1, "position", m_position);
writePoint(file, indentLevel + 1, "translation", m_translation);
writePoint(file, indentLevel + 1, "anchor", m_anchorPoint);
writePoint(file, indentLevel + 1, "scale", m_scale);
if (m_doRotation)
writeFloatVal(file, indentLevel + 1, "angle", m_angleTransform);
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);
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, "}");
}
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::setFindOnPage(FindOnPage* findOnPage)
{
m_findOnPage = findOnPage;
for (int i = 0; i < countChildren(); i++)
getChild(i)->setFindOnPage(findOnPage);
}
} // namespace WebCore
#endif // USE(ACCELERATED_COMPOSITING)