diff options
Diffstat (limited to 'WebCore/platform/graphics/ca')
-rw-r--r-- | WebCore/platform/graphics/ca/GraphicsLayerCA.cpp | 2300 | ||||
-rw-r--r-- | WebCore/platform/graphics/ca/GraphicsLayerCA.h | 386 | ||||
-rw-r--r-- | WebCore/platform/graphics/ca/PlatformCAAnimation.h | 158 | ||||
-rw-r--r-- | WebCore/platform/graphics/ca/PlatformCALayer.h | 211 | ||||
-rw-r--r-- | WebCore/platform/graphics/ca/TransformationMatrixCA.cpp | 69 | ||||
-rw-r--r-- | WebCore/platform/graphics/ca/mac/PlatformCAAnimationMac.mm | 574 | ||||
-rw-r--r-- | WebCore/platform/graphics/ca/mac/PlatformCALayerMac.mm | 725 |
7 files changed, 4423 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) diff --git a/WebCore/platform/graphics/ca/GraphicsLayerCA.h b/WebCore/platform/graphics/ca/GraphicsLayerCA.h new file mode 100644 index 0000000..22921c1 --- /dev/null +++ b/WebCore/platform/graphics/ca/GraphicsLayerCA.h @@ -0,0 +1,386 @@ +/* + * 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. + */ + +#ifndef GraphicsLayerCA_h +#define GraphicsLayerCA_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "GraphicsLayer.h" +#include "Image.h" +#include "PlatformCAAnimation.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/RetainPtr.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +class PlatformCALayer; + +class GraphicsLayerCA : public GraphicsLayer { +public: + // The width and height of a single tile in a tiled layer. Should be large enough to + // avoid lots of small tiles (and therefore lots of drawing callbacks), but small enough + // to keep the overall tile cost low. + static const int kTiledLayerTileSize = 512; + + GraphicsLayerCA(GraphicsLayerClient*); + virtual ~GraphicsLayerCA(); + + virtual void animationStarted(CFTimeInterval beginTime); + + virtual void setName(const String&); + + virtual PlatformLayer* platformLayer() const; + + virtual bool setChildren(const Vector<GraphicsLayer*>&); + virtual void addChild(GraphicsLayer*); + virtual void addChildAtIndex(GraphicsLayer*, int index); + virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual void addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild); + + virtual void removeFromParent(); + + virtual void setMaskLayer(GraphicsLayer*); + virtual void setReplicatedLayer(GraphicsLayer*); + + virtual void setPosition(const FloatPoint&); + virtual void setAnchorPoint(const FloatPoint3D&); + virtual void setSize(const FloatSize&); + + virtual void setTransform(const TransformationMatrix&); + + virtual void setChildrenTransform(const TransformationMatrix&); + + virtual void setPreserves3D(bool); + virtual void setMasksToBounds(bool); + virtual void setDrawsContent(bool); + virtual void setAcceleratesDrawing(bool); + + virtual void setBackgroundColor(const Color&); + virtual void clearBackgroundColor(); + + virtual void setContentsOpaque(bool); + virtual void setBackfaceVisibility(bool); + + // return true if we started an animation + virtual void setOpacity(float); + + virtual void setNeedsDisplay(); + virtual void setNeedsDisplayInRect(const FloatRect&); + virtual void setContentsNeedsDisplay(); + + virtual void setContentsRect(const IntRect&); + + virtual void suspendAnimations(double time); + virtual void resumeAnimations(); + + virtual bool addAnimation(const KeyframeValueList&, const IntSize& boxSize, const Animation*, const String& animationName, double timeOffset); + virtual void pauseAnimation(const String& animationName, double timeOffset); + virtual void removeAnimation(const String& animationName); + + virtual void setContentsToImage(Image*); + virtual void setContentsToMedia(PlatformLayer*); + virtual void setContentsToCanvas(PlatformLayer*); + + virtual bool hasContentsLayer() const { return m_contentsLayer; } + + virtual void setDebugBackgroundColor(const Color&); + virtual void setDebugBorder(const Color&, float borderWidth); + + virtual void didDisplay(PlatformLayer*); + + void recursiveCommitChanges(); + + virtual void syncCompositingState(); + virtual void syncCompositingStateForThisLayerOnly(); + +protected: + virtual void setOpacityInternal(float); + +private: + void updateOpacityOnLayer(); + + PlatformCALayer* primaryLayer() const { return m_structuralLayer.get() ? m_structuralLayer.get() : m_layer.get(); } + PlatformCALayer* hostLayerForSublayers() const; + PlatformCALayer* layerForSuperlayer() const; + PlatformCALayer* animatedLayer(AnimatedPropertyID) const; + + typedef String CloneID; // Identifier for a given clone, based on original/replica branching down the tree. + static bool isReplicatedRootClone(const CloneID& cloneID) { return cloneID[0U] & 1; } + + typedef HashMap<CloneID, RefPtr<PlatformCALayer> > LayerMap; + LayerMap* primaryLayerClones() const { return m_structuralLayer.get() ? m_structuralLayerClones.get() : m_layerClones.get(); } + LayerMap* animatedLayerClones(AnimatedPropertyID) const; + + bool createAnimationFromKeyframes(const KeyframeValueList&, const Animation*, const String& animationName, double timeOffset); + bool createTransformAnimationsFromKeyframes(const KeyframeValueList&, const Animation*, const String& animationName, double timeOffset, const IntSize& boxSize); + + // Return autoreleased animation (use RetainPtr?) + PassRefPtr<PlatformCAAnimation> createBasicAnimation(const Animation*, AnimatedPropertyID, bool additive); + PassRefPtr<PlatformCAAnimation> createKeyframeAnimation(const Animation*, AnimatedPropertyID, bool additive); + void setupAnimation(PlatformCAAnimation*, const Animation*, bool additive); + + const TimingFunction* timingFunctionForAnimationValue(const AnimationValue*, const Animation*); + + bool setAnimationEndpoints(const KeyframeValueList&, const Animation*, PlatformCAAnimation*); + bool setAnimationKeyframes(const KeyframeValueList&, const Animation*, PlatformCAAnimation*); + + bool setTransformAnimationEndpoints(const KeyframeValueList&, const Animation*, PlatformCAAnimation*, int functionIndex, TransformOperation::OperationType, bool isMatrixAnimation, const IntSize& boxSize); + bool setTransformAnimationKeyframes(const KeyframeValueList&, const Animation*, PlatformCAAnimation*, int functionIndex, TransformOperation::OperationType, bool isMatrixAnimation, const IntSize& boxSize); + + bool animationIsRunning(const String& animationName) const + { + return m_runningAnimations.find(animationName) != m_runningAnimations.end(); + } + + void commitLayerChangesBeforeSublayers(); + void commitLayerChangesAfterSublayers(); + + FloatSize constrainedSize() const; + + bool requiresTiledLayer(const FloatSize&) const; + void swapFromOrToTiledLayer(bool useTiledLayer); + + CompositingCoordinatesOrientation defaultContentsOrientation() const; + void updateContentsTransform(); + + void setupContentsLayer(PlatformCALayer*); + PlatformCALayer* contentsLayer() const { return m_contentsLayer.get(); } + + virtual void setReplicatedByLayer(GraphicsLayer*); + + // Used to track the path down the tree for replica layers. + struct ReplicaState { + static const size_t maxReplicaDepth = 16; + enum ReplicaBranchType { ChildBranch = 0, ReplicaBranch = 1 }; + ReplicaState(ReplicaBranchType firstBranch) + : m_replicaDepth(0) + { + push(firstBranch); + } + + // Called as we walk down the tree to build replicas. + void push(ReplicaBranchType branchType) + { + m_replicaBranches.append(branchType); + if (branchType == ReplicaBranch) + ++m_replicaDepth; + } + + void setBranchType(ReplicaBranchType branchType) + { + ASSERT(!m_replicaBranches.isEmpty()); + + if (m_replicaBranches.last() != branchType) { + if (branchType == ReplicaBranch) + ++m_replicaDepth; + else + --m_replicaDepth; + } + + m_replicaBranches.last() = branchType; + } + + void pop() + { + if (m_replicaBranches.last() == ReplicaBranch) + --m_replicaDepth; + m_replicaBranches.removeLast(); + } + + size_t depth() const { return m_replicaBranches.size(); } + size_t replicaDepth() const { return m_replicaDepth; } + + CloneID cloneID() const; + + private: + Vector<ReplicaBranchType> m_replicaBranches; + size_t m_replicaDepth; + }; + PassRefPtr<PlatformCALayer>replicatedLayerRoot(ReplicaState&); + + enum CloneLevel { RootCloneLevel, IntermediateCloneLevel }; + PassRefPtr<PlatformCALayer> fetchCloneLayers(GraphicsLayer* replicaRoot, ReplicaState&, CloneLevel); + + PassRefPtr<PlatformCALayer> cloneLayer(PlatformCALayer *, CloneLevel); + PassRefPtr<PlatformCALayer> findOrMakeClone(CloneID, PlatformCALayer *, LayerMap*, CloneLevel); + + void ensureCloneLayers(CloneID cloneID, RefPtr<PlatformCALayer>& primaryLayer, RefPtr<PlatformCALayer>& structuralLayer, RefPtr<PlatformCALayer>& contentsLayer, CloneLevel cloneLevel); + + bool hasCloneLayers() const { return m_layerClones; } + void removeCloneLayers(); + FloatPoint positionForCloneRootLayer() const; + + void propagateLayerChangeToReplicas(); + + // All these "update" methods will be called inside a BEGIN_BLOCK_OBJC_EXCEPTIONS/END_BLOCK_OBJC_EXCEPTIONS block. + void updateLayerNames(); + void updateSublayerList(); + void updateLayerPosition(); + void updateLayerSize(); + void updateAnchorPoint(); + void updateTransform(); + void updateChildrenTransform(); + void updateMasksToBounds(); + void updateContentsOpaque(); + void updateBackfaceVisibility(); + void updateStructuralLayer(); + void updateLayerDrawsContent(); + void updateLayerBackgroundColor(); + + void updateContentsImage(); + void updateContentsMediaLayer(); + void updateContentsCanvasLayer(); + void updateContentsRect(); + void updateMaskLayer(); + void updateReplicatedLayers(); + + void updateLayerAnimations(); + void updateContentsNeedsDisplay(); + void updateAcceleratesDrawing(); + + enum StructuralLayerPurpose { + NoStructuralLayer = 0, + StructuralLayerForPreserves3D, + StructuralLayerForReplicaFlattening + }; + void ensureStructuralLayer(StructuralLayerPurpose); + StructuralLayerPurpose structuralLayerPurpose() const; + + void setAnimationOnLayer(PlatformCAAnimation*, AnimatedPropertyID, const String& animationName, int index, double timeOffset); + bool removeCAAnimationFromLayer(AnimatedPropertyID, const String& animationName, int index); + void pauseCAAnimationOnLayer(AnimatedPropertyID, const String& animationName, int index, double timeOffset); + + enum MoveOrCopy { Move, Copy }; + static void moveOrCopyLayerAnimation(MoveOrCopy, const String& animationIdentifier, PlatformCALayer *fromLayer, PlatformCALayer *toLayer); + void moveOrCopyAnimationsForProperty(MoveOrCopy, AnimatedPropertyID, PlatformCALayer * fromLayer, PlatformCALayer * toLayer); + + enum LayerChange { + NoChange = 0, + NameChanged = 1 << 1, + ChildrenChanged = 1 << 2, // also used for content layer, and preserves-3d, and size if tiling changes? + PositionChanged = 1 << 3, + AnchorPointChanged = 1 << 4, + SizeChanged = 1 << 5, + TransformChanged = 1 << 6, + ChildrenTransformChanged = 1 << 7, + Preserves3DChanged = 1 << 8, + MasksToBoundsChanged = 1 << 9, + DrawsContentChanged = 1 << 10, // need this? + BackgroundColorChanged = 1 << 11, + ContentsOpaqueChanged = 1 << 12, + BackfaceVisibilityChanged = 1 << 13, + OpacityChanged = 1 << 14, + AnimationChanged = 1 << 15, + DirtyRectsChanged = 1 << 16, + ContentsImageChanged = 1 << 17, + ContentsMediaLayerChanged = 1 << 18, + ContentsCanvasLayerChanged = 1 << 19, + ContentsRectChanged = 1 << 20, + MaskLayerChanged = 1 << 21, + ReplicatedLayerChanged = 1 << 22, + ContentsNeedsDisplay = 1 << 23, + AcceleratesDrawingChanged = 1 << 24 + }; + typedef unsigned LayerChangeFlags; + void noteLayerPropertyChanged(LayerChangeFlags flags); + void noteSublayersChanged(); + + void repaintLayerDirtyRects(); + + RefPtr<PlatformCALayer> m_layer; // The main layer + RefPtr<PlatformCALayer> m_structuralLayer; // A layer used for structural reasons, like preserves-3d or replica-flattening. Is the parent of m_layer. + RefPtr<PlatformCALayer> m_contentsLayer; // A layer used for inner content, like image and video + + // References to clones of our layers, for replicated layers. + OwnPtr<LayerMap> m_layerClones; + OwnPtr<LayerMap> m_structuralLayerClones; + OwnPtr<LayerMap> m_contentsLayerClones; + + enum ContentsLayerPurpose { + NoContentsLayer = 0, + ContentsLayerForImage, + ContentsLayerForMedia, + ContentsLayerForCanvas + }; + + ContentsLayerPurpose m_contentsLayerPurpose; + bool m_contentsLayerHasBackgroundColor : 1; + + RetainPtr<CGImageRef> m_uncorrectedContentsImage; + RetainPtr<CGImageRef> m_pendingContentsImage; + + // This represents the animation of a single property. There may be multiple transform animations for + // a single transition or keyframe animation, so index is used to distinguish these. + struct LayerPropertyAnimation { + LayerPropertyAnimation(PassRefPtr<PlatformCAAnimation> caAnimation, const String& animationName, AnimatedPropertyID property, int index, double timeOffset) + : m_animation(caAnimation) + , m_name(animationName) + , m_property(property) + , m_index(index) + , m_timeOffset(timeOffset) + { } + + RefPtr<PlatformCAAnimation> m_animation; + String m_name; + AnimatedPropertyID m_property; + int m_index; + double m_timeOffset; + }; + + // Uncommitted transitions and animations. + Vector<LayerPropertyAnimation> m_uncomittedAnimations; + + enum Action { Remove, Pause }; + struct AnimationProcessingAction { + AnimationProcessingAction(Action action = Remove, double timeOffset = 0) + : action(action) + , timeOffset(timeOffset) + { + } + Action action; + double timeOffset; // only used for pause + }; + typedef HashMap<String, AnimationProcessingAction> AnimationsToProcessMap; + AnimationsToProcessMap m_animationsToProcess; + + // Map of animation names to their associated lists of property animations, so we can remove/pause them. + typedef HashMap<String, Vector<LayerPropertyAnimation> > AnimationsMap; + AnimationsMap m_runningAnimations; + + Vector<FloatRect> m_dirtyRects; + + LayerChangeFlags m_uncommittedChanges; +}; + +} // namespace WebCore + + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // GraphicsLayerCA_h diff --git a/WebCore/platform/graphics/ca/PlatformCAAnimation.h b/WebCore/platform/graphics/ca/PlatformCAAnimation.h new file mode 100644 index 0000000..d802700 --- /dev/null +++ b/WebCore/platform/graphics/ca/PlatformCAAnimation.h @@ -0,0 +1,158 @@ +/* + * 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. + */ + +#ifndef PlatformCAAnimation_h +#define PlatformCAAnimation_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "Color.h" +#include "FloatPoint3D.h" +#include "TransformationMatrix.h" +#include <wtf/RefCounted.h> +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> + +#if PLATFORM(MAC) +#ifdef __OBJC__ +@class CAPropertyAnimation; +typedef CAPropertyAnimation* PlatformAnimationRef; +#else +typedef void* CAPropertyAnimation; // So the m_animation declaration works +typedef void* PlatformAnimationRef; +#endif +#elif PLATFORM(WIN) +namespace WebCore { +class WKCACFAnimation; +typedef WKCACFAnimation* PlatformAnimationRef; +} +#endif + +namespace WebCore { + +class FloatRect; +class PlatformCAAnimation; +class TimingFunction; + +class PlatformCAAnimation : public RefCounted<PlatformCAAnimation> { +public: + friend class PlatformCALayer; + + enum AnimationType { Basic, Keyframe }; + enum FillModeType { NoFillMode, Forwards, Backwards, Both }; + enum ValueFunctionType { NoValueFunction, RotateX, RotateY, RotateZ, ScaleX, ScaleY, ScaleZ, Scale, TranslateX, TranslateY, TranslateZ, Translate }; + + static PassRefPtr<PlatformCAAnimation> create(AnimationType, const String& keyPath); + static PassRefPtr<PlatformCAAnimation> create(PlatformAnimationRef animation); + static PassRefPtr<PlatformCAAnimation> create(const PlatformCAAnimation* animation); + + ~PlatformCAAnimation(); + + static bool supportsValueFunction(); + + PlatformAnimationRef platformAnimation() const; + + AnimationType animationType() const { return m_type; } + String keyPath() const; + + CFTimeInterval beginTime() const; + void setBeginTime(CFTimeInterval); + + CFTimeInterval duration() const; + void setDuration(CFTimeInterval); + + float speed() const; + void setSpeed(float); + + CFTimeInterval timeOffset() const; + void setTimeOffset(CFTimeInterval); + + float repeatCount() const; + void setRepeatCount(float); + + bool autoreverses() const; + void setAutoreverses(bool); + + FillModeType fillMode() const; + void setFillMode(FillModeType); + + void setTimingFunction(const TimingFunction*); + void copyTimingFunctionFrom(const PlatformCAAnimation*); + + bool isRemovedOnCompletion() const; + void setRemovedOnCompletion(bool); + + bool isAdditive() const; + void setAdditive(bool); + + ValueFunctionType valueFunction() const; + void setValueFunction(ValueFunctionType); + + // Basic-animation properties. + void setFromValue(float); + void setFromValue(const WebCore::TransformationMatrix&); + void setFromValue(const FloatPoint3D&); + void setFromValue(const WebCore::Color&); + void copyFromValueFrom(const PlatformCAAnimation*); + + void setToValue(float); + void setToValue(const WebCore::TransformationMatrix&); + void setToValue(const FloatPoint3D&); + void setToValue(const WebCore::Color&); + void copyToValueFrom(const PlatformCAAnimation*); + + // Keyframe-animation properties. + void setValues(const Vector<float>&); + void setValues(const Vector<WebCore::TransformationMatrix>&); + void setValues(const Vector<FloatPoint3D>&); + void setValues(const Vector<WebCore::Color>&); + void copyValuesFrom(const PlatformCAAnimation*); + + void setKeyTimes(const Vector<float>&); + void copyKeyTimesFrom(const PlatformCAAnimation*); + + void setTimingFunctions(const Vector<const TimingFunction*>&); + void copyTimingFunctionsFrom(const PlatformCAAnimation*); + +protected: + PlatformCAAnimation(AnimationType, const String& keyPath); + PlatformCAAnimation(PlatformAnimationRef animation); + PlatformCAAnimation(const PlatformCAAnimation* animation); + +private: + AnimationType m_type; + +#if PLATFORM(MAC) + RetainPtr<CAPropertyAnimation> m_animation; +#elif PLATFORM(WIN) + RetainPtr<CACFAnimationRef> m_animation; +#endif +}; + +} + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // PlatformCAAnimation_h diff --git a/WebCore/platform/graphics/ca/PlatformCALayer.h b/WebCore/platform/graphics/ca/PlatformCALayer.h new file mode 100644 index 0000000..9cf0ccb --- /dev/null +++ b/WebCore/platform/graphics/ca/PlatformCALayer.h @@ -0,0 +1,211 @@ +/* + * 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. + */ + +#ifndef PlatformCALayer_h +#define PlatformCALayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "GraphicsContext.h" +#include "GraphicsLayerCA.h" +#include "PlatformCAAnimation.h" +#include "PlatformString.h" +#include <wtf/HashMap.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +class PlatformCALayer; + +typedef Vector<RefPtr<PlatformCALayer> > PlatformCALayerList; + +class PlatformCALayer : public RefCounted<PlatformCALayer> { +public: + // TiledLayer used in GraphicsLayer has constant settings: + // cTiledLayerTileSize = 512 + // setTileSize:(cTiledLayerTileSize, cTiledLayerTileSize) + // setLevelsOfDetail:1 + // setLevelsOfDetailBias:0 + // setContentsGravity:@"bottomLeft" + // + // TiledLayer also has drawing functions like WebLayer + // + // WebLayer is a CALayer with drawing functions specific to WebKit + // + // Layer and TransformLayer are used as is + + enum LayerType { LayerTypeLayer, LayerTypeWebLayer, LayerTypeTransformLayer, LayerTypeWebTiledLayer, LayerTypeCustom }; + enum FilterType { Linear, Nearest, Trilinear }; + + static PassRefPtr<PlatformCALayer> create(LayerType, GraphicsLayerCA*); + + // This function passes the layer as a void* rather than a PlatformLayer because PlatformLayer + // is defined differently for Obj C and C++. This allows callers from both languages. + static PassRefPtr<PlatformCALayer> create(void* platformLayer, GraphicsLayerCA*); + + ~PlatformCALayer(); + + // This function passes the layer as a void* rather than a PlatformLayer because PlatformLayer + // is defined differently for Obj C and C++. This allows callers from both languages. + static PlatformCALayer* platformCALayer(void* platformLayer); + + PlatformLayer* platformLayer() const; + + static bool isValueFunctionSupported(); + + GraphicsLayerCA* owner() const { return m_owner; } + void setOwner(GraphicsLayerCA* owner); + + void animationStarted(CFTimeInterval beginTime) + { + if (m_owner) + m_owner->animationStarted(beginTime); + } + + void setNeedsDisplay(const FloatRect* dirtyRect = 0); + + void setContentsChanged(); + + LayerType layerType() const { return m_layerType; } + + PlatformCALayer* superlayer() const; + void removeFromSuperlayer(); + void setSublayers(const PlatformCALayerList&); + void removeAllSublayers(); + void appendSublayer(PlatformCALayer*); + void insertSublayer(PlatformCALayer*, size_t index); + void replaceSublayer(PlatformCALayer* reference, PlatformCALayer*); + size_t sublayerCount() const; + + // This method removes the sublayers from the source and reparents them to the current layer. + void adoptSublayers(PlatformCALayer* source); + + void addAnimationForKey(const String& key, PlatformCAAnimation* animation); + void removeAnimationForKey(const String& key); + PassRefPtr<PlatformCAAnimation> animationForKey(const String& key); + + PlatformCALayer* mask() const; + void setMask(PlatformCALayer*); + + bool isOpaque() const; + void setOpaque(bool); + + FloatRect bounds() const; + void setBounds(const FloatRect&); + + FloatPoint3D position() const; + void setPosition(const FloatPoint3D&); + void setPosition(const FloatPoint& pos) { setPosition(FloatPoint3D(pos.x(), pos.y(), 0)); } + + FloatPoint3D anchorPoint() const; + void setAnchorPoint(const FloatPoint3D&); + + TransformationMatrix transform() const; + void setTransform(const TransformationMatrix&); + + TransformationMatrix sublayerTransform() const; + void setSublayerTransform(const TransformationMatrix&); + + TransformationMatrix contentsTransform() const; + void setContentsTransform(const TransformationMatrix&); + + bool isHidden() const; + void setHidden(bool); + + bool isGeometryFlipped() const; + void setGeometryFlipped(bool); + + bool isDoubleSided() const; + void setDoubleSided(bool); + + bool masksToBounds() const; + void setMasksToBounds(bool); + + bool acceleratesDrawing() const; + void setAcceleratesDrawing(bool); + + void* contents() const; + void setContents(void*); + + FloatRect contentsRect() const; + void setContentsRect(const FloatRect&); + + void setMinificationFilter(FilterType); + void setMagnificationFilter(FilterType); + + Color backgroundColor() const; + void setBackgroundColor(const Color&); + + float borderWidth() const; + void setBorderWidth(float); + + Color borderColor() const; + void setBorderColor(const Color&); + + float opacity() const; + void setOpacity(float); + + String name() const; + void setName(const String&); + + FloatRect frame() const; + void setFrame(const FloatRect&); + + float speed() const; + void setSpeed(float); + + CFTimeInterval timeOffset() const; + void setTimeOffset(CFTimeInterval); + +protected: + PlatformCALayer(LayerType, PlatformLayer*, GraphicsLayerCA*); + +private: + + GraphicsLayerCA* m_owner; + LayerType m_layerType; + +#if PLATFORM(MAC) || PLATFORM(WIN) + RetainPtr<PlatformLayer> m_layer; +#endif + +#if PLATFORM(MAC) +#ifdef __OBJC__ + RetainPtr<NSObject> m_delegate; +#else + RetainPtr<void> m_delegate; +#endif +#endif +}; + +} + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // PlatformCALayer_h diff --git a/WebCore/platform/graphics/ca/TransformationMatrixCA.cpp b/WebCore/platform/graphics/ca/TransformationMatrixCA.cpp new file mode 100644 index 0000000..27805e6 --- /dev/null +++ b/WebCore/platform/graphics/ca/TransformationMatrixCA.cpp @@ -0,0 +1,69 @@ +/* + * 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 PLATFORM(CA) + +#include "TransformationMatrix.h" + +#include "FloatConversion.h" + +namespace WebCore { + +TransformationMatrix::TransformationMatrix(const CATransform3D& t) +{ + setMatrix( + t.m11, t.m12, t.m13, t.m14, + t.m21, t.m22, t.m23, t.m24, + t.m31, t.m32, t.m33, t.m34, + t.m41, t.m42, t.m43, t.m44); +} + +TransformationMatrix::operator CATransform3D() const +{ + CATransform3D toT3D; + toT3D.m11 = narrowPrecisionToFloat(m11()); + toT3D.m12 = narrowPrecisionToFloat(m12()); + toT3D.m13 = narrowPrecisionToFloat(m13()); + toT3D.m14 = narrowPrecisionToFloat(m14()); + toT3D.m21 = narrowPrecisionToFloat(m21()); + toT3D.m22 = narrowPrecisionToFloat(m22()); + toT3D.m23 = narrowPrecisionToFloat(m23()); + toT3D.m24 = narrowPrecisionToFloat(m24()); + toT3D.m31 = narrowPrecisionToFloat(m31()); + toT3D.m32 = narrowPrecisionToFloat(m32()); + toT3D.m33 = narrowPrecisionToFloat(m33()); + toT3D.m34 = narrowPrecisionToFloat(m34()); + toT3D.m41 = narrowPrecisionToFloat(m41()); + toT3D.m42 = narrowPrecisionToFloat(m42()); + toT3D.m43 = narrowPrecisionToFloat(m43()); + toT3D.m44 = narrowPrecisionToFloat(m44()); + return toT3D; +} + +} + +#endif // PLATFORM(CA) diff --git a/WebCore/platform/graphics/ca/mac/PlatformCAAnimationMac.mm b/WebCore/platform/graphics/ca/mac/PlatformCAAnimationMac.mm new file mode 100644 index 0000000..2a00857 --- /dev/null +++ b/WebCore/platform/graphics/ca/mac/PlatformCAAnimationMac.mm @@ -0,0 +1,574 @@ +/* + * 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) + +#import "PlatformCAAnimation.h" + +#import "FloatConversion.h" +#import "PlatformString.h" +#import "TimingFunction.h" +#import <QuartzCore/QuartzCore.h> +#import <wtf/UnusedParam.h> + +#define HAVE_MODERN_QUARTZCORE (!defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)) + +using namespace WebCore; + +// This value must be the same as in PlatformCALayerMac.mm +static NSString * const WKNonZeroBeginTimeFlag = @"WKPlatformCAAnimationNonZeroBeginTimeFlag"; + +static bool hasNonZeroBeginTimeFlag(const PlatformCAAnimation* animation) +{ + return [[animation->platformAnimation() valueForKey:WKNonZeroBeginTimeFlag] boolValue]; +} + +static void setNonZeroBeginTimeFlag(PlatformCAAnimation* animation, bool value) +{ + [animation->platformAnimation() setValue:[NSNumber numberWithBool:value] forKey:WKNonZeroBeginTimeFlag]; +} + +static NSString* toCAFillModeType(PlatformCAAnimation::FillModeType type) +{ + switch (type) { + case PlatformCAAnimation::NoFillMode: + case PlatformCAAnimation::Forwards: return kCAFillModeForwards; + case PlatformCAAnimation::Backwards: return kCAFillModeBackwards; + case PlatformCAAnimation::Both: return kCAFillModeBoth; + } + return @""; +} + +static PlatformCAAnimation::FillModeType fromCAFillModeType(NSString* string) +{ + if ([string isEqualToString:kCAFillModeBackwards]) + return PlatformCAAnimation::Backwards; + + if ([string isEqualToString:kCAFillModeBoth]) + return PlatformCAAnimation::Both; + + return PlatformCAAnimation::Forwards; +} + +#if HAVE_MODERN_QUARTZCORE +static NSString* toCAValueFunctionType(PlatformCAAnimation::ValueFunctionType type) +{ + switch (type) { + case PlatformCAAnimation::NoValueFunction: return @""; + case PlatformCAAnimation::RotateX: return kCAValueFunctionRotateX; + case PlatformCAAnimation::RotateY: return kCAValueFunctionRotateY; + case PlatformCAAnimation::RotateZ: return kCAValueFunctionRotateZ; + case PlatformCAAnimation::ScaleX: return kCAValueFunctionScaleX; + case PlatformCAAnimation::ScaleY: return kCAValueFunctionScaleY; + case PlatformCAAnimation::ScaleZ: return kCAValueFunctionScaleZ; + case PlatformCAAnimation::Scale: return kCAValueFunctionScale; + case PlatformCAAnimation::TranslateX: return kCAValueFunctionTranslateX; + case PlatformCAAnimation::TranslateY: return kCAValueFunctionTranslateY; + case PlatformCAAnimation::TranslateZ: return kCAValueFunctionTranslateZ; + case PlatformCAAnimation::Translate: return kCAValueFunctionTranslate; + } + return @""; +} + +static PlatformCAAnimation::ValueFunctionType fromCAValueFunctionType(NSString* string) +{ + if ([string isEqualToString:kCAValueFunctionRotateX]) + return PlatformCAAnimation::RotateX; + + if ([string isEqualToString:kCAValueFunctionRotateY]) + return PlatformCAAnimation::RotateY; + + if ([string isEqualToString:kCAValueFunctionRotateZ]) + return PlatformCAAnimation::RotateZ; + + if ([string isEqualToString:kCAValueFunctionScaleX]) + return PlatformCAAnimation::ScaleX; + + if ([string isEqualToString:kCAValueFunctionScaleY]) + return PlatformCAAnimation::ScaleY; + + if ([string isEqualToString:kCAValueFunctionScaleZ]) + return PlatformCAAnimation::ScaleZ; + + if ([string isEqualToString:kCAValueFunctionScale]) + return PlatformCAAnimation::Scale; + + if ([string isEqualToString:kCAValueFunctionTranslateX]) + return PlatformCAAnimation::TranslateX; + + if ([string isEqualToString:kCAValueFunctionTranslateY]) + return PlatformCAAnimation::TranslateY; + + if ([string isEqualToString:kCAValueFunctionTranslateZ]) + return PlatformCAAnimation::TranslateZ; + + if ([string isEqualToString:kCAValueFunctionTranslate]) + return PlatformCAAnimation::Translate; + + return PlatformCAAnimation::NoValueFunction; +} +#endif + +static CAMediaTimingFunction* toCAMediaTimingFunction(const TimingFunction* timingFunction) +{ + if (!timingFunction) + return [CAMediaTimingFunction functionWithControlPoints:0.25f :0.1f :0.25f :0.1f]; + + if (timingFunction->isCubicBezierTimingFunction()) { + const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction); + return [CAMediaTimingFunction functionWithControlPoints:static_cast<float>(ctf->x1()) :static_cast<float>(ctf->y1()) + :static_cast<float>(ctf->x2()) :static_cast<float>(ctf->y2())]; + } + + return [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + + return 0; +} + +PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::create(AnimationType type, const String& keyPath) +{ + return adoptRef(new PlatformCAAnimation(type, keyPath)); +} + +PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::create(PlatformAnimationRef animation) +{ + return adoptRef(new PlatformCAAnimation(animation)); +} + +PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::create(const PlatformCAAnimation* animation) +{ + return adoptRef(new PlatformCAAnimation(animation)); +} + +PlatformCAAnimation::PlatformCAAnimation(AnimationType type, const String& keyPath) + : m_type(type) +{ + if (type == Basic) + m_animation.adoptNS([[CABasicAnimation animationWithKeyPath:keyPath] retain]); + else + m_animation.adoptNS([[CAKeyframeAnimation animationWithKeyPath:keyPath] retain]); +} + +PlatformCAAnimation::PlatformCAAnimation(PlatformAnimationRef animation) +{ + if ([static_cast<CAAnimation*>(animation) isKindOfClass:[CABasicAnimation class]]) + m_type = Basic; + else if ([static_cast<CAAnimation*>(animation) isKindOfClass:[CAKeyframeAnimation class]]) + m_type = Keyframe; + else { + ASSERT(0); + return; + } + + m_animation = static_cast<CAPropertyAnimation*>(animation); +} + +PlatformCAAnimation::PlatformCAAnimation(const PlatformCAAnimation* animation) +{ + PlatformCAAnimation* newAnimation = new PlatformCAAnimation(animation->animationType(), animation->keyPath()); + + newAnimation->setBeginTime(animation->beginTime()); + newAnimation->setDuration(animation->duration()); + newAnimation->setSpeed(animation->speed()); + newAnimation->setTimeOffset(animation->timeOffset()); + newAnimation->setRepeatCount(animation->repeatCount()); + newAnimation->setAutoreverses(animation->autoreverses()); + newAnimation->setFillMode(animation->fillMode()); + newAnimation->setRemovedOnCompletion(animation->isRemovedOnCompletion()); + newAnimation->setAdditive(animation->isAdditive()); + newAnimation->copyTimingFunctionFrom(animation); + +#if HAVE_MODERN_QUARTZCORE + newAnimation->setValueFunction(animation->valueFunction()); +#endif + + setNonZeroBeginTimeFlag(newAnimation, hasNonZeroBeginTimeFlag(animation)); + + // Copy the specific Basic or Keyframe values + if (animation->animationType() == Keyframe) { + newAnimation->copyValuesFrom(animation); + newAnimation->copyKeyTimesFrom(animation); + newAnimation->copyTimingFunctionsFrom(animation); + } else { + newAnimation->copyFromValueFrom(animation); + newAnimation->copyToValueFrom(animation); + } +} + +PlatformCAAnimation::~PlatformCAAnimation() +{ +} + +bool PlatformCAAnimation::supportsValueFunction() +{ + static bool sHaveValueFunction = [CAPropertyAnimation instancesRespondToSelector:@selector(setValueFunction:)]; + return sHaveValueFunction; +} + +PlatformAnimationRef PlatformCAAnimation::platformAnimation() const +{ + return m_animation.get(); +} + +String PlatformCAAnimation::keyPath() const +{ + return [m_animation.get() keyPath]; +} + +CFTimeInterval PlatformCAAnimation::beginTime() const +{ + return [m_animation.get() beginTime]; +} + +void PlatformCAAnimation::setBeginTime(CFTimeInterval value) +{ + [m_animation.get() setBeginTime:value]; + + // Also set a flag to tell us if we've passed in a 0 value. + // The flag is needed because later beginTime will get changed + // to the time at which it fired and we need to know whether + // or not it was 0 to begin with. + if (value) + setNonZeroBeginTimeFlag(this, true); +} + +CFTimeInterval PlatformCAAnimation::duration() const +{ + return [m_animation.get() duration]; +} + +void PlatformCAAnimation::setDuration(CFTimeInterval value) +{ + [m_animation.get() setDuration:value]; +} + +float PlatformCAAnimation::speed() const +{ + return [m_animation.get() speed]; +} + +void PlatformCAAnimation::setSpeed(float value) +{ + [m_animation.get() setSpeed:value]; +} + +CFTimeInterval PlatformCAAnimation::timeOffset() const +{ + return [m_animation.get() timeOffset]; +} + +void PlatformCAAnimation::setTimeOffset(CFTimeInterval value) +{ + [m_animation.get() setTimeOffset:value]; +} + +float PlatformCAAnimation::repeatCount() const +{ + return [m_animation.get() repeatCount]; +} + +void PlatformCAAnimation::setRepeatCount(float value) +{ + [m_animation.get() setRepeatCount:value]; +} + +bool PlatformCAAnimation::autoreverses() const +{ + return [m_animation.get() autoreverses]; +} + +void PlatformCAAnimation::setAutoreverses(bool value) +{ + [m_animation.get() setAutoreverses:value]; +} + +PlatformCAAnimation::FillModeType PlatformCAAnimation::fillMode() const +{ + return fromCAFillModeType([m_animation.get() fillMode]); +} + +void PlatformCAAnimation::setFillMode(FillModeType value) +{ + [m_animation.get() setFillMode:toCAFillModeType(value)]; +} + +void PlatformCAAnimation::setTimingFunction(const TimingFunction* value) +{ + [m_animation.get() setTimingFunction:toCAMediaTimingFunction(value)]; +} + +void PlatformCAAnimation::copyTimingFunctionFrom(const PlatformCAAnimation* value) +{ + [m_animation.get() setTimingFunction:[value->m_animation.get() timingFunction]]; +} + +bool PlatformCAAnimation::isRemovedOnCompletion() const +{ + return [m_animation.get() isRemovedOnCompletion]; +} + +void PlatformCAAnimation::setRemovedOnCompletion(bool value) +{ + [m_animation.get() setRemovedOnCompletion:value]; +} + +bool PlatformCAAnimation::isAdditive() const +{ + return [m_animation.get() isAdditive]; +} + +void PlatformCAAnimation::setAdditive(bool value) +{ + [m_animation.get() setAdditive:value]; +} + +PlatformCAAnimation::ValueFunctionType PlatformCAAnimation::valueFunction() const +{ +#if HAVE_MODERN_QUARTZCORE + CAValueFunction* vf = [m_animation.get() valueFunction]; + return fromCAValueFunctionType([vf name]); +#else + return NoValueFunction; +#endif +} + +void PlatformCAAnimation::setValueFunction(ValueFunctionType value) +{ +#if HAVE_MODERN_QUARTZCORE + [m_animation.get() setValueFunction:[CAValueFunction functionWithName:toCAValueFunctionType(value)]]; +#else + UNUSED_PARAM(value); +#endif +} + +void PlatformCAAnimation::setFromValue(float value) +{ + if (animationType() != Basic) + return; + [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:[NSNumber numberWithDouble:value]]; +} + +void PlatformCAAnimation::setFromValue(const WebCore::TransformationMatrix& value) +{ + if (animationType() != Basic) + return; + + [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:[NSValue valueWithCATransform3D:value]]; +} + +void PlatformCAAnimation::setFromValue(const FloatPoint3D& value) +{ + if (animationType() != Basic) + return; + + NSArray* array = [NSArray arrayWithObjects: + [NSNumber numberWithDouble:value.x()], + [NSNumber numberWithDouble:value.y()], + [NSNumber numberWithDouble:value.z()], + nil]; + [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:array]; +} + +void PlatformCAAnimation::setFromValue(const WebCore::Color& value) +{ + if (animationType() != Basic) + return; + + NSArray* array = [NSArray arrayWithObjects: + [NSNumber numberWithDouble:value.red()], + [NSNumber numberWithDouble:value.green()], + [NSNumber numberWithDouble:value.blue()], + [NSNumber numberWithDouble:value.alpha()], + nil]; + [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:array]; +} + +void PlatformCAAnimation::copyFromValueFrom(const PlatformCAAnimation* value) +{ + if (animationType() != Basic || value->animationType() != Basic) + return; + + CABasicAnimation* otherAnimation = static_cast<CABasicAnimation*>(value->m_animation.get()); + [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:[otherAnimation fromValue]]; +} + +void PlatformCAAnimation::setToValue(float value) +{ + if (animationType() != Basic) + return; + [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:[NSNumber numberWithDouble:value]]; +} + +void PlatformCAAnimation::setToValue(const WebCore::TransformationMatrix& value) +{ + if (animationType() != Basic) + return; + + [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:[NSValue valueWithCATransform3D:value]]; +} + +void PlatformCAAnimation::setToValue(const FloatPoint3D& value) +{ + if (animationType() != Basic) + return; + + NSArray* array = [NSArray arrayWithObjects: + [NSNumber numberWithDouble:value.x()], + [NSNumber numberWithDouble:value.y()], + [NSNumber numberWithDouble:value.z()], + nil]; + [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:array]; +} + +void PlatformCAAnimation::setToValue(const WebCore::Color& value) +{ + if (animationType() != Basic) + return; + + NSArray* array = [NSArray arrayWithObjects: + [NSNumber numberWithDouble:value.red()], + [NSNumber numberWithDouble:value.green()], + [NSNumber numberWithDouble:value.blue()], + [NSNumber numberWithDouble:value.alpha()], + nil]; + [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:array]; +} + +void PlatformCAAnimation::copyToValueFrom(const PlatformCAAnimation* value) +{ + if (animationType() != Basic || value->animationType() != Basic) + return; + + CABasicAnimation* otherAnimation = static_cast<CABasicAnimation*>(value->m_animation.get()); + [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:[otherAnimation toValue]]; +} + + +// Keyframe-animation properties. +void PlatformCAAnimation::setValues(const Vector<float>& value) +{ + if (animationType() != Keyframe) + return; + + NSMutableArray* array = [NSMutableArray array]; + for (size_t i = 0; i < value.size(); ++i) + [array addObject:[NSNumber numberWithDouble:value[i]]]; + [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:array]; +} + +void PlatformCAAnimation::setValues(const Vector<WebCore::TransformationMatrix>& value) +{ + if (animationType() != Keyframe) + return; + + NSMutableArray* array = [NSMutableArray array]; + + for (size_t i = 0; i < value.size(); ++i) + [array addObject:[NSValue valueWithCATransform3D:value[i]]]; + + [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:array]; +} + +void PlatformCAAnimation::setValues(const Vector<FloatPoint3D>& value) +{ + if (animationType() != Keyframe) + return; + + NSMutableArray* array = [NSMutableArray array]; + + for (size_t i = 0; i < value.size(); ++i) { + NSArray* object = [NSArray arrayWithObjects: + [NSNumber numberWithDouble:value[i].x()], + [NSNumber numberWithDouble:value[i].y()], + [NSNumber numberWithDouble:value[i].z()], + nil]; + [array addObject:object]; + } + [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:array]; +} + +void PlatformCAAnimation::setValues(const Vector<WebCore::Color>& value) +{ + if (animationType() != Keyframe) + return; + + NSMutableArray* array = [NSMutableArray array]; + + for (size_t i = 0; i < value.size(); ++i) { + NSArray* object = [NSArray arrayWithObjects: + [NSNumber numberWithDouble:value[i].red()], + [NSNumber numberWithDouble:value[i].green()], + [NSNumber numberWithDouble:value[i].blue()], + [NSNumber numberWithDouble:value[i].alpha()], + nil]; + [array addObject:object]; + } + [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:array]; +} + +void PlatformCAAnimation::copyValuesFrom(const PlatformCAAnimation* value) +{ + if (animationType() != Keyframe || value->animationType() != Keyframe) + return; + + CAKeyframeAnimation* otherAnimation = static_cast<CAKeyframeAnimation*>(value->m_animation.get()); + [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:[otherAnimation values]]; +} + +void PlatformCAAnimation::setKeyTimes(const Vector<float>& value) +{ + NSMutableArray* array = [NSMutableArray array]; + + for (size_t i = 0; i < value.size(); ++i) + [array addObject:[NSNumber numberWithFloat:value[i]]]; + + [static_cast<CAKeyframeAnimation*>(m_animation.get()) setKeyTimes:array]; +} + +void PlatformCAAnimation::copyKeyTimesFrom(const PlatformCAAnimation* value) +{ + CAKeyframeAnimation* other = static_cast<CAKeyframeAnimation*>(value->m_animation.get()); + [static_cast<CAKeyframeAnimation*>(m_animation.get()) setKeyTimes:[other keyTimes]]; +} + +void PlatformCAAnimation::setTimingFunctions(const Vector<const TimingFunction*>& value) +{ + NSMutableArray* array = [NSMutableArray array]; + + for (size_t i = 0; i < value.size(); ++i) + [array addObject:toCAMediaTimingFunction(value[i])]; + + [static_cast<CAKeyframeAnimation*>(m_animation.get()) setTimingFunctions:array]; +} + +void PlatformCAAnimation::copyTimingFunctionsFrom(const PlatformCAAnimation* value) +{ + CAKeyframeAnimation* other = static_cast<CAKeyframeAnimation*>(value->m_animation.get()); + [static_cast<CAKeyframeAnimation*>(m_animation.get()) setTimingFunctions:[other timingFunctions]]; +} + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/ca/mac/PlatformCALayerMac.mm b/WebCore/platform/graphics/ca/mac/PlatformCALayerMac.mm new file mode 100644 index 0000000..0aecf14 --- /dev/null +++ b/WebCore/platform/graphics/ca/mac/PlatformCALayerMac.mm @@ -0,0 +1,725 @@ +/* + * 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) + +#import "PlatformCALayer.h" + +#import "BlockExceptions.h" +#import "FloatConversion.h" +#import "GraphicsContext.h" +#import "GraphicsLayerCA.h" +#import "WebLayer.h" +#import "WebTiledLayer.h" +#import <objc/objc-auto.h> +#import <objc/objc-runtime.h> +#import <QuartzCore/QuartzCore.h> +#import <wtf/CurrentTime.h> +#import <wtf/UnusedParam.h> + +#define HAVE_MODERN_QUARTZCORE (!defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)) + +using namespace WebCore; + +// This value must be the same as in PlatformCAAnimationMac.mm +static NSString * const WKNonZeroBeginTimeFlag = @"WKPlatformCAAnimationNonZeroBeginTimeFlag"; + +static double mediaTimeToCurrentTime(CFTimeInterval t) +{ + return WTF::currentTime() + t - CACurrentMediaTime(); +} + +// Delegate for animationDidStart callback +@interface WebAnimationDelegate : NSObject { + PlatformCALayer* m_owner; +} + +- (void)animationDidStart:(CAAnimation *)anim; +- (void)setOwner:(PlatformCALayer*)owner; + +@end + +@implementation WebAnimationDelegate + +- (void)animationDidStart:(CAAnimation *)animation +{ + // hasNonZeroBeginTime is stored in a key in the animation + bool hasNonZeroBeginTime = [[animation valueForKey:WKNonZeroBeginTimeFlag] boolValue]; + CFTimeInterval startTime; + + if (hasNonZeroBeginTime) { + // We don't know what time CA used to commit the animation, so just use the current time + // (even though this will be slightly off). + startTime = mediaTimeToCurrentTime(CACurrentMediaTime()); + } else + startTime = mediaTimeToCurrentTime([animation beginTime]); + + if (m_owner) + m_owner->animationStarted(startTime); +} + +- (void)setOwner:(PlatformCALayer*)owner +{ + m_owner = owner; +} + +@end + +@interface CALayer(Private) +- (void)setContentsChanged; +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +- (void)setAcceleratesDrawing:(BOOL)flag; +- (BOOL)acceleratesDrawing; +#endif +@end + +static NSString * const platformCALayerPointer = @"WKPlatformCALayer"; + +bool PlatformCALayer::isValueFunctionSupported() +{ + static bool sHaveValueFunction = [CAPropertyAnimation instancesRespondToSelector:@selector(setValueFunction:)]; + return sHaveValueFunction; +} + +void PlatformCALayer::setOwner(GraphicsLayerCA* owner) +{ + m_owner = owner; + + // Change the delegate's owner if needed + if (m_delegate) + [static_cast<WebAnimationDelegate*>(m_delegate.get()) setOwner:this]; +} + +static NSDictionary* nullActionsDictionary() +{ + NSNull* nullValue = [NSNull null]; + NSDictionary* actions = [NSDictionary dictionaryWithObjectsAndKeys: + nullValue, @"anchorPoint", + nullValue, @"bounds", + nullValue, @"contents", + nullValue, @"contentsRect", + nullValue, @"opacity", + nullValue, @"position", + nullValue, @"shadowColor", + nullValue, @"sublayerTransform", + nullValue, @"sublayers", + nullValue, @"transform", + nullValue, @"zPosition", + nil]; + return actions; +} + +#if HAVE_MODERN_QUARTZCORE +static NSString* toCAFilterType(PlatformCALayer::FilterType type) +{ + switch (type) { + case PlatformCALayer::Linear: return kCAFilterLinear; + case PlatformCALayer::Nearest: return kCAFilterNearest; + case PlatformCALayer::Trilinear: return kCAFilterTrilinear; + default: return 0; + } +} +#endif + +PassRefPtr<PlatformCALayer> PlatformCALayer::create(LayerType layerType, GraphicsLayerCA* owner) +{ + return adoptRef(new PlatformCALayer(layerType, 0, owner)); +} + +PassRefPtr<PlatformCALayer> PlatformCALayer::create(void* platformLayer, GraphicsLayerCA* owner) +{ + return adoptRef(new PlatformCALayer(LayerTypeCustom, static_cast<PlatformLayer*>(platformLayer), owner)); +} + +PlatformCALayer::PlatformCALayer(LayerType layerType, PlatformLayer* layer, GraphicsLayerCA* owner) + : m_owner(owner) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + if (layer) { + m_layerType = LayerTypeCustom; + m_layer = layer; + } else { + m_layerType = layerType; + + Class layerClass = Nil; + switch(layerType) { + case LayerTypeLayer: + layerClass = [CALayer class]; + break; + case LayerTypeWebLayer: + layerClass = [WebLayer class]; + break; + case LayerTypeTransformLayer: + layerClass = NSClassFromString(@"CATransformLayer"); + break; + case LayerTypeWebTiledLayer: + layerClass = [WebTiledLayer class]; + break; + case LayerTypeCustom: + break; + } + + if (layerClass) + m_layer.adoptNS([[layerClass alloc] init]); + } + + // Save a pointer to 'this' in the CALayer + [m_layer.get() setValue:[NSValue valueWithPointer:this] forKey:platformCALayerPointer]; + + // Clear all the implicit animations on the CALayer + [m_layer.get() setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; + + // If this is a TiledLayer, set some initial values + if (m_layerType == LayerTypeWebTiledLayer) { + WebTiledLayer* tiledLayer = static_cast<WebTiledLayer*>(m_layer.get()); + [tiledLayer setTileSize:CGSizeMake(GraphicsLayerCA::kTiledLayerTileSize, GraphicsLayerCA::kTiledLayerTileSize)]; + [tiledLayer setLevelsOfDetail:1]; + [tiledLayer setLevelsOfDetailBias:0]; + [tiledLayer setContentsGravity:@"bottomLeft"]; + } + + END_BLOCK_OBJC_EXCEPTIONS +} + +PlatformCALayer::~PlatformCALayer() +{ + [m_layer.get() setValue:nil forKey:platformCALayerPointer]; + + // Clear the owner, which also clears it in the delegate to prevent attempts + // to use the GraphicsLayerCA after it has been destroyed. + setOwner(0); + + // Remove the owner pointer from the delegate in case there is a pending animationStarted event. + [static_cast<WebAnimationDelegate*>(m_delegate.get()) setOwner:nil]; +} + +PlatformCALayer* PlatformCALayer::platformCALayer(void* platformLayer) +{ + if (!platformLayer) + return 0; + + // Pointer to PlatformCALayer is kept in a key of the CALayer + PlatformCALayer* platformCALayer = nil; + BEGIN_BLOCK_OBJC_EXCEPTIONS + platformCALayer = static_cast<PlatformCALayer*>([[static_cast<CALayer*>(platformLayer) valueForKey:platformCALayerPointer] pointerValue]); + END_BLOCK_OBJC_EXCEPTIONS + return platformCALayer; +} + +PlatformLayer* PlatformCALayer::platformLayer() const +{ + return m_layer.get(); +} + +void PlatformCALayer::setNeedsDisplay(const FloatRect* dirtyRect) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + if (dirtyRect) + [m_layer.get() setNeedsDisplayInRect:*dirtyRect]; + else + [m_layer.get() setNeedsDisplay]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void PlatformCALayer::setContentsChanged() +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setContentsChanged]; + END_BLOCK_OBJC_EXCEPTIONS +} + +PlatformCALayer* PlatformCALayer::superlayer() const +{ + return platformCALayer([m_layer.get() superlayer]); +} + +void PlatformCALayer::removeFromSuperlayer() +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() removeFromSuperlayer]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void PlatformCALayer::setSublayers(const PlatformCALayerList& list) +{ + // Short circuiting here not only avoids the allocation of sublayers, but avoids <rdar://problem/7390716> (see below) + if (list.size() == 0) { + removeAllSublayers(); + return; + } + + BEGIN_BLOCK_OBJC_EXCEPTIONS + NSMutableArray* sublayers = [[NSMutableArray alloc] init]; + for (size_t i = 0; i < list.size(); ++i) + [sublayers addObject:list[i]->m_layer.get()]; + + [m_layer.get() setSublayers:sublayers]; + [sublayers release]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void PlatformCALayer::removeAllSublayers() +{ + // Workaround for <rdar://problem/7390716>: -[CALayer setSublayers:] crashes if sublayers is an empty array, or nil, under GC. + BEGIN_BLOCK_OBJC_EXCEPTIONS + if (objc_collectingEnabled()) + while ([[m_layer.get() sublayers] count]) + [[[m_layer.get() sublayers] objectAtIndex:0] removeFromSuperlayer]; + else + [m_layer.get() setSublayers:nil]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void PlatformCALayer::appendSublayer(PlatformCALayer* layer) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() addSublayer:layer->m_layer.get()]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void PlatformCALayer::insertSublayer(PlatformCALayer* layer, size_t index) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() insertSublayer:layer->m_layer.get() atIndex:index]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void PlatformCALayer::replaceSublayer(PlatformCALayer* reference, PlatformCALayer* layer) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() replaceSublayer:reference->m_layer.get() with:layer->m_layer.get()]; + END_BLOCK_OBJC_EXCEPTIONS +} + +size_t PlatformCALayer::sublayerCount() const +{ + return [[m_layer.get() sublayers] count]; +} + +void PlatformCALayer::adoptSublayers(PlatformCALayer* source) +{ + // Workaround for <rdar://problem/7390716>: -[CALayer setSublayers:] crashes if sublayers is an empty array, or nil, under GC. + NSArray* sublayers = [source->m_layer.get() sublayers]; + + if (objc_collectingEnabled() && ![sublayers count]) { + BEGIN_BLOCK_OBJC_EXCEPTIONS + while ([[m_layer.get() sublayers] count]) + [[[m_layer.get() sublayers] objectAtIndex:0] removeFromSuperlayer]; + END_BLOCK_OBJC_EXCEPTIONS + return; + } + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setSublayers:sublayers]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void PlatformCALayer::addAnimationForKey(const String& key, PlatformCAAnimation* animation) +{ + // Add the delegate + if (!m_delegate) { + WebAnimationDelegate* webAnimationDelegate = [[WebAnimationDelegate alloc] init]; + m_delegate.adoptNS(webAnimationDelegate); + [webAnimationDelegate setOwner:this]; + } + + CAPropertyAnimation* propertyAnimation = static_cast<CAPropertyAnimation*>(animation->platformAnimation()); + + if (![propertyAnimation delegate]) + [propertyAnimation setDelegate:static_cast<id>(m_delegate.get())]; + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() addAnimation:animation->m_animation.get() forKey:key]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void PlatformCALayer::removeAnimationForKey(const String& key) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() removeAnimationForKey:key]; + END_BLOCK_OBJC_EXCEPTIONS +} + +PassRefPtr<PlatformCAAnimation> PlatformCALayer::animationForKey(const String& key) +{ + CAPropertyAnimation* propertyAnimation = static_cast<CAPropertyAnimation*>([m_layer.get() animationForKey:key]); + if (!propertyAnimation) + return 0; + return PlatformCAAnimation::create(propertyAnimation); +} + +PlatformCALayer* PlatformCALayer::mask() const +{ + return platformCALayer([m_layer.get() mask]); +} + +void PlatformCALayer::setMask(PlatformCALayer* layer) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setMask:layer ? layer->platformLayer() : 0]; + END_BLOCK_OBJC_EXCEPTIONS +} + +bool PlatformCALayer::isOpaque() const +{ + return [m_layer.get() isOpaque]; +} + +void PlatformCALayer::setOpaque(bool value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setOpaque:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +FloatRect PlatformCALayer::bounds() const +{ + return [m_layer.get() bounds]; +} + +void PlatformCALayer::setBounds(const FloatRect& value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setBounds:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +FloatPoint3D PlatformCALayer::position() const +{ + CGPoint point = [m_layer.get() position]; + return FloatPoint3D(point.x, point.y, [m_layer.get() zPosition]); +} + +void PlatformCALayer::setPosition(const FloatPoint3D& value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setPosition:CGPointMake(value.x(), value.y())]; + [m_layer.get() setZPosition:value.z()]; + END_BLOCK_OBJC_EXCEPTIONS +} + +FloatPoint3D PlatformCALayer::anchorPoint() const +{ + CGPoint point = [m_layer.get() anchorPoint]; + float z = 0; +#if HAVE_MODERN_QUARTZCORE + z = [m_layer.get() anchorPointZ]; +#endif + return FloatPoint3D(point.x, point.y, z); +} + +void PlatformCALayer::setAnchorPoint(const FloatPoint3D& value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setAnchorPoint:CGPointMake(value.x(), value.y())]; +#if HAVE_MODERN_QUARTZCORE + [m_layer.get() setAnchorPointZ:value.z()]; +#endif + END_BLOCK_OBJC_EXCEPTIONS +} + +TransformationMatrix PlatformCALayer::transform() const +{ + return [m_layer.get() transform]; +} + +void PlatformCALayer::setTransform(const TransformationMatrix& value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setTransform:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +TransformationMatrix PlatformCALayer::sublayerTransform() const +{ + return [m_layer.get() sublayerTransform]; +} + +void PlatformCALayer::setSublayerTransform(const TransformationMatrix& value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setSublayerTransform:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +TransformationMatrix PlatformCALayer::contentsTransform() const +{ +#if !HAVE_MODERN_QUARTZCORE + if (m_layerType != LayerTypeWebLayer) + return TransformationMatrix(); + + return [static_cast<WebLayer*>(m_layer.get()) contentsTransform]; +#else + return TransformationMatrix(); +#endif +} + +void PlatformCALayer::setContentsTransform(const TransformationMatrix& value) +{ +#if !HAVE_MODERN_QUARTZCORE + if (m_layerType != LayerTypeWebLayer) + return; + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setContentsTransform:value]; + END_BLOCK_OBJC_EXCEPTIONS +#else + UNUSED_PARAM(value); +#endif +} + +bool PlatformCALayer::isHidden() const +{ + return [m_layer.get() isHidden]; +} + +void PlatformCALayer::setHidden(bool value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setHidden:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +bool PlatformCALayer::isGeometryFlipped() const +{ +#if HAVE_MODERN_QUARTZCORE + return [m_layer.get() isGeometryFlipped]; +#else + return false; +#endif +} + +void PlatformCALayer::setGeometryFlipped(bool value) +{ +#if HAVE_MODERN_QUARTZCORE + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setGeometryFlipped:value]; + END_BLOCK_OBJC_EXCEPTIONS +#else + UNUSED_PARAM(value); +#endif +} + +bool PlatformCALayer::isDoubleSided() const +{ + return [m_layer.get() isDoubleSided]; +} + +void PlatformCALayer::setDoubleSided(bool value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setDoubleSided:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +bool PlatformCALayer::masksToBounds() const +{ + return [m_layer.get() masksToBounds]; +} + +void PlatformCALayer::setMasksToBounds(bool value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setMasksToBounds:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +bool PlatformCALayer::acceleratesDrawing() const +{ +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + return [m_layer.get() acceleratesDrawing]; +#else + return false; +#endif +} + +void PlatformCALayer::setAcceleratesDrawing(bool acceleratesDrawing) +{ +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setAcceleratesDrawing:acceleratesDrawing]; + END_BLOCK_OBJC_EXCEPTIONS +#else + UNUSED_PARAM(acceleratesDrawing); +#endif +} + +void* PlatformCALayer::contents() const +{ + return [m_layer.get() contents]; +} + +void PlatformCALayer::setContents(void* value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setContents:static_cast<id>(value)]; + END_BLOCK_OBJC_EXCEPTIONS +} + +FloatRect PlatformCALayer::contentsRect() const +{ + return [m_layer.get() contentsRect]; +} + +void PlatformCALayer::setContentsRect(const FloatRect& value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setContentsRect:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void PlatformCALayer::setMinificationFilter(FilterType value) +{ +#if HAVE_MODERN_QUARTZCORE + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setMinificationFilter:toCAFilterType(value)]; + END_BLOCK_OBJC_EXCEPTIONS +#else + UNUSED_PARAM(value); +#endif +} + +void PlatformCALayer::setMagnificationFilter(FilterType value) +{ +#if HAVE_MODERN_QUARTZCORE + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setMagnificationFilter:toCAFilterType(value)]; + END_BLOCK_OBJC_EXCEPTIONS +#else + UNUSED_PARAM(value); +#endif +} + +Color PlatformCALayer::backgroundColor() const +{ + return [m_layer.get() backgroundColor]; +} + +void PlatformCALayer::setBackgroundColor(const Color& value) +{ + CGFloat components[4]; + value.getRGBA(components[0], components[1], components[2], components[3]); + + RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); + RetainPtr<CGColorRef> color(AdoptCF, CGColorCreate(colorSpace.get(), components)); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setBackgroundColor:color.get()]; + END_BLOCK_OBJC_EXCEPTIONS +} + +float PlatformCALayer::borderWidth() const +{ + return [m_layer.get() borderWidth]; +} + +void PlatformCALayer::setBorderWidth(float value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setBorderWidth:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +Color PlatformCALayer::borderColor() const +{ + return [m_layer.get() borderColor]; +} + +void PlatformCALayer::setBorderColor(const Color& value) +{ + CGFloat components[4]; + value.getRGBA(components[0], components[1], components[2], components[3]); + + RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); + RetainPtr<CGColorRef> color(AdoptCF, CGColorCreate(colorSpace.get(), components)); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setBorderColor:color.get()]; + END_BLOCK_OBJC_EXCEPTIONS +} + +float PlatformCALayer::opacity() const +{ + return [m_layer.get() opacity]; +} + +void PlatformCALayer::setOpacity(float value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setOpacity:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +String PlatformCALayer::name() const +{ + return [m_layer.get() name]; +} + +void PlatformCALayer::setName(const String& value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setName:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +FloatRect PlatformCALayer::frame() const +{ + return [m_layer.get() frame]; +} + +void PlatformCALayer::setFrame(const FloatRect& value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setFrame:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +float PlatformCALayer::speed() const +{ + return [m_layer.get() speed]; +} + +void PlatformCALayer::setSpeed(float value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setSpeed:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +CFTimeInterval PlatformCALayer::timeOffset() const +{ + return [m_layer.get() timeOffset]; +} + +void PlatformCALayer::setTimeOffset(CFTimeInterval value) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setTimeOffset:value]; + END_BLOCK_OBJC_EXCEPTIONS +} + +#endif // USE(ACCELERATED_COMPOSITING) |