diff options
Diffstat (limited to 'WebCore/platform/graphics/ca/GraphicsLayerCA.cpp')
-rw-r--r-- | WebCore/platform/graphics/ca/GraphicsLayerCA.cpp | 2300 |
1 files changed, 2300 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp b/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp new file mode 100644 index 0000000..8bde9fd --- /dev/null +++ b/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp @@ -0,0 +1,2300 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "GraphicsLayerCA.h" + +#include "Animation.h" +#include "FloatConversion.h" +#include "FloatRect.h" +#include "PlatformCALayer.h" +#include "PlatformString.h" +#include "RotateTransformOperation.h" +#include "ScaleTransformOperation.h" +#include "SystemTime.h" +#include "TranslateTransformOperation.h" +#include <QuartzCore/QuartzCore.h> +#include <limits.h> +#include <wtf/CurrentTime.h> +#include <wtf/text/StringConcatenate.h> + +using namespace std; + +#define HAVE_MODERN_QUARTZCORE (!defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)) + +namespace WebCore { + +// The threshold width or height above which a tiled layer will be used. This should be +// large enough to avoid tiled layers for most GraphicsLayers, but less than the OpenGL +// texture size limit on all supported hardware. +static const int cMaxPixelDimension = 2000; + +// If we send a duration of 0 to CA, then it will use the default duration +// of 250ms. So send a very small value instead. +static const float cAnimationAlmostZeroDuration = 1e-3f; + +// CACurrentMediaTime() is a time since boot. These methods convert between that and +// WebCore time, which is system time (UTC). +static CFTimeInterval currentTimeToMediaTime(double t) +{ + return CACurrentMediaTime() + t - WTF::currentTime(); +} + +static bool isTransformTypeTransformationMatrix(TransformOperation::OperationType transformType) +{ + switch (transformType) { + case TransformOperation::SKEW_X: + case TransformOperation::SKEW_Y: + case TransformOperation::SKEW: + case TransformOperation::MATRIX: + case TransformOperation::ROTATE_3D: + case TransformOperation::MATRIX_3D: + case TransformOperation::PERSPECTIVE: + case TransformOperation::IDENTITY: + case TransformOperation::NONE: + return true; + default: + return false; + } +} + +static bool isTransformTypeFloatPoint3D(TransformOperation::OperationType transformType) +{ + switch (transformType) { + case TransformOperation::SCALE: + case TransformOperation::SCALE_3D: + case TransformOperation::TRANSLATE: + case TransformOperation::TRANSLATE_3D: + return true; + default: + return false; + } +} + +static bool isTransformTypeNumber(TransformOperation::OperationType transformType) +{ + return !isTransformTypeTransformationMatrix(transformType) && !isTransformTypeFloatPoint3D(transformType); +} + +static void getTransformFunctionValue(const TransformOperation* transformOp, TransformOperation::OperationType transformType, const IntSize& size, float& value) +{ + switch (transformType) { + case TransformOperation::ROTATE: + case TransformOperation::ROTATE_X: + case TransformOperation::ROTATE_Y: + value = transformOp ? narrowPrecisionToFloat(deg2rad(static_cast<const RotateTransformOperation*>(transformOp)->angle())) : 0; + break; + case TransformOperation::SCALE_X: + value = transformOp ? narrowPrecisionToFloat(static_cast<const ScaleTransformOperation*>(transformOp)->x()) : 1; + break; + case TransformOperation::SCALE_Y: + value = transformOp ? narrowPrecisionToFloat(static_cast<const ScaleTransformOperation*>(transformOp)->y()) : 1; + break; + case TransformOperation::SCALE_Z: + value = transformOp ? narrowPrecisionToFloat(static_cast<const ScaleTransformOperation*>(transformOp)->z()) : 1; + break; + case TransformOperation::TRANSLATE_X: + value = transformOp ? narrowPrecisionToFloat(static_cast<const TranslateTransformOperation*>(transformOp)->x(size)) : 0; + break; + case TransformOperation::TRANSLATE_Y: + value = transformOp ? narrowPrecisionToFloat(static_cast<const TranslateTransformOperation*>(transformOp)->y(size)) : 0; + break; + case TransformOperation::TRANSLATE_Z: + value = transformOp ? narrowPrecisionToFloat(static_cast<const TranslateTransformOperation*>(transformOp)->z(size)) : 0; + break; + default: + break; + } +} + +static void getTransformFunctionValue(const TransformOperation* transformOp, TransformOperation::OperationType transformType, const IntSize& size, FloatPoint3D& value) +{ + switch (transformType) { + case TransformOperation::SCALE: + case TransformOperation::SCALE_3D: + value.setX(transformOp ? narrowPrecisionToFloat(static_cast<const ScaleTransformOperation*>(transformOp)->x()) : 1); + value.setY(transformOp ? narrowPrecisionToFloat(static_cast<const ScaleTransformOperation*>(transformOp)->y()) : 1); + value.setZ(transformOp ? narrowPrecisionToFloat(static_cast<const ScaleTransformOperation*>(transformOp)->z()) : 1); + break; + case TransformOperation::TRANSLATE: + case TransformOperation::TRANSLATE_3D: + value.setX(transformOp ? narrowPrecisionToFloat(static_cast<const TranslateTransformOperation*>(transformOp)->x(size)) : 0); + value.setY(transformOp ? narrowPrecisionToFloat(static_cast<const TranslateTransformOperation*>(transformOp)->y(size)) : 0); + value.setZ(transformOp ? narrowPrecisionToFloat(static_cast<const TranslateTransformOperation*>(transformOp)->z(size)) : 0); + break; + default: + break; + } +} + +static void getTransformFunctionValue(const TransformOperation* transformOp, TransformOperation::OperationType transformType, const IntSize& size, TransformationMatrix& value) +{ + switch (transformType) { + case TransformOperation::SKEW_X: + case TransformOperation::SKEW_Y: + case TransformOperation::SKEW: + case TransformOperation::MATRIX: + case TransformOperation::ROTATE_3D: + case TransformOperation::MATRIX_3D: + case TransformOperation::PERSPECTIVE: + case TransformOperation::IDENTITY: + case TransformOperation::NONE: + if (transformOp) + transformOp->apply(value, size); + else + value.makeIdentity(); + break; + default: + break; + } +} + +#if HAVE_MODERN_QUARTZCORE +static PlatformCAAnimation::ValueFunctionType getValueFunctionNameForTransformOperation(TransformOperation::OperationType transformType) +{ + // Use literal strings to avoid link-time dependency on those symbols. + switch (transformType) { + case TransformOperation::ROTATE_X: + return PlatformCAAnimation::RotateX; + case TransformOperation::ROTATE_Y: + return PlatformCAAnimation::RotateY; + case TransformOperation::ROTATE: + return PlatformCAAnimation::RotateZ; + case TransformOperation::SCALE_X: + return PlatformCAAnimation::ScaleX; + case TransformOperation::SCALE_Y: + return PlatformCAAnimation::ScaleY; + case TransformOperation::SCALE_Z: + return PlatformCAAnimation::ScaleZ; + case TransformOperation::TRANSLATE_X: + return PlatformCAAnimation::TranslateX; + case TransformOperation::TRANSLATE_Y: + return PlatformCAAnimation::TranslateY; + case TransformOperation::TRANSLATE_Z: + return PlatformCAAnimation::TranslateZ; + case TransformOperation::SCALE: + case TransformOperation::SCALE_3D: + return PlatformCAAnimation::Scale; + case TransformOperation::TRANSLATE: + case TransformOperation::TRANSLATE_3D: + return PlatformCAAnimation::Translate; + default: + return PlatformCAAnimation::NoValueFunction; + } +} +#endif + +static String propertyIdToString(AnimatedPropertyID property) +{ + switch (property) { + case AnimatedPropertyWebkitTransform: + return "transform"; + case AnimatedPropertyOpacity: + return "opacity"; + case AnimatedPropertyBackgroundColor: + return "backgroundColor"; + case AnimatedPropertyInvalid: + ASSERT_NOT_REACHED(); + } + ASSERT_NOT_REACHED(); + return ""; +} + +static String animationIdentifier(const String& animationName, AnimatedPropertyID property, int index) +{ + return makeString(animationName, '_', String::number(property), '_', String::number(index)); +} + +static bool animationHasStepsTimingFunction(const KeyframeValueList& valueList, const Animation* anim) +{ + if (anim->timingFunction()->isStepsTimingFunction()) + return true; + + for (unsigned i = 0; i < valueList.size(); ++i) { + const TimingFunction* timingFunction = valueList.at(i)->timingFunction(); + if (timingFunction && timingFunction->isStepsTimingFunction()) + return true; + } + + return false; +} + +PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client) +{ + return new GraphicsLayerCA(client); +} + +GraphicsLayerCA::GraphicsLayerCA(GraphicsLayerClient* client) + : GraphicsLayer(client) + , m_contentsLayerPurpose(NoContentsLayer) + , m_contentsLayerHasBackgroundColor(false) + , m_uncommittedChanges(NoChange) +{ + m_layer = PlatformCALayer::create(PlatformCALayer::LayerTypeWebLayer, this); + +#if !HAVE_MODERN_QUARTZCORE + setContentsOrientation(defaultContentsOrientation()); +#endif + + updateDebugIndicators(); +} + +GraphicsLayerCA::~GraphicsLayerCA() +{ + // We release our references to the PlatformCALayers here, but do not actively unparent them, + // since that will cause a commit and break our batched commit model. The layers will + // get released when the rootmost modified GraphicsLayerCA rebuilds its child layers. + + // Clean up the layer. + if (m_layer) + m_layer->setOwner(0); + + if (m_contentsLayer) + m_contentsLayer->setOwner(0); + + if (m_structuralLayer) + m_structuralLayer->setOwner(0); + + removeCloneLayers(); +} + +void GraphicsLayerCA::setName(const String& name) +{ + String longName = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + name; + GraphicsLayer::setName(longName); + noteLayerPropertyChanged(NameChanged); +} + +PlatformLayer* GraphicsLayerCA::platformLayer() const +{ + return primaryLayer()->platformLayer(); +} + +bool GraphicsLayerCA::setChildren(const Vector<GraphicsLayer*>& children) +{ + bool childrenChanged = GraphicsLayer::setChildren(children); + if (childrenChanged) + noteSublayersChanged(); + + return childrenChanged; +} + +void GraphicsLayerCA::addChild(GraphicsLayer* childLayer) +{ + GraphicsLayer::addChild(childLayer); + noteSublayersChanged(); +} + +void GraphicsLayerCA::addChildAtIndex(GraphicsLayer* childLayer, int index) +{ + GraphicsLayer::addChildAtIndex(childLayer, index); + noteSublayersChanged(); +} + +void GraphicsLayerCA::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + GraphicsLayer::addChildBelow(childLayer, sibling); + noteSublayersChanged(); +} + +void GraphicsLayerCA::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + GraphicsLayer::addChildAbove(childLayer, sibling); + noteSublayersChanged(); +} + +bool GraphicsLayerCA::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + if (GraphicsLayer::replaceChild(oldChild, newChild)) { + noteSublayersChanged(); + return true; + } + return false; +} + +void GraphicsLayerCA::removeFromParent() +{ + if (m_parent) + static_cast<GraphicsLayerCA*>(m_parent)->noteSublayersChanged(); + GraphicsLayer::removeFromParent(); +} + +void GraphicsLayerCA::setMaskLayer(GraphicsLayer* layer) +{ + if (layer == m_maskLayer) + return; + + GraphicsLayer::setMaskLayer(layer); + noteLayerPropertyChanged(MaskLayerChanged); + + propagateLayerChangeToReplicas(); + + if (m_replicatedLayer) + static_cast<GraphicsLayerCA*>(m_replicatedLayer)->propagateLayerChangeToReplicas(); +} + +void GraphicsLayerCA::setReplicatedLayer(GraphicsLayer* layer) +{ + if (layer == m_replicatedLayer) + return; + + GraphicsLayer::setReplicatedLayer(layer); + noteLayerPropertyChanged(ReplicatedLayerChanged); +} + +void GraphicsLayerCA::setReplicatedByLayer(GraphicsLayer* layer) +{ + if (layer == m_replicaLayer) + return; + + GraphicsLayer::setReplicatedByLayer(layer); + noteSublayersChanged(); + noteLayerPropertyChanged(ReplicatedLayerChanged); +} + +void GraphicsLayerCA::setPosition(const FloatPoint& point) +{ + if (point == m_position) + return; + + GraphicsLayer::setPosition(point); + noteLayerPropertyChanged(PositionChanged); +} + +void GraphicsLayerCA::setAnchorPoint(const FloatPoint3D& point) +{ + if (point == m_anchorPoint) + return; + + GraphicsLayer::setAnchorPoint(point); + noteLayerPropertyChanged(AnchorPointChanged); +} + +void GraphicsLayerCA::setSize(const FloatSize& size) +{ + if (size == m_size) + return; + + GraphicsLayer::setSize(size); + noteLayerPropertyChanged(SizeChanged); +} + +void GraphicsLayerCA::setTransform(const TransformationMatrix& t) +{ + if (t == m_transform) + return; + + GraphicsLayer::setTransform(t); + noteLayerPropertyChanged(TransformChanged); +} + +void GraphicsLayerCA::setChildrenTransform(const TransformationMatrix& t) +{ + if (t == m_childrenTransform) + return; + + GraphicsLayer::setChildrenTransform(t); + noteLayerPropertyChanged(ChildrenTransformChanged); +} + +void GraphicsLayerCA::moveOrCopyLayerAnimation(MoveOrCopy operation, const String& animationIdentifier, PlatformCALayer *fromLayer, PlatformCALayer *toLayer) +{ + RefPtr<PlatformCAAnimation> anim = fromLayer->animationForKey(animationIdentifier); + if (!anim) + return; + + switch (operation) { + case Move: + fromLayer->removeAnimationForKey(animationIdentifier); + toLayer->addAnimationForKey(animationIdentifier, anim.get()); + break; + + case Copy: + toLayer->addAnimationForKey(animationIdentifier, anim.get()); + break; + } +} + +void GraphicsLayerCA::moveOrCopyAnimationsForProperty(MoveOrCopy operation, AnimatedPropertyID property, PlatformCALayer *fromLayer, PlatformCALayer *toLayer) +{ + // Look for running animations affecting this property. + AnimationsMap::const_iterator end = m_runningAnimations.end(); + for (AnimationsMap::const_iterator it = m_runningAnimations.begin(); it != end; ++it) { + const Vector<LayerPropertyAnimation>& propertyAnimations = it->second; + size_t numAnimations = propertyAnimations.size(); + for (size_t i = 0; i < numAnimations; ++i) { + const LayerPropertyAnimation& currAnimation = propertyAnimations[i]; + if (currAnimation.m_property == property) + moveOrCopyLayerAnimation(operation, animationIdentifier(currAnimation.m_name, currAnimation.m_property, currAnimation.m_index), fromLayer, toLayer); + } + } +} + +void GraphicsLayerCA::setPreserves3D(bool preserves3D) +{ + if (preserves3D == m_preserves3D) + return; + + GraphicsLayer::setPreserves3D(preserves3D); + noteLayerPropertyChanged(Preserves3DChanged); +} + +void GraphicsLayerCA::setMasksToBounds(bool masksToBounds) +{ + if (masksToBounds == m_masksToBounds) + return; + + GraphicsLayer::setMasksToBounds(masksToBounds); + noteLayerPropertyChanged(MasksToBoundsChanged); +} + +void GraphicsLayerCA::setDrawsContent(bool drawsContent) +{ + if (drawsContent == m_drawsContent) + return; + + GraphicsLayer::setDrawsContent(drawsContent); + noteLayerPropertyChanged(DrawsContentChanged); +} + +void GraphicsLayerCA::setAcceleratesDrawing(bool acceleratesDrawing) +{ + if (acceleratesDrawing == m_acceleratesDrawing) + return; + + GraphicsLayer::setAcceleratesDrawing(acceleratesDrawing); + noteLayerPropertyChanged(DrawsContentChanged); +} + +void GraphicsLayerCA::setBackgroundColor(const Color& color) +{ + if (m_backgroundColorSet && m_backgroundColor == color) + return; + + GraphicsLayer::setBackgroundColor(color); + + m_contentsLayerHasBackgroundColor = true; + noteLayerPropertyChanged(BackgroundColorChanged); +} + +void GraphicsLayerCA::clearBackgroundColor() +{ + if (!m_backgroundColorSet) + return; + + GraphicsLayer::clearBackgroundColor(); + m_contentsLayerHasBackgroundColor = false; + noteLayerPropertyChanged(BackgroundColorChanged); +} + +void GraphicsLayerCA::setContentsOpaque(bool opaque) +{ + if (m_contentsOpaque == opaque) + return; + + GraphicsLayer::setContentsOpaque(opaque); + noteLayerPropertyChanged(ContentsOpaqueChanged); +} + +void GraphicsLayerCA::setBackfaceVisibility(bool visible) +{ + if (m_backfaceVisibility == visible) + return; + + GraphicsLayer::setBackfaceVisibility(visible); + noteLayerPropertyChanged(BackfaceVisibilityChanged); +} + +void GraphicsLayerCA::setOpacity(float opacity) +{ + float clampedOpacity = max(0.0f, min(opacity, 1.0f)); + + if (clampedOpacity == m_opacity) + return; + + GraphicsLayer::setOpacity(clampedOpacity); + noteLayerPropertyChanged(OpacityChanged); +} + +void GraphicsLayerCA::setNeedsDisplay() +{ + FloatRect hugeRect(-numeric_limits<float>::max() / 2, -numeric_limits<float>::max() / 2, + numeric_limits<float>::max(), numeric_limits<float>::max()); + + setNeedsDisplayInRect(hugeRect); +} + +void GraphicsLayerCA::setNeedsDisplayInRect(const FloatRect& rect) +{ + if (!drawsContent()) + return; + + const size_t maxDirtyRects = 32; + + for (size_t i = 0; i < m_dirtyRects.size(); ++i) { + if (m_dirtyRects[i].contains(rect)) + return; + } + + if (m_dirtyRects.size() < maxDirtyRects) + m_dirtyRects.append(rect); + else + m_dirtyRects[0].unite(rect); + + noteLayerPropertyChanged(DirtyRectsChanged); +} + +void GraphicsLayerCA::setContentsNeedsDisplay() +{ + noteLayerPropertyChanged(ContentsNeedsDisplay); +} + +void GraphicsLayerCA::setContentsRect(const IntRect& rect) +{ + if (rect == m_contentsRect) + return; + + GraphicsLayer::setContentsRect(rect); + noteLayerPropertyChanged(ContentsRectChanged); +} + +bool GraphicsLayerCA::addAnimation(const KeyframeValueList& valueList, const IntSize& boxSize, const Animation* anim, const String& animationName, double timeOffset) +{ + ASSERT(!animationName.isEmpty()); + + if (!anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) + return false; + +#if !HAVE_MODERN_QUARTZCORE + // Older versions of QuartzCore do not handle opacity in transform layers properly, so we will + // always do software animation in that case. + if (valueList.property() == AnimatedPropertyOpacity) + return false; +#endif + + // CoreAnimation does not handle the steps() timing function. Fall back + // to software animation in that case. + if (animationHasStepsTimingFunction(valueList, anim)) + return false; + + bool createdAnimations = false; + if (valueList.property() == AnimatedPropertyWebkitTransform) + createdAnimations = createTransformAnimationsFromKeyframes(valueList, anim, animationName, timeOffset, boxSize); + else + createdAnimations = createAnimationFromKeyframes(valueList, anim, animationName, timeOffset); + + if (createdAnimations) + noteLayerPropertyChanged(AnimationChanged); + + return createdAnimations; +} + +void GraphicsLayerCA::pauseAnimation(const String& animationName, double timeOffset) +{ + if (!animationIsRunning(animationName)) + return; + + AnimationsToProcessMap::iterator it = m_animationsToProcess.find(animationName); + if (it != m_animationsToProcess.end()) { + AnimationProcessingAction& processingInfo = it->second; + // If an animation is scheduled to be removed, don't change the remove to a pause. + if (processingInfo.action != Remove) + processingInfo.action = Pause; + } else + m_animationsToProcess.add(animationName, AnimationProcessingAction(Pause, timeOffset)); + + noteLayerPropertyChanged(AnimationChanged); +} + +void GraphicsLayerCA::removeAnimation(const String& animationName) +{ + if (!animationIsRunning(animationName)) + return; + + m_animationsToProcess.add(animationName, AnimationProcessingAction(Remove)); + noteLayerPropertyChanged(AnimationChanged); +} + +void GraphicsLayerCA::animationStarted(CFTimeInterval startTime) +{ + if (m_client) + m_client->notifyAnimationStarted(this, startTime); +} + +void GraphicsLayerCA::setContentsToImage(Image* image) +{ + if (image) { + CGImageRef newImage = image->nativeImageForCurrentFrame(); + if (!newImage) + return; + + // Check to see if the image changed; we have to do this because the call to + // CGImageCreateCopyWithColorSpace() below can create a new image every time. + if (m_uncorrectedContentsImage && m_uncorrectedContentsImage.get() == newImage) + return; + + m_uncorrectedContentsImage = newImage; + m_pendingContentsImage = newImage; + CGColorSpaceRef colorSpace = CGImageGetColorSpace(m_pendingContentsImage.get()); + + static CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); + if (colorSpace && CFEqual(colorSpace, deviceRGB)) { + // CoreGraphics renders images tagged with DeviceRGB using the color space of the main display. When we hand such + // images to CA we need to tag them similarly so CA rendering matches CG rendering. + static CGColorSpaceRef genericRGB = CGDisplayCopyColorSpace(kCGDirectMainDisplay); + m_pendingContentsImage.adoptCF(CGImageCreateCopyWithColorSpace(m_pendingContentsImage.get(), genericRGB)); + } + m_contentsLayerPurpose = ContentsLayerForImage; + if (!m_contentsLayer) + noteSublayersChanged(); + } else { + m_uncorrectedContentsImage = 0; + m_pendingContentsImage = 0; + m_contentsLayerPurpose = NoContentsLayer; + if (m_contentsLayer) + noteSublayersChanged(); + } + + noteLayerPropertyChanged(ContentsImageChanged); +} + +void GraphicsLayerCA::setContentsToMedia(PlatformLayer* mediaLayer) +{ + if (m_contentsLayer && mediaLayer == m_contentsLayer->platformLayer()) + return; + + // Create the PlatformCALayer to wrap the incoming layer + m_contentsLayer = mediaLayer ? PlatformCALayer::create(mediaLayer, this) : 0; + + m_contentsLayerPurpose = mediaLayer ? ContentsLayerForMedia : NoContentsLayer; + + noteSublayersChanged(); + noteLayerPropertyChanged(ContentsMediaLayerChanged); +} + +void GraphicsLayerCA::setContentsToCanvas(PlatformLayer* canvasLayer) +{ + if (m_contentsLayer && canvasLayer == m_contentsLayer->platformLayer()) + return; + + // Create the PlatformCALayer to wrap the incoming layer + m_contentsLayer = canvasLayer ? PlatformCALayer::create(canvasLayer, this) : 0; + + m_contentsLayerPurpose = canvasLayer ? ContentsLayerForCanvas : NoContentsLayer; + + noteSublayersChanged(); + noteLayerPropertyChanged(ContentsCanvasLayerChanged); +} + +void GraphicsLayerCA::didDisplay(PlatformLayer* layer) +{ + PlatformCALayer* currentLayer = PlatformCALayer::platformCALayer(layer); + PlatformCALayer* sourceLayer; + LayerMap* layerCloneMap; + + if (currentLayer == m_layer) { + sourceLayer = m_layer.get(); + layerCloneMap = m_layerClones.get(); + } else if (currentLayer == m_contentsLayer) { + sourceLayer = m_contentsLayer.get(); + layerCloneMap = m_contentsLayerClones.get(); + } else + return; + + if (layerCloneMap) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + PlatformCALayer* currClone = it->second.get(); + if (!currClone) + continue; + + if (currClone->contents() != sourceLayer->contents()) + currClone->setContents(sourceLayer->contents()); + else + currClone->setContentsChanged(); + } + } +} + +void GraphicsLayerCA::syncCompositingState() +{ + recursiveCommitChanges(); +} + +void GraphicsLayerCA::syncCompositingStateForThisLayerOnly() +{ + commitLayerChangesBeforeSublayers(); + commitLayerChangesAfterSublayers(); +} + +void GraphicsLayerCA::recursiveCommitChanges() +{ + commitLayerChangesBeforeSublayers(); + + if (m_maskLayer) + static_cast<GraphicsLayerCA*>(m_maskLayer)->commitLayerChangesBeforeSublayers(); + + const Vector<GraphicsLayer*>& childLayers = children(); + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerCA* curChild = static_cast<GraphicsLayerCA*>(childLayers[i]); + curChild->recursiveCommitChanges(); + } + + if (m_replicaLayer) + static_cast<GraphicsLayerCA*>(m_replicaLayer)->recursiveCommitChanges(); + + if (m_maskLayer) + static_cast<GraphicsLayerCA*>(m_maskLayer)->commitLayerChangesAfterSublayers(); + + commitLayerChangesAfterSublayers(); +} + +void GraphicsLayerCA::commitLayerChangesBeforeSublayers() +{ + if (!m_uncommittedChanges) + return; + + // Need to handle Preserves3DChanged first, because it affects which layers subsequent properties are applied to + if (m_uncommittedChanges & (Preserves3DChanged | ReplicatedLayerChanged)) + updateStructuralLayer(); + + if (m_uncommittedChanges & NameChanged) + updateLayerNames(); + + if (m_uncommittedChanges & ContentsImageChanged) // Needs to happen before ChildrenChanged + updateContentsImage(); + + if (m_uncommittedChanges & ContentsMediaLayerChanged) // Needs to happen before ChildrenChanged + updateContentsMediaLayer(); + + if (m_uncommittedChanges & ContentsCanvasLayerChanged) // Needs to happen before ChildrenChanged + updateContentsCanvasLayer(); + + if (m_uncommittedChanges & BackgroundColorChanged) // Needs to happen before ChildrenChanged, and after updating image or video + updateLayerBackgroundColor(); + + if (m_uncommittedChanges & ChildrenChanged) + updateSublayerList(); + + if (m_uncommittedChanges & PositionChanged) + updateLayerPosition(); + + if (m_uncommittedChanges & AnchorPointChanged) + updateAnchorPoint(); + + if (m_uncommittedChanges & SizeChanged) + updateLayerSize(); + + if (m_uncommittedChanges & TransformChanged) + updateTransform(); + + if (m_uncommittedChanges & ChildrenTransformChanged) + updateChildrenTransform(); + + if (m_uncommittedChanges & MasksToBoundsChanged) + updateMasksToBounds(); + + if (m_uncommittedChanges & DrawsContentChanged) + updateLayerDrawsContent(); + + if (m_uncommittedChanges & ContentsOpaqueChanged) + updateContentsOpaque(); + + if (m_uncommittedChanges & BackfaceVisibilityChanged) + updateBackfaceVisibility(); + + if (m_uncommittedChanges & OpacityChanged) + updateOpacityOnLayer(); + + if (m_uncommittedChanges & AnimationChanged) + updateLayerAnimations(); + + if (m_uncommittedChanges & DirtyRectsChanged) + repaintLayerDirtyRects(); + + if (m_uncommittedChanges & ContentsRectChanged) + updateContentsRect(); + + if (m_uncommittedChanges & MaskLayerChanged) + updateMaskLayer(); + + if (m_uncommittedChanges & ContentsNeedsDisplay) + updateContentsNeedsDisplay(); + + if (m_uncommittedChanges & AcceleratesDrawingChanged) + updateAcceleratesDrawing(); +} + +void GraphicsLayerCA::commitLayerChangesAfterSublayers() +{ + if (!m_uncommittedChanges) + return; + + if (m_uncommittedChanges & ReplicatedLayerChanged) + updateReplicatedLayers(); + + m_uncommittedChanges = NoChange; +} + +void GraphicsLayerCA::updateLayerNames() +{ + switch (structuralLayerPurpose()) { + case StructuralLayerForPreserves3D: + m_structuralLayer->setName("Transform layer " + name()); + break; + case StructuralLayerForReplicaFlattening: + m_structuralLayer->setName("Replica flattening layer " + name()); + break; + case NoStructuralLayer: + break; + } + m_layer->setName(name()); +} + +void GraphicsLayerCA::updateSublayerList() +{ + PlatformCALayerList newSublayers; + const Vector<GraphicsLayer*>& childLayers = children(); + + if (m_structuralLayer || m_contentsLayer || childLayers.size() > 0) { + if (m_structuralLayer) { + // Add the replica layer first. + if (m_replicaLayer) + newSublayers.append(static_cast<GraphicsLayerCA*>(m_replicaLayer)->primaryLayer()); + // Add the primary layer. Even if we have negative z-order children, the primary layer always comes behind. + newSublayers.append(m_layer); + } else if (m_contentsLayer) { + // FIXME: add the contents layer in the correct order with negative z-order children. + // This does not cause visible rendering issues because currently contents layers are only used + // for replaced elements that don't have children. + newSublayers.append(m_contentsLayer); + } + + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerCA* curChild = static_cast<GraphicsLayerCA*>(childLayers[i]); + PlatformCALayer* childLayer = curChild->layerForSuperlayer(); + newSublayers.append(childLayer); + } + + for (size_t i = 0; i < newSublayers.size(); ++i) + newSublayers[i]->removeFromSuperlayer(); + } + + if (m_structuralLayer) { + m_structuralLayer->setSublayers(newSublayers); + + if (m_contentsLayer) { + // If we have a transform layer, then the contents layer is parented in the + // primary layer (which is itself a child of the transform layer). + m_layer->removeAllSublayers(); + m_layer->appendSublayer(m_contentsLayer.get()); + } + } else + m_layer->setSublayers(newSublayers); +} + +void GraphicsLayerCA::updateLayerPosition() +{ + FloatSize usedSize = m_usingTiledLayer ? constrainedSize() : m_size; + + // Position is offset on the layer by the layer anchor point. + FloatPoint posPoint(m_position.x() + m_anchorPoint.x() * usedSize.width(), + m_position.y() + m_anchorPoint.y() * usedSize.height()); + + primaryLayer()->setPosition(posPoint); + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + FloatPoint clonePosition = posPoint; + if (m_replicaLayer && isReplicatedRootClone(it->first)) { + // Maintain the special-case position for the root of a clone subtree, + // which we set up in replicatedLayerRoot(). + clonePosition = positionForCloneRootLayer(); + } + it->second->setPosition(clonePosition); + } + } +} + +void GraphicsLayerCA::updateLayerSize() +{ + FloatRect rect(0, 0, m_size.width(), m_size.height()); + if (m_structuralLayer) { + m_structuralLayer->setBounds(rect); + + if (LayerMap* layerCloneMap = m_structuralLayerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + it->second->setBounds(rect); + } + + // The anchor of the contents layer is always at 0.5, 0.5, so the position is center-relative. + CGPoint centerPoint = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); + m_layer->setPosition(centerPoint); + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + it->second->setPosition(centerPoint); + } + } + + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); + + if (m_usingTiledLayer) { + FloatSize sizeToUse = constrainedSize(); + rect = CGRectMake(0, 0, sizeToUse.width(), sizeToUse.height()); + } + + m_layer->setBounds(rect); + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + it->second->setBounds(rect); + } + + // Contents transform may depend on height. + updateContentsTransform(); + + // Note that we don't resize m_contentsLayer. It's up the caller to do that. + + // if we've changed the bounds, we need to recalculate the position + // of the layer, taking anchor point into account. + updateLayerPosition(); +} + +void GraphicsLayerCA::updateAnchorPoint() +{ + primaryLayer()->setAnchorPoint(m_anchorPoint); + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + PlatformCALayer* currLayer = it->second.get(); + currLayer->setAnchorPoint(m_anchorPoint); + } + } + + updateLayerPosition(); +} + +void GraphicsLayerCA::updateTransform() +{ + primaryLayer()->setTransform(m_transform); + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + PlatformCALayer* currLayer = it->second.get(); + if (m_replicaLayer && isReplicatedRootClone(it->first)) { + // Maintain the special-case transform for the root of a clone subtree, + // which we set up in replicatedLayerRoot(). + currLayer->setTransform(TransformationMatrix()); + } else + currLayer->setTransform(m_transform); + } + } +} + +void GraphicsLayerCA::updateChildrenTransform() +{ + primaryLayer()->setSublayerTransform(m_childrenTransform); + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + it->second->setSublayerTransform(m_childrenTransform); + } +} + +void GraphicsLayerCA::updateMasksToBounds() +{ + m_layer->setMasksToBounds(m_masksToBounds); + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + it->second->setMasksToBounds(m_masksToBounds); + } + + updateDebugIndicators(); +} + +void GraphicsLayerCA::updateContentsOpaque() +{ + m_layer.get()->setOpaque(m_contentsOpaque); + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + it->second->setOpaque(m_contentsOpaque); + } +} + +void GraphicsLayerCA::updateBackfaceVisibility() +{ + if (m_structuralLayer && structuralLayerPurpose() == StructuralLayerForReplicaFlattening) { + m_structuralLayer->setDoubleSided(m_backfaceVisibility); + + if (LayerMap* layerCloneMap = m_structuralLayerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + it->second->setDoubleSided(m_backfaceVisibility); + } + } + + m_layer->setDoubleSided(m_backfaceVisibility); + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + it->second->setDoubleSided(m_backfaceVisibility); + } +} + +void GraphicsLayerCA::updateStructuralLayer() +{ + ensureStructuralLayer(structuralLayerPurpose()); +} + +void GraphicsLayerCA::ensureStructuralLayer(StructuralLayerPurpose purpose) +{ + if (purpose == NoStructuralLayer) { + if (m_structuralLayer) { + // Replace the transformLayer in the parent with this layer. + m_layer->removeFromSuperlayer(); + m_structuralLayer->superlayer()->replaceSublayer(m_structuralLayer.get(), m_layer.get()); + + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyWebkitTransform, m_structuralLayer.get(), m_layer.get()); + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyOpacity, m_structuralLayer.get(), m_layer.get()); + + // Release the structural layer. + m_structuralLayer = 0; + + // Update the properties of m_layer now that we no longer have a structural layer. + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + + updateSublayerList(); + updateOpacityOnLayer(); + } + return; + } + + bool structuralLayerChanged = false; + + if (purpose == StructuralLayerForPreserves3D) { + if (m_structuralLayer && m_structuralLayer->layerType() != PlatformCALayer::LayerTypeTransformLayer) + m_structuralLayer = 0; + + if (!m_structuralLayer) { + m_structuralLayer = PlatformCALayer::create(PlatformCALayer::LayerTypeTransformLayer, this); + structuralLayerChanged = true; + } + } else { + if (m_structuralLayer && m_structuralLayer->layerType() != PlatformCALayer::LayerTypeLayer) + m_structuralLayer = 0; + + if (!m_structuralLayer) { + m_structuralLayer = PlatformCALayer::create(PlatformCALayer::LayerTypeLayer, this); + structuralLayerChanged = true; + } + } + + if (!structuralLayerChanged) + return; + + updateLayerNames(); + + // Update the properties of the structural layer. + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + updateBackfaceVisibility(); + + // Set properties of m_layer to their default values, since these are expressed on on the structural layer. + FloatPoint point(m_size.width() / 2.0f, m_size.height() / 2.0f); + FloatPoint3D anchorPoint(0.5f, 0.5f, 0); + m_layer->setPosition(point); + m_layer->setAnchorPoint(anchorPoint); + m_layer->setTransform(TransformationMatrix()); + m_layer->setOpacity(1); + if (m_layerClones) { + LayerMap::const_iterator end = m_layerClones->end(); + for (LayerMap::const_iterator it = m_layerClones->begin(); it != end; ++it) { + PlatformCALayer* currLayer = it->second.get(); + currLayer->setPosition(point); + currLayer->setAnchorPoint(anchorPoint); + currLayer->setTransform(TransformationMatrix()); + currLayer->setOpacity(1); + } + } + + // Move this layer to be a child of the transform layer. + m_layer->superlayer()->replaceSublayer(m_layer.get(), m_structuralLayer.get()); + m_structuralLayer->appendSublayer(m_layer.get()); + + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyWebkitTransform, m_layer.get(), m_structuralLayer.get()); + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyOpacity, m_layer.get(), m_structuralLayer.get()); + + updateSublayerList(); + updateOpacityOnLayer(); +} + +GraphicsLayerCA::StructuralLayerPurpose GraphicsLayerCA::structuralLayerPurpose() const +{ + if (preserves3D()) + return StructuralLayerForPreserves3D; + + if (isReplicated()) + return StructuralLayerForReplicaFlattening; + + return NoStructuralLayer; +} + +void GraphicsLayerCA::updateLayerDrawsContent() +{ + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); + + if (m_drawsContent) + m_layer->setNeedsDisplay(); + else + m_layer->setContents(0); + + updateDebugIndicators(); +} + +void GraphicsLayerCA::updateAcceleratesDrawing() +{ + m_layer->setAcceleratesDrawing(m_acceleratesDrawing); +} + +void GraphicsLayerCA::updateLayerBackgroundColor() +{ + if (!m_contentsLayer) + return; + + // We never create the contents layer just for background color yet. + if (m_backgroundColorSet) + m_contentsLayer->setBackgroundColor(m_backgroundColor); + else + m_contentsLayer->setBackgroundColor(Color::transparent); +} + +void GraphicsLayerCA::updateContentsImage() +{ + if (m_pendingContentsImage) { + if (!m_contentsLayer.get()) { + m_contentsLayer = PlatformCALayer::create(PlatformCALayer::LayerTypeLayer, this); +#ifndef NDEBUG + m_contentsLayer->setName("Image Layer"); +#endif + setupContentsLayer(m_contentsLayer.get()); + // m_contentsLayer will be parented by updateSublayerList + } + + // FIXME: maybe only do trilinear if the image is being scaled down, + // but then what if the layer size changes? +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + m_contentsLayer->setMinificationFilter(PlatformCALayer::Trilinear); +#endif + m_contentsLayer->setContents(m_pendingContentsImage.get()); + m_pendingContentsImage = 0; + + if (m_contentsLayerClones) { + LayerMap::const_iterator end = m_contentsLayerClones->end(); + for (LayerMap::const_iterator it = m_contentsLayerClones->begin(); it != end; ++it) + it->second->setContents(m_contentsLayer->contents()); + } + + updateContentsRect(); + } else { + // No image. + // m_contentsLayer will be removed via updateSublayerList. + m_contentsLayer = 0; + } +} + +void GraphicsLayerCA::updateContentsMediaLayer() +{ + // Video layer was set as m_contentsLayer, and will get parented in updateSublayerList(). + if (m_contentsLayer) { + setupContentsLayer(m_contentsLayer.get()); + updateContentsRect(); + } +} + +void GraphicsLayerCA::updateContentsCanvasLayer() +{ + // CanvasLayer was set as m_contentsLayer, and will get parented in updateSublayerList(). + if (m_contentsLayer) { + setupContentsLayer(m_contentsLayer.get()); + m_contentsLayer->setNeedsDisplay(); + updateContentsRect(); + } +} + +void GraphicsLayerCA::updateContentsRect() +{ + if (!m_contentsLayer) + return; + + FloatPoint point(m_contentsRect.x(), m_contentsRect.y()); + FloatRect rect(0, 0, m_contentsRect.width(), m_contentsRect.height()); + + m_contentsLayer->setPosition(point); + m_contentsLayer->setBounds(rect); + + if (m_contentsLayerClones) { + LayerMap::const_iterator end = m_contentsLayerClones->end(); + for (LayerMap::const_iterator it = m_contentsLayerClones->begin(); it != end; ++it) { + it->second->setPosition(point); + it->second->setBounds(rect); + } + } +} + +void GraphicsLayerCA::updateMaskLayer() +{ + PlatformCALayer* maskCALayer = m_maskLayer ? static_cast<GraphicsLayerCA*>(m_maskLayer)->primaryLayer() : 0; + m_layer->setMask(maskCALayer); + + LayerMap* maskLayerCloneMap = m_maskLayer ? static_cast<GraphicsLayerCA*>(m_maskLayer)->primaryLayerClones() : 0; + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + PlatformCALayer* maskClone = maskLayerCloneMap ? maskLayerCloneMap->get(it->first).get() : 0; + it->second->setMask(maskClone); + } + } +} + +void GraphicsLayerCA::updateReplicatedLayers() +{ + // Clone the descendants of the replicated layer, and parent under us. + ReplicaState replicaState(ReplicaState::ReplicaBranch); + + RefPtr<PlatformCALayer>replicaRoot = replicatedLayerRoot(replicaState); + if (!replicaRoot) + return; + + if (m_structuralLayer) + m_structuralLayer->insertSublayer(replicaRoot.get(), 0); + else + m_layer->insertSublayer(replicaRoot.get(), 0); +} + +// For now, this assumes that layers only ever have one replica, so replicaIndices contains only 0 and 1. +GraphicsLayerCA::CloneID GraphicsLayerCA::ReplicaState::cloneID() const +{ + size_t depth = m_replicaBranches.size(); + + const size_t bitsPerUChar = sizeof(UChar) * 8; + size_t vectorSize = (depth + bitsPerUChar - 1) / bitsPerUChar; + + Vector<UChar> result(vectorSize); + result.fill(0); + + // Create a string from the bit sequence which we can use to identify the clone. + // Note that the string may contain embedded nulls, but that's OK. + for (size_t i = 0; i < depth; ++i) { + UChar& currChar = result[i / bitsPerUChar]; + currChar = (currChar << 1) | m_replicaBranches[i]; + } + + return String::adopt(result); +} + +PassRefPtr<PlatformCALayer> GraphicsLayerCA::replicatedLayerRoot(ReplicaState& replicaState) +{ + // Limit replica nesting, to avoid 2^N explosion of replica layers. + if (!m_replicatedLayer || replicaState.replicaDepth() == ReplicaState::maxReplicaDepth) + return 0; + + GraphicsLayerCA* replicatedLayer = static_cast<GraphicsLayerCA*>(m_replicatedLayer); + + RefPtr<PlatformCALayer> clonedLayerRoot = replicatedLayer->fetchCloneLayers(this, replicaState, RootCloneLevel); + FloatPoint cloneRootPosition = replicatedLayer->positionForCloneRootLayer(); + + // Replica root has no offset or transform + clonedLayerRoot->setPosition(cloneRootPosition); + clonedLayerRoot->setTransform(TransformationMatrix()); + + return clonedLayerRoot; +} + +void GraphicsLayerCA::updateLayerAnimations() +{ + if (m_animationsToProcess.size()) { + AnimationsToProcessMap::const_iterator end = m_animationsToProcess.end(); + for (AnimationsToProcessMap::const_iterator it = m_animationsToProcess.begin(); it != end; ++it) { + const String& currAnimationName = it->first; + AnimationsMap::iterator animationIt = m_runningAnimations.find(currAnimationName); + if (animationIt == m_runningAnimations.end()) + continue; + + const AnimationProcessingAction& processingInfo = it->second; + const Vector<LayerPropertyAnimation>& animations = animationIt->second; + for (size_t i = 0; i < animations.size(); ++i) { + const LayerPropertyAnimation& currAnimation = animations[i]; + switch (processingInfo.action) { + case Remove: + removeCAAnimationFromLayer(currAnimation.m_property, currAnimationName, currAnimation.m_index); + break; + case Pause: + pauseCAAnimationOnLayer(currAnimation.m_property, currAnimationName, currAnimation.m_index, processingInfo.timeOffset); + break; + } + } + + if (processingInfo.action == Remove) + m_runningAnimations.remove(currAnimationName); + } + + m_animationsToProcess.clear(); + } + + size_t numAnimations; + if ((numAnimations = m_uncomittedAnimations.size())) { + for (size_t i = 0; i < numAnimations; ++i) { + const LayerPropertyAnimation& pendingAnimation = m_uncomittedAnimations[i]; + setAnimationOnLayer(pendingAnimation.m_animation.get(), pendingAnimation.m_property, pendingAnimation.m_name, pendingAnimation.m_index, pendingAnimation.m_timeOffset); + + AnimationsMap::iterator it = m_runningAnimations.find(pendingAnimation.m_name); + if (it == m_runningAnimations.end()) { + Vector<LayerPropertyAnimation> animations; + animations.append(pendingAnimation); + m_runningAnimations.add(pendingAnimation.m_name, animations); + } else { + Vector<LayerPropertyAnimation>& animations = it->second; + animations.append(pendingAnimation); + } + } + + m_uncomittedAnimations.clear(); + } +} + +void GraphicsLayerCA::setAnimationOnLayer(PlatformCAAnimation* caAnim, AnimatedPropertyID property, const String& animationName, int index, double timeOffset) +{ + PlatformCALayer* layer = animatedLayer(property); + + if (timeOffset) + caAnim->setBeginTime(CACurrentMediaTime() - timeOffset); + + String animationID = animationIdentifier(animationName, property, index); + + layer->removeAnimationForKey(animationID); + layer->addAnimationForKey(animationID, caAnim); + + if (LayerMap* layerCloneMap = animatedLayerClones(property)) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + // Skip immediate replicas, since they move with the original. + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + + it->second->removeAnimationForKey(animationID); + it->second->addAnimationForKey(animationID, caAnim); + } + } +} + +// Workaround for <rdar://problem/7311367> +static void bug7311367Workaround(PlatformCALayer* transformLayer, const TransformationMatrix& transform) +{ + if (!transformLayer) + return; + + TransformationMatrix caTransform = transform; + caTransform.setM41(caTransform.m41() + 1); + transformLayer->setTransform(caTransform); + + caTransform.setM41(caTransform.m41() - 1); + transformLayer->setTransform(caTransform); +} + +bool GraphicsLayerCA::removeCAAnimationFromLayer(AnimatedPropertyID property, const String& animationName, int index) +{ + PlatformCALayer* layer = animatedLayer(property); + + String animationID = animationIdentifier(animationName, property, index); + + if (!layer->animationForKey(animationID)) + return false; + + layer->removeAnimationForKey(animationID); + bug7311367Workaround(m_structuralLayer.get(), m_transform); + + if (LayerMap* layerCloneMap = animatedLayerClones(property)) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + // Skip immediate replicas, since they move with the original. + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + + it->second ->removeAnimationForKey(animationID); + } + } + return true; +} + +void GraphicsLayerCA::pauseCAAnimationOnLayer(AnimatedPropertyID property, const String& animationName, int index, double timeOffset) +{ + PlatformCALayer* layer = animatedLayer(property); + + String animationID = animationIdentifier(animationName, property, index); + + RefPtr<PlatformCAAnimation> curAnim = layer->animationForKey(animationID); + if (!curAnim) + return; + + // Animations on the layer are immutable, so we have to clone and modify. + RefPtr<PlatformCAAnimation> newAnim = PlatformCAAnimation::create(curAnim.get()); + + newAnim->setSpeed(0); + newAnim->setTimeOffset(timeOffset); + + layer->addAnimationForKey(animationID, newAnim.get()); // This will replace the running animation. + + // Pause the animations on the clones too. + if (LayerMap* layerCloneMap = animatedLayerClones(property)) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + // Skip immediate replicas, since they move with the original. + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + it->second->addAnimationForKey(animationID, newAnim.get()); + } + } +} + +void GraphicsLayerCA::repaintLayerDirtyRects() +{ + if (!m_dirtyRects.size()) + return; + + for (size_t i = 0; i < m_dirtyRects.size(); ++i) + m_layer->setNeedsDisplay(&(m_dirtyRects[i])); + + m_dirtyRects.clear(); +} + +void GraphicsLayerCA::updateContentsNeedsDisplay() +{ + if (m_contentsLayer) + m_contentsLayer->setNeedsDisplay(); +} + +bool GraphicsLayerCA::createAnimationFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& animationName, double timeOffset) +{ + ASSERT(valueList.property() != AnimatedPropertyWebkitTransform); + + bool isKeyframe = valueList.size() > 2; + bool valuesOK; + + bool additive = false; + int animationIndex = 0; + + RefPtr<PlatformCAAnimation> caAnimation; + + if (isKeyframe) { + caAnimation = createKeyframeAnimation(animation, valueList.property(), additive); + valuesOK = setAnimationKeyframes(valueList, animation, caAnimation.get()); + } else { + caAnimation = createBasicAnimation(animation, valueList.property(), additive); + valuesOK = setAnimationEndpoints(valueList, animation, caAnimation.get()); + } + + if (!valuesOK) + return false; + + m_uncomittedAnimations.append(LayerPropertyAnimation(caAnimation, animationName, valueList.property(), animationIndex, timeOffset)); + + return true; +} + +bool GraphicsLayerCA::createTransformAnimationsFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& animationName, double timeOffset, const IntSize& boxSize) +{ + ASSERT(valueList.property() == AnimatedPropertyWebkitTransform); + + TransformOperationList functionList; + bool listsMatch, hasBigRotation; + fetchTransformOperationList(valueList, functionList, listsMatch, hasBigRotation); + + // We need to fall back to software animation if we don't have setValueFunction:, and + // we would need to animate each incoming transform function separately. This is the + // case if we have a rotation >= 180 or we have more than one transform function. + if ((hasBigRotation || functionList.size() > 1) && !PlatformCAAnimation::supportsValueFunction()) + return false; + + bool validMatrices = true; + + // If functionLists don't match we do a matrix animation, otherwise we do a component hardware animation. + // Also, we can't do component animation unless we have valueFunction, so we need to do matrix animation + // if that's not true as well. + bool isMatrixAnimation = !listsMatch || !PlatformCAAnimation::supportsValueFunction(); + + size_t numAnimations = isMatrixAnimation ? 1 : functionList.size(); + bool isKeyframe = valueList.size() > 2; + + // Iterate through the transform functions, sending an animation for each one. + for (size_t animationIndex = 0; animationIndex < numAnimations; ++animationIndex) { + TransformOperation::OperationType transformOp = isMatrixAnimation ? TransformOperation::MATRIX_3D : functionList[animationIndex]; + RefPtr<PlatformCAAnimation> caAnimation; + +#if defined(BUILDING_ON_LEOPARD) || defined(BUILDING_ON_SNOW_LEOPARD) + // CA applies animations in reverse order (<rdar://problem/7095638>) so we need the last one we add (per property) + // to be non-additive. + bool additive = animationIndex < (numAnimations - 1); +#else + bool additive = animationIndex > 0; +#endif + if (isKeyframe) { + caAnimation = createKeyframeAnimation(animation, valueList.property(), additive); + validMatrices = setTransformAnimationKeyframes(valueList, animation, caAnimation.get(), animationIndex, transformOp, isMatrixAnimation, boxSize); + } else { + caAnimation = createBasicAnimation(animation, valueList.property(), additive); + validMatrices = setTransformAnimationEndpoints(valueList, animation, caAnimation.get(), animationIndex, transformOp, isMatrixAnimation, boxSize); + } + + if (!validMatrices) + break; + + m_uncomittedAnimations.append(LayerPropertyAnimation(caAnimation, animationName, valueList.property(), animationIndex, timeOffset)); + } + + return validMatrices; +} + +PassRefPtr<PlatformCAAnimation> GraphicsLayerCA::createBasicAnimation(const Animation* anim, AnimatedPropertyID property, bool additive) +{ + RefPtr<PlatformCAAnimation> basicAnim = PlatformCAAnimation::create(PlatformCAAnimation::Basic, propertyIdToString(property)); + setupAnimation(basicAnim.get(), anim, additive); + return basicAnim; +} + +PassRefPtr<PlatformCAAnimation>GraphicsLayerCA::createKeyframeAnimation(const Animation* anim, AnimatedPropertyID property, bool additive) +{ + RefPtr<PlatformCAAnimation> keyframeAnim = PlatformCAAnimation::create(PlatformCAAnimation::Keyframe, propertyIdToString(property)); + setupAnimation(keyframeAnim.get(), anim, additive); + return keyframeAnim; +} + +void GraphicsLayerCA::setupAnimation(PlatformCAAnimation* propertyAnim, const Animation* anim, bool additive) +{ + double duration = anim->duration(); + if (duration <= 0) + duration = cAnimationAlmostZeroDuration; + + float repeatCount = anim->iterationCount(); + if (repeatCount == Animation::IterationCountInfinite) + repeatCount = FLT_MAX; + else if (anim->direction() == Animation::AnimationDirectionAlternate) + repeatCount /= 2; + + PlatformCAAnimation::FillModeType fillMode = PlatformCAAnimation::NoFillMode; + switch (anim->fillMode()) { + case AnimationFillModeNone: + fillMode = PlatformCAAnimation::Forwards; // Use "forwards" rather than "removed" because the style system will remove the animation when it is finished. This avoids a flash. + break; + case AnimationFillModeBackwards: + fillMode = PlatformCAAnimation::Both; // Use "both" rather than "backwards" because the style system will remove the animation when it is finished. This avoids a flash. + break; + case AnimationFillModeForwards: + fillMode = PlatformCAAnimation::Forwards; + break; + case AnimationFillModeBoth: + fillMode = PlatformCAAnimation::Both; + break; + } + + propertyAnim->setDuration(duration); + propertyAnim->setRepeatCount(repeatCount); + propertyAnim->setAutoreverses(anim->direction()); + propertyAnim->setRemovedOnCompletion(false); + propertyAnim->setAdditive(additive); + propertyAnim->setFillMode(fillMode); +} + +const TimingFunction* GraphicsLayerCA::timingFunctionForAnimationValue(const AnimationValue* animValue, const Animation* anim) +{ + if (animValue->timingFunction()) + return animValue->timingFunction(); + if (anim->isTimingFunctionSet()) + return anim->timingFunction().get(); + + return 0; +} + +bool GraphicsLayerCA::setAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, PlatformCAAnimation* basicAnim) +{ + switch (valueList.property()) { + case AnimatedPropertyOpacity: { + basicAnim->setFromValue(static_cast<const FloatAnimationValue*>(valueList.at(0))->value()); + basicAnim->setToValue(static_cast<const FloatAnimationValue*>(valueList.at(1))->value()); + break; + } + default: + ASSERT_NOT_REACHED(); // we don't animate color yet + break; + } + + // This codepath is used for 2-keyframe animations, so we still need to look in the start + // for a timing function. + const TimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim); + if (timingFunction) + basicAnim->setTimingFunction(timingFunction); + + return true; +} + +bool GraphicsLayerCA::setAnimationKeyframes(const KeyframeValueList& valueList, const Animation* anim, PlatformCAAnimation* keyframeAnim) +{ + Vector<float> keyTimes; + Vector<float> values; + Vector<const TimingFunction*> timingFunctions; + + for (unsigned i = 0; i < valueList.size(); ++i) { + const AnimationValue* curValue = valueList.at(i); + keyTimes.append(curValue->keyTime()); + + switch (valueList.property()) { + case AnimatedPropertyOpacity: { + const FloatAnimationValue* floatValue = static_cast<const FloatAnimationValue*>(curValue); + values.append(floatValue->value()); + break; + } + default: + ASSERT_NOT_REACHED(); // we don't animate color yet + break; + } + + timingFunctions.append(timingFunctionForAnimationValue(curValue, anim)); + } + + // We toss the last tfArray value because it has to one shorter than the others. + timingFunctions.removeLast(); + + keyframeAnim->setKeyTimes(keyTimes); + keyframeAnim->setValues(values); + keyframeAnim->setTimingFunctions(timingFunctions); + + return true; +} + +bool GraphicsLayerCA::setTransformAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, PlatformCAAnimation* basicAnim, int functionIndex, TransformOperation::OperationType transformOpType, bool isMatrixAnimation, const IntSize& boxSize) +{ + ASSERT(valueList.size() == 2); + const TransformAnimationValue* startValue = static_cast<const TransformAnimationValue*>(valueList.at(0)); + const TransformAnimationValue* endValue = static_cast<const TransformAnimationValue*>(valueList.at(1)); + + if (isMatrixAnimation) { + TransformationMatrix fromTransform, toTransform; + startValue->value()->apply(boxSize, fromTransform); + endValue->value()->apply(boxSize, toTransform); + + // If any matrix is singular, CA won't animate it correctly. So fall back to software animation + if (!fromTransform.isInvertible() || !toTransform.isInvertible()) + return false; + + basicAnim->setFromValue(fromTransform); + basicAnim->setToValue(toTransform); + } else { + if (isTransformTypeNumber(transformOpType)) { + float value; + getTransformFunctionValue(startValue->value()->at(functionIndex), transformOpType, boxSize, value); + basicAnim->setFromValue(value); + getTransformFunctionValue(endValue->value()->at(functionIndex), transformOpType, boxSize, value); + basicAnim->setToValue(value); + } else if (isTransformTypeFloatPoint3D(transformOpType)) { + FloatPoint3D value; + getTransformFunctionValue(startValue->value()->at(functionIndex), transformOpType, boxSize, value); + basicAnim->setFromValue(value); + getTransformFunctionValue(endValue->value()->at(functionIndex), transformOpType, boxSize, value); + basicAnim->setToValue(value); + } else { + TransformationMatrix value; + getTransformFunctionValue(startValue->value()->at(functionIndex), transformOpType, boxSize, value); + basicAnim->setFromValue(value); + getTransformFunctionValue(endValue->value()->at(functionIndex), transformOpType, boxSize, value); + basicAnim->setToValue(value); + } + } + + // This codepath is used for 2-keyframe animations, so we still need to look in the start + // for a timing function. + const TimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim); + basicAnim->setTimingFunction(timingFunction); + +#if HAVE_MODERN_QUARTZCORE + PlatformCAAnimation::ValueFunctionType valueFunction = getValueFunctionNameForTransformOperation(transformOpType); + if (valueFunction != PlatformCAAnimation::NoValueFunction) + basicAnim->setValueFunction(valueFunction); +#endif + + return true; +} + +bool GraphicsLayerCA::setTransformAnimationKeyframes(const KeyframeValueList& valueList, const Animation* animation, PlatformCAAnimation* keyframeAnim, int functionIndex, TransformOperation::OperationType transformOpType, bool isMatrixAnimation, const IntSize& boxSize) +{ + Vector<float> keyTimes; + Vector<float> floatValues; + Vector<FloatPoint3D> floatPoint3DValues; + Vector<TransformationMatrix> transformationMatrixValues; + Vector<const TimingFunction*> timingFunctions; + + for (unsigned i = 0; i < valueList.size(); ++i) { + const TransformAnimationValue* curValue = static_cast<const TransformAnimationValue*>(valueList.at(i)); + keyTimes.append(curValue->keyTime()); + + if (isMatrixAnimation) { + TransformationMatrix transform; + curValue->value()->apply(boxSize, transform); + + // If any matrix is singular, CA won't animate it correctly. So fall back to software animation + if (!transform.isInvertible()) + return false; + + transformationMatrixValues.append(transform); + } else { + const TransformOperation* transformOp = curValue->value()->at(functionIndex); + if (isTransformTypeNumber(transformOpType)) { + float value; + getTransformFunctionValue(transformOp, transformOpType, boxSize, value); + floatValues.append(value); + } else if (isTransformTypeFloatPoint3D(transformOpType)) { + FloatPoint3D value; + getTransformFunctionValue(transformOp, transformOpType, boxSize, value); + floatPoint3DValues.append(value); + } else { + TransformationMatrix value; + getTransformFunctionValue(transformOp, transformOpType, boxSize, value); + transformationMatrixValues.append(value); + } + } + + const TimingFunction* timingFunction = timingFunctionForAnimationValue(curValue, animation); + timingFunctions.append(timingFunction); + } + + // We toss the last tfArray value because it has to one shorter than the others. + timingFunctions.removeLast(); + + keyframeAnim->setKeyTimes(keyTimes); + + if (isTransformTypeNumber(transformOpType)) + keyframeAnim->setValues(floatValues); + else if (isTransformTypeFloatPoint3D(transformOpType)) + keyframeAnim->setValues(floatPoint3DValues); + else + keyframeAnim->setValues(transformationMatrixValues); + + keyframeAnim->setTimingFunctions(timingFunctions); + +#if HAVE_MODERN_QUARTZCORE + PlatformCAAnimation::ValueFunctionType valueFunction = getValueFunctionNameForTransformOperation(transformOpType); + if (valueFunction != PlatformCAAnimation::NoValueFunction) + keyframeAnim->setValueFunction(valueFunction); +#endif + return true; +} + +void GraphicsLayerCA::suspendAnimations(double time) +{ + double t = currentTimeToMediaTime(time ? time : currentTime()); + primaryLayer()->setSpeed(0); + primaryLayer()->setTimeOffset(t); + + // Suspend the animations on the clones too. + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + it->second->setSpeed(0); + it->second->setTimeOffset(t); + } + } +} + +void GraphicsLayerCA::resumeAnimations() +{ + primaryLayer()->setSpeed(1); + primaryLayer()->setTimeOffset(0); + + // Resume the animations on the clones too. + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + it->second->setSpeed(1); + it->second->setTimeOffset(0); + } + } +} + +PlatformCALayer* GraphicsLayerCA::hostLayerForSublayers() const +{ + return m_structuralLayer.get() ? m_structuralLayer.get() : m_layer.get(); +} + +PlatformCALayer* GraphicsLayerCA::layerForSuperlayer() const +{ + return m_structuralLayer ? m_structuralLayer.get() : m_layer.get(); +} + +PlatformCALayer* GraphicsLayerCA::animatedLayer(AnimatedPropertyID property) const +{ + return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayer.get() : primaryLayer(); +} + +GraphicsLayerCA::LayerMap* GraphicsLayerCA::animatedLayerClones(AnimatedPropertyID property) const +{ + return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayerClones.get() : primaryLayerClones(); +} + +void GraphicsLayerCA::setDebugBackgroundColor(const Color& color) +{ + if (color.isValid()) + m_layer->setBackgroundColor(color); + else + m_layer->setBackgroundColor(Color::transparent); +} + +void GraphicsLayerCA::setDebugBorder(const Color& color, float borderWidth) +{ + if (color.isValid()) { + m_layer->setBorderColor(color); + m_layer->setBorderWidth(borderWidth); + } else { + m_layer->setBorderColor(Color::transparent); + m_layer->setBorderWidth(0); + } +} + +FloatSize GraphicsLayerCA::constrainedSize() const +{ + float tileColumns = ceilf(m_size.width() / kTiledLayerTileSize); + float tileRows = ceilf(m_size.height() / kTiledLayerTileSize); + double numTiles = tileColumns * tileRows; + + FloatSize constrainedSize = m_size; + const unsigned cMaxTileCount = 512; + while (numTiles > cMaxTileCount) { + // Constrain the wider dimension. + if (constrainedSize.width() >= constrainedSize.height()) { + tileColumns = max(floorf(cMaxTileCount / tileRows), 1.0f); + constrainedSize.setWidth(tileColumns * kTiledLayerTileSize); + } else { + tileRows = max(floorf(cMaxTileCount / tileColumns), 1.0f); + constrainedSize.setHeight(tileRows * kTiledLayerTileSize); + } + numTiles = tileColumns * tileRows; + } + + return constrainedSize; +} + +bool GraphicsLayerCA::requiresTiledLayer(const FloatSize& size) const +{ + if (!m_drawsContent) + return false; + + // FIXME: catch zero-size height or width here (or earlier)? + return size.width() > cMaxPixelDimension || size.height() > cMaxPixelDimension; +} + +void GraphicsLayerCA::swapFromOrToTiledLayer(bool useTiledLayer) +{ + if (useTiledLayer == m_usingTiledLayer) + return; + + RefPtr<PlatformCALayer> oldLayer = m_layer; + + m_layer = PlatformCALayer::create(useTiledLayer ? PlatformCALayer::LayerTypeWebTiledLayer : PlatformCALayer::LayerTypeWebLayer, this); + + m_usingTiledLayer = useTiledLayer; + + if (useTiledLayer) { +#if !HAVE_MODERN_QUARTZCORE + // Tiled layer has issues with flipped coordinates. + setContentsOrientation(CompositingCoordinatesTopDown); +#endif + } else { +#if !HAVE_MODERN_QUARTZCORE + setContentsOrientation(GraphicsLayerCA::defaultContentsOrientation()); +#endif + } + + m_layer->adoptSublayers(oldLayer.get()); + + oldLayer->superlayer()->replaceSublayer(oldLayer.get(), m_layer.get()); + + updateContentsTransform(); + + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + updateMasksToBounds(); + updateContentsOpaque(); + updateBackfaceVisibility(); + updateLayerBackgroundColor(); + + updateOpacityOnLayer(); + +#ifndef NDEBUG + String name = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + m_name; + m_layer->setName(name); +#endif + + // move over animations + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyWebkitTransform, oldLayer.get(), m_layer.get()); + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyOpacity, oldLayer.get(), m_layer.get()); + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyBackgroundColor, oldLayer.get(), m_layer.get()); + + // need to tell new layer to draw itself + setNeedsDisplay(); + + updateDebugIndicators(); +} + +GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayerCA::defaultContentsOrientation() const +{ +#if !HAVE_MODERN_QUARTZCORE + // Older QuartzCore does not support -geometryFlipped, so we manually flip the root + // layer geometry, and then flip the contents of each layer back so that the CTM for CG + // is unflipped, allowing it to do the correct font auto-hinting. + return CompositingCoordinatesBottomUp; +#else + return CompositingCoordinatesTopDown; +#endif +} + +void GraphicsLayerCA::updateContentsTransform() +{ +#if !HAVE_MODERN_QUARTZCORE + if (contentsOrientation() == CompositingCoordinatesBottomUp) { + CGAffineTransform contentsTransform = CGAffineTransformMakeScale(1, -1); + contentsTransform = CGAffineTransformTranslate(contentsTransform, 0, -m_layer->bounds().size().height()); + m_layer->setContentsTransform(TransformationMatrix(contentsTransform)); + } +#endif +} + +void GraphicsLayerCA::setupContentsLayer(PlatformCALayer* contentsLayer) +{ + // Turn off implicit animations on the inner layer. + contentsLayer->setMasksToBounds(true); + + if (defaultContentsOrientation() == CompositingCoordinatesBottomUp) { + TransformationMatrix flipper( + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + contentsLayer->setTransform(flipper); + contentsLayer->setAnchorPoint(FloatPoint3D(0, 1, 0)); + } else + contentsLayer->setAnchorPoint(FloatPoint3D()); + + if (showDebugBorders()) { + contentsLayer->setBorderColor(Color(0, 0, 128, 180)); + contentsLayer->setBorderWidth(1.0f); + } +} + +PassRefPtr<PlatformCALayer> GraphicsLayerCA::findOrMakeClone(CloneID cloneID, PlatformCALayer *sourceLayer, LayerMap* clones, CloneLevel cloneLevel) +{ + if (!sourceLayer) + return 0; + + RefPtr<PlatformCALayer> resultLayer; + + // Add with a dummy value to get an iterator for the insertion position, and a boolean that tells + // us whether there's an item there. This technique avoids two hash lookups. + RefPtr<PlatformCALayer> dummy; + pair<LayerMap::iterator, bool> addResult = clones->add(cloneID, dummy); + if (!addResult.second) { + // Value was not added, so it exists already. + resultLayer = addResult.first->second.get(); + } else { + resultLayer = cloneLayer(sourceLayer, cloneLevel); +#ifndef NDEBUG + resultLayer->setName(String::format("Clone %d of layer %p", cloneID[0U], sourceLayer)); +#endif + addResult.first->second = resultLayer; + } + + return resultLayer; +} + +void GraphicsLayerCA::ensureCloneLayers(CloneID cloneID, RefPtr<PlatformCALayer>& primaryLayer, RefPtr<PlatformCALayer>& structuralLayer, RefPtr<PlatformCALayer>& contentsLayer, CloneLevel cloneLevel) +{ + structuralLayer = 0; + contentsLayer = 0; + + if (!m_layerClones) + m_layerClones = new LayerMap; + + if (!m_structuralLayerClones && m_structuralLayer) + m_structuralLayerClones = new LayerMap; + + if (!m_contentsLayerClones && m_contentsLayer) + m_contentsLayerClones = new LayerMap; + + primaryLayer = findOrMakeClone(cloneID, m_layer.get(), m_layerClones.get(), cloneLevel); + structuralLayer = findOrMakeClone(cloneID, m_structuralLayer.get(), m_structuralLayerClones.get(), cloneLevel); + contentsLayer = findOrMakeClone(cloneID, m_contentsLayer.get(), m_contentsLayerClones.get(), cloneLevel); +} + +void GraphicsLayerCA::removeCloneLayers() +{ + m_layerClones = 0; + m_structuralLayerClones = 0; + m_contentsLayerClones = 0; +} + +FloatPoint GraphicsLayerCA::positionForCloneRootLayer() const +{ + // This can get called during a sync when we've just removed the m_replicaLayer. + if (!m_replicaLayer) + return FloatPoint(); + + FloatPoint replicaPosition = m_replicaLayer->replicatedLayerPosition(); + return FloatPoint(replicaPosition.x() + m_anchorPoint.x() * m_size.width(), + replicaPosition.y() + m_anchorPoint.y() * m_size.height()); +} + +void GraphicsLayerCA::propagateLayerChangeToReplicas() +{ + for (GraphicsLayer* currLayer = this; currLayer; currLayer = currLayer->parent()) { + GraphicsLayerCA* currLayerCA = static_cast<GraphicsLayerCA*>(currLayer); + if (!currLayerCA->hasCloneLayers()) + break; + + if (currLayerCA->replicaLayer()) + static_cast<GraphicsLayerCA*>(currLayerCA->replicaLayer())->noteLayerPropertyChanged(ReplicatedLayerChanged); + } +} + +PassRefPtr<PlatformCALayer> GraphicsLayerCA::fetchCloneLayers(GraphicsLayer* replicaRoot, ReplicaState& replicaState, CloneLevel cloneLevel) +{ + RefPtr<PlatformCALayer> primaryLayer; + RefPtr<PlatformCALayer> structuralLayer; + RefPtr<PlatformCALayer> contentsLayer; + ensureCloneLayers(replicaState.cloneID(), primaryLayer, structuralLayer, contentsLayer, cloneLevel); + + if (m_maskLayer) { + RefPtr<PlatformCALayer> maskClone = static_cast<GraphicsLayerCA*>(m_maskLayer)->fetchCloneLayers(replicaRoot, replicaState, IntermediateCloneLevel); + primaryLayer->setMask(maskClone.get()); + } + + if (m_replicatedLayer) { + // We are a replica being asked for clones of our layers. + RefPtr<PlatformCALayer> replicaRoot = replicatedLayerRoot(replicaState); + if (!replicaRoot) + return 0; + + if (structuralLayer) { + structuralLayer->insertSublayer(replicaRoot.get(), 0); + return structuralLayer; + } + + primaryLayer->insertSublayer(replicaRoot.get(), 0); + return primaryLayer; + } + + const Vector<GraphicsLayer*>& childLayers = children(); + Vector<RefPtr<PlatformCALayer> > clonalSublayers; + + RefPtr<PlatformCALayer> replicaLayer; + + if (m_replicaLayer && m_replicaLayer != replicaRoot) { + // We have nested replicas. Ask the replica layer for a clone of its contents. + replicaState.setBranchType(ReplicaState::ReplicaBranch); + replicaLayer = static_cast<GraphicsLayerCA*>(m_replicaLayer)->fetchCloneLayers(replicaRoot, replicaState, RootCloneLevel); + replicaState.setBranchType(ReplicaState::ChildBranch); + } + + if (replicaLayer || structuralLayer || contentsLayer || childLayers.size() > 0) { + if (structuralLayer) { + // Replicas render behind the actual layer content. + if (replicaLayer) + clonalSublayers.append(replicaLayer); + + // Add the primary layer next. Even if we have negative z-order children, the primary layer always comes behind. + clonalSublayers.append(primaryLayer); + } else if (contentsLayer) { + // FIXME: add the contents layer in the correct order with negative z-order children. + // This does not cause visible rendering issues because currently contents layers are only used + // for replaced elements that don't have children. + clonalSublayers.append(contentsLayer); + } + + replicaState.push(ReplicaState::ChildBranch); + + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerCA* curChild = static_cast<GraphicsLayerCA*>(childLayers[i]); + + RefPtr<PlatformCALayer> childLayer = curChild->fetchCloneLayers(replicaRoot, replicaState, IntermediateCloneLevel); + if (childLayer) + clonalSublayers.append(childLayer); + } + + replicaState.pop(); + + for (size_t i = 0; i < clonalSublayers.size(); ++i) + clonalSublayers[i]->removeFromSuperlayer(); + } + + RefPtr<PlatformCALayer> result; + if (structuralLayer) { + structuralLayer->setSublayers(clonalSublayers); + + if (contentsLayer) { + // If we have a transform layer, then the contents layer is parented in the + // primary layer (which is itself a child of the transform layer). + primaryLayer->removeAllSublayers(); + primaryLayer->appendSublayer(contentsLayer.get()); + } + + result = structuralLayer; + } else { + primaryLayer->setSublayers(clonalSublayers); + result = primaryLayer; + } + + return result; +} + +PassRefPtr<PlatformCALayer> GraphicsLayerCA::cloneLayer(PlatformCALayer *layer, CloneLevel cloneLevel) +{ + PlatformCALayer::LayerType layerType = (layer->layerType() == PlatformCALayer::LayerTypeTransformLayer) ? + PlatformCALayer::LayerTypeTransformLayer : PlatformCALayer::LayerTypeLayer; + RefPtr<PlatformCALayer> newLayer = PlatformCALayer::create(layerType, this); + + newLayer->setPosition(layer->position()); + newLayer->setBounds(layer->bounds()); + newLayer->setAnchorPoint(layer->anchorPoint()); + newLayer->setTransform(layer->transform()); + newLayer->setSublayerTransform(layer->sublayerTransform()); + newLayer->setContents(layer->contents()); + newLayer->setMasksToBounds(layer->masksToBounds()); + newLayer->setDoubleSided(layer->isDoubleSided()); + newLayer->setOpaque(layer->isOpaque()); + newLayer->setBackgroundColor(layer->backgroundColor()); + + if (cloneLevel == IntermediateCloneLevel) { + newLayer->setOpacity(layer->opacity()); + moveOrCopyAnimationsForProperty(Copy, AnimatedPropertyWebkitTransform, layer, newLayer.get()); + moveOrCopyAnimationsForProperty(Copy, AnimatedPropertyOpacity, layer, newLayer.get()); + } + + if (showDebugBorders()) { + newLayer->setBorderColor(Color(255, 122, 251)); + newLayer->setBorderWidth(2); + } + + return newLayer; +} + +void GraphicsLayerCA::setOpacityInternal(float accumulatedOpacity) +{ + LayerMap* layerCloneMap = 0; + + if (preserves3D()) { + m_layer->setOpacity(accumulatedOpacity); + layerCloneMap = m_layerClones.get(); + } else { + primaryLayer()->setOpacity(accumulatedOpacity); + layerCloneMap = primaryLayerClones(); + } + + if (layerCloneMap) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + it->second->setOpacity(m_opacity); + } + } +} + +void GraphicsLayerCA::updateOpacityOnLayer() +{ +#if !HAVE_MODERN_QUARTZCORE + // Distribute opacity either to our own layer or to our children. We pass in the + // contribution from our parent(s). + distributeOpacity(parent() ? parent()->accumulatedOpacity() : 1); +#else + primaryLayer()->setOpacity(m_opacity); + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + + it->second->setOpacity(m_opacity); + } + + } +#endif +} + +void GraphicsLayerCA::noteSublayersChanged() +{ + noteLayerPropertyChanged(ChildrenChanged); + propagateLayerChangeToReplicas(); +} + +void GraphicsLayerCA::noteLayerPropertyChanged(LayerChangeFlags flags) +{ + if (!m_uncommittedChanges && m_client) + m_client->notifySyncRequired(this); + + m_uncommittedChanges |= flags; +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) |