TextureMapperNode.cpp [plain text]
#include "config.h"
#include "TextureMapperNode.h"
#if USE(ACCELERATED_COMPOSITING)
#include "GraphicsLayerTextureMapper.h"
namespace {
static const float gTileDimension = 1024.0;
}
namespace WebCore {
class TextureMapperSurfaceManager {
public:
TextureMapper* textureMapper;
Vector<RefPtr<BitmapTexture> > surfaces;
IntSize viewportSize;
PassRefPtr<BitmapTexture> getIntermediateSurface()
{
IntSize newViewportSize = textureMapper->viewportSize();
if (newViewportSize != viewportSize) {
viewportSize = newViewportSize;
surfaces.clear();
}
if (surfaces.isEmpty()) {
RefPtr<BitmapTexture> newSurface = textureMapper->createTexture();
newSurface->reset(viewportSize, false);
return newSurface.get();
}
RefPtr<BitmapTexture> surface = surfaces.last();
surface->reset(viewportSize, false);
surfaces.removeLast();
surface->lock();
return surface.get();
}
void releaseIntermediateSurface(BitmapTexture* surface)
{
if (!surface)
return;
surfaces.append(surface);
surface->unlock();
}
};
TextureMapperNode* toTextureMapperNode(GraphicsLayer* layer)
{
return layer ? toGraphicsLayerTextureMapper(layer)->node() : 0;
}
TextureMapperNode* TextureMapperNode::rootLayer()
{
if (m_effectTarget)
return m_effectTarget->rootLayer();
if (m_parent)
return m_parent->rootLayer();
return this;
}
void TextureMapperNode::setTransform(const TransformationMatrix& matrix)
{
if (m_transforms.base == matrix)
return;
m_transforms.base = matrix;
}
void TextureMapperNode::computePerspectiveTransformIfNeeded()
{
if (m_children.isEmpty() || m_state.childrenTransform.isIdentity())
return;
const FloatPoint centerPoint = FloatPoint(m_size.width() / 2, m_size.height() / 2);
m_transforms.perspective = TransformationMatrix()
.translate(centerPoint.x(), centerPoint.y())
.multiply(m_state.childrenTransform)
.translate(-centerPoint.x(), -centerPoint.y());
}
int TextureMapperNode::countDescendantsWithContent() const
{
if (!m_state.visible || m_opacity < 0.01 || (!m_size.width() && !m_size.height() && m_state.masksToBounds))
return 0;
int count = (m_size.width() && m_size.height() && (m_state.drawsContent || m_currentContent.contentType != HTMLContentType)) ? 1 : 0;
for (size_t i = 0; i < m_children.size(); ++i)
count += m_children[i]->countDescendantsWithContent();
return count;
}
void TextureMapperNode::computeOverlapsIfNeeded()
{
m_state.mightHaveOverlaps = countDescendantsWithContent() > 1;
}
void TextureMapperNode::computeReplicaTransformIfNeeded()
{
if (!m_state.replicaLayer)
return;
m_transforms.replica =
TransformationMatrix(m_transforms.target)
.multiply(m_state.replicaLayer->m_transforms.local)
.multiply(TransformationMatrix(m_transforms.target).inverse());
}
void TextureMapperNode::computeLocalTransformIfNeeded()
{
float originX = m_state.anchorPoint.x() * m_size.width();
float originY = m_state.anchorPoint.y() * m_size.height();
m_transforms.local =
TransformationMatrix()
.translate3d(originX + m_state.pos.x(), originY + m_state.pos.y(), m_state.anchorPoint.z() )
.multiply(m_transforms.base)
.translate3d(-originX, -originY, -m_state.anchorPoint.z());
}
bool TextureMapperNode::needsToComputeBoundingRect() const
{
if (m_size.width() > gTileDimension || m_size.height() > gTileDimension)
return true;
if (!m_state.masksToBounds)
return false;
for (size_t i = 0; i < m_children.size(); ++i)
if (m_children[i]->needsToComputeBoundingRect())
return true;
return false;
}
void TextureMapperNode::computeAllTransforms()
{
if (m_size.isEmpty() && m_state.masksToBounds)
return;
computeLocalTransformIfNeeded();
computeReplicaTransformIfNeeded();
computePerspectiveTransformIfNeeded();
m_transforms.target = TransformationMatrix(m_parent ? m_parent->m_transforms.forDescendants : TransformationMatrix()).multiply(m_transforms.local);
m_transforms.targetBoundingRect = FloatRect(m_transforms.target.mapRect(targetRect()));
m_state.visible = m_state.backfaceVisibility || m_transforms.target.inverse().m33() >= 0;
if (!m_state.visible)
return;
if (m_parent && m_parent->m_state.preserves3D)
m_transforms.centerZ = m_transforms.target.mapPoint(FloatPoint3D(m_size.width() / 2, m_size.height() / 2, 0)).z();
if (!m_children.size())
return;
m_transforms.forDescendants = m_transforms.target;
if (!m_state.preserves3D) {
m_transforms.forDescendants = TransformationMatrix(
m_transforms.forDescendants.m11(), m_transforms.forDescendants.m12(), 0, m_transforms.forDescendants.m14(),
m_transforms.forDescendants.m21(), m_transforms.forDescendants.m22(), 0, m_transforms.forDescendants.m24(),
0, 0, 1, 0,
m_transforms.forDescendants.m41(), m_transforms.forDescendants.m42(), 0, m_transforms.forDescendants.m44());
}
m_transforms.forDescendants.multiply(m_transforms.perspective);
}
void TextureMapperNode::computeBoundingRectFromRootIfNeeded()
{
if (!needsToComputeBoundingRect())
return;
if (!m_parent) {
m_transforms.boundingRectFromRoot = m_transforms.boundingRectFromRootForDescendants = IntRect(0, 0, -1, -1);
return;
}
const FloatRect targetRectInRootCoordinates = m_transforms.target.mapRect(targetRect());
const FloatRect parentBoundingRect = m_parent->m_transforms.boundingRectFromRootForDescendants;
if (parentBoundingRect.width() < 0)
m_transforms.boundingRectFromRoot = targetRectInRootCoordinates;
else {
m_transforms.boundingRectFromRootForDescendants = m_transforms.boundingRectFromRoot = parentBoundingRect;
m_transforms.boundingRectFromRoot.intersect(targetRectInRootCoordinates);
}
if (m_state.masksToBounds)
m_transforms.boundingRectFromRootForDescendants.intersect(m_transforms.boundingRectFromRoot);
}
void TextureMapperNode::computeTiles()
{
if (m_currentContent.contentType == HTMLContentType && !m_state.drawsContent) {
m_tiles.clear();
return;
}
Vector<FloatRect> tilesToAdd;
Vector<int> tilesToRemove;
const int TileEraseThreshold = 6;
FloatSize size = contentSize();
FloatRect contentRect(0, 0, size.width() * m_state.contentScale, size.height() * m_state.contentScale);
for (float y = 0; y < contentRect.height(); y += gTileDimension) {
for (float x = 0; x < contentRect.width(); x += gTileDimension) {
FloatRect tileRect(x, y, gTileDimension, gTileDimension);
tileRect.intersect(contentRect);
FloatRect tileRectInRootCoordinates = tileRect;
tileRectInRootCoordinates.scale(1.0 / m_state.contentScale);
tileRectInRootCoordinates = m_transforms.target.mapRect(tileRectInRootCoordinates);
static bool sDiscardHiddenTiles = false;
if (!sDiscardHiddenTiles || !needsToComputeBoundingRect() || tileRectInRootCoordinates.intersects(m_state.visibleRect))
tilesToAdd.append(tileRect);
}
}
for (int i = m_tiles.size() - 1; i >= 0; --i) {
const FloatRect oldTile = m_tiles[i].rect;
bool found = false;
for (int j = tilesToAdd.size() - 1; j >= 0; --j) {
const FloatRect newTile = tilesToAdd[j];
if (oldTile != newTile)
continue;
found = true;
tilesToAdd.remove(j);
break;
}
if (!found)
tilesToRemove.append(i);
}
for (size_t i = 0; i < tilesToAdd.size(); ++i) {
if (!tilesToRemove.isEmpty()) {
Tile& tile = m_tiles[tilesToRemove[0]];
tilesToRemove.remove(0);
tile.rect = tilesToAdd[i];
tile.needsReset = true;
continue;
}
Tile tile;
tile.rect = tilesToAdd[i];
tile.needsReset = true;
m_tiles.append(tile);
}
for (size_t i = 0; i < tilesToRemove.size() && m_tiles.size() > TileEraseThreshold; ++i)
m_tiles.remove(tilesToRemove[i]);
}
void TextureMapperNode::computeVisibleRectIfNeeded()
{
if (!needsToComputeBoundingRect())
return;
FloatRect rootVisibleRect;
if (!m_parent)
rootVisibleRect = m_state.rootVisibleRect;
else if (m_parent)
rootVisibleRect = m_parent->m_state.rootVisibleRect;
m_state.rootVisibleRect = rootVisibleRect;
m_state.visibleRect = m_state.rootVisibleRect;
m_state.visibleRect.intersect(m_transforms.boundingRectFromRoot);
}
void TextureMapperNode::renderContent(TextureMapper* textureMapper, GraphicsLayer* layer)
{
if (m_size.isEmpty() || !layer || (!m_state.drawsContent && m_currentContent.contentType == HTMLContentType)) {
m_tiles.clear();
return;
}
if (!textureMapper)
return;
if (m_currentContent.contentType == MediaContentType)
return;
FloatRect dirtyRect = m_currentContent.needsDisplay ? entireRect() : m_currentContent.needsDisplayRect;
dirtyRect.scale(m_state.contentScale);
for (size_t tileIndex = 0; tileIndex < m_tiles.size(); ++tileIndex) {
Tile& tile = m_tiles[tileIndex];
FloatRect rect = dirtyRect;
if (!tile.texture)
tile.texture = textureMapper->createTexture();
RefPtr<BitmapTexture>& texture = tile.texture;
IntSize tileSize = enclosingIntRect(tile.rect).size();
if (tile.needsReset || texture->contentSize() != tileSize || !texture->isValid()) {
tile.needsReset = false;
texture->reset(tileSize, m_currentContent.contentType == DirectImageContentType ? false : m_state.contentsOpaque);
rect = tile.rect;
}
IntRect contentRect = enclosingIntRect(tile.rect);
contentRect.intersect(enclosingIntRect(rect));
if (contentRect.isEmpty())
continue;
FloatRect contentRectInTileCoordinates = contentRect;
FloatPoint offset(-tile.rect.x(), -tile.rect.y());
contentRectInTileCoordinates.move(offset.x(), offset.y());
{
GraphicsContext context(texture->beginPaint(enclosingIntRect(contentRectInTileCoordinates)));
context.setImageInterpolationQuality(textureMapper->imageInterpolationQuality());
context.setTextDrawingMode(textureMapper->textDrawingMode());
context.translate(offset.x(), offset.y());
context.scale(FloatSize(m_state.contentScale, m_state.contentScale));
FloatRect scaledContentRect(contentRect);
scaledContentRect.scale(1.0 / m_state.contentScale);
if (m_currentContent.contentType == DirectImageContentType)
context.drawImage(m_currentContent.image.get(), ColorSpaceDeviceRGB, IntPoint(0, 0));
else
layer->paintGraphicsLayerContents(context, enclosingIntRect(scaledContentRect));
texture->endPaint();
}
}
m_currentContent.needsDisplay = false;
m_currentContent.needsDisplayRect = IntRect();
}
void TextureMapperNode::paint()
{
if (m_size.isEmpty())
return;
if (!m_surfaceManager)
m_surfaceManager = new TextureMapperSurfaceManager;
m_surfaceManager->textureMapper = m_textureMapper;
TextureMapperPaintOptions opt;
opt.surfaceManager = m_surfaceManager;
opt.textureMapper = m_textureMapper;
opt.textureMapper->bindSurface(0);
paintRecursive(opt);
}
FloatRect TextureMapperNode::targetRectForTileRect(const FloatRect& targetRect, const FloatRect& tileRect) const
{
return FloatRect(
targetRect.x() + (tileRect.x() - targetRect.x()) / m_state.contentScale,
targetRect.y() + (tileRect.y() - targetRect.y()) / m_state.contentScale,
tileRect.width() / m_state.contentScale,
tileRect.height() / m_state.contentScale);
}
void TextureMapperNode::paintSelf(const TextureMapperPaintOptions& options)
{
if (m_size.isEmpty() || (!m_state.drawsContent && m_currentContent.contentType == HTMLContentType))
return;
RefPtr<BitmapTexture> maskTexture;
RefPtr<BitmapTexture> replicaMaskTexture;
if (m_state.maskLayer)
maskTexture = m_state.maskLayer->texture();
if (m_state.replicaLayer && m_state.replicaLayer->m_state.maskLayer)
replicaMaskTexture = m_state.replicaLayer->m_state.maskLayer->texture();
const float opacity = options.isSurface ? 1 : options.opacity;
const FloatRect targetRect = this->targetRect();
if (m_currentContent.contentType == MediaContentType && m_currentContent.media) {
if (m_state.replicaLayer && !options.isSurface)
m_currentContent.media->paintToTextureMapper(options.textureMapper, targetRect,
TransformationMatrix(m_transforms.target).multiply(m_state.replicaLayer->m_transforms.local),
opacity * m_state.replicaLayer->m_opacity,
replicaMaskTexture ? replicaMaskTexture.get() : maskTexture.get());
m_currentContent.media->paintToTextureMapper(options.textureMapper, targetRect, m_transforms.target, opacity, options.isSurface ? 0 : maskTexture.get());
return;
}
for (size_t i = 0; i < m_tiles.size(); ++i) {
BitmapTexture* texture = m_tiles[i].texture.get();
if (m_state.replicaLayer && !options.isSurface) {
options.textureMapper->drawTexture(*texture, targetRectForTileRect(targetRect, m_tiles[i].rect),
TransformationMatrix(m_transforms.target).multiply(m_state.replicaLayer->m_transforms.local),
opacity * m_state.replicaLayer->m_opacity,
replicaMaskTexture ? replicaMaskTexture.get() : maskTexture.get());
}
const FloatRect rect = targetRectForTileRect(targetRect, m_tiles[i].rect);
options.textureMapper->drawTexture(*texture, rect, m_transforms.target, opacity, options.isSurface ? 0 : maskTexture.get());
}
}
int TextureMapperNode::compareGraphicsLayersZValue(const void* a, const void* b)
{
TextureMapperNode* const* nodeA = static_cast<TextureMapperNode* const*>(a);
TextureMapperNode* const* nodeB = static_cast<TextureMapperNode* const*>(b);
return int(((*nodeA)->m_transforms.centerZ - (*nodeB)->m_transforms.centerZ) * 1000);
}
void TextureMapperNode::sortByZOrder(Vector<TextureMapperNode* >& array, int first, int last)
{
qsort(array.data(), array.size(), sizeof(TextureMapperNode*), compareGraphicsLayersZValue);
}
void TextureMapperNode::paintSelfAndChildren(const TextureMapperPaintOptions& options, TextureMapperPaintOptions& optionsForDescendants)
{
bool hasClip = m_state.masksToBounds && !m_children.isEmpty();
if (hasClip)
options.textureMapper->beginClip(m_transforms.forDescendants, FloatRect(0, 0, m_size.width(), m_size.height()));
paintSelf(options);
for (int i = 0; i < m_children.size(); ++i)
m_children[i]->paintRecursive(optionsForDescendants);
if (hasClip)
options.textureMapper->endClip();
}
bool TextureMapperNode::paintReflection(const TextureMapperPaintOptions& options, BitmapTexture* contentSurface)
{
if (!m_state.replicaLayer)
return false;
RefPtr<BitmapTexture> surface(contentSurface);
RefPtr<BitmapTexture> maskSurface;
RefPtr<BitmapTexture> replicaMaskSurface;
RefPtr<BitmapTexture> replicaMaskTexture;
if (TextureMapperNode* replicaMask = m_state.replicaLayer->m_state.maskLayer)
replicaMaskTexture = replicaMask->texture();
RefPtr<BitmapTexture> maskTexture;
if (TextureMapperNode* mask = m_state.maskLayer)
maskTexture = mask->texture();
const IntSize viewportSize = options.textureMapper->viewportSize();
const bool useIntermediateBufferForReplica = m_state.replicaLayer && options.opacity < 0.99;
const bool useIntermediateBufferForMask = maskTexture && replicaMaskTexture;
const FloatRect viewportRect(0, 0, viewportSize.width(), viewportSize.height());
if (maskTexture) {
maskSurface = options.surfaceManager->getIntermediateSurface();
options.textureMapper->bindSurface(maskSurface.get());
options.textureMapper->drawTexture(*maskTexture.get(), entireRect(), m_transforms.target, 1, 0);
maskTexture = maskSurface;
}
if (replicaMaskTexture) {
replicaMaskSurface = options.surfaceManager->getIntermediateSurface();
options.textureMapper->bindSurface(replicaMaskSurface.get());
options.textureMapper->drawTexture(*replicaMaskTexture.get(), entireRect(), m_transforms.target, 1, 0);
replicaMaskTexture = replicaMaskSurface;
}
if (useIntermediateBufferForMask) {
RefPtr<BitmapTexture> maskSurface = options.surfaceManager->getIntermediateSurface();
options.textureMapper->bindSurface(maskSurface.get());
options.textureMapper->drawTexture(*surface.get(), viewportRect, TransformationMatrix(), 1, maskTexture.get());
options.surfaceManager->releaseIntermediateSurface(surface.get());
surface = maskSurface;
maskTexture.clear();
}
if (useIntermediateBufferForReplica) {
RefPtr<BitmapTexture> replicaSurface = options.surfaceManager->getIntermediateSurface();
options.textureMapper->bindSurface(replicaSurface.get());
options.textureMapper->drawTexture(*surface.get(), viewportRect, m_transforms.replica, m_state.replicaLayer->m_opacity, replicaMaskTexture.get());
options.textureMapper->drawTexture(*surface.get(), viewportRect, TransformationMatrix(), 1, maskTexture.get());
options.surfaceManager->releaseIntermediateSurface(surface.get());
surface = replicaSurface;
}
options.textureMapper->bindSurface(options.surface);
if (!useIntermediateBufferForReplica)
options.textureMapper->drawTexture(*surface.get(), viewportRect, m_transforms.replica, m_state.replicaLayer->m_opacity, replicaMaskTexture.get());
options.textureMapper->drawTexture(*surface.get(), viewportRect, TransformationMatrix(), options.opacity, maskTexture.get());
options.surfaceManager->releaseIntermediateSurface(maskSurface.get());
options.surfaceManager->releaseIntermediateSurface(replicaMaskSurface.get());
return true;
}
void TextureMapperNode::paintRecursive(TextureMapperPaintOptions options)
{
if ((m_size.isEmpty() && (m_state.masksToBounds
|| m_children.isEmpty())) || !m_state.visible || options.opacity < 0.01 || m_opacity < 0.01)
return;
options.opacity *= m_opacity;
RefPtr<BitmapTexture> surface;
const bool needsTwoPass = ((m_state.replicaLayer || m_state.maskLayer) && !m_children.isEmpty()) || (m_opacity < 0.99 && m_state.mightHaveOverlaps) || (m_opacity < 0.99 && m_state.replicaLayer);
const IntSize viewportSize = options.textureMapper->viewportSize();
options.isSurface = false;
TextureMapperPaintOptions optionsForDescendants(options);
if (!needsTwoPass) {
paintSelfAndChildren(options, optionsForDescendants);
return;
}
FloatRect viewportRect(0, 0, viewportSize.width(), viewportSize.height());
RefPtr<BitmapTexture> maskSurface;
if (m_state.maskLayer) {
maskSurface = options.surfaceManager->getIntermediateSurface();
options.textureMapper->bindSurface(maskSurface.get());
options.textureMapper->drawTexture(*m_state.maskLayer->texture(), entireRect(), m_transforms.target, 1.0, 0);
}
surface = options.surfaceManager->getIntermediateSurface();
optionsForDescendants.surface = surface.get();
options.isSurface = true;
optionsForDescendants.opacity = 1;
options.textureMapper->bindSurface(surface.get());
paintSelfAndChildren(options, optionsForDescendants);
if (!paintReflection(options, surface.get())) {
options.textureMapper->bindSurface(options.surface);
options.textureMapper->drawTexture(*surface.get(), viewportRect, TransformationMatrix(), options.opacity, 0);
}
options.surfaceManager->releaseIntermediateSurface(surface.get());
options.surfaceManager->releaseIntermediateSurface(maskSurface.get());
}
TextureMapperNode::~TextureMapperNode()
{
for (int i = m_children.size() - 1; i >= 0; --i)
m_children[i]->m_parent = 0;
if (m_parent)
m_parent->m_children.remove(m_parent->m_children.find(this));
}
void TextureMapperNode::setVisibleRect(const IntRect& rect)
{
m_state.rootVisibleRect = m_state.visibleRect = rect;
}
void TextureMapperNode::syncCompositingState(GraphicsLayerTextureMapper* graphicsLayer, int options)
{
syncCompositingState(graphicsLayer, rootLayer()->m_textureMapper, options);
}
void TextureMapperNode::syncCompositingStateSelf(GraphicsLayerTextureMapper* graphicsLayer, TextureMapper* textureMapper)
{
int changeMask = graphicsLayer->changeMask();
const TextureMapperNode::ContentData& pendingContent = graphicsLayer->pendingContent();
if (changeMask == NoChanges && graphicsLayer->m_animations.isEmpty() && pendingContent.needsDisplayRect.isEmpty() && !pendingContent.needsDisplay)
return;
if (m_currentContent.contentType == HTMLContentType && (changeMask & ParentChange)) {
m_parent = toTextureMapperNode(graphicsLayer->parent());
if (!graphicsLayer->parent() && m_parent) {
size_t index = m_parent->m_children.find(this);
m_parent->m_children.remove(index);
}
}
if (changeMask & ChildrenChange) {
m_children.clear();
for (size_t i = 0; i < graphicsLayer->children().size(); ++i) {
TextureMapperNode* child = toTextureMapperNode(graphicsLayer->children()[i]);
if (!child)
continue;
m_children.append(child);
child->m_parent = this;
}
}
if (changeMask & (SizeChange | ContentsRectChange)) {
FloatSize wantedSize(graphicsLayer->size().width(), graphicsLayer->size().height());
if (wantedSize.isEmpty() && pendingContent.contentType == HTMLContentType)
wantedSize = FloatSize(graphicsLayer->contentsRect().width(), graphicsLayer->contentsRect().height());
if (wantedSize != m_size)
m_tiles.clear();
m_size = wantedSize;
}
if (changeMask & MaskLayerChange) {
if (TextureMapperNode* layer = toTextureMapperNode(graphicsLayer->maskLayer()))
layer->m_effectTarget = this;
}
if (changeMask & ReplicaLayerChange) {
if (TextureMapperNode* layer = toTextureMapperNode(graphicsLayer->replicaLayer()))
layer->m_effectTarget = this;
}
if (changeMask & AnimationChange) {
m_animations.clear();
for (size_t i = 0; i < graphicsLayer->m_animations.size(); ++i)
m_animations.append(graphicsLayer->m_animations[i]);
}
m_state.maskLayer = toTextureMapperNode(graphicsLayer->maskLayer());
m_state.replicaLayer = toTextureMapperNode(graphicsLayer->replicaLayer());
m_state.pos = graphicsLayer->position();
m_state.anchorPoint = graphicsLayer->anchorPoint();
m_state.size = graphicsLayer->size();
m_state.contentsRect = graphicsLayer->contentsRect();
m_state.transform = graphicsLayer->transform();
m_state.contentsRect = graphicsLayer->contentsRect();
m_state.preserves3D = graphicsLayer->preserves3D();
m_state.masksToBounds = graphicsLayer->masksToBounds();
m_state.drawsContent = graphicsLayer->drawsContent();
m_state.contentsOpaque = graphicsLayer->contentsOpaque();
m_state.backfaceVisibility = graphicsLayer->backfaceVisibility();
m_state.childrenTransform = graphicsLayer->childrenTransform();
m_state.opacity = graphicsLayer->opacity();
m_currentContent.contentType = pendingContent.contentType;
m_currentContent.image = pendingContent.image;
m_currentContent.media = pendingContent.media;
m_currentContent.needsDisplay = m_currentContent.needsDisplay || pendingContent.needsDisplay;
if (!m_currentContent.needsDisplay)
m_currentContent.needsDisplayRect.unite(pendingContent.needsDisplayRect);
if (!hasRunningOpacityAnimation())
m_opacity = m_state.opacity;
if (!hasRunningTransformAnimation())
m_transforms.base = m_state.transform;
}
bool TextureMapperNode::descendantsOrSelfHaveRunningAnimations() const
{
for (size_t i = 0; i < m_animations.size(); ++i) {
if (!m_animations[i]->paused)
return true;
}
for (size_t i = 0; i < m_children.size(); ++i) {
if (m_children[i]->descendantsOrSelfHaveRunningAnimations())
return true;
}
return false;
}
static double normalizedAnimationValue(double runningTime, double duration, bool alternate)
{
if (!duration)
return 0;
const int loopCount = runningTime / duration;
const double lastFullLoop = duration * double(loopCount);
const double remainder = runningTime - lastFullLoop;
const double normalized = remainder / duration;
return (loopCount % 2 && alternate) ? (1 - normalized) : normalized;
}
void TextureMapperNode::applyOpacityAnimation(float fromOpacity, float toOpacity, double progress)
{
if (progress == 1.0)
setOpacity(toOpacity);
else if (!progress)
setOpacity(fromOpacity);
else
setOpacity(fromOpacity + progress * (toOpacity - fromOpacity));
}
static inline double solveEpsilon(double duration)
{
return 1.0 / (200.0 * duration);
}
static inline double solveCubicBezierFunction(qreal p1x, qreal p1y, qreal p2x, qreal p2y, double t, double duration)
{
UnitBezier bezier(p1x, p1y, p2x, p2y);
return bezier.solve(t, solveEpsilon(duration));
}
static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t)
{
if (stepAtStart)
return qMin(1.0, (floor(numSteps * t) + 1) / numSteps);
return floor(numSteps * t) / numSteps;
}
static inline float applyTimingFunction(const TimingFunction* timingFunction, float progress, double duration)
{
if (!timingFunction)
return progress;
if (timingFunction->isCubicBezierTimingFunction()) {
const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction);
return solveCubicBezierFunction(ctf->x1(),
ctf->y1(),
ctf->x2(),
ctf->y2(),
progress, duration);
}
if (timingFunction->isStepsTimingFunction()) {
const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(timingFunction);
return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), double(progress));
}
return progress;
}
void TextureMapperNode::applyTransformAnimation(const TextureMapperAnimation& animation, const TransformOperations* from, const TransformOperations* to, double progress)
{
if (progress == 1.0 || !progress) {
TransformationMatrix matrix;
const TransformOperations* ops = progress ? to : from;
ops->apply(animation.boxSize, matrix);
setTransform(matrix);
}
if (!animation.listsMatch) {
TransformationMatrix toMatrix, fromMatrix;
to->apply(animation.boxSize, toMatrix);
from->apply(animation.boxSize, fromMatrix);
toMatrix.blend(fromMatrix, progress);
setTransform(toMatrix);
return;
}
TransformationMatrix matrix;
if (!to->size()) {
const TransformOperations* swap = to;
to = from;
from = swap;
} else if (!from->size())
progress = 1.0 - progress;
TransformOperations blended(*to);
for (size_t i = 0; i < animation.functionList.size(); ++i)
blended.operations()[i]->blend(from->at(i), progress, !from->at(i))->apply(matrix, animation.boxSize);
setTransform(matrix);
}
void TextureMapperNode::applyAnimationFrame(const TextureMapperAnimation& animation, const AnimationValue* from, const AnimationValue* to, float progress)
{
switch (animation.keyframes.property()) {
case AnimatedPropertyOpacity:
applyOpacityAnimation((static_cast<const FloatAnimationValue*>(from)->value()), (static_cast<const FloatAnimationValue*>(to)->value()), progress);
return;
case AnimatedPropertyWebkitTransform:
applyTransformAnimation(animation, static_cast<const TransformAnimationValue*>(from)->value(), static_cast<const TransformAnimationValue*>(to)->value(), progress);
return;
default:
ASSERT_NOT_REACHED();
}
}
void TextureMapperNode::applyAnimation(const TextureMapperAnimation& animation, double normalizedValue)
{
if (!normalizedValue) {
applyAnimationFrame(animation, animation.keyframes.at(0), animation.keyframes.at(1), 0);
return;
}
if (normalizedValue == 1.0) {
applyAnimationFrame(animation, animation.keyframes.at(animation.keyframes.size() - 2), animation.keyframes.at(animation.keyframes.size() - 1), 1);
return;
}
if (animation.keyframes.size() == 2) {
normalizedValue = applyTimingFunction(animation.animation->timingFunction().get(), normalizedValue, animation.animation->duration());
applyAnimationFrame(animation, animation.keyframes.at(0), animation.keyframes.at(1), normalizedValue);
return;
}
for (size_t i = 0; i < animation.keyframes.size() - 1; ++i) {
const AnimationValue* from = animation.keyframes.at(i);
const AnimationValue* to = animation.keyframes.at(i + 1);
if (from->keyTime() > normalizedValue || to->keyTime() < normalizedValue)
continue;
normalizedValue = (normalizedValue - from->keyTime()) / (to->keyTime() - from->keyTime());
normalizedValue = applyTimingFunction(from->timingFunction(), normalizedValue, animation.animation->duration());
applyAnimationFrame(animation, from, to, normalizedValue);
break;
}
}
bool TextureMapperNode::hasRunningOpacityAnimation() const
{
for (size_t i = 0; i < m_animations.size(); ++i) {
const TextureMapperAnimation& animation = *m_animations[i].get();
if (!animation.paused && animation.keyframes.property() == AnimatedPropertyOpacity)
return true;
}
return false;
}
bool TextureMapperNode::hasRunningTransformAnimation() const
{
for (size_t i = 0; i < m_animations.size(); ++i) {
const TextureMapperAnimation& animation = *m_animations[i].get();
if (!animation.paused && animation.keyframes.property() == AnimatedPropertyWebkitTransform)
return true;
}
return false;
}
void TextureMapperNode::syncAnimations(GraphicsLayerTextureMapper* layer)
{
for (int i = m_animations.size() - 1; i >= 0; --i) {
RefPtr<TextureMapperAnimation> animation = m_animations[i];
double totalRunningTime = WTF::currentTime() - animation->startTime;
RefPtr<Animation> anim = animation->animation;
double normalizedValue = normalizedAnimationValue(totalRunningTime, anim->duration(), true);
if (anim->iterationCount() != Animation::IterationCountInfinite && totalRunningTime >= anim->duration() * anim->iterationCount()) {
if (anim->fillsForwards()) {
if (animation->keyframes.property() == AnimatedPropertyWebkitTransform)
m_state.transform = m_transforms.base;
else if (animation->keyframes.property() == AnimatedPropertyOpacity)
m_opacity = m_state.opacity;
}
m_animations.remove(i);
continue;
}
if (!animation->paused)
applyAnimation(*animation.get(), normalizedValue);
}
}
void TextureMapperNode::syncCompositingState(GraphicsLayerTextureMapper* graphicsLayer, TextureMapper* textureMapper, int options)
{
if (graphicsLayer) {
syncCompositingStateSelf(graphicsLayer, textureMapper);
graphicsLayer->didSynchronize();
}
if (graphicsLayer && m_state.maskLayer) {
m_state.maskLayer->syncCompositingState(toGraphicsLayerTextureMapper(graphicsLayer->maskLayer()), textureMapper);
if (m_state.maskLayer->m_size.isEmpty())
m_state.maskLayer->m_size = m_size;
}
if (m_state.replicaLayer)
m_state.replicaLayer->syncCompositingState(toGraphicsLayerTextureMapper(graphicsLayer->replicaLayer()), textureMapper);
syncAnimations(graphicsLayer);
computeAllTransforms();
computeBoundingRectFromRootIfNeeded();
computePerspectiveTransformIfNeeded();
computeTiles();
computeOverlapsIfNeeded();
if (graphicsLayer)
renderContent(textureMapper, graphicsLayer);
if (!(options & TraverseDescendants))
return;
if (graphicsLayer) {
Vector<GraphicsLayer*> children = graphicsLayer->children();
for (int i = children.size() - 1; i >= 0; --i) {
TextureMapperNode* node = toTextureMapperNode(children[i]);
if (!node)
continue;
node->syncCompositingState(toGraphicsLayerTextureMapper(children[i]), textureMapper, options);
}
} else {
for (int i = m_children.size() - 1; i >= 0; --i)
m_children[i]->syncCompositingState(0, textureMapper, options);
}
if (m_state.preserves3D)
sortByZOrder(m_children, 0, m_children.size());
}
static PassRefPtr<TimingFunction> copyTimingFunction(const TimingFunction* tfunc)
{
if (!tfunc)
return 0;
if (tfunc->isLinearTimingFunction())
return LinearTimingFunction::create();
if (tfunc->isCubicBezierTimingFunction()) {
const CubicBezierTimingFunction* btfunc = static_cast<const CubicBezierTimingFunction*>(tfunc);
return CubicBezierTimingFunction::create(btfunc->x1(), btfunc->y1(), btfunc->x2(), btfunc->y2());
}
if (tfunc->isStepsTimingFunction()) {
const StepsTimingFunction* stfunc = static_cast<const StepsTimingFunction*>(tfunc);
return StepsTimingFunction::create(stfunc->numberOfSteps(), stfunc->stepAtStart());
}
return 0;
}
static const AnimationValue* copyAnimationValue(AnimatedPropertyID property, const AnimationValue* value)
{
switch (property) {
case AnimatedPropertyWebkitTransform: {
const TransformAnimationValue* transformValue = static_cast<const TransformAnimationValue*>(value);
return new TransformAnimationValue(transformValue->keyTime(), transformValue->value(), copyTimingFunction(transformValue->timingFunction()));
}
case AnimatedPropertyOpacity: {
const FloatAnimationValue* floatValue = static_cast<const FloatAnimationValue*>(value);
return new FloatAnimationValue(floatValue->keyTime(), floatValue->value(), copyTimingFunction(floatValue->timingFunction()));
}
default:
ASSERT_NOT_REACHED();
}
return 0;
}
TextureMapperAnimation::TextureMapperAnimation(const KeyframeValueList& values)
: keyframes(values.property())
{
for (size_t i = 0; i < values.size(); ++i)
keyframes.insert(copyAnimationValue(values.property(), values.at(i)));
}
}
#endif